文章目錄
- 前言
- JAVA new MediaRecorder() 源碼分析
- android_media_MediaRecorder.cpp native_init()
- MediaRecorder.java postEventFromNative
- android_media_MediaRecorder.cpp native_setup()
- MediaRecorder 參數設置
- MediaRecorder.prepare 分析
- MediaRecorder.start 分析
- MediaRecorder.stop 分析
- 結語
本文首發地址 https://blog.csdn.net/CSqingchen/article/details/134634628
最新更新地址 https://gitee.com/chenjim/chenjimblog
前言
通過前文 安卓MediaRecorder(1)錄制音頻的詳細使用,我們已經知道如何使用。
本文主要分析一下 Framework 中相關流程。
下圖是谷歌提供的MediaRecorder狀態關系圖
JAVA new MediaRecorder() 源碼分析
public class MediaRecorder implements AudioRouting,AudioRecordingMonitor,AudioRecordingMonitorClient,MicrophoneDirection {static {// 靜態代碼塊,加載鏈接 liblibmedia_jni.soSystem.loadLibrary("media_jni");native_init();}// 已經廢棄 public MediaRecorder() {// 傳入 APP Contextthis(ActivityThread.currentApplication());}public MediaRecorder(@NonNull Context context) {// 要求 Context 不為空Objects.requireNonNull(context);// 創建EventHandler,主要用于JNI層回調時切換到當前App端線程中Looper looper;if ((looper = Looper.myLooper()) != null) {// 如果當前線程有Looper,就使用當前線程的LoopermEventHandler = new EventHandler(this, looper);} else if ((looper = Looper.getMainLooper()) != null) {// 使用主線程的 Looper,Jni回調信息會在主線程執行 mEventHandler = new EventHandler(this, looper);} else {mEventHandler = null;}// 錄制音頻的聲道數,此處默認 1 (mono即單聲道),2 (stereo即雙聲道立體聲),可以通過 setAudioChannels 修改mChannelCount = 1;// 創建弱引用,并初始化try (ScopedParcelState attributionSourceState = context.getAttributionSource().asScopedParcelState()) {native_setup(new WeakReference<>(this), ActivityThread.currentPackageName(),attributionSourceState.getParcel());}}
android_media_MediaRecorder.cpp native_init()
獲取JAVA層 android.media.MediaRecorder 對象
并將 JAVA 對象的屬性 mNativeContext、mSurface、postEventFromNative 保存在 fields
詳細代碼如下
static void
android_media_MediaRecorder_native_init(JNIEnv *env)
{jclass clazz;clazz = env->FindClass("android/media/MediaRecorder");if (clazz == NULL) {return;}fields.context = env->GetFieldID(clazz, "mNativeContext", "J");if (fields.context == NULL) {return;}fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");if (fields.surface == NULL) {return;}jclass surface = env->FindClass("android/view/Surface");if (surface == NULL) {return;}fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative","(Ljava/lang/Object;IIILjava/lang/Object;)V");if (fields.post_event == NULL) {return;}clazz = env->FindClass("java/util/ArrayList");if (clazz == NULL) {return;}gArrayListFields.add = env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z");gArrayListFields.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
}
MediaRecorder.java postEventFromNative
這個是Jni消息回來的接口,最終會發到 MediaRecorder.EventHandler 的 handleMessage 中
進而可以通過 MediaRecorder.OnInfoListener 、MediaRecorder.OnErrorListener、
AudioRouting.OnRoutingChangedListener 回調到 APP
對應源碼如下
private static void postEventFromNative(Object mediarecorder_ref,int what, int arg1, int arg2, Object obj) {MediaRecorder mr = (MediaRecorder)((WeakReference)mediarecorder_ref).get();if (mr == null) {return;}if (mr.mEventHandler != null) {Message m = mr.mEventHandler.obtainMessage(what, arg1, arg2, obj);mr.mEventHandler.sendMessage(m);}
}// EventHandler 如下
public class MediaRecorder {...private class EventHandler extends Handler {@Overridepublic void handleMessage(Message msg) {switch(msg.what) {case MEDIA_RECORDER_EVENT_ERROR:case MEDIA_RECORDER_TRACK_EVENT_ERROR:if (mOnErrorListener != null)mOnErrorListener.onError(mMediaRecorder, msg.arg1, msg.arg2);return;case MEDIA_RECORDER_EVENT_INFO:case MEDIA_RECORDER_TRACK_EVENT_INFO:if (mOnInfoListener != null)mOnInfoListener.onInfo(mMediaRecorder, msg.arg1, msg.arg2);return;case MEDIA_RECORDER_AUDIO_ROUTING_CHANGED:// 耳機使能的消息 return;}}}
android_media_MediaRecorder.cpp native_setup()
static void
android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,jstring packageName, jobject jAttributionSource)
{// 構建 JNI 對象 MediaRecorder,attributionSource 可以認為是 JNI 中的上下文 Context sp<MediaRecorder> mr = new MediaRecorder(attributionSource);...// 創建 JNI JNIMediaRecorderListener , 其收到的消息最終通過 fields.post_event 回到JAVAsp<JNIMediaRecorderListener> listener = new JNIMediaRecorderListener(env, thiz, weak_this);mr->setListener(listener);...// 傳遞客戶端包名,以進行權限跟蹤 mr->setClientName(clientName);// 將創建的 mr 保存到 fields.context,也就是 Java 層 MediaRecorder 中的 mNativeContext setMediaRecorder(env, thiz, mr);
}
MediaRecorder JNI 構造
// frameworks/av/media/libmedia/mediarecorder.cpp
MediaRecorder::MediaRecorder(const AttributionSourceState &attributionSource): mSurfaceMediaSource(NULL)
{// 通過 binder 獲取 MediaPlayerService const sp<IMediaPlayerService> service(getMediaPlayerService());if (service != NULL) {// 通過 MediaPlayerService 創建 MediaRecorderClient mMediaRecorder = service->createMediaRecorder(attributionSource);}...
}
MediaRecorder 類關系如下
class MediaRecorder : public BnMediaRecorderClient, public virtual IMediaDeathNotifier {...}
class BnMediaRecorderClient: public BnInterface<IMediaRecorderClient> {...}
MediaPlayerService 類關系如下
class MediaPlayerService : public BnMediaPlayerService {...}
class BnMediaPlayerService: public BnInterface<IMediaPlayerService> {...}
MediaPlayerService 中 createMediaRecorder 如下
// frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(const AttributionSourceState& attributionSource)
{...sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, verifiedAttributionSource);...return recorder;
}
MediaRecorderClient 構造如下
// frameworks/av/media/libmediaplayerservice/MediaRecorderClient.cpp
MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service,const AttributionSourceState& attributionSource)
{// 構造StagefrightRecorder,mRecorder = new StagefrightRecorder(attributionSource);mMediaPlayerService = service;
}StagefrightRecorder 類繼承關系如下
struct StagefrightRecorder : public MediaRecorderBase {...}
到此 JAVA 層 new MediaRecord()
相關源碼已經分析完成
MediaRecorder 參數設置
Java層 setAudioSource 、 setOutputFormat 等均調用了 Native 接口,下面以 setAudioSource 為例
// frameworks/base/media/java/android/media/MediaRecorder.java
public native void setAudioSource(@Source int audioSource) throws IllegalStateException;
// frameworks/base/media/jni/android_media_MediaRecorder.cpp
static void android_media_MediaRecorder_setAudioSource(JNIEnv *env, jobject thiz, jint as)
{...// 讀取初始化時保存的mrsp<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.");
}// frameworks/av/media/libmedia/mediarecorder.cpp
status_t MediaRecorder::setVideoSource(int vs)
{...// 上面知道,這里的 mMediaRecorder 是 MediaRecorderClient status_t ret = mMediaRecorder->setVideoSource(vs);return ret;
}// frameworks/av/media/libmediaplayerservice/MediaRecorderClient.cpp
status_t MediaRecorderClient::setVideoSource(int vs)
{...// 上面知道,這里的 mRecorder 是 StagefrightRecorder return mRecorder->setVideoSource((video_source)vs);
}// frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
status_t StagefrightRecorder::setVideoSource(video_source vs) {...// 最終數據保存在 mVideoSource 中 if (vs == VIDEO_SOURCE_DEFAULT) {mVideoSource = VIDEO_SOURCE_CAMERA;} else {mVideoSource = vs;}return OK;
}
同理,其它參數設置多數最終都是保存在 StagefrightRecorder 中,錄制相關的流程很多也是在其中控制
本文首發地址 https://blog.csdn.net/CSqingchen/article/details/134634628
MediaRecorder.prepare 分析
依據前面的分析,最終 prepare 真正實現如下
// frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
status_t StagefrightRecorder::prepareInternal() {...status_t status = OK;// 依據不同的輸出格式,執行不同的 setup switch (mOutputFormat) {case OUTPUT_FORMAT_DEFAULT:case OUTPUT_FORMAT_THREE_GPP:case OUTPUT_FORMAT_MPEG_4:case OUTPUT_FORMAT_WEBM:status = setupMPEG4orWEBMRecording();break;case OUTPUT_FORMAT_AMR_NB:case OUTPUT_FORMAT_AMR_WB:status = setupAMRRecording();break;case OUTPUT_FORMAT_AAC_ADIF:case OUTPUT_FORMAT_AAC_ADTS:status = setupAACRecording();break;case OUTPUT_FORMAT_RTP_AVP:status = setupRTPRecording();break;case OUTPUT_FORMAT_MPEG2TS:status = setupMPEG2TSRecording();break;case OUTPUT_FORMAT_OGG:status = setupOggRecording();break;default:ALOGE("Unsupported output file format: %d", mOutputFormat);status = UNKNOWN_ERROR;break;}return status;
}
這里我們分析一下錄制 MP4 的 prepare 流程 setupMPEG4orWEBMRecording
status_t StagefrightRecorder::setupMPEG4orWEBMRecording() {// 先清理 MediaWriter mWriter.clear();mTotalBitRate = 0;status_t err = OK;sp<MediaWriter> writer;sp<MPEG4Writer> mp4writer;if (mOutputFormat == OUTPUT_FORMAT_WEBM) {writer = new WebmWriter(mOutputFd);} else {// 我們這里分析 MP4 錄制,MPEG4Writer 主要是用來寫入編碼后的音視頻內容 writer = mp4writer = new MPEG4Writer(mOutputFd);}if (mVideoSource < VIDEO_SOURCE_LIST_END) {// 如果編碼器未配置,設置默認的編碼器 setDefaultVideoEncoderIfNecessary();sp<MediaSource> mediaSource;// 設置 視頻源err = setupMediaSource(&mediaSource);if (err != OK) {return err;}sp<MediaCodecSource> encoder;// 編碼參數配置,然后通過 MediaCodecSource::Create 創建編碼器err = setupVideoEncoder(mediaSource, &encoder);if (err != OK) {return err;}// MPEG4Writer 添加編碼通道,一般會有audio、video 兩個,這里是 videowriter->addSource(encoder);mVideoEncoderSource = encoder;// 輸出文件的碼率,是視頻和音頻總碼率之和mTotalBitRate += mVideoBitRate;}// Audio source is added at the end if it exists.// This help make sure that the "recoding" sound is suppressed for// camcorder applications in the recorded files.// disable audio for time lapse recordingconst bool disableAudio = mCaptureFpsEnable && mCaptureFps < mFrameRate;if (!disableAudio && mAudioSource != AUDIO_SOURCE_CNT) {// 通過 createAudioSource() 配置音頻編碼參數,進而通過 MediaCodecSource::Create 創建 編碼器err = setupAudioEncoder(writer);if (err != OK) return err;mTotalBitRate += mAudioBitRate;}...// 監聽 MPEG4Writer 一些參數的回調 writer->setListener(mListener);mWriter = writer;return OK;
}
通過如上,可以看到 MediaRecorder.prepare 主要是進行參數的配置、編碼器的初始化
MediaRecorder.start 分析
依據前面的分析,最終 start 真正實現如下
// frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
status_t StagefrightRecorder::start() {Mutex::Autolock autolock(mLock);if (mOutputFd < 0) {ALOGE("Output file descriptor is invalid");return INVALID_OPERATION;}status_t status = OK;if (mVideoSource != VIDEO_SOURCE_SURFACE) {status = prepareInternal();if (status != OK) {return status;}}switch (mOutputFormat) {case OUTPUT_FORMAT_DEFAULT:case OUTPUT_FORMAT_THREE_GPP:case OUTPUT_FORMAT_MPEG_4:case OUTPUT_FORMAT_WEBM:{sp<MetaData> meta = new MetaData;// 設置 meta 信息setupMPEG4orWEBMMetaData(&meta);// MPEG4Writer 傳入 meta 信息,// startWriterThread 開啟寫入線程 // setupAndStartLooper 啟動 ALooper, mReflector 用于信息傳遞 // 通過 writeFtypBox(MetaData *param) 寫入// startTracks(MetaData *params) 啟動音、視頻Track,參見 MPEG4Writer::Track::startstatus = mWriter->start(meta.get());break;}...}if (status != OK) {// start 異常 mWriter.clear();mWriter = NULL;}if ((status == OK) && (!mStarted)) {mAnalyticsDirty = true;mStarted = true;...// 用于編碼耗電統計 addBatteryData(params);}return status;
}
MediaRecorder.stop 分析
依據前面的分析,最終 stop 真正實現如下
// frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
status_t StagefrightRecorder::stop() {Mutex::Autolock autolock(mLock);status_t err = OK;if (mCaptureFpsEnable && mCameraSourceTimeLapse != NULL) {// 延時錄制,詳細可參見 CameraSourceTimeLapse.cppmCameraSourceTimeLapse->startQuickReadReturns();mCameraSourceTimeLapse = NULL;}int64_t stopTimeUs = systemTime() / 1000;for (const auto &source : { mAudioEncoderSource, mVideoEncoderSource }) {// 設置停止時間戳if (source != nullptr && OK != source->setStopTimeUs(stopTimeUs)) {}}if (mWriter != NULL) {// MPEG4Writer 的停止 ,實際調用其 reset(true, true)// stopWriterThread() 停止寫的線程// Track 停止// release 關閉文件,停止釋放Looper,資源狀態重置err = mWriter->stop();mLastSeqNo = mWriter->getSequenceNum();mWriter.clear();}// 寫入參數相關信息flushAndResetMetrics(true);// 重置參數狀態mDurationRecordedUs = 0;mDurationPausedUs = 0;mNPauses = 0;mTotalPausedDurationUs = 0;mPauseStartTimeUs = 0;mStartedRecordingUs = 0;mGraphicBufferProducer.clear();mPersistentSurface.clear();mAudioEncoderSource.clear();mVideoEncoderSource.clear();......return err;
}
結語
到這里,已經完成了 MediaRecorder 錄制 Framework 源碼的分析。
其它部分流程,可以對照參見 StagefrightRecorder.cpp 中源碼。希望對你有所幫助。
如果你在使用MediaRecorder的過程中遇到了其他問題,歡迎留言討論。
如果你覺得本文還不錯,可以點贊+收藏。
相關文章
安卓MediaRecorder(1)錄制音頻的詳細使用
安卓MediaRecorder(2)錄制源碼分析
安卓MediaRecorder(3)音頻采集編碼寫入詳細源碼分析
安卓MediaRecorder(4)視頻采集編碼寫入詳細源碼分析