Provide progress status report during authoring
- Track either the number of A/V frames authored, or the time elapsed
- Track the completion of the authoring
- Add multiple camera support for authoring by accepting a camera id parameter
- Set file type based on the OUTPUT_FORMAT requested
Change-Id: I0f9d31b3b7a8fa43eb53f572410fb0ebd4fa0bb7
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index eead166..4f4ec43 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -136,7 +136,9 @@
MEDIA_RECORDER_INFO_UNKNOWN = 1,
MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800,
MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801,
- MEDIA_RECORDER_INFO_STOP_PREMATURELY = 802
+ MEDIA_RECORDER_INFO_COMPLETION_STATUS = 802,
+ MEDIA_RECORDER_INFO_PROGRESS_FRAME_STATUS = 803,
+ MEDIA_RECORDER_INFO_PROGRESS_TIME_STATUS = 804,
};
// ----------------------------------------------------------------------------
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 7a2de1e..962b38b 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -81,7 +81,7 @@
void setStartTimestampUs(int64_t timeUs);
int64_t getStartTimestampUs(); // Not const
- status_t startTracks();
+ status_t startTracks(MetaData *params);
size_t numTracks();
int64_t estimateMoovBoxSize(int32_t bitRate);
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index d28d1ca..95fe6f6 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -43,7 +43,6 @@
kKeyVorbisInfo = 'vinf', // raw data
kKeyVorbisBooks = 'vboo', // raw data
kKeyWantsNALFragments = 'NALf',
- kKey64BitFileOffset = 'fobt', // int32_t (bool)
kKeyIsSyncFrame = 'sync', // int32_t (bool)
kKeyIsCodecConfig = 'conf', // int32_t (bool)
kKeyTime = 'time', // int64_t (usecs)
@@ -69,6 +68,21 @@
kKeyDiscNumber = 'dnum', // cstring
kKeyDate = 'date', // cstring
kKeyWriter = 'writ', // cstring
+
+ // Set this key to enable authoring files in 64-bit offset
+ kKey64BitFileOffset = 'fobt', // int32_t (bool)
+
+ // Identify the file output format for authoring
+ // Please see <media/mediarecorder.h> for the supported
+ // file output formats.
+ kKeyFileType = 'ftyp', // int32_t
+
+ // Track authoring progress status
+ // kKeyTrackTimeStatus is used to track progress in elapsed time
+ // kKeyTrackFrameStatus is used to track progress in authored frames
+ kKeyTrackFrameStatus = 'tkfm', // int32_t
+ kKeyTrackTimeStatus = 'tktm', // int64_t
+
};
enum {
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 6834491..1e20f7e 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -310,8 +310,8 @@
// If interval < 0, only the first frame is I frame, and rest are all P frames
// If interval == 0, all frames are encoded as I frames. No P frames
// If interval > 0, it is the time spacing between 2 neighboring I frames
-status_t StagefrightRecorder::setParamIFramesInterval(int32_t interval) {
- LOGV("setParamIFramesInterval: %d seconds", interval);
+status_t StagefrightRecorder::setParamVideoIFramesInterval(int32_t interval) {
+ LOGV("setParamVideoIFramesInterval: %d seconds", interval);
mIFramesInterval = interval;
return OK;
}
@@ -323,6 +323,33 @@
return OK;
}
+status_t StagefrightRecorder::setParamVideoCameraId(int32_t cameraId) {
+ LOGV("setParamVideoCameraId: %d", cameraId);
+ if (cameraId < 0) {
+ return BAD_VALUE;
+ }
+ mCameraId = cameraId;
+ return OK;
+}
+
+status_t StagefrightRecorder::setParamTrackFrameStatus(int32_t nFrames) {
+ LOGV("setParamTrackFrameStatus: %d", nFrames);
+ if (nFrames <= 0) {
+ return BAD_VALUE;
+ }
+ mTrackEveryNumberOfFrames = nFrames;
+ return OK;
+}
+
+status_t StagefrightRecorder::setParamTrackTimeStatus(int64_t timeDurationUs) {
+ LOGV("setParamTrackTimeStatus: %lld", timeDurationUs);
+ if (timeDurationUs < 20000) { // Infeasible if shorter than 20 ms?
+ return BAD_VALUE;
+ }
+ mTrackEveryTimeDurationUs = timeDurationUs;
+ return OK;
+}
+
status_t StagefrightRecorder::setParameter(
const String8 &key, const String8 &value) {
LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
@@ -338,6 +365,26 @@
return setParamMaxDurationOrFileSize(
max_filesize_bytes, false /* limit is filesize */);
}
+ } else if (key == "interleave-duration-us") {
+ int32_t durationUs;
+ if (safe_strtoi32(value.string(), &durationUs)) {
+ return setParamInterleaveDuration(durationUs);
+ }
+ } else if (key == "param-use-64bit-offset") {
+ int32_t use64BitOffset;
+ if (safe_strtoi32(value.string(), &use64BitOffset)) {
+ return setParam64BitFileOffset(use64BitOffset != 0);
+ }
+ } else if (key == "param-track-frame-status") {
+ int32_t nFrames;
+ if (safe_strtoi32(value.string(), &nFrames)) {
+ return setParamTrackFrameStatus(nFrames);
+ }
+ } else if (key == "param-track-time-status") {
+ int64_t timeDurationUs;
+ if (safe_strtoi64(value.string(), &timeDurationUs)) {
+ return setParamTrackTimeStatus(timeDurationUs);
+ }
} else if (key == "audio-param-sampling-rate") {
int32_t sampling_rate;
if (safe_strtoi32(value.string(), &sampling_rate)) {
@@ -358,20 +405,15 @@
if (safe_strtoi32(value.string(), &video_bitrate)) {
return setParamVideoEncodingBitRate(video_bitrate);
}
- } else if (key == "param-interleave-duration-us") {
- int32_t durationUs;
- if (safe_strtoi32(value.string(), &durationUs)) {
- return setParamInterleaveDuration(durationUs);
- }
- } else if (key == "param-i-frames-interval") {
+ } else if (key == "video-param-i-frames-interval") {
int32_t interval;
if (safe_strtoi32(value.string(), &interval)) {
- return setParamIFramesInterval(interval);
+ return setParamVideoIFramesInterval(interval);
}
- } else if (key == "param-use-64bit-offset") {
- int32_t use64BitOffset;
- if (safe_strtoi32(value.string(), &use64BitOffset)) {
- return setParam64BitFileOffset(use64BitOffset != 0);
+ } else if (key == "video-param-camera-id") {
+ int32_t cameraId;
+ if (safe_strtoi32(value.string(), &cameraId)) {
+ return setParamVideoCameraId(cameraId);
}
} else {
LOGE("setParameter: failed to find key %s", key.string());
@@ -677,7 +719,7 @@
int64_t token = IPCThreadState::self()->clearCallingIdentity();
if (mCamera == 0) {
- mCamera = Camera::connect(0);
+ mCamera = Camera::connect(mCameraId);
mCamera->lock();
}
@@ -778,8 +820,16 @@
}
mWriter->setListener(mListener);
sp<MetaData> meta = new MetaData;
+ meta->setInt64(kKeyTime, systemTime() / 1000);
+ meta->setInt32(kKeyFileType, mOutputFormat);
meta->setInt32(kKeyBitRate, totalBitRate);
meta->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
+ if (mTrackEveryNumberOfFrames > 0) {
+ meta->setInt32(kKeyTrackFrameStatus, mTrackEveryNumberOfFrames);
+ }
+ if (mTrackEveryTimeDurationUs > 0) {
+ meta->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
+ }
mWriter->start(meta.get());
return OK;
}
@@ -842,6 +892,9 @@
mIFramesInterval = 1;
mAudioSourceNode = 0;
mUse64BitFileOffset = false;
+ mCameraId = 0;
+ mTrackEveryNumberOfFrames = 0;
+ mTrackEveryTimeDurationUs = 0;
mEncoderProfiles = MediaProfiles::getInstance();
mOutputFd = -1;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 2943e97..9fb7e8f 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -81,8 +81,11 @@
int32_t mSampleRate;
int32_t mInterleaveDurationUs;
int32_t mIFramesInterval;
+ int32_t mCameraId;
int64_t mMaxFileSizeBytes;
int64_t mMaxFileDurationUs;
+ int32_t mTrackEveryNumberOfFrames;
+ int64_t mTrackEveryTimeDurationUs;
String8 mParams;
int mOutputFd;
@@ -95,12 +98,15 @@
status_t startAACRecording();
sp<MediaSource> createAudioSource();
status_t setParameter(const String8 &key, const String8 &value);
- status_t setParamVideoEncodingBitRate(int32_t bitRate);
status_t setParamAudioEncodingBitRate(int32_t bitRate);
status_t setParamAudioNumberOfChannels(int32_t channles);
status_t setParamAudioSamplingRate(int32_t sampleRate);
+ status_t setParamVideoEncodingBitRate(int32_t bitRate);
+ status_t setParamVideoIFramesInterval(int32_t interval);
+ status_t setParamVideoCameraId(int32_t cameraId);
+ status_t setParamTrackTimeStatus(int64_t timeDurationUs);
+ status_t setParamTrackFrameStatus(int32_t nFrames);
status_t setParamInterleaveDuration(int32_t durationUs);
- status_t setParamIFramesInterval(int32_t interval);
status_t setParam64BitFileOffset(bool use64BitFileOffset);
status_t setParamMaxDurationOrFileSize(int64_t limit, bool limit_is_duration);
void clipVideoBitRate();
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 6d1dd16..c71743e 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -253,7 +253,7 @@
}
if (stoppedPrematurely) {
- notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_STOP_PREMATURELY, 0);
+ notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_COMPLETION_STATUS, UNKNOWN_ERROR);
}
fflush(mFile);
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 65d109b..b3e1a01 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -41,7 +41,7 @@
Track(MPEG4Writer *owner, const sp<MediaSource> &source);
~Track();
- status_t start(int64_t startTimeUs);
+ status_t start(MetaData *params);
void stop();
void pause();
bool reachedEOS();
@@ -101,9 +101,13 @@
void *mCodecSpecificData;
size_t mCodecSpecificDataSize;
bool mGotAllCodecSpecificData;
+ bool mTrackingProgressStatus;
bool mReachedEOS;
int64_t mStartTimestampUs;
+ int64_t mPreviousTrackTimeUs;
+ int64_t mTrackEveryTimeDurationUs;
+ int32_t mTrackEveryNumberOfFrames;
static void *ThreadWrapper(void *me);
void threadEntry();
@@ -114,6 +118,8 @@
void logStatisticalData(bool isAudio);
void findMinMaxFrameRates(float *minFps, float *maxFps);
void findMinMaxChunkDurations(int64_t *min, int64_t *max);
+ void trackProgressStatus(int32_t nFrames, int64_t timeUs);
+ void initTrackingProgressStatus(MetaData *params);
Track(const Track &);
Track &operator=(const Track &);
@@ -162,11 +168,10 @@
return OK;
}
-status_t MPEG4Writer::startTracks() {
- int64_t startTimeUs = systemTime() / 1000;
+status_t MPEG4Writer::startTracks(MetaData *params) {
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
- status_t err = (*it)->start(startTimeUs);
+ status_t err = (*it)->start(params);
if (err != OK) {
for (List<Track *>::iterator it2 = mTracks.begin();
@@ -247,10 +252,11 @@
}
mStartTimestampUs = -1;
+
if (mStarted) {
if (mPaused) {
mPaused = false;
- return startTracks();
+ return startTracks(param);
}
return OK;
}
@@ -261,9 +267,18 @@
mMoovBoxBufferOffset = 0;
beginBox("ftyp");
- writeFourcc("isom");
+ {
+ int32_t fileType;
+ if (param && param->findInt32(kKeyFileType, &fileType) &&
+ fileType != OUTPUT_FORMAT_MPEG_4) {
+ writeFourcc("3gp4");
+ } else {
+ writeFourcc("isom");
+ }
+ }
writeInt32(0);
writeFourcc("isom");
+ writeFourcc("3gp4");
endBox();
mFreeBoxOffset = mOffset;
@@ -288,8 +303,7 @@
} else {
write("\x00\x00\x00\x01mdat????????", 16);
}
-
- status_t err = startTracks();
+ status_t err = startTracks(param);
if (err != OK) {
return err;
}
@@ -670,13 +684,41 @@
}
}
-status_t MPEG4Writer::Track::start(int64_t startTimeUs) {
+void MPEG4Writer::Track::initTrackingProgressStatus(MetaData *params) {
+ LOGV("initTrackingProgressStatus");
+ mPreviousTrackTimeUs = -1;
+ mTrackingProgressStatus = false;
+ mTrackEveryTimeDurationUs = 0;
+ mTrackEveryNumberOfFrames = 0;
+ {
+ int64_t timeUs;
+ if (params && params->findInt64(kKeyTrackTimeStatus, &timeUs)) {
+ LOGV("Receive request to track progress status for every %lld us", timeUs);
+ mTrackEveryTimeDurationUs = timeUs;
+ mTrackingProgressStatus = true;
+ }
+ }
+ {
+ int32_t nFrames;
+ if (params && params->findInt32(kKeyTrackFrameStatus, &nFrames)) {
+ LOGV("Receive request to track progress status for every %d frames", nFrames);
+ mTrackEveryNumberOfFrames = nFrames;
+ mTrackingProgressStatus = true;
+ }
+ }
+}
+
+status_t MPEG4Writer::Track::start(MetaData *params) {
if (!mDone && mPaused) {
mPaused = false;
mResumed = true;
return OK;
}
+ int64_t startTimeUs;
+ CHECK(params && params->findInt64(kKeyTime, &startTimeUs));
+ initTrackingProgressStatus(params);
+
sp<MetaData> meta = new MetaData;
meta->setInt64(kKeyTime, startTimeUs);
status_t err = mSource->start(meta.get());
@@ -848,8 +890,9 @@
int64_t previousPausedDurationUs = 0;
sp<MetaData> meta_data;
+ status_t err = OK;
MediaBuffer *buffer;
- while (!mDone && mSource->read(&buffer) == OK) {
+ while (!mDone && (err = mSource->read(&buffer)) == OK) {
if (buffer->range_length() == 0) {
buffer->release();
buffer = NULL;
@@ -1062,6 +1105,12 @@
mStssTableEntries.push_back(mSampleInfos.size());
}
+ if (mTrackingProgressStatus) {
+ if (mPreviousTrackTimeUs <= 0) {
+ mPreviousTrackTimeUs = mStartTimestampUs;
+ }
+ trackProgressStatus(mSampleInfos.size(), timestampUs);
+ }
if (mOwner->numTracks() == 1) {
off_t offset = is_avc? mOwner->addLengthPrefixedSample_l(copy)
: mOwner->addSample_l(copy);
@@ -1101,8 +1150,9 @@
}
if (mSampleInfos.empty()) {
- mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_STOP_PREMATURELY, 0);
+ err = UNKNOWN_ERROR;
}
+ mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_COMPLETION_STATUS, err);
// Last chunk
if (mOwner->numTracks() == 1) {
@@ -1132,6 +1182,24 @@
logStatisticalData(is_audio);
}
+void MPEG4Writer::Track::trackProgressStatus(int32_t nFrames, int64_t timeUs) {
+ LOGV("trackProgressStatus: %d frames and %lld us", nFrames, timeUs);
+ if (nFrames % mTrackEveryNumberOfFrames == 0) {
+ LOGV("Fire frame tracking progress status at frame %d", nFrames);
+ mOwner->notify(MEDIA_RECORDER_EVENT_INFO,
+ MEDIA_RECORDER_INFO_PROGRESS_FRAME_STATUS,
+ nFrames);
+ }
+
+ if (timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
+ LOGV("Fire time tracking progress status at %lld us", timeUs);
+ mOwner->notify(MEDIA_RECORDER_EVENT_INFO,
+ MEDIA_RECORDER_INFO_PROGRESS_TIME_STATUS,
+ timeUs / 1000);
+ mPreviousTrackTimeUs = timeUs;
+ }
+}
+
void MPEG4Writer::Track::findMinMaxFrameRates(float *minFps, float *maxFps) {
int32_t minSampleDuration = 0x7FFFFFFF;
int32_t maxSampleDuration = 0;