diff options
Diffstat (limited to 'media/libstagefright/MPEG4Writer.cpp')
-rw-r--r-- | media/libstagefright/MPEG4Writer.cpp | 161 |
1 files changed, 115 insertions, 46 deletions
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 47f114a..2f1b6ac 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -38,10 +38,11 @@ #include <media/stagefright/MediaSource.h> #include <media/stagefright/Utils.h> #include <media/mediarecorder.h> +#include <stagefright/AVExtensions.h> #include <cutils/properties.h> #include "include/ESDS.h" - +#include <stagefright/AVExtensions.h> #ifndef __predict_false #define __predict_false(exp) __builtin_expect((exp) != 0, 0) @@ -91,6 +92,7 @@ public: bool isAvc() const { return mIsAvc; } bool isAudio() const { return mIsAudio; } bool isMPEG4() const { return mIsMPEG4; } + bool isHEVC() const { return mIsHEVC; } void addChunkOffset(off64_t offset); int32_t getTrackId() const { return mTrackId; } status_t dump(int fd, const Vector<String16>& args) const; @@ -236,6 +238,7 @@ private: bool mIsAvc; bool mIsAudio; bool mIsMPEG4; + bool mIsHEVC; int32_t mTrackId; int64_t mTrackDurationUs; int64_t mMaxChunkDurationUs; @@ -318,6 +321,7 @@ private: // Simple validation on the codec specific data status_t checkCodecSpecificData() const; int32_t mRotation; + int32_t mHFRRatio; void updateTrackSizeEstimate(); void addOneStscTableEntry(size_t chunkId, size_t sampleId); @@ -385,7 +389,10 @@ MPEG4Writer::MPEG4Writer(int fd) mLongitudex10000(0), mAreGeoTagsAvailable(false), mStartTimeOffsetMs(-1), - mMetaKeys(new AMessage()) { + mMetaKeys(new AMessage()), + mIsVideoHEVC(false), + mIsAudioAMR(false), + mHFRRatio(1) { addDeviceMeta(); // Verify mFd is seekable @@ -463,6 +470,8 @@ const char *MPEG4Writer::Track::getFourCCForMime(const char *mime) { return "s263"; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { return "avc1"; + } else { + return AVUtils::get()->HEVCMuxerUtils().getFourCCForMime(mime); } } else { ALOGE("Track (%s) other than video or audio is not supported", mime); @@ -488,10 +497,23 @@ status_t MPEG4Writer::addSource(const sp<MediaSource> &source) { const char *mime; source->getFormat()->findCString(kKeyMIMEType, &mime); bool isAudio = !strncasecmp(mime, "audio/", 6); + bool isVideo = !strncasecmp(mime, "video/", 6); if (Track::getFourCCForMime(mime) == NULL) { ALOGE("Unsupported mime '%s'", mime); return ERROR_UNSUPPORTED; } + mIsAudioAMR = isAudio && (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime) || + !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)); + + + if (isVideo) { + mIsVideoHEVC = AVUtils::get()->HEVCMuxerUtils().isVideoHEVC(mime); + } + + if (isAudio && !AVUtils::get()->isAudioMuxFormatSupported(mime)) { + ALOGE("Muxing is not supported for %s", mime); + return ERROR_UNSUPPORTED; + } // At this point, we know the track to be added is either // video or audio. Thus, we only need to check whether it @@ -512,6 +534,8 @@ status_t MPEG4Writer::addSource(const sp<MediaSource> &source) { Track *track = new Track(this, source, 1 + mTracks.size()); mTracks.push_back(track); + mHFRRatio = AVUtils::get()->HFRUtils().getHFRRatio(source->getFormat()); + return OK; } @@ -580,7 +604,7 @@ int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) { // If the estimation is wrong, we will pay the price of wasting // some reserved space. This should not happen so often statistically. - static const int32_t factor = mUse32BitOffset? 1: 2; + int32_t factor = mUse32BitOffset? 1: 2; static const int64_t MIN_MOOV_BOX_SIZE = 3 * 1024; // 3 KB static const int64_t MAX_MOOV_BOX_SIZE = (180 * 3000000 * 6LL / 8000); int64_t size = MIN_MOOV_BOX_SIZE; @@ -677,8 +701,6 @@ status_t MPEG4Writer::start(MetaData *param) { mIsRealTimeRecording = isRealTimeRecording; } - mStartTimestampUs = -1; - if (mStarted) { if (mPaused) { mPaused = false; @@ -687,6 +709,8 @@ status_t MPEG4Writer::start(MetaData *param) { return OK; } + mStartTimestampUs = -1; + if (!param || !param->findInt32(kKeyTimeScale, &mTimeScale)) { mTimeScale = 1000; @@ -1009,7 +1033,7 @@ void MPEG4Writer::writeMvhdBox(int64_t durationUs) { writeInt32(0); // version=0, flags=0 writeInt32(now); // creation time writeInt32(now); // modification time - writeInt32(mTimeScale); // mvhd timescale + writeInt32(mTimeScale / mHFRRatio); // mvhd timescale int32_t duration = (durationUs * mTimeScale + 5E5) / 1E6; writeInt32(duration); writeInt32(0x10000); // rate: 1.0 @@ -1047,8 +1071,10 @@ void MPEG4Writer::writeFtypBox(MetaData *param) { beginBox("ftyp"); int32_t fileType; - if (param && param->findInt32(kKeyFileType, &fileType) && - fileType != OUTPUT_FORMAT_MPEG_4) { + if (mIsVideoHEVC) { + AVUtils::get()->HEVCMuxerUtils().writeHEVCFtypBox(this); + } else if (mIsAudioAMR || (param && param->findInt32(kKeyFileType, &fileType) && + fileType != OUTPUT_FORMAT_MPEG_4)) { writeFourcc("3gp4"); writeInt32(0); writeFourcc("isom"); @@ -1455,7 +1481,8 @@ MPEG4Writer::Track::Track( mCodecSpecificDataSize(0), mGotAllCodecSpecificData(false), mReachedEOS(false), - mRotation(0) { + mRotation(0), + mHFRRatio(1) { getCodecSpecificDataFromInputFormatIfPossible(); const char *mime; @@ -1464,6 +1491,12 @@ MPEG4Writer::Track::Track( mIsAudio = !strncasecmp(mime, "audio/", 6); mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC); + mIsHEVC = AVUtils::get()->HEVCMuxerUtils().isVideoHEVC(mime); + + if (mIsHEVC) { + AVUtils::get()->HEVCMuxerUtils().getHEVCCodecSpecificDataFromInputFormatIfPossible( + mMeta, &mCodecSpecificData, &mCodecSpecificDataSize, &mGotAllCodecSpecificData); + } setTimeScale(); } @@ -1562,6 +1595,7 @@ void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() { size_t size; if (mMeta->findData(kKeyAVCC, &type, &data, &size)) { mCodecSpecificData = malloc(size); + CHECK(mCodecSpecificData != NULL); mCodecSpecificDataSize = size; memcpy(mCodecSpecificData, data, size); mGotAllCodecSpecificData = true; @@ -1575,6 +1609,7 @@ void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() { ESDS esds(data, size); if (esds.getCodecSpecificInfo(&data, &size) == OK) { mCodecSpecificData = malloc(size); + CHECK(mCodecSpecificData != NULL); mCodecSpecificDataSize = size; memcpy(mCodecSpecificData, data, size); mGotAllCodecSpecificData = true; @@ -1657,7 +1692,7 @@ void MPEG4Writer::writeChunkToFile(Chunk* chunk) { while (!chunk->mSamples.empty()) { List<MediaBuffer *>::iterator it = chunk->mSamples.begin(); - off64_t offset = chunk->mTrack->isAvc() + off64_t offset = (chunk->mTrack->isAvc() || chunk->mTrack->isHEVC()) ? addLengthPrefixedSample_l(*it) : addSample_l(*it); @@ -1852,6 +1887,8 @@ status_t MPEG4Writer::Track::start(MetaData *params) { pthread_create(&mThread, &attr, ThreadWrapper, this); pthread_attr_destroy(&attr); + mHFRRatio = AVUtils::get()->HFRUtils().getHFRRatio(mMeta); + return OK; } @@ -1881,6 +1918,10 @@ status_t MPEG4Writer::Track::stop() { status_t err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy)); ALOGD("%s track stopped", mIsAudio? "Audio": "Video"); + if (mOwner->exceedsFileSizeLimit() && mStszTableEntries->count() == 0) { + ALOGE(" Filesize limit exceeded and zero samples written "); + return ERROR_END_OF_STREAM; + } return err; } @@ -1971,6 +2012,7 @@ status_t MPEG4Writer::Track::copyAVCCodecSpecificData( mCodecSpecificDataSize = size; mCodecSpecificData = malloc(size); + CHECK(mCodecSpecificData != NULL); memcpy(mCodecSpecificData, data, size); return OK; } @@ -2093,6 +2135,7 @@ status_t MPEG4Writer::Track::makeAVCCodecSpecificData( // ISO 14496-15: AVC file format mCodecSpecificDataSize += 7; // 7 more bytes in the header mCodecSpecificData = malloc(mCodecSpecificDataSize); + CHECK(mCodecSpecificData != NULL); uint8_t *header = (uint8_t *)mCodecSpecificData; header[0] = 1; // version header[1] = mProfileIdc; // profile indication @@ -2195,7 +2238,9 @@ status_t MPEG4Writer::Track::threadEntry() { MediaBuffer *buffer; const char *trackName = mIsAudio ? "Audio" : "Video"; while (!mDone && (err = mSource->read(&buffer)) == OK) { - if (buffer->range_length() == 0) { + if (buffer == NULL) { + continue; + } else if (buffer->range_length() == 0) { buffer->release(); buffer = NULL; ++nZeroLengthFrames; @@ -2227,10 +2272,18 @@ status_t MPEG4Writer::Track::threadEntry() { } else if (mIsMPEG4) { mCodecSpecificDataSize = buffer->range_length(); mCodecSpecificData = malloc(mCodecSpecificDataSize); + CHECK(mCodecSpecificData != NULL); memcpy(mCodecSpecificData, (const uint8_t *)buffer->data() + buffer->range_offset(), buffer->range_length()); + } else if (mIsHEVC) { + status_t err = AVUtils::get()->HEVCMuxerUtils().makeHEVCCodecSpecificData( + (const uint8_t *)buffer->data() + buffer->range_offset(), + buffer->range_length(), &mCodecSpecificData, &mCodecSpecificDataSize); + if ((status_t)OK != err) { + return err; + } } buffer->release(); @@ -2240,20 +2293,28 @@ status_t MPEG4Writer::Track::threadEntry() { continue; } - // Make a deep copy of the MediaBuffer and Metadata and release - // the original as soon as we can - 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()); - meta_data = new MetaData(*buffer->meta_data().get()); - buffer->release(); - buffer = NULL; + MediaBuffer *copy = NULL; + // Check if the upstream source hints it is OK to hold on to the + // buffer without releasing immediately and avoid cloning the buffer + if (AVUtils::get()->canDeferRelease(buffer->meta_data())) { + copy = buffer; + meta_data = new MetaData(*buffer->meta_data().get()); + } else { + // Make a deep copy of the MediaBuffer and Metadata and release + // the original as soon as we can + 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()); + meta_data = new MetaData(*buffer->meta_data().get()); + buffer->release(); + buffer = NULL; + } - if (mIsAvc) StripStartcode(copy); + if (mIsAvc || mIsHEVC) StripStartcode(copy); size_t sampleSize = copy->range_length(); - if (mIsAvc) { + if (mIsAvc || mIsHEVC) { if (mOwner->useNalLengthFour()) { sampleSize += 4; } else { @@ -2287,22 +2348,11 @@ status_t MPEG4Writer::Track::threadEntry() { previousPausedDurationUs = mStartTimestampUs; } +#if 0 if (mResumed) { - int64_t durExcludingEarlierPausesUs = timestampUs - previousPausedDurationUs; - if (WARN_UNLESS(durExcludingEarlierPausesUs >= 0ll, "for %s track", trackName)) { - copy->release(); - return ERROR_MALFORMED; - } - - int64_t pausedDurationUs = durExcludingEarlierPausesUs - mTrackDurationUs; - if (WARN_UNLESS(pausedDurationUs >= lastDurationUs, "for %s track", trackName)) { - copy->release(); - return ERROR_MALFORMED; - } - - previousPausedDurationUs += pausedDurationUs - lastDurationUs; mResumed = false; } +#endif timestampUs -= previousPausedDurationUs; if (WARN_UNLESS(timestampUs >= 0ll, "for %s track", trackName)) { @@ -2320,8 +2370,8 @@ status_t MPEG4Writer::Track::threadEntry() { CHECK(meta_data->findInt64(kKeyDecodingTime, &decodingTimeUs)); decodingTimeUs -= previousPausedDurationUs; cttsOffsetTimeUs = - timestampUs + kMaxCttsOffsetTimeUs - decodingTimeUs; - if (WARN_UNLESS(cttsOffsetTimeUs >= 0ll, "for %s track", trackName)) { + timestampUs - decodingTimeUs; + if (WARN_UNLESS(kMaxCttsOffsetTimeUs >= decodingTimeUs - timestampUs, "for %s track", trackName)) { copy->release(); return ERROR_MALFORMED; } @@ -2453,7 +2503,7 @@ status_t MPEG4Writer::Track::threadEntry() { trackProgressStatus(timestampUs); } if (!hasMultipleTracks) { - off64_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy) + off64_t offset = (mIsAvc || mIsHEVC)? mOwner->addLengthPrefixedSample_l(copy) : mOwner->addSample_l(copy); uint32_t count = (mOwner->use32BitFileOffset() @@ -2546,8 +2596,13 @@ status_t MPEG4Writer::Track::threadEntry() { if (mIsAudio) { ALOGI("Audio track drift time: %" PRId64 " us", mOwner->getDriftTimeUs()); } - - if (err == ERROR_END_OF_STREAM) { + // if err is ERROR_IO (ex: during SSR), return OK to save the + // recorded file successfully. Session tear down will happen as part of + // client callback + if (mIsAudio && (err == ERROR_IO)) { + return OK; + } + else if (err == ERROR_END_OF_STREAM) { return OK; } return err; @@ -2705,7 +2760,8 @@ status_t MPEG4Writer::Track::checkCodecSpecificData() const { CHECK(mMeta->findCString(kKeyMIMEType, &mime)); if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime) || !strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) || - !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { + !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime) || + mIsHEVC) { if (!mCodecSpecificData || mCodecSpecificDataSize <= 0) { ALOGE("Missing codec specific data"); @@ -2726,6 +2782,11 @@ void MPEG4Writer::Track::writeTrackHeader(bool use32BitOffset) { ALOGV("%s track time scale: %d", mIsAudio? "Audio": "Video", mTimeScale); + if (mMdatSizeBytes == 0) { + ALOGW("Track data is not available."); + return; + } + uint32_t now = getMpeg4Time(); mOwner->beginBox("trak"); writeTkhdBox(now); @@ -2803,17 +2864,22 @@ void MPEG4Writer::Track::writeVideoFourCCBox() { mOwner->writeInt16(0x18); // depth mOwner->writeInt16(-1); // predefined - CHECK_LT(23 + mCodecSpecificDataSize, 128); - if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { writeMp4vEsdsBox(); } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { writeD263Box(); } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { writeAvccBox(); + } else if (mIsHEVC) { + AVUtils::get()->HEVCMuxerUtils().writeHvccBox(mOwner, mCodecSpecificData, + mCodecSpecificDataSize, + mOwner->useNalLengthFour()); + } + + if (!mIsHEVC) { + writePaspBox(); } - writePaspBox(); mOwner->endBox(); // mp4v, s263 or avc1 } @@ -2898,6 +2964,9 @@ void MPEG4Writer::Track::writeMp4vEsdsBox() { CHECK_GT(mCodecSpecificDataSize, 0); mOwner->beginBox("esds"); + // Make sure all sizes encode to a single byte. + CHECK_LT(mCodecSpecificDataSize + 23, 128); + mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt8(0x03); // ES_DescrTag @@ -3007,7 +3076,7 @@ void MPEG4Writer::Track::writeMdhdBox(uint32_t now) { mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt32(now); // creation time mOwner->writeInt32(now); // modification time - mOwner->writeInt32(mTimeScale); // media timescale + mOwner->writeInt32(mTimeScale / mHFRRatio); // media timescale int32_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6; mOwner->writeInt32(mdhdDuration); // use media timescale // Language follows the three letter standard ISO-639-2/T @@ -3086,7 +3155,7 @@ void MPEG4Writer::Track::writePaspBox() { int32_t MPEG4Writer::Track::getStartTimeOffsetScaledTime() const { int64_t trackStartTimeOffsetUs = 0; int64_t moovStartTimeUs = mOwner->getStartTimestampUs(); - if (mStartTimestampUs != moovStartTimeUs) { + if (mStartTimestampUs != moovStartTimeUs && mStszTableEntries->count() != 0) { CHECK_GT(mStartTimestampUs, moovStartTimeUs); trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs; } |