{"id":590,"date":"2013-06-20T12:32:25","date_gmt":"2013-06-20T04:32:25","guid":{"rendered":"http:\/\/guoh.ai\/lifelog\/?p=590"},"modified":"2013-11-12T12:29:27","modified_gmt":"2013-11-12T04:29:27","slug":"android-mediarecorder-architecture","status":"publish","type":"post","link":"https:\/\/guoh.ai\/lifelog\/2013\/06\/android-mediarecorder-architecture\/","title":{"rendered":"Android MediaRecorder\u7cfb\u7edf\u7ed3\u6784"},"content":{"rendered":"<div class=\"gfmr-markdown-container\"><div class=\"gfmr-markdown-source\" style=\"display: none;\">&lt;p&gt;\u524d\u9762\u6709\u5206\u6790\u8fc7Camera\u7684\u5b9e\u73b0\uff0c\u73b0\u5728\u6765\u770b\u770bMediaRecorder\u7684\u5b9e\u73b0\uff0c\u8fd9\u91cc\u6211\u4e0d\u4f1a\u592a\u53bb\u5173\u6ce8\u5b83\u7684\u5206\u5c42\u7ed3\u6784\uff0c\u6211\u66f4\u5173\u6ce8\u5b83\u7684\u903b\u8f91\uff01&lt;\/p&gt;\n&lt;p&gt;APP\u5c42&lt;a href=&quot;https:\/\/android.googlesource.com\/platform\/frameworks\/base\/+\/android-4.2.2_r1.2\/media\/java\/android\/media\/MediaRecorder.java&quot; target=&quot;_blank&quot;&gt; \/path\/to\/aosp\/frameworks\/base\/media\/java\/android\/media\/MediaRecorder.java&lt;\/a&gt;&lt;br \/&gt;\nJNI\u5c42&lt;a href=&quot;https:\/\/android.googlesource.com\/platform\/frameworks\/base\/+\/android-4.2.2_r1.2\/media\/jni\/android_media_MediaRecorder.cpp&quot; target=&quot;_blank&quot;&gt; \/path\/to\/aosp\/frameworks\/base\/media\/jni\/android_media_MediaRecorder.cpp&lt;\/a&gt;&lt;br \/&gt;\n\u8c03\u7528NATIVE\u5c42\u7684MediaRecorder(\u8fd9\u91cc\u662fBnMediaRecorderClient)&lt;br \/&gt;\nheader&lt;a href=&quot;https:\/\/android.googlesource.com\/platform\/frameworks\/av\/+\/android-4.2.2_r1.2\/include\/media\/mediarecorder.h&quot; target=&quot;_blank&quot;&gt; \/path\/to\/aosp\/frameworks\/av\/include\/media\/mediarecorder.h&lt;\/a&gt;&lt;br \/&gt;\nimplementation &lt;a href=&quot;https:\/\/android.googlesource.com\/platform\/frameworks\/av\/+\/android-4.2.2_r1.2\/media\/libmedia\/mediarecorder.cpp&quot; target=&quot;_blank&quot;&gt;\/path\/to\/aosp\/frameworks\/av\/media\/libmedia\/mediarecorder.cpp&lt;\/a&gt;&lt;\/p&gt;\n&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;MediaRecorder::MediaRecorder() : mSurfaceMediaSource(NULL)\r\n{\r\n    ALOGV(&quot;constructor&quot;);\r\n\r\n    const sp&lt;IMediaPlayerService&gt;&amp; service(getMediaPlayerService());\r\n    if (service != NULL) {\r\n        mMediaRecorder = service-&gt;createMediaRecorder(getpid());\r\n    }\r\n    if (mMediaRecorder != NULL) {\r\n        mCurrentState = MEDIA_RECORDER_IDLE;\r\n    }\r\n\r\n    doCleanUp();\r\n}&lt;\/pre&gt;\n&lt;p&gt;getMediaPlayerService()\u8fd9\u4e2a\u65b9\u6cd5\u4f4d\u4e8e&lt;a href=&quot;https:\/\/android.googlesource.com\/platform\/frameworks\/av\/+\/android-4.2.2_r1.2\/include\/media\/IMediaMetadataRetriever.h&quot; target=&quot;_blank&quot;&gt;\/path\/to\/aosp\/frameworks\/av\/include\/media\/IMediaDeathNotifier.h&lt;\/a&gt;&lt;\/p&gt;\n&lt;p&gt;\u83b7\u53d6\u5230MediaPlayerService(\u8fd9\u4e2a\u662fBpMediaPlayerService)\u4e4b\u540e&lt;br \/&gt;\n\u8c03\u7528IMediaPlayerService\u5f53\u4e2d\u7684&lt;\/p&gt;\n&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;sp&lt;IMediaRecorder&gt; MediaPlayerService::createMediaRecorder(pid_t pid)\r\n{\r\n    sp&lt;MediaRecorderClient&gt; recorder = new MediaRecorderClient(this, pid);\r\n    wp&lt;MediaRecorderClient&gt; w = recorder;\r\n    Mutex::Autolock lock(mLock);\r\n    mMediaRecorderClients.add(w);\r\n    ALOGV(&quot;Create new media recorder client from pid %d&quot;, pid);\r\n    return recorder;\r\n}&lt;\/pre&gt;\n&lt;p&gt;\u521b\u5efaMediaRecorderClient(\u8fd9\u91cc\u662fBnMediaRecorder)&lt;\/p&gt;\n&lt;p&gt;\u4f46\u662f\u901a\u8fc7binder\u62ff\u5230\u7684\u662fBpMediaRecorder&lt;br \/&gt;\n\u56e0\u4e3a\u6709\u5982\u4e0b\u7684interface_cast\u8fc7\u7a0b&lt;\/p&gt;\n&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;virtual sp&lt;IMediaRecorder&gt; createMediaRecorder(pid_t pid)\r\n{\r\n    Parcel data, reply;\r\n    data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());\r\n    data.writeInt32(pid);\r\n    remote()-&gt;transact(CREATE_MEDIA_RECORDER, data, &amp;reply);\r\n    return interface_cast&lt;IMediaRecorder&gt;(reply.readStrongBinder());\r\n}&lt;\/pre&gt;\n&lt;p&gt;\u800cMediaRecorderClient\u5f53\u4e2d\u53c8\u4f1a\u521b\u5efaStagefrightRecorder(MediaRecorderBase)\uff0c\u5b83\u4f4d\u4e8e&lt;br \/&gt;\n&lt;a href=&quot;https:\/\/android.googlesource.com\/platform\/frameworks\/av\/+\/android-4.2.2_r1.2\/media\/libmediaplayerservice\/StagefrightRecorder.cpp&quot; target=&quot;_blank&quot;&gt;\/path\/to\/aosp\/frameworks\/av\/media\/libmediaplayerservice\/StagefrightRecorder.cpp&lt;\/a&gt;&lt;\/p&gt;\n&lt;p&gt;\u76ee\u524d\u6211\u4eec\u53ef\u4ee5\u8ba4\u4e3a\u5728APP\/JNI\/NATIVE\u8fd9\u8fb9\u662f\u5728\u4e00\u4e2a\u8fdb\u7a0b\u5f53\u4e2d\uff0c\u5728MediaPlayerService\u5f53\u4e2d\u7684MediaRecorderClient\/StagefrightRecorder\u662f\u5728\u53e6\u5916\u4e00\u4e2a\u8fdb\u7a0b\u5f53\u4e2d\uff0c\u4ed6\u4eec\u4e4b\u95f4\u901a\u8fc7binder\u901a\u4fe1\uff0c\u800c\u4e14Bp\u548cBn\u6211\u4eec\u4e5f\u90fd\u6709\u62ff\u5230\uff0c\u540e\u9762\u6211\u4eec\u5c06\u4e0d\u518d\u4ed4\u7ec6\u533a\u5206Bp\u548cBn\u3002&lt;\/p&gt;\n&lt;blockquote&gt;&lt;p&gt;\u5ba2\u6237\u7aef\u8fd9\u8fb9&lt;br \/&gt;\nBnMediaRecorderClient&lt;br \/&gt;\nBpMediaRecorder&lt;br \/&gt;\nBpMediaPlayerService&lt;\/p&gt;\n&lt;p&gt;\u670d\u52a1\u7aef\u8fd9\u8fb9&lt;br \/&gt;\nBpMediaRecorderClient(\u5982\u679c\u9700\u8981\u901a\u77e5\u5ba2\u6237\u7aef\u7684\u8bdd\uff0c\u5b83\u53ef\u4ee5\u83b7\u5f97\u8fd9\u4e2aBp)&lt;br \/&gt;\nBnMediaRecorder&lt;br \/&gt;\nBnMediaPlayerService&lt;\/p&gt;&lt;\/blockquote&gt;\n&lt;p&gt;\u8fd9\u6709\u5f20\u56fe(\u70b9\u8fc7\u53bb\u770b\u539f\u59cb\u5927\u56fe)&lt;br \/&gt;\n&lt;a href=&quot;https:\/\/www.flickr.com\/photos\/46848122@N08\/9410173938\/&quot; title=&quot;Android MediaRecorder Diagram by DeliberateKids, on Flickr&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https:\/\/farm6.staticflickr.com\/5474\/9410173938_9d2028ea46_o.png&quot; width=&quot;3942&quot; height=&quot;2652&quot; alt=&quot;Android MediaRecorder Diagram&quot;&gt;&lt;\/a&gt;&lt;\/p&gt;\n&lt;p&gt;\u6211\u4eec\u4ee5\u5f00\u59cb\u5f55\u5f71\u4e3a\u4f8b\u5b50\uff0c\u6bd4\u5982start()&lt;\/p&gt;\n&lt;p&gt;\u5728\u8fd9\u91cc\u5c31\u5175\u5206\u4e24\u8def\uff0c\u4e00\u4e2aCameraSource\uff0c\u4e00\u4e2aMPEG4Writer(sp&lt;MediaWriter&gt; mWriter)&lt;br \/&gt;\n\u8fd9\u4e24\u4e2aclass\u90fd\u4f4d\u4e8e&lt;a href=&quot;https:\/\/android.googlesource.com\/platform\/frameworks\/av\/+\/android-4.2.2_r1.2\/media\/libstagefright\/&quot; target=&quot;_blank&quot;&gt;\/path\/to\/aosp\/frameworks\/av\/media\/libstagefright\/&lt;\/a&gt;\u5f53\u4e2d&lt;\/p&gt;\n&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;status_t StagefrightRecorder::startMPEG4Recording() {\r\n    int32_t totalBitRate;\r\n    status_t err = setupMPEG4Recording(\r\n            mOutputFd, mVideoWidth, mVideoHeight,\r\n            mVideoBitRate, &amp;totalBitRate, &amp;mWriter);\r\n    if (err != OK) {\r\n        return err;\r\n    }\r\n\r\n    int64_t startTimeUs = systemTime() \/ 1000;\r\n    sp&lt;MetaData&gt; meta = new MetaData;\r\n    setupMPEG4MetaData(startTimeUs, totalBitRate, &amp;meta);\r\n\r\n    err = mWriter-&gt;start(meta.get());\r\n    if (err != OK) {\r\n        return err;\r\n    }\r\n\r\n    return OK;\r\n}&lt;\/pre&gt;\n&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;status_t StagefrightRecorder::setupMPEG4Recording(\r\n        int outputFd,\r\n        int32_t videoWidth, int32_t videoHeight,\r\n        int32_t videoBitRate,\r\n        int32_t *totalBitRate,\r\n        sp&lt;MediaWriter&gt; *mediaWriter) {\r\n    mediaWriter-&gt;clear();\r\n    *totalBitRate = 0;\r\n    status_t err = OK;\r\n    sp&lt;MediaWriter&gt; writer = new MPEG4Writer(outputFd);\r\n\r\n    if (mVideoSource &lt; VIDEO_SOURCE_LIST_END) {\r\n\r\n        sp&lt;MediaSource&gt; mediaSource;\r\n        err = setupMediaSource(&amp;mediaSource); \/\/ very important\r\n        if (err != OK) {\r\n            return err;\r\n        }\r\n\r\n        sp&lt;MediaSource&gt; encoder;\r\n        err = setupVideoEncoder(mediaSource, videoBitRate, &amp;encoder); \/\/ very important\r\n        if (err != OK) {\r\n            return err;\r\n        }\r\n\r\n        writer-&gt;addSource(encoder);\r\n        *totalBitRate += videoBitRate;\r\n    }\r\n\r\n    \/\/ Audio source is added at the end if it exists.\r\n    \/\/ This help make sure that the &quot;recoding&quot; sound is suppressed for\r\n    \/\/ camcorder applications in the recorded files.\r\n    if (!mCaptureTimeLapse &amp;&amp; (mAudioSource != AUDIO_SOURCE_CNT)) {\r\n        err = setupAudioEncoder(writer); \/\/ very important\r\n        if (err != OK) return err;\r\n        *totalBitRate += mAudioBitRate;\r\n    }\r\n\r\n    ...\r\n\r\n    writer-&gt;setListener(mListener);\r\n    *mediaWriter = writer;\r\n    return OK;\r\n}&lt;\/pre&gt;\n&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;\r\n\/\/ Set up the appropriate MediaSource depending on the chosen option\r\nstatus_t StagefrightRecorder::setupMediaSource(\r\n                      sp&lt;MediaSource&gt; *mediaSource) {\r\n    if (mVideoSource == VIDEO_SOURCE_DEFAULT\r\n            || mVideoSource == VIDEO_SOURCE_CAMERA) {\r\n        sp&lt;CameraSource&gt; cameraSource;\r\n        status_t err = setupCameraSource(&amp;cameraSource);\r\n        if (err != OK) {\r\n            return err;\r\n        }\r\n        *mediaSource = cameraSource;\r\n    } else if (mVideoSource == VIDEO_SOURCE_GRALLOC_BUFFER) {\r\n        \/\/ If using GRAlloc buffers, setup surfacemediasource.\r\n        \/\/ Later a handle to that will be passed\r\n        \/\/ to the client side when queried\r\n        status_t err = setupSurfaceMediaSource();\r\n        if (err != OK) {\r\n            return err;\r\n        }\r\n        *mediaSource = mSurfaceMediaSource;\r\n    } else {\r\n        return INVALID_OPERATION;\r\n    }\r\n    return OK;\r\n}&lt;\/pre&gt;\n&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;status_t StagefrightRecorder::setupCameraSource(\r\n        sp&lt;CameraSource&gt; *cameraSource) {\r\n    status_t err = OK;\r\n    if ((err = checkVideoEncoderCapabilities()) != OK) {\r\n        return err;\r\n    }\r\n    Size videoSize;\r\n    videoSize.width = mVideoWidth;\r\n    videoSize.height = mVideoHeight;\r\n    if (mCaptureTimeLapse) {\r\n        if (mTimeBetweenTimeLapseFrameCaptureUs &lt; 0) {\r\n            ALOGE(&quot;Invalid mTimeBetweenTimeLapseFrameCaptureUs value: %lld&quot;,\r\n                mTimeBetweenTimeLapseFrameCaptureUs);\r\n            return BAD_VALUE;\r\n        }\r\n\r\n        mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera(\r\n                mCamera, mCameraProxy, mCameraId,\r\n                videoSize, mFrameRate, mPreviewSurface,\r\n                mTimeBetweenTimeLapseFrameCaptureUs);\r\n        *cameraSource = mCameraSourceTimeLapse;\r\n    } else {\r\n        *cameraSource = CameraSource::CreateFromCamera(\r\n                mCamera, mCameraProxy, mCameraId, videoSize, mFrameRate,\r\n                mPreviewSurface, true \/*storeMetaDataInVideoBuffers*\/);\r\n    }\r\n    mCamera.clear();\r\n    mCameraProxy.clear();\r\n    if (*cameraSource == NULL) {\r\n        return UNKNOWN_ERROR;\r\n    }\r\n\r\n    if ((*cameraSource)-&gt;initCheck() != OK) {\r\n        (*cameraSource).clear();\r\n        *cameraSource = NULL;\r\n        return NO_INIT;\r\n    }\r\n\r\n    \/\/ When frame rate is not set, the actual frame rate will be set to\r\n    \/\/ the current frame rate being used.\r\n    if (mFrameRate == -1) {\r\n        int32_t frameRate = 0;\r\n        CHECK ((*cameraSource)-&gt;getFormat()-&gt;findInt32(\r\n                    kKeyFrameRate, &amp;frameRate));\r\n        ALOGI(&quot;Frame rate is not explicitly set. Use the current frame &quot;\r\n             &quot;rate (%d fps)&quot;, frameRate);\r\n        mFrameRate = frameRate;\r\n    }\r\n\r\n    CHECK(mFrameRate != -1);\r\n\r\n    mIsMetaDataStoredInVideoBuffers =\r\n        (*cameraSource)-&gt;isMetaDataStoredInVideoBuffers();\r\n\r\n    return OK;\r\n}&lt;\/pre&gt;\n&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;status_t StagefrightRecorder::setupVideoEncoder(\r\n        sp&lt;MediaSource&gt; cameraSource,\r\n        int32_t videoBitRate,\r\n        sp&lt;MediaSource&gt; *source) {\r\n    source-&gt;clear();\r\n\r\n    sp&lt;MetaData&gt; enc_meta = new MetaData;\r\n    enc_meta-&gt;setInt32(kKeyBitRate, videoBitRate);\r\n    enc_meta-&gt;setInt32(kKeyFrameRate, mFrameRate);\r\n\r\n    switch (mVideoEncoder) {\r\n        case VIDEO_ENCODER_H263:\r\n            enc_meta-&gt;setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);\r\n            break;\r\n\r\n        case VIDEO_ENCODER_MPEG_4_SP:\r\n            enc_meta-&gt;setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);\r\n            break;\r\n\r\n        case VIDEO_ENCODER_H264:\r\n            enc_meta-&gt;setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);\r\n            break;\r\n\r\n        default:\r\n            CHECK(!&quot;Should not be here, unsupported video encoding.&quot;);\r\n            break;\r\n    }\r\n\r\n    sp&lt;MetaData&gt; meta = cameraSource-&gt;getFormat();\r\n\r\n    int32_t width, height, stride, sliceHeight, colorFormat;\r\n    CHECK(meta-&gt;findInt32(kKeyWidth, &amp;width));\r\n    CHECK(meta-&gt;findInt32(kKeyHeight, &amp;height));\r\n    CHECK(meta-&gt;findInt32(kKeyStride, &amp;stride));\r\n    CHECK(meta-&gt;findInt32(kKeySliceHeight, &amp;sliceHeight));\r\n    CHECK(meta-&gt;findInt32(kKeyColorFormat, &amp;colorFormat));\r\n\r\n    enc_meta-&gt;setInt32(kKeyWidth, width);\r\n    enc_meta-&gt;setInt32(kKeyHeight, height);\r\n    enc_meta-&gt;setInt32(kKeyIFramesInterval, mIFramesIntervalSec);\r\n    enc_meta-&gt;setInt32(kKeyStride, stride);\r\n    enc_meta-&gt;setInt32(kKeySliceHeight, sliceHeight);\r\n    enc_meta-&gt;setInt32(kKeyColorFormat, colorFormat);\r\n    if (mVideoTimeScale &gt; 0) {\r\n        enc_meta-&gt;setInt32(kKeyTimeScale, mVideoTimeScale);\r\n    }\r\n    if (mVideoEncoderProfile != -1) {\r\n        enc_meta-&gt;setInt32(kKeyVideoProfile, mVideoEncoderProfile);\r\n    }\r\n    if (mVideoEncoderLevel != -1) {\r\n        enc_meta-&gt;setInt32(kKeyVideoLevel, mVideoEncoderLevel);\r\n    }\r\n\r\n    OMXClient client;\r\n    CHECK_EQ(client.connect(), (status_t)OK);\r\n\r\n    uint32_t encoder_flags = 0;\r\n    if (mIsMetaDataStoredInVideoBuffers) {\r\n        encoder_flags |= OMXCodec::kStoreMetaDataInVideoBuffers;\r\n    }\r\n\r\n    \/\/ Do not wait for all the input buffers to become available.\r\n    \/\/ This give timelapse video recording faster response in\r\n    \/\/ receiving output from video encoder component.\r\n    if (mCaptureTimeLapse) {\r\n        encoder_flags |= OMXCodec::kOnlySubmitOneInputBufferAtOneTime;\r\n    }\r\n\r\n    sp&lt;MediaSource&gt; encoder = OMXCodec::Create(\r\n            client.interface(), enc_meta,\r\n            true \/* createEncoder *\/, cameraSource,\r\n            NULL, encoder_flags);\r\n    if (encoder == NULL) {\r\n        ALOGW(&quot;Failed to create the encoder&quot;);\r\n        \/\/ When the encoder fails to be created, we need\r\n        \/\/ release the camera source due to the camera&#039;s lock\r\n        \/\/ and unlock mechanism.\r\n        cameraSource-&gt;stop();\r\n        return UNKNOWN_ERROR;\r\n    }\r\n\r\n    *source = encoder;\r\n\r\n    return OK;\r\n}&lt;\/pre&gt;\n&lt;p&gt;\u8fd9\u91cc\u548cOMXCodec\u5173\u8054\u8d77\u6765&lt;br \/&gt;\n\u6709\u4e00\u4e2a\u53ebmedia_codecs.xml\u7684\u914d\u7f6e\u6587\u4ef6\u6765\u8868\u660e\u8bbe\u5907\u652f\u6301\u54ea\u4e9bcodec&lt;\/p&gt;\n&lt;p&gt;\u6211\u4eec\u5f55\u5236MPEG 4\u7684\u65f6\u5019\u8fd8\u4f1a\u6709\u58f0\u97f3\uff0c\u6240\u4ee5\u540e\u9762\u8fd8\u6709\u4e2asetupAudioEncoder\uff0c\u5177\u4f53\u7684\u65b9\u6cd5\u5c31\u4e0d\u5c55\u5f00\u4e86\uff0c\u603b\u4e4b\u5c31\u662f\u628a\u58f0\u97f3\u4e5f\u4f5c\u4e3a\u4e00\u4e2aTrack\u52a0\u5165\u5230MPEG4Writer\u5f53\u4e2d\u53bb\u3002&lt;br \/&gt;\n\u8fd9\u91cc\u63d2\u4e2a\u9898\u5916\u8bdd\uff0cGoogle\u8bf4\u628asetupAudioEncoder\u653e\u5230\u540e\u9762\u662f\u4e3a\u4e86\u907f\u514d\u5f00\u59cb\u5f55\u5f71\u7684\u90a3\u4e00\u4e2a\u63d0\u793a\u58f0\u97f3\u4e5f\u88ab\u5f55\u5236\u8fdb\u53bb\uff0c\u4f46\u662f\u5b9e\u9645\u53d1\u73b0\u5b83\u8fd9\u6837\u505a\u8fd8\u662f\u4f1a\u6709bug\uff0c\u5728\u4e00\u4e9b\u8bbe\u5907\u4e0a\u8fd8\u662f\u4f1a\u628a\u90a3\u58f0\u5f55\u5236\u8fdb\u53bb\uff0c\u8fd9\u4e2a\u9047\u5230\u7684\u90fd\u662f\u9760APP\u81ea\u5df1\u6765\u64ad\u653e\u58f0\u97f3\u6765\u7ed5\u8fc7\u8fd9\u4e2a\u95ee\u9898\u7684\u3002&lt;\/p&gt;\n&lt;p&gt;\u53e6\u5916MPEG4Writer\u5f53\u4e2d\u6709\u4e2a&lt;br \/&gt;\nstart(MetaData*)&lt;br \/&gt;\n\u542f\u52a8\u4e24\u4e2a\u65b9\u6cd5&lt;br \/&gt;\n a) startWriterThread&lt;\/p&gt;\n&lt;p&gt;    \u542f\u52a8\u4e00\u4e2athread\u53bb\u5199&lt;\/p&gt;\n&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;    void MPEG4Writer::threadFunc() {\r\n        ALOGV(&quot;threadFunc&quot;);\r\n\r\n        prctl(PR_SET_NAME, (unsigned long)&quot;MPEG4Writer&quot;, 0, 0, 0);\r\n\r\n        Mutex::Autolock autoLock(mLock);\r\n        while (!mDone) {\r\n            Chunk chunk;\r\n            bool chunkFound = false;\r\n\r\n            while (!mDone &amp;&amp; !(chunkFound = findChunkToWrite(&amp;chunk))) {\r\n                mChunkReadyCondition.wait(mLock);\r\n            }\r\n\r\n            \/\/ Actual write without holding the lock in order to\r\n            \/\/ reduce the blocking time for media track threads.\r\n            if (chunkFound) {\r\n                mLock.unlock();\r\n                writeChunkToFile(&amp;chunk);\r\n                mLock.lock();\r\n            }\r\n        }\r\n\r\n        writeAllChunks();\r\n    }&lt;\/pre&gt;\n&lt;p&gt; b) startTracks&lt;\/p&gt;\n&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;    status_t MPEG4Writer::startTracks(MetaData *params) {\r\n        for (List&lt;Track *&gt;::iterator it = mTracks.begin();\r\n             it != mTracks.end(); ++it) {\r\n            status_t err = (*it)-&gt;start(params);\r\n\r\n            if (err != OK) {\r\n                for (List&lt;Track *&gt;::iterator it2 = mTracks.begin();\r\n                     it2 != it; ++it2) {\r\n                    (*it2)-&gt;stop();\r\n                }\r\n\r\n                return err;\r\n            }\r\n        }\r\n        return OK;\r\n    }&lt;\/pre&gt;\n&lt;p&gt;    \u7136\u540e\u8c03\u7528\u6bcf\u4e2aTrack\u7684start\u65b9\u6cd5&lt;\/p&gt;\n&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;    status_t MPEG4Writer::Track::start(MetaData *params) {\r\n        ...\r\n\r\n        initTrackingProgressStatus(params);\r\n\r\n        ...\r\n\r\n        status_t err = mSource-&gt;start(meta.get()); \/\/ \u8fd9\u91cc\u4f1a\u53bb\u6267\u884cCameraSource(start)\uff0c\u8fd9\u4e24\u4e2a\u662f\u76f8\u4e92\u5173\u8054\u7684\r\n\r\n        ...\r\n\r\n        pthread_create(&amp;mThread, &amp;attr, ThreadWrapper, this);\r\n        return OK;\r\n    }\r\n\r\n    void *MPEG4Writer::Track::ThreadWrapper(void *me) {\r\n        Track *track = static_cast&lt;Track *&gt;(me);\r\n\r\n        status_t err = track-&gt;threadEntry();\r\n        return (void *) err;\r\n    }&lt;\/pre&gt;\n&lt;p&gt;    \u901a\u8fc7status_t MPEG4Writer::Track::threadEntry()&lt;br \/&gt;\n    \u662f\u65b0\u542f\u52a8\u53e6\u5916\u4e00\u4e2athread\uff0c\u5b83\u91cc\u9762\u4f1a\u901a\u8fc7\u4e00\u4e2a\u5faa\u73af\u6765\u4e0d\u65ad\u8bfb\u53d6CameraSource(read)\u91cc\u9762\u7684\u6570\u636e\uff0cCameraSource\u91cc\u9762\u7684\u6570\u636e\u5f53\u7136\u662f\u4ecedriver\u8fd4\u56de\u8fc7\u6765\u7684(\u53ef\u4ee5\u53c2\u89c1CameraSourceListener\uff0cCameraSource\u7528\u4e00\u4e2a\u53eb\u505amFrameReceived\u7684List\u4e13\u95e8\u5b58\u653e\u4ecedriver\u8fc7\u6765\u7684\u6570\u636e\uff0c\u5982\u679c\u6536\u5230\u6570\u636e\u4f1a\u8c03\u7528mFrameAvailableCondition.signal\uff0c\u82e5\u8fd8\u6ca1\u6709\u5f00\u59cb\u5f55\u5f71\uff0c\u8fd9\u4e2a\u65f6\u5019\u6536\u5230\u7684\u6570\u636e\u662f\u88ab\u4e22\u5f03\u7684\uff0c\u5f53\u7136MediaWriter\u5148\u542f\u52a8\u7684\u662fCameraSource\u7684start\u65b9\u6cd5\uff0c\u518d\u542f\u52a8\u5199Track)\uff0c\u7136\u540e\u5199\u5230\u6587\u4ef6\u5f53\u4e2d\u3002&lt;br \/&gt;\n\u6ce8\u610f\uff1a\u51c6\u786e\u6765\u8bf4\u8fd9\u91ccMPEG4Writer\u8bfb\u53d6\u7684\u662fOMXCodec\u91cc\u7684\u6570\u636e\uff0c\u56e0\u4e3a\u6570\u636e\u5148\u5230CameraSource\uff0ccodec\u8d1f\u8d23\u7f16\u7801\u4e4b\u540e\uff0cMPEG4Writer\u624d\u8d1f\u8d23\u5199\u5230\u6587\u4ef6\u5f53\u4e2d\uff01\u5173\u4e8e\u6570\u636e\u5728CameraSource\/OMXCodec\/MPEG4Writer\u4e4b\u95f4\u662f\u600e\u4e48\u4f20\u9012\u7684\uff0c\u53ef\u4ee5\u53c2\u89c1&lt;a href=&quot;http:\/\/guoh.ai\/lifelog\/2013\/06\/interaction-between-stagefright-and-codec\/&quot; target=&quot;_blank&quot;&gt;http:\/\/guoh.ai\/lifelog\/2013\/06\/interaction-between-stagefright-and-codec\/&lt;\/a&gt;\u5f53\u4e2d\u8bb2Buffer\u7684\u4f20\u8f93\u8fc7\u7a0b\u3002&lt;\/p&gt;\n&lt;p&gt;\u56de\u5934\u518d\u6765\u770b\uff0cStagefright\u505a\u4e86\u4ec0\u4e48\u4e8b\u60c5\uff1f\u6211\u66f4\u89c9\u5f97\u5b83\u53ea\u662f\u4e00\u4e2a\u7c98\u5408\u5242(glue)\u7684\u7528\u5904\uff0c\u5b83\u5de5\u4f5c\u5728MediaPlayerService\u8fd9\u4e00\u5c42\uff0c\u628aMediaSource\uff0cMediaWriter\uff0cCodec\u4ee5\u53ca\u4e0a\u5c42\u7684MediaRecorder\u7ed1\u5b9a\u5728\u4e00\u8d77\uff0c\u8fd9\u5e94\u8be5\u5c31\u662f\u5b83\u6700\u5927\u7684\u4f5c\u7528\uff0cGoogle\u7528\u5b83\u6765\u66ff\u6362Opencore\u4e5f\u662f\u7b26\u5408\u5176\u4e00\u8d2f\u7684\u5de5\u7a0b\u6d3e\u4f5c\u98ce(\u76f8\u6bd4\u590d\u6742\u7684\u5b66\u672f\u6d3e\u800c\u8a00\uff0c\u867d\u7136Google\u5f88\u591a\u4e1c\u897f\u4e5f\u5f88\u590d\u6742\uff0c\u4f46\u662f\u5b83\u4e00\u822c\u90fd\u662f\u4ee5\u5c3d\u91cf\u7b80\u5355\u7684\u65b9\u5f0f\u6765\u89e3\u51b3\u95ee\u9898)\u3002&lt;br \/&gt;\n\u8ba9\u5927\u5bb6\u89c9\u5f97\u6709\u70b9\u4e0d\u4e60\u60ef\u7684\u662f\uff0c\u5b83\u628aMediaRecorder\u653e\u5728MediaPlayerService\u5f53\u4e2d\uff0c\u8fd9\u4e24\u4e2a\u770b\u8d77\u6765\u662f\u5bf9\u7acb\u7684\u4e8b\u60c5\uff0c\u6216\u8005\u67d0\u4e00\u5929\u5b83\u4eec\u4f1a\u6539\u540d\u5b57\uff0c\u6216\u8005\u662f\u4e24\u8005\u5206\u5f00\uff0c\u4e0d\u77e5\u9053\uff5e\uff5e&lt;\/p&gt;\n&lt;p&gt;\u5f53\u7136\u8fd9\u53ea\u662f\u4e2a\u7b80\u5355\u7684\u5927\u4f53\u4ecb\u7ecd\uff0cCodec\u76f8\u5173\u7684\u540e\u9762\u4e89\u53d6\u4e13\u95e8\u6765\u5206\u6790\u4e00\u4e0b\uff01&lt;\/p&gt;\n&lt;p&gt;\u6709\u4e9b\u7ec6\u8282\u7684\u4e1c\u897f\u5728\u8fd9\u91cc\u6ca1\u6709\u5217\u51fa\uff0c\u9700\u8981\u7684\u8bdd\u4f1a\u628a\u4e00\u4e9b\u6ce8\u610f\u70b9\u5217\u51fa\u6765\uff1a&lt;\/p&gt;\n&lt;p&gt;1. \u65f6\u5149\u6d41\u901d\u5f55\u5f71&lt;br \/&gt;\nCameraSource\u5bf9\u5e94\u7684\u5c31\u662fCameraSourceTimeLapse&lt;\/p&gt;\n&lt;p&gt;\u5177\u4f53\u505a\u6cd5\u5c31\u662f\u5728&lt;br \/&gt;\ndataCallbackTimestamp&lt;br \/&gt;\n\u5f53\u4e2d\u6709skipCurrentFrame&lt;\/p&gt;\n&lt;p&gt;\u5f53\u7136\u5b83\u662f\u7528\u4e9b\u53d8\u91cf\u6765\u8bb0\u5f55\u548c\u8ba1\u7b97&lt;br \/&gt;\nmTimeBetweenTimeLapseVideoFramesUs(1E6\/videoFrameRate) \/\/ \u4e24\u4e2aframe\u4e4b\u95f4\u7684\u95f4\u9694\u65f6\u95f4&lt;br \/&gt;\n\u8bb0\u5f55\u4e0a\u4e00\u4e2aframe\u7684(mLastTimeLapseFrameRealTimestampUs) \/\/ \u4e0a\u4e00\u4e2aframe\u53d1\u751f\u7684\u65f6\u95f4&lt;br \/&gt;\n\u7136\u540e\u901a\u8fc7frame rate\u8ba1\u7b97\u51fa\u4e24\u4e2aframe\u4e4b\u95f4\u7684\u76f8\u8ddd\u79bb\u65f6\u95f4\uff0c\u4e2d\u95f4\u7684\u90fd\u900f\u8fc7releaseOneRecordingFrame\u6765drop\u6389&lt;br \/&gt;\n\u4e5f\u5c31\u662f\u8bf4driver\u8fd4\u56de\u7684\u4e1c\u897f\u90fd\u4e0d\u53d8\uff0c\u53ea\u662f\u5728SW\u8fd9\u5c42\u6211\u4eec\u81ea\u5df1\u6765\u5904\u7406\u6389&lt;\/p&gt;\n&lt;p&gt;\u5173\u4e8eTime-lapse\u76f8\u5173\u7684\u53ef\u4ee5\u53c2\u9605&lt;br \/&gt;\n&lt;a href=&quot;https:\/\/en.wikipedia.org\/wiki\/Time-lapse_photography&quot; target=&quot;_blank&quot;&gt;https:\/\/en.wikipedia.org\/wiki\/Time-lapse_photography&lt;\/a&gt;&lt;\/p&gt;\n&lt;p&gt;2. \u5f55\u5f71\u5f53\u4e2d\u9700\u8981\u7528\u5230Camera\u7684\u8bdd\u662f\u901a\u8fc7ICameraRecordingProxy\uff0c\u5373Camera\u5f53\u4e2d\u7684RecordingProxy(\u8fd9\u662f\u4e00\u4e2aBnCameraRecordingProxy)&lt;br \/&gt;\n\u5f53\u900f\u8fc7binder\uff0c\u5c06ICameraRecordingProxy\u4f20\u5230\u670d\u52a1\u7aef\u8fdb\u7a0b\u4e4b\u540e\uff0c\u5b83\u5c31\u53d8\u6210\u4e86Bp\uff0c\u5982\u4e0b\uff1a&lt;\/p&gt;\n&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;case SET_CAMERA: {\r\n    ALOGV(&quot;SET_CAMERA&quot;);\r\n    CHECK_INTERFACE(IMediaRecorder, data, reply);\r\n    sp&lt;ICamera&gt; camera = interface_cast&lt;ICamera&gt;(data.readStrongBinder());\r\n    sp&lt;ICameraRecordingProxy&gt; proxy =\r\n        interface_cast&lt;ICameraRecordingProxy&gt;(data.readStrongBinder());\r\n    reply-&gt;writeInt32(setCamera(camera, proxy));\r\n    return NO_ERROR;\r\n} break;&lt;\/pre&gt;\n&lt;p&gt;\u5728CameraSource\u5f53\u4e2d\u4f1a\u8fd9\u6837\u53bb\u4f7f\u7528&lt;\/p&gt;\n&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;\/\/ We get the proxy from Camera, not ICamera. We need to get the proxy\r\n\/\/ to the remote Camera owned by the application. Here mCamera is a\r\n\/\/ local Camera object created by us. We cannot use the proxy from\r\n\/\/ mCamera here.\r\nmCamera = Camera::create(camera);\r\nif (mCamera == 0) return -EBUSY;\r\nmCameraRecordingProxy = proxy;\r\nmCameraFlags |= FLAGS_HOT_CAMERA;&lt;\/pre&gt;\n&lt;p&gt;\u7591\u95ee\u70b9\uff1a&lt;\/p&gt;\n&lt;p&gt;CameraSource\u5f53\u4e2d\u8fd9\u4e2a&lt;br \/&gt;\n&lt;strong&gt;List&lt;sp&lt;IMemory&gt; &gt; mFramesBeingEncoded;&lt;\/strong&gt;&lt;br \/&gt;\n\u6709\u4ec0\u4e48\u7528\uff1f&lt;br \/&gt;\n\u6bcf\u7f16\u7801\u5b8c\u4e00\u4e2aframe\uff0cCameraSource\u5c31\u4f1a\u5c06\u5176\u4fdd\u5b58\u8d77\u6765\uff0cBuffer\u88abrelease\u7684\u65f6\u5019\uff0c\u4f1a\u53cd\u8fc7\u6765release\u6389\u8fd9\u4e9bframe(s)\uff0c\u8fd9\u79cd\u505a\u6cd5\u662f\u4e3a\u4e86\u6548\u7387\u4e48\uff1f\u4e3a\u4ec0\u4e48\u4e0d\u7f16\u7801\u5b8c\u4e00\u4e2aframe\u5c31\u5c06\u5176release\u6389\uff1f&lt;br \/&gt;\n\u53e6\u5916\u4e0d\u5f97\u4e0d\u518d\u611f\u53f9\u4e0bGoogle\u7ecf\u5e38\u7684&lt;strong&gt;delete this;&lt;\/strong&gt;\u884c\u4e3a\uff0c\u7cbe\u5999\uff0c\u4f46\u662f\u770b\u8d77\u6765\u53cd\u5e38\uff01&lt;\/p&gt;\n<\/div><div class=\"gfmr-markdown-rendered\"><p>\u524d\u9762\u6709\u5206\u6790\u8fc7Camera\u7684\u5b9e\u73b0\uff0c\u73b0\u5728\u6765\u770b\u770bMediaRecorder\u7684\u5b9e\u73b0\uff0c\u8fd9\u91cc\u6211\u4e0d\u4f1a\u592a\u53bb\u5173\u6ce8\u5b83\u7684\u5206\u5c42\u7ed3\u6784\uff0c\u6211\u66f4\u5173\u6ce8\u5b83\u7684\u903b\u8f91\uff01<\/p>\n<p>APP\u5c42<a href=\"https:\/\/android.googlesource.com\/platform\/frameworks\/base\/+\/android-4.2.2_r1.2\/media\/java\/android\/media\/MediaRecorder.java\" target=\"_blank\"> \/path\/to\/aosp\/frameworks\/base\/media\/java\/android\/media\/MediaRecorder.java<\/a><br \/>\nJNI\u5c42<a href=\"https:\/\/android.googlesource.com\/platform\/frameworks\/base\/+\/android-4.2.2_r1.2\/media\/jni\/android_media_MediaRecorder.cpp\" target=\"_blank\"> \/path\/to\/aosp\/frameworks\/base\/media\/jni\/android_media_MediaRecorder.cpp<\/a><br \/>\n\u8c03\u7528NATIVE\u5c42\u7684MediaRecorder(\u8fd9\u91cc\u662fBnMediaRecorderClient)<br \/>\nheader<a href=\"https:\/\/android.googlesource.com\/platform\/frameworks\/av\/+\/android-4.2.2_r1.2\/include\/media\/mediarecorder.h\" target=\"_blank\"> \/path\/to\/aosp\/frameworks\/av\/include\/media\/mediarecorder.h<\/a><br \/>\nimplementation <a href=\"https:\/\/android.googlesource.com\/platform\/frameworks\/av\/+\/android-4.2.2_r1.2\/media\/libmedia\/mediarecorder.cpp\" target=\"_blank\">\/path\/to\/aosp\/frameworks\/av\/media\/libmedia\/mediarecorder.cpp<\/a><\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">MediaRecorder::MediaRecorder() : mSurfaceMediaSource(NULL)\r\n{\r\n    ALOGV(&quot;constructor&quot;);\r\n\r\n    const sp&lt;IMediaPlayerService&gt;&amp; service(getMediaPlayerService());\r\n    if (service != NULL) {\r\n        mMediaRecorder = service-&gt;createMediaRecorder(getpid());\r\n    }\r\n    if (mMediaRecorder != NULL) {\r\n        mCurrentState = MEDIA_RECORDER_IDLE;\r\n    }\r\n\r\n    doCleanUp();\r\n}<\/pre>\n<p>getMediaPlayerService()\u8fd9\u4e2a\u65b9\u6cd5\u4f4d\u4e8e<a href=\"https:\/\/android.googlesource.com\/platform\/frameworks\/av\/+\/android-4.2.2_r1.2\/include\/media\/IMediaMetadataRetriever.h\" target=\"_blank\">\/path\/to\/aosp\/frameworks\/av\/include\/media\/IMediaDeathNotifier.h<\/a><\/p>\n<p>\u83b7\u53d6\u5230MediaPlayerService(\u8fd9\u4e2a\u662fBpMediaPlayerService)\u4e4b\u540e<br \/>\n\u8c03\u7528IMediaPlayerService\u5f53\u4e2d\u7684<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">sp&lt;IMediaRecorder&gt; MediaPlayerService::createMediaRecorder(pid_t pid)\r\n{\r\n    sp&lt;MediaRecorderClient&gt; recorder = new MediaRecorderClient(this, pid);\r\n    wp&lt;MediaRecorderClient&gt; w = recorder;\r\n    Mutex::Autolock lock(mLock);\r\n    mMediaRecorderClients.add(w);\r\n    ALOGV(&quot;Create new media recorder client from pid %d&quot;, pid);\r\n    return recorder;\r\n}<\/pre>\n<p>\u521b\u5efaMediaRecorderClient(\u8fd9\u91cc\u662fBnMediaRecorder)<\/p>\n<p>\u4f46\u662f\u901a\u8fc7binder\u62ff\u5230\u7684\u662fBpMediaRecorder<br \/>\n\u56e0\u4e3a\u6709\u5982\u4e0b\u7684interface_cast\u8fc7\u7a0b<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">virtual sp&lt;IMediaRecorder&gt; createMediaRecorder(pid_t pid)\r\n{\r\n    Parcel data, reply;\r\n    data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());\r\n    data.writeInt32(pid);\r\n    remote()-&gt;transact(CREATE_MEDIA_RECORDER, data, &amp;reply);\r\n    return interface_cast&lt;IMediaRecorder&gt;(reply.readStrongBinder());\r\n}<\/pre>\n<p>\u800cMediaRecorderClient\u5f53\u4e2d\u53c8\u4f1a\u521b\u5efaStagefrightRecorder(MediaRecorderBase)\uff0c\u5b83\u4f4d\u4e8e<br \/>\n<a href=\"https:\/\/android.googlesource.com\/platform\/frameworks\/av\/+\/android-4.2.2_r1.2\/media\/libmediaplayerservice\/StagefrightRecorder.cpp\" target=\"_blank\">\/path\/to\/aosp\/frameworks\/av\/media\/libmediaplayerservice\/StagefrightRecorder.cpp<\/a><\/p>\n<p>\u76ee\u524d\u6211\u4eec\u53ef\u4ee5\u8ba4\u4e3a\u5728APP\/JNI\/NATIVE\u8fd9\u8fb9\u662f\u5728\u4e00\u4e2a\u8fdb\u7a0b\u5f53\u4e2d\uff0c\u5728MediaPlayerService\u5f53\u4e2d\u7684MediaRecorderClient\/StagefrightRecorder\u662f\u5728\u53e6\u5916\u4e00\u4e2a\u8fdb\u7a0b\u5f53\u4e2d\uff0c\u4ed6\u4eec\u4e4b\u95f4\u901a\u8fc7binder\u901a\u4fe1\uff0c\u800c\u4e14Bp\u548cBn\u6211\u4eec\u4e5f\u90fd\u6709\u62ff\u5230\uff0c\u540e\u9762\u6211\u4eec\u5c06\u4e0d\u518d\u4ed4\u7ec6\u533a\u5206Bp\u548cBn\u3002<\/p>\n<blockquote><p>\u5ba2\u6237\u7aef\u8fd9\u8fb9<br \/>\nBnMediaRecorderClient<br \/>\nBpMediaRecorder<br \/>\nBpMediaPlayerService<\/p>\n<p>\u670d\u52a1\u7aef\u8fd9\u8fb9<br \/>\nBpMediaRecorderClient(\u5982\u679c\u9700\u8981\u901a\u77e5\u5ba2\u6237\u7aef\u7684\u8bdd\uff0c\u5b83\u53ef\u4ee5\u83b7\u5f97\u8fd9\u4e2aBp)<br \/>\nBnMediaRecorder<br \/>\nBnMediaPlayerService<\/p><\/blockquote>\n<p>\u8fd9\u6709\u5f20\u56fe(\u70b9\u8fc7\u53bb\u770b\u539f\u59cb\u5927\u56fe)<br \/>\n<a href=\"https:\/\/www.flickr.com\/photos\/46848122@N08\/9410173938\/\" title=\"Android MediaRecorder Diagram by DeliberateKids, on Flickr\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/farm6.staticflickr.com\/5474\/9410173938_9d2028ea46_o.png\" width=\"3942\" height=\"2652\" alt=\"Android MediaRecorder Diagram\"><\/a><\/p>\n<p>\u6211\u4eec\u4ee5\u5f00\u59cb\u5f55\u5f71\u4e3a\u4f8b\u5b50\uff0c\u6bd4\u5982start()<\/p>\n<p>\u5728\u8fd9\u91cc\u5c31\u5175\u5206\u4e24\u8def\uff0c\u4e00\u4e2aCameraSource\uff0c\u4e00\u4e2aMPEG4Writer(sp mWriter)<br \/>\n\u8fd9\u4e24\u4e2aclass\u90fd\u4f4d\u4e8e<a href=\"https:\/\/android.googlesource.com\/platform\/frameworks\/av\/+\/android-4.2.2_r1.2\/media\/libstagefright\/\" target=\"_blank\">\/path\/to\/aosp\/frameworks\/av\/media\/libstagefright\/<\/a>\u5f53\u4e2d<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">status_t StagefrightRecorder::startMPEG4Recording() {\r\n    int32_t totalBitRate;\r\n    status_t err = setupMPEG4Recording(\r\n            mOutputFd, mVideoWidth, mVideoHeight,\r\n            mVideoBitRate, &amp;totalBitRate, &amp;mWriter);\r\n    if (err != OK) {\r\n        return err;\r\n    }\r\n\r\n    int64_t startTimeUs = systemTime() \/ 1000;\r\n    sp&lt;MetaData&gt; meta = new MetaData;\r\n    setupMPEG4MetaData(startTimeUs, totalBitRate, &amp;meta);\r\n\r\n    err = mWriter-&gt;start(meta.get());\r\n    if (err != OK) {\r\n        return err;\r\n    }\r\n\r\n    return OK;\r\n}<\/pre>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">status_t StagefrightRecorder::setupMPEG4Recording(\r\n        int outputFd,\r\n        int32_t videoWidth, int32_t videoHeight,\r\n        int32_t videoBitRate,\r\n        int32_t *totalBitRate,\r\n        sp&lt;MediaWriter&gt; *mediaWriter) {\r\n    mediaWriter-&gt;clear();\r\n    *totalBitRate = 0;\r\n    status_t err = OK;\r\n    sp&lt;MediaWriter&gt; writer = new MPEG4Writer(outputFd);\r\n\r\n    if (mVideoSource &lt; VIDEO_SOURCE_LIST_END) {\r\n\r\n        sp&lt;MediaSource&gt; mediaSource;\r\n        err = setupMediaSource(&amp;mediaSource); \/\/ very important\r\n        if (err != OK) {\r\n            return err;\r\n        }\r\n\r\n        sp&lt;MediaSource&gt; encoder;\r\n        err = setupVideoEncoder(mediaSource, videoBitRate, &amp;encoder); \/\/ very important\r\n        if (err != OK) {\r\n            return err;\r\n        }\r\n\r\n        writer-&gt;addSource(encoder);\r\n        *totalBitRate += videoBitRate;\r\n    }\r\n\r\n    \/\/ Audio source is added at the end if it exists.\r\n    \/\/ This help make sure that the &quot;recoding&quot; sound is suppressed for\r\n    \/\/ camcorder applications in the recorded files.\r\n    if (!mCaptureTimeLapse &amp;&amp; (mAudioSource != AUDIO_SOURCE_CNT)) {\r\n        err = setupAudioEncoder(writer); \/\/ very important\r\n        if (err != OK) return err;\r\n        *totalBitRate += mAudioBitRate;\r\n    }\r\n\r\n    ...\r\n\r\n    writer-&gt;setListener(mListener);\r\n    *mediaWriter = writer;\r\n    return OK;\r\n}<\/pre>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n\/\/ Set up the appropriate MediaSource depending on the chosen option\r\nstatus_t StagefrightRecorder::setupMediaSource(\r\n                      sp&lt;MediaSource&gt; *mediaSource) {\r\n    if (mVideoSource == VIDEO_SOURCE_DEFAULT\r\n            || mVideoSource == VIDEO_SOURCE_CAMERA) {\r\n        sp&lt;CameraSource&gt; cameraSource;\r\n        status_t err = setupCameraSource(&amp;cameraSource);\r\n        if (err != OK) {\r\n            return err;\r\n        }\r\n        *mediaSource = cameraSource;\r\n    } else if (mVideoSource == VIDEO_SOURCE_GRALLOC_BUFFER) {\r\n        \/\/ If using GRAlloc buffers, setup surfacemediasource.\r\n        \/\/ Later a handle to that will be passed\r\n        \/\/ to the client side when queried\r\n        status_t err = setupSurfaceMediaSource();\r\n        if (err != OK) {\r\n            return err;\r\n        }\r\n        *mediaSource = mSurfaceMediaSource;\r\n    } else {\r\n        return INVALID_OPERATION;\r\n    }\r\n    return OK;\r\n}<\/pre>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">status_t StagefrightRecorder::setupCameraSource(\r\n        sp&lt;CameraSource&gt; *cameraSource) {\r\n    status_t err = OK;\r\n    if ((err = checkVideoEncoderCapabilities()) != OK) {\r\n        return err;\r\n    }\r\n    Size videoSize;\r\n    videoSize.width = mVideoWidth;\r\n    videoSize.height = mVideoHeight;\r\n    if (mCaptureTimeLapse) {\r\n        if (mTimeBetweenTimeLapseFrameCaptureUs &lt; 0) {\r\n            ALOGE(&quot;Invalid mTimeBetweenTimeLapseFrameCaptureUs value: %lld&quot;,\r\n                mTimeBetweenTimeLapseFrameCaptureUs);\r\n            return BAD_VALUE;\r\n        }\r\n\r\n        mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera(\r\n                mCamera, mCameraProxy, mCameraId,\r\n                videoSize, mFrameRate, mPreviewSurface,\r\n                mTimeBetweenTimeLapseFrameCaptureUs);\r\n        *cameraSource = mCameraSourceTimeLapse;\r\n    } else {\r\n        *cameraSource = CameraSource::CreateFromCamera(\r\n                mCamera, mCameraProxy, mCameraId, videoSize, mFrameRate,\r\n                mPreviewSurface, true \/*storeMetaDataInVideoBuffers*\/);\r\n    }\r\n    mCamera.clear();\r\n    mCameraProxy.clear();\r\n    if (*cameraSource == NULL) {\r\n        return UNKNOWN_ERROR;\r\n    }\r\n\r\n    if ((*cameraSource)-&gt;initCheck() != OK) {\r\n        (*cameraSource).clear();\r\n        *cameraSource = NULL;\r\n        return NO_INIT;\r\n    }\r\n\r\n    \/\/ When frame rate is not set, the actual frame rate will be set to\r\n    \/\/ the current frame rate being used.\r\n    if (mFrameRate == -1) {\r\n        int32_t frameRate = 0;\r\n        CHECK ((*cameraSource)-&gt;getFormat()-&gt;findInt32(\r\n                    kKeyFrameRate, &amp;frameRate));\r\n        ALOGI(&quot;Frame rate is not explicitly set. Use the current frame &quot;\r\n             &quot;rate (%d fps)&quot;, frameRate);\r\n        mFrameRate = frameRate;\r\n    }\r\n\r\n    CHECK(mFrameRate != -1);\r\n\r\n    mIsMetaDataStoredInVideoBuffers =\r\n        (*cameraSource)-&gt;isMetaDataStoredInVideoBuffers();\r\n\r\n    return OK;\r\n}<\/pre>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">status_t StagefrightRecorder::setupVideoEncoder(\r\n        sp&lt;MediaSource&gt; cameraSource,\r\n        int32_t videoBitRate,\r\n        sp&lt;MediaSource&gt; *source) {\r\n    source-&gt;clear();\r\n\r\n    sp&lt;MetaData&gt; enc_meta = new MetaData;\r\n    enc_meta-&gt;setInt32(kKeyBitRate, videoBitRate);\r\n    enc_meta-&gt;setInt32(kKeyFrameRate, mFrameRate);\r\n\r\n    switch (mVideoEncoder) {\r\n        case VIDEO_ENCODER_H263:\r\n            enc_meta-&gt;setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);\r\n            break;\r\n\r\n        case VIDEO_ENCODER_MPEG_4_SP:\r\n            enc_meta-&gt;setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);\r\n            break;\r\n\r\n        case VIDEO_ENCODER_H264:\r\n            enc_meta-&gt;setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);\r\n            break;\r\n\r\n        default:\r\n            CHECK(!&quot;Should not be here, unsupported video encoding.&quot;);\r\n            break;\r\n    }\r\n\r\n    sp&lt;MetaData&gt; meta = cameraSource-&gt;getFormat();\r\n\r\n    int32_t width, height, stride, sliceHeight, colorFormat;\r\n    CHECK(meta-&gt;findInt32(kKeyWidth, &amp;width));\r\n    CHECK(meta-&gt;findInt32(kKeyHeight, &amp;height));\r\n    CHECK(meta-&gt;findInt32(kKeyStride, &amp;stride));\r\n    CHECK(meta-&gt;findInt32(kKeySliceHeight, &amp;sliceHeight));\r\n    CHECK(meta-&gt;findInt32(kKeyColorFormat, &amp;colorFormat));\r\n\r\n    enc_meta-&gt;setInt32(kKeyWidth, width);\r\n    enc_meta-&gt;setInt32(kKeyHeight, height);\r\n    enc_meta-&gt;setInt32(kKeyIFramesInterval, mIFramesIntervalSec);\r\n    enc_meta-&gt;setInt32(kKeyStride, stride);\r\n    enc_meta-&gt;setInt32(kKeySliceHeight, sliceHeight);\r\n    enc_meta-&gt;setInt32(kKeyColorFormat, colorFormat);\r\n    if (mVideoTimeScale &gt; 0) {\r\n        enc_meta-&gt;setInt32(kKeyTimeScale, mVideoTimeScale);\r\n    }\r\n    if (mVideoEncoderProfile != -1) {\r\n        enc_meta-&gt;setInt32(kKeyVideoProfile, mVideoEncoderProfile);\r\n    }\r\n    if (mVideoEncoderLevel != -1) {\r\n        enc_meta-&gt;setInt32(kKeyVideoLevel, mVideoEncoderLevel);\r\n    }\r\n\r\n    OMXClient client;\r\n    CHECK_EQ(client.connect(), (status_t)OK);\r\n\r\n    uint32_t encoder_flags = 0;\r\n    if (mIsMetaDataStoredInVideoBuffers) {\r\n        encoder_flags |= OMXCodec::kStoreMetaDataInVideoBuffers;\r\n    }\r\n\r\n    \/\/ Do not wait for all the input buffers to become available.\r\n    \/\/ This give timelapse video recording faster response in\r\n    \/\/ receiving output from video encoder component.\r\n    if (mCaptureTimeLapse) {\r\n        encoder_flags |= OMXCodec::kOnlySubmitOneInputBufferAtOneTime;\r\n    }\r\n\r\n    sp&lt;MediaSource&gt; encoder = OMXCodec::Create(\r\n            client.interface(), enc_meta,\r\n            true \/* createEncoder *\/, cameraSource,\r\n            NULL, encoder_flags);\r\n    if (encoder == NULL) {\r\n        ALOGW(&quot;Failed to create the encoder&quot;);\r\n        \/\/ When the encoder fails to be created, we need\r\n        \/\/ release the camera source due to the camera&#039;s lock\r\n        \/\/ and unlock mechanism.\r\n        cameraSource-&gt;stop();\r\n        return UNKNOWN_ERROR;\r\n    }\r\n\r\n    *source = encoder;\r\n\r\n    return OK;\r\n}<\/pre>\n<p>\u8fd9\u91cc\u548cOMXCodec\u5173\u8054\u8d77\u6765<br \/>\n\u6709\u4e00\u4e2a\u53ebmedia_codecs.xml\u7684\u914d\u7f6e\u6587\u4ef6\u6765\u8868\u660e\u8bbe\u5907\u652f\u6301\u54ea\u4e9bcodec<\/p>\n<p>\u6211\u4eec\u5f55\u5236MPEG 4\u7684\u65f6\u5019\u8fd8\u4f1a\u6709\u58f0\u97f3\uff0c\u6240\u4ee5\u540e\u9762\u8fd8\u6709\u4e2asetupAudioEncoder\uff0c\u5177\u4f53\u7684\u65b9\u6cd5\u5c31\u4e0d\u5c55\u5f00\u4e86\uff0c\u603b\u4e4b\u5c31\u662f\u628a\u58f0\u97f3\u4e5f\u4f5c\u4e3a\u4e00\u4e2aTrack\u52a0\u5165\u5230MPEG4Writer\u5f53\u4e2d\u53bb\u3002<br \/>\n\u8fd9\u91cc\u63d2\u4e2a\u9898\u5916\u8bdd\uff0cGoogle\u8bf4\u628asetupAudioEncoder\u653e\u5230\u540e\u9762\u662f\u4e3a\u4e86\u907f\u514d\u5f00\u59cb\u5f55\u5f71\u7684\u90a3\u4e00\u4e2a\u63d0\u793a\u58f0\u97f3\u4e5f\u88ab\u5f55\u5236\u8fdb\u53bb\uff0c\u4f46\u662f\u5b9e\u9645\u53d1\u73b0\u5b83\u8fd9\u6837\u505a\u8fd8\u662f\u4f1a\u6709bug\uff0c\u5728\u4e00\u4e9b\u8bbe\u5907\u4e0a\u8fd8\u662f\u4f1a\u628a\u90a3\u58f0\u5f55\u5236\u8fdb\u53bb\uff0c\u8fd9\u4e2a\u9047\u5230\u7684\u90fd\u662f\u9760APP\u81ea\u5df1\u6765\u64ad\u653e\u58f0\u97f3\u6765\u7ed5\u8fc7\u8fd9\u4e2a\u95ee\u9898\u7684\u3002<\/p>\n<p>\u53e6\u5916MPEG4Writer\u5f53\u4e2d\u6709\u4e2a<br \/>\nstart(MetaData*)<br \/>\n\u542f\u52a8\u4e24\u4e2a\u65b9\u6cd5<br \/>\n a) startWriterThread<\/p>\n<p>    \u542f\u52a8\u4e00\u4e2athread\u53bb\u5199<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">    void MPEG4Writer::threadFunc() {\r\n        ALOGV(&quot;threadFunc&quot;);\r\n\r\n        prctl(PR_SET_NAME, (unsigned long)&quot;MPEG4Writer&quot;, 0, 0, 0);\r\n\r\n        Mutex::Autolock autoLock(mLock);\r\n        while (!mDone) {\r\n            Chunk chunk;\r\n            bool chunkFound = false;\r\n\r\n            while (!mDone &amp;&amp; !(chunkFound = findChunkToWrite(&amp;chunk))) {\r\n                mChunkReadyCondition.wait(mLock);\r\n            }\r\n\r\n            \/\/ Actual write without holding the lock in order to\r\n            \/\/ reduce the blocking time for media track threads.\r\n            if (chunkFound) {\r\n                mLock.unlock();\r\n                writeChunkToFile(&amp;chunk);\r\n                mLock.lock();\r\n            }\r\n        }\r\n\r\n        writeAllChunks();\r\n    }<\/pre>\n<p> b) startTracks<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">    status_t MPEG4Writer::startTracks(MetaData *params) {\r\n        for (List&lt;Track *&gt;::iterator it = mTracks.begin();\r\n             it != mTracks.end(); ++it) {\r\n            status_t err = (*it)-&gt;start(params);\r\n\r\n            if (err != OK) {\r\n                for (List&lt;Track *&gt;::iterator it2 = mTracks.begin();\r\n                     it2 != it; ++it2) {\r\n                    (*it2)-&gt;stop();\r\n                }\r\n\r\n                return err;\r\n            }\r\n        }\r\n        return OK;\r\n    }<\/pre>\n<p>    \u7136\u540e\u8c03\u7528\u6bcf\u4e2aTrack\u7684start\u65b9\u6cd5<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">    status_t MPEG4Writer::Track::start(MetaData *params) {\r\n        ...\r\n\r\n        initTrackingProgressStatus(params);\r\n\r\n        ...\r\n\r\n        status_t err = mSource-&gt;start(meta.get()); \/\/ \u8fd9\u91cc\u4f1a\u53bb\u6267\u884cCameraSource(start)\uff0c\u8fd9\u4e24\u4e2a\u662f\u76f8\u4e92\u5173\u8054\u7684\r\n\r\n        ...\r\n\r\n        pthread_create(&amp;mThread, &amp;attr, ThreadWrapper, this);\r\n        return OK;\r\n    }\r\n\r\n    void *MPEG4Writer::Track::ThreadWrapper(void *me) {\r\n        Track *track = static_cast&lt;Track *&gt;(me);\r\n\r\n        status_t err = track-&gt;threadEntry();\r\n        return (void *) err;\r\n    }<\/pre>\n<p>    \u901a\u8fc7status_t MPEG4Writer::Track::threadEntry()<br \/>\n    \u662f\u65b0\u542f\u52a8\u53e6\u5916\u4e00\u4e2athread\uff0c\u5b83\u91cc\u9762\u4f1a\u901a\u8fc7\u4e00\u4e2a\u5faa\u73af\u6765\u4e0d\u65ad\u8bfb\u53d6CameraSource(read)\u91cc\u9762\u7684\u6570\u636e\uff0cCameraSource\u91cc\u9762\u7684\u6570\u636e\u5f53\u7136\u662f\u4ecedriver\u8fd4\u56de\u8fc7\u6765\u7684(\u53ef\u4ee5\u53c2\u89c1CameraSourceListener\uff0cCameraSource\u7528\u4e00\u4e2a\u53eb\u505amFrameReceived\u7684List\u4e13\u95e8\u5b58\u653e\u4ecedriver\u8fc7\u6765\u7684\u6570\u636e\uff0c\u5982\u679c\u6536\u5230\u6570\u636e\u4f1a\u8c03\u7528mFrameAvailableCondition.signal\uff0c\u82e5\u8fd8\u6ca1\u6709\u5f00\u59cb\u5f55\u5f71\uff0c\u8fd9\u4e2a\u65f6\u5019\u6536\u5230\u7684\u6570\u636e\u662f\u88ab\u4e22\u5f03\u7684\uff0c\u5f53\u7136MediaWriter\u5148\u542f\u52a8\u7684\u662fCameraSource\u7684start\u65b9\u6cd5\uff0c\u518d\u542f\u52a8\u5199Track)\uff0c\u7136\u540e\u5199\u5230\u6587\u4ef6\u5f53\u4e2d\u3002<br \/>\n\u6ce8\u610f\uff1a\u51c6\u786e\u6765\u8bf4\u8fd9\u91ccMPEG4Writer\u8bfb\u53d6\u7684\u662fOMXCodec\u91cc\u7684\u6570\u636e\uff0c\u56e0\u4e3a\u6570\u636e\u5148\u5230CameraSource\uff0ccodec\u8d1f\u8d23\u7f16\u7801\u4e4b\u540e\uff0cMPEG4Writer\u624d\u8d1f\u8d23\u5199\u5230\u6587\u4ef6\u5f53\u4e2d\uff01\u5173\u4e8e\u6570\u636e\u5728CameraSource\/OMXCodec\/MPEG4Writer\u4e4b\u95f4\u662f\u600e\u4e48\u4f20\u9012\u7684\uff0c\u53ef\u4ee5\u53c2\u89c1<a href=\"http:\/\/guoh.ai\/lifelog\/2013\/06\/interaction-between-stagefright-and-codec\/\" target=\"_blank\">http:\/\/guoh.ai\/lifelog\/2013\/06\/interaction-between-stagefright-and-codec\/<\/a>\u5f53\u4e2d\u8bb2Buffer\u7684\u4f20\u8f93\u8fc7\u7a0b\u3002<\/p>\n<p>\u56de\u5934\u518d\u6765\u770b\uff0cStagefright\u505a\u4e86\u4ec0\u4e48\u4e8b\u60c5\uff1f\u6211\u66f4\u89c9\u5f97\u5b83\u53ea\u662f\u4e00\u4e2a\u7c98\u5408\u5242(glue)\u7684\u7528\u5904\uff0c\u5b83\u5de5\u4f5c\u5728MediaPlayerService\u8fd9\u4e00\u5c42\uff0c\u628aMediaSource\uff0cMediaWriter\uff0cCodec\u4ee5\u53ca\u4e0a\u5c42\u7684MediaRecorder\u7ed1\u5b9a\u5728\u4e00\u8d77\uff0c\u8fd9\u5e94\u8be5\u5c31\u662f\u5b83\u6700\u5927\u7684\u4f5c\u7528\uff0cGoogle\u7528\u5b83\u6765\u66ff\u6362Opencore\u4e5f\u662f\u7b26\u5408\u5176\u4e00\u8d2f\u7684\u5de5\u7a0b\u6d3e\u4f5c\u98ce(\u76f8\u6bd4\u590d\u6742\u7684\u5b66\u672f\u6d3e\u800c\u8a00\uff0c\u867d\u7136Google\u5f88\u591a\u4e1c\u897f\u4e5f\u5f88\u590d\u6742\uff0c\u4f46\u662f\u5b83\u4e00\u822c\u90fd\u662f\u4ee5\u5c3d\u91cf\u7b80\u5355\u7684\u65b9\u5f0f\u6765\u89e3\u51b3\u95ee\u9898)\u3002<br \/>\n\u8ba9\u5927\u5bb6\u89c9\u5f97\u6709\u70b9\u4e0d\u4e60\u60ef\u7684\u662f\uff0c\u5b83\u628aMediaRecorder\u653e\u5728MediaPlayerService\u5f53\u4e2d\uff0c\u8fd9\u4e24\u4e2a\u770b\u8d77\u6765\u662f\u5bf9\u7acb\u7684\u4e8b\u60c5\uff0c\u6216\u8005\u67d0\u4e00\u5929\u5b83\u4eec\u4f1a\u6539\u540d\u5b57\uff0c\u6216\u8005\u662f\u4e24\u8005\u5206\u5f00\uff0c\u4e0d\u77e5\u9053\uff5e\uff5e<\/p>\n<p>\u5f53\u7136\u8fd9\u53ea\u662f\u4e2a\u7b80\u5355\u7684\u5927\u4f53\u4ecb\u7ecd\uff0cCodec\u76f8\u5173\u7684\u540e\u9762\u4e89\u53d6\u4e13\u95e8\u6765\u5206\u6790\u4e00\u4e0b\uff01<\/p>\n<p>\u6709\u4e9b\u7ec6\u8282\u7684\u4e1c\u897f\u5728\u8fd9\u91cc\u6ca1\u6709\u5217\u51fa\uff0c\u9700\u8981\u7684\u8bdd\u4f1a\u628a\u4e00\u4e9b\u6ce8\u610f\u70b9\u5217\u51fa\u6765\uff1a<\/p>\n<p>1. \u65f6\u5149\u6d41\u901d\u5f55\u5f71<br \/>\nCameraSource\u5bf9\u5e94\u7684\u5c31\u662fCameraSourceTimeLapse<\/p>\n<p>\u5177\u4f53\u505a\u6cd5\u5c31\u662f\u5728<br \/>\ndataCallbackTimestamp<br \/>\n\u5f53\u4e2d\u6709skipCurrentFrame<\/p>\n<p>\u5f53\u7136\u5b83\u662f\u7528\u4e9b\u53d8\u91cf\u6765\u8bb0\u5f55\u548c\u8ba1\u7b97<br \/>\nmTimeBetweenTimeLapseVideoFramesUs(1E6\/videoFrameRate) \/\/ \u4e24\u4e2aframe\u4e4b\u95f4\u7684\u95f4\u9694\u65f6\u95f4<br \/>\n\u8bb0\u5f55\u4e0a\u4e00\u4e2aframe\u7684(mLastTimeLapseFrameRealTimestampUs) \/\/ \u4e0a\u4e00\u4e2aframe\u53d1\u751f\u7684\u65f6\u95f4<br \/>\n\u7136\u540e\u901a\u8fc7frame rate\u8ba1\u7b97\u51fa\u4e24\u4e2aframe\u4e4b\u95f4\u7684\u76f8\u8ddd\u79bb\u65f6\u95f4\uff0c\u4e2d\u95f4\u7684\u90fd\u900f\u8fc7releaseOneRecordingFrame\u6765drop\u6389<br \/>\n\u4e5f\u5c31\u662f\u8bf4driver\u8fd4\u56de\u7684\u4e1c\u897f\u90fd\u4e0d\u53d8\uff0c\u53ea\u662f\u5728SW\u8fd9\u5c42\u6211\u4eec\u81ea\u5df1\u6765\u5904\u7406\u6389<\/p>\n<p>\u5173\u4e8eTime-lapse\u76f8\u5173\u7684\u53ef\u4ee5\u53c2\u9605<br \/>\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Time-lapse_photography\" target=\"_blank\">https:\/\/en.wikipedia.org\/wiki\/Time-lapse_photography<\/a><\/p>\n<p>2. \u5f55\u5f71\u5f53\u4e2d\u9700\u8981\u7528\u5230Camera\u7684\u8bdd\u662f\u901a\u8fc7ICameraRecordingProxy\uff0c\u5373Camera\u5f53\u4e2d\u7684RecordingProxy(\u8fd9\u662f\u4e00\u4e2aBnCameraRecordingProxy)<br \/>\n\u5f53\u900f\u8fc7binder\uff0c\u5c06ICameraRecordingProxy\u4f20\u5230\u670d\u52a1\u7aef\u8fdb\u7a0b\u4e4b\u540e\uff0c\u5b83\u5c31\u53d8\u6210\u4e86Bp\uff0c\u5982\u4e0b\uff1a<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">case SET_CAMERA: {\r\n    ALOGV(&quot;SET_CAMERA&quot;);\r\n    CHECK_INTERFACE(IMediaRecorder, data, reply);\r\n    sp&lt;ICamera&gt; camera = interface_cast&lt;ICamera&gt;(data.readStrongBinder());\r\n    sp&lt;ICameraRecordingProxy&gt; proxy =\r\n        interface_cast&lt;ICameraRecordingProxy&gt;(data.readStrongBinder());\r\n    reply-&gt;writeInt32(setCamera(camera, proxy));\r\n    return NO_ERROR;\r\n} break;<\/pre>\n<p>\u5728CameraSource\u5f53\u4e2d\u4f1a\u8fd9\u6837\u53bb\u4f7f\u7528<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\/\/ We get the proxy from Camera, not ICamera. We need to get the proxy\r\n\/\/ to the remote Camera owned by the application. Here mCamera is a\r\n\/\/ local Camera object created by us. We cannot use the proxy from\r\n\/\/ mCamera here.\r\nmCamera = Camera::create(camera);\r\nif (mCamera == 0) return -EBUSY;\r\nmCameraRecordingProxy = proxy;\r\nmCameraFlags |= FLAGS_HOT_CAMERA;<\/pre>\n<p>\u7591\u95ee\u70b9\uff1a<\/p>\n<p>CameraSource\u5f53\u4e2d\u8fd9\u4e2a<br \/>\n<strong>List&lt;sp &gt; mFramesBeingEncoded;<\/strong><br \/>\n\u6709\u4ec0\u4e48\u7528\uff1f<br \/>\n\u6bcf\u7f16\u7801\u5b8c\u4e00\u4e2aframe\uff0cCameraSource\u5c31\u4f1a\u5c06\u5176\u4fdd\u5b58\u8d77\u6765\uff0cBuffer\u88abrelease\u7684\u65f6\u5019\uff0c\u4f1a\u53cd\u8fc7\u6765release\u6389\u8fd9\u4e9bframe(s)\uff0c\u8fd9\u79cd\u505a\u6cd5\u662f\u4e3a\u4e86\u6548\u7387\u4e48\uff1f\u4e3a\u4ec0\u4e48\u4e0d\u7f16\u7801\u5b8c\u4e00\u4e2aframe\u5c31\u5c06\u5176release\u6389\uff1f<br \/>\n\u53e6\u5916\u4e0d\u5f97\u4e0d\u518d\u611f\u53f9\u4e0bGoogle\u7ecf\u5e38\u7684<strong>delete this;<\/strong>\u884c\u4e3a\uff0c\u7cbe\u5999\uff0c\u4f46\u662f\u770b\u8d77\u6765\u53cd\u5e38\uff01<\/p>\n<\/div><\/div>","protected":false},"excerpt":{"rendered":"<p>&lt;p&gt;\u524d\u9762\u6709\u5206\u6790\u8fc7Camera\u7684\u5b9e\u73b0\uff0c\u73b0\u5728\u6765\u770b\u770bMediaRecorder\u7684\u5b9e\u73b0\uff0c\u8fd9\u91cc\u6211\u4e0d\u4f1a\u592a\u53bb\u5173\u6ce8\u5b83\u7684\u5206\u5c42\u7ed3\u6784\uff0c\u6211\u66f4\u5173\u6ce8\u5b83\u7684\u903b\u8f91\uff01&lt;\/p&gt; &lt;p&gt;APP\u5c42&lt;a href=&quot;https:\/\/android.googlesource.com\/platform\/frameworks\/base\/+\/android-4.2.2_r1.2\/media\/java\/android\/media\/MediaRecorder.java&quot; target=&quot;_blank&quot;&gt; \/path\/to\/aosp\/frameworks\/base\/media\/java\/android\/media\/MediaRecorder.java&lt;\/a&gt;&lt;br \/&gt; JNI\u5c42&lt;a href=&quot;https:\/\/android.googlesource.com\/platform\/frameworks\/base\/+\/android-4.2.2_r1.2\/media\/jni\/android_media_MediaRecorder.cpp&quot; target=&quot;_blank&quot;&gt; \/path\/to\/aosp\/frameworks\/base\/media\/jni\/android_media_MediaRecorder.cpp&lt;\/a&gt;&lt;br \/&gt; \u8c03\u7528NATIVE\u5c42\u7684MediaRecorder(\u8fd9\u91cc\u662fBnMediaRecorderClient)&lt;br \/&gt; header&lt;a href=&quot;https:\/\/android.googlesource.com\/platform\/frameworks\/av\/+\/android-4.2.2_r1.2\/include\/media\/mediarecorder.h&quot; target=&quot;_blank&quot;&gt; \/path\/to\/aosp\/frameworks\/av\/include\/media\/mediarecorder.h&lt;\/a&gt;&lt;br \/&gt; implementation &lt;a href=&quot;https:\/\/android.googlesource.com\/platform\/frameworks\/av\/+\/android-4.2.2_r1.2\/media\/libmedia\/mediarecorder.cpp&quot; target=&quot;_blank&quot;&gt;\/path\/to\/aosp\/frameworks\/av\/media\/libmedia\/mediarecorder.cpp&lt;\/a&gt;&lt;\/p&gt; &lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;MediaRecorder::MediaRecorder() : mSurfaceMediaSource(NULL) { ALOGV(&quot;constructor&quot;); const sp&lt;IMediaPlayerService&gt;&amp; service(getMediaPlayerService()); if (service != NULL) { mMediaRecorder = service-&gt;createMediaRecorder(getpid()); } if (mMediaRecorder != NULL) { mCurrentState = MEDIA_RECORDER_IDLE; } doCleanUp(); &hellip; <a href=\"https:\/\/guoh.ai\/lifelog\/2013\/06\/android-mediarecorder-architecture\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Android MediaRecorder\u7cfb\u7edf\u7ed3\u6784<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_gfmr_meta_descriptions":[],"_gfmr_multilingual_taxonomy_terms":[],"footnotes":""},"categories":[7,28,34],"tags":[27],"class_list":["post-590","post","type-post","status-publish","format-standard","hentry","category-android","category-cpp","category-multimedia","tag-camera"],"_links":{"self":[{"href":"https:\/\/guoh.ai\/lifelog\/wp-json\/wp\/v2\/posts\/590","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/guoh.ai\/lifelog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/guoh.ai\/lifelog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/guoh.ai\/lifelog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/guoh.ai\/lifelog\/wp-json\/wp\/v2\/comments?post=590"}],"version-history":[{"count":15,"href":"https:\/\/guoh.ai\/lifelog\/wp-json\/wp\/v2\/posts\/590\/revisions"}],"predecessor-version":[{"id":851,"href":"https:\/\/guoh.ai\/lifelog\/wp-json\/wp\/v2\/posts\/590\/revisions\/851"}],"wp:attachment":[{"href":"https:\/\/guoh.ai\/lifelog\/wp-json\/wp\/v2\/media?parent=590"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/guoh.ai\/lifelog\/wp-json\/wp\/v2\/categories?post=590"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/guoh.ai\/lifelog\/wp-json\/wp\/v2\/tags?post=590"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}