Android MPEG4Writer.cpp 简单跟读

       首先在前面申明一点,本人接触Android的时间并不长,只是因为喜欢 Android开发,并且项目有需要,才会去看Android framework 层MPEG4Writer.cpp 的代码。在这里也只是想简单的记录下自己这几天跟读代码的结果,也好给自己个交代。其中有些知识还是感谢网上其它大神的指点,这里给出我参考的博文的链接点击打开链接 http://m.blog.csdn.net/blog/liwendovo/8478259

    下面的一些内容和图片,我就不自己总结了,直接粘贴上面链接博文的内容:

--------------------------------------------------------------------------------------------------------------

Android系统录像封装流程主要有三个步骤:

1)       录制开始时,写入文件头部。

2)       录制进行时,实时写入音视频轨迹的数据块。

3)       录制结束时,写入索引信息并更新头部参数。

  索引负责描述音视频轨迹的特征,会随着音视频轨迹的存储而变化,所以通常做法会将录像文件索引信息放在音视频轨迹流后面,在媒体流数据写完(录像结束)后才能写入。可以看到,存放音视频数据的mdat box是位于第二位的,而负责检索音视频的moov box是位于最后的,这与通常的MP4封装的排列顺序不同,当然这是为了符合录制而产生的结果。因为 moov的大小是随着 mdat 变化的,而我们录制视频的时间预先是不知道的,所以需要先将mdat 数据写入,最后再写入moov,完成封装。 

  现有Android系统上录像都是录制是MP4或3GP格式,底层就是使用MPEG4Writer组合器类来完成的,它将编码后的音视频轨迹按照MPEG4规范进行封装,填入各个参数,就组合成完整的MP4格式文件。MPEG4Writer的组合功能主要由两种线程完成,一种是负责音视频数据写入封装文件的写线程(WriterThread),一种是音视频数据读取处理的轨迹线程(TrackThread)。轨迹线程一般有两个:视频轨迹数据读取线程和音频轨迹数据读取线程,而写线程只有一个,负责将轨迹线程中打包成Chunk的数据写入封装文件。

  如图3所示,轨迹线程是以帧为单位获取数据帧(Sample),并将每帧中的信息及系统环境信息提取汇总存储在内存的trak表中,其中需要维持的信息有Chunk写入文件的偏移地址Stco(Chunk Offset)、Sample与Chunk的映射关系Stsc(Sample-to-Chunk)、关键帧Stss(Sync Sample)、每一帧的持续时间Stts(Time-to-Sample)等,这些信息是跟每一帧的信息密切相关的,由图可以看出trak表由各自的线程维护,当录像结束时trak表会就会写入封装文件。而每一帧的数据流会先存入一个链表缓存中,当帧的数量达到一定值时,轨迹线程会将这些帧数据打包成块(Chunk)并通知写线程写入到封装文件。写线程接到Chunk已准备好的通知后就马上搜索Chunk链表(链表个数与轨迹线程个数相关,一般有两个,音视频轨迹线程各有一个),将找到的第一个Chunk后便写入封装文件,并会将写入的偏移地址更新到相应的trak表的Stco项(但trak表中其它数据是由轨迹线程更新)。音视频的Chunk数据是存储于同一mdat box中,按添加到Chunk链表时间先后顺序排列。等到录像结束时,录像应用会调用MPEG4Writer的stop方法,此时就会将音视频的trak表分别写入moov。

 

------------------------------------------------------------------------------------------------------------------------------------------------

     其实看完上面的内容,应该对Android录制视频过程中,录制的视频的封装过程有一个大体了解,我们平时所说的视频后缀名.mp4/.mkv等等就是视频封装的各种格式。

     下面将给出MPEG4Writer.cpp 的跟读过程:

      MPEG4Writer.cpp 的构造函数,在这里将实现一些参数的初始化,fd是传进来的录制文件的文件描述符。

  1. MPEG4Writer::MPEG4Writer(int fd)  
  2.       mFd(dup(fd)),  
  3.      ifReRecording(false),  
  4.      mInitCheck(mFd < 0? NO_INIT: OK),  
  5.      mIsRealTimeRecording(true),  
  6.      mUse4ByteNalLength(true),  
  7.      mUse32BitOffset(true),  
  8.      mIsFileSizeLimitExplicitlyRequested(false),  
  9.      mPaused(false),  
  10.      mStarted(false),  
  11.      mWriterThreadStarted(false),  
  12.      mOffset(0),  
  13.      mMdatOffset(0),  
  14.      mEstimatedMoovBoxSize(0),  
  15.      mInterleaveDurationUs(1000000),  
  16.      mLatitudex10000(0),  
  17.      mLongitudex10000(0),  
  18.      mAreGeoTagsAvailable(false),  
  19.      mStartTimeOffsetMs(-1),  
  20.      mHFRRatio(1) {  
  21.        
  22.      ALOGD("*** MPEG4Writer(int fd):mFd is:%d",mFd);  
  23.     

     应用层 MediaRecorder.start();时,往framework层调用时,将会调用到MPEG4Writer.cpp 中的 start部分,在start部分,我们看到在这一部分,writeFtypBox(param) 将实现录制文件文件头部信息的相关信息的写入操作;startWriterThread() 开启封装视频文件的写线程;startTracks(param) 开启视频数据的读线程,也就是前面文件部分所说的轨迹线程。

  1. status_t MPEG4Writer::start(MetaData *param) {  
  2.           ......  
  3.   
  4.     //暂停后,再次start时,mStarted = true;mPaused = false;  
  5.     if (mStarted) {  
  6.         if (mPaused) {  
  7.             mPaused = false;  
  8.             return startTracks(param);   
  9.         }  
  10.         return OK;  
  11.     }  
  12.   
  13.       ......  
  14.   
  15.     writeFtypBox(param); //写入封装文件头部信息  
  16.   
  17.     mFreeBoxOffset = mOffset;  
  18.   
  19.       ......  
  20.   
  21.     mOffset = mMdatOffset;   
  22.    lseek64(mFd, mMdatOffset, SEEK_SET);//将文件指针移动到mMdatOffset的位置  
  1. status_t err = startWriterThread(); //开启写线程  
  1.      if (err != OK) { return err; }  
  1. err = startTracks(param);//开启轨迹线程  
  1. if (err != OK) { return err; }  
  1. mStarted = truereturn OK;}  


   继续看下 startWriterThread()部分,在startWriterThread()函数中,将真正建立新的子线程,并在子线程中执行ThreadWrappe函数中的操作。

  1. status_t MPEG4Writer::startWriterThread() {  
  2.     ALOGV("****** startWriterThread");  
  3.   
  4.     mDone = false;  
  5.     mIsFirstChunk = true;  
  6.     mDriftTimeUs = 0;  
  7.     for (List<Track *>::iterator it = mTracks.begin();  
  8.          it != mTracks.end(); ++it) {  
  9.         ChunkInfo info;  
  10.         info.mTrack = *it;  
  11.         info.mPrevChunkTimestampUs = 0;  
  12.         info.mMaxInterChunkDurUs = 0;  
  13.         mChunkInfos.push_back(info);  //  
  14.     }  
  15.   
  16.     pthread_attr_t attr;  
  17.     pthread_attr_init(&attr);  
  18.     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);  
  19.     pthread_create(&mThread, &attr,ThreadWrapper, this);  
  20.     pthread_attr_destroy(&attr);  
  21.     mWriterThreadStarted = true;  
  22.     return OK;  
  23. }  
    接着继续看 ThreadWrapper()函数,在这里new 了一个MPEGWriter对象,真正的操作在threadFunc()中体现

  1. void *MPEG4Writer::ThreadWrapper(void *me) {  
  2.     MPEG4Writer *writer = static_cast<MPEG4Writer *>(me);  
  3.     writer->threadFunc();  
  4.     return NULL;  
  5. }  
   下面看下threadFun()。在这个函数中,将根据变量mDone 进行while循环,一直检测是否有数据块Chunk可写。轨迹线程是一直将读数据的数据往buffer中写入,buffer到了一定量后,就是chunk,这时就会通过信号量 mChunkReadyCondition来通知封装文件的写线程去检测链表,然后将检索到的Chunk数据写入文件的数据区,当然写之前,肯定会去判断下是否真的有数据可写。

  1. void MPEG4Writer::threadFunc() {  
  2.   
  3.     prctl(PR_SET_NAME, (unsigned long)"MPEG4Writer", 0, 0, 0);  
  4.   
  5.     Mutex::Autolock autoLock(mLock);  
  6.     while (!mDone) {    
  7.         Chunk chunk;  
  8.         bool chunkFound = false;  
  9.   
  10.         while (!mDone && !(chunkFound = findChunkToWrite(&chunk))) {  
  11.             mChunkReadyCondition.wait(mLock);  
  12.         }  
  13.   
  14.         // In real time recording mode, write without holding the lock in order  
  15.         // to reduce the blocking time for media track threads.  
  16.         // Otherwise, hold the lock until the existing chunks get written to the  
  17.         // file.  
  18.         if (chunkFound) {  
  19.             if (mIsRealTimeRecording) {  
  20.                 mLock.unlock();  
  21.             }  
  22.             writeChunkToFile(&chunk);  
  23.             if (mIsRealTimeRecording) {  
  24.                 mLock.lock();  
  25.             }  
  26.         }  
  27.     }  
  28.   
  29.      writeAllChunks();//这个是在while(!mDone)之后了,应用层MediaRecorder.stop()时,mDone的值将为true,这里应该是录制文件结束时,将剩下的所有数据都写入封装文件,具体也没有跟读。  
  30. }  
    下面看下writerChunkToFile(&chunk);轨迹线程读数据时是以数据帧Sample为单位,所以这里将Chunk写入封装文件,也是以Sample为单位,遍历整个链表,将数据写入封装文件,真正的写入操作是addSamole_l(*it);

  1. void MPEG4Writer::writeChunkToFile(Chunk* chunk) {  
  2.     ALOGV("******writeChunkToFile: %lld from %s track",  
  3.         chunk->mTimeStampUs, chunk->mTrack->isAudio()? "audio""video");  
  4.   
  5.     int32_t isFirstSample = true;  
  6.     while (!chunk->mSamples.empty()) {  
  7.         List<MediaBuffer *>::iterator it = chunk->mSamples.begin();  
  8.   
  9.         off64_t offset = chunk->mTrack->isAvc()  
  10.                                 ? addLengthPrefixedSample_l(*it)  
  11.                                 :addSample_l(*it);  
  12.   
  13.         if (isFirstSample) {  
  14.             chunk->mTrack->addChunkOffset(offset);  
  15.             isFirstSample = false;  
  16.         }  
  17.   
  18.         (*it)->release();  
  19.         (*it) = NULL;  
  20.         chunk->mSamples.erase(it);  
  21.     }  
  22.     chunk->mSamples.clear();  
  23. }  
   下面看下 addSamole_l(*it) 函数,wirte写入操作,mFd 是上层设置录制的文件路径传下来的文件描述符

  1. off64_t MPEG4Writer::addSample_l(MediaBuffer *buffer) {  
  2.       
  3.     off64_t old_offset = mOffset;  
  4.   
  5.     ::write(mFd,  
  6.           (const uint8_t *)buffer->data() + buffer->range_offset(),  
  7.           buffer->range_length());  
  8.   
  9.     mOffset += buffer->range_length();  
  10.   
  11.     return old_offset;  
  12. }  
     到此,封装文件的写入线程的操作大体走完,下面看轨迹线程的操作。

---------------------------------------------------------------------------------------------------------------------------------

   startTracks(param) 轨迹线程的开启。文件的录制过程中是有2条轨迹线程,一个是视频的轨迹线程,另一条则是音频的轨迹线程,在starTrack(param)中是在for 循环中start了两条轨迹线程。

  1. status_t MPEG4Writer::startTracks(MetaData *params) {  
  2.   
  3.     if (mTracks.empty()) {  
  4.         ALOGE("No source added");  
  5.         return INVALID_OPERATION;  
  6.     }  
  7.   
  8.     for (List<Track *>::iterator it = mTracks.begin();  
  9.          it != mTracks.end(); ++it) {  
  10.         status_t err =(*it)->start(params);  
  11.   
  12.         if (err != OK) {  
  13.             for (List<Track *>::iterator it2 = mTracks.begin();  
  14.                  it2 != it; ++it2) {  
  15.                 (*it2)->stop();  
  16.             }  
  17.   
  18.             return err;  
  19.         }  
  20.     }  
  21.     return OK;  
  22. }  
   (*it)->start(params) 将会执行status_t MPEG4Writer::Track::start(MetaData *params) {} 。在这边也是同样新建子线程,在子线程中执行轨迹线程的相应操作。

  1. status_t MPEG4Writer::Track::start(MetaData *params) {  
  2.     
  3.     ......  
  4.     pthread_attr_t attr;  
  5.     pthread_attr_init(&attr);  
  6.     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);  
  7.   
  8.     mDone = false;  
  9.     mStarted = true;  
  10.     mTrackDurationUs = 0;  
  11.     mReachedEOS = false;  
  12.     mEstimatedTrackSizeBytes = 0;  
  13.     mMdatSizeBytes = 0;  
  14.     mMaxChunkDurationUs = 0;  
  15.   
  16.     pthread_create(&mThread, &attr,ThreadWrapper, this);//在子线程中执行ThreadWrapper函数  
  17.     pthread_attr_destroy(&attr);  
  18.   
  19.     mHFRRatio = ExtendedUtils::HFR::getHFRRatio(mMeta);  
  20.   
  21.     return OK;  
  22. }  
   下面看下上面ThreadWrapper函数,真正的操作又是放到了threadEntry()中去执行
  1. void *MPEG4Writer::Track::ThreadWrapper(void *me) {  
  2.   
  3.     Track *track = static_cast<Track *>(me);  
  4.   
  5.     status_t err = track->threadEntry();  
  6.     return (void *) err;  
  7. }  
    下面看thrck->threadEntry(),算是比较重要的一个函数了。有些具体的我自己也没看懂,只是知道到大概。

  1. status_t MPEG4Writer::Track::threadEntry() {  
  2.     ......  
  3.     int64_t lastTimestampUs = 0;      // Previous sample time stamp  
  4.     int64_t lastDurationUs = 0;       // Between the previous two samples  
  5.     int64_t currDurationTicks = 0;    // Timescale based ticks  
  6.     int64_t lastDurationTicks = 0;    // Timescale based ticks  
  7.     int32_t sampleCount = 1;          // Sample count in the current stts table entry  
  8.     uint32_t previousSampleSize = 0;  // Size of the previous sample  
  9.     int64_t previousPausedDurationUs = 0;  
  10.     int64_t timestampUs = 0;  
  11.     int64_t cttsOffsetTimeUs = 0;  
  12.     int64_t currCttsOffsetTimeTicks = 0;   // Timescale based ticks  
  13.     int64_t lastCttsOffsetTimeTicks = -1;  // Timescale based ticks  
  14.     int32_t cttsSampleCount = 0;           // Sample count in the current ctts table entry  
  15.     uint32_t lastSamplesPerChunk = 0;  
  16.   
  17.    
  18.     sp<MetaData> meta_data;  
  19.   
  20.     status_t err = OK;  
  21.     MediaBuffer *buffer;       
  22.     while (!mDone && (err = mSource->read(&buffer)) == OK) {   
  1. //mSource->read(&buffer) 将会调用CameraSource.cpp中的相应接口,进行视频数据的读取  
  1. if (buffer->range_length() == 0) {  
  2.         buffer->release();  
  3.         buffer = NULL;  
  4.         ++nZeroLengthFrames;  
  5.         continue;  
  6.     }  
  7.   
  8.     // If the codec specific data has not been received yet, delay pause.  
  9.     // After the codec specific data is received, discard what we received  
  10.     // when the track is to be paused.  
  11.     if (mPaused && !mResumed) {  
  12.         buffer->release();  
  13.         buffer = NULL;  
  14.         continue;  
  15.     }  
  16.   
  17.     ++count;  
  18.   
  19.     int32_t isCodecConfig;  //???  
  20.       
  21.     if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecConfig)  
  22.             && isCodecConfig) {  
  23.         CHECK(!mGotAllCodecSpecificData);  
  24.   
  25.         if (mIsAvc) {  
  26.             status_t err = makeAVCCodecSpecificData(  
  27.                     (const uint8_t *)buffer->data()  
  28.                         + buffer->range_offset(),  
  29.                     buffer->range_length());  
  30.             CHECK_EQ((status_t)OK, err);  
  31.         } else if (mIsMPEG4) {  
  32.             mCodecSpecificDataSize = buffer->range_length();  
  33.             mCodecSpecificData = malloc(mCodecSpecificDataSize);  
  34.             memcpy(mCodecSpecificData,  
  35.                     (const uint8_t *)buffer->data()  
  36.                         + buffer->range_offset(),  
  37.                    buffer->range_length());  
  38.         }  
  39.   
  40.         buffer->release();  
  41.         buffer = NULL;  
  42.   
  43.         mGotAllCodecSpecificData = true;  
  44.         continue;  
  45.     }  
  46.   
  47.     // Make a deep copy of the MediaBuffer and Metadata and release  
  48.     // the original as soon as we can  
  49.     MediaBuffer *copy = new MediaBuffer(buffer->range_length());  
  50.     memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(),  
  51.             buffer->range_length());  
  52.     copy->set_range(0, buffer->range_length());  
  53.     meta_data = new MetaData(*buffer->meta_data().get());  
  54.     buffer->release();  
  55.     buffer = NULL;  
  56.   
  57.     if (mIsAvc) StripStartcode(copy);  
  58.   
  59.     size_t sampleSize = copy->range_length();  
  60.     if (mIsAvc) {  
  61.         if (mOwner->useNalLengthFour()) {  
  62.             sampleSize += 4;  
  63.         } else {  
  64.             sampleSize += 2;  
  65.         }  
  66.     }  
  67.   
  68.     // Max file size or duration handling  
  69.     mMdatSizeBytes += sampleSize;  
  70.     updateTrackSizeEstimate();  
  1. /*写过视频录制的同学应该比较清楚,在上层应用,初始化MediaRecorder时,设置了录制文件录制的大小或者录制录制的段长时,为了实现循环录制,就需要监听底层的回调接口。而底层的回调就是在下面实现的。  
  1. exceedsFileSizeLimit()函数将会判断录制的文件的大小是否已经达到设置的大小,从而决定是否向上层回馈信息。同理exceedsFileDurationLimit()函数则是判断是否到达设置的段长。  
  1.         */  
  2.         if (mOwner->exceedsFileSizeLimit()) {  
  3.             mOwner->notify(MEDIA_RECORDER_EVENT_INFO,MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);  
  4.             break;  
  5.         }  
  6.           
  7.         if (mOwner->exceedsFileDurationLimit()) {  
  8.             mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);  
  9.               
  10.             break;  
  11.         }  
  12.   
  13.         int32_t isSync = false;  
  14.         meta_data->findInt32(kKeyIsSyncFrame, &isSync);  
  15.         CHECK(meta_data->findInt64(kKeyTime, ×tampUs));  
  16.   
  17.   
  18.         if (mStszTableEntries->count() == 0) {  
  19.             mFirstSampleTimeRealUs = systemTime() / 1000;  
  20.             mStartTimestampUs = timestampUs;  
  21.             mOwner->setStartTimestampUs(mStartTimestampUs);  
  22.             previousPausedDurationUs = mStartTimestampUs;  
  23.         }  
  24.   
  25.         if (mResumed) {  
  26.             mResumed = false;  
  27.         }  
  28.   
  29.         timestampUs -= previousPausedDurationUs;  
  30.         CHECK_GE(timestampUs, 0ll);  
  31.         if (!mIsAudio) {  
  32.             /* 
  33.              * Composition time: timestampUs 
  34.              * Decoding time: decodingTimeUs 
  35.              * Composition time offset = composition time - decoding time 
  36.              */  
  37.             int64_t decodingTimeUs;  
  38.             CHECK(meta_data->findInt64(kKeyDecodingTime, &decodingTimeUs));  
  39.             decodingTimeUs -= previousPausedDurationUs;  
  40.             cttsOffsetTimeUs =  
  41.                     timestampUs - decodingTimeUs;  
  42.             CHECK_GE(kMaxCttsOffsetTimeUs, decodingTimeUs - timestampUs);  
  43.             timestampUs = decodingTimeUs;  
  44.             ALOGV("decoding time: %lld and ctts offset time: %lld",  
  45.                 timestampUs, cttsOffsetTimeUs);  
  46.   
  47.             // Update ctts box table if necessary  
  48.             currCttsOffsetTimeTicks =  
  49.                     (cttsOffsetTimeUs * mTimeScale + 500000LL) / 1000000LL;  
  50.             CHECK_LE(currCttsOffsetTimeTicks, 0x0FFFFFFFFLL);  
  51.             if (mStszTableEntries->count() == 0) {  
  52.                 // Force the first ctts table entry to have one single entry  
  53.                 // so that we can do adjustment for the initial track start  
  54.                 // time offset easily in writeCttsBox().  
  55.                 lastCttsOffsetTimeTicks = currCttsOffsetTimeTicks;  
  56.                 addOneCttsTableEntry(1, currCttsOffsetTimeTicks);  
  57.                 cttsSampleCount = 0;      // No sample in ctts box is pending  
  58.             } else {  
  59.                 if (currCttsOffsetTimeTicks != lastCttsOffsetTimeTicks) {  
  60.                     addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);  
  61.                     lastCttsOffsetTimeTicks = currCttsOffsetTimeTicks;  
  62.                     cttsSampleCount = 1;  // One sample in ctts box is pending  
  63.                 } else {  
  64.                     ++cttsSampleCount;  
  65.                 }  
  66.             }  
  67.   
  68.             // Update ctts time offset range  
  69.             if (mStszTableEntries->count() == 0) {  
  70.                 mMinCttsOffsetTimeUs = currCttsOffsetTimeTicks;  
  71.                 mMaxCttsOffsetTimeUs = currCttsOffsetTimeTicks;  
  72.             } else {  
  73.                 if (currCttsOffsetTimeTicks > mMaxCttsOffsetTimeUs) {  
  74.                     mMaxCttsOffsetTimeUs = currCttsOffsetTimeTicks;  
  75.                 } else if (currCttsOffsetTimeTicks < mMinCttsOffsetTimeUs) {  
  76.                     mMinCttsOffsetTimeUs = currCttsOffsetTimeTicks;  
  77.                 }  
  78.             }  
  79.   
  80.         }  
  81.   
  82.         if (mOwner->isRealTimeRecording()) {  
  83.             if (mIsAudio) {  
  84.                 updateDriftTime(meta_data);  
  85.             }  
  86.         }  
  87.   
  88.         CHECK_GE(timestampUs, 0ll);  
  89.         ALOGV("%s media time stamp: %lld and previous paused duration %lld",  
  90.                 mIsAudio? "Audio""Video", timestampUs, previousPausedDurationUs);  
  91.         if (timestampUs > mTrackDurationUs) {  
  92.             mTrackDurationUs = timestampUs;  
  93.         }  
  94.   
  95.         // We need to use the time scale based ticks, rather than the  
  96.         // timestamp itself to determine whether we have to use a new  
  97.         // stts entry, since we may have rounding errors.  
  98.         // The calculation is intended to reduce the accumulated  
  99.         // rounding errors.  
  100.         currDurationTicks =  
  101.             ((timestampUs * mTimeScale + 500000LL) / 1000000LL -  
  102.                 (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);  
  103.         if (currDurationTicks < 0ll) {  
  104.             ALOGE("timestampUs %lld < lastTimestampUs %lld for %s track",  
  105.                 timestampUs, lastTimestampUs, mIsAudio? "Audio""Video");  
  106.             err = UNKNOWN_ERROR;  
  107.             mSource->notifyError(err);  
  108.             return err;  
  109.         }  
  110.   
  111.         mStszTableEntries->add(htonl(sampleSize));  
  112.         if (mStszTableEntries->count() > 2) {  
  113.   
  114.             // Force the first sample to have its own stts entry so that  
  115.             // we can adjust its value later to maintain the A/V sync.  
  116.             if (mStszTableEntries->count() == 3 || currDurationTicks != lastDurationTicks) {  
  117.                 addOneSttsTableEntry(sampleCount, lastDurationTicks);  
  118.                 sampleCount = 1;  
  119.             } else {  
  120.                 ++sampleCount;  
  121.             }  
  122.   
  123.         }  
  124.         if (mSamplesHaveSameSize) {  
  125.             if (mStszTableEntries->count() >= 2 && previousSampleSize != sampleSize) {  
  126.                 mSamplesHaveSameSize = false;  
  127.             }  
  128.             previousSampleSize = sampleSize;  
  129.         }  
  130.         ALOGV("%s timestampUs/lastTimestampUs: %lld/%lld",  
  131.                 mIsAudio? "Audio""Video", timestampUs, lastTimestampUs);  
  132.         lastDurationUs = timestampUs - lastTimestampUs;  
  133.         lastDurationTicks = currDurationTicks;  
  134.         lastTimestampUs = timestampUs;  
  135.   
  136.         if (isSync != 0) {  
  137.             addOneStssTableEntry(mStszTableEntries->count());  
  138.         }  
  139.   
  140.         if (mTrackingProgressStatus) {  
  141.             if (mPreviousTrackTimeUs <= 0) {  
  142.                 mPreviousTrackTimeUs = mStartTimestampUs;  
  143.             }  
  144.             trackProgressStatus(timestampUs);  
  145.         }  
  146.   
  147.         // use File write in seperate thread for video only recording  
  148.         if (!hasMultipleTracks && mIsAudio) {  
  149.             off64_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy)  
  150.                                  : mOwner->addSample_l(copy);  
  151.   
  152.             uint32_t count = (mOwner->use32BitFileOffset()  
  153.                         ? mStcoTableEntries->count()  
  154.                         : mCo64TableEntries->count());  
  155.   
  156.             if (count == 0) {  
  157.                 addChunkOffset(offset);  
  158.             }  
  159.             copy->release();  
  160.             copy = NULL;  
  161.             continue;  
  162.         }  
  163.   
  164.      mChunkSamples.push_back(copy);//push_back 是将读取到的数据添加到数据块链表的尾部  
  165.           
  166.        ......  
  167.   
  168.     }  
  169.   
  170.     if (isTrackMalFormed()) {  
  171.         err = ERROR_MALFORMED;  
  172.     }  
  173.   
  174.     mOwner->trackProgressStatus(mTrackId, -1, err);  
  175.   
  176.     // Last chunk  
  177.     if (!hasMultipleTracks && mIsAudio) {  
  178.         addOneStscTableEntry(1, mStszTableEntries->count());  
  179.     } else if (!mChunkSamples.empty()) {  
  180.         addOneStscTableEntry(++nChunks, mChunkSamples.size());  
  181.         bufferChunk(timestampUs);  
  182.     }  
  183.   
  184.     // We don't really know how long the last frame lasts, since  
  185.     // there is no frame time after it, just repeat the previous  
  186.     // frame's duration.  
  187.     if (mStszTableEntries->count() == 1) {  
  188.         lastDurationUs = 0;  // A single sample's duration  
  189.         lastDurationTicks = 0;  
  190.     } else {  
  191.         ++sampleCount;  // Count for the last sample  
  192.     }  
  193.   
  194.     if (mStszTableEntries->count() <= 2) {  
  195.         addOneSttsTableEntry(1, lastDurationTicks);  
  196.         if (sampleCount - 1 > 0) {  
  197.             addOneSttsTableEntry(sampleCount - 1, lastDurationTicks);  
  198.         }  
  199.     } else {  
  200.         addOneSttsTableEntry(sampleCount, lastDurationTicks);  
  201.     }  
  202.   
  203.     // The last ctts box may not have been written yet, and this  
  204.     // is to make sure that we write out the last ctts box.  
  205.     if (currCttsOffsetTimeTicks == lastCttsOffsetTimeTicks) {  
  206.         if (cttsSampleCount > 0) {  
  207.             addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);  
  208.         }  
  209.     }  
  210.   
  211.     mTrackDurationUs += lastDurationUs;  
  212.     mReachedEOS = true;  
  213.   
  214.     sendTrackSummary(hasMultipleTracks);  
  215.   
  216.     ALOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s",  
  217.             count, nZeroLengthFrames, mStszTableEntries->count(), mIsAudio? "audio""video");  
  218.     if (mIsAudio) {  
  219.         ALOGI("Audio track drift time: %lld us", mOwner->getDriftTimeUs());  
  220.     }  
  221.   
  222.     if (err == ERROR_END_OF_STREAM) {  
  223.         return OK;  
  224.     }  
  225.     return err;  
  226. }  
------------------------------------------------------------------------------------------------------------

     下面看下,录制文件结束时的一些操作。录制文件结束时,上层应用分别是调用 MediaRecorder的stop()、reset()release()方法,下面看下MPEG4Writer.cpp中相对应的操作。

  1. status_t MPEG4Writer::Track::stop() {  
  2.     if (!mStarted) {  
  3.         ALOGE("Stop() called but track is not started");  
  4.         return ERROR_END_OF_STREAM;  
  5.     }  
  6.   
  7.     if (mDone) {  
  8.         return OK;  
  9.     }  
  10.     mDone = true;  
  11.   
  12.     void *dummy;  
  13.     pthread_join(mThread, &dummy);//等待mThread 的结束  
  14.   
  15.     status_t err = (status_t) dummy;  
  16.   
  17.     ALOGD("Stopping %s track source", mIsAudio? "Audio""Video");  
  18.     {  
  19.         status_t status = mSource->stop();  
  20.         if (err == OK && status != OK && status != ERROR_END_OF_STREAM) {  
  21.             err = status;  
  22.         }  
  23.     }  
  24.   
  25.     ALOGD("%s track stopped", mIsAudio? "Audio""Video");  
  26.     return err;  
  27. }  

  reset()函数中将完成轨迹线程、写入线程的停止、封装文件尾部信息的写入等操作

  1. status_t MPEG4Writer::reset() {  
  2.   
  3.     if (mInitCheck != OK) {  
  4.         return OK;  
  5.     } else {  
  6.         if (!mWriterThreadStarted ||  
  7.             !mStarted) {  
  8.             if (mWriterThreadStarted) {  
  9.                 stopWriterThread();  
  10.             }  
  11.             release();  
  12.             return OK;  
  13.         }  
  14.     }  
  15.   
  16.     status_t err = OK;  
  17.     int64_t maxDurationUs = 0;  
  18.     int64_t minDurationUs = 0x7fffffffffffffffLL;  
  19.     for (List<Track *>::iterator it = mTracks.begin();  
  20.          it != mTracks.end(); ++it) {  
  21.         status_t status = (*it)->stop();//停止轨迹线程  
  22.         if (err == OK && status != OK) {  
  23.             err = status;  
  24.         }  
  25.   
  26.         int64_t durationUs = (*it)->getDurationUs();  
  27.         if (durationUs > maxDurationUs) {  
  28.             maxDurationUs = durationUs;  
  29.         }  
  30.         if (durationUs < minDurationUs) {  
  31.             minDurationUs = durationUs;  
  32.         }  
  33.     }  
  34.   
  35.     if (mTracks.size() > 1) {  
  36.         ALOGD("Duration from tracks range is [%lld, %lld] us",  
  37.             minDurationUs, maxDurationUs);  
  38.     }  
  39.   
  40.      
  41.    stopWriterThread(); //停止封装文件的写线程  
  42.       
  43.   
  44.     // Do not write out movie header on error.  
  45.     if (err != OK) {  
  46.         release();  
  47.         return err;  
  48.     }  
  49.   
  50.     // Fix up the size of the 'mdat' chunk.  
  51.     if (mUse32BitOffset) {  
  52.         lseek64(mFd, mMdatOffset, SEEK_SET);  
  53.         uint32_t size = htonl(static_cast<uint32_t>(mOffset - mMdatOffset));  
  54.         ::write(mFd, &size, 4);  
  55.     } else {  
  56.         lseek64(mFd, mMdatOffset + 8, SEEK_SET);  
  57.         uint64_t size = mOffset - mMdatOffset;  
  58.         size = hton64(size);  
  59.         ::write(mFd, &size, 8);  
  60.     }  
  61.     lseek64(mFd, mOffset, SEEK_SET);  
  62.   
  63.     // Construct moov box now  
  64.     mMoovBoxBufferOffset = 0;  
  65.     mWriteMoovBoxToMemory = mStreamableFile;  
  66.     if (mWriteMoovBoxToMemory) {  
  67.         // There is no need to allocate in-memory cache  
  68.         // for moov box if the file is not streamable.  
  69.   
  70.         mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);  
  71.         CHECK(mMoovBoxBuffer != NULL);  
  72.     }  
  73.     writeMoovBox(maxDurationUs);//封装文件尾部相应信息的写入  
  74.   
  75.     // mWriteMoovBoxToMemory could be set to false in  
  76.     // MPEG4Writer::write() method  
  77.     if (mWriteMoovBoxToMemory) {  
  78.         mWriteMoovBoxToMemory = false;  
  79.         // Content of the moov box is saved in the cache, and the in-memory  
  80.         // moov box needs to be written to the file in a single shot.  
  81.   
  82.         CHECK_LE(mMoovBoxBufferOffset + 8, mEstimatedMoovBoxSize);  
  83.   
  84.         // Moov box  
  85.         lseek64(mFd, mFreeBoxOffset, SEEK_SET);  
  86.         mOffset = mFreeBoxOffset;  
  87.         write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset);  
  88.   
  89.         // Free box  
  90.         lseek64(mFd, mOffset, SEEK_SET);  
  91.         writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset);  
  92.         write("free", 4);  
  93.     } else {  
  94.         ALOGI("The mp4 file will not be streamable.");  
  95.     }  
  96.   
  97.     // Free in-memory cache for moov box  
  98.     if (mMoovBoxBuffer != NULL) {  
  99.         free(mMoovBoxBuffer);  
  100.         mMoovBoxBuffer = NULL;  
  101.         mMoovBoxBufferOffset = 0;  
  102.     }  
  103.   
  104.     CHECK(mBoxes.empty());  
  105.   
  106.     release();  
  107.     return err;  
  108. }  

  release()中关闭文件描述符

  1. void MPEG4Writer::release() {  
  2.     ALOGD("***** release()!!!");  
  3.     close(mFd);  
  4.     mFd = -1;  
  5.     mInitCheck = NO_INIT;  
  6.     mStarted = false;  
  7. }  
    至此,整个录制文件的封装过程,大体流程走完,就是有轨迹线程不停的读取数据,然后读取的数据达到一定的大小时,也就是成chunk(块)时,将会通知写入线程去检测是否有chunk可写,有的话,将进行数据的写入操作。整个封装文件必须有相应的头部信息和尾部信息。   由于能力有限,只是进行了简单的跟读,对整个流程有大体的了解。其中不免有说错的地方,还望指教。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值