summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/MPEG4Writer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libstagefright/MPEG4Writer.cpp')
-rw-r--r--media/libstagefright/MPEG4Writer.cpp183
1 files changed, 134 insertions, 49 deletions
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 47f114a..16da3eb 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;
@@ -999,7 +1023,11 @@ uint32_t MPEG4Writer::getMpeg4Time() {
// MP4 file uses time counting seconds since midnight, Jan. 1, 1904
// while time function returns Unix epoch values which starts
// at 1970-01-01. Lets add the number of seconds between them
- uint32_t mpeg4Time = now + (66 * 365 + 17) * (24 * 60 * 60);
+ static const uint32_t delta = (66 * 365 + 17) * (24 * 60 * 60);
+ if (now < 0 || uint32_t(now) > UINT32_MAX - delta) {
+ return 0;
+ }
+ uint32_t mpeg4Time = uint32_t(now) + delta;
return mpeg4Time;
}
@@ -1009,7 +1037,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 +1075,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");
@@ -1118,7 +1148,7 @@ off64_t MPEG4Writer::addSample_l(MediaBuffer *buffer) {
return old_offset;
}
-static void StripStartcode(MediaBuffer *buffer) {
+void MPEG4Writer::StripStartcode(MediaBuffer *buffer) {
if (buffer->range_length() < 4) {
return;
}
@@ -1455,7 +1485,8 @@ MPEG4Writer::Track::Track(
mCodecSpecificDataSize(0),
mGotAllCodecSpecificData(false),
mReachedEOS(false),
- mRotation(0) {
+ mRotation(0),
+ mHFRRatio(1) {
getCodecSpecificDataFromInputFormatIfPossible();
const char *mime;
@@ -1464,6 +1495,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 +1599,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 +1613,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 +1696,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);
@@ -1798,9 +1837,16 @@ status_t MPEG4Writer::Track::start(MetaData *params) {
}
int64_t startTimeUs;
+
if (params == NULL || !params->findInt64(kKeyTime, &startTimeUs)) {
startTimeUs = 0;
}
+
+ int64_t startTimeBootUs;
+ if (params == NULL || !params->findInt64(kKeyTimeBoot, &startTimeBootUs)) {
+ startTimeBootUs = 0;
+ }
+
mStartTimeRealUs = startTimeUs;
int32_t rotationDegrees;
@@ -1811,6 +1857,7 @@ status_t MPEG4Writer::Track::start(MetaData *params) {
initTrackingProgressStatus(params);
sp<MetaData> meta = new MetaData;
+
if (mOwner->isRealTimeRecording() && mOwner->numTracks() > 1) {
/*
* This extra delay of accepting incoming audio/video signals
@@ -1826,10 +1873,12 @@ status_t MPEG4Writer::Track::start(MetaData *params) {
startTimeOffsetUs = kInitialDelayTimeUs;
}
startTimeUs += startTimeOffsetUs;
+ startTimeBootUs += startTimeOffsetUs;
ALOGI("Start time offset: %" PRId64 " us", startTimeOffsetUs);
}
meta->setInt64(kKeyTime, startTimeUs);
+ meta->setInt64(kKeyTimeBoot, startTimeBootUs);
status_t err = mSource->start(meta.get());
if (err != OK) {
@@ -1852,6 +1901,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 +1932,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 +2026,7 @@ status_t MPEG4Writer::Track::copyAVCCodecSpecificData(
mCodecSpecificDataSize = size;
mCodecSpecificData = malloc(size);
+ CHECK(mCodecSpecificData != NULL);
memcpy(mCodecSpecificData, data, size);
return OK;
}
@@ -2093,6 +2149,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 +2252,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 +2286,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 +2307,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 +2362,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 +2384,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;
}
@@ -2398,7 +2462,9 @@ status_t MPEG4Writer::Track::threadEntry() {
ALOGE("timestampUs %" PRId64 " < lastTimestampUs %" PRId64 " for %s track",
timestampUs, lastTimestampUs, trackName);
copy->release();
- return UNKNOWN_ERROR;
+ err = UNKNOWN_ERROR;
+ mSource->notifyError(err);
+ return err;
}
// if the duration is different for this sample, see if it is close enough to the previous
@@ -2453,7 +2519,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 +2612,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 +2776,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 +2798,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 +2880,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 +2980,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 +3092,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 +3171,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;
}