Loading include/media/stagefright/MPEG4Writer.h +41 −0 Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ private: uint32_t mInterleaveDurationUs; int32_t mTimeScale; int64_t mStartTimestampUs; Mutex mLock; List<Track *> mTracks; Loading @@ -87,6 +88,46 @@ private: size_t numTracks(); int64_t estimateMoovBoxSize(int32_t bitRate); struct Chunk { Track *mTrack; // Owner int64_t mTimeStampUs; // Timestamp of the 1st sample List<MediaBuffer *> mSamples; // Sample data // Convenient constructor Chunk(Track *track, int64_t timeUs, List<MediaBuffer *> samples) : mTrack(track), mTimeStampUs(timeUs), mSamples(samples) { } }; struct ChunkInfo { Track *mTrack; // Owner List<Chunk> mChunks; // Remaining chunks to be written }; bool mIsFirstChunk; volatile bool mDone; // Writer thread is done? pthread_t mThread; // Thread id for the writer List<ChunkInfo> mChunkInfos; // Chunk infos Condition mChunkReadyCondition; // Signal that chunks are available // Writer thread handling status_t startWriterThread(); void stopWriterThread(); static void *ThreadWrapper(void *me); void threadFunc(); // Buffer a single chunk to be written out later. void bufferChunk(const Chunk& chunk); // Write all buffered chunks from all tracks void writeChunks(); // Write a chunk if there is one status_t writeOneChunk(); // Write the first chunk from the given ChunkInfo. void writeFirstChunk(ChunkInfo* info); void lock(); void unlock(); Loading media/libstagefright/MPEG4Writer.cpp +215 −49 Original line number Diff line number Diff line Loading @@ -52,6 +52,11 @@ public: int64_t getDurationUs() const; int64_t getEstimatedTrackSizeBytes() const; void writeTrackHeader(int32_t trackID, bool use32BitOffset = true); void bufferChunk(int64_t timestampUs); bool isAvc() const { return mIsAvc; } bool isAudio() const { return mIsAudio; } bool isMPEG4() const { return mIsMPEG4; } void addChunkOffset(off_t offset) { mChunkOffsets.push_back(offset); } private: MPEG4Writer *mOwner; Loading @@ -60,8 +65,12 @@ private: volatile bool mDone; volatile bool mPaused; volatile bool mResumed; bool mIsAvc; bool mIsAudio; bool mIsMPEG4; int64_t mMaxTimeStampUs; int64_t mEstimatedTrackSizeBytes; int64_t mMaxWriteTimeUs; int32_t mTimeScale; pthread_t mThread; Loading Loading @@ -117,7 +126,6 @@ private: status_t makeAVCCodecSpecificData( const uint8_t *data, size_t size); void writeOneChunk(bool isAvc); // Track authoring progress status void trackProgressStatus(int64_t timeUs, status_t err = OK); Loading Loading @@ -320,10 +328,17 @@ status_t MPEG4Writer::start(MetaData *param) { } else { write("\x00\x00\x00\x01mdat????????", 16); } status_t err = startTracks(param); status_t err = startWriterThread(); if (err != OK) { return err; } err = startTracks(param); if (err != OK) { return err; } mStarted = true; return OK; } Loading @@ -339,6 +354,20 @@ void MPEG4Writer::pause() { } } void MPEG4Writer::stopWriterThread() { LOGV("stopWriterThread"); { Mutex::Autolock autolock(mLock); mDone = true; mChunkReadyCondition.signal(); } void *dummy; pthread_join(mThread, &dummy); } void MPEG4Writer::stop() { if (mFile == NULL) { return; Loading @@ -355,6 +384,7 @@ void MPEG4Writer::stop() { } } stopWriterThread(); // Fix up the size of the 'mdat' chunk. if (mUse32BitOffset) { Loading Loading @@ -693,6 +723,14 @@ MPEG4Writer::Track::Track( if (!mMeta->findInt32(kKeyTimeScale, &mTimeScale)) { mTimeScale = 1000; } const char *mime; mMeta->findCString(kKeyMIMEType, &mime); mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); mIsAudio = !strncasecmp(mime, "audio/", 6); mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC); CHECK(mTimeScale > 0); } Loading Loading @@ -751,6 +789,148 @@ void MPEG4Writer::Track::initTrackingProgressStatus(MetaData *params) { } } // static void *MPEG4Writer::ThreadWrapper(void *me) { LOGV("ThreadWrapper: %p", me); MPEG4Writer *writer = static_cast<MPEG4Writer *>(me); writer->threadFunc(); return NULL; } void MPEG4Writer::bufferChunk(const Chunk& chunk) { LOGV("bufferChunk: %p", chunk.mTrack); Mutex::Autolock autolock(mLock); CHECK_EQ(mDone, false); for (List<ChunkInfo>::iterator it = mChunkInfos.begin(); it != mChunkInfos.end(); ++it) { if (chunk.mTrack == it->mTrack) { // Found owner it->mChunks.push_back(chunk); mChunkReadyCondition.signal(); return; } } CHECK("Received a chunk for a unknown track" == 0); } void MPEG4Writer::writeFirstChunk(ChunkInfo* info) { LOGV("writeFirstChunk: %p", info->mTrack); List<Chunk>::iterator chunkIt = info->mChunks.begin(); for (List<MediaBuffer *>::iterator it = chunkIt->mSamples.begin(); it != chunkIt->mSamples.end(); ++it) { off_t offset = info->mTrack->isAvc() ? addLengthPrefixedSample_l(*it) : addSample_l(*it); if (it == chunkIt->mSamples.begin()) { info->mTrack->addChunkOffset(offset); } } // Done with the current chunk. // Release all the samples in this chunk. while (!chunkIt->mSamples.empty()) { List<MediaBuffer *>::iterator it = chunkIt->mSamples.begin(); (*it)->release(); (*it) = NULL; chunkIt->mSamples.erase(it); } chunkIt->mSamples.clear(); info->mChunks.erase(chunkIt); } void MPEG4Writer::writeChunks() { LOGV("writeChunks"); size_t outstandingChunks = 0; while (!mChunkInfos.empty()) { List<ChunkInfo>::iterator it = mChunkInfos.begin(); while (!it->mChunks.empty()) { CHECK_EQ(OK, writeOneChunk()); ++outstandingChunks; } it->mTrack = NULL; mChunkInfos.erase(it); } mChunkInfos.clear(); LOGD("%d chunks are written in the last batch", outstandingChunks); } status_t MPEG4Writer::writeOneChunk() { LOGV("writeOneChunk"); // Find the smallest timestamp, and write that chunk out // XXX: What if some track is just too slow? int64_t minTimestampUs = 0x7FFFFFFFFFFFFFFFLL; Track *track = NULL; for (List<ChunkInfo>::iterator it = mChunkInfos.begin(); it != mChunkInfos.end(); ++it) { if (!it->mChunks.empty()) { List<Chunk>::iterator chunkIt = it->mChunks.begin(); if (chunkIt->mTimeStampUs < minTimestampUs) { minTimestampUs = chunkIt->mTimeStampUs; track = it->mTrack; } } } if (track == NULL) { LOGV("Nothing to be written after all"); return OK; } if (mIsFirstChunk) { mIsFirstChunk = false; } for (List<ChunkInfo>::iterator it = mChunkInfos.begin(); it != mChunkInfos.end(); ++it) { if (it->mTrack == track) { writeFirstChunk(&(*it)); } } return OK; } void MPEG4Writer::threadFunc() { LOGV("threadFunc"); while (!mDone) { { Mutex::Autolock autolock(mLock); mChunkReadyCondition.wait(mLock); CHECK_EQ(writeOneChunk(), OK); } } { // Write ALL samples Mutex::Autolock autolock(mLock); writeChunks(); } } status_t MPEG4Writer::startWriterThread() { LOGV("startWriterThread"); mDone = false; mIsFirstChunk = true; for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it) { ChunkInfo info; info.mTrack = *it; mChunkInfos.push_back(info); } pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_create(&mThread, &attr, ThreadWrapper, this); pthread_attr_destroy(&attr); return OK; } status_t MPEG4Writer::Track::start(MetaData *params) { if (!mDone && mPaused) { mPaused = false; Loading Loading @@ -926,13 +1106,6 @@ static bool collectStatisticalData() { } void MPEG4Writer::Track::threadEntry() { sp<MetaData> meta = mSource->getFormat(); const char *mime; meta->findCString(kKeyMIMEType, &mime); bool is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC); bool is_avc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); bool is_audio = !strncasecmp(mime, "audio/", 6); int32_t count = 0; const int64_t interleaveDurationUs = mOwner->interleaveDuration(); int64_t chunkTimestampUs = 0; Loading @@ -943,10 +1116,12 @@ void MPEG4Writer::Track::threadEntry() { int32_t sampleCount = 1; // Sample count in the current stts table entry uint32_t previousSampleSize = 0; // Size of the previous sample int64_t previousPausedDurationUs = 0; int64_t timestampUs; sp<MetaData> meta_data; bool collectStats = collectStatisticalData(); mNumSamples = 0; mMaxWriteTimeUs = 0; status_t err = OK; MediaBuffer *buffer; while (!mDone && (err = mSource->read(&buffer)) == OK) { Loading @@ -973,13 +1148,13 @@ void MPEG4Writer::Track::threadEntry() { && isCodecConfig) { CHECK(!mGotAllCodecSpecificData); if (is_avc) { if (mIsAvc) { status_t err = makeAVCCodecSpecificData( (const uint8_t *)buffer->data() + buffer->range_offset(), buffer->range_length()); CHECK_EQ(OK, err); } else if (is_mpeg4) { } else if (mIsMPEG4) { mCodecSpecificDataSize = buffer->range_length(); mCodecSpecificData = malloc(mCodecSpecificDataSize); memcpy(mCodecSpecificData, Loading @@ -994,7 +1169,7 @@ void MPEG4Writer::Track::threadEntry() { mGotAllCodecSpecificData = true; continue; } else if (!mGotAllCodecSpecificData && count == 1 && is_mpeg4 && mCodecSpecificData == NULL) { count == 1 && mIsMPEG4 && mCodecSpecificData == NULL) { // The TI mpeg4 encoder does not properly set the // codec-specific-data flag. Loading Loading @@ -1034,7 +1209,7 @@ void MPEG4Writer::Track::threadEntry() { } mGotAllCodecSpecificData = true; } else if (!mGotAllCodecSpecificData && is_avc && count < 3) { } else if (!mGotAllCodecSpecificData && mIsAvc && count < 3) { // The TI video encoder does not flag codec specific data // as such and also splits up SPS and PPS across two buffers. Loading Loading @@ -1090,10 +1265,10 @@ void MPEG4Writer::Track::threadEntry() { buffer->release(); buffer = NULL; if (is_avc) StripStartcode(copy); if (mIsAvc) StripStartcode(copy); size_t sampleSize; sampleSize = is_avc sampleSize = mIsAvc #if USE_NALLEN_FOUR ? copy->range_length() + 4 #else Loading @@ -1116,7 +1291,6 @@ void MPEG4Writer::Track::threadEntry() { int32_t isSync = false; meta_data->findInt32(kKeyIsSyncFrame, &isSync); int64_t timestampUs; CHECK(meta_data->findInt64(kKeyTime, ×tampUs)); //////////////////////////////////////////////////////////////////////////////// Loading Loading @@ -1168,7 +1342,7 @@ void MPEG4Writer::Track::threadEntry() { trackProgressStatus(timestampUs); } if (mOwner->numTracks() == 1) { off_t offset = is_avc? mOwner->addLengthPrefixedSample_l(copy) off_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy) : mOwner->addSample_l(copy); if (mChunkOffsets.empty()) { mChunkOffsets.push_back(offset); Loading @@ -1182,7 +1356,7 @@ void MPEG4Writer::Track::threadEntry() { if (interleaveDurationUs == 0) { StscTableEntry stscEntry(++nChunks, 1, 1); mStscTableEntries.push_back(stscEntry); writeOneChunk(is_avc); bufferChunk(timestampUs); } else { if (chunkTimestampUs == 0) { chunkTimestampUs = timestampUs; Loading @@ -1199,7 +1373,7 @@ void MPEG4Writer::Track::threadEntry() { mChunkSamples.size(), 1); mStscTableEntries.push_back(stscEntry); } writeOneChunk(is_avc); bufferChunk(timestampUs); chunkTimestampUs = timestampUs; } } Loading @@ -1220,7 +1394,7 @@ void MPEG4Writer::Track::threadEntry() { ++nChunks; StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1); mStscTableEntries.push_back(stscEntry); writeOneChunk(is_avc); bufferChunk(timestampUs); } // We don't really know how long the last frame lasts, since Loading @@ -1234,10 +1408,10 @@ void MPEG4Writer::Track::threadEntry() { SttsTableEntry sttsEntry(sampleCount, lastDurationUs); mSttsTableEntries.push_back(sttsEntry); mReachedEOS = true; LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames - %s", count, nZeroLengthFrames, mNumSamples, is_audio? "audio": "video"); LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. Max write time: %lld us - %s", count, nZeroLengthFrames, mNumSamples, mMaxWriteTimeUs, mIsAudio? "audio": "video"); logStatisticalData(is_audio); logStatisticalData(mIsAudio); } void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) { Loading Loading @@ -1380,24 +1554,17 @@ void MPEG4Writer::Track::logStatisticalData(bool isAudio) { } } 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); } void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) { LOGV("bufferChunk"); int64_t startTimeUs = systemTime() / 1000; Chunk chunk(this, timestampUs, mChunkSamples); mOwner->bufferChunk(chunk); mChunkSamples.clear(); int64_t endTimeUs = systemTime() / 1000; if (mMaxWriteTimeUs < endTimeUs - startTimeUs) { mMaxWriteTimeUs = endTimeUs - startTimeUs; } } int64_t MPEG4Writer::Track::getDurationUs() const { Loading @@ -1414,9 +1581,8 @@ void MPEG4Writer::Track::writeTrackHeader( bool success = mMeta->findCString(kKeyMIMEType, &mime); CHECK(success); bool is_audio = !strncasecmp(mime, "audio/", 6); LOGV("%s track time scale: %d", is_audio? "Audio": "Video", mTimeScale); mIsAudio? "Audio": "Video", mTimeScale); time_t now = time(NULL); Loading @@ -1440,7 +1606,7 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->writeInt32(0); // reserved mOwner->writeInt16(0); // layer mOwner->writeInt16(0); // alternate group mOwner->writeInt16(is_audio ? 0x100 : 0); // volume mOwner->writeInt16(mIsAudio ? 0x100 : 0); // volume mOwner->writeInt16(0); // reserved mOwner->writeInt32(0x10000); // matrix Loading @@ -1453,7 +1619,7 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->writeInt32(0); mOwner->writeInt32(0x40000000); if (is_audio) { if (mIsAudio) { mOwner->writeInt32(0); mOwner->writeInt32(0); } else { Loading Loading @@ -1511,16 +1677,16 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->beginBox("hdlr"); mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt32(0); // component type: should be mhlr mOwner->writeFourcc(is_audio ? "soun" : "vide"); // component subtype mOwner->writeFourcc(mIsAudio ? "soun" : "vide"); // component subtype mOwner->writeInt32(0); // reserved mOwner->writeInt32(0); // reserved mOwner->writeInt32(0); // reserved // Removing "r" for the name string just makes the string 4 byte aligned mOwner->writeCString(is_audio ? "SoundHandle": "VideoHandle"); // name mOwner->writeCString(mIsAudio ? "SoundHandle": "VideoHandle"); // name mOwner->endBox(); mOwner->beginBox("minf"); if (is_audio) { if (mIsAudio) { mOwner->beginBox("smhd"); mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt16(0); // balance Loading Loading @@ -1553,7 +1719,7 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->beginBox("stsd"); mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt32(1); // entry count if (is_audio) { if (mIsAudio) { const char *fourcc = NULL; if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) { fourcc = "samr"; Loading Loading @@ -1735,7 +1901,7 @@ void MPEG4Writer::Track::writeTrackHeader( } mOwner->endBox(); // stts if (!is_audio) { if (!mIsAudio) { mOwner->beginBox("stss"); mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt32(mStssTableEntries.size()); // number of sync frames Loading Loading
include/media/stagefright/MPEG4Writer.h +41 −0 Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ private: uint32_t mInterleaveDurationUs; int32_t mTimeScale; int64_t mStartTimestampUs; Mutex mLock; List<Track *> mTracks; Loading @@ -87,6 +88,46 @@ private: size_t numTracks(); int64_t estimateMoovBoxSize(int32_t bitRate); struct Chunk { Track *mTrack; // Owner int64_t mTimeStampUs; // Timestamp of the 1st sample List<MediaBuffer *> mSamples; // Sample data // Convenient constructor Chunk(Track *track, int64_t timeUs, List<MediaBuffer *> samples) : mTrack(track), mTimeStampUs(timeUs), mSamples(samples) { } }; struct ChunkInfo { Track *mTrack; // Owner List<Chunk> mChunks; // Remaining chunks to be written }; bool mIsFirstChunk; volatile bool mDone; // Writer thread is done? pthread_t mThread; // Thread id for the writer List<ChunkInfo> mChunkInfos; // Chunk infos Condition mChunkReadyCondition; // Signal that chunks are available // Writer thread handling status_t startWriterThread(); void stopWriterThread(); static void *ThreadWrapper(void *me); void threadFunc(); // Buffer a single chunk to be written out later. void bufferChunk(const Chunk& chunk); // Write all buffered chunks from all tracks void writeChunks(); // Write a chunk if there is one status_t writeOneChunk(); // Write the first chunk from the given ChunkInfo. void writeFirstChunk(ChunkInfo* info); void lock(); void unlock(); Loading
media/libstagefright/MPEG4Writer.cpp +215 −49 Original line number Diff line number Diff line Loading @@ -52,6 +52,11 @@ public: int64_t getDurationUs() const; int64_t getEstimatedTrackSizeBytes() const; void writeTrackHeader(int32_t trackID, bool use32BitOffset = true); void bufferChunk(int64_t timestampUs); bool isAvc() const { return mIsAvc; } bool isAudio() const { return mIsAudio; } bool isMPEG4() const { return mIsMPEG4; } void addChunkOffset(off_t offset) { mChunkOffsets.push_back(offset); } private: MPEG4Writer *mOwner; Loading @@ -60,8 +65,12 @@ private: volatile bool mDone; volatile bool mPaused; volatile bool mResumed; bool mIsAvc; bool mIsAudio; bool mIsMPEG4; int64_t mMaxTimeStampUs; int64_t mEstimatedTrackSizeBytes; int64_t mMaxWriteTimeUs; int32_t mTimeScale; pthread_t mThread; Loading Loading @@ -117,7 +126,6 @@ private: status_t makeAVCCodecSpecificData( const uint8_t *data, size_t size); void writeOneChunk(bool isAvc); // Track authoring progress status void trackProgressStatus(int64_t timeUs, status_t err = OK); Loading Loading @@ -320,10 +328,17 @@ status_t MPEG4Writer::start(MetaData *param) { } else { write("\x00\x00\x00\x01mdat????????", 16); } status_t err = startTracks(param); status_t err = startWriterThread(); if (err != OK) { return err; } err = startTracks(param); if (err != OK) { return err; } mStarted = true; return OK; } Loading @@ -339,6 +354,20 @@ void MPEG4Writer::pause() { } } void MPEG4Writer::stopWriterThread() { LOGV("stopWriterThread"); { Mutex::Autolock autolock(mLock); mDone = true; mChunkReadyCondition.signal(); } void *dummy; pthread_join(mThread, &dummy); } void MPEG4Writer::stop() { if (mFile == NULL) { return; Loading @@ -355,6 +384,7 @@ void MPEG4Writer::stop() { } } stopWriterThread(); // Fix up the size of the 'mdat' chunk. if (mUse32BitOffset) { Loading Loading @@ -693,6 +723,14 @@ MPEG4Writer::Track::Track( if (!mMeta->findInt32(kKeyTimeScale, &mTimeScale)) { mTimeScale = 1000; } const char *mime; mMeta->findCString(kKeyMIMEType, &mime); mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); mIsAudio = !strncasecmp(mime, "audio/", 6); mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC); CHECK(mTimeScale > 0); } Loading Loading @@ -751,6 +789,148 @@ void MPEG4Writer::Track::initTrackingProgressStatus(MetaData *params) { } } // static void *MPEG4Writer::ThreadWrapper(void *me) { LOGV("ThreadWrapper: %p", me); MPEG4Writer *writer = static_cast<MPEG4Writer *>(me); writer->threadFunc(); return NULL; } void MPEG4Writer::bufferChunk(const Chunk& chunk) { LOGV("bufferChunk: %p", chunk.mTrack); Mutex::Autolock autolock(mLock); CHECK_EQ(mDone, false); for (List<ChunkInfo>::iterator it = mChunkInfos.begin(); it != mChunkInfos.end(); ++it) { if (chunk.mTrack == it->mTrack) { // Found owner it->mChunks.push_back(chunk); mChunkReadyCondition.signal(); return; } } CHECK("Received a chunk for a unknown track" == 0); } void MPEG4Writer::writeFirstChunk(ChunkInfo* info) { LOGV("writeFirstChunk: %p", info->mTrack); List<Chunk>::iterator chunkIt = info->mChunks.begin(); for (List<MediaBuffer *>::iterator it = chunkIt->mSamples.begin(); it != chunkIt->mSamples.end(); ++it) { off_t offset = info->mTrack->isAvc() ? addLengthPrefixedSample_l(*it) : addSample_l(*it); if (it == chunkIt->mSamples.begin()) { info->mTrack->addChunkOffset(offset); } } // Done with the current chunk. // Release all the samples in this chunk. while (!chunkIt->mSamples.empty()) { List<MediaBuffer *>::iterator it = chunkIt->mSamples.begin(); (*it)->release(); (*it) = NULL; chunkIt->mSamples.erase(it); } chunkIt->mSamples.clear(); info->mChunks.erase(chunkIt); } void MPEG4Writer::writeChunks() { LOGV("writeChunks"); size_t outstandingChunks = 0; while (!mChunkInfos.empty()) { List<ChunkInfo>::iterator it = mChunkInfos.begin(); while (!it->mChunks.empty()) { CHECK_EQ(OK, writeOneChunk()); ++outstandingChunks; } it->mTrack = NULL; mChunkInfos.erase(it); } mChunkInfos.clear(); LOGD("%d chunks are written in the last batch", outstandingChunks); } status_t MPEG4Writer::writeOneChunk() { LOGV("writeOneChunk"); // Find the smallest timestamp, and write that chunk out // XXX: What if some track is just too slow? int64_t minTimestampUs = 0x7FFFFFFFFFFFFFFFLL; Track *track = NULL; for (List<ChunkInfo>::iterator it = mChunkInfos.begin(); it != mChunkInfos.end(); ++it) { if (!it->mChunks.empty()) { List<Chunk>::iterator chunkIt = it->mChunks.begin(); if (chunkIt->mTimeStampUs < minTimestampUs) { minTimestampUs = chunkIt->mTimeStampUs; track = it->mTrack; } } } if (track == NULL) { LOGV("Nothing to be written after all"); return OK; } if (mIsFirstChunk) { mIsFirstChunk = false; } for (List<ChunkInfo>::iterator it = mChunkInfos.begin(); it != mChunkInfos.end(); ++it) { if (it->mTrack == track) { writeFirstChunk(&(*it)); } } return OK; } void MPEG4Writer::threadFunc() { LOGV("threadFunc"); while (!mDone) { { Mutex::Autolock autolock(mLock); mChunkReadyCondition.wait(mLock); CHECK_EQ(writeOneChunk(), OK); } } { // Write ALL samples Mutex::Autolock autolock(mLock); writeChunks(); } } status_t MPEG4Writer::startWriterThread() { LOGV("startWriterThread"); mDone = false; mIsFirstChunk = true; for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it) { ChunkInfo info; info.mTrack = *it; mChunkInfos.push_back(info); } pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_create(&mThread, &attr, ThreadWrapper, this); pthread_attr_destroy(&attr); return OK; } status_t MPEG4Writer::Track::start(MetaData *params) { if (!mDone && mPaused) { mPaused = false; Loading Loading @@ -926,13 +1106,6 @@ static bool collectStatisticalData() { } void MPEG4Writer::Track::threadEntry() { sp<MetaData> meta = mSource->getFormat(); const char *mime; meta->findCString(kKeyMIMEType, &mime); bool is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC); bool is_avc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); bool is_audio = !strncasecmp(mime, "audio/", 6); int32_t count = 0; const int64_t interleaveDurationUs = mOwner->interleaveDuration(); int64_t chunkTimestampUs = 0; Loading @@ -943,10 +1116,12 @@ void MPEG4Writer::Track::threadEntry() { int32_t sampleCount = 1; // Sample count in the current stts table entry uint32_t previousSampleSize = 0; // Size of the previous sample int64_t previousPausedDurationUs = 0; int64_t timestampUs; sp<MetaData> meta_data; bool collectStats = collectStatisticalData(); mNumSamples = 0; mMaxWriteTimeUs = 0; status_t err = OK; MediaBuffer *buffer; while (!mDone && (err = mSource->read(&buffer)) == OK) { Loading @@ -973,13 +1148,13 @@ void MPEG4Writer::Track::threadEntry() { && isCodecConfig) { CHECK(!mGotAllCodecSpecificData); if (is_avc) { if (mIsAvc) { status_t err = makeAVCCodecSpecificData( (const uint8_t *)buffer->data() + buffer->range_offset(), buffer->range_length()); CHECK_EQ(OK, err); } else if (is_mpeg4) { } else if (mIsMPEG4) { mCodecSpecificDataSize = buffer->range_length(); mCodecSpecificData = malloc(mCodecSpecificDataSize); memcpy(mCodecSpecificData, Loading @@ -994,7 +1169,7 @@ void MPEG4Writer::Track::threadEntry() { mGotAllCodecSpecificData = true; continue; } else if (!mGotAllCodecSpecificData && count == 1 && is_mpeg4 && mCodecSpecificData == NULL) { count == 1 && mIsMPEG4 && mCodecSpecificData == NULL) { // The TI mpeg4 encoder does not properly set the // codec-specific-data flag. Loading Loading @@ -1034,7 +1209,7 @@ void MPEG4Writer::Track::threadEntry() { } mGotAllCodecSpecificData = true; } else if (!mGotAllCodecSpecificData && is_avc && count < 3) { } else if (!mGotAllCodecSpecificData && mIsAvc && count < 3) { // The TI video encoder does not flag codec specific data // as such and also splits up SPS and PPS across two buffers. Loading Loading @@ -1090,10 +1265,10 @@ void MPEG4Writer::Track::threadEntry() { buffer->release(); buffer = NULL; if (is_avc) StripStartcode(copy); if (mIsAvc) StripStartcode(copy); size_t sampleSize; sampleSize = is_avc sampleSize = mIsAvc #if USE_NALLEN_FOUR ? copy->range_length() + 4 #else Loading @@ -1116,7 +1291,6 @@ void MPEG4Writer::Track::threadEntry() { int32_t isSync = false; meta_data->findInt32(kKeyIsSyncFrame, &isSync); int64_t timestampUs; CHECK(meta_data->findInt64(kKeyTime, ×tampUs)); //////////////////////////////////////////////////////////////////////////////// Loading Loading @@ -1168,7 +1342,7 @@ void MPEG4Writer::Track::threadEntry() { trackProgressStatus(timestampUs); } if (mOwner->numTracks() == 1) { off_t offset = is_avc? mOwner->addLengthPrefixedSample_l(copy) off_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy) : mOwner->addSample_l(copy); if (mChunkOffsets.empty()) { mChunkOffsets.push_back(offset); Loading @@ -1182,7 +1356,7 @@ void MPEG4Writer::Track::threadEntry() { if (interleaveDurationUs == 0) { StscTableEntry stscEntry(++nChunks, 1, 1); mStscTableEntries.push_back(stscEntry); writeOneChunk(is_avc); bufferChunk(timestampUs); } else { if (chunkTimestampUs == 0) { chunkTimestampUs = timestampUs; Loading @@ -1199,7 +1373,7 @@ void MPEG4Writer::Track::threadEntry() { mChunkSamples.size(), 1); mStscTableEntries.push_back(stscEntry); } writeOneChunk(is_avc); bufferChunk(timestampUs); chunkTimestampUs = timestampUs; } } Loading @@ -1220,7 +1394,7 @@ void MPEG4Writer::Track::threadEntry() { ++nChunks; StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1); mStscTableEntries.push_back(stscEntry); writeOneChunk(is_avc); bufferChunk(timestampUs); } // We don't really know how long the last frame lasts, since Loading @@ -1234,10 +1408,10 @@ void MPEG4Writer::Track::threadEntry() { SttsTableEntry sttsEntry(sampleCount, lastDurationUs); mSttsTableEntries.push_back(sttsEntry); mReachedEOS = true; LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames - %s", count, nZeroLengthFrames, mNumSamples, is_audio? "audio": "video"); LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. Max write time: %lld us - %s", count, nZeroLengthFrames, mNumSamples, mMaxWriteTimeUs, mIsAudio? "audio": "video"); logStatisticalData(is_audio); logStatisticalData(mIsAudio); } void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) { Loading Loading @@ -1380,24 +1554,17 @@ void MPEG4Writer::Track::logStatisticalData(bool isAudio) { } } 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); } void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) { LOGV("bufferChunk"); int64_t startTimeUs = systemTime() / 1000; Chunk chunk(this, timestampUs, mChunkSamples); mOwner->bufferChunk(chunk); mChunkSamples.clear(); int64_t endTimeUs = systemTime() / 1000; if (mMaxWriteTimeUs < endTimeUs - startTimeUs) { mMaxWriteTimeUs = endTimeUs - startTimeUs; } } int64_t MPEG4Writer::Track::getDurationUs() const { Loading @@ -1414,9 +1581,8 @@ void MPEG4Writer::Track::writeTrackHeader( bool success = mMeta->findCString(kKeyMIMEType, &mime); CHECK(success); bool is_audio = !strncasecmp(mime, "audio/", 6); LOGV("%s track time scale: %d", is_audio? "Audio": "Video", mTimeScale); mIsAudio? "Audio": "Video", mTimeScale); time_t now = time(NULL); Loading @@ -1440,7 +1606,7 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->writeInt32(0); // reserved mOwner->writeInt16(0); // layer mOwner->writeInt16(0); // alternate group mOwner->writeInt16(is_audio ? 0x100 : 0); // volume mOwner->writeInt16(mIsAudio ? 0x100 : 0); // volume mOwner->writeInt16(0); // reserved mOwner->writeInt32(0x10000); // matrix Loading @@ -1453,7 +1619,7 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->writeInt32(0); mOwner->writeInt32(0x40000000); if (is_audio) { if (mIsAudio) { mOwner->writeInt32(0); mOwner->writeInt32(0); } else { Loading Loading @@ -1511,16 +1677,16 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->beginBox("hdlr"); mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt32(0); // component type: should be mhlr mOwner->writeFourcc(is_audio ? "soun" : "vide"); // component subtype mOwner->writeFourcc(mIsAudio ? "soun" : "vide"); // component subtype mOwner->writeInt32(0); // reserved mOwner->writeInt32(0); // reserved mOwner->writeInt32(0); // reserved // Removing "r" for the name string just makes the string 4 byte aligned mOwner->writeCString(is_audio ? "SoundHandle": "VideoHandle"); // name mOwner->writeCString(mIsAudio ? "SoundHandle": "VideoHandle"); // name mOwner->endBox(); mOwner->beginBox("minf"); if (is_audio) { if (mIsAudio) { mOwner->beginBox("smhd"); mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt16(0); // balance Loading Loading @@ -1553,7 +1719,7 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->beginBox("stsd"); mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt32(1); // entry count if (is_audio) { if (mIsAudio) { const char *fourcc = NULL; if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) { fourcc = "samr"; Loading Loading @@ -1735,7 +1901,7 @@ void MPEG4Writer::Track::writeTrackHeader( } mOwner->endBox(); // stts if (!is_audio) { if (!mIsAudio) { mOwner->beginBox("stss"); mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt32(mStssTableEntries.size()); // number of sync frames Loading