diff options
author | James Dong <jdong@google.com> | 2010-04-21 16:14:15 -0700 |
---|---|---|
committer | James Dong <jdong@google.com> | 2010-05-05 11:34:43 -0700 |
commit | 3300e9667e3daa374659b4a8b97dd92c4d34e501 (patch) | |
tree | 7fb447a7a23b8a992d929a5c30ca3a01cacefe18 /media | |
parent | 51867f231355b82fd0a8804352403fec102cb0dc (diff) | |
download | frameworks_base-3300e9667e3daa374659b4a8b97dd92c4d34e501.zip frameworks_base-3300e9667e3daa374659b4a8b97dd92c4d34e501.tar.gz frameworks_base-3300e9667e3daa374659b4a8b97dd92c4d34e501.tar.bz2 |
Support audio and video track interleaving in the recorded mp4 file
Change-Id: Ifa27eb23ee265f84fe06773b29b0eb2b0b075b60
Diffstat (limited to 'media')
-rw-r--r-- | media/libmediaplayerservice/StagefrightRecorder.cpp | 20 | ||||
-rw-r--r-- | media/libmediaplayerservice/StagefrightRecorder.h | 4 | ||||
-rw-r--r-- | media/libstagefright/CameraSource.cpp | 23 | ||||
-rw-r--r-- | media/libstagefright/MPEG4Writer.cpp | 149 |
4 files changed, 148 insertions, 48 deletions
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 682ff3a..57db7e4 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -213,25 +213,31 @@ status_t StagefrightRecorder::setParamVideoEncodingBitRate(int32_t bitRate) { return OK; } -status_t StagefrightRecorder::setMaxDurationOrFileSize(int32_t limit, bool limit_is_duration) { - LOGV("setMaxDurationOrFileSize: limit (%d) for %s", +status_t StagefrightRecorder::setParamMaxDurationOrFileSize(int32_t limit, + bool limit_is_duration) { + LOGV("setParamMaxDurationOrFileSize: limit (%d) for %s", limit, limit_is_duration?"duration":"size"); return OK; } +status_t StagefrightRecorder::setParamInterleaveDuration(int32_t durationUs) { + LOGV("setParamInterleaveDuration: %d", durationUs); + mInterleaveDurationUs = durationUs; + return OK; +} status_t StagefrightRecorder::setParameter( const String8 &key, const String8 &value) { LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string()); if (key == "max-duration") { int32_t max_duration_ms; if (safe_strtoi64(value.string(), &max_duration_ms)) { - return setMaxDurationOrFileSize( + return setParamMaxDurationOrFileSize( max_duration_ms, true /* limit_is_duration */); } } else if (key == "max-filesize") { int32_t max_filesize_bytes; if (safe_strtoi64(value.string(), &max_filesize_bytes)) { - return setMaxDurationOrFileSize( + return setParamMaxDurationOrFileSize( max_filesize_bytes, false /* limit is filesize */); } } else if (key == "audio-param-sampling-rate") { @@ -254,6 +260,11 @@ status_t StagefrightRecorder::setParameter( if (safe_strtoi64(value.string(), &video_bitrate)) { return setParamVideoEncodingBitRate(video_bitrate); } + } else if (key == "param-interleave-duration-us") { + int32_t durationUs; + if (safe_strtoi64(value.string(), &durationUs)) { + return setParamInterleaveDuration(durationUs); + } } else { LOGE("setParameter: failed to find key %s", key.string()); return BAD_VALUE; @@ -480,6 +491,7 @@ status_t StagefrightRecorder::startMPEG4Recording() { mWriter->addSource(encoder); } + ((MPEG4Writer *)mWriter.get())->setInterleaveDuration(mInterleaveDurationUs); mWriter->start(); return OK; } diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index ad1153c..3b99e91 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -74,6 +74,7 @@ private: int32_t mAudioBitRate; int32_t mAudioChannels; int32_t mSampleRate; + int32_t mInterleaveDurationUs; String8 mParams; int mOutputFd; @@ -87,7 +88,8 @@ private: status_t setParamAudioEncodingBitRate(int32_t bitRate); status_t setParamAudioNumberOfChannels(int32_t channles); status_t setParamAudioSamplingRate(int32_t sampleRate); - status_t setMaxDurationOrFileSize(int32_t limit, bool limit_is_duration); + status_t setParamInterleaveDuration(int32_t durationUs); + status_t setParamMaxDurationOrFileSize(int32_t limit, bool limit_is_duration); StagefrightRecorder(const StagefrightRecorder &); StagefrightRecorder &operator=(const StagefrightRecorder &); diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index b07bd0e..b046a9c 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -130,8 +130,9 @@ CameraSource::CameraSource(const sp<Camera> &camera) mHeight(0), mFirstFrameTimeUs(0), mLastFrameTimestampUs(0), - mNumFrames(0), - mNumFramesReleased(0), + mNumFramesReceived(0), + mNumFramesEncoded(0), + mNumFramesDropped(0), mStarted(false) { String8 s = mCamera->getParameters(); printf("params: \"%s\"\n", s.string()); @@ -178,9 +179,11 @@ status_t CameraSource::stop() { mCamera->stopRecording(); releaseQueuedFrames(); - LOGI("Frames received/released: %d/%d, timestamp (us) last/first: %lld/%lld", - mNumFrames, mNumFramesReleased, + LOGI("Frames received/encoded/dropped: %d/%d/%d, timestamp (us) last/first: %lld/%lld", + mNumFramesReceived, mNumFramesEncoded, mNumFramesDropped, mLastFrameTimestampUs, mFirstFrameTimeUs); + + CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped); return OK; } @@ -190,7 +193,7 @@ void CameraSource::releaseQueuedFrames() { it = mFrames.begin(); mCamera->releaseRecordingFrame(*it); mFrames.erase(it); - ++mNumFramesReleased; + ++mNumFramesDropped; } } @@ -231,7 +234,7 @@ status_t CameraSource::read( frameTime = *mFrameTimes.begin(); mFrameTimes.erase(mFrameTimes.begin()); - ++mNumFramesReleased; + ++mNumFramesEncoded; } *buffer = new MediaBuffer(frame->size()); @@ -252,15 +255,15 @@ void CameraSource::dataCallbackTimestamp(int64_t timestampUs, Mutex::Autolock autoLock(mLock); if (!mStarted) { mCamera->releaseRecordingFrame(data); - ++mNumFrames; - ++mNumFramesReleased; + ++mNumFramesReceived; + ++mNumFramesDropped; return; } - if (mNumFrames == 0) { + if (mNumFramesReceived == 0) { mFirstFrameTimeUs = timestampUs; } - ++mNumFrames; + ++mNumFramesReceived; mFrames.push_back(data); mFrameTimes.push_back(timestampUs - mFirstFrameTimeUs); diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index e6336e7..29ec876 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -57,10 +57,24 @@ private: struct SampleInfo { size_t size; - off_t offset; int64_t timestamp; }; - List<SampleInfo> mSampleInfos; + List<SampleInfo> mSampleInfos; + List<MediaBuffer *> mChunkSamples; + List<off_t> mChunkOffsets; + + struct StscTableEntry { + + StscTableEntry(uint32_t chunk, uint32_t samples, uint32_t id) + : firstChunk(chunk), + samplesPerChunk(samples), + sampleDescriptionId(id) {} + + uint32_t firstChunk; + uint32_t samplesPerChunk; + uint32_t sampleDescriptionId; + }; + List<StscTableEntry> mStscTableEntries; List<int32_t> mStssTableEntries; @@ -75,6 +89,7 @@ private: status_t makeAVCCodecSpecificData( const uint8_t *data, size_t size); + void writeOneChunk(bool isAvc); Track(const Track &); Track &operator=(const Track &); @@ -85,14 +100,16 @@ private: MPEG4Writer::MPEG4Writer(const char *filename) : mFile(fopen(filename, "wb")), mOffset(0), - mMdatOffset(0) { + mMdatOffset(0), + mInterleaveDurationUs(500000) { CHECK(mFile != NULL); } MPEG4Writer::MPEG4Writer(int fd) : mFile(fdopen(fd, "wb")), mOffset(0), - mMdatOffset(0) { + mMdatOffset(0), + mInterleaveDurationUs(500000) { CHECK(mFile != NULL); } @@ -213,9 +230,20 @@ void MPEG4Writer::stop() { mFile = NULL; } -off_t MPEG4Writer::addSample(MediaBuffer *buffer) { - Mutex::Autolock autoLock(mLock); +status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) { + mInterleaveDurationUs = durationUs; + return OK; +} + +void MPEG4Writer::lock() { + mLock.lock(); +} + +void MPEG4Writer::unlock() { + mLock.unlock(); +} +off_t MPEG4Writer::addSample_l(MediaBuffer *buffer) { off_t old_offset = mOffset; fwrite((const uint8_t *)buffer->data() + buffer->range_offset(), @@ -240,9 +268,7 @@ static void StripStartcode(MediaBuffer *buffer) { } } -off_t MPEG4Writer::addLengthPrefixedSample(MediaBuffer *buffer) { - Mutex::Autolock autoLock(mLock); - +off_t MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) { StripStartcode(buffer); off_t old_offset = mOffset; @@ -532,13 +558,17 @@ void MPEG4Writer::Track::threadEntry() { !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC); bool is_avc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); int32_t count = 0; + const int64_t interleaveDurationUs = mOwner->interleaveDuration(); + int64_t chunkTimestampUs = 0; + int32_t nChunks = 0; + int32_t nZeroLengthFrames = 0; MediaBuffer *buffer; while (!mDone && mSource->read(&buffer) == OK) { if (buffer->range_length() == 0) { buffer->release(); buffer = NULL; - + ++nZeroLengthFrames; continue; } @@ -661,20 +691,14 @@ void MPEG4Writer::Track::threadEntry() { continue; } - off_t offset = is_avc ? mOwner->addLengthPrefixedSample(buffer) - : mOwner->addSample(buffer); - SampleInfo info; info.size = is_avc #if USE_NALLEN_FOUR - ? buffer->range_length() + 4 + ? buffer->range_length() + 4 #else - ? buffer->range_length() + 2 + ? buffer->range_length() + 2 #endif - : buffer->range_length(); - - info.offset = offset; - + : buffer->range_length(); bool is_audio = !strncasecmp(mime, "audio/", 6); @@ -687,12 +711,42 @@ void MPEG4Writer::Track::threadEntry() { // Our timestamp is in ms. info.timestamp = (timestampUs + 500) / 1000; - mSampleInfos.push_back(info); +//////////////////////////////////////////////////////////////////////////////// + // Make a deep copy of the MediaBuffer less Metadata + MediaBuffer *copy = new MediaBuffer(buffer->range_length()); + memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(), + buffer->range_length()); + copy->set_range(0, buffer->range_length()); + + mChunkSamples.push_back(copy); + if (interleaveDurationUs == 0) { + StscTableEntry stscEntry(++nChunks, 1, 1); + mStscTableEntries.push_back(stscEntry); + writeOneChunk(is_avc); + } else { + if (chunkTimestampUs == 0) { + chunkTimestampUs = timestampUs; + } else { + if (timestampUs - chunkTimestampUs > interleaveDurationUs) { + ++nChunks; + if (nChunks == 1 || // First chunk + (--(mStscTableEntries.end()))->samplesPerChunk != + mChunkSamples.size()) { + StscTableEntry stscEntry(nChunks, + mChunkSamples.size(), 1); + mStscTableEntries.push_back(stscEntry); + } + writeOneChunk(is_avc); + chunkTimestampUs = timestampUs; + } + } + } + int32_t isSync = false; - buffer->meta_data()->findInt32(kKeyIsSyncFrame, &isSync); - if (isSync) { + if (buffer->meta_data()->findInt32(kKeyIsSyncFrame, &isSync) && + isSync != 0) { mStssTableEntries.push_back(mSampleInfos.size()); } // Our timestamp is in ms. @@ -700,7 +754,37 @@ void MPEG4Writer::Track::threadEntry() { buffer = NULL; } + // Last chunk + if (!mChunkSamples.empty()) { + ++nChunks; + StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1); + mStscTableEntries.push_back(stscEntry); + writeOneChunk(is_avc); + } + mReachedEOS = true; + LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames", + count, nZeroLengthFrames, mSampleInfos.size()); +} + +void MPEG4Writer::Track::writeOneChunk(bool isAvc) { + mOwner->lock(); + for (List<MediaBuffer *>::iterator it = mChunkSamples.begin(); + it != mChunkSamples.end(); ++it) { + off_t offset = isAvc? mOwner->addLengthPrefixedSample_l(*it) + : mOwner->addSample_l(*it); + if (it == mChunkSamples.begin()) { + mChunkOffsets.push_back(offset); + } + } + mOwner->unlock(); + while (!mChunkSamples.empty()) { + List<MediaBuffer *>::iterator it = mChunkSamples.begin(); + (*it)->release(); + (*it) = NULL; + mChunkSamples.erase(it); + } + mChunkSamples.clear(); } int64_t MPEG4Writer::Track::getDurationUs() const { @@ -1018,22 +1102,21 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { mOwner->beginBox("stsc"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mSampleInfos.size()); - int32_t n = 1; - for (List<SampleInfo>::iterator it = mSampleInfos.begin(); - it != mSampleInfos.end(); ++it, ++n) { - mOwner->writeInt32(n); - mOwner->writeInt32(1); - mOwner->writeInt32(1); + mOwner->writeInt32(mStscTableEntries.size()); + for (List<StscTableEntry>::iterator it = mStscTableEntries.begin(); + it != mStscTableEntries.end(); ++it) { + mOwner->writeInt32(it->firstChunk); + mOwner->writeInt32(it->samplesPerChunk); + mOwner->writeInt32(it->sampleDescriptionId); } mOwner->endBox(); // stsc mOwner->beginBox("co64"); mOwner->writeInt32(0); // version=0, flags=0 - mOwner->writeInt32(mSampleInfos.size()); - for (List<SampleInfo>::iterator it = mSampleInfos.begin(); - it != mSampleInfos.end(); ++it) { - mOwner->writeInt64((*it).offset); + mOwner->writeInt32(mChunkOffsets.size()); + for (List<off_t>::iterator it = mChunkOffsets.begin(); + it != mChunkOffsets.end(); ++it) { + mOwner->writeInt64((*it)); } mOwner->endBox(); // co64 |