Android 10 源码MediaRecorder 录像流程:MediaRecorder 配置
Posted TYYJ-洪伟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 10 源码MediaRecorder 录像流程:MediaRecorder 配置相关的知识,希望对你有一定的参考价值。
MediaRecorder 录像配置主要涉及输出文件路径、音频来源、视频来源、输出格式、音频编码格式、视频编码格式、比特率、帧率和视频尺寸等。
我们假设视频输入源来自 Camera,Camera2 API 将相机图像渲染到 MediaRecorder 提供的 Surface 上,而 MediaRecorder 将这个渲染数据编码为 H264。
/**
* 配置录制视频相关数据
*/
private void configMediaRecorder()
File file = new File(getExternalCacheDir(),"demo.mp4");
if (file.exists())
file.delete();
mMediaRecorder.setAudiosource(MediaRecorder.AudioSource.MIC);//设置音频来源
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);//设置视频来源
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);//设置输出格式
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);//设置音频编码格式
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);//设置视频编码格式
mMediaRecorder.setVideoEncodingBitRate(1920 * 1080 * 30 * 0.2);//设置比特率
mMediaRecorder.setVideoFrameRate(30);//设置帧数
mMediaRecorder.setVideoSize(1920, 1080);
mMediaRecorder.setOutputFile(file.getAbsolutePath());
try
mMediaRecorder.prepare();
catch (IOException e)
e.printStackTrace();
...
mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
List<Surface> surfaces = new ArrayList<>();
// Set up Surface for the camera preview
Surface previewSurface = new Surface(texture);
surfaces.add(previewSurface);
mPreviewBuilder.addTarget(previewSurface);
// Set up Surface for the MediaRecorder
Surface recorderSurface = mMediaRecorder.getSurface();
surfaces.add(recorderSurface);
mPreviewBuilder.addTarget(recorderSurface);
// Start a capture session
// Once the session starts, we can update the UI and start recording
mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback()
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession)
...
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession)
...
, mBackgroundHandler);
获取使用 surface 视频源时要录制的 SURFACE。必须要在 prepare(…) 之后调用。getSurface() 是个 native 方法。
frameworks/base/media/java/android/media/MediaRecorder.java
public class MediaRecorder implements AudioRouting,
AudioRecordingMonitor,
AudioRecordingMonitorClient,
MicrophoneDirection
......
/**
* Gets the surface to record from when using SURFACE video source.
*
* <p> May only be called after @link #prepare. Frames rendered to the Surface before
* @link #start will be discarded.</p>
*
* @throws IllegalStateException if it is called before @link #prepare, after
* @link #stop, or is called when VideoSource is not set to SURFACE.
* @see android.media.MediaRecorder.VideoSource
*/
public native Surface getSurface();
......
- 调用 getMediaRecorder(…) 从 Java MediaRecorder 类 mNativeContext field 中取出 jlong 类型的值,并强转为 MediaRecorder* 指针,这是在初始化 MediaRecorder 的时候写入的。
- 调用 MediaRecorder 类 querySurfaceMediaSourceFromMediaServer(…) 获取指向 IGraphicBufferProducer 的代理强指针。
- 调用 android_view_Surface_createFromIGraphicBufferProducer(…) 将 IGraphicBufferProducer 包装成 Java Surface 返回。
frameworks/base/media/jni/android_media_MediaRecorder.cpp
static sp<MediaRecorder> getMediaRecorder(JNIEnv* env, jobject thiz)
Mutex::Autolock l(sLock);
MediaRecorder* const p = (MediaRecorder*)env->GetLongField(thiz, fields.context);
return sp<MediaRecorder>(p);
......
static jobject
android_media_MediaRecorder_getSurface(JNIEnv *env, jobject thiz)
ALOGV("getSurface");
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
if (mr == NULL)
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return NULL;
sp<IGraphicBufferProducer> bufferProducer = mr->querySurfaceMediaSourceFromMediaServer();
if (bufferProducer == NULL)
jniThrowException(
env,
"java/lang/IllegalStateException",
"failed to get surface");
return NULL;
// Wrap the IGBP in a Java-language Surface.
return android_view_Surface_createFromIGraphicBufferProducer(env,
bufferProducer);
使用 binder 接口,通过 Mediaserver 查询 SurfaceMediaSurface。mSurfaceMediaSource 是个 sp 类型的字段,mMediaRecorder 是个 sp 类型的字段。由初始化一节《【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 初始化》不难知道此处 querySurfaceMediaSource() 调用最终会调到 MediaRecorderClient 同名方法处理。
frameworks/av/media/libmedia/mediarecorder.cpp
// Query a SurfaceMediaSurface through the Mediaserver, over the
// binder interface. This is used by the Filter Framework (MediaEncoder)
// to get an <IGraphicBufferProducer> object to hook up to ANativeWindow.
sp<IGraphicBufferProducer> MediaRecorder::
querySurfaceMediaSourceFromMediaServer()
Mutex::Autolock _l(mLock);
mSurfaceMediaSource =
mMediaRecorder->querySurfaceMediaSource();
if (mSurfaceMediaSource == NULL)
ALOGE("SurfaceMediaSource could not be initialized!");
return mSurfaceMediaSource;
MediaRecorderClient::querySurfaceMediaSource() 仅仅做了简单的检查后,最终委托给 StagefrightRecorder::querySurfaceMediaSource() 处理。
frameworks/av/media/libmediaplayerservice/MediaRecorderClient.cpp
sp<IGraphicBufferProducer> MediaRecorderClient::querySurfaceMediaSource()
ALOGV("Query SurfaceMediaSource");
Mutex::Autolock lock(mLock);
if (mRecorder == NULL)
ALOGE("recorder is not initialized");
return NULL;
return mRecorder->querySurfaceMediaSource();
注释里写的很明白了,mediaserver 的客户端要求它创建一个 SurfaceMediaSource 并返回一个接口引用。客户端将在编码 GL 帧时使用它。
这里只是简单的返回了 StagefrightRecorder 类的 mGraphicBufferProducer field 指向的值。给 mGraphicBufferProducer 赋值是在什么位置呢?分析后面的配置流程相信一定会水落石出的!
frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
// The client side of mediaserver asks it to create a SurfaceMediaSource
// and return a interface reference. The client side will use that
// while encoding GL Frames
sp<IGraphicBufferProducer> StagefrightRecorder::querySurfaceMediaSource() const
ALOGV("Get SurfaceMediaSource");
return mGraphicBufferProducer;
再来分析 android_view_Surface_createFromIGraphicBufferProducer(…)。
- 调用 Surface cpp 构造函数创建 Surface 对象,入参 controlledByApp 设置为 true,代表这个缓冲区 producer 由应用程序控制。
- 接着调用 android_view_Surface_createFromSurface(…) 将 cpp Surface 转化为 java Surface。
frameworks/base/core/jni/android_view_Surface.cpp
jobject android_view_Surface_createFromSurface(JNIEnv* env, const sp<Surface>& surface)
jobject surfaceObj = env->NewObject(gSurfaceClassInfo.clazz,
gSurfaceClassInfo.ctor, (jlong)surface.get());
if (surfaceObj == NULL)
if (env->ExceptionCheck())
ALOGE("Could not create instance of Surface from IGraphicBufferProducer.");
LOGE_EX(env);
env->ExceptionClear();
return NULL;
surface->incStrong(&sRefBaseOwner);
return surfaceObj;
jobject android_view_Surface_createFromIGraphicBufferProducer(JNIEnv* env,
const sp<IGraphicBufferProducer>& bufferProducer)
if (bufferProducer == NULL)
return NULL;
sp<Surface> surface(new Surface(bufferProducer, true));
return android_view_Surface_createFromSurface(env, surface);
现在继续分析主线:setAudioSource(MediaRecorder.AudioSource.MIC)。setAudioSource(…) 是个 JNI 方法。
frameworks/base/media/java/android/media/MediaRecorder.java
public class MediaRecorder implements AudioRouting,
AudioRecordingMonitor,
AudioRecordingMonitorClient,
MicrophoneDirection
......
/**
* Sets the audio source to be used for recording. If this method is not
* called, the output file will not contain an audio track. The source needs
* to be specified before setting recording-parameters or encoders. Call
* this only before setOutputFormat().
*
* @param audioSource the audio source to use
* @throws IllegalStateException if it is called after setOutputFormat()
* @see android.media.MediaRecorder.AudioSource
*/
public native void setAudioSource(@Source int audioSource)
throws IllegalStateException;
......
- 检查入参 as,对应 Java 方法入参 audioSource,不满足条件则直接调动 jniThrowException(…) 抛出 IllegalArgumentException 异常;
- 调用 getMediaRecorder(…) 获取 MediaRecorder native 对象;
- 调用 process_media_recorder_call(…) 检查 MediaRecorder native 对象的 setAudioSource(…) 调用是否存在非法操作,只要不等于 OK,就调用 jniThrowException(…) 抛出异常。
frameworks/base/media/jni/android_media_MediaRecorder.cpp
// Returns true if it throws an exception.
static bool process_media_recorder_call(JNIEnv *env, status_t opStatus, const char* exception, const char* message)
ALOGV("process_media_recorder_call");
if (opStatus == (status_t)INVALID_OPERATION)
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return true;
else if (opStatus != (status_t)OK)
jniThrowException(env, exception, message);
return true;
return false;
......
static void
android_media_MediaRecorder_setAudioSource(JNIEnv *env, jobject thiz, jint as)
ALOGV("setAudioSource(%d)", as);
if (as < AUDIO_SOURCE_DEFAULT ||
(as >= AUDIO_SOURCE_CNT && as != AUDIO_SOURCE_FM_TUNER))
jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid audio source");
return;
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
if (mr == NULL)
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
process_media_recorder_call(env, mr->setAudioSource(as), "java/lang/RuntimeException", "setAudioSource failed.");
- mMediaRecorder 是个 sp 类型的强指针,实际指向 BpMediaRecorder,如果为空直接返回 INVALID_OPERATION(无效操作);
- 在 MediaRecorder 构造函数中,mCurrentState 被赋值为 MEDIA_RECORDER_IDLE,因此此处会先调用 init() 进行初始化;
- 判断 mIsAudioSourceSet 标志是否置位,如果已经被置位那么直接返回 INVALID_OPERATION;
- 现在判断 mCurrentState 状态是否为 MEDIA_RECORDER_INITIALIZED(已经初始化),如果不是说明初始化发生了异常,这里也返回 INVALID_OPERATION;
- 现在开始调用 BpMediaRecorder setAudioSource(…) 进一步处理,最终实际上由 MediaRecorderClient::setAudioSource(…) 服务,此时 mIsAudioSourceSet 可以设置为 true 了,表征 Audio 源已设置。
frameworks/av/media/libmedia/mediarecorder.cpp
status_t MediaRecorder::setAudioSource(int as)
ALOGV("setAudioSource(%d)", as);
if (mMediaRecorder == NULL)
ALOGE("media recorder is not initialized yet");
return INVALID_OPERATION;
if (mCurrentState & MEDIA_RECORDER_IDLE)
ALOGV("Call init() since the media recorder is not initialized yet");
status_t ret = init();
if (OK != ret)
return ret;
if (mIsAudioSourceSet)
ALOGE("audio source has already been set");
return INVALID_OPERATION;
if (!(mCurrentState & MEDIA_RECORDER_INITIALIZED))
ALOGE("setAudioSource called in an invalid state(%d)", mCurrentState);
return INVALID_OPERATION;
status_t ret = mMediaRecorder->setAudioSource(as);
if (OK != ret)
ALOGV("setAudioSource failed: %d", ret);
mCurrentState = MEDIA_RECORDER_ERROR;
return ret;
mIsAudioSourceSet = true;
return ret;
init() 方法中主要调用 MediaRecorderClient::init() 和 MediaRecorderClient::setListener(…) 进一步处理,并且把 mCurrentState 置为 MEDIA_RECORDER_INITIALIZED。
frameworks/av/media/libmedia/mediarecorder.cpp
status_t MediaRecorder::init()
ALOGV("init");
if (mMediaRecorder == NULL)
ALOGE("media recorder is not initialized yet");
return INVALID_OPERATION;
if (!(mCurrentState & MEDIA_RECORDER_IDLE))
ALOGE("init called in an invalid state(%d)", mCurrentState);
return INVALID_OPERATION;
status_t ret = mMediaRecorder->init();
if (OK != ret)
ALOGV("init failed: %d", ret);
mCurrentState = MEDIA_RECORDER_ERROR;
return ret;
ret = mMediaRecorder->setListener(this);
if (OK != ret)
ALOGV("setListener failed: %d", ret);
mCurrentState = MEDIA_RECORDER_ERROR;
return ret;
mCurrentState = MEDIA_RECORDER_INITIALIZED;
return ret;
MediaRecorderClient::init() 仅仅将 init 动作交给 StagefrightRecorder::init() 进一步处理。
MediaRecorderClient::setListener(…) 看似很复杂,其实做的事非常简单,首先调用 StagefrightRecorder::setListener(…) 设置 Listener;接着将 DeathNotifier 对象逐个插入到 mDeathNotifiers 引用的 std::vector 容器中。从代码不难看出分别在 camera service、OMX service、Codec2 service 死掉的情况下发送相应通知告知 MediaRecorder native 对象,进而会调用其 notify(…) 方法将消息丢给 JNIMediaRecorderListener 继续抛到 Java 层。
frameworks/av/media/libmediaplayerservice/MediaRecorderClient.cpp
status_t MediaRecorderClient::init()
ALOGV("init");
Mutex::Autolock lock(mLock);
if (mRecorder == NULL)
ALOGE("recorder is not initialized");
return NO_INIT;
return mRecorder->init();
......
status_t MediaRecorderClient::setListener(const sp<IMediaRecorderClient>& listener)
ALOGV("setListener");
Mutex::Autolock lock(mLock);
mDeathNotifiers.clear();
if (mRecorder == NULL)
ALOGE("recorder is not initialized");
return NO_INIT;
mRecorder->setListener(listener);
sp<IServiceManager> sm = defaultServiceManager();
// WORKAROUND: We don't know if camera exists here and getService might block for 5 seconds.
// Use checkService for camera if we don't know it exists.
static std::atomic<bool> sCameraChecked(false); // once true never becomes false.
static std::atomic<bool> sCameraVerified(false); // once true never becomes false.
sp<IBinder> binder = (sCameraVerified || !sCameraChecked)
? sm->getService(String16("media.camera")) : sm->checkService(String16("media.camera"));
// If the device does not have a camera, do not create a death listener for it.
if (binder != NULL)
sCameraVerified = true;
mDeathNotifiers.emplace_back(
binder, [l = wp<IMediaRecorderClient>(listener)]()
sp<IMediaRecorderClient> listener = l.promote();
if (listener)
ALOGV("media.camera service died. "
"Sending death notification.");
listener->notify(
MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED,
MediaPlayerService::CAMERA_PROCESS_DEATH);
else
ALOGW("media.camera service died without a death handler.");
);
sCameraChecked = true;
using ::android::hidl::base::V1_0::IBase;
// Listen to OMX's IOmxStore/default
sp<IBase> base = ::android::hardware::media::omx::V1_0::
IOmx::getService();
if (base == nullptr)
ALOGD(如何在 Android 设备上将 WAV 编码为 mp3
【中文标题】如何在 Android 设备上将 WAV 编码为 mp3【英文标题】:How to encode a WAV to a mp3 on a Android device
【发布时间】:2010-09-04 10:50:49
【问题描述】:
我已经简化了我的问题并提供了赏金:
有哪些选项可用于在 Android 设备上将原始 PCM 音频数据压缩为 mp3。
我的原帖:
我正在我的 Android 手机上创建一个合成器,并且我一直在生成 PCM 数据以发送到扬声器。现在我想知道是否可以将此 PCM 数据编码为 mp3 以保存到 SD 卡。 MediaRecorder 对象可以将来自麦克风的音频编码为各种格式,但不允许对以编程方式生成的音频数据进行编码。
所以我的问题是,是否有用于编码音频的标准 Android API?如果没有,有哪些纯 Java 或基于 NDK 的解决方案?你能推荐其中任何一个吗?
如果做不到这一点,我只需将生成的音频保存为 WAV 文件,这很容易做到。
【问题讨论】:
“所以我的问题是,有没有用于编码音频的标准 Android API?” - 不。我会用java
标签重新标记您的问题,以帮助您获得意见...
仅供参考:MP3 技术已获得专利,因此将其包含在商业应用程序中会使您面临潜在的诉讼。你最好的防御就是不要让你的产品成功。 :)
是否还有另一种比 mp3 更开放的紧凑且受良好支持的(Android、Mac、Windows)格式?另外,mp3 真的会追随任何人使用这种格式吗?
@VictorGrazi Ogg Vorbis 音频 (.ogg) 实际上是一种比 mp3 更好的格式(文件大小更小,音质更好),但使用较少。
【参考方案1】:
纯 Java
查看 Tritonus 的 javasound 洁净室实现,它在此处提供 MP3 编码器 插件:http://www.tritonus.org/plugins.html
其次,我建议查看 jzoom 的库 JLayer 或 JLayerME:http://www.javazoom.net/javalayer/javalayer.html(这可能只是解码,不确定)
如果这些不适合您的需要,您可以查看 2000 年关于将 MP3 功能添加到 J2SE 的这篇文章(带有源代码):http://www.javaworld.com/javaworld/jw-11-2000/jw-1103-mp3.html
原生路线
如果您想要“本机”性能,我会查看适用于 Android 的 FFmpeg 或 Lame 端口。
跛脚:http://lame.sourceforge.net/
【讨论】:
嗨,我正在使用 Android 上的 AudioRecorder 录制音频,结果输出格式为 WAV。输出的大小非常大,例如 1 分钟的记录最多需要 2mb 大小。我可以在使用 tritonus jar 转换为 mp3 时减小大小吗?如何在 android 上实现 tritonus。请帮帮我。【参考方案2】:
据我所知,仅使用 SDK 中的工具是无法做到这一点的。根据官方开发者指南,平台中没有 MP3 编码器(Android Supported Media Formats),因此您必须使用 NDK 自行移植编码器,然后编写一些包装器代码以通过 JNI 接收音频样本。
我目前正在为我自己的音乐播放器从 Rockbox 项目中移植一些音频解码器,它可以将音频录制到 MP3 中,所以也许你应该尝试查看它的源并找到编码器库。大多数解码器都有 ARM 优化,可以加快速度,所以我猜一些编码器也有这个添加。
【讨论】:
【参考方案3】:
Mp3 编码器在 android 中不可用。你必须使用 mp3 lame lib 编译 libav,你可以从中找到代码
http://libavandroid.wordpress.com
【讨论】:
以上是关于Android 10 源码MediaRecorder 录像流程:MediaRecorder 配置的主要内容,如果未能解决你的问题,请参考以下文章
Android的PackageManagerService10.0源码解读(AndroidManifest.xml解析)
Android的PackageManagerService10.0源码解读(AndroidManifest.xml解析)
Android的PackageManagerService10.0源码解读(AndroidManifest.xml解析)
Android的PackageManagerService10.0源码解读(AndroidManifest.xml解析)