Android API Guide for Media and Camera(二)—— 媒体与相机(媒体记录器)

本文介绍如何使用Android的MediaRecorder API来捕获音频并进行播放。通过实例代码展示了设置音频源、输出格式、编码器等步骤,实现了从麦克风录制音频、保存文件及播放录音的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MediaRecorder

Android框架支持各种捕获和编码各种常见的音频和视频格式。你可以使用MediaRecorder APIs如果设备硬件支持的话。

本文展示如何使用MediaRecorder编写一个从设备麦克风捕获音频,并保存和播放(使用MediaPlayer)的应用程序。为了记录视频,你需要通过MediaRecorder使用设备的相机。这里有相机指南的描述。
https://developer.android.google.cn/guide/topics/media/camera.html

Note:Android模拟器不能捕获音频,不过真实设备上是有提供这些功能的。

实现音频的捕获

从设备捕获音频比音频和视频的播放相对难一点,但仍然相当简单:

1.创建android.media.MediaRecorder实例.
2.使用 MediaRecorder.setAudioSource(). 设置音频的资源。你可能需要使用MediaRecorder.AudioSource.MIC.
3.使用MediaRecorder.setOutputFormat().设置输出文件的格式。
4.使用 MediaRecorder.setOutputFile().设置输出文件的名字。
5.使用 MediaRecorder.setAudioEncoder().设置音频的编码器。
6.在MediaRecorder的示例中调用MediaRecorder.prepare()。
7.调用MediaRecorder.start().开始捕获音频。
8.调用MediaRecorder.stop().停止捕获音频。
9.当你使用MediaRecorder示例完成工作之后,记得调用 MediaRecorder.release() 立即释放它所占用资源。

示例:记录和回放音频

下面的示例类演示如何创建,开始和停止捕获音频,并播放记录的音频文件。

/*
 * The application needs to have the permission to write to external storage
 * if the output file is written to the external storage, and also the
 * permission to record audio. These permissions must be set in the
 * application's AndroidManifest.xml file, with something like:
 *
 * <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 * <uses-permission android:name="android.permission.RECORD_AUDIO" />
 *
 */
package com.android.audiorecordtest;

import android.app.Activity;
import android.widget.LinearLayout;
import android.os.Bundle;
import android.os.Environment;
import android.view.ViewGroup;
import android.widget.Button;
import android.view.View;
import android.view.View.OnClickListener;
import android.content.Context;
import android.util.Log;
import android.media.MediaRecorder;
import android.media.MediaPlayer;

import java.io.IOException;


public class AudioRecordTest extends Activity
{
    private static final String LOG_TAG = "AudioRecordTest";
    private static String mFileName = null;

    private RecordButton mRecordButton = null;
    private MediaRecorder mRecorder = null;

    private PlayButton   mPlayButton = null;
    private MediaPlayer   mPlayer = null;

    private void onRecord(boolean start) {
        if (start) {
            startRecording();
        } else {
            stopRecording();
        }
    }

    private void onPlay(boolean start) {
        if (start) {
            startPlaying();
        } else {
            stopPlaying();
        }
    }

    private void startPlaying() {
        mPlayer = new MediaPlayer();
        try {
            mPlayer.setDataSource(mFileName);
            mPlayer.prepare();
            mPlayer.start();
        } catch (IOException e) {
            Log.e(LOG_TAG, "prepare() failed");
        }
    }

    private void stopPlaying() {
        mPlayer.release();
        mPlayer = null;
    }

    private void startRecording() {
        mRecorder = new MediaRecorder();
        mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
        mRecorder.setOutputFile(mFileName);
        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

        try {
            mRecorder.prepare();
        } catch (IOException e) {
            Log.e(LOG_TAG, "prepare() failed");
        }

        mRecorder.start();
    }

    private void stopRecording() {
        mRecorder.stop();
        mRecorder.release();
        mRecorder = null;
    }

    class RecordButton extends Button {
        boolean mStartRecording = true;

        OnClickListener clicker = new OnClickListener() {
            public void onClick(View v) {
                onRecord(mStartRecording);
                if (mStartRecording) {
                    setText("Stop recording");
                } else {
                    setText("Start recording");
                }
                mStartRecording = !mStartRecording;
            }
        };

        public RecordButton(Context ctx) {
            super(ctx);
            setText("Start recording");
            setOnClickListener(clicker);
        }
    }

    class PlayButton extends Button {
        boolean mStartPlaying = true;

        OnClickListener clicker = new OnClickListener() {
            public void onClick(View v) {
                onPlay(mStartPlaying);
                if (mStartPlaying) {
                    setText("Stop playing");
                } else {
                    setText("Start playing");
                }
                mStartPlaying = !mStartPlaying;
            }
        };

        public PlayButton(Context ctx) {
            super(ctx);
            setText("Start playing");
            setOnClickListener(clicker);
        }
    }

    public AudioRecordTest() {
        mFileName = Environment.getExternalStorageDirectory().getAbsolutePath();
        mFileName += "/audiorecordtest.3gp";
    }

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        LinearLayout ll = new LinearLayout(this);
        mRecordButton = new RecordButton(this);
        ll.addView(mRecordButton,
            new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT,
                0));
        mPlayButton = new PlayButton(this);
        ll.addView(mPlayButton,
            new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT,
                0));
        setContentView(ll);
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mRecorder != null) {
            mRecorder.release();
            mRecorder = null;
        }

        if (mPlayer != null) {
            mPlayer.release();
            mPlayer = null;
        }
    }
}

原文链接:https://developer.android.google.cn/guide/topics/media/mediarecorder.html

<think>嗯,用户这次需要的是关于AndroidCameraAPI2的具体Java实现代码。考虑到用户明确指向Java(而不是Kotlin),且需求聚焦在拍照功能的实现上,这应该是一个Android相机开发入门需求。之前对话历史里提到过系统级指令要求,特别要注意代码块格式和中文回答的要求。用户作为开发者显然需要可直接运行的示例,但CameraAPI2的复杂性决定了必须分步解释。从用户提问方式看,ta可能已经尝试过基础开发但卡在具体实现环节。需要重点解释三个核心对象:CameraDevice、CameraCaptureSession和CaptureRequest的协作关系,这是API2最容易被误解的部分。考虑到安全规范,必须包含动态权限申请的代码模板,否则用户在Android6.0+设备无法运行。同时提醒Camera2特有的特征:相机权限不仅需要manifest声明,运行时还要额外申请CAMERA权限和WRITE_EXTERNAL_STORAGE权限。硬件兼容性方面,需要特别说明API21最低版本要求,以及前置/后置摄像头选择方法。示例使用后置摄像头作为基准,但应注明getCameraCharacteristics的关键作用。拍摄流程中imageReader的处理是重点难点:照片完成回调的线程问题容易引发崩溃,必须强调使用Handler切换线程。示例里采用ImageSaver封装类处理耗时保存操作是较优方案。最后考虑延伸问题设计:焦点控制预览回调都是实际开发中的高频痛点,自定义回调处理功能对高级用户也有参考价值。</think> 以下是使用Android Camera2 API实现拍照功能的Java示例代码,包含关键步骤解析: --- ### 1. 添加权限和特性声明 **AndroidManifest.xml** ```xml <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application> <activity ... /> <!-- 声明相机特性 --> <uses-feature android:name="android.hardware.camera" /> </application> ``` --- ### 2. 实现拍照核心代码 ```java public class CameraActivity extends AppCompatActivity { private CameraDevice cameraDevice; private CameraCaptureSession captureSession; private ImageReader imageReader; private String cameraId; private Handler backgroundHandler; // 1. 初始化预览Surface private SurfaceTexture textureViewSurface; private TextureView textureView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); textureView = new TextureView(this); setContentView(textureView); // 2. 打开相机 CameraManager manager = (CameraManager) getSystemService(CAMERA_SERVICE); cameraId = manager.getCameraIdList()[0]; // 默认使用后置摄像头 // 3. 创建图片读取器(保存照片) imageReader = ImageReader.newInstance( 1920, 1080, ImageFormat.JPEG, 1); imageReader.setOnImageAvailableListener( reader -> saveImage(reader), null); } // 4. 相机准备就绪回调 private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice camera) { cameraDevice = camera; startPreview(); // 开始预览 } // ... 省略错误处理 }; // 5. 启动相机预览 private void startPreview() { Surface previewSurface = new Surface(textureViewSurface); try { // 配置捕获会话 cameraDevice.createCaptureSession( Arrays.asList(previewSurface, imageReader.getSurface()), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { captureSession = session; try { // 6. 设置持续预览请求 CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); builder.addTarget(previewSurface); session.setRepeatingRequest(builder.build(), null, null); } catch (CameraAccessException e) { e.printStackTrace(); } } }, backgroundHandler ); } catch (CameraAccessException e) { e.printStackTrace(); } } // 7. 拍照按钮点击事件 public void takePicture(View view) { if (cameraDevice == null) return; try { // 创建拍照请求 CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(imageReader.getSurface()); captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 8. 执行拍照 captureSession.capture(captureBuilder.build(), new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(...) { Toast.makeText(CameraActivity.this, "照片已保存", Toast.LENGTH_SHORT).show(); } }, backgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } // 9. 保存图片到存储 private void saveImage(ImageReader reader) { Image image = reader.acquireNextImage(); ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] data = new byte[buffer.remaining()]; buffer.get(data); // 在子线程保存文件 AsyncTask.execute(() -> { File file = new File(getExternalFilesDir(null), "photo_" + System.currentTimeMillis() + ".jpg"); try (FileOutputStream fos = new FileOutputStream(file)) { fos.write(data); } catch (IOException e) { e.printStackTrace(); } image.close(); }); } } ``` --- ### 关键步骤说明: 1. **相机权限动态申请**:Android 6.0+需运行时申请权限 2. **选择合适分辨率**:通过`StreamConfigurationMap`获取设备支持的分辨率 3. **焦点控制**:通过`CONTROL_AF_TRIGGER_START`实现自动对焦 4. **方向处理**:根据设备旋转角度设置`JPEG_ORIENTATION` 5. **生命周期管理**: ```java @Override protected void onPause() { closeCamera(); super.onPause(); } ``` --- ### 注意事项: 1. **相机ID选择**:使用`CameraCharacteristics.LENS_FACING_BACK`选择后置摄像头 2. **线程管理**:通过`HandlerThread`创建后台线程处理相机操作 3. **异常处理**:捕获`CameraAccessException`和`NullPointerException` 4. **兼容性检查**:使用`PackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)`检测设备是否支持相机 --- ### 延伸问题: 1. 如何处理多摄像头设备的镜头切换? 2. 如何实现手动对焦/曝光控制? 3. 预览画面如何实现适配不同屏幕比例? 4. Camera2和已废弃的Camera API有何核心区别? 5. 如何为拍照添加实时图像分析功能? > 建议参考官方文档:[Camera2 API Overview](https://developer.android.com/reference/android/hardware/camera2/package-summary) 和[相机开发指南](https://developer.android.com/guide/topics/media/camera)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值