Loading media/libmediaplayerservice/StagefrightRecorder.cpp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -2011,6 +2011,7 @@ void StagefrightRecorder::setupMPEG4orWEBMMetaData(sp<MetaData> *meta) { } } if (mOutputFormat == OUTPUT_FORMAT_MPEG_4 || mOutputFormat == OUTPUT_FORMAT_THREE_GPP) { if (mOutputFormat == OUTPUT_FORMAT_MPEG_4 || mOutputFormat == OUTPUT_FORMAT_THREE_GPP) { (*meta)->setInt32(kKeyEmptyTrackMalFormed, true); (*meta)->setInt32(kKeyEmptyTrackMalFormed, true); (*meta)->setInt32(kKey4BitTrackIds, true); } } } } Loading media/libstagefright/HevcUtils.cpp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -83,6 +83,7 @@ status_t HevcParameterSets::addNalUnit(const uint8_t* data, size_t size) { } } if (err != OK) { if (err != OK) { ALOGE("error parsing VPS or SPS or PPS"); return err; return err; } } Loading media/libstagefright/MPEG4Writer.cpp +146 −61 Original line number Original line Diff line number Diff line Loading @@ -103,8 +103,40 @@ static const uint8_t kHevcNalUnitTypes[5] = { //#define SHOW_MODEL_BUILD 1 //#define SHOW_MODEL_BUILD 1 class MPEG4Writer::Track { class MPEG4Writer::Track { struct TrackId { TrackId(uint32_t aId) :mId(aId), mTrackIdValid(false) { } bool isValid(bool akKey4BitTrackIds) { // trackId cannot be zero, ISO/IEC 14496-12 8.3.2.3 if (mId == 0) { return false; } /* MediaRecorder uses only 4 bit to represent track ids during notifying clients. * MediaMuxer's track ids are restricted by container allowed size only. * MPEG4 Container defines unsigned int (32), ISO/IEC 14496-12 8.3.2.2 */ if (akKey4BitTrackIds && mId > 15) { return false; } mTrackIdValid = true; return true; } uint32_t getId() const { CHECK(mTrackIdValid); return mId; } TrackId() = delete; DISALLOW_EVIL_CONSTRUCTORS(TrackId); private: // unsigned int (32), ISO/IEC 14496-12 8.3.2.2 uint32_t mId; bool mTrackIdValid; }; public: public: Track(MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId); Track(MPEG4Writer *owner, const sp<MediaSource> &source, uint32_t aTrackId); ~Track(); ~Track(); Loading @@ -129,7 +161,7 @@ public: void addChunkOffset(off64_t offset); void addChunkOffset(off64_t offset); void addItemOffsetAndSize(off64_t offset, size_t size, bool isExif); void addItemOffsetAndSize(off64_t offset, size_t size, bool isExif); void flushItemRefs(); void flushItemRefs(); int32_t getTrackId() const { return mTrackId; } TrackId& getTrackId() { return mTrackId; } status_t dump(int fd, const Vector<String16>& args) const; status_t dump(int fd, const Vector<String16>& args) const; static const char *getFourCCForMime(const char *mime); static const char *getFourCCForMime(const char *mime); const char *getTrackType() const; const char *getTrackType() const; Loading Loading @@ -290,7 +322,7 @@ private: bool mIsMPEG4; bool mIsMPEG4; bool mGotStartKeyFrame; bool mGotStartKeyFrame; bool mIsMalformed; bool mIsMalformed; int32_t mTrackId; TrackId mTrackId; int64_t mTrackDurationUs; int64_t mTrackDurationUs; int64_t mMaxChunkDurationUs; int64_t mMaxChunkDurationUs; int64_t mLastDecodingTimeUs; int64_t mLastDecodingTimeUs; Loading Loading @@ -413,7 +445,7 @@ private: void addOneElstTableEntry(uint32_t segmentDuration, int32_t mediaTime, void addOneElstTableEntry(uint32_t segmentDuration, int32_t mediaTime, int16_t mediaRate, int16_t mediaRateFraction); int16_t mediaRate, int16_t mediaRateFraction); bool isTrackMalFormed() const; bool isTrackMalFormed(); void sendTrackSummary(bool hasMultipleTracks); void sendTrackSummary(bool hasMultipleTracks); // Write the boxes // Write the boxes Loading Loading @@ -534,7 +566,7 @@ void MPEG4Writer::initInternal(int fd, bool isFirstSession) { release(); release(); } } if (fallocate(mFd, 0, 0, 1) == 0) { if (fallocate(mFd, FALLOC_FL_KEEP_SIZE, 0, 1) == 0) { ALOGD("PreAllocation enabled"); ALOGD("PreAllocation enabled"); mPreAllocationEnabled = true; mPreAllocationEnabled = true; } else { } else { Loading Loading @@ -744,8 +776,7 @@ int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) { // where 1MB is the common file size limit for MMS application. // where 1MB is the common file size limit for MMS application. // The default MAX _MOOV_BOX_SIZE value is based on about 3 // The default MAX _MOOV_BOX_SIZE value is based on about 3 // minute video recording with a bit rate about 3 Mbps, because // minute video recording with a bit rate about 3 Mbps, because // statistics also show that most of the video captured are going // statistics show that most captured videos are less than 3 minutes. // to be less than 3 minutes. // If the estimation is wrong, we will pay the price of wasting // If the estimation is wrong, we will pay the price of wasting // some reserved space. This should not happen so often statistically. // some reserved space. This should not happen so often statistically. Loading Loading @@ -796,6 +827,15 @@ int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) { return size; return size; } } status_t MPEG4Writer::validateAllTracksId(bool akKey4BitTrackIds) { for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it) { if (!(*it)->getTrackId().isValid(akKey4BitTrackIds)) { return BAD_VALUE; } } return OK; } status_t MPEG4Writer::start(MetaData *param) { status_t MPEG4Writer::start(MetaData *param) { if (mInitCheck != OK) { if (mInitCheck != OK) { return UNKNOWN_ERROR; return UNKNOWN_ERROR; Loading @@ -810,6 +850,9 @@ status_t MPEG4Writer::start(MetaData *param) { mIsFileSizeLimitExplicitlyRequested = true; mIsFileSizeLimitExplicitlyRequested = true; } } /* mMaxFileSizeLimitBytes has to be set everytime fd is switched, hence the following code is * appropriate in start() method. */ int32_t fileSizeBits = fpathconf(mFd, _PC_FILESIZEBITS); int32_t fileSizeBits = fpathconf(mFd, _PC_FILESIZEBITS); ALOGD("fpathconf _PC_FILESIZEBITS:%" PRId32, fileSizeBits); ALOGD("fpathconf _PC_FILESIZEBITS:%" PRId32, fileSizeBits); fileSizeBits = std::min(fileSizeBits, 52 /* cap it below 4 peta bytes */); fileSizeBits = std::min(fileSizeBits, 52 /* cap it below 4 peta bytes */); Loading Loading @@ -944,6 +987,17 @@ status_t MPEG4Writer::start(MetaData *param) { setupAndStartLooper(); setupAndStartLooper(); int32_t is4bitTrackId = false; if (param && param->findInt32(kKey4BitTrackIds, &is4bitTrackId) && is4bitTrackId) { err = validateAllTracksId(true); } else { err = validateAllTracksId(false); } if (err != OK) { return err; } err = startTracks(param); err = startTracks(param); if (err != OK) { if (err != OK) { return err; return err; Loading @@ -961,6 +1015,7 @@ status_t MPEG4Writer::pause() { status_t MPEG4Writer::stopWriterThread() { status_t MPEG4Writer::stopWriterThread() { ALOGV("Stopping writer thread"); ALOGV("Stopping writer thread"); if (!mWriterThreadStarted) { if (!mWriterThreadStarted) { ALOGD("Writer thread not started"); return OK; return OK; } } { { Loading @@ -975,7 +1030,8 @@ status_t MPEG4Writer::stopWriterThread() { err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy)); err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy)); mWriterThreadStarted = false; mWriterThreadStarted = false; WARN_UNLESS(err == 0, "stopWriterThread pthread_join retVal: %d, writer thread stopped", err); WARN_UNLESS(err == 0, "stopWriterThread pthread_join retVal: %d", err); ALOGD("Writer thread stopped"); return err; return err; } } Loading Loading @@ -1031,26 +1087,32 @@ void MPEG4Writer::writeCompositionMatrix(int degrees) { writeInt32(0x40000000); // w writeInt32(0x40000000); // w } } void MPEG4Writer::release() { status_t MPEG4Writer::release() { ALOGD("release()"); ALOGD("release()"); if (mPreAllocationEnabled) { if (mPreAllocationEnabled) { truncatePreAllocation(); truncatePreAllocation(); } } int err = OK; int retVal = fsync(mFd); int retVal = fsync(mFd); WARN_UNLESS(retVal == 0, "fsync retVal:%d", retVal); WARN_UNLESS(retVal == 0, "fsync err:%s(%d)", std::strerror(errno), errno); err |= retVal; retVal = close(mFd); retVal = close(mFd); WARN_UNLESS(retVal == 0, "close mFd retVal :%d", retVal); WARN_UNLESS(retVal == 0, "close err:%s(%d)", std::strerror(errno), errno); err |= retVal; mFd = -1; mFd = -1; if (mNextFd != -1) { if (mNextFd != -1) { retVal = close(mNextFd); retVal = close(mNextFd); mNextFd = -1; mNextFd = -1; WARN_UNLESS(retVal == 0, "close mNextFd retVal :%d", retVal); WARN_UNLESS(retVal == 0, "close mNextFd error:%s(%d)", std::strerror(errno), errno); err |= retVal; } } stopAndReleaseLooper(); stopAndReleaseLooper(); mInitCheck = NO_INIT; mInitCheck = NO_INIT; mStarted = false; mStarted = false; free(mInMemoryCache); free(mInMemoryCache); mInMemoryCache = NULL; mInMemoryCache = NULL; return err; } } void MPEG4Writer::finishCurrentSession() { void MPEG4Writer::finishCurrentSession() { Loading @@ -1065,7 +1127,7 @@ status_t MPEG4Writer::switchFd() { } } if (mNextFd == -1) { if (mNextFd == -1) { ALOGW("No FileDescripter for next recording"); ALOGW("No FileDescriptor for next recording"); return INVALID_OPERATION; return INVALID_OPERATION; } } Loading @@ -1084,12 +1146,15 @@ status_t MPEG4Writer::reset(bool stopSource) { } else { } else { if (!mWriterThreadStarted || if (!mWriterThreadStarted || !mStarted) { !mStarted) { status_t err = OK; status_t writerErr = OK; if (mWriterThreadStarted) { if (mWriterThreadStarted) { err = stopWriterThread(); writerErr = stopWriterThread(); } } release(); status_t retErr = release(); return err; if (writerErr != OK) { retErr = writerErr; } return retErr; } } } } Loading @@ -1100,8 +1165,9 @@ status_t MPEG4Writer::reset(bool stopSource) { for (List<Track *>::iterator it = mTracks.begin(); for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it) { it != mTracks.end(); ++it) { status_t trackErr = (*it)->stop(stopSource); status_t trackErr = (*it)->stop(stopSource); WARN_UNLESS(trackErr == 0, "%s track stopped with an error", (*it)->getTrackType()); if (err == OK && trackErr != OK) { if (err == OK && trackErr != OK) { ALOGW("%s track stopped with an error", (*it)->getTrackType()); err = trackErr; err = trackErr; } } Loading @@ -1118,7 +1184,6 @@ status_t MPEG4Writer::reset(bool stopSource) { } } } } if (nonImageTrackCount > 1) { if (nonImageTrackCount > 1) { ALOGD("Duration from tracks range is [%" PRId64 ", %" PRId64 "] us", ALOGD("Duration from tracks range is [%" PRId64 ", %" PRId64 "] us", minDurationUs, maxDurationUs); minDurationUs, maxDurationUs); Loading @@ -1126,13 +1191,15 @@ status_t MPEG4Writer::reset(bool stopSource) { status_t writerErr = stopWriterThread(); status_t writerErr = stopWriterThread(); // TODO: which error to propagage, writerErr or trackErr? // Propagating writer error if (err == OK && writerErr != OK) { if (err == OK && writerErr != OK) { err = writerErr; err = writerErr; } } // Do not write out movie header on error except malformed track. // Do not write out movie header on error except malformed track. // TODO: Remove samples of malformed tracks added in mdat. if (err != OK && err != ERROR_MALFORMED) { if (err != OK && err != ERROR_MALFORMED) { // Ignoring release() return value as there was an "err" already. release(); release(); return err; return err; } } Loading Loading @@ -1174,6 +1241,7 @@ status_t MPEG4Writer::reset(bool stopSource) { } else { } else { ALOGI("The mp4 file will not be streamable."); ALOGI("The mp4 file will not be streamable."); } } ALOGI("MOOV atom was written to the file"); } } mWriteBoxToMemory = false; mWriteBoxToMemory = false; Loading @@ -1186,7 +1254,7 @@ status_t MPEG4Writer::reset(bool stopSource) { CHECK(mBoxes.empty()); CHECK(mBoxes.empty()); release(); err = release(); return err; return err; } } Loading Loading @@ -1354,7 +1422,7 @@ void MPEG4Writer::sendSessionSummary() { for (List<ChunkInfo>::iterator it = mChunkInfos.begin(); for (List<ChunkInfo>::iterator it = mChunkInfos.begin(); it != mChunkInfos.end(); ++it) { it != mChunkInfos.end(); ++it) { int trackNum = it->mTrack->getTrackId() << 28; uint32_t trackNum = (it->mTrack->getTrackId().getId() << 28); notify(MEDIA_RECORDER_TRACK_EVENT_INFO, notify(MEDIA_RECORDER_TRACK_EVENT_INFO, trackNum | MEDIA_RECORDER_TRACK_INTER_CHUNK_TIME_MS, trackNum | MEDIA_RECORDER_TRACK_INTER_CHUNK_TIME_MS, it->mMaxInterChunkDurUs); it->mMaxInterChunkDurUs); Loading Loading @@ -1512,7 +1580,8 @@ void MPEG4Writer::writeOrPostError(int fd, const void* buf, size_t count) { std::strerror(errno), errno); std::strerror(errno), errno); // Can't guarantee that file is usable or write would succeed anymore, hence signal to stop. // Can't guarantee that file is usable or write would succeed anymore, hence signal to stop. sp<AMessage> msg = new AMessage(kWhatHandleIOError, mReflector); sp<AMessage> msg = new AMessage(kWhatIOError, mReflector); msg->setInt32("errno", errno); status_t err = msg->post(); status_t err = msg->post(); ALOGE("writeOrPostError post:%d", err); ALOGE("writeOrPostError post:%d", err); } } Loading @@ -1531,7 +1600,8 @@ void MPEG4Writer::seekOrPostError(int fd, off64_t offset, int whence) { offset, std::strerror(errno), errno); offset, std::strerror(errno), errno); // Can't guarantee that file is usable or seek would succeed anymore, hence signal to stop. // Can't guarantee that file is usable or seek would succeed anymore, hence signal to stop. sp<AMessage> msg = new AMessage(kWhatHandleIOError, mReflector); sp<AMessage> msg = new AMessage(kWhatIOError, mReflector); msg->setInt32("errno", errno); status_t err = msg->post(); status_t err = msg->post(); ALOGE("seekOrPostError post:%d", err); ALOGE("seekOrPostError post:%d", err); } } Loading Loading @@ -1768,10 +1838,11 @@ bool MPEG4Writer::preAllocate(uint64_t wantSize) { ALOGV("preAllocateSize :%" PRIu64 " lastFileEndOffset:%" PRIu64, preAllocateSize, ALOGV("preAllocateSize :%" PRIu64 " lastFileEndOffset:%" PRIu64, preAllocateSize, lastFileEndOffset); lastFileEndOffset); int res = fallocate(mFd, 0, lastFileEndOffset, preAllocateSize); int res = fallocate(mFd, FALLOC_FL_KEEP_SIZE, lastFileEndOffset, preAllocateSize); if (res == -1) { if (res == -1) { ALOGE("fallocate err:%s, %d, fd:%d", strerror(errno), errno, mFd); ALOGE("fallocate err:%s, %d, fd:%d", strerror(errno), errno, mFd); sp<AMessage> msg = new AMessage(kWhatHandleFallocateError, mReflector); sp<AMessage> msg = new AMessage(kWhatFallocateError, mReflector); msg->setInt32("errno", errno); status_t err = msg->post(); status_t err = msg->post(); mFallocateErr = true; mFallocateErr = true; ALOGD("preAllocation post:%d", err); ALOGD("preAllocation post:%d", err); Loading Loading @@ -1899,7 +1970,7 @@ size_t MPEG4Writer::numTracks() { //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// MPEG4Writer::Track::Track( MPEG4Writer::Track::Track( MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId) MPEG4Writer *owner, const sp<MediaSource> &source, uint32_t aTrackId) : mOwner(owner), : mOwner(owner), mMeta(source->getFormat()), mMeta(source->getFormat()), mSource(source), mSource(source), Loading @@ -1909,7 +1980,7 @@ MPEG4Writer::Track::Track( mStarted(false), mStarted(false), mGotStartKeyFrame(false), mGotStartKeyFrame(false), mIsMalformed(false), mIsMalformed(false), mTrackId(trackId), mTrackId(aTrackId), mTrackDurationUs(0), mTrackDurationUs(0), mEstimatedTrackSizeBytes(0), mEstimatedTrackSizeBytes(0), mSamplesHaveSameSize(true), mSamplesHaveSameSize(true), Loading Loading @@ -2089,7 +2160,7 @@ void MPEG4Writer::Track::addOneElstTableEntry( void MPEG4Writer::setupAndStartLooper() { void MPEG4Writer::setupAndStartLooper() { if (mLooper == nullptr) { if (mLooper == nullptr) { mLooper = new ALooper; mLooper = new ALooper; mLooper->setName("MP4WriterLooper"); mLooper->setName("MP4WtrCtrlHlpLooper"); mLooper->start(); mLooper->start(); mReflector = new AHandlerReflector<MPEG4Writer>(this); mReflector = new AHandlerReflector<MPEG4Writer>(this); mLooper->registerHandler(mReflector); mLooper->registerHandler(mReflector); Loading @@ -2099,12 +2170,12 @@ void MPEG4Writer::setupAndStartLooper() { void MPEG4Writer::stopAndReleaseLooper() { void MPEG4Writer::stopAndReleaseLooper() { if (mLooper != nullptr) { if (mLooper != nullptr) { if (mReflector != nullptr) { if (mReflector != nullptr) { ALOGD("unregisterHandler"); mLooper->unregisterHandler(mReflector->id()); mLooper->unregisterHandler(mReflector->id()); mReflector.clear(); mReflector.clear(); } } mLooper->stop(); mLooper->stop(); mLooper.clear(); mLooper.clear(); ALOGD("MP4WtrCtrlHlpLooper stopped"); } } } } Loading Loading @@ -2329,18 +2400,22 @@ void MPEG4Writer::onMessageReceived(const sp<AMessage> &msg) { break; break; } } // ::write() or lseek64() wasn't a success, file could be malformed // ::write() or lseek64() wasn't a success, file could be malformed case kWhatHandleIOError: { case kWhatIOError: { ALOGE("kWhatHandleIOError"); ALOGE("kWhatIOError"); int32_t err; CHECK(msg->findInt32("errno", &err)); // Stop tracks' threads and main writer thread. // Stop tracks' threads and main writer thread. notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, ERROR_MALFORMED); notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, err); stop(); stop(); break; break; } } // fallocate() failed, hence notify app about it and stop(). // fallocate() failed, hence notify app about it and stop(). case kWhatHandleFallocateError: { case kWhatFallocateError: { ALOGE("kWhatHandleFallocateError"); ALOGE("kWhatFallocateError"); //TODO: introduce new MEDIA_RECORDER_INFO_STOPPED instead MEDIA_RECORDER_INFO_UNKNOWN? int32_t err; notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_UNKNOWN, ERROR_IO); CHECK(msg->findInt32("errno", &err)); //TODO: introduce a suitable MEDIA_RECORDER_ERROR_* instead MEDIA_RECORDER_ERROR_UNKNOWN? notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, err); stop(); stop(); break; break; } } Loading Loading @@ -2708,9 +2783,9 @@ status_t MPEG4Writer::Track::stop(bool stopSource) { void *dummy; void *dummy; status_t err = pthread_join(mThread, &dummy); status_t err = pthread_join(mThread, &dummy); WARN_UNLESS(err == 0, "track::stop: pthread_join status:%d", err); WARN_UNLESS(err == 0, "track::stop: pthread_join status:%d", err); err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy)); status_t threadRetVal = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy)); WARN_UNLESS(err == 0, "%s track stopped. Status :%d. %s source", getTrackType(), err, WARN_UNLESS(threadRetVal == 0, "%s track stopped. Status :%d. %s source", stopSource ? "Stop" : "Not Stop"); getTrackType(), err, stopSource ? "Stop" : "Not Stop"); mStarted = false; mStarted = false; return err; return err; } } Loading Loading @@ -2849,6 +2924,7 @@ status_t MPEG4Writer::Track::parseAVCCodecSpecificData( } } if (nextStartCode == NULL) { if (nextStartCode == NULL) { ALOGE("nextStartCode is null"); return ERROR_MALFORMED; return ERROR_MALFORMED; } } Loading Loading @@ -3126,11 +3202,11 @@ status_t MPEG4Writer::Track::threadEntry() { int64_t lastSampleDurationTicks = -1; // Timescale based ticks int64_t lastSampleDurationTicks = -1; // Timescale based ticks if (mIsAudio) { if (mIsAudio) { prctl(PR_SET_NAME, (unsigned long)"AudioTrackWriterThread", 0, 0, 0); prctl(PR_SET_NAME, (unsigned long)"MP4WtrAudTrkThread", 0, 0, 0); } else if (mIsVideo) { } else if (mIsVideo) { prctl(PR_SET_NAME, (unsigned long)"VideoTrackWriterThread", 0, 0, 0); prctl(PR_SET_NAME, (unsigned long)"MP4WtrVidTrkThread", 0, 0, 0); } else { } else { prctl(PR_SET_NAME, (unsigned long)"MetadataTrackWriterThread", 0, 0, 0); prctl(PR_SET_NAME, (unsigned long)"MP4WtrMetaTrkThread", 0, 0, 0); } } if (mOwner->isRealTimeRecording()) { if (mOwner->isRealTimeRecording()) { Loading Loading @@ -3181,6 +3257,7 @@ status_t MPEG4Writer::Track::threadEntry() { } } ++count; ++count; int32_t isCodecConfig; int32_t isCodecConfig; if (buffer->meta_data().findInt32(kKeyIsCodecConfig, &isCodecConfig) if (buffer->meta_data().findInt32(kKeyIsCodecConfig, &isCodecConfig) && isCodecConfig) { && isCodecConfig) { Loading @@ -3204,7 +3281,7 @@ status_t MPEG4Writer::Track::threadEntry() { + buffer->range_offset(), + buffer->range_offset(), buffer->range_length()); buffer->range_length()); } else if (mIsMPEG4) { } else if (mIsMPEG4) { copyCodecSpecificData((const uint8_t *)buffer->data() + buffer->range_offset(), err = copyCodecSpecificData((const uint8_t *)buffer->data() + buffer->range_offset(), buffer->range_length()); buffer->range_length()); } } } } Loading @@ -3213,8 +3290,10 @@ status_t MPEG4Writer::Track::threadEntry() { buffer = NULL; buffer = NULL; if (OK != err) { if (OK != err) { mSource->stop(); mSource->stop(); mIsMalformed = true; uint32_t trackNum = (mTrackId.getId() << 28); mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_ERROR, mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_ERROR, mTrackId | MEDIA_RECORDER_TRACK_ERROR_GENERAL, err); trackNum | MEDIA_RECORDER_TRACK_ERROR_GENERAL, err); break; break; } } Loading Loading @@ -3251,7 +3330,7 @@ status_t MPEG4Writer::Track::threadEntry() { * Reserve space in the file for the current sample + to be written MOOV box. If reservation * Reserve space in the file for the current sample + to be written MOOV box. If reservation * for a new sample fails, preAllocate(...) stops muxing session completely. Stop() could * for a new sample fails, preAllocate(...) stops muxing session completely. Stop() could * write MOOV box successfully as space for the same was reserved in the prior call. * write MOOV box successfully as space for the same was reserved in the prior call. * Release the current buffer/sample only here. * Release the current buffer/sample here. */ */ if (!mOwner->preAllocate(buffer->range_length())) { if (!mOwner->preAllocate(buffer->range_length())) { buffer->release(); buffer->release(); Loading Loading @@ -3291,6 +3370,7 @@ status_t MPEG4Writer::Track::threadEntry() { updateTrackSizeEstimate(); updateTrackSizeEstimate(); if (mOwner->exceedsFileSizeLimit()) { if (mOwner->exceedsFileSizeLimit()) { copy->release(); if (mOwner->switchFd() != OK) { if (mOwner->switchFd() != OK) { ALOGW("Recorded file size exceeds limit %" PRId64 "bytes", ALOGW("Recorded file size exceeds limit %" PRId64 "bytes", mOwner->mMaxFileSizeLimitBytes); mOwner->mMaxFileSizeLimitBytes); Loading @@ -3301,16 +3381,15 @@ status_t MPEG4Writer::Track::threadEntry() { ALOGV("%s Current recorded file size exceeds limit %" PRId64 "bytes. Switching output", ALOGV("%s Current recorded file size exceeds limit %" PRId64 "bytes. Switching output", getTrackType(), mOwner->mMaxFileSizeLimitBytes); getTrackType(), mOwner->mMaxFileSizeLimitBytes); } } copy->release(); break; break; } } if (mOwner->exceedsFileDurationLimit()) { if (mOwner->exceedsFileDurationLimit()) { ALOGW("Recorded file duration exceeds limit %" PRId64 "microseconds", ALOGW("Recorded file duration exceeds limit %" PRId64 "microseconds", mOwner->mMaxFileDurationLimitUs); mOwner->mMaxFileDurationLimitUs); mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0); copy->release(); copy->release(); mSource->stop(); mSource->stop(); mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0); break; break; } } Loading Loading @@ -3592,13 +3671,13 @@ status_t MPEG4Writer::Track::threadEntry() { } } } } } } if (isTrackMalFormed()) { if (isTrackMalFormed()) { mIsMalformed = true; dumpTimeStamps(); dumpTimeStamps(); err = ERROR_MALFORMED; err = ERROR_MALFORMED; } } mOwner->trackProgressStatus(mTrackId, -1, err); mOwner->trackProgressStatus(mTrackId.getId(), -1, err); // Add final entries only for non-empty tracks. // Add final entries only for non-empty tracks. if (mStszTableEntries->count() > 0) { if (mStszTableEntries->count() > 0) { Loading Loading @@ -3665,7 +3744,7 @@ status_t MPEG4Writer::Track::threadEntry() { return err; return err; } } bool MPEG4Writer::Track::isTrackMalFormed() const { bool MPEG4Writer::Track::isTrackMalFormed() { if (mIsMalformed) { if (mIsMalformed) { return true; return true; } } Loading @@ -3674,23 +3753,29 @@ bool MPEG4Writer::Track::isTrackMalFormed() const { if (mOwner->mStartMeta && if (mOwner->mStartMeta && mOwner->mStartMeta->findInt32(kKeyEmptyTrackMalFormed, &emptyTrackMalformed) && mOwner->mStartMeta->findInt32(kKeyEmptyTrackMalFormed, &emptyTrackMalformed) && emptyTrackMalformed) { emptyTrackMalformed) { // MediaRecorder(sets kKeyEmptyTrackMalFormed by default) report empty tracks as malformed. if (!mIsHeic && mStszTableEntries->count() == 0) { // no samples written if (!mIsHeic && mStszTableEntries->count() == 0) { // no samples written ALOGE("The number of recorded samples is 0"); ALOGE("The number of recorded samples is 0"); mIsMalformed = true; return true; return true; } } if (mIsVideo && mStssTableEntries->count() == 0) { // no sync frames for video if (mIsVideo && mStssTableEntries->count() == 0) { // no sync frames for video ALOGE("There are no sync frames for video track"); ALOGE("There are no sync frames for video track"); mIsMalformed = true; return true; return true; } } } else { } else { // No sync frames for video. // Through MediaMuxer, empty tracks can be added. No sync frames for video. if (mIsVideo && mStszTableEntries->count() > 0 && mStssTableEntries->count() == 0) { if (mIsVideo && mStszTableEntries->count() > 0 && mStssTableEntries->count() == 0) { ALOGE("There are no sync frames for video track"); ALOGE("There are no sync frames for video track"); mIsMalformed = true; return true; return true; } } } } // Don't check for CodecSpecificData when track is empty. if (OK != checkCodecSpecificData()) { // no codec specific data if (mStszTableEntries->count() > 0 && OK != checkCodecSpecificData()) { // No codec specific data. mIsMalformed = true; return true; return true; } } Loading @@ -3704,7 +3789,7 @@ void MPEG4Writer::Track::sendTrackSummary(bool hasMultipleTracks) { return; return; } } int trackNum = (mTrackId << 28); uint32_t trackNum = (mTrackId.getId() << 28); mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO, mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO, trackNum | MEDIA_RECORDER_TRACK_INFO_TYPE, trackNum | MEDIA_RECORDER_TRACK_INFO_TYPE, Loading Loading @@ -3758,15 +3843,15 @@ void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) { if (mTrackEveryTimeDurationUs > 0 && if (mTrackEveryTimeDurationUs > 0 && timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) { timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) { ALOGV("Fire time tracking progress status at %" PRId64 " us", timeUs); ALOGV("Fire time tracking progress status at %" PRId64 " us", timeUs); mOwner->trackProgressStatus(mTrackId, timeUs - mPreviousTrackTimeUs, err); mOwner->trackProgressStatus(mTrackId.getId(), timeUs - mPreviousTrackTimeUs, err); mPreviousTrackTimeUs = timeUs; mPreviousTrackTimeUs = timeUs; } } } } void MPEG4Writer::trackProgressStatus( void MPEG4Writer::trackProgressStatus( size_t trackId, int64_t timeUs, status_t err) { uint32_t trackId, int64_t timeUs, status_t err) { Mutex::Autolock lock(mLock); Mutex::Autolock lock(mLock); int32_t trackNum = (trackId << 28); uint32_t trackNum = (trackId << 28); // Error notification // Error notification // Do not consider ERROR_END_OF_STREAM an error // Do not consider ERROR_END_OF_STREAM an error Loading Loading @@ -3936,8 +4021,8 @@ int64_t MPEG4Writer::Track::getMinCttsOffsetTimeUs() { void MPEG4Writer::Track::writeStblBox() { void MPEG4Writer::Track::writeStblBox() { mOwner->beginBox("stbl"); mOwner->beginBox("stbl"); // Add subboxes only for non-empty tracks. // Add subboxes for only non-empty and well-formed tracks. if (mStszTableEntries->count() > 0) { if (mStszTableEntries->count() > 0 && !isTrackMalFormed()) { mOwner->beginBox("stsd"); mOwner->beginBox("stsd"); mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt32(1); // entry count mOwner->writeInt32(1); // entry count Loading Loading @@ -4242,7 +4327,7 @@ void MPEG4Writer::Track::writeTkhdBox(uint32_t now) { mOwner->writeInt32(0x07); // version=0, flags=7 mOwner->writeInt32(0x07); // version=0, flags=7 mOwner->writeInt32(now); // creation time mOwner->writeInt32(now); // creation time mOwner->writeInt32(now); // modification time mOwner->writeInt32(now); // modification time mOwner->writeInt32(mTrackId); // track id starts with 1 mOwner->writeInt32(mTrackId.getId()); // track id starts with 1 mOwner->writeInt32(0); // reserved mOwner->writeInt32(0); // reserved int64_t trakDurationUs = getDurationUs(); int64_t trakDurationUs = getDurationUs(); int32_t mvhdTimeScale = mOwner->getTimeScale(); int32_t mvhdTimeScale = mOwner->getTimeScale(); Loading media/libstagefright/MediaMuxer.cpp +4 −1 Original line number Original line Diff line number Diff line Loading @@ -171,7 +171,10 @@ status_t MediaMuxer::stop() { if (err != OK || mError != OK) { if (err != OK || mError != OK) { ALOGE("stop err: %d, mError:%d", err, mError); ALOGE("stop err: %d, mError:%d", err, mError); } } // Prioritize mError over err. /* Prioritize mError over err as writer would have got stopped on any * internal error and notified muxer already. Clients might issue * stop again later, and mWriter->stop() would return success. */ if (mError != OK) { if (mError != OK) { err = mError; err = mError; } } Loading media/libstagefright/include/media/stagefright/MPEG4Writer.h +7 −6 Original line number Original line Diff line number Diff line Loading @@ -86,8 +86,8 @@ private: enum { enum { kWhatSwitch = 'swch', kWhatSwitch = 'swch', kWhatHandleIOError = 'ioer', kWhatIOError = 'ioer', kWhatHandleFallocateError = 'faer' kWhatFallocateError = 'faer' }; }; int mFd; int mFd; Loading Loading @@ -287,7 +287,8 @@ private: bool exceedsFileDurationLimit(); bool exceedsFileDurationLimit(); bool approachingFileSizeLimit(); bool approachingFileSizeLimit(); bool isFileStreamable() const; bool isFileStreamable() const; void trackProgressStatus(size_t trackId, int64_t timeUs, status_t err = OK); void trackProgressStatus(uint32_t trackId, int64_t timeUs, status_t err = OK); status_t validateAllTracksId(bool akKey4BitTrackIds); void writeCompositionMatrix(int32_t degrees); void writeCompositionMatrix(int32_t degrees); void writeMvhdBox(int64_t durationUs); void writeMvhdBox(int64_t durationUs); void writeMoovBox(int64_t durationUs); void writeMoovBox(int64_t durationUs); Loading Loading @@ -327,7 +328,7 @@ private: void writeFileLevelMetaBox(); void writeFileLevelMetaBox(); void sendSessionSummary(); void sendSessionSummary(); void release(); status_t release(); status_t switchFd(); status_t switchFd(); status_t reset(bool stopSource = true); status_t reset(bool stopSource = true); Loading Loading
media/libmediaplayerservice/StagefrightRecorder.cpp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -2011,6 +2011,7 @@ void StagefrightRecorder::setupMPEG4orWEBMMetaData(sp<MetaData> *meta) { } } if (mOutputFormat == OUTPUT_FORMAT_MPEG_4 || mOutputFormat == OUTPUT_FORMAT_THREE_GPP) { if (mOutputFormat == OUTPUT_FORMAT_MPEG_4 || mOutputFormat == OUTPUT_FORMAT_THREE_GPP) { (*meta)->setInt32(kKeyEmptyTrackMalFormed, true); (*meta)->setInt32(kKeyEmptyTrackMalFormed, true); (*meta)->setInt32(kKey4BitTrackIds, true); } } } } Loading
media/libstagefright/HevcUtils.cpp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -83,6 +83,7 @@ status_t HevcParameterSets::addNalUnit(const uint8_t* data, size_t size) { } } if (err != OK) { if (err != OK) { ALOGE("error parsing VPS or SPS or PPS"); return err; return err; } } Loading
media/libstagefright/MPEG4Writer.cpp +146 −61 Original line number Original line Diff line number Diff line Loading @@ -103,8 +103,40 @@ static const uint8_t kHevcNalUnitTypes[5] = { //#define SHOW_MODEL_BUILD 1 //#define SHOW_MODEL_BUILD 1 class MPEG4Writer::Track { class MPEG4Writer::Track { struct TrackId { TrackId(uint32_t aId) :mId(aId), mTrackIdValid(false) { } bool isValid(bool akKey4BitTrackIds) { // trackId cannot be zero, ISO/IEC 14496-12 8.3.2.3 if (mId == 0) { return false; } /* MediaRecorder uses only 4 bit to represent track ids during notifying clients. * MediaMuxer's track ids are restricted by container allowed size only. * MPEG4 Container defines unsigned int (32), ISO/IEC 14496-12 8.3.2.2 */ if (akKey4BitTrackIds && mId > 15) { return false; } mTrackIdValid = true; return true; } uint32_t getId() const { CHECK(mTrackIdValid); return mId; } TrackId() = delete; DISALLOW_EVIL_CONSTRUCTORS(TrackId); private: // unsigned int (32), ISO/IEC 14496-12 8.3.2.2 uint32_t mId; bool mTrackIdValid; }; public: public: Track(MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId); Track(MPEG4Writer *owner, const sp<MediaSource> &source, uint32_t aTrackId); ~Track(); ~Track(); Loading @@ -129,7 +161,7 @@ public: void addChunkOffset(off64_t offset); void addChunkOffset(off64_t offset); void addItemOffsetAndSize(off64_t offset, size_t size, bool isExif); void addItemOffsetAndSize(off64_t offset, size_t size, bool isExif); void flushItemRefs(); void flushItemRefs(); int32_t getTrackId() const { return mTrackId; } TrackId& getTrackId() { return mTrackId; } status_t dump(int fd, const Vector<String16>& args) const; status_t dump(int fd, const Vector<String16>& args) const; static const char *getFourCCForMime(const char *mime); static const char *getFourCCForMime(const char *mime); const char *getTrackType() const; const char *getTrackType() const; Loading Loading @@ -290,7 +322,7 @@ private: bool mIsMPEG4; bool mIsMPEG4; bool mGotStartKeyFrame; bool mGotStartKeyFrame; bool mIsMalformed; bool mIsMalformed; int32_t mTrackId; TrackId mTrackId; int64_t mTrackDurationUs; int64_t mTrackDurationUs; int64_t mMaxChunkDurationUs; int64_t mMaxChunkDurationUs; int64_t mLastDecodingTimeUs; int64_t mLastDecodingTimeUs; Loading Loading @@ -413,7 +445,7 @@ private: void addOneElstTableEntry(uint32_t segmentDuration, int32_t mediaTime, void addOneElstTableEntry(uint32_t segmentDuration, int32_t mediaTime, int16_t mediaRate, int16_t mediaRateFraction); int16_t mediaRate, int16_t mediaRateFraction); bool isTrackMalFormed() const; bool isTrackMalFormed(); void sendTrackSummary(bool hasMultipleTracks); void sendTrackSummary(bool hasMultipleTracks); // Write the boxes // Write the boxes Loading Loading @@ -534,7 +566,7 @@ void MPEG4Writer::initInternal(int fd, bool isFirstSession) { release(); release(); } } if (fallocate(mFd, 0, 0, 1) == 0) { if (fallocate(mFd, FALLOC_FL_KEEP_SIZE, 0, 1) == 0) { ALOGD("PreAllocation enabled"); ALOGD("PreAllocation enabled"); mPreAllocationEnabled = true; mPreAllocationEnabled = true; } else { } else { Loading Loading @@ -744,8 +776,7 @@ int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) { // where 1MB is the common file size limit for MMS application. // where 1MB is the common file size limit for MMS application. // The default MAX _MOOV_BOX_SIZE value is based on about 3 // The default MAX _MOOV_BOX_SIZE value is based on about 3 // minute video recording with a bit rate about 3 Mbps, because // minute video recording with a bit rate about 3 Mbps, because // statistics also show that most of the video captured are going // statistics show that most captured videos are less than 3 minutes. // to be less than 3 minutes. // If the estimation is wrong, we will pay the price of wasting // If the estimation is wrong, we will pay the price of wasting // some reserved space. This should not happen so often statistically. // some reserved space. This should not happen so often statistically. Loading Loading @@ -796,6 +827,15 @@ int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) { return size; return size; } } status_t MPEG4Writer::validateAllTracksId(bool akKey4BitTrackIds) { for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it) { if (!(*it)->getTrackId().isValid(akKey4BitTrackIds)) { return BAD_VALUE; } } return OK; } status_t MPEG4Writer::start(MetaData *param) { status_t MPEG4Writer::start(MetaData *param) { if (mInitCheck != OK) { if (mInitCheck != OK) { return UNKNOWN_ERROR; return UNKNOWN_ERROR; Loading @@ -810,6 +850,9 @@ status_t MPEG4Writer::start(MetaData *param) { mIsFileSizeLimitExplicitlyRequested = true; mIsFileSizeLimitExplicitlyRequested = true; } } /* mMaxFileSizeLimitBytes has to be set everytime fd is switched, hence the following code is * appropriate in start() method. */ int32_t fileSizeBits = fpathconf(mFd, _PC_FILESIZEBITS); int32_t fileSizeBits = fpathconf(mFd, _PC_FILESIZEBITS); ALOGD("fpathconf _PC_FILESIZEBITS:%" PRId32, fileSizeBits); ALOGD("fpathconf _PC_FILESIZEBITS:%" PRId32, fileSizeBits); fileSizeBits = std::min(fileSizeBits, 52 /* cap it below 4 peta bytes */); fileSizeBits = std::min(fileSizeBits, 52 /* cap it below 4 peta bytes */); Loading Loading @@ -944,6 +987,17 @@ status_t MPEG4Writer::start(MetaData *param) { setupAndStartLooper(); setupAndStartLooper(); int32_t is4bitTrackId = false; if (param && param->findInt32(kKey4BitTrackIds, &is4bitTrackId) && is4bitTrackId) { err = validateAllTracksId(true); } else { err = validateAllTracksId(false); } if (err != OK) { return err; } err = startTracks(param); err = startTracks(param); if (err != OK) { if (err != OK) { return err; return err; Loading @@ -961,6 +1015,7 @@ status_t MPEG4Writer::pause() { status_t MPEG4Writer::stopWriterThread() { status_t MPEG4Writer::stopWriterThread() { ALOGV("Stopping writer thread"); ALOGV("Stopping writer thread"); if (!mWriterThreadStarted) { if (!mWriterThreadStarted) { ALOGD("Writer thread not started"); return OK; return OK; } } { { Loading @@ -975,7 +1030,8 @@ status_t MPEG4Writer::stopWriterThread() { err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy)); err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy)); mWriterThreadStarted = false; mWriterThreadStarted = false; WARN_UNLESS(err == 0, "stopWriterThread pthread_join retVal: %d, writer thread stopped", err); WARN_UNLESS(err == 0, "stopWriterThread pthread_join retVal: %d", err); ALOGD("Writer thread stopped"); return err; return err; } } Loading Loading @@ -1031,26 +1087,32 @@ void MPEG4Writer::writeCompositionMatrix(int degrees) { writeInt32(0x40000000); // w writeInt32(0x40000000); // w } } void MPEG4Writer::release() { status_t MPEG4Writer::release() { ALOGD("release()"); ALOGD("release()"); if (mPreAllocationEnabled) { if (mPreAllocationEnabled) { truncatePreAllocation(); truncatePreAllocation(); } } int err = OK; int retVal = fsync(mFd); int retVal = fsync(mFd); WARN_UNLESS(retVal == 0, "fsync retVal:%d", retVal); WARN_UNLESS(retVal == 0, "fsync err:%s(%d)", std::strerror(errno), errno); err |= retVal; retVal = close(mFd); retVal = close(mFd); WARN_UNLESS(retVal == 0, "close mFd retVal :%d", retVal); WARN_UNLESS(retVal == 0, "close err:%s(%d)", std::strerror(errno), errno); err |= retVal; mFd = -1; mFd = -1; if (mNextFd != -1) { if (mNextFd != -1) { retVal = close(mNextFd); retVal = close(mNextFd); mNextFd = -1; mNextFd = -1; WARN_UNLESS(retVal == 0, "close mNextFd retVal :%d", retVal); WARN_UNLESS(retVal == 0, "close mNextFd error:%s(%d)", std::strerror(errno), errno); err |= retVal; } } stopAndReleaseLooper(); stopAndReleaseLooper(); mInitCheck = NO_INIT; mInitCheck = NO_INIT; mStarted = false; mStarted = false; free(mInMemoryCache); free(mInMemoryCache); mInMemoryCache = NULL; mInMemoryCache = NULL; return err; } } void MPEG4Writer::finishCurrentSession() { void MPEG4Writer::finishCurrentSession() { Loading @@ -1065,7 +1127,7 @@ status_t MPEG4Writer::switchFd() { } } if (mNextFd == -1) { if (mNextFd == -1) { ALOGW("No FileDescripter for next recording"); ALOGW("No FileDescriptor for next recording"); return INVALID_OPERATION; return INVALID_OPERATION; } } Loading @@ -1084,12 +1146,15 @@ status_t MPEG4Writer::reset(bool stopSource) { } else { } else { if (!mWriterThreadStarted || if (!mWriterThreadStarted || !mStarted) { !mStarted) { status_t err = OK; status_t writerErr = OK; if (mWriterThreadStarted) { if (mWriterThreadStarted) { err = stopWriterThread(); writerErr = stopWriterThread(); } } release(); status_t retErr = release(); return err; if (writerErr != OK) { retErr = writerErr; } return retErr; } } } } Loading @@ -1100,8 +1165,9 @@ status_t MPEG4Writer::reset(bool stopSource) { for (List<Track *>::iterator it = mTracks.begin(); for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it) { it != mTracks.end(); ++it) { status_t trackErr = (*it)->stop(stopSource); status_t trackErr = (*it)->stop(stopSource); WARN_UNLESS(trackErr == 0, "%s track stopped with an error", (*it)->getTrackType()); if (err == OK && trackErr != OK) { if (err == OK && trackErr != OK) { ALOGW("%s track stopped with an error", (*it)->getTrackType()); err = trackErr; err = trackErr; } } Loading @@ -1118,7 +1184,6 @@ status_t MPEG4Writer::reset(bool stopSource) { } } } } if (nonImageTrackCount > 1) { if (nonImageTrackCount > 1) { ALOGD("Duration from tracks range is [%" PRId64 ", %" PRId64 "] us", ALOGD("Duration from tracks range is [%" PRId64 ", %" PRId64 "] us", minDurationUs, maxDurationUs); minDurationUs, maxDurationUs); Loading @@ -1126,13 +1191,15 @@ status_t MPEG4Writer::reset(bool stopSource) { status_t writerErr = stopWriterThread(); status_t writerErr = stopWriterThread(); // TODO: which error to propagage, writerErr or trackErr? // Propagating writer error if (err == OK && writerErr != OK) { if (err == OK && writerErr != OK) { err = writerErr; err = writerErr; } } // Do not write out movie header on error except malformed track. // Do not write out movie header on error except malformed track. // TODO: Remove samples of malformed tracks added in mdat. if (err != OK && err != ERROR_MALFORMED) { if (err != OK && err != ERROR_MALFORMED) { // Ignoring release() return value as there was an "err" already. release(); release(); return err; return err; } } Loading Loading @@ -1174,6 +1241,7 @@ status_t MPEG4Writer::reset(bool stopSource) { } else { } else { ALOGI("The mp4 file will not be streamable."); ALOGI("The mp4 file will not be streamable."); } } ALOGI("MOOV atom was written to the file"); } } mWriteBoxToMemory = false; mWriteBoxToMemory = false; Loading @@ -1186,7 +1254,7 @@ status_t MPEG4Writer::reset(bool stopSource) { CHECK(mBoxes.empty()); CHECK(mBoxes.empty()); release(); err = release(); return err; return err; } } Loading Loading @@ -1354,7 +1422,7 @@ void MPEG4Writer::sendSessionSummary() { for (List<ChunkInfo>::iterator it = mChunkInfos.begin(); for (List<ChunkInfo>::iterator it = mChunkInfos.begin(); it != mChunkInfos.end(); ++it) { it != mChunkInfos.end(); ++it) { int trackNum = it->mTrack->getTrackId() << 28; uint32_t trackNum = (it->mTrack->getTrackId().getId() << 28); notify(MEDIA_RECORDER_TRACK_EVENT_INFO, notify(MEDIA_RECORDER_TRACK_EVENT_INFO, trackNum | MEDIA_RECORDER_TRACK_INTER_CHUNK_TIME_MS, trackNum | MEDIA_RECORDER_TRACK_INTER_CHUNK_TIME_MS, it->mMaxInterChunkDurUs); it->mMaxInterChunkDurUs); Loading Loading @@ -1512,7 +1580,8 @@ void MPEG4Writer::writeOrPostError(int fd, const void* buf, size_t count) { std::strerror(errno), errno); std::strerror(errno), errno); // Can't guarantee that file is usable or write would succeed anymore, hence signal to stop. // Can't guarantee that file is usable or write would succeed anymore, hence signal to stop. sp<AMessage> msg = new AMessage(kWhatHandleIOError, mReflector); sp<AMessage> msg = new AMessage(kWhatIOError, mReflector); msg->setInt32("errno", errno); status_t err = msg->post(); status_t err = msg->post(); ALOGE("writeOrPostError post:%d", err); ALOGE("writeOrPostError post:%d", err); } } Loading @@ -1531,7 +1600,8 @@ void MPEG4Writer::seekOrPostError(int fd, off64_t offset, int whence) { offset, std::strerror(errno), errno); offset, std::strerror(errno), errno); // Can't guarantee that file is usable or seek would succeed anymore, hence signal to stop. // Can't guarantee that file is usable or seek would succeed anymore, hence signal to stop. sp<AMessage> msg = new AMessage(kWhatHandleIOError, mReflector); sp<AMessage> msg = new AMessage(kWhatIOError, mReflector); msg->setInt32("errno", errno); status_t err = msg->post(); status_t err = msg->post(); ALOGE("seekOrPostError post:%d", err); ALOGE("seekOrPostError post:%d", err); } } Loading Loading @@ -1768,10 +1838,11 @@ bool MPEG4Writer::preAllocate(uint64_t wantSize) { ALOGV("preAllocateSize :%" PRIu64 " lastFileEndOffset:%" PRIu64, preAllocateSize, ALOGV("preAllocateSize :%" PRIu64 " lastFileEndOffset:%" PRIu64, preAllocateSize, lastFileEndOffset); lastFileEndOffset); int res = fallocate(mFd, 0, lastFileEndOffset, preAllocateSize); int res = fallocate(mFd, FALLOC_FL_KEEP_SIZE, lastFileEndOffset, preAllocateSize); if (res == -1) { if (res == -1) { ALOGE("fallocate err:%s, %d, fd:%d", strerror(errno), errno, mFd); ALOGE("fallocate err:%s, %d, fd:%d", strerror(errno), errno, mFd); sp<AMessage> msg = new AMessage(kWhatHandleFallocateError, mReflector); sp<AMessage> msg = new AMessage(kWhatFallocateError, mReflector); msg->setInt32("errno", errno); status_t err = msg->post(); status_t err = msg->post(); mFallocateErr = true; mFallocateErr = true; ALOGD("preAllocation post:%d", err); ALOGD("preAllocation post:%d", err); Loading Loading @@ -1899,7 +1970,7 @@ size_t MPEG4Writer::numTracks() { //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// MPEG4Writer::Track::Track( MPEG4Writer::Track::Track( MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId) MPEG4Writer *owner, const sp<MediaSource> &source, uint32_t aTrackId) : mOwner(owner), : mOwner(owner), mMeta(source->getFormat()), mMeta(source->getFormat()), mSource(source), mSource(source), Loading @@ -1909,7 +1980,7 @@ MPEG4Writer::Track::Track( mStarted(false), mStarted(false), mGotStartKeyFrame(false), mGotStartKeyFrame(false), mIsMalformed(false), mIsMalformed(false), mTrackId(trackId), mTrackId(aTrackId), mTrackDurationUs(0), mTrackDurationUs(0), mEstimatedTrackSizeBytes(0), mEstimatedTrackSizeBytes(0), mSamplesHaveSameSize(true), mSamplesHaveSameSize(true), Loading Loading @@ -2089,7 +2160,7 @@ void MPEG4Writer::Track::addOneElstTableEntry( void MPEG4Writer::setupAndStartLooper() { void MPEG4Writer::setupAndStartLooper() { if (mLooper == nullptr) { if (mLooper == nullptr) { mLooper = new ALooper; mLooper = new ALooper; mLooper->setName("MP4WriterLooper"); mLooper->setName("MP4WtrCtrlHlpLooper"); mLooper->start(); mLooper->start(); mReflector = new AHandlerReflector<MPEG4Writer>(this); mReflector = new AHandlerReflector<MPEG4Writer>(this); mLooper->registerHandler(mReflector); mLooper->registerHandler(mReflector); Loading @@ -2099,12 +2170,12 @@ void MPEG4Writer::setupAndStartLooper() { void MPEG4Writer::stopAndReleaseLooper() { void MPEG4Writer::stopAndReleaseLooper() { if (mLooper != nullptr) { if (mLooper != nullptr) { if (mReflector != nullptr) { if (mReflector != nullptr) { ALOGD("unregisterHandler"); mLooper->unregisterHandler(mReflector->id()); mLooper->unregisterHandler(mReflector->id()); mReflector.clear(); mReflector.clear(); } } mLooper->stop(); mLooper->stop(); mLooper.clear(); mLooper.clear(); ALOGD("MP4WtrCtrlHlpLooper stopped"); } } } } Loading Loading @@ -2329,18 +2400,22 @@ void MPEG4Writer::onMessageReceived(const sp<AMessage> &msg) { break; break; } } // ::write() or lseek64() wasn't a success, file could be malformed // ::write() or lseek64() wasn't a success, file could be malformed case kWhatHandleIOError: { case kWhatIOError: { ALOGE("kWhatHandleIOError"); ALOGE("kWhatIOError"); int32_t err; CHECK(msg->findInt32("errno", &err)); // Stop tracks' threads and main writer thread. // Stop tracks' threads and main writer thread. notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, ERROR_MALFORMED); notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, err); stop(); stop(); break; break; } } // fallocate() failed, hence notify app about it and stop(). // fallocate() failed, hence notify app about it and stop(). case kWhatHandleFallocateError: { case kWhatFallocateError: { ALOGE("kWhatHandleFallocateError"); ALOGE("kWhatFallocateError"); //TODO: introduce new MEDIA_RECORDER_INFO_STOPPED instead MEDIA_RECORDER_INFO_UNKNOWN? int32_t err; notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_UNKNOWN, ERROR_IO); CHECK(msg->findInt32("errno", &err)); //TODO: introduce a suitable MEDIA_RECORDER_ERROR_* instead MEDIA_RECORDER_ERROR_UNKNOWN? notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, err); stop(); stop(); break; break; } } Loading Loading @@ -2708,9 +2783,9 @@ status_t MPEG4Writer::Track::stop(bool stopSource) { void *dummy; void *dummy; status_t err = pthread_join(mThread, &dummy); status_t err = pthread_join(mThread, &dummy); WARN_UNLESS(err == 0, "track::stop: pthread_join status:%d", err); WARN_UNLESS(err == 0, "track::stop: pthread_join status:%d", err); err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy)); status_t threadRetVal = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy)); WARN_UNLESS(err == 0, "%s track stopped. Status :%d. %s source", getTrackType(), err, WARN_UNLESS(threadRetVal == 0, "%s track stopped. Status :%d. %s source", stopSource ? "Stop" : "Not Stop"); getTrackType(), err, stopSource ? "Stop" : "Not Stop"); mStarted = false; mStarted = false; return err; return err; } } Loading Loading @@ -2849,6 +2924,7 @@ status_t MPEG4Writer::Track::parseAVCCodecSpecificData( } } if (nextStartCode == NULL) { if (nextStartCode == NULL) { ALOGE("nextStartCode is null"); return ERROR_MALFORMED; return ERROR_MALFORMED; } } Loading Loading @@ -3126,11 +3202,11 @@ status_t MPEG4Writer::Track::threadEntry() { int64_t lastSampleDurationTicks = -1; // Timescale based ticks int64_t lastSampleDurationTicks = -1; // Timescale based ticks if (mIsAudio) { if (mIsAudio) { prctl(PR_SET_NAME, (unsigned long)"AudioTrackWriterThread", 0, 0, 0); prctl(PR_SET_NAME, (unsigned long)"MP4WtrAudTrkThread", 0, 0, 0); } else if (mIsVideo) { } else if (mIsVideo) { prctl(PR_SET_NAME, (unsigned long)"VideoTrackWriterThread", 0, 0, 0); prctl(PR_SET_NAME, (unsigned long)"MP4WtrVidTrkThread", 0, 0, 0); } else { } else { prctl(PR_SET_NAME, (unsigned long)"MetadataTrackWriterThread", 0, 0, 0); prctl(PR_SET_NAME, (unsigned long)"MP4WtrMetaTrkThread", 0, 0, 0); } } if (mOwner->isRealTimeRecording()) { if (mOwner->isRealTimeRecording()) { Loading Loading @@ -3181,6 +3257,7 @@ status_t MPEG4Writer::Track::threadEntry() { } } ++count; ++count; int32_t isCodecConfig; int32_t isCodecConfig; if (buffer->meta_data().findInt32(kKeyIsCodecConfig, &isCodecConfig) if (buffer->meta_data().findInt32(kKeyIsCodecConfig, &isCodecConfig) && isCodecConfig) { && isCodecConfig) { Loading @@ -3204,7 +3281,7 @@ status_t MPEG4Writer::Track::threadEntry() { + buffer->range_offset(), + buffer->range_offset(), buffer->range_length()); buffer->range_length()); } else if (mIsMPEG4) { } else if (mIsMPEG4) { copyCodecSpecificData((const uint8_t *)buffer->data() + buffer->range_offset(), err = copyCodecSpecificData((const uint8_t *)buffer->data() + buffer->range_offset(), buffer->range_length()); buffer->range_length()); } } } } Loading @@ -3213,8 +3290,10 @@ status_t MPEG4Writer::Track::threadEntry() { buffer = NULL; buffer = NULL; if (OK != err) { if (OK != err) { mSource->stop(); mSource->stop(); mIsMalformed = true; uint32_t trackNum = (mTrackId.getId() << 28); mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_ERROR, mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_ERROR, mTrackId | MEDIA_RECORDER_TRACK_ERROR_GENERAL, err); trackNum | MEDIA_RECORDER_TRACK_ERROR_GENERAL, err); break; break; } } Loading Loading @@ -3251,7 +3330,7 @@ status_t MPEG4Writer::Track::threadEntry() { * Reserve space in the file for the current sample + to be written MOOV box. If reservation * Reserve space in the file for the current sample + to be written MOOV box. If reservation * for a new sample fails, preAllocate(...) stops muxing session completely. Stop() could * for a new sample fails, preAllocate(...) stops muxing session completely. Stop() could * write MOOV box successfully as space for the same was reserved in the prior call. * write MOOV box successfully as space for the same was reserved in the prior call. * Release the current buffer/sample only here. * Release the current buffer/sample here. */ */ if (!mOwner->preAllocate(buffer->range_length())) { if (!mOwner->preAllocate(buffer->range_length())) { buffer->release(); buffer->release(); Loading Loading @@ -3291,6 +3370,7 @@ status_t MPEG4Writer::Track::threadEntry() { updateTrackSizeEstimate(); updateTrackSizeEstimate(); if (mOwner->exceedsFileSizeLimit()) { if (mOwner->exceedsFileSizeLimit()) { copy->release(); if (mOwner->switchFd() != OK) { if (mOwner->switchFd() != OK) { ALOGW("Recorded file size exceeds limit %" PRId64 "bytes", ALOGW("Recorded file size exceeds limit %" PRId64 "bytes", mOwner->mMaxFileSizeLimitBytes); mOwner->mMaxFileSizeLimitBytes); Loading @@ -3301,16 +3381,15 @@ status_t MPEG4Writer::Track::threadEntry() { ALOGV("%s Current recorded file size exceeds limit %" PRId64 "bytes. Switching output", ALOGV("%s Current recorded file size exceeds limit %" PRId64 "bytes. Switching output", getTrackType(), mOwner->mMaxFileSizeLimitBytes); getTrackType(), mOwner->mMaxFileSizeLimitBytes); } } copy->release(); break; break; } } if (mOwner->exceedsFileDurationLimit()) { if (mOwner->exceedsFileDurationLimit()) { ALOGW("Recorded file duration exceeds limit %" PRId64 "microseconds", ALOGW("Recorded file duration exceeds limit %" PRId64 "microseconds", mOwner->mMaxFileDurationLimitUs); mOwner->mMaxFileDurationLimitUs); mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0); copy->release(); copy->release(); mSource->stop(); mSource->stop(); mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0); break; break; } } Loading Loading @@ -3592,13 +3671,13 @@ status_t MPEG4Writer::Track::threadEntry() { } } } } } } if (isTrackMalFormed()) { if (isTrackMalFormed()) { mIsMalformed = true; dumpTimeStamps(); dumpTimeStamps(); err = ERROR_MALFORMED; err = ERROR_MALFORMED; } } mOwner->trackProgressStatus(mTrackId, -1, err); mOwner->trackProgressStatus(mTrackId.getId(), -1, err); // Add final entries only for non-empty tracks. // Add final entries only for non-empty tracks. if (mStszTableEntries->count() > 0) { if (mStszTableEntries->count() > 0) { Loading Loading @@ -3665,7 +3744,7 @@ status_t MPEG4Writer::Track::threadEntry() { return err; return err; } } bool MPEG4Writer::Track::isTrackMalFormed() const { bool MPEG4Writer::Track::isTrackMalFormed() { if (mIsMalformed) { if (mIsMalformed) { return true; return true; } } Loading @@ -3674,23 +3753,29 @@ bool MPEG4Writer::Track::isTrackMalFormed() const { if (mOwner->mStartMeta && if (mOwner->mStartMeta && mOwner->mStartMeta->findInt32(kKeyEmptyTrackMalFormed, &emptyTrackMalformed) && mOwner->mStartMeta->findInt32(kKeyEmptyTrackMalFormed, &emptyTrackMalformed) && emptyTrackMalformed) { emptyTrackMalformed) { // MediaRecorder(sets kKeyEmptyTrackMalFormed by default) report empty tracks as malformed. if (!mIsHeic && mStszTableEntries->count() == 0) { // no samples written if (!mIsHeic && mStszTableEntries->count() == 0) { // no samples written ALOGE("The number of recorded samples is 0"); ALOGE("The number of recorded samples is 0"); mIsMalformed = true; return true; return true; } } if (mIsVideo && mStssTableEntries->count() == 0) { // no sync frames for video if (mIsVideo && mStssTableEntries->count() == 0) { // no sync frames for video ALOGE("There are no sync frames for video track"); ALOGE("There are no sync frames for video track"); mIsMalformed = true; return true; return true; } } } else { } else { // No sync frames for video. // Through MediaMuxer, empty tracks can be added. No sync frames for video. if (mIsVideo && mStszTableEntries->count() > 0 && mStssTableEntries->count() == 0) { if (mIsVideo && mStszTableEntries->count() > 0 && mStssTableEntries->count() == 0) { ALOGE("There are no sync frames for video track"); ALOGE("There are no sync frames for video track"); mIsMalformed = true; return true; return true; } } } } // Don't check for CodecSpecificData when track is empty. if (OK != checkCodecSpecificData()) { // no codec specific data if (mStszTableEntries->count() > 0 && OK != checkCodecSpecificData()) { // No codec specific data. mIsMalformed = true; return true; return true; } } Loading @@ -3704,7 +3789,7 @@ void MPEG4Writer::Track::sendTrackSummary(bool hasMultipleTracks) { return; return; } } int trackNum = (mTrackId << 28); uint32_t trackNum = (mTrackId.getId() << 28); mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO, mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO, trackNum | MEDIA_RECORDER_TRACK_INFO_TYPE, trackNum | MEDIA_RECORDER_TRACK_INFO_TYPE, Loading Loading @@ -3758,15 +3843,15 @@ void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) { if (mTrackEveryTimeDurationUs > 0 && if (mTrackEveryTimeDurationUs > 0 && timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) { timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) { ALOGV("Fire time tracking progress status at %" PRId64 " us", timeUs); ALOGV("Fire time tracking progress status at %" PRId64 " us", timeUs); mOwner->trackProgressStatus(mTrackId, timeUs - mPreviousTrackTimeUs, err); mOwner->trackProgressStatus(mTrackId.getId(), timeUs - mPreviousTrackTimeUs, err); mPreviousTrackTimeUs = timeUs; mPreviousTrackTimeUs = timeUs; } } } } void MPEG4Writer::trackProgressStatus( void MPEG4Writer::trackProgressStatus( size_t trackId, int64_t timeUs, status_t err) { uint32_t trackId, int64_t timeUs, status_t err) { Mutex::Autolock lock(mLock); Mutex::Autolock lock(mLock); int32_t trackNum = (trackId << 28); uint32_t trackNum = (trackId << 28); // Error notification // Error notification // Do not consider ERROR_END_OF_STREAM an error // Do not consider ERROR_END_OF_STREAM an error Loading Loading @@ -3936,8 +4021,8 @@ int64_t MPEG4Writer::Track::getMinCttsOffsetTimeUs() { void MPEG4Writer::Track::writeStblBox() { void MPEG4Writer::Track::writeStblBox() { mOwner->beginBox("stbl"); mOwner->beginBox("stbl"); // Add subboxes only for non-empty tracks. // Add subboxes for only non-empty and well-formed tracks. if (mStszTableEntries->count() > 0) { if (mStszTableEntries->count() > 0 && !isTrackMalFormed()) { mOwner->beginBox("stsd"); mOwner->beginBox("stsd"); mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt32(1); // entry count mOwner->writeInt32(1); // entry count Loading Loading @@ -4242,7 +4327,7 @@ void MPEG4Writer::Track::writeTkhdBox(uint32_t now) { mOwner->writeInt32(0x07); // version=0, flags=7 mOwner->writeInt32(0x07); // version=0, flags=7 mOwner->writeInt32(now); // creation time mOwner->writeInt32(now); // creation time mOwner->writeInt32(now); // modification time mOwner->writeInt32(now); // modification time mOwner->writeInt32(mTrackId); // track id starts with 1 mOwner->writeInt32(mTrackId.getId()); // track id starts with 1 mOwner->writeInt32(0); // reserved mOwner->writeInt32(0); // reserved int64_t trakDurationUs = getDurationUs(); int64_t trakDurationUs = getDurationUs(); int32_t mvhdTimeScale = mOwner->getTimeScale(); int32_t mvhdTimeScale = mOwner->getTimeScale(); Loading
media/libstagefright/MediaMuxer.cpp +4 −1 Original line number Original line Diff line number Diff line Loading @@ -171,7 +171,10 @@ status_t MediaMuxer::stop() { if (err != OK || mError != OK) { if (err != OK || mError != OK) { ALOGE("stop err: %d, mError:%d", err, mError); ALOGE("stop err: %d, mError:%d", err, mError); } } // Prioritize mError over err. /* Prioritize mError over err as writer would have got stopped on any * internal error and notified muxer already. Clients might issue * stop again later, and mWriter->stop() would return success. */ if (mError != OK) { if (mError != OK) { err = mError; err = mError; } } Loading
media/libstagefright/include/media/stagefright/MPEG4Writer.h +7 −6 Original line number Original line Diff line number Diff line Loading @@ -86,8 +86,8 @@ private: enum { enum { kWhatSwitch = 'swch', kWhatSwitch = 'swch', kWhatHandleIOError = 'ioer', kWhatIOError = 'ioer', kWhatHandleFallocateError = 'faer' kWhatFallocateError = 'faer' }; }; int mFd; int mFd; Loading Loading @@ -287,7 +287,8 @@ private: bool exceedsFileDurationLimit(); bool exceedsFileDurationLimit(); bool approachingFileSizeLimit(); bool approachingFileSizeLimit(); bool isFileStreamable() const; bool isFileStreamable() const; void trackProgressStatus(size_t trackId, int64_t timeUs, status_t err = OK); void trackProgressStatus(uint32_t trackId, int64_t timeUs, status_t err = OK); status_t validateAllTracksId(bool akKey4BitTrackIds); void writeCompositionMatrix(int32_t degrees); void writeCompositionMatrix(int32_t degrees); void writeMvhdBox(int64_t durationUs); void writeMvhdBox(int64_t durationUs); void writeMoovBox(int64_t durationUs); void writeMoovBox(int64_t durationUs); Loading Loading @@ -327,7 +328,7 @@ private: void writeFileLevelMetaBox(); void writeFileLevelMetaBox(); void sendSessionSummary(); void sendSessionSummary(); void release(); status_t release(); status_t switchFd(); status_t switchFd(); status_t reset(bool stopSource = true); status_t reset(bool stopSource = true); Loading