Loading include/media/stagefright/MPEG4Writer.h +2 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ public: void endBox(); uint32_t interleaveDuration() const { return mInterleaveDurationUs; } status_t setInterleaveDuration(uint32_t duration); int32_t getTimeScale() const { return mTimeScale; } protected: virtual ~MPEG4Writer(); Loading @@ -72,6 +73,7 @@ private: bool mStreamableFile; off_t mEstimatedMoovBoxSize; uint32_t mInterleaveDurationUs; int32_t mTimeScale; int64_t mStartTimestampUs; Mutex mLock; Loading include/media/stagefright/MetaData.h +1 −0 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ enum { kKeyDiscNumber = 'dnum', // cstring kKeyDate = 'date', // cstring kKeyWriter = 'writ', // cstring kKeyTimeScale = 'tmsl', // int32_t // video profile and level kKeyVideoProfile = 'vprf', // int32_t Loading media/libmediaplayerservice/StagefrightRecorder.cpp +72 −12 Original line number Diff line number Diff line Loading @@ -381,12 +381,12 @@ status_t StagefrightRecorder::setParamInterleaveDuration(int32_t durationUs) { return OK; } // 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 (seconds) between 2 neighboring I frames status_t StagefrightRecorder::setParamVideoIFramesInterval(int32_t interval) { LOGV("setParamVideoIFramesInterval: %d seconds", interval); mIFramesInterval = interval; // If seconds < 0, only the first frame is I frame, and rest are all P frames // If seconds == 0, all frames are encoded as I frames. No P frames // If seconds > 0, it is the time spacing (seconds) between 2 neighboring I frames status_t StagefrightRecorder::setParamVideoIFramesInterval(int32_t seconds) { LOGV("setParamVideoIFramesInterval: %d seconds", seconds); mIFramesIntervalSec = seconds; return OK; } Loading Loading @@ -444,6 +444,44 @@ status_t StagefrightRecorder::setParamVideoEncoderLevel(int32_t level) { return OK; } status_t StagefrightRecorder::setParamMovieTimeScale(int32_t timeScale) { LOGV("setParamMovieTimeScale: %d", timeScale); // The range is set to be the same as the audio's time scale range // since audio's time scale has a wider range. if (timeScale < 600 || timeScale > 96000) { LOGE("Time scale (%d) for movie is out of range [600, 96000]", timeScale); return BAD_VALUE; } mMovieTimeScale = timeScale; return OK; } status_t StagefrightRecorder::setParamVideoTimeScale(int32_t timeScale) { LOGV("setParamVideoTimeScale: %d", timeScale); // 60000 is chosen to make sure that each video frame from a 60-fps // video has 1000 ticks. if (timeScale < 600 || timeScale > 60000) { LOGE("Time scale (%d) for video is out of range [600, 60000]", timeScale); return BAD_VALUE; } mVideoTimeScale = timeScale; return OK; } status_t StagefrightRecorder::setParamAudioTimeScale(int32_t timeScale) { LOGV("setParamAudioTimeScale: %d", timeScale); // 96000 Hz is the highest sampling rate support in AAC. if (timeScale < 600 || timeScale > 96000) { LOGE("Time scale (%d) for audio is out of range [600, 96000]", timeScale); return BAD_VALUE; } mAudioTimeScale = timeScale; return OK; } status_t StagefrightRecorder::setParameter( const String8 &key, const String8 &value) { LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string()); Loading @@ -462,6 +500,11 @@ status_t StagefrightRecorder::setParameter( if (safe_strtoi32(value.string(), &durationUs)) { return setParamInterleaveDuration(durationUs); } } else if (key == "param-movie-time-scale") { int32_t timeScale; if (safe_strtoi32(value.string(), &timeScale)) { return setParamMovieTimeScale(timeScale); } } else if (key == "param-use-64bit-offset") { int32_t use64BitOffset; if (safe_strtoi32(value.string(), &use64BitOffset)) { Loading Loading @@ -492,15 +535,20 @@ status_t StagefrightRecorder::setParameter( if (safe_strtoi32(value.string(), &audio_bitrate)) { return setParamAudioEncodingBitRate(audio_bitrate); } } else if (key == "audio-param-time-scale") { int32_t timeScale; if (safe_strtoi32(value.string(), &timeScale)) { return setParamAudioTimeScale(timeScale); } } else if (key == "video-param-encoding-bitrate") { int32_t video_bitrate; if (safe_strtoi32(value.string(), &video_bitrate)) { return setParamVideoEncodingBitRate(video_bitrate); } } else if (key == "video-param-i-frames-interval") { int32_t interval; if (safe_strtoi32(value.string(), &interval)) { return setParamVideoIFramesInterval(interval); int32_t seconds; if (safe_strtoi32(value.string(), &seconds)) { return setParamVideoIFramesInterval(seconds); } } else if (key == "video-param-encoder-profile") { int32_t profile; Loading @@ -517,6 +565,11 @@ status_t StagefrightRecorder::setParameter( if (safe_strtoi32(value.string(), &cameraId)) { return setParamVideoCameraId(cameraId); } } else if (key == "video-param-time-scale") { int32_t timeScale; if (safe_strtoi32(value.string(), &timeScale)) { return setParamVideoTimeScale(timeScale); } } else { LOGE("setParameter: failed to find key %s", key.string()); } Loading Loading @@ -637,6 +690,7 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() { encMeta->setInt32(kKeyChannelCount, mAudioChannels); encMeta->setInt32(kKeySampleRate, mSampleRate); encMeta->setInt32(kKeyBitRate, mAudioBitRate); encMeta->setInt32(kKeyTimeScale, mAudioTimeScale); OMXClient client; CHECK_EQ(client.connect(), OK); Loading Loading @@ -877,10 +931,11 @@ status_t StagefrightRecorder::setupVideoEncoder(const sp<MediaWriter>& writer) { enc_meta->setInt32(kKeyWidth, width); enc_meta->setInt32(kKeyHeight, height); enc_meta->setInt32(kKeyIFramesInterval, mIFramesInterval); enc_meta->setInt32(kKeyIFramesInterval, mIFramesIntervalSec); enc_meta->setInt32(kKeyStride, stride); enc_meta->setInt32(kKeySliceHeight, sliceHeight); enc_meta->setInt32(kKeyColorFormat, colorFormat); enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale); if (mVideoEncoderProfile != -1) { enc_meta->setInt32(kKeyVideoProfile, mVideoEncoderProfile); } Loading Loading @@ -918,6 +973,7 @@ status_t StagefrightRecorder::setupAudioEncoder(const sp<MediaWriter>& writer) { if (audioEncoder == NULL) { return UNKNOWN_ERROR; } writer->addSource(audioEncoder); return OK; } Loading Loading @@ -954,6 +1010,7 @@ status_t StagefrightRecorder::startMPEG4Recording() { meta->setInt32(kKeyFileType, mOutputFormat); meta->setInt32(kKeyBitRate, totalBitRate); meta->setInt32(kKey64BitFileOffset, mUse64BitFileOffset); meta->setInt32(kKeyTimeScale, mMovieTimeScale); if (mTrackEveryNumberOfFrames > 0) { meta->setInt32(kKeyTrackFrameStatus, mTrackEveryNumberOfFrames); } Loading Loading @@ -1024,9 +1081,12 @@ status_t StagefrightRecorder::reset() { mAudioChannels = 1; mAudioBitRate = 12200; mInterleaveDurationUs = 0; mIFramesInterval = 1; mIFramesIntervalSec = 1; mAudioSourceNode = 0; mUse64BitFileOffset = false; mMovieTimeScale = 1000; mAudioTimeScale = 1000; mVideoTimeScale = 1000; mCameraId = 0; mVideoEncoderProfile = -1; mVideoEncoderLevel = -1; Loading Loading @@ -1108,7 +1168,7 @@ status_t StagefrightRecorder::dump(int fd, const Vector<String16>& args) const { result.append(buffer); snprintf(buffer, SIZE, " Encoder level: %d\n", mVideoEncoderLevel); result.append(buffer); snprintf(buffer, SIZE, " I frames interval (s): %d\n", mIFramesInterval); snprintf(buffer, SIZE, " I frames interval (s): %d\n", mIFramesIntervalSec); result.append(buffer); snprintf(buffer, SIZE, " Frame size (pixels): %dx%d\n", mVideoWidth, mVideoHeight); result.append(buffer); Loading media/libmediaplayerservice/StagefrightRecorder.h +8 −2 Original line number Diff line number Diff line Loading @@ -81,10 +81,13 @@ private: int32_t mAudioChannels; int32_t mSampleRate; int32_t mInterleaveDurationUs; int32_t mIFramesInterval; int32_t mIFramesIntervalSec; int32_t mCameraId; int32_t mVideoEncoderProfile; int32_t mVideoEncoderLevel; int32_t mMovieTimeScale; int32_t mVideoTimeScale; int32_t mAudioTimeScale; int64_t mMaxFileSizeBytes; int64_t mMaxFileDurationUs; int32_t mTrackEveryNumberOfFrames; Loading @@ -109,17 +112,20 @@ private: status_t setParamAudioEncodingBitRate(int32_t bitRate); status_t setParamAudioNumberOfChannels(int32_t channles); status_t setParamAudioSamplingRate(int32_t sampleRate); status_t setParamAudioTimeScale(int32_t timeScale); status_t setParamVideoEncodingBitRate(int32_t bitRate); status_t setParamVideoIFramesInterval(int32_t interval); status_t setParamVideoIFramesInterval(int32_t seconds); status_t setParamVideoEncoderProfile(int32_t profile); status_t setParamVideoEncoderLevel(int32_t level); status_t setParamVideoCameraId(int32_t cameraId); status_t setParamVideoTimeScale(int32_t timeScale); status_t setParamTrackTimeStatus(int64_t timeDurationUs); status_t setParamTrackFrameStatus(int32_t nFrames); status_t setParamInterleaveDuration(int32_t durationUs); status_t setParam64BitFileOffset(bool use64BitFileOffset); status_t setParamMaxFileDurationUs(int64_t timeUs); status_t setParamMaxFileSizeBytes(int64_t bytes); status_t setParamMovieTimeScale(int32_t timeScale); void clipVideoBitRate(); void clipVideoFrameRate(); void clipVideoFrameWidth(); Loading media/libstagefright/MPEG4Writer.cpp +63 −39 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ namespace android { class MPEG4Writer::Track { public: Track(MPEG4Writer *owner, const sp<MediaSource> &source); ~Track(); status_t start(MetaData *params); Loading @@ -61,12 +62,13 @@ private: volatile bool mResumed; int64_t mMaxTimeStampUs; int64_t mEstimatedTrackSizeBytes; int32_t mTimeScale; pthread_t mThread; struct SampleInfo { size_t size; int64_t timestamp; int64_t timestampUs; }; List<SampleInfo> mSampleInfos; bool mSamplesHaveSameSize; Loading @@ -92,11 +94,11 @@ private: struct SttsTableEntry { SttsTableEntry(uint32_t count, uint32_t duration) : sampleCount(count), sampleDuration(duration) {} SttsTableEntry(uint32_t count, uint32_t durationUs) : sampleCount(count), sampleDurationUs(durationUs) {} uint32_t sampleCount; uint32_t sampleDuration; uint32_t sampleDurationUs; }; List<SttsTableEntry> mSttsTableEntries; Loading Loading @@ -270,6 +272,13 @@ status_t MPEG4Writer::start(MetaData *param) { return OK; } if (!param || !param->findInt32(kKeyTimeScale, &mTimeScale)) { mTimeScale = 1000; } CHECK(mTimeScale > 0); LOGV("movie time scale: %d", mTimeScale); mStreamableFile = true; mWriteMoovBoxToMemory = false; mMoovBoxBuffer = NULL; Loading Loading @@ -336,14 +345,14 @@ void MPEG4Writer::stop() { return; } int64_t max_duration = 0; int64_t maxDurationUs = 0; for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it) { (*it)->stop(); int64_t duration = (*it)->getDurationUs(); if (duration > max_duration) { max_duration = duration; int64_t durationUs = (*it)->getDurationUs(); if (durationUs > maxDurationUs) { maxDurationUs = durationUs; } } Loading @@ -367,8 +376,7 @@ void MPEG4Writer::stop() { mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize); mMoovBoxBufferOffset = 0; CHECK(mMoovBoxBuffer != NULL); int32_t timeScale = 1000; int32_t duration = max_duration / timeScale; int32_t duration = (maxDurationUs * mTimeScale) / 1E6; beginBox("moov"); Loading @@ -376,7 +384,7 @@ void MPEG4Writer::stop() { writeInt32(0); // version=0, flags=0 writeInt32(now); // creation time writeInt32(now); // modification time writeInt32(timeScale); // timescale writeInt32(mTimeScale); // mvhd timescale writeInt32(duration); writeInt32(0x10000); // rate: 1.0 writeInt16(0x100); // volume Loading Loading @@ -655,7 +663,6 @@ void MPEG4Writer::setStartTimestampUs(int64_t timeUs) { } int64_t MPEG4Writer::getStartTimestampUs() { LOGI("getStartTimestampUs: %lld", mStartTimestampUs); Mutex::Autolock autoLock(mLock); return mStartTimestampUs; } Loading Loading @@ -683,6 +690,11 @@ MPEG4Writer::Track::Track( mGotAllCodecSpecificData(false), mReachedEOS(false) { getCodecSpecificDataFromInputFormatIfPossible(); if (!mMeta->findInt32(kKeyTimeScale, &mTimeScale)) { mTimeScale = 1000; } CHECK(mTimeScale > 0); } void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() { Loading Loading @@ -927,8 +939,8 @@ void MPEG4Writer::Track::threadEntry() { int64_t chunkTimestampUs = 0; int32_t nChunks = 0; int32_t nZeroLengthFrames = 0; int64_t lastTimestamp = 0; // Timestamp of the previous sample int64_t lastDuration = 0; // Time spacing between the previous two samples int64_t lastTimestampUs = 0; // Previous sample time stamp in ms int64_t lastDurationUs = 0; // Between the previous two samples in ms int32_t sampleCount = 1; // Sample count in the current stts table entry uint32_t previousSampleSize = 0; // Size of the previous sample int64_t previousPausedDurationUs = 0; Loading Loading @@ -1113,7 +1125,7 @@ void MPEG4Writer::Track::threadEntry() { } if (mResumed) { previousPausedDurationUs += (timestampUs - mMaxTimeStampUs - 1000 * lastDuration); previousPausedDurationUs += (timestampUs - mMaxTimeStampUs - lastDurationUs); mResumed = false; } Loading @@ -1124,12 +1136,11 @@ void MPEG4Writer::Track::threadEntry() { mMaxTimeStampUs = timestampUs; } // Our timestamp is in ms. info.timestamp = (timestampUs + 500) / 1000; info.timestampUs = timestampUs; mSampleInfos.push_back(info); if (mSampleInfos.size() > 2) { if (lastDuration != info.timestamp - lastTimestamp) { SttsTableEntry sttsEntry(sampleCount, lastDuration); if (lastDurationUs != info.timestampUs - lastTimestampUs) { SttsTableEntry sttsEntry(sampleCount, lastDurationUs); mSttsTableEntries.push_back(sttsEntry); sampleCount = 1; } else { Loading @@ -1142,8 +1153,8 @@ void MPEG4Writer::Track::threadEntry() { } previousSampleSize = info.size; } lastDuration = info.timestamp - lastTimestamp; lastTimestamp = info.timestamp; lastDurationUs = info.timestampUs - lastTimestampUs; lastTimestampUs = info.timestampUs; if (isSync != 0) { mStssTableEntries.push_back(mSampleInfos.size()); Loading Loading @@ -1213,11 +1224,11 @@ void MPEG4Writer::Track::threadEntry() { // there is no frame time after it, just repeat the previous // frame's duration. if (mSampleInfos.size() == 1) { lastDuration = 0; // A single sample's duration lastDurationUs = 0; // A single sample's duration } else { ++sampleCount; // Count for the last sample } SttsTableEntry sttsEntry(sampleCount, lastDuration); SttsTableEntry sttsEntry(sampleCount, lastDurationUs); mSttsTableEntries.push_back(sttsEntry); mReachedEOS = true; LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames - %s", Loading Loading @@ -1254,7 +1265,8 @@ void MPEG4Writer::Track::findMinAvgMaxSampleDurationMs( int32_t maxSampleDurationMs = 0; for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin(); it != mSttsTableEntries.end(); ++it) { int32_t sampleDurationMs = static_cast<int32_t>(it->sampleDuration); int32_t sampleDurationMs = (static_cast<int32_t>(it->sampleDurationUs) + 500) / 1000; if (sampleDurationMs > maxSampleDurationMs) { maxSampleDurationMs = sampleDurationMs; } else if (sampleDurationMs < minSampleDurationMs) { Loading Loading @@ -1370,10 +1382,13 @@ void MPEG4Writer::Track::writeTrackHeader( CHECK(success); bool is_audio = !strncasecmp(mime, "audio/", 6); int32_t timeScale = 1000; int32_t duration = getDurationUs() / timeScale; LOGV("%s track time scale: %d", is_audio? "Audio": "Video", mTimeScale); time_t now = time(NULL); int32_t mvhdTimeScale = mOwner->getTimeScale(); int64_t trakDurationUs = getDurationUs(); mOwner->beginBox("trak"); Loading @@ -1385,7 +1400,9 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->writeInt32(now); // modification time mOwner->writeInt32(trackID); mOwner->writeInt32(0); // reserved mOwner->writeInt32(duration); int32_t tkhdDuration = (trakDurationUs * mvhdTimeScale + 5E5) / 1E6; mOwner->writeInt32(tkhdDuration); // in mvhd timescale mOwner->writeInt32(0); // reserved mOwner->writeInt32(0); // reserved mOwner->writeInt16(0); // layer Loading Loading @@ -1423,12 +1440,17 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->beginBox("elst"); mOwner->writeInt32(0); // version=0, flags=0: 32-bit time mOwner->writeInt32(2); // never ends with an empty list int64_t durationMs = (mStartTimestampUs - moovStartTimeUs) / 1000; mOwner->writeInt32(durationMs); // edit duration mOwner->writeInt32(-1); // empty edit box to signal starting time offset mOwner->writeInt32(1 << 16); // x1 rate mOwner->writeInt32(duration); // First elst entry: specify the starting time offset int64_t offsetUs = mStartTimestampUs - moovStartTimeUs; int32_t seg = (offsetUs * mvhdTimeScale + 5E5) / 1E6; mOwner->writeInt32(seg); // in mvhd timecale mOwner->writeInt32(-1); // starting time offset mOwner->writeInt32(1 << 16); // rate = 1.0 // Second elst entry: specify the track duration seg = (trakDurationUs * mvhdTimeScale + 5E5) / 1E6; mOwner->writeInt32(seg); // in mvhd timescale mOwner->writeInt32(0); mOwner->writeInt32(1 << 16); mOwner->endBox(); Loading @@ -1441,8 +1463,9 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt32(now); // creation time mOwner->writeInt32(now); // modification time mOwner->writeInt32(timeScale); // timescale mOwner->writeInt32(duration); // duration mOwner->writeInt32(mTimeScale); // 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 // 'e', 'n', 'g' for "English", for instance. // Each character is packed as the difference between its ASCII value and 0x60. Loading Loading @@ -1664,7 +1687,8 @@ void MPEG4Writer::Track::writeTrackHeader( for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin(); it != mSttsTableEntries.end(); ++it) { mOwner->writeInt32(it->sampleCount); mOwner->writeInt32(it->sampleDuration); int32_t dur = (it->sampleDurationUs * mTimeScale + 5E5) / 1E6; mOwner->writeInt32(dur); } mOwner->endBox(); // stts Loading Loading @@ -1717,7 +1741,7 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->writeInt64((*it)); } } mOwner->endBox(); // co64 mOwner->endBox(); // stco or co64 mOwner->endBox(); // stbl mOwner->endBox(); // minf Loading Loading
include/media/stagefright/MPEG4Writer.h +2 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ public: void endBox(); uint32_t interleaveDuration() const { return mInterleaveDurationUs; } status_t setInterleaveDuration(uint32_t duration); int32_t getTimeScale() const { return mTimeScale; } protected: virtual ~MPEG4Writer(); Loading @@ -72,6 +73,7 @@ private: bool mStreamableFile; off_t mEstimatedMoovBoxSize; uint32_t mInterleaveDurationUs; int32_t mTimeScale; int64_t mStartTimestampUs; Mutex mLock; Loading
include/media/stagefright/MetaData.h +1 −0 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ enum { kKeyDiscNumber = 'dnum', // cstring kKeyDate = 'date', // cstring kKeyWriter = 'writ', // cstring kKeyTimeScale = 'tmsl', // int32_t // video profile and level kKeyVideoProfile = 'vprf', // int32_t Loading
media/libmediaplayerservice/StagefrightRecorder.cpp +72 −12 Original line number Diff line number Diff line Loading @@ -381,12 +381,12 @@ status_t StagefrightRecorder::setParamInterleaveDuration(int32_t durationUs) { return OK; } // 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 (seconds) between 2 neighboring I frames status_t StagefrightRecorder::setParamVideoIFramesInterval(int32_t interval) { LOGV("setParamVideoIFramesInterval: %d seconds", interval); mIFramesInterval = interval; // If seconds < 0, only the first frame is I frame, and rest are all P frames // If seconds == 0, all frames are encoded as I frames. No P frames // If seconds > 0, it is the time spacing (seconds) between 2 neighboring I frames status_t StagefrightRecorder::setParamVideoIFramesInterval(int32_t seconds) { LOGV("setParamVideoIFramesInterval: %d seconds", seconds); mIFramesIntervalSec = seconds; return OK; } Loading Loading @@ -444,6 +444,44 @@ status_t StagefrightRecorder::setParamVideoEncoderLevel(int32_t level) { return OK; } status_t StagefrightRecorder::setParamMovieTimeScale(int32_t timeScale) { LOGV("setParamMovieTimeScale: %d", timeScale); // The range is set to be the same as the audio's time scale range // since audio's time scale has a wider range. if (timeScale < 600 || timeScale > 96000) { LOGE("Time scale (%d) for movie is out of range [600, 96000]", timeScale); return BAD_VALUE; } mMovieTimeScale = timeScale; return OK; } status_t StagefrightRecorder::setParamVideoTimeScale(int32_t timeScale) { LOGV("setParamVideoTimeScale: %d", timeScale); // 60000 is chosen to make sure that each video frame from a 60-fps // video has 1000 ticks. if (timeScale < 600 || timeScale > 60000) { LOGE("Time scale (%d) for video is out of range [600, 60000]", timeScale); return BAD_VALUE; } mVideoTimeScale = timeScale; return OK; } status_t StagefrightRecorder::setParamAudioTimeScale(int32_t timeScale) { LOGV("setParamAudioTimeScale: %d", timeScale); // 96000 Hz is the highest sampling rate support in AAC. if (timeScale < 600 || timeScale > 96000) { LOGE("Time scale (%d) for audio is out of range [600, 96000]", timeScale); return BAD_VALUE; } mAudioTimeScale = timeScale; return OK; } status_t StagefrightRecorder::setParameter( const String8 &key, const String8 &value) { LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string()); Loading @@ -462,6 +500,11 @@ status_t StagefrightRecorder::setParameter( if (safe_strtoi32(value.string(), &durationUs)) { return setParamInterleaveDuration(durationUs); } } else if (key == "param-movie-time-scale") { int32_t timeScale; if (safe_strtoi32(value.string(), &timeScale)) { return setParamMovieTimeScale(timeScale); } } else if (key == "param-use-64bit-offset") { int32_t use64BitOffset; if (safe_strtoi32(value.string(), &use64BitOffset)) { Loading Loading @@ -492,15 +535,20 @@ status_t StagefrightRecorder::setParameter( if (safe_strtoi32(value.string(), &audio_bitrate)) { return setParamAudioEncodingBitRate(audio_bitrate); } } else if (key == "audio-param-time-scale") { int32_t timeScale; if (safe_strtoi32(value.string(), &timeScale)) { return setParamAudioTimeScale(timeScale); } } else if (key == "video-param-encoding-bitrate") { int32_t video_bitrate; if (safe_strtoi32(value.string(), &video_bitrate)) { return setParamVideoEncodingBitRate(video_bitrate); } } else if (key == "video-param-i-frames-interval") { int32_t interval; if (safe_strtoi32(value.string(), &interval)) { return setParamVideoIFramesInterval(interval); int32_t seconds; if (safe_strtoi32(value.string(), &seconds)) { return setParamVideoIFramesInterval(seconds); } } else if (key == "video-param-encoder-profile") { int32_t profile; Loading @@ -517,6 +565,11 @@ status_t StagefrightRecorder::setParameter( if (safe_strtoi32(value.string(), &cameraId)) { return setParamVideoCameraId(cameraId); } } else if (key == "video-param-time-scale") { int32_t timeScale; if (safe_strtoi32(value.string(), &timeScale)) { return setParamVideoTimeScale(timeScale); } } else { LOGE("setParameter: failed to find key %s", key.string()); } Loading Loading @@ -637,6 +690,7 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() { encMeta->setInt32(kKeyChannelCount, mAudioChannels); encMeta->setInt32(kKeySampleRate, mSampleRate); encMeta->setInt32(kKeyBitRate, mAudioBitRate); encMeta->setInt32(kKeyTimeScale, mAudioTimeScale); OMXClient client; CHECK_EQ(client.connect(), OK); Loading Loading @@ -877,10 +931,11 @@ status_t StagefrightRecorder::setupVideoEncoder(const sp<MediaWriter>& writer) { enc_meta->setInt32(kKeyWidth, width); enc_meta->setInt32(kKeyHeight, height); enc_meta->setInt32(kKeyIFramesInterval, mIFramesInterval); enc_meta->setInt32(kKeyIFramesInterval, mIFramesIntervalSec); enc_meta->setInt32(kKeyStride, stride); enc_meta->setInt32(kKeySliceHeight, sliceHeight); enc_meta->setInt32(kKeyColorFormat, colorFormat); enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale); if (mVideoEncoderProfile != -1) { enc_meta->setInt32(kKeyVideoProfile, mVideoEncoderProfile); } Loading Loading @@ -918,6 +973,7 @@ status_t StagefrightRecorder::setupAudioEncoder(const sp<MediaWriter>& writer) { if (audioEncoder == NULL) { return UNKNOWN_ERROR; } writer->addSource(audioEncoder); return OK; } Loading Loading @@ -954,6 +1010,7 @@ status_t StagefrightRecorder::startMPEG4Recording() { meta->setInt32(kKeyFileType, mOutputFormat); meta->setInt32(kKeyBitRate, totalBitRate); meta->setInt32(kKey64BitFileOffset, mUse64BitFileOffset); meta->setInt32(kKeyTimeScale, mMovieTimeScale); if (mTrackEveryNumberOfFrames > 0) { meta->setInt32(kKeyTrackFrameStatus, mTrackEveryNumberOfFrames); } Loading Loading @@ -1024,9 +1081,12 @@ status_t StagefrightRecorder::reset() { mAudioChannels = 1; mAudioBitRate = 12200; mInterleaveDurationUs = 0; mIFramesInterval = 1; mIFramesIntervalSec = 1; mAudioSourceNode = 0; mUse64BitFileOffset = false; mMovieTimeScale = 1000; mAudioTimeScale = 1000; mVideoTimeScale = 1000; mCameraId = 0; mVideoEncoderProfile = -1; mVideoEncoderLevel = -1; Loading Loading @@ -1108,7 +1168,7 @@ status_t StagefrightRecorder::dump(int fd, const Vector<String16>& args) const { result.append(buffer); snprintf(buffer, SIZE, " Encoder level: %d\n", mVideoEncoderLevel); result.append(buffer); snprintf(buffer, SIZE, " I frames interval (s): %d\n", mIFramesInterval); snprintf(buffer, SIZE, " I frames interval (s): %d\n", mIFramesIntervalSec); result.append(buffer); snprintf(buffer, SIZE, " Frame size (pixels): %dx%d\n", mVideoWidth, mVideoHeight); result.append(buffer); Loading
media/libmediaplayerservice/StagefrightRecorder.h +8 −2 Original line number Diff line number Diff line Loading @@ -81,10 +81,13 @@ private: int32_t mAudioChannels; int32_t mSampleRate; int32_t mInterleaveDurationUs; int32_t mIFramesInterval; int32_t mIFramesIntervalSec; int32_t mCameraId; int32_t mVideoEncoderProfile; int32_t mVideoEncoderLevel; int32_t mMovieTimeScale; int32_t mVideoTimeScale; int32_t mAudioTimeScale; int64_t mMaxFileSizeBytes; int64_t mMaxFileDurationUs; int32_t mTrackEveryNumberOfFrames; Loading @@ -109,17 +112,20 @@ private: status_t setParamAudioEncodingBitRate(int32_t bitRate); status_t setParamAudioNumberOfChannels(int32_t channles); status_t setParamAudioSamplingRate(int32_t sampleRate); status_t setParamAudioTimeScale(int32_t timeScale); status_t setParamVideoEncodingBitRate(int32_t bitRate); status_t setParamVideoIFramesInterval(int32_t interval); status_t setParamVideoIFramesInterval(int32_t seconds); status_t setParamVideoEncoderProfile(int32_t profile); status_t setParamVideoEncoderLevel(int32_t level); status_t setParamVideoCameraId(int32_t cameraId); status_t setParamVideoTimeScale(int32_t timeScale); status_t setParamTrackTimeStatus(int64_t timeDurationUs); status_t setParamTrackFrameStatus(int32_t nFrames); status_t setParamInterleaveDuration(int32_t durationUs); status_t setParam64BitFileOffset(bool use64BitFileOffset); status_t setParamMaxFileDurationUs(int64_t timeUs); status_t setParamMaxFileSizeBytes(int64_t bytes); status_t setParamMovieTimeScale(int32_t timeScale); void clipVideoBitRate(); void clipVideoFrameRate(); void clipVideoFrameWidth(); Loading
media/libstagefright/MPEG4Writer.cpp +63 −39 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ namespace android { class MPEG4Writer::Track { public: Track(MPEG4Writer *owner, const sp<MediaSource> &source); ~Track(); status_t start(MetaData *params); Loading @@ -61,12 +62,13 @@ private: volatile bool mResumed; int64_t mMaxTimeStampUs; int64_t mEstimatedTrackSizeBytes; int32_t mTimeScale; pthread_t mThread; struct SampleInfo { size_t size; int64_t timestamp; int64_t timestampUs; }; List<SampleInfo> mSampleInfos; bool mSamplesHaveSameSize; Loading @@ -92,11 +94,11 @@ private: struct SttsTableEntry { SttsTableEntry(uint32_t count, uint32_t duration) : sampleCount(count), sampleDuration(duration) {} SttsTableEntry(uint32_t count, uint32_t durationUs) : sampleCount(count), sampleDurationUs(durationUs) {} uint32_t sampleCount; uint32_t sampleDuration; uint32_t sampleDurationUs; }; List<SttsTableEntry> mSttsTableEntries; Loading Loading @@ -270,6 +272,13 @@ status_t MPEG4Writer::start(MetaData *param) { return OK; } if (!param || !param->findInt32(kKeyTimeScale, &mTimeScale)) { mTimeScale = 1000; } CHECK(mTimeScale > 0); LOGV("movie time scale: %d", mTimeScale); mStreamableFile = true; mWriteMoovBoxToMemory = false; mMoovBoxBuffer = NULL; Loading Loading @@ -336,14 +345,14 @@ void MPEG4Writer::stop() { return; } int64_t max_duration = 0; int64_t maxDurationUs = 0; for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it) { (*it)->stop(); int64_t duration = (*it)->getDurationUs(); if (duration > max_duration) { max_duration = duration; int64_t durationUs = (*it)->getDurationUs(); if (durationUs > maxDurationUs) { maxDurationUs = durationUs; } } Loading @@ -367,8 +376,7 @@ void MPEG4Writer::stop() { mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize); mMoovBoxBufferOffset = 0; CHECK(mMoovBoxBuffer != NULL); int32_t timeScale = 1000; int32_t duration = max_duration / timeScale; int32_t duration = (maxDurationUs * mTimeScale) / 1E6; beginBox("moov"); Loading @@ -376,7 +384,7 @@ void MPEG4Writer::stop() { writeInt32(0); // version=0, flags=0 writeInt32(now); // creation time writeInt32(now); // modification time writeInt32(timeScale); // timescale writeInt32(mTimeScale); // mvhd timescale writeInt32(duration); writeInt32(0x10000); // rate: 1.0 writeInt16(0x100); // volume Loading Loading @@ -655,7 +663,6 @@ void MPEG4Writer::setStartTimestampUs(int64_t timeUs) { } int64_t MPEG4Writer::getStartTimestampUs() { LOGI("getStartTimestampUs: %lld", mStartTimestampUs); Mutex::Autolock autoLock(mLock); return mStartTimestampUs; } Loading Loading @@ -683,6 +690,11 @@ MPEG4Writer::Track::Track( mGotAllCodecSpecificData(false), mReachedEOS(false) { getCodecSpecificDataFromInputFormatIfPossible(); if (!mMeta->findInt32(kKeyTimeScale, &mTimeScale)) { mTimeScale = 1000; } CHECK(mTimeScale > 0); } void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() { Loading Loading @@ -927,8 +939,8 @@ void MPEG4Writer::Track::threadEntry() { int64_t chunkTimestampUs = 0; int32_t nChunks = 0; int32_t nZeroLengthFrames = 0; int64_t lastTimestamp = 0; // Timestamp of the previous sample int64_t lastDuration = 0; // Time spacing between the previous two samples int64_t lastTimestampUs = 0; // Previous sample time stamp in ms int64_t lastDurationUs = 0; // Between the previous two samples in ms int32_t sampleCount = 1; // Sample count in the current stts table entry uint32_t previousSampleSize = 0; // Size of the previous sample int64_t previousPausedDurationUs = 0; Loading Loading @@ -1113,7 +1125,7 @@ void MPEG4Writer::Track::threadEntry() { } if (mResumed) { previousPausedDurationUs += (timestampUs - mMaxTimeStampUs - 1000 * lastDuration); previousPausedDurationUs += (timestampUs - mMaxTimeStampUs - lastDurationUs); mResumed = false; } Loading @@ -1124,12 +1136,11 @@ void MPEG4Writer::Track::threadEntry() { mMaxTimeStampUs = timestampUs; } // Our timestamp is in ms. info.timestamp = (timestampUs + 500) / 1000; info.timestampUs = timestampUs; mSampleInfos.push_back(info); if (mSampleInfos.size() > 2) { if (lastDuration != info.timestamp - lastTimestamp) { SttsTableEntry sttsEntry(sampleCount, lastDuration); if (lastDurationUs != info.timestampUs - lastTimestampUs) { SttsTableEntry sttsEntry(sampleCount, lastDurationUs); mSttsTableEntries.push_back(sttsEntry); sampleCount = 1; } else { Loading @@ -1142,8 +1153,8 @@ void MPEG4Writer::Track::threadEntry() { } previousSampleSize = info.size; } lastDuration = info.timestamp - lastTimestamp; lastTimestamp = info.timestamp; lastDurationUs = info.timestampUs - lastTimestampUs; lastTimestampUs = info.timestampUs; if (isSync != 0) { mStssTableEntries.push_back(mSampleInfos.size()); Loading Loading @@ -1213,11 +1224,11 @@ void MPEG4Writer::Track::threadEntry() { // there is no frame time after it, just repeat the previous // frame's duration. if (mSampleInfos.size() == 1) { lastDuration = 0; // A single sample's duration lastDurationUs = 0; // A single sample's duration } else { ++sampleCount; // Count for the last sample } SttsTableEntry sttsEntry(sampleCount, lastDuration); SttsTableEntry sttsEntry(sampleCount, lastDurationUs); mSttsTableEntries.push_back(sttsEntry); mReachedEOS = true; LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames - %s", Loading Loading @@ -1254,7 +1265,8 @@ void MPEG4Writer::Track::findMinAvgMaxSampleDurationMs( int32_t maxSampleDurationMs = 0; for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin(); it != mSttsTableEntries.end(); ++it) { int32_t sampleDurationMs = static_cast<int32_t>(it->sampleDuration); int32_t sampleDurationMs = (static_cast<int32_t>(it->sampleDurationUs) + 500) / 1000; if (sampleDurationMs > maxSampleDurationMs) { maxSampleDurationMs = sampleDurationMs; } else if (sampleDurationMs < minSampleDurationMs) { Loading Loading @@ -1370,10 +1382,13 @@ void MPEG4Writer::Track::writeTrackHeader( CHECK(success); bool is_audio = !strncasecmp(mime, "audio/", 6); int32_t timeScale = 1000; int32_t duration = getDurationUs() / timeScale; LOGV("%s track time scale: %d", is_audio? "Audio": "Video", mTimeScale); time_t now = time(NULL); int32_t mvhdTimeScale = mOwner->getTimeScale(); int64_t trakDurationUs = getDurationUs(); mOwner->beginBox("trak"); Loading @@ -1385,7 +1400,9 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->writeInt32(now); // modification time mOwner->writeInt32(trackID); mOwner->writeInt32(0); // reserved mOwner->writeInt32(duration); int32_t tkhdDuration = (trakDurationUs * mvhdTimeScale + 5E5) / 1E6; mOwner->writeInt32(tkhdDuration); // in mvhd timescale mOwner->writeInt32(0); // reserved mOwner->writeInt32(0); // reserved mOwner->writeInt16(0); // layer Loading Loading @@ -1423,12 +1440,17 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->beginBox("elst"); mOwner->writeInt32(0); // version=0, flags=0: 32-bit time mOwner->writeInt32(2); // never ends with an empty list int64_t durationMs = (mStartTimestampUs - moovStartTimeUs) / 1000; mOwner->writeInt32(durationMs); // edit duration mOwner->writeInt32(-1); // empty edit box to signal starting time offset mOwner->writeInt32(1 << 16); // x1 rate mOwner->writeInt32(duration); // First elst entry: specify the starting time offset int64_t offsetUs = mStartTimestampUs - moovStartTimeUs; int32_t seg = (offsetUs * mvhdTimeScale + 5E5) / 1E6; mOwner->writeInt32(seg); // in mvhd timecale mOwner->writeInt32(-1); // starting time offset mOwner->writeInt32(1 << 16); // rate = 1.0 // Second elst entry: specify the track duration seg = (trakDurationUs * mvhdTimeScale + 5E5) / 1E6; mOwner->writeInt32(seg); // in mvhd timescale mOwner->writeInt32(0); mOwner->writeInt32(1 << 16); mOwner->endBox(); Loading @@ -1441,8 +1463,9 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt32(now); // creation time mOwner->writeInt32(now); // modification time mOwner->writeInt32(timeScale); // timescale mOwner->writeInt32(duration); // duration mOwner->writeInt32(mTimeScale); // 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 // 'e', 'n', 'g' for "English", for instance. // Each character is packed as the difference between its ASCII value and 0x60. Loading Loading @@ -1664,7 +1687,8 @@ void MPEG4Writer::Track::writeTrackHeader( for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin(); it != mSttsTableEntries.end(); ++it) { mOwner->writeInt32(it->sampleCount); mOwner->writeInt32(it->sampleDuration); int32_t dur = (it->sampleDurationUs * mTimeScale + 5E5) / 1E6; mOwner->writeInt32(dur); } mOwner->endBox(); // stts Loading Loading @@ -1717,7 +1741,7 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->writeInt64((*it)); } } mOwner->endBox(); // co64 mOwner->endBox(); // stco or co64 mOwner->endBox(); // stbl mOwner->endBox(); // minf Loading