blob: 5d6ea7cddc8a471200f11d02a39a9d6d8a6b8c96 [file] [log] [blame]
Andreas Hubere46b7be2009-07-14 16:56:47 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
James Dongabed93a2010-04-22 17:27:04 -070017//#define LOG_NDEBUG 0
18#define LOG_TAG "MPEG4Writer"
19#include <utils/Log.h>
20
Andreas Hubere46b7be2009-07-14 16:56:47 -070021#include <arpa/inet.h>
22
Andreas Hubere46b7be2009-07-14 16:56:47 -070023#include <pthread.h>
James Dongc67acb22010-10-07 20:20:59 -070024#include <sys/prctl.h>
25#include <sys/resource.h>
Andreas Hubere46b7be2009-07-14 16:56:47 -070026
27#include <media/stagefright/MPEG4Writer.h>
28#include <media/stagefright/MediaBuffer.h>
29#include <media/stagefright/MetaData.h>
Andreas Huberb5ceb9e2009-08-26 14:48:20 -070030#include <media/stagefright/MediaDebug.h>
Andreas Hubere6c40962009-09-10 14:13:30 -070031#include <media/stagefright/MediaDefs.h>
Andreas Huber71c27d92010-03-19 11:43:15 -070032#include <media/stagefright/MediaErrors.h>
Andreas Hubere46b7be2009-07-14 16:56:47 -070033#include <media/stagefright/MediaSource.h>
34#include <media/stagefright/Utils.h>
James Dong18244862010-05-11 14:57:02 -070035#include <media/mediarecorder.h>
James Dong2747e0e2010-11-18 20:59:13 -080036#include <sys/types.h>
37#include <sys/stat.h>
38#include <fcntl.h>
39#include <unistd.h>
Andreas Hubere46b7be2009-07-14 16:56:47 -070040
Andreas Huber45bac572010-07-01 08:19:52 -070041#include "include/ESDS.h"
42
Andreas Hubere46b7be2009-07-14 16:56:47 -070043namespace android {
44
James Dongcb7e65c2010-09-02 11:19:11 -070045static const int64_t kMax32BitFileSize = 0x007fffffffLL;
James Dong481e05e2010-08-06 00:29:03 -070046static const uint8_t kNalUnitTypeSeqParamSet = 0x07;
47static const uint8_t kNalUnitTypePicParamSet = 0x08;
James Dong89a01042010-10-21 17:58:14 -070048
49// Using longer adjustment period to suppress fluctuations in
50// the audio encoding paths
51static const int64_t kVideoMediaTimeAdjustPeriodTimeUs = 600000000LL; // 10 minutes
James Dong481e05e2010-08-06 00:29:03 -070052
Andreas Hubere46b7be2009-07-14 16:56:47 -070053class MPEG4Writer::Track {
54public:
Andreas Huber033e6c32009-09-09 16:36:12 -070055 Track(MPEG4Writer *owner, const sp<MediaSource> &source);
James Dong52d13f02010-07-02 11:39:06 -070056
Andreas Hubere46b7be2009-07-14 16:56:47 -070057 ~Track();
58
James Dong09936ed2010-06-24 19:04:27 -070059 status_t start(MetaData *params);
James Dongd0366622010-08-18 19:10:39 -070060 status_t stop();
61 status_t pause();
Andreas Huber033e6c32009-09-09 16:36:12 -070062 bool reachedEOS();
Andreas Hubere46b7be2009-07-14 16:56:47 -070063
Andreas Huber716582e2010-02-02 12:13:30 -080064 int64_t getDurationUs() const;
James Dong18244862010-05-11 14:57:02 -070065 int64_t getEstimatedTrackSizeBytes() const;
James Dong39a0b212010-06-23 00:18:40 -070066 void writeTrackHeader(int32_t trackID, bool use32BitOffset = true);
James Dongda8073c2010-07-30 17:41:22 -070067 void bufferChunk(int64_t timestampUs);
68 bool isAvc() const { return mIsAvc; }
69 bool isAudio() const { return mIsAudio; }
70 bool isMPEG4() const { return mIsMPEG4; }
James Dongb1262a82010-11-16 14:04:54 -080071 void addChunkOffset(off64_t offset);
James Dong3f51fa72010-08-18 03:32:26 -070072 status_t dump(int fd, const Vector<String16>& args) const;
Andreas Hubere46b7be2009-07-14 16:56:47 -070073
74private:
75 MPEG4Writer *mOwner;
76 sp<MetaData> mMeta;
Andreas Huberbe06d262009-08-14 14:37:10 -070077 sp<MediaSource> mSource;
Andreas Hubere46b7be2009-07-14 16:56:47 -070078 volatile bool mDone;
James Dong08c74732010-06-10 12:28:15 -070079 volatile bool mPaused;
80 volatile bool mResumed;
James Dongd7ef5b62011-01-25 12:37:43 -080081 volatile bool mStarted;
James Dongda8073c2010-07-30 17:41:22 -070082 bool mIsAvc;
83 bool mIsAudio;
84 bool mIsMPEG4;
James Dong91b22a92010-08-05 10:46:13 -070085 int64_t mTrackDurationUs;
James Dongb7208192010-08-02 19:13:40 -070086
87 // For realtime applications, we need to adjust the media clock
88 // for video track based on the audio media clock
89 bool mIsRealTimeRecording;
90 int64_t mMaxTimeStampUs;
James Dong18244862010-05-11 14:57:02 -070091 int64_t mEstimatedTrackSizeBytes;
James Dongcb7e65c2010-09-02 11:19:11 -070092 int64_t mMdatSizeBytes;
James Dong52d13f02010-07-02 11:39:06 -070093 int32_t mTimeScale;
Andreas Hubere46b7be2009-07-14 16:56:47 -070094
95 pthread_t mThread;
96
James Donge991e5f2010-07-28 13:18:14 -070097 // mNumSamples is used to track how many samples in mSampleSizes List.
98 // This is to reduce the cost associated with mSampleSizes.size() call,
99 // since it is O(n). Ideally, the fix should be in List class.
100 size_t mNumSamples;
James Dong7e397842010-07-28 10:24:39 -0700101 List<size_t> mSampleSizes;
James Dong2a4767e2010-04-20 11:50:11 -0700102 bool mSamplesHaveSameSize;
103
James Dong3300e962010-04-21 16:14:15 -0700104 List<MediaBuffer *> mChunkSamples;
James Dongcb7e65c2010-09-02 11:19:11 -0700105
106 size_t mNumStcoTableEntries;
James Dongb1262a82010-11-16 14:04:54 -0800107 List<off64_t> mChunkOffsets;
James Dong3300e962010-04-21 16:14:15 -0700108
James Dongcb7e65c2010-09-02 11:19:11 -0700109 size_t mNumStscTableEntries;
James Dong3300e962010-04-21 16:14:15 -0700110 struct StscTableEntry {
111
112 StscTableEntry(uint32_t chunk, uint32_t samples, uint32_t id)
113 : firstChunk(chunk),
114 samplesPerChunk(samples),
115 sampleDescriptionId(id) {}
116
117 uint32_t firstChunk;
118 uint32_t samplesPerChunk;
119 uint32_t sampleDescriptionId;
120 };
121 List<StscTableEntry> mStscTableEntries;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700122
James Dongcb7e65c2010-09-02 11:19:11 -0700123 size_t mNumStssTableEntries;
James Dongabed93a2010-04-22 17:27:04 -0700124 List<int32_t> mStssTableEntries;
125
James Dongcb7e65c2010-09-02 11:19:11 -0700126 size_t mNumSttsTableEntries;
James Dong2a4767e2010-04-20 11:50:11 -0700127 struct SttsTableEntry {
128
James Dong52d13f02010-07-02 11:39:06 -0700129 SttsTableEntry(uint32_t count, uint32_t durationUs)
130 : sampleCount(count), sampleDurationUs(durationUs) {}
James Dong2a4767e2010-04-20 11:50:11 -0700131
132 uint32_t sampleCount;
James Dong52d13f02010-07-02 11:39:06 -0700133 uint32_t sampleDurationUs;
James Dong2a4767e2010-04-20 11:50:11 -0700134 };
135 List<SttsTableEntry> mSttsTableEntries;
136
James Dong481e05e2010-08-06 00:29:03 -0700137 // Sequence parameter set or picture parameter set
138 struct AVCParamSet {
139 AVCParamSet(uint16_t length, const uint8_t *data)
140 : mLength(length), mData(data) {}
141
142 uint16_t mLength;
143 const uint8_t *mData;
144 };
145 List<AVCParamSet> mSeqParamSets;
146 List<AVCParamSet> mPicParamSets;
147 uint8_t mProfileIdc;
148 uint8_t mProfileCompatible;
149 uint8_t mLevelIdc;
150
Andreas Hubere46b7be2009-07-14 16:56:47 -0700151 void *mCodecSpecificData;
152 size_t mCodecSpecificDataSize;
Andreas Huberf4e5baa2010-04-09 14:25:46 -0700153 bool mGotAllCodecSpecificData;
James Dong09936ed2010-06-24 19:04:27 -0700154 bool mTrackingProgressStatus;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700155
Andreas Huber033e6c32009-09-09 16:36:12 -0700156 bool mReachedEOS;
James Dong9db798d2010-05-13 11:47:36 -0700157 int64_t mStartTimestampUs;
James Dong09936ed2010-06-24 19:04:27 -0700158 int64_t mPreviousTrackTimeUs;
159 int64_t mTrackEveryTimeDurationUs;
Andreas Huber033e6c32009-09-09 16:36:12 -0700160
James Dongacee8e72010-10-03 10:59:26 -0700161 // Has the media time adjustment for video started?
162 bool mIsMediaTimeAdjustmentOn;
163 // The time stamp when previous media time adjustment period starts
164 int64_t mPrevMediaTimeAdjustTimestampUs;
165 // Number of vidoe frames whose time stamp may be adjusted
166 int64_t mMediaTimeAdjustNumFrames;
167 // The sample number when previous meida time adjustmnet period starts
168 int64_t mPrevMediaTimeAdjustSample;
169 // The total accumulated drift time within a period of
170 // kVideoMediaTimeAdjustPeriodTimeUs.
171 int64_t mTotalDriftTimeToAdjustUs;
172 // The total accumalated drift time since the start of the recording
173 // excluding the current time adjustment period
174 int64_t mPrevTotalAccumDriftTimeUs;
175
176 // Update the audio track's drift information.
177 void updateDriftTime(const sp<MetaData>& meta);
178
179 // Adjust the time stamp of the video track according to
180 // the drift time information from the audio track.
181 void adjustMediaTime(int64_t *timestampUs);
182
Andreas Hubere46b7be2009-07-14 16:56:47 -0700183 static void *ThreadWrapper(void *me);
James Dongd0366622010-08-18 19:10:39 -0700184 status_t threadEntry();
Andreas Hubere46b7be2009-07-14 16:56:47 -0700185
James Dong481e05e2010-08-06 00:29:03 -0700186 const uint8_t *parseParamSet(
187 const uint8_t *data, size_t length, int type, size_t *paramSetLen);
188
Andreas Huber71c27d92010-03-19 11:43:15 -0700189 status_t makeAVCCodecSpecificData(
190 const uint8_t *data, size_t size);
James Dong481e05e2010-08-06 00:29:03 -0700191 status_t copyAVCCodecSpecificData(
192 const uint8_t *data, size_t size);
193 status_t parseAVCCodecSpecificData(
194 const uint8_t *data, size_t size);
James Dongd4760c22010-06-26 08:24:47 -0700195
196 // Track authoring progress status
James Dong85edea72010-07-15 19:08:20 -0700197 void trackProgressStatus(int64_t timeUs, status_t err = OK);
James Dong09936ed2010-06-24 19:04:27 -0700198 void initTrackingProgressStatus(MetaData *params);
Andreas Huber71c27d92010-03-19 11:43:15 -0700199
Andreas Huber45bac572010-07-01 08:19:52 -0700200 void getCodecSpecificDataFromInputFormatIfPossible();
201
James Dongeff30e32010-08-13 14:16:26 -0700202 // Determine the track time scale
203 // If it is an audio track, try to use the sampling rate as
204 // the time scale; however, if user chooses the overwrite
205 // value, the user-supplied time scale will be used.
206 void setTimeScale();
207
James Dong62948fa2010-08-19 13:52:47 -0700208 // Simple validation on the codec specific data
209 status_t checkCodecSpecificData() const;
James Dongb9d7e012010-11-09 11:15:47 -0800210 int32_t mRotation;
James Dong62948fa2010-08-19 13:52:47 -0700211
James Dongcb7e65c2010-09-02 11:19:11 -0700212 void updateTrackSizeEstimate();
213 void addOneStscTableEntry(size_t chunkId, size_t sampleId);
214 void addOneStssTableEntry(size_t sampleId);
215 void addOneSttsTableEntry(size_t sampleCount, int64_t durationUs);
216
Andreas Hubere46b7be2009-07-14 16:56:47 -0700217 Track(const Track &);
218 Track &operator=(const Track &);
219};
220
221MPEG4Writer::MPEG4Writer(const char *filename)
James Dong2747e0e2010-11-18 20:59:13 -0800222 : mFd(-1),
223 mInitCheck(NO_INIT),
James Dong7755cdd2010-09-02 10:49:55 -0700224 mUse4ByteNalLength(true),
James Dong39a0b212010-06-23 00:18:40 -0700225 mUse32BitOffset(true),
James Dong6a9e39a2010-10-04 16:41:53 -0700226 mIsFileSizeLimitExplicitlyRequested(false),
James Dong08c74732010-06-10 12:28:15 -0700227 mPaused(false),
228 mStarted(false),
Andreas Hubere46b7be2009-07-14 16:56:47 -0700229 mOffset(0),
James Dong3300e962010-04-21 16:14:15 -0700230 mMdatOffset(0),
James Dongb5e74232010-05-07 10:26:24 -0700231 mEstimatedMoovBoxSize(0),
James Dong63299c02010-07-28 10:08:03 -0700232 mInterleaveDurationUs(1000000) {
James Dong2747e0e2010-11-18 20:59:13 -0800233
234 mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC);
235 if (mFd >= 0) {
236 mInitCheck = OK;
237 }
Andreas Hubere46b7be2009-07-14 16:56:47 -0700238}
239
Andreas Huberea6a38c2009-11-16 15:43:38 -0800240MPEG4Writer::MPEG4Writer(int fd)
James Dong2747e0e2010-11-18 20:59:13 -0800241 : mFd(dup(fd)),
242 mInitCheck(mFd < 0? NO_INIT: OK),
James Dong7755cdd2010-09-02 10:49:55 -0700243 mUse4ByteNalLength(true),
James Dong39a0b212010-06-23 00:18:40 -0700244 mUse32BitOffset(true),
James Dong6a9e39a2010-10-04 16:41:53 -0700245 mIsFileSizeLimitExplicitlyRequested(false),
James Dong08c74732010-06-10 12:28:15 -0700246 mPaused(false),
247 mStarted(false),
Andreas Huberea6a38c2009-11-16 15:43:38 -0800248 mOffset(0),
James Dong3300e962010-04-21 16:14:15 -0700249 mMdatOffset(0),
James Dongb5e74232010-05-07 10:26:24 -0700250 mEstimatedMoovBoxSize(0),
James Dong63299c02010-07-28 10:08:03 -0700251 mInterleaveDurationUs(1000000) {
Andreas Huberea6a38c2009-11-16 15:43:38 -0800252}
253
Andreas Hubere46b7be2009-07-14 16:56:47 -0700254MPEG4Writer::~MPEG4Writer() {
255 stop();
256
James Dongcb7e65c2010-09-02 11:19:11 -0700257 while (!mTracks.empty()) {
258 List<Track *>::iterator it = mTracks.begin();
Andreas Hubere46b7be2009-07-14 16:56:47 -0700259 delete *it;
James Dongcb7e65c2010-09-02 11:19:11 -0700260 (*it) = NULL;
261 mTracks.erase(it);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700262 }
263 mTracks.clear();
264}
265
James Dong3f51fa72010-08-18 03:32:26 -0700266status_t MPEG4Writer::dump(
267 int fd, const Vector<String16>& args) {
268 const size_t SIZE = 256;
269 char buffer[SIZE];
270 String8 result;
271 snprintf(buffer, SIZE, " MPEG4Writer %p\n", this);
272 result.append(buffer);
273 snprintf(buffer, SIZE, " mStarted: %s\n", mStarted? "true": "false");
274 result.append(buffer);
275 ::write(fd, result.string(), result.size());
276 for (List<Track *>::iterator it = mTracks.begin();
277 it != mTracks.end(); ++it) {
278 (*it)->dump(fd, args);
279 }
280 return OK;
281}
282
283status_t MPEG4Writer::Track::dump(
284 int fd, const Vector<String16>& args) const {
285 const size_t SIZE = 256;
286 char buffer[SIZE];
287 String8 result;
288 snprintf(buffer, SIZE, " %s track\n", mIsAudio? "Audio": "Video");
289 result.append(buffer);
290 snprintf(buffer, SIZE, " reached EOS: %s\n",
291 mReachedEOS? "true": "false");
292 result.append(buffer);
293 ::write(fd, result.string(), result.size());
294 return OK;
295}
296
Andreas Huber996dddf2010-01-25 15:30:31 -0800297status_t MPEG4Writer::addSource(const sp<MediaSource> &source) {
Andreas Huber033e6c32009-09-09 16:36:12 -0700298 Track *track = new Track(this, source);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700299 mTracks.push_back(track);
Andreas Huber996dddf2010-01-25 15:30:31 -0800300
301 return OK;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700302}
303
James Dong09936ed2010-06-24 19:04:27 -0700304status_t MPEG4Writer::startTracks(MetaData *params) {
James Dong08c74732010-06-10 12:28:15 -0700305 for (List<Track *>::iterator it = mTracks.begin();
306 it != mTracks.end(); ++it) {
James Dong09936ed2010-06-24 19:04:27 -0700307 status_t err = (*it)->start(params);
James Dong08c74732010-06-10 12:28:15 -0700308
309 if (err != OK) {
310 for (List<Track *>::iterator it2 = mTracks.begin();
311 it2 != it; ++it2) {
312 (*it2)->stop();
313 }
314
315 return err;
316 }
317 }
318 return OK;
319}
320
James Dong6feaa462010-06-20 08:20:54 -0700321int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) {
322 // This implementation is highly experimental/heurisitic.
323 //
324 // Statistical analysis shows that metadata usually accounts
325 // for a small portion of the total file size, usually < 0.6%.
James Dong6feaa462010-06-20 08:20:54 -0700326
James Dong22b37fa2010-10-19 21:28:47 -0700327 // The default MIN_MOOV_BOX_SIZE is set to 0.6% x 1MB / 2,
James Dong6feaa462010-06-20 08:20:54 -0700328 // where 1MB is the common file size limit for MMS application.
James Dong22b37fa2010-10-19 21:28:47 -0700329 // The default MAX _MOOV_BOX_SIZE value is based on about 3
James Dong6feaa462010-06-20 08:20:54 -0700330 // minute video recording with a bit rate about 3 Mbps, because
331 // statistics also show that most of the video captured are going
332 // to be less than 3 minutes.
333
334 // If the estimation is wrong, we will pay the price of wasting
335 // some reserved space. This should not happen so often statistically.
336 static const int32_t factor = mUse32BitOffset? 1: 2;
James Dong22b37fa2010-10-19 21:28:47 -0700337 static const int64_t MIN_MOOV_BOX_SIZE = 3 * 1024; // 3 KB
James Dong6feaa462010-06-20 08:20:54 -0700338 static const int64_t MAX_MOOV_BOX_SIZE = (180 * 3000000 * 6LL / 8000);
339 int64_t size = MIN_MOOV_BOX_SIZE;
340
James Dong22b37fa2010-10-19 21:28:47 -0700341 // Max file size limit is set
James Dong6a9e39a2010-10-04 16:41:53 -0700342 if (mMaxFileSizeLimitBytes != 0 && mIsFileSizeLimitExplicitlyRequested) {
James Dong22b37fa2010-10-19 21:28:47 -0700343 size = mMaxFileSizeLimitBytes * 6 / 1000;
344 }
345
346 // Max file duration limit is set
347 if (mMaxFileDurationLimitUs != 0) {
348 if (bitRate > 0) {
349 int64_t size2 =
350 ((mMaxFileDurationLimitUs * bitRate * 6) / 1000 / 8000000);
351 if (mMaxFileSizeLimitBytes != 0 && mIsFileSizeLimitExplicitlyRequested) {
352 // When both file size and duration limits are set,
353 // we use the smaller limit of the two.
354 if (size > size2) {
355 size = size2;
356 }
357 } else {
358 // Only max file duration limit is set
359 size = size2;
360 }
James Dong6feaa462010-06-20 08:20:54 -0700361 }
362 }
James Dong22b37fa2010-10-19 21:28:47 -0700363
James Dong6feaa462010-06-20 08:20:54 -0700364 if (size < MIN_MOOV_BOX_SIZE) {
365 size = MIN_MOOV_BOX_SIZE;
366 }
367
368 // Any long duration recording will be probably end up with
369 // non-streamable mp4 file.
370 if (size > MAX_MOOV_BOX_SIZE) {
371 size = MAX_MOOV_BOX_SIZE;
372 }
373
James Dong6a9e39a2010-10-04 16:41:53 -0700374 LOGI("limits: %lld/%lld bytes/us, bit rate: %d bps and the estimated"
James Dong6feaa462010-06-20 08:20:54 -0700375 " moov size %lld bytes",
376 mMaxFileSizeLimitBytes, mMaxFileDurationLimitUs, bitRate, size);
377 return factor * size;
378}
379
380status_t MPEG4Writer::start(MetaData *param) {
James Dong2747e0e2010-11-18 20:59:13 -0800381 if (mInitCheck != OK) {
Andreas Huber033e6c32009-09-09 16:36:12 -0700382 return UNKNOWN_ERROR;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700383 }
384
James Dong6a9e39a2010-10-04 16:41:53 -0700385 /*
386 * Check mMaxFileSizeLimitBytes at the beginning
387 * since mMaxFileSizeLimitBytes may be implicitly
388 * changed later for 32-bit file offset even if
389 * user does not ask to set it explicitly.
390 */
391 if (mMaxFileSizeLimitBytes != 0) {
392 mIsFileSizeLimitExplicitlyRequested = true;
393 }
394
James Dong6feaa462010-06-20 08:20:54 -0700395 int32_t use64BitOffset;
396 if (param &&
397 param->findInt32(kKey64BitFileOffset, &use64BitOffset) &&
398 use64BitOffset) {
399 mUse32BitOffset = false;
400 }
401
James Dongcb7e65c2010-09-02 11:19:11 -0700402 if (mUse32BitOffset) {
403 // Implicit 32 bit file size limit
404 if (mMaxFileSizeLimitBytes == 0) {
405 mMaxFileSizeLimitBytes = kMax32BitFileSize;
406 }
407
408 // If file size is set to be larger than the 32 bit file
409 // size limit, treat it as an error.
410 if (mMaxFileSizeLimitBytes > kMax32BitFileSize) {
James Dongacee8e72010-10-03 10:59:26 -0700411 LOGW("32-bit file size limit (%lld bytes) too big. "
James Donga4fb8162010-09-08 15:13:36 -0700412 "It is changed to %lld bytes",
413 mMaxFileSizeLimitBytes, kMax32BitFileSize);
414 mMaxFileSizeLimitBytes = kMax32BitFileSize;
James Dongcb7e65c2010-09-02 11:19:11 -0700415 }
416 }
417
James Dong7755cdd2010-09-02 10:49:55 -0700418 int32_t use2ByteNalLength;
419 if (param &&
420 param->findInt32(kKey2ByteNalLength, &use2ByteNalLength) &&
421 use2ByteNalLength) {
422 mUse4ByteNalLength = false;
James Dong6feaa462010-06-20 08:20:54 -0700423 }
424
James Dong438e4f62010-06-23 16:51:39 -0700425 mStartTimestampUs = -1;
James Dong09936ed2010-06-24 19:04:27 -0700426
James Dong08c74732010-06-10 12:28:15 -0700427 if (mStarted) {
428 if (mPaused) {
429 mPaused = false;
James Dong09936ed2010-06-24 19:04:27 -0700430 return startTracks(param);
James Dong08c74732010-06-10 12:28:15 -0700431 }
432 return OK;
433 }
434
James Dong52d13f02010-07-02 11:39:06 -0700435 if (!param ||
436 !param->findInt32(kKeyTimeScale, &mTimeScale)) {
437 mTimeScale = 1000;
438 }
439 CHECK(mTimeScale > 0);
440 LOGV("movie time scale: %d", mTimeScale);
441
James Dongb5e74232010-05-07 10:26:24 -0700442 mStreamableFile = true;
443 mWriteMoovBoxToMemory = false;
444 mMoovBoxBuffer = NULL;
445 mMoovBoxBufferOffset = 0;
446
Andreas Hubere46b7be2009-07-14 16:56:47 -0700447 beginBox("ftyp");
James Dong09936ed2010-06-24 19:04:27 -0700448 {
449 int32_t fileType;
450 if (param && param->findInt32(kKeyFileType, &fileType) &&
451 fileType != OUTPUT_FORMAT_MPEG_4) {
452 writeFourcc("3gp4");
453 } else {
454 writeFourcc("isom");
455 }
456 }
Andreas Hubere46b7be2009-07-14 16:56:47 -0700457 writeInt32(0);
458 writeFourcc("isom");
James Dong09936ed2010-06-24 19:04:27 -0700459 writeFourcc("3gp4");
Andreas Hubere46b7be2009-07-14 16:56:47 -0700460 endBox();
461
James Dongb5e74232010-05-07 10:26:24 -0700462 mFreeBoxOffset = mOffset;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700463
James Dongb5e74232010-05-07 10:26:24 -0700464 if (mEstimatedMoovBoxSize == 0) {
James Dong6feaa462010-06-20 08:20:54 -0700465 int32_t bitRate = -1;
466 if (param) {
467 param->findInt32(kKeyBitRate, &bitRate);
468 }
469 mEstimatedMoovBoxSize = estimateMoovBoxSize(bitRate);
James Dongb5e74232010-05-07 10:26:24 -0700470 }
471 CHECK(mEstimatedMoovBoxSize >= 8);
James Dongb1262a82010-11-16 14:04:54 -0800472 lseek64(mFd, mFreeBoxOffset, SEEK_SET);
James Dongb5e74232010-05-07 10:26:24 -0700473 writeInt32(mEstimatedMoovBoxSize);
474 write("free", 4);
475
476 mMdatOffset = mFreeBoxOffset + mEstimatedMoovBoxSize;
477 mOffset = mMdatOffset;
James Dongb1262a82010-11-16 14:04:54 -0800478 lseek64(mFd, mMdatOffset, SEEK_SET);
James Dong39a0b212010-06-23 00:18:40 -0700479 if (mUse32BitOffset) {
480 write("????mdat", 8);
481 } else {
482 write("\x00\x00\x00\x01mdat????????", 16);
483 }
James Dongda8073c2010-07-30 17:41:22 -0700484
485 status_t err = startWriterThread();
James Dong08c74732010-06-10 12:28:15 -0700486 if (err != OK) {
487 return err;
488 }
James Dongda8073c2010-07-30 17:41:22 -0700489
490 err = startTracks(param);
491 if (err != OK) {
492 return err;
493 }
494
James Dong08c74732010-06-10 12:28:15 -0700495 mStarted = true;
496 return OK;
497}
498
James Dongcb7e65c2010-09-02 11:19:11 -0700499bool MPEG4Writer::use32BitFileOffset() const {
500 return mUse32BitOffset;
501}
502
James Dongd0366622010-08-18 19:10:39 -0700503status_t MPEG4Writer::pause() {
James Dong2747e0e2010-11-18 20:59:13 -0800504 if (mInitCheck != OK) {
James Dongd0366622010-08-18 19:10:39 -0700505 return OK;
James Dong08c74732010-06-10 12:28:15 -0700506 }
507 mPaused = true;
James Dongd0366622010-08-18 19:10:39 -0700508 status_t err = OK;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700509 for (List<Track *>::iterator it = mTracks.begin();
510 it != mTracks.end(); ++it) {
James Dongd0366622010-08-18 19:10:39 -0700511 status_t status = (*it)->pause();
512 if (status != OK) {
513 err = status;
514 }
Andreas Hubere46b7be2009-07-14 16:56:47 -0700515 }
James Dongd0366622010-08-18 19:10:39 -0700516 return err;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700517}
518
James Dongda8073c2010-07-30 17:41:22 -0700519void MPEG4Writer::stopWriterThread() {
James Dong42338ff2010-10-29 16:01:06 -0700520 LOGD("Stopping writer thread");
James Dongda8073c2010-07-30 17:41:22 -0700521
522 {
523 Mutex::Autolock autolock(mLock);
524
525 mDone = true;
526 mChunkReadyCondition.signal();
527 }
528
529 void *dummy;
530 pthread_join(mThread, &dummy);
James Dong42338ff2010-10-29 16:01:06 -0700531 LOGD("Writer thread stopped");
James Dongda8073c2010-07-30 17:41:22 -0700532}
533
James Dongb9d7e012010-11-09 11:15:47 -0800534/*
535 * MP4 file standard defines a composition matrix:
536 * | a b u |
537 * | c d v |
538 * | x y w |
539 *
540 * the element in the matrix is stored in the following
541 * order: {a, b, u, c, d, v, x, y, w},
542 * where a, b, c, d, x, and y is in 16.16 format, while
543 * u, v and w is in 2.30 format.
544 */
545void MPEG4Writer::writeCompositionMatrix(int degrees) {
546 LOGV("writeCompositionMatrix");
547 uint32_t a = 0x00010000;
548 uint32_t b = 0;
549 uint32_t c = 0;
550 uint32_t d = 0x00010000;
551 switch (degrees) {
552 case 0:
553 break;
554 case 90:
555 a = 0;
556 b = 0x00010000;
557 c = 0xFFFF0000;
558 d = 0;
559 break;
560 case 180:
561 a = 0xFFFF0000;
562 d = 0xFFFF0000;
563 break;
564 case 270:
565 a = 0;
566 b = 0xFFFF0000;
567 c = 0x00010000;
568 d = 0;
569 break;
570 default:
571 CHECK(!"Should never reach this unknown rotation");
572 break;
573 }
574
575 writeInt32(a); // a
576 writeInt32(b); // b
577 writeInt32(0); // u
578 writeInt32(c); // c
579 writeInt32(d); // d
580 writeInt32(0); // v
581 writeInt32(0); // x
582 writeInt32(0); // y
583 writeInt32(0x40000000); // w
584}
585
586
James Dongd0366622010-08-18 19:10:39 -0700587status_t MPEG4Writer::stop() {
James Dong2747e0e2010-11-18 20:59:13 -0800588 if (mInitCheck != OK) {
James Dongd0366622010-08-18 19:10:39 -0700589 return OK;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700590 }
591
James Dongd0366622010-08-18 19:10:39 -0700592 status_t err = OK;
James Dong52d13f02010-07-02 11:39:06 -0700593 int64_t maxDurationUs = 0;
James Dongda580762011-01-19 11:50:19 -0800594 int64_t minDurationUs = 0x7fffffffffffffffLL;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700595 for (List<Track *>::iterator it = mTracks.begin();
596 it != mTracks.end(); ++it) {
James Dongd0366622010-08-18 19:10:39 -0700597 status_t status = (*it)->stop();
598 if (err == OK && status != OK) {
599 err = status;
600 }
Andreas Hubere46b7be2009-07-14 16:56:47 -0700601
James Dong52d13f02010-07-02 11:39:06 -0700602 int64_t durationUs = (*it)->getDurationUs();
603 if (durationUs > maxDurationUs) {
604 maxDurationUs = durationUs;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700605 }
James Dongda580762011-01-19 11:50:19 -0800606 if (durationUs < minDurationUs) {
607 minDurationUs = durationUs;
608 }
609 }
610
611 if (mTracks.size() > 1) {
612 LOGD("Duration from tracks range is [%lld, %lld] us",
613 minDurationUs, maxDurationUs);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700614 }
615
James Dongda8073c2010-07-30 17:41:22 -0700616 stopWriterThread();
James Dongb5e74232010-05-07 10:26:24 -0700617
James Dongd0366622010-08-18 19:10:39 -0700618 // Do not write out movie header on error.
619 if (err != OK) {
James Dong2747e0e2010-11-18 20:59:13 -0800620 close(mFd);
621 mFd = -1;
622 mInitCheck = NO_INIT;
James Dongd0366622010-08-18 19:10:39 -0700623 mStarted = false;
624 return err;
625 }
626
Andreas Hubere46b7be2009-07-14 16:56:47 -0700627 // Fix up the size of the 'mdat' chunk.
James Dong39a0b212010-06-23 00:18:40 -0700628 if (mUse32BitOffset) {
James Dongb1262a82010-11-16 14:04:54 -0800629 lseek64(mFd, mMdatOffset, SEEK_SET);
James Dong39a0b212010-06-23 00:18:40 -0700630 int32_t size = htonl(static_cast<int32_t>(mOffset - mMdatOffset));
James Dongb1262a82010-11-16 14:04:54 -0800631 ::write(mFd, &size, 4);
James Dong39a0b212010-06-23 00:18:40 -0700632 } else {
James Dongb1262a82010-11-16 14:04:54 -0800633 lseek64(mFd, mMdatOffset + 8, SEEK_SET);
James Dong39a0b212010-06-23 00:18:40 -0700634 int64_t size = mOffset - mMdatOffset;
635 size = hton64(size);
James Dongb1262a82010-11-16 14:04:54 -0800636 ::write(mFd, &size, 8);
James Dong39a0b212010-06-23 00:18:40 -0700637 }
James Dongb1262a82010-11-16 14:04:54 -0800638 lseek64(mFd, mOffset, SEEK_SET);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700639
640 time_t now = time(NULL);
James Dongb1262a82010-11-16 14:04:54 -0800641 const off64_t moovOffset = mOffset;
James Dongb5e74232010-05-07 10:26:24 -0700642 mWriteMoovBoxToMemory = true;
643 mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
644 mMoovBoxBufferOffset = 0;
645 CHECK(mMoovBoxBuffer != NULL);
James Dongeff30e32010-08-13 14:16:26 -0700646 int32_t duration = (maxDurationUs * mTimeScale + 5E5) / 1E6;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700647
648 beginBox("moov");
649
650 beginBox("mvhd");
651 writeInt32(0); // version=0, flags=0
652 writeInt32(now); // creation time
653 writeInt32(now); // modification time
James Dong52d13f02010-07-02 11:39:06 -0700654 writeInt32(mTimeScale); // mvhd timescale
James Dong39a0b212010-06-23 00:18:40 -0700655 writeInt32(duration);
656 writeInt32(0x10000); // rate: 1.0
Andreas Hubere46b7be2009-07-14 16:56:47 -0700657 writeInt16(0x100); // volume
658 writeInt16(0); // reserved
659 writeInt32(0); // reserved
660 writeInt32(0); // reserved
James Dongb9d7e012010-11-09 11:15:47 -0800661 writeCompositionMatrix(0); // matrix
Andreas Hubere46b7be2009-07-14 16:56:47 -0700662 writeInt32(0); // predefined
663 writeInt32(0); // predefined
664 writeInt32(0); // predefined
665 writeInt32(0); // predefined
666 writeInt32(0); // predefined
667 writeInt32(0); // predefined
668 writeInt32(mTracks.size() + 1); // nextTrackID
669 endBox(); // mvhd
670
671 int32_t id = 1;
672 for (List<Track *>::iterator it = mTracks.begin();
673 it != mTracks.end(); ++it, ++id) {
James Dong39a0b212010-06-23 00:18:40 -0700674 (*it)->writeTrackHeader(id, mUse32BitOffset);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700675 }
676 endBox(); // moov
677
James Dongb5e74232010-05-07 10:26:24 -0700678 mWriteMoovBoxToMemory = false;
679 if (mStreamableFile) {
680 CHECK(mMoovBoxBufferOffset + 8 <= mEstimatedMoovBoxSize);
681
682 // Moov box
James Dongb1262a82010-11-16 14:04:54 -0800683 lseek64(mFd, mFreeBoxOffset, SEEK_SET);
James Dongb5e74232010-05-07 10:26:24 -0700684 mOffset = mFreeBoxOffset;
James Dong2747e0e2010-11-18 20:59:13 -0800685 write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset);
James Dongb5e74232010-05-07 10:26:24 -0700686
687 // Free box
James Dongb1262a82010-11-16 14:04:54 -0800688 lseek64(mFd, mOffset, SEEK_SET);
James Dongb5e74232010-05-07 10:26:24 -0700689 writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset);
690 write("free", 4);
691
692 // Free temp memory
693 free(mMoovBoxBuffer);
694 mMoovBoxBuffer = NULL;
695 mMoovBoxBufferOffset = 0;
James Dong6feaa462010-06-20 08:20:54 -0700696 } else {
697 LOGI("The mp4 file will not be streamable.");
James Dongb5e74232010-05-07 10:26:24 -0700698 }
699
Andreas Huberb5ceb9e2009-08-26 14:48:20 -0700700 CHECK(mBoxes.empty());
Andreas Hubere46b7be2009-07-14 16:56:47 -0700701
James Dong2747e0e2010-11-18 20:59:13 -0800702 close(mFd);
703 mFd = -1;
704 mInitCheck = NO_INIT;
James Dong08c74732010-06-10 12:28:15 -0700705 mStarted = false;
James Dongd0366622010-08-18 19:10:39 -0700706 return err;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700707}
708
James Dong3300e962010-04-21 16:14:15 -0700709status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) {
710 mInterleaveDurationUs = durationUs;
711 return OK;
712}
Andreas Hubere46b7be2009-07-14 16:56:47 -0700713
James Dong3300e962010-04-21 16:14:15 -0700714void MPEG4Writer::lock() {
715 mLock.lock();
716}
717
718void MPEG4Writer::unlock() {
719 mLock.unlock();
720}
721
James Dongb1262a82010-11-16 14:04:54 -0800722off64_t MPEG4Writer::addSample_l(MediaBuffer *buffer) {
723 off64_t old_offset = mOffset;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700724
James Dongb1262a82010-11-16 14:04:54 -0800725 ::write(mFd,
726 (const uint8_t *)buffer->data() + buffer->range_offset(),
727 buffer->range_length());
Andreas Hubere46b7be2009-07-14 16:56:47 -0700728
729 mOffset += buffer->range_length();
730
731 return old_offset;
732}
733
Andreas Huber71c27d92010-03-19 11:43:15 -0700734static void StripStartcode(MediaBuffer *buffer) {
735 if (buffer->range_length() < 4) {
736 return;
737 }
738
739 const uint8_t *ptr =
740 (const uint8_t *)buffer->data() + buffer->range_offset();
741
742 if (!memcmp(ptr, "\x00\x00\x00\x01", 4)) {
743 buffer->set_range(
744 buffer->range_offset() + 4, buffer->range_length() - 4);
745 }
746}
747
James Dongb1262a82010-11-16 14:04:54 -0800748off64_t MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
749 off64_t old_offset = mOffset;
Andreas Huberea6a38c2009-11-16 15:43:38 -0800750
751 size_t length = buffer->range_length();
Andreas Huber71c27d92010-03-19 11:43:15 -0700752
James Dong7755cdd2010-09-02 10:49:55 -0700753 if (mUse4ByteNalLength) {
754 uint8_t x = length >> 24;
James Dongb1262a82010-11-16 14:04:54 -0800755 ::write(mFd, &x, 1);
James Dong7755cdd2010-09-02 10:49:55 -0700756 x = (length >> 16) & 0xff;
James Dongb1262a82010-11-16 14:04:54 -0800757 ::write(mFd, &x, 1);
James Dong7755cdd2010-09-02 10:49:55 -0700758 x = (length >> 8) & 0xff;
James Dongb1262a82010-11-16 14:04:54 -0800759 ::write(mFd, &x, 1);
James Dong7755cdd2010-09-02 10:49:55 -0700760 x = length & 0xff;
James Dongb1262a82010-11-16 14:04:54 -0800761 ::write(mFd, &x, 1);
Andreas Huberea6a38c2009-11-16 15:43:38 -0800762
James Dongb1262a82010-11-16 14:04:54 -0800763 ::write(mFd,
764 (const uint8_t *)buffer->data() + buffer->range_offset(),
765 length);
766
James Dong7755cdd2010-09-02 10:49:55 -0700767 mOffset += length + 4;
768 } else {
769 CHECK(length < 65536);
Andreas Huberea6a38c2009-11-16 15:43:38 -0800770
James Dong7755cdd2010-09-02 10:49:55 -0700771 uint8_t x = length >> 8;
James Dongb1262a82010-11-16 14:04:54 -0800772 ::write(mFd, &x, 1);
James Dong7755cdd2010-09-02 10:49:55 -0700773 x = length & 0xff;
James Dongb1262a82010-11-16 14:04:54 -0800774 ::write(mFd, &x, 1);
775 ::write(mFd, (const uint8_t *)buffer->data() + buffer->range_offset(), length);
James Dong7755cdd2010-09-02 10:49:55 -0700776 mOffset += length + 2;
777 }
Andreas Huberea6a38c2009-11-16 15:43:38 -0800778
779 return old_offset;
780}
781
James Dongb5e74232010-05-07 10:26:24 -0700782size_t MPEG4Writer::write(
James Dong2747e0e2010-11-18 20:59:13 -0800783 const void *ptr, size_t size, size_t nmemb) {
James Dongb5e74232010-05-07 10:26:24 -0700784
785 const size_t bytes = size * nmemb;
786 if (mWriteMoovBoxToMemory) {
James Dong2747e0e2010-11-18 20:59:13 -0800787 // This happens only when we write the moov box at the end of
788 // recording, not for each output video/audio frame we receive.
James Dongb1262a82010-11-16 14:04:54 -0800789 off64_t moovBoxSize = 8 + mMoovBoxBufferOffset + bytes;
James Dong39a0b212010-06-23 00:18:40 -0700790 if (moovBoxSize > mEstimatedMoovBoxSize) {
James Dongb1262a82010-11-16 14:04:54 -0800791 for (List<off64_t>::iterator it = mBoxes.begin();
James Dongb5e74232010-05-07 10:26:24 -0700792 it != mBoxes.end(); ++it) {
793 (*it) += mOffset;
794 }
James Dong2747e0e2010-11-18 20:59:13 -0800795 lseek64(mFd, mOffset, SEEK_SET);
796 ::write(mFd, mMoovBoxBuffer, mMoovBoxBufferOffset);
797 ::write(mFd, ptr, size * nmemb);
James Dongb5e74232010-05-07 10:26:24 -0700798 mOffset += (bytes + mMoovBoxBufferOffset);
799 free(mMoovBoxBuffer);
800 mMoovBoxBuffer = NULL;
801 mMoovBoxBufferOffset = 0;
802 mWriteMoovBoxToMemory = false;
803 mStreamableFile = false;
804 } else {
805 memcpy(mMoovBoxBuffer + mMoovBoxBufferOffset, ptr, bytes);
806 mMoovBoxBufferOffset += bytes;
807 }
808 } else {
James Dong2747e0e2010-11-18 20:59:13 -0800809 ::write(mFd, ptr, size * nmemb);
James Dongb5e74232010-05-07 10:26:24 -0700810 mOffset += bytes;
811 }
812 return bytes;
813}
814
Andreas Hubere46b7be2009-07-14 16:56:47 -0700815void MPEG4Writer::beginBox(const char *fourcc) {
Andreas Huberb5ceb9e2009-08-26 14:48:20 -0700816 CHECK_EQ(strlen(fourcc), 4);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700817
James Dongb5e74232010-05-07 10:26:24 -0700818 mBoxes.push_back(mWriteMoovBoxToMemory?
819 mMoovBoxBufferOffset: mOffset);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700820
821 writeInt32(0);
822 writeFourcc(fourcc);
823}
824
825void MPEG4Writer::endBox() {
Andreas Huberb5ceb9e2009-08-26 14:48:20 -0700826 CHECK(!mBoxes.empty());
Andreas Hubere46b7be2009-07-14 16:56:47 -0700827
James Dongb1262a82010-11-16 14:04:54 -0800828 off64_t offset = *--mBoxes.end();
Andreas Hubere46b7be2009-07-14 16:56:47 -0700829 mBoxes.erase(--mBoxes.end());
830
James Dongb5e74232010-05-07 10:26:24 -0700831 if (mWriteMoovBoxToMemory) {
832 int32_t x = htonl(mMoovBoxBufferOffset - offset);
833 memcpy(mMoovBoxBuffer + offset, &x, 4);
834 } else {
James Dongb1262a82010-11-16 14:04:54 -0800835 lseek64(mFd, offset, SEEK_SET);
James Dongb5e74232010-05-07 10:26:24 -0700836 writeInt32(mOffset - offset);
837 mOffset -= 4;
James Dongb1262a82010-11-16 14:04:54 -0800838 lseek64(mFd, mOffset, SEEK_SET);
James Dongb5e74232010-05-07 10:26:24 -0700839 }
Andreas Hubere46b7be2009-07-14 16:56:47 -0700840}
841
842void MPEG4Writer::writeInt8(int8_t x) {
James Dong2747e0e2010-11-18 20:59:13 -0800843 write(&x, 1, 1);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700844}
845
846void MPEG4Writer::writeInt16(int16_t x) {
847 x = htons(x);
James Dong2747e0e2010-11-18 20:59:13 -0800848 write(&x, 1, 2);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700849}
850
851void MPEG4Writer::writeInt32(int32_t x) {
852 x = htonl(x);
James Dong2747e0e2010-11-18 20:59:13 -0800853 write(&x, 1, 4);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700854}
855
856void MPEG4Writer::writeInt64(int64_t x) {
857 x = hton64(x);
James Dong2747e0e2010-11-18 20:59:13 -0800858 write(&x, 1, 8);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700859}
860
861void MPEG4Writer::writeCString(const char *s) {
862 size_t n = strlen(s);
James Dong2747e0e2010-11-18 20:59:13 -0800863 write(s, 1, n + 1);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700864}
865
866void MPEG4Writer::writeFourcc(const char *s) {
Andreas Huberb5ceb9e2009-08-26 14:48:20 -0700867 CHECK_EQ(strlen(s), 4);
James Dong2747e0e2010-11-18 20:59:13 -0800868 write(s, 1, 4);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700869}
870
871void MPEG4Writer::write(const void *data, size_t size) {
James Dong2747e0e2010-11-18 20:59:13 -0800872 write(data, 1, size);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700873}
874
James Dong22b37fa2010-10-19 21:28:47 -0700875bool MPEG4Writer::isFileStreamable() const {
876 return mStreamableFile;
877}
878
James Dong18244862010-05-11 14:57:02 -0700879bool MPEG4Writer::exceedsFileSizeLimit() {
880 // No limit
881 if (mMaxFileSizeLimitBytes == 0) {
882 return false;
883 }
884
James Dong17299ab2010-05-14 15:45:22 -0700885 int64_t nTotalBytesEstimate = static_cast<int64_t>(mEstimatedMoovBoxSize);
James Dong18244862010-05-11 14:57:02 -0700886 for (List<Track *>::iterator it = mTracks.begin();
887 it != mTracks.end(); ++it) {
888 nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
889 }
James Dongcb7e65c2010-09-02 11:19:11 -0700890
James Dong5cdcf162010-11-30 18:18:08 -0800891 // Be conservative in the estimate: do not exceed 95% of
892 // the target file limit. For small target file size limit, though,
893 // this will not help.
894 return (nTotalBytesEstimate >= (95 * mMaxFileSizeLimitBytes) / 100);
James Dong18244862010-05-11 14:57:02 -0700895}
896
897bool MPEG4Writer::exceedsFileDurationLimit() {
898 // No limit
899 if (mMaxFileDurationLimitUs == 0) {
900 return false;
901 }
902
903 for (List<Track *>::iterator it = mTracks.begin();
904 it != mTracks.end(); ++it) {
905 if ((*it)->getDurationUs() >= mMaxFileDurationLimitUs) {
906 return true;
907 }
908 }
909 return false;
910}
911
Andreas Huber033e6c32009-09-09 16:36:12 -0700912bool MPEG4Writer::reachedEOS() {
913 bool allDone = true;
914 for (List<Track *>::iterator it = mTracks.begin();
915 it != mTracks.end(); ++it) {
916 if (!(*it)->reachedEOS()) {
917 allDone = false;
918 break;
919 }
920 }
921
922 return allDone;
923}
924
James Dong36e573b2010-06-19 09:04:18 -0700925void MPEG4Writer::setStartTimestampUs(int64_t timeUs) {
926 LOGI("setStartTimestampUs: %lld", timeUs);
927 CHECK(timeUs >= 0);
James Dong9db798d2010-05-13 11:47:36 -0700928 Mutex::Autolock autoLock(mLock);
James Dong438e4f62010-06-23 16:51:39 -0700929 if (mStartTimestampUs < 0 || mStartTimestampUs > timeUs) {
James Dong36e573b2010-06-19 09:04:18 -0700930 mStartTimestampUs = timeUs;
931 LOGI("Earliest track starting time: %lld", mStartTimestampUs);
James Dong9db798d2010-05-13 11:47:36 -0700932 }
James Dong9db798d2010-05-13 11:47:36 -0700933}
934
James Dong36e573b2010-06-19 09:04:18 -0700935int64_t MPEG4Writer::getStartTimestampUs() {
James Dong9db798d2010-05-13 11:47:36 -0700936 Mutex::Autolock autoLock(mLock);
937 return mStartTimestampUs;
938}
939
James Dongb54a91782010-06-22 11:27:37 -0700940size_t MPEG4Writer::numTracks() {
941 Mutex::Autolock autolock(mLock);
942 return mTracks.size();
943}
944
Andreas Hubere46b7be2009-07-14 16:56:47 -0700945////////////////////////////////////////////////////////////////////////////////
946
947MPEG4Writer::Track::Track(
Andreas Huber033e6c32009-09-09 16:36:12 -0700948 MPEG4Writer *owner, const sp<MediaSource> &source)
Andreas Hubere46b7be2009-07-14 16:56:47 -0700949 : mOwner(owner),
Andreas Huber033e6c32009-09-09 16:36:12 -0700950 mMeta(source->getFormat()),
Andreas Hubere46b7be2009-07-14 16:56:47 -0700951 mSource(source),
952 mDone(false),
James Dong08c74732010-06-10 12:28:15 -0700953 mPaused(false),
954 mResumed(false),
James Dongd7ef5b62011-01-25 12:37:43 -0800955 mStarted(false),
James Dong91b22a92010-08-05 10:46:13 -0700956 mTrackDurationUs(0),
James Dong17299ab2010-05-14 15:45:22 -0700957 mEstimatedTrackSizeBytes(0),
James Dong2a4767e2010-04-20 11:50:11 -0700958 mSamplesHaveSameSize(true),
Andreas Hubere46b7be2009-07-14 16:56:47 -0700959 mCodecSpecificData(NULL),
Andreas Huber033e6c32009-09-09 16:36:12 -0700960 mCodecSpecificDataSize(0),
Andreas Huberf4e5baa2010-04-09 14:25:46 -0700961 mGotAllCodecSpecificData(false),
James Dongb9d7e012010-11-09 11:15:47 -0800962 mReachedEOS(false),
963 mRotation(0) {
Andreas Huber45bac572010-07-01 08:19:52 -0700964 getCodecSpecificDataFromInputFormatIfPossible();
James Dong52d13f02010-07-02 11:39:06 -0700965
James Dongda8073c2010-07-30 17:41:22 -0700966 const char *mime;
967 mMeta->findCString(kKeyMIMEType, &mime);
968 mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
969 mIsAudio = !strncasecmp(mime, "audio/", 6);
970 mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
971 !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
972
James Dongeff30e32010-08-13 14:16:26 -0700973 setTimeScale();
974}
975
James Dongcb7e65c2010-09-02 11:19:11 -0700976void MPEG4Writer::Track::updateTrackSizeEstimate() {
977
978 int64_t stcoBoxSizeBytes = mOwner->use32BitFileOffset()
979 ? mNumStcoTableEntries * 4
980 : mNumStcoTableEntries * 8;
981
982 int64_t stszBoxSizeBytes = mSamplesHaveSameSize? 4: (mNumSamples * 4);
983
James Dong22b37fa2010-10-19 21:28:47 -0700984 mEstimatedTrackSizeBytes = mMdatSizeBytes; // media data size
985 if (!mOwner->isFileStreamable()) {
986 // Reserved free space is not large enough to hold
987 // all meta data and thus wasted.
988 mEstimatedTrackSizeBytes += mNumStscTableEntries * 12 + // stsc box size
989 mNumStssTableEntries * 4 + // stss box size
990 mNumSttsTableEntries * 8 + // stts box size
991 stcoBoxSizeBytes + // stco box size
992 stszBoxSizeBytes; // stsz box size
993 }
James Dongcb7e65c2010-09-02 11:19:11 -0700994}
995
996void MPEG4Writer::Track::addOneStscTableEntry(
997 size_t chunkId, size_t sampleId) {
998
999 StscTableEntry stscEntry(chunkId, sampleId, 1);
1000 mStscTableEntries.push_back(stscEntry);
1001 ++mNumStscTableEntries;
1002}
1003
1004void MPEG4Writer::Track::addOneStssTableEntry(size_t sampleId) {
1005 mStssTableEntries.push_back(sampleId);
1006 ++mNumStssTableEntries;
1007}
1008
1009void MPEG4Writer::Track::addOneSttsTableEntry(
1010 size_t sampleCount, int64_t durationUs) {
1011
1012 SttsTableEntry sttsEntry(sampleCount, durationUs);
1013 mSttsTableEntries.push_back(sttsEntry);
1014 ++mNumSttsTableEntries;
1015}
1016
James Dongb1262a82010-11-16 14:04:54 -08001017void MPEG4Writer::Track::addChunkOffset(off64_t offset) {
James Dongcb7e65c2010-09-02 11:19:11 -07001018 ++mNumStcoTableEntries;
1019 mChunkOffsets.push_back(offset);
1020}
1021
James Dongeff30e32010-08-13 14:16:26 -07001022void MPEG4Writer::Track::setTimeScale() {
1023 LOGV("setTimeScale");
1024 // Default time scale
1025 mTimeScale = 90000;
1026
1027 if (mIsAudio) {
1028 // Use the sampling rate as the default time scale for audio track.
1029 int32_t sampleRate;
1030 bool success = mMeta->findInt32(kKeySampleRate, &sampleRate);
1031 CHECK(success);
1032 mTimeScale = sampleRate;
1033 }
1034
1035 // If someone would like to overwrite the timescale, use user-supplied value.
1036 int32_t timeScale;
1037 if (mMeta->findInt32(kKeyTimeScale, &timeScale)) {
1038 mTimeScale = timeScale;
1039 }
1040
James Dong52d13f02010-07-02 11:39:06 -07001041 CHECK(mTimeScale > 0);
Andreas Huber45bac572010-07-01 08:19:52 -07001042}
1043
1044void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() {
1045 const char *mime;
1046 CHECK(mMeta->findCString(kKeyMIMEType, &mime));
1047
1048 if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
1049 uint32_t type;
1050 const void *data;
1051 size_t size;
1052 if (mMeta->findData(kKeyAVCC, &type, &data, &size)) {
1053 mCodecSpecificData = malloc(size);
1054 mCodecSpecificDataSize = size;
1055 memcpy(mCodecSpecificData, data, size);
1056 mGotAllCodecSpecificData = true;
1057 }
1058 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
1059 || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
1060 uint32_t type;
1061 const void *data;
1062 size_t size;
1063 if (mMeta->findData(kKeyESDS, &type, &data, &size)) {
1064 ESDS esds(data, size);
1065 if (esds.getCodecSpecificInfo(&data, &size) == OK) {
1066 mCodecSpecificData = malloc(size);
1067 mCodecSpecificDataSize = size;
1068 memcpy(mCodecSpecificData, data, size);
1069 mGotAllCodecSpecificData = true;
1070 }
1071 }
1072 }
Andreas Hubere46b7be2009-07-14 16:56:47 -07001073}
1074
1075MPEG4Writer::Track::~Track() {
1076 stop();
1077
1078 if (mCodecSpecificData != NULL) {
1079 free(mCodecSpecificData);
1080 mCodecSpecificData = NULL;
1081 }
1082}
1083
James Dong09936ed2010-06-24 19:04:27 -07001084void MPEG4Writer::Track::initTrackingProgressStatus(MetaData *params) {
1085 LOGV("initTrackingProgressStatus");
1086 mPreviousTrackTimeUs = -1;
1087 mTrackingProgressStatus = false;
1088 mTrackEveryTimeDurationUs = 0;
James Dong09936ed2010-06-24 19:04:27 -07001089 {
1090 int64_t timeUs;
1091 if (params && params->findInt64(kKeyTrackTimeStatus, &timeUs)) {
1092 LOGV("Receive request to track progress status for every %lld us", timeUs);
1093 mTrackEveryTimeDurationUs = timeUs;
1094 mTrackingProgressStatus = true;
1095 }
1096 }
James Dong09936ed2010-06-24 19:04:27 -07001097}
1098
James Dongda8073c2010-07-30 17:41:22 -07001099// static
1100void *MPEG4Writer::ThreadWrapper(void *me) {
1101 LOGV("ThreadWrapper: %p", me);
1102 MPEG4Writer *writer = static_cast<MPEG4Writer *>(me);
1103 writer->threadFunc();
1104 return NULL;
1105}
1106
1107void MPEG4Writer::bufferChunk(const Chunk& chunk) {
1108 LOGV("bufferChunk: %p", chunk.mTrack);
1109 Mutex::Autolock autolock(mLock);
1110 CHECK_EQ(mDone, false);
1111
1112 for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
1113 it != mChunkInfos.end(); ++it) {
1114
1115 if (chunk.mTrack == it->mTrack) { // Found owner
1116 it->mChunks.push_back(chunk);
1117 mChunkReadyCondition.signal();
1118 return;
1119 }
1120 }
1121
1122 CHECK("Received a chunk for a unknown track" == 0);
1123}
1124
James Dongf6a2bff2011-02-09 14:00:55 -08001125void MPEG4Writer::writeChunkToFile(Chunk* chunk) {
1126 LOGV("writeChunkToFile: %lld from %s track",
1127 chunk.mTimestampUs, chunk.mTrack->isAudio()? "audio": "video");
James Dongda8073c2010-07-30 17:41:22 -07001128
James Dongf6a2bff2011-02-09 14:00:55 -08001129 int32_t isFirstSample = true;
1130 while (!chunk->mSamples.empty()) {
1131 List<MediaBuffer *>::iterator it = chunk->mSamples.begin();
James Dongda8073c2010-07-30 17:41:22 -07001132
James Dongf6a2bff2011-02-09 14:00:55 -08001133 off64_t offset = chunk->mTrack->isAvc()
1134 ? addLengthPrefixedSample_l(*it)
1135 : addSample_l(*it);
1136
1137 if (isFirstSample) {
1138 chunk->mTrack->addChunkOffset(offset);
1139 isFirstSample = false;
James Dongda8073c2010-07-30 17:41:22 -07001140 }
James Dongda8073c2010-07-30 17:41:22 -07001141
James Dongda8073c2010-07-30 17:41:22 -07001142 (*it)->release();
1143 (*it) = NULL;
James Dongf6a2bff2011-02-09 14:00:55 -08001144 chunk->mSamples.erase(it);
James Dongda8073c2010-07-30 17:41:22 -07001145 }
James Dongf6a2bff2011-02-09 14:00:55 -08001146 chunk->mSamples.clear();
James Dongda8073c2010-07-30 17:41:22 -07001147}
1148
James Dongf6a2bff2011-02-09 14:00:55 -08001149void MPEG4Writer::writeAllChunks() {
1150 LOGV("writeAllChunks");
James Dongda8073c2010-07-30 17:41:22 -07001151 size_t outstandingChunks = 0;
1152 while (!mChunkInfos.empty()) {
1153 List<ChunkInfo>::iterator it = mChunkInfos.begin();
1154 while (!it->mChunks.empty()) {
James Dongf6a2bff2011-02-09 14:00:55 -08001155 Chunk chunk;
1156 if (findChunkToWrite(&chunk)) {
1157 writeChunkToFile(&chunk);
1158 ++outstandingChunks;
1159 }
James Dongda8073c2010-07-30 17:41:22 -07001160 }
1161 it->mTrack = NULL;
1162 mChunkInfos.erase(it);
1163 }
1164 mChunkInfos.clear();
1165 LOGD("%d chunks are written in the last batch", outstandingChunks);
1166}
1167
James Dongf6a2bff2011-02-09 14:00:55 -08001168bool MPEG4Writer::findChunkToWrite(Chunk *chunk) {
1169 LOGV("findChunkToWrite");
James Dongda8073c2010-07-30 17:41:22 -07001170
1171 // Find the smallest timestamp, and write that chunk out
1172 // XXX: What if some track is just too slow?
1173 int64_t minTimestampUs = 0x7FFFFFFFFFFFFFFFLL;
1174 Track *track = NULL;
1175 for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
1176 it != mChunkInfos.end(); ++it) {
1177 if (!it->mChunks.empty()) {
1178 List<Chunk>::iterator chunkIt = it->mChunks.begin();
1179 if (chunkIt->mTimeStampUs < minTimestampUs) {
1180 minTimestampUs = chunkIt->mTimeStampUs;
1181 track = it->mTrack;
1182 }
1183 }
1184 }
1185
1186 if (track == NULL) {
1187 LOGV("Nothing to be written after all");
James Dongf6a2bff2011-02-09 14:00:55 -08001188 return false;
James Dongda8073c2010-07-30 17:41:22 -07001189 }
1190
1191 if (mIsFirstChunk) {
1192 mIsFirstChunk = false;
1193 }
James Dongf6a2bff2011-02-09 14:00:55 -08001194
James Dongda8073c2010-07-30 17:41:22 -07001195 for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
1196 it != mChunkInfos.end(); ++it) {
1197 if (it->mTrack == track) {
James Dongf6a2bff2011-02-09 14:00:55 -08001198 *chunk = *(it->mChunks.begin());
1199 it->mChunks.erase(it->mChunks.begin());
1200 CHECK_EQ(chunk->mTrack, track);
1201 return true;
James Dongda8073c2010-07-30 17:41:22 -07001202 }
1203 }
James Dongf6a2bff2011-02-09 14:00:55 -08001204
1205 return false;
James Dongda8073c2010-07-30 17:41:22 -07001206}
1207
1208void MPEG4Writer::threadFunc() {
1209 LOGV("threadFunc");
1210
James Dongc67acb22010-10-07 20:20:59 -07001211 prctl(PR_SET_NAME, (unsigned long)"MPEG4Writer", 0, 0, 0);
James Dongf6a2bff2011-02-09 14:00:55 -08001212
1213 Mutex::Autolock autoLock(mLock);
James Dongda8073c2010-07-30 17:41:22 -07001214 while (!mDone) {
James Dongf6a2bff2011-02-09 14:00:55 -08001215 Chunk chunk;
1216 bool chunkFound = false;
1217
1218 while (!mDone && !(chunkFound = findChunkToWrite(&chunk))) {
James Dongda8073c2010-07-30 17:41:22 -07001219 mChunkReadyCondition.wait(mLock);
James Dongf6a2bff2011-02-09 14:00:55 -08001220 }
1221
1222 // Actual write without holding the lock in order to
1223 // reduce the blocking time for media track threads.
1224 if (chunkFound) {
1225 mLock.unlock();
1226 writeChunkToFile(&chunk);
1227 mLock.lock();
James Dongda8073c2010-07-30 17:41:22 -07001228 }
1229 }
1230
James Dongf6a2bff2011-02-09 14:00:55 -08001231 writeAllChunks();
James Dongda8073c2010-07-30 17:41:22 -07001232}
1233
1234status_t MPEG4Writer::startWriterThread() {
1235 LOGV("startWriterThread");
1236
1237 mDone = false;
1238 mIsFirstChunk = true;
James Dongb7208192010-08-02 19:13:40 -07001239 mDriftTimeUs = 0;
James Dongda8073c2010-07-30 17:41:22 -07001240 for (List<Track *>::iterator it = mTracks.begin();
1241 it != mTracks.end(); ++it) {
1242 ChunkInfo info;
1243 info.mTrack = *it;
1244 mChunkInfos.push_back(info);
1245 }
1246
1247 pthread_attr_t attr;
1248 pthread_attr_init(&attr);
1249 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1250 pthread_create(&mThread, &attr, ThreadWrapper, this);
1251 pthread_attr_destroy(&attr);
1252 return OK;
1253}
1254
James Dongcb7e65c2010-09-02 11:19:11 -07001255
James Dong09936ed2010-06-24 19:04:27 -07001256status_t MPEG4Writer::Track::start(MetaData *params) {
James Dong08c74732010-06-10 12:28:15 -07001257 if (!mDone && mPaused) {
1258 mPaused = false;
1259 mResumed = true;
1260 return OK;
1261 }
Andreas Huber033e6c32009-09-09 16:36:12 -07001262
James Dong09936ed2010-06-24 19:04:27 -07001263 int64_t startTimeUs;
Andreas Huber45bac572010-07-01 08:19:52 -07001264 if (params == NULL || !params->findInt64(kKeyTime, &startTimeUs)) {
1265 startTimeUs = 0;
1266 }
1267
James Dongb9d7e012010-11-09 11:15:47 -08001268 int32_t rotationDegrees;
1269 if (!mIsAudio && params && params->findInt32(kKeyRotation, &rotationDegrees)) {
1270 mRotation = rotationDegrees;
1271 }
1272
James Dong89a01042010-10-21 17:58:14 -07001273 mIsRealTimeRecording = true;
James Dongb7208192010-08-02 19:13:40 -07001274 {
1275 int32_t isNotRealTime;
1276 if (params && params->findInt32(kKeyNotRealTime, &isNotRealTime)) {
1277 mIsRealTimeRecording = (isNotRealTime == 0);
1278 }
1279 }
1280
James Dong09936ed2010-06-24 19:04:27 -07001281 initTrackingProgressStatus(params);
1282
James Dong36e573b2010-06-19 09:04:18 -07001283 sp<MetaData> meta = new MetaData;
James Dongcbeebb12011-02-16 12:28:26 -08001284 if (mIsRealTimeRecording && mOwner->numTracks() > 1) {
1285 /*
1286 * This extra delay of accepting incoming audio/video signals
1287 * helps to align a/v start time at the beginning of a recording
1288 * session, and it also helps eliminate the "recording" sound for
1289 * camcorder applications.
1290 *
1291 * Ideally, this platform-specific value should be defined
1292 * in media_profiles.xml file
1293 */
1294 startTimeUs += 700000;
1295 }
1296
James Dong36e573b2010-06-19 09:04:18 -07001297 meta->setInt64(kKeyTime, startTimeUs);
James Dongcbeebb12011-02-16 12:28:26 -08001298
James Dong36e573b2010-06-19 09:04:18 -07001299 status_t err = mSource->start(meta.get());
Andreas Huber033e6c32009-09-09 16:36:12 -07001300 if (err != OK) {
1301 mDone = mReachedEOS = true;
1302 return err;
1303 }
Andreas Hubere46b7be2009-07-14 16:56:47 -07001304
1305 pthread_attr_t attr;
1306 pthread_attr_init(&attr);
1307 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1308
1309 mDone = false;
James Dongd7ef5b62011-01-25 12:37:43 -08001310 mStarted = true;
James Dong91b22a92010-08-05 10:46:13 -07001311 mTrackDurationUs = 0;
Andreas Huber033e6c32009-09-09 16:36:12 -07001312 mReachedEOS = false;
James Dong17299ab2010-05-14 15:45:22 -07001313 mEstimatedTrackSizeBytes = 0;
James Dongcb7e65c2010-09-02 11:19:11 -07001314 mNumStcoTableEntries = 0;
1315 mNumStssTableEntries = 0;
1316 mNumStscTableEntries = 0;
1317 mNumSttsTableEntries = 0;
1318 mMdatSizeBytes = 0;
James Dongacee8e72010-10-03 10:59:26 -07001319 mIsMediaTimeAdjustmentOn = false;
1320 mPrevMediaTimeAdjustTimestampUs = 0;
1321 mMediaTimeAdjustNumFrames = 0;
1322 mPrevMediaTimeAdjustSample = 0;
1323 mTotalDriftTimeToAdjustUs = 0;
1324 mPrevTotalAccumDriftTimeUs = 0;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001325
Andreas Huber033e6c32009-09-09 16:36:12 -07001326 pthread_create(&mThread, &attr, ThreadWrapper, this);
Andreas Hubere46b7be2009-07-14 16:56:47 -07001327 pthread_attr_destroy(&attr);
Andreas Huber033e6c32009-09-09 16:36:12 -07001328
1329 return OK;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001330}
1331
James Dongd0366622010-08-18 19:10:39 -07001332status_t MPEG4Writer::Track::pause() {
James Dong08c74732010-06-10 12:28:15 -07001333 mPaused = true;
James Dongd0366622010-08-18 19:10:39 -07001334 return OK;
James Dong08c74732010-06-10 12:28:15 -07001335}
1336
James Dongd0366622010-08-18 19:10:39 -07001337status_t MPEG4Writer::Track::stop() {
James Dong42338ff2010-10-29 16:01:06 -07001338 LOGD("Stopping %s track", mIsAudio? "Audio": "Video");
James Dongd7ef5b62011-01-25 12:37:43 -08001339 if (!mStarted) {
1340 LOGE("Stop() called but track is not started");
1341 return ERROR_END_OF_STREAM;
1342 }
1343
Andreas Hubere46b7be2009-07-14 16:56:47 -07001344 if (mDone) {
James Dongd0366622010-08-18 19:10:39 -07001345 return OK;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001346 }
Andreas Hubere46b7be2009-07-14 16:56:47 -07001347 mDone = true;
1348
1349 void *dummy;
1350 pthread_join(mThread, &dummy);
1351
James Dongd0366622010-08-18 19:10:39 -07001352 status_t err = (status_t) dummy;
1353
James Dong42338ff2010-10-29 16:01:06 -07001354 LOGD("Stopping %s track source", mIsAudio? "Audio": "Video");
James Dongd0366622010-08-18 19:10:39 -07001355 {
1356 status_t status = mSource->stop();
1357 if (err == OK && status != OK && status != ERROR_END_OF_STREAM) {
1358 err = status;
1359 }
1360 }
1361
James Dong42338ff2010-10-29 16:01:06 -07001362 LOGD("%s track stopped", mIsAudio? "Audio": "Video");
James Dongd0366622010-08-18 19:10:39 -07001363 return err;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001364}
1365
Andreas Huber033e6c32009-09-09 16:36:12 -07001366bool MPEG4Writer::Track::reachedEOS() {
1367 return mReachedEOS;
1368}
1369
Andreas Hubere46b7be2009-07-14 16:56:47 -07001370// static
1371void *MPEG4Writer::Track::ThreadWrapper(void *me) {
1372 Track *track = static_cast<Track *>(me);
1373
James Dongd0366622010-08-18 19:10:39 -07001374 status_t err = track->threadEntry();
1375 return (void *) err;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001376}
1377
James Dong481e05e2010-08-06 00:29:03 -07001378static void getNalUnitType(uint8_t byte, uint8_t* type) {
1379 LOGV("getNalUnitType: %d", byte);
1380
1381 // nal_unit_type: 5-bit unsigned integer
1382 *type = (byte & 0x1F);
1383}
1384
1385static const uint8_t *findNextStartCode(
1386 const uint8_t *data, size_t length) {
1387
1388 LOGV("findNextStartCode: %p %d", data, length);
1389
1390 size_t bytesLeft = length;
1391 while (bytesLeft > 4 &&
1392 memcmp("\x00\x00\x00\x01", &data[length - bytesLeft], 4)) {
1393 --bytesLeft;
1394 }
1395 if (bytesLeft <= 4) {
1396 bytesLeft = 0; // Last parameter set
1397 }
1398 return &data[length - bytesLeft];
1399}
1400
1401const uint8_t *MPEG4Writer::Track::parseParamSet(
1402 const uint8_t *data, size_t length, int type, size_t *paramSetLen) {
1403
1404 LOGV("parseParamSet");
1405 CHECK(type == kNalUnitTypeSeqParamSet ||
1406 type == kNalUnitTypePicParamSet);
1407
1408 const uint8_t *nextStartCode = findNextStartCode(data, length);
1409 *paramSetLen = nextStartCode - data;
1410 if (*paramSetLen == 0) {
1411 LOGE("Param set is malformed, since its length is 0");
1412 return NULL;
1413 }
1414
1415 AVCParamSet paramSet(*paramSetLen, data);
1416 if (type == kNalUnitTypeSeqParamSet) {
1417 if (*paramSetLen < 4) {
1418 LOGE("Seq parameter set malformed");
1419 return NULL;
1420 }
1421 if (mSeqParamSets.empty()) {
1422 mProfileIdc = data[1];
1423 mProfileCompatible = data[2];
1424 mLevelIdc = data[3];
1425 } else {
1426 if (mProfileIdc != data[1] ||
1427 mProfileCompatible != data[2] ||
1428 mLevelIdc != data[3]) {
1429 LOGE("Inconsistent profile/level found in seq parameter sets");
1430 return NULL;
1431 }
1432 }
1433 mSeqParamSets.push_back(paramSet);
1434 } else {
1435 mPicParamSets.push_back(paramSet);
1436 }
1437 return nextStartCode;
1438}
1439
1440status_t MPEG4Writer::Track::copyAVCCodecSpecificData(
1441 const uint8_t *data, size_t size) {
1442 LOGV("copyAVCCodecSpecificData");
1443
1444 // 2 bytes for each of the parameter set length field
1445 // plus the 7 bytes for the header
1446 if (size < 4 + 7) {
1447 LOGE("Codec specific data length too short: %d", size);
1448 return ERROR_MALFORMED;
1449 }
1450
1451 mCodecSpecificDataSize = size;
1452 mCodecSpecificData = malloc(size);
1453 memcpy(mCodecSpecificData, data, size);
1454 return OK;
1455}
1456
1457status_t MPEG4Writer::Track::parseAVCCodecSpecificData(
1458 const uint8_t *data, size_t size) {
1459
1460 LOGV("parseAVCCodecSpecificData");
1461 // Data starts with a start code.
1462 // SPS and PPS are separated with start codes.
1463 // Also, SPS must come before PPS
1464 uint8_t type = kNalUnitTypeSeqParamSet;
1465 bool gotSps = false;
1466 bool gotPps = false;
1467 const uint8_t *tmp = data;
1468 const uint8_t *nextStartCode = data;
1469 size_t bytesLeft = size;
1470 size_t paramSetLen = 0;
1471 mCodecSpecificDataSize = 0;
1472 while (bytesLeft > 4 && !memcmp("\x00\x00\x00\x01", tmp, 4)) {
1473 getNalUnitType(*(tmp + 4), &type);
1474 if (type == kNalUnitTypeSeqParamSet) {
1475 if (gotPps) {
1476 LOGE("SPS must come before PPS");
1477 return ERROR_MALFORMED;
1478 }
1479 if (!gotSps) {
1480 gotSps = true;
1481 }
1482 nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, &paramSetLen);
1483 } else if (type == kNalUnitTypePicParamSet) {
1484 if (!gotSps) {
1485 LOGE("SPS must come before PPS");
1486 return ERROR_MALFORMED;
1487 }
1488 if (!gotPps) {
1489 gotPps = true;
1490 }
1491 nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, &paramSetLen);
1492 } else {
1493 LOGE("Only SPS and PPS Nal units are expected");
1494 return ERROR_MALFORMED;
1495 }
1496
1497 if (nextStartCode == NULL) {
1498 return ERROR_MALFORMED;
1499 }
1500
1501 // Move on to find the next parameter set
1502 bytesLeft -= nextStartCode - tmp;
1503 tmp = nextStartCode;
1504 mCodecSpecificDataSize += (2 + paramSetLen);
1505 }
1506
1507 {
1508 // Check on the number of seq parameter sets
1509 size_t nSeqParamSets = mSeqParamSets.size();
1510 if (nSeqParamSets == 0) {
1511 LOGE("Cound not find sequence parameter set");
1512 return ERROR_MALFORMED;
1513 }
1514
1515 if (nSeqParamSets > 0x1F) {
1516 LOGE("Too many seq parameter sets (%d) found", nSeqParamSets);
1517 return ERROR_MALFORMED;
1518 }
1519 }
1520
1521 {
1522 // Check on the number of pic parameter sets
1523 size_t nPicParamSets = mPicParamSets.size();
1524 if (nPicParamSets == 0) {
1525 LOGE("Cound not find picture parameter set");
1526 return ERROR_MALFORMED;
1527 }
1528 if (nPicParamSets > 0xFF) {
1529 LOGE("Too many pic parameter sets (%d) found", nPicParamSets);
1530 return ERROR_MALFORMED;
1531 }
1532 }
1533
1534 {
1535 // Check on the profiles
1536 // These profiles requires additional parameter set extensions
1537 if (mProfileIdc == 100 || mProfileIdc == 110 ||
1538 mProfileIdc == 122 || mProfileIdc == 144) {
1539 LOGE("Sorry, no support for profile_idc: %d!", mProfileIdc);
1540 return BAD_VALUE;
1541 }
1542 }
1543
1544 return OK;
1545}
Andreas Huberf4e5baa2010-04-09 14:25:46 -07001546
Andreas Huber71c27d92010-03-19 11:43:15 -07001547status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
1548 const uint8_t *data, size_t size) {
Andreas Huberf4e5baa2010-04-09 14:25:46 -07001549
Andreas Huber71c27d92010-03-19 11:43:15 -07001550 if (mCodecSpecificData != NULL) {
Andreas Huberf4e5baa2010-04-09 14:25:46 -07001551 LOGE("Already have codec specific data");
Andreas Huber71c27d92010-03-19 11:43:15 -07001552 return ERROR_MALFORMED;
1553 }
1554
James Dong481e05e2010-08-06 00:29:03 -07001555 if (size < 4) {
1556 LOGE("Codec specific data length too short: %d", size);
Andreas Huber71c27d92010-03-19 11:43:15 -07001557 return ERROR_MALFORMED;
1558 }
1559
James Dong481e05e2010-08-06 00:29:03 -07001560 // Data is in the form of AVCCodecSpecificData
1561 if (memcmp("\x00\x00\x00\x01", data, 4)) {
1562 return copyAVCCodecSpecificData(data, size);
Andreas Huber71c27d92010-03-19 11:43:15 -07001563 }
1564
James Dong481e05e2010-08-06 00:29:03 -07001565 if (parseAVCCodecSpecificData(data, size) != OK) {
Andreas Huber71c27d92010-03-19 11:43:15 -07001566 return ERROR_MALFORMED;
1567 }
1568
James Dong481e05e2010-08-06 00:29:03 -07001569 // ISO 14496-15: AVC file format
1570 mCodecSpecificDataSize += 7; // 7 more bytes in the header
Andreas Huber71c27d92010-03-19 11:43:15 -07001571 mCodecSpecificData = malloc(mCodecSpecificDataSize);
1572 uint8_t *header = (uint8_t *)mCodecSpecificData;
James Dong481e05e2010-08-06 00:29:03 -07001573 header[0] = 1; // version
1574 header[1] = mProfileIdc; // profile indication
1575 header[2] = mProfileCompatible; // profile compatibility
1576 header[3] = mLevelIdc;
Andreas Huber71c27d92010-03-19 11:43:15 -07001577
James Dong481e05e2010-08-06 00:29:03 -07001578 // 6-bit '111111' followed by 2-bit to lengthSizeMinuusOne
James Dong7755cdd2010-09-02 10:49:55 -07001579 if (mOwner->useNalLengthFour()) {
1580 header[4] = 0xfc | 3; // length size == 4 bytes
1581 } else {
1582 header[4] = 0xfc | 1; // length size == 2 bytes
1583 }
Andreas Huber71c27d92010-03-19 11:43:15 -07001584
James Dong481e05e2010-08-06 00:29:03 -07001585 // 3-bit '111' followed by 5-bit numSequenceParameterSets
1586 int nSequenceParamSets = mSeqParamSets.size();
1587 header[5] = 0xe0 | nSequenceParamSets;
1588 header += 6;
1589 for (List<AVCParamSet>::iterator it = mSeqParamSets.begin();
1590 it != mSeqParamSets.end(); ++it) {
1591 // 16-bit sequence parameter set length
1592 uint16_t seqParamSetLength = it->mLength;
1593 header[0] = seqParamSetLength >> 8;
1594 header[1] = seqParamSetLength & 0xff;
1595
1596 // SPS NAL unit (sequence parameter length bytes)
1597 memcpy(&header[2], it->mData, seqParamSetLength);
1598 header += (2 + seqParamSetLength);
1599 }
1600
1601 // 8-bit nPictureParameterSets
1602 int nPictureParamSets = mPicParamSets.size();
1603 header[0] = nPictureParamSets;
1604 header += 1;
1605 for (List<AVCParamSet>::iterator it = mPicParamSets.begin();
1606 it != mPicParamSets.end(); ++it) {
1607 // 16-bit picture parameter set length
1608 uint16_t picParamSetLength = it->mLength;
1609 header[0] = picParamSetLength >> 8;
1610 header[1] = picParamSetLength & 0xff;
1611
1612 // PPS Nal unit (picture parameter set length bytes)
1613 memcpy(&header[2], it->mData, picParamSetLength);
1614 header += (2 + picParamSetLength);
1615 }
Andreas Huber71c27d92010-03-19 11:43:15 -07001616
1617 return OK;
1618}
1619
James Dongacee8e72010-10-03 10:59:26 -07001620/*
1621* The video track's media time adjustment for real-time applications
1622* is described as follows:
1623*
1624* First, the media time adjustment is done for every period of
1625* kVideoMediaTimeAdjustPeriodTimeUs. kVideoMediaTimeAdjustPeriodTimeUs
1626* is currently a fixed value chosen heuristically. The value of
1627* kVideoMediaTimeAdjustPeriodTimeUs should not be very large or very small
1628* for two considerations: on one hand, a relatively large value
1629* helps reduce large fluctuation of drift time in the audio encoding
1630* path; while on the other hand, a relatively small value helps keep
1631* restoring synchronization in audio/video more frequently. Note for the
1632* very first period of kVideoMediaTimeAdjustPeriodTimeUs, there is
1633* no media time adjustment for the video track.
1634*
1635* Second, the total accumulated audio track time drift found
1636* in a period of kVideoMediaTimeAdjustPeriodTimeUs is distributed
1637* over a stream of incoming video frames. The number of video frames
1638* affected is determined based on the number of recorded video frames
1639* within the past kVideoMediaTimeAdjustPeriodTimeUs period.
1640* We choose to distribute the drift time over only a portion
1641* (rather than all) of the total number of recorded video frames
1642* in order to make sure that the video track media time adjustment is
1643* completed for the current period before the next video track media
1644* time adjustment period starts. Currently, the portion chosen is a
1645* half (0.5).
1646*
1647* Last, various additional checks are performed to ensure that
1648* the actual audio encoding path does not have too much drift.
1649* In particular, 1) we want to limit the average incremental time
1650* adjustment for each video frame to be less than a threshold
1651* for a single period of kVideoMediaTimeAdjustPeriodTimeUs.
1652* Currently, the threshold is set to 5 ms. If the average incremental
1653* media time adjustment for a video frame is larger than the
1654* threshold, the audio encoding path has too much time drift.
1655* 2) We also want to limit the total time drift in the audio
1656* encoding path to be less than a threshold for a period of
1657* kVideoMediaTimeAdjustPeriodTimeUs. Currently, the threshold
1658* is 0.5% of kVideoMediaTimeAdjustPeriodTimeUs. If the time drift of
1659* the audio encoding path is larger than the threshold, the audio
1660* encoding path has too much time drift. We treat the large time
1661* drift of the audio encoding path as errors, since there is no
1662* way to keep audio/video in synchronization for real-time
1663* applications if the time drift is too large unless we drop some
1664* video frames, which has its own problems that we don't want
1665* to get into for the time being.
1666*/
1667void MPEG4Writer::Track::adjustMediaTime(int64_t *timestampUs) {
1668 if (*timestampUs - mPrevMediaTimeAdjustTimestampUs >=
1669 kVideoMediaTimeAdjustPeriodTimeUs) {
1670
1671 LOGV("New media time adjustment period at %lld us", *timestampUs);
1672 mIsMediaTimeAdjustmentOn = true;
1673 mMediaTimeAdjustNumFrames =
1674 (mNumSamples - mPrevMediaTimeAdjustSample) >> 1;
1675
1676 mPrevMediaTimeAdjustTimestampUs = *timestampUs;
1677 mPrevMediaTimeAdjustSample = mNumSamples;
1678 int64_t totalAccumDriftTimeUs = mOwner->getDriftTimeUs();
1679 mTotalDriftTimeToAdjustUs =
1680 totalAccumDriftTimeUs - mPrevTotalAccumDriftTimeUs;
1681
1682 mPrevTotalAccumDriftTimeUs = totalAccumDriftTimeUs;
1683
1684 // Check on incremental adjusted time per frame
1685 int64_t adjustTimePerFrameUs =
1686 mTotalDriftTimeToAdjustUs / mMediaTimeAdjustNumFrames;
1687
1688 if (adjustTimePerFrameUs < 0) {
1689 adjustTimePerFrameUs = -adjustTimePerFrameUs;
1690 }
1691 if (adjustTimePerFrameUs >= 5000) {
1692 LOGE("Adjusted time per video frame is %lld us",
1693 adjustTimePerFrameUs);
1694 CHECK(!"Video frame time adjustment is too large!");
1695 }
1696
1697 // Check on total accumulated time drift within a period of
1698 // kVideoMediaTimeAdjustPeriodTimeUs.
1699 int64_t driftPercentage = (mTotalDriftTimeToAdjustUs * 1000)
1700 / kVideoMediaTimeAdjustPeriodTimeUs;
1701
1702 if (driftPercentage < 0) {
1703 driftPercentage = -driftPercentage;
1704 }
1705 if (driftPercentage > 5) {
1706 LOGE("Audio track has time drift %lld us over %lld us",
1707 mTotalDriftTimeToAdjustUs,
1708 kVideoMediaTimeAdjustPeriodTimeUs);
1709
1710 CHECK(!"The audio track media time drifts too much!");
1711 }
1712
1713 }
1714
1715 if (mIsMediaTimeAdjustmentOn) {
1716 if (mNumSamples - mPrevMediaTimeAdjustSample <=
1717 mMediaTimeAdjustNumFrames) {
1718
1719 // Do media time incremental adjustment
1720 int64_t incrementalAdjustTimeUs =
1721 (mTotalDriftTimeToAdjustUs *
1722 (mNumSamples - mPrevMediaTimeAdjustSample))
1723 / mMediaTimeAdjustNumFrames;
1724
1725 *timestampUs +=
1726 (incrementalAdjustTimeUs + mPrevTotalAccumDriftTimeUs);
1727
1728 LOGV("Incremental video frame media time adjustment: %lld us",
1729 (incrementalAdjustTimeUs + mPrevTotalAccumDriftTimeUs));
1730 } else {
1731 // Within the remaining adjustment period,
1732 // no incremental adjustment is needed.
1733 *timestampUs +=
1734 (mTotalDriftTimeToAdjustUs + mPrevTotalAccumDriftTimeUs);
1735
1736 LOGV("Fixed video frame media time adjustment: %lld us",
1737 (mTotalDriftTimeToAdjustUs + mPrevTotalAccumDriftTimeUs));
1738 }
1739 }
1740}
1741
1742/*
1743 * Updates the drift time from the audio track so that
1744 * the video track can get the updated drift time information
1745 * from the file writer. The fluctuation of the drift time of the audio
1746 * encoding path is smoothed out with a simple filter by giving a larger
1747 * weight to more recently drift time. The filter coefficients, 0.5 and 0.5,
1748 * are heuristically determined.
1749 */
1750void MPEG4Writer::Track::updateDriftTime(const sp<MetaData>& meta) {
1751 int64_t driftTimeUs = 0;
1752 if (meta->findInt64(kKeyDriftTime, &driftTimeUs)) {
1753 int64_t prevDriftTimeUs = mOwner->getDriftTimeUs();
1754 int64_t timeUs = (driftTimeUs + prevDriftTimeUs) >> 1;
1755 mOwner->setDriftTimeUs(timeUs);
1756 }
1757}
1758
James Dongd0366622010-08-18 19:10:39 -07001759status_t MPEG4Writer::Track::threadEntry() {
Andreas Huberea6a38c2009-11-16 15:43:38 -08001760 int32_t count = 0;
James Dong3300e962010-04-21 16:14:15 -07001761 const int64_t interleaveDurationUs = mOwner->interleaveDuration();
1762 int64_t chunkTimestampUs = 0;
1763 int32_t nChunks = 0;
1764 int32_t nZeroLengthFrames = 0;
James Dong52d13f02010-07-02 11:39:06 -07001765 int64_t lastTimestampUs = 0; // Previous sample time stamp in ms
1766 int64_t lastDurationUs = 0; // Between the previous two samples in ms
James Dongeff30e32010-08-13 14:16:26 -07001767 int64_t currDurationTicks = 0; // Timescale based ticks
1768 int64_t lastDurationTicks = 0; // Timescale based ticks
James Dong52d13f02010-07-02 11:39:06 -07001769 int32_t sampleCount = 1; // Sample count in the current stts table entry
James Dong2a4767e2010-04-20 11:50:11 -07001770 uint32_t previousSampleSize = 0; // Size of the previous sample
James Dong08c74732010-06-10 12:28:15 -07001771 int64_t previousPausedDurationUs = 0;
James Dongda8073c2010-07-30 17:41:22 -07001772 int64_t timestampUs;
James Dongb7208192010-08-02 19:13:40 -07001773
James Dongc67acb22010-10-07 20:20:59 -07001774 if (mIsAudio) {
1775 prctl(PR_SET_NAME, (unsigned long)"AudioTrackEncoding", 0, 0, 0);
1776 } else {
1777 prctl(PR_SET_NAME, (unsigned long)"VideoTrackEncoding", 0, 0, 0);
1778 }
James Dongfc8b7c92010-12-07 14:37:27 -08001779 setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO);
1780
James Dong4f501f02010-06-07 14:41:41 -07001781 sp<MetaData> meta_data;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001782
James Donge991e5f2010-07-28 13:18:14 -07001783 mNumSamples = 0;
James Dong09936ed2010-06-24 19:04:27 -07001784 status_t err = OK;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001785 MediaBuffer *buffer;
James Dong09936ed2010-06-24 19:04:27 -07001786 while (!mDone && (err = mSource->read(&buffer)) == OK) {
Andreas Hubere46b7be2009-07-14 16:56:47 -07001787 if (buffer->range_length() == 0) {
1788 buffer->release();
1789 buffer = NULL;
James Dong3300e962010-04-21 16:14:15 -07001790 ++nZeroLengthFrames;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001791 continue;
1792 }
1793
James Dong08c74732010-06-10 12:28:15 -07001794 // If the codec specific data has not been received yet, delay pause.
1795 // After the codec specific data is received, discard what we received
1796 // when the track is to be paused.
1797 if (mPaused && !mResumed) {
1798 buffer->release();
1799 buffer = NULL;
1800 continue;
1801 }
1802
Andreas Huberea6a38c2009-11-16 15:43:38 -08001803 ++count;
1804
Andreas Huber71c27d92010-03-19 11:43:15 -07001805 int32_t isCodecConfig;
1806 if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecConfig)
1807 && isCodecConfig) {
Andreas Huberf4e5baa2010-04-09 14:25:46 -07001808 CHECK(!mGotAllCodecSpecificData);
1809
James Dongda8073c2010-07-30 17:41:22 -07001810 if (mIsAvc) {
Andreas Huber71c27d92010-03-19 11:43:15 -07001811 status_t err = makeAVCCodecSpecificData(
1812 (const uint8_t *)buffer->data()
1813 + buffer->range_offset(),
1814 buffer->range_length());
James Dong2a4767e2010-04-20 11:50:11 -07001815 CHECK_EQ(OK, err);
James Dongda8073c2010-07-30 17:41:22 -07001816 } else if (mIsMPEG4) {
Andreas Huber71c27d92010-03-19 11:43:15 -07001817 mCodecSpecificDataSize = buffer->range_length();
1818 mCodecSpecificData = malloc(mCodecSpecificDataSize);
1819 memcpy(mCodecSpecificData,
1820 (const uint8_t *)buffer->data()
1821 + buffer->range_offset(),
1822 buffer->range_length());
Andreas Huberea6a38c2009-11-16 15:43:38 -08001823 }
1824
1825 buffer->release();
1826 buffer = NULL;
1827
Andreas Huberf4e5baa2010-04-09 14:25:46 -07001828 mGotAllCodecSpecificData = true;
Andreas Huberea6a38c2009-11-16 15:43:38 -08001829 continue;
James Dong08c74732010-06-10 12:28:15 -07001830 }
1831
James Dong4f501f02010-06-07 14:41:41 -07001832 // Make a deep copy of the MediaBuffer and Metadata and release
1833 // the original as soon as we can
1834 MediaBuffer *copy = new MediaBuffer(buffer->range_length());
1835 memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(),
1836 buffer->range_length());
1837 copy->set_range(0, buffer->range_length());
1838 meta_data = new MetaData(*buffer->meta_data().get());
1839 buffer->release();
1840 buffer = NULL;
1841
James Dongda8073c2010-07-30 17:41:22 -07001842 if (mIsAvc) StripStartcode(copy);
James Dongc6161722010-05-20 17:55:52 -07001843
James Dong7755cdd2010-09-02 10:49:55 -07001844 size_t sampleSize = copy->range_length();
1845 if (mIsAvc) {
1846 if (mOwner->useNalLengthFour()) {
1847 sampleSize += 4;
1848 } else {
1849 sampleSize += 2;
1850 }
1851 }
James Dongabed93a2010-04-22 17:27:04 -07001852
James Dong18244862010-05-11 14:57:02 -07001853 // Max file size or duration handling
James Dongcb7e65c2010-09-02 11:19:11 -07001854 mMdatSizeBytes += sampleSize;
1855 updateTrackSizeEstimate();
1856
James Dong18244862010-05-11 14:57:02 -07001857 if (mOwner->exceedsFileSizeLimit()) {
James Dong18244862010-05-11 14:57:02 -07001858 mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
1859 break;
1860 }
1861 if (mOwner->exceedsFileDurationLimit()) {
James Dong18244862010-05-11 14:57:02 -07001862 mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
1863 break;
1864 }
1865
James Dongabed93a2010-04-22 17:27:04 -07001866
James Dong4f501f02010-06-07 14:41:41 -07001867 int32_t isSync = false;
1868 meta_data->findInt32(kKeyIsSyncFrame, &isSync);
1869
James Dongdacebe62010-09-24 10:01:29 -07001870 /*
1871 * The original timestamp found in the data buffer will be modified as below:
1872 *
1873 * There is a playback offset into this track if the track's start time
1874 * is not the same as the movie start time, which will be recorded in edst
1875 * box of the output file. The playback offset is to make sure that the
1876 * starting time of the audio/video tracks are synchronized. Although the
1877 * track's media timestamp may be subject to various modifications
1878 * as outlined below, the track's playback offset time remains unchanged
1879 * once the first data buffer of the track is received.
1880 *
1881 * The media time stamp will be calculated by subtracting the playback offset
1882 * (and potential pause durations) from the original timestamp in the buffer.
1883 *
1884 * If this track is a video track for a real-time recording application with
1885 * both audio and video tracks, its media timestamp will subject to further
1886 * modification based on the media clock of the audio track. This modification
1887 * is needed for the purpose of maintaining good audio/video synchronization.
1888 *
1889 * If the recording session is paused and resumed multiple times, the track
1890 * media timestamp will be modified as if the recording session had never been
1891 * paused at all during playback of the recorded output file. In other words,
1892 * the output file will have no memory of pause/resume durations.
1893 *
1894 */
James Dong4f501f02010-06-07 14:41:41 -07001895 CHECK(meta_data->findInt64(kKeyTime, &timestampUs));
James Dongdacebe62010-09-24 10:01:29 -07001896 LOGV("%s timestampUs: %lld", mIsAudio? "Audio": "Video", timestampUs);
James Dong4f501f02010-06-07 14:41:41 -07001897
1898////////////////////////////////////////////////////////////////////////////////
James Dong7e397842010-07-28 10:24:39 -07001899 if (mSampleSizes.empty()) {
James Dong36e573b2010-06-19 09:04:18 -07001900 mStartTimestampUs = timestampUs;
1901 mOwner->setStartTimestampUs(mStartTimestampUs);
James Dongdacebe62010-09-24 10:01:29 -07001902 previousPausedDurationUs = mStartTimestampUs;
James Dong9db798d2010-05-13 11:47:36 -07001903 }
Andreas Hubere46b7be2009-07-14 16:56:47 -07001904
James Dong08c74732010-06-10 12:28:15 -07001905 if (mResumed) {
James Dongdacebe62010-09-24 10:01:29 -07001906 int64_t durExcludingEarlierPausesUs = timestampUs - previousPausedDurationUs;
1907 CHECK(durExcludingEarlierPausesUs >= 0);
1908 int64_t pausedDurationUs = durExcludingEarlierPausesUs - mTrackDurationUs;
1909 CHECK(pausedDurationUs >= lastDurationUs);
1910 previousPausedDurationUs += pausedDurationUs - lastDurationUs;
James Dong08c74732010-06-10 12:28:15 -07001911 mResumed = false;
1912 }
1913
1914 timestampUs -= previousPausedDurationUs;
James Dongdacebe62010-09-24 10:01:29 -07001915 CHECK(timestampUs >= 0);
James Dongacee8e72010-10-03 10:59:26 -07001916
1917 // Media time adjustment for real-time applications
1918 if (mIsRealTimeRecording) {
1919 if (mIsAudio) {
1920 updateDriftTime(meta_data);
1921 } else {
1922 adjustMediaTime(&timestampUs);
James Dongb7208192010-08-02 19:13:40 -07001923 }
1924 }
James Dongacee8e72010-10-03 10:59:26 -07001925
James Dongb7208192010-08-02 19:13:40 -07001926 CHECK(timestampUs >= 0);
1927 if (mNumSamples > 1) {
1928 if (timestampUs <= lastTimestampUs) {
James Dong0ea4ed32010-08-19 10:52:39 -07001929 LOGW("Frame arrives too late!");
James Dong0ea4ed32010-08-19 10:52:39 -07001930 // Don't drop the late frame, since dropping a frame may cause
1931 // problems later during playback
1932
1933 // The idea here is to avoid having two or more samples with the
1934 // same timestamp in the output file.
1935 if (mTimeScale >= 1000000LL) {
James Dong05e80b42010-08-24 12:28:02 -07001936 timestampUs = lastTimestampUs + 1;
James Dong0ea4ed32010-08-19 10:52:39 -07001937 } else {
James Dong05e80b42010-08-24 12:28:02 -07001938 timestampUs = lastTimestampUs + (1000000LL + (mTimeScale >> 1)) / mTimeScale;
James Dong0ea4ed32010-08-19 10:52:39 -07001939 }
James Dongb7208192010-08-02 19:13:40 -07001940 }
1941 }
1942
James Dongdacebe62010-09-24 10:01:29 -07001943 LOGV("%s media time stamp: %lld and previous paused duration %lld",
1944 mIsAudio? "Audio": "Video", timestampUs, previousPausedDurationUs);
James Dong91b22a92010-08-05 10:46:13 -07001945 if (timestampUs > mTrackDurationUs) {
1946 mTrackDurationUs = timestampUs;
Andreas Huber716582e2010-02-02 12:13:30 -08001947 }
1948
James Dong7e397842010-07-28 10:24:39 -07001949 mSampleSizes.push_back(sampleSize);
James Donge991e5f2010-07-28 13:18:14 -07001950 ++mNumSamples;
1951 if (mNumSamples > 2) {
James Dongeff30e32010-08-13 14:16:26 -07001952 // We need to use the time scale based ticks, rather than the
1953 // timestamp itself to determine whether we have to use a new
1954 // stts entry, since we may have rounding errors.
1955 // The calculation is intended to reduce the accumulated
1956 // rounding errors.
1957 currDurationTicks =
1958 ((timestampUs * mTimeScale + 500000LL) / 1000000LL -
1959 (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
1960
James Dongcbeebb12011-02-16 12:28:26 -08001961 // Force the first sample to have its own stts entry so that
1962 // we can adjust its value later to maintain the A/V sync.
1963 if (mNumSamples == 3 || currDurationTicks != lastDurationTicks) {
1964 LOGV("%s lastDurationUs: %lld us, currDurationTicks: %lld us",
1965 mIsAudio? "Audio": "Video", lastDurationUs, currDurationTicks);
James Dongcb7e65c2010-09-02 11:19:11 -07001966 addOneSttsTableEntry(sampleCount, lastDurationUs);
James Dong2a4767e2010-04-20 11:50:11 -07001967 sampleCount = 1;
1968 } else {
1969 ++sampleCount;
1970 }
1971 }
1972 if (mSamplesHaveSameSize) {
James Donge991e5f2010-07-28 13:18:14 -07001973 if (mNumSamples >= 2 && previousSampleSize != sampleSize) {
James Dong2a4767e2010-04-20 11:50:11 -07001974 mSamplesHaveSameSize = false;
1975 }
James Dong7e397842010-07-28 10:24:39 -07001976 previousSampleSize = sampleSize;
James Dong2a4767e2010-04-20 11:50:11 -07001977 }
James Dongcbeebb12011-02-16 12:28:26 -08001978 LOGV("%s timestampUs/lastTimestampUs: %lld/%lld",
1979 mIsAudio? "Audio": "Video", timestampUs, lastTimestampUs);
James Dong7e397842010-07-28 10:24:39 -07001980 lastDurationUs = timestampUs - lastTimestampUs;
James Dongeff30e32010-08-13 14:16:26 -07001981 lastDurationTicks = currDurationTicks;
James Dong7e397842010-07-28 10:24:39 -07001982 lastTimestampUs = timestampUs;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001983
James Dong4f501f02010-06-07 14:41:41 -07001984 if (isSync != 0) {
James Dongcb7e65c2010-09-02 11:19:11 -07001985 addOneStssTableEntry(mNumSamples);
James Dong4f501f02010-06-07 14:41:41 -07001986 }
1987
James Dong09936ed2010-06-24 19:04:27 -07001988 if (mTrackingProgressStatus) {
1989 if (mPreviousTrackTimeUs <= 0) {
1990 mPreviousTrackTimeUs = mStartTimestampUs;
1991 }
James Dong85edea72010-07-15 19:08:20 -07001992 trackProgressStatus(timestampUs);
James Dong09936ed2010-06-24 19:04:27 -07001993 }
James Dongb54a91782010-06-22 11:27:37 -07001994 if (mOwner->numTracks() == 1) {
James Dongb1262a82010-11-16 14:04:54 -08001995 off64_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy)
James Dongb54a91782010-06-22 11:27:37 -07001996 : mOwner->addSample_l(copy);
1997 if (mChunkOffsets.empty()) {
James Dongcb7e65c2010-09-02 11:19:11 -07001998 addChunkOffset(offset);
James Dongb54a91782010-06-22 11:27:37 -07001999 }
2000 copy->release();
2001 copy = NULL;
2002 continue;
2003 }
James Dong3300e962010-04-21 16:14:15 -07002004
2005 mChunkSamples.push_back(copy);
2006 if (interleaveDurationUs == 0) {
James Dongcb7e65c2010-09-02 11:19:11 -07002007 addOneStscTableEntry(++nChunks, 1);
James Dongda8073c2010-07-30 17:41:22 -07002008 bufferChunk(timestampUs);
James Dong3300e962010-04-21 16:14:15 -07002009 } else {
2010 if (chunkTimestampUs == 0) {
2011 chunkTimestampUs = timestampUs;
2012 } else {
2013 if (timestampUs - chunkTimestampUs > interleaveDurationUs) {
2014 ++nChunks;
2015 if (nChunks == 1 || // First chunk
2016 (--(mStscTableEntries.end()))->samplesPerChunk !=
2017 mChunkSamples.size()) {
James Dongcb7e65c2010-09-02 11:19:11 -07002018 addOneStscTableEntry(nChunks, mChunkSamples.size());
James Dong3300e962010-04-21 16:14:15 -07002019 }
James Dongda8073c2010-07-30 17:41:22 -07002020 bufferChunk(timestampUs);
James Dong3300e962010-04-21 16:14:15 -07002021 chunkTimestampUs = timestampUs;
2022 }
2023 }
2024 }
2025
Andreas Hubere46b7be2009-07-14 16:56:47 -07002026 }
Andreas Huber033e6c32009-09-09 16:36:12 -07002027
James Dongb5024da2010-09-13 16:30:51 -07002028 if (mSampleSizes.empty() || // no samples written
2029 (!mIsAudio && mNumStssTableEntries == 0) || // no sync frames for video
2030 (OK != checkCodecSpecificData())) { // no codec specific data
James Dong62948fa2010-08-19 13:52:47 -07002031 err = ERROR_MALFORMED;
James Dong68510e62010-05-14 11:48:00 -07002032 }
James Dong85edea72010-07-15 19:08:20 -07002033 mOwner->trackProgressStatus(this, -1, err);
James Dong2a4767e2010-04-20 11:50:11 -07002034
James Dong3300e962010-04-21 16:14:15 -07002035 // Last chunk
James Dongb54a91782010-06-22 11:27:37 -07002036 if (mOwner->numTracks() == 1) {
James Dongcb7e65c2010-09-02 11:19:11 -07002037 addOneStscTableEntry(1, mNumSamples);
James Dongb54a91782010-06-22 11:27:37 -07002038 } else if (!mChunkSamples.empty()) {
James Dongcb7e65c2010-09-02 11:19:11 -07002039 addOneStscTableEntry(++nChunks, mChunkSamples.size());
James Dongda8073c2010-07-30 17:41:22 -07002040 bufferChunk(timestampUs);
James Dong3300e962010-04-21 16:14:15 -07002041 }
2042
James Dong2a4767e2010-04-20 11:50:11 -07002043 // We don't really know how long the last frame lasts, since
2044 // there is no frame time after it, just repeat the previous
2045 // frame's duration.
James Donge991e5f2010-07-28 13:18:14 -07002046 if (mNumSamples == 1) {
James Dong52d13f02010-07-02 11:39:06 -07002047 lastDurationUs = 0; // A single sample's duration
James Dong2a4767e2010-04-20 11:50:11 -07002048 } else {
2049 ++sampleCount; // Count for the last sample
2050 }
James Dongcbeebb12011-02-16 12:28:26 -08002051
2052 if (mNumSamples <= 2) {
2053 addOneSttsTableEntry(1, lastDurationUs);
2054 if (sampleCount - 1 > 0) {
2055 addOneSttsTableEntry(sampleCount - 1, lastDurationUs);
2056 }
2057 } else {
2058 addOneSttsTableEntry(sampleCount, lastDurationUs);
2059 }
2060
James Dong91b22a92010-08-05 10:46:13 -07002061 mTrackDurationUs += lastDurationUs;
Andreas Huber033e6c32009-09-09 16:36:12 -07002062 mReachedEOS = true;
James Dongcb7e65c2010-09-02 11:19:11 -07002063 LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s",
2064 count, nZeroLengthFrames, mNumSamples, mIsAudio? "audio": "video");
James Dongacee8e72010-10-03 10:59:26 -07002065 if (mIsAudio) {
2066 LOGI("Audio track drift time: %lld us", mOwner->getDriftTimeUs());
2067 }
James Dongdae9fd32010-06-04 13:59:27 -07002068
James Dongd0366622010-08-18 19:10:39 -07002069 if (err == ERROR_END_OF_STREAM) {
2070 return OK;
2071 }
2072 return err;
James Dongdae9fd32010-06-04 13:59:27 -07002073}
2074
James Dong85edea72010-07-15 19:08:20 -07002075void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) {
2076 LOGV("trackProgressStatus: %lld us", timeUs);
James Dongd4760c22010-06-26 08:24:47 -07002077 if (mTrackEveryTimeDurationUs > 0 &&
2078 timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
James Dong09936ed2010-06-24 19:04:27 -07002079 LOGV("Fire time tracking progress status at %lld us", timeUs);
James Dong85edea72010-07-15 19:08:20 -07002080 mOwner->trackProgressStatus(this, timeUs - mPreviousTrackTimeUs, err);
James Dong09936ed2010-06-24 19:04:27 -07002081 mPreviousTrackTimeUs = timeUs;
2082 }
2083}
2084
James Dong85edea72010-07-15 19:08:20 -07002085void MPEG4Writer::trackProgressStatus(
2086 const MPEG4Writer::Track* track, int64_t timeUs, status_t err) {
2087 Mutex::Autolock lock(mLock);
2088 int32_t nTracks = mTracks.size();
2089 CHECK(nTracks >= 1);
2090 CHECK(nTracks < 64); // Arbitrary number
2091
2092 int32_t trackNum = 0;
James Dong85edea72010-07-15 19:08:20 -07002093 CHECK(trackNum < nTracks);
2094 trackNum <<= 16;
2095
2096 // Error notification
2097 // Do not consider ERROR_END_OF_STREAM an error
2098 if (err != OK && err != ERROR_END_OF_STREAM) {
2099 notify(MEDIA_RECORDER_EVENT_ERROR,
2100 trackNum | MEDIA_RECORDER_ERROR_UNKNOWN,
2101 err);
2102 return;
2103 }
2104
2105 if (timeUs == -1) {
2106 // Send completion notification
2107 notify(MEDIA_RECORDER_EVENT_INFO,
2108 trackNum | MEDIA_RECORDER_INFO_COMPLETION_STATUS,
2109 err);
2110 return;
2111 } else {
2112 // Send progress status
2113 notify(MEDIA_RECORDER_EVENT_INFO,
2114 trackNum | MEDIA_RECORDER_INFO_PROGRESS_TIME_STATUS,
2115 timeUs / 1000);
2116 }
2117}
2118
James Dong4c238152010-09-01 18:48:35 -07002119void MPEG4Writer::setDriftTimeUs(int64_t driftTimeUs) {
2120 LOGV("setDriftTimeUs: %lld us", driftTimeUs);
James Dongb7208192010-08-02 19:13:40 -07002121 Mutex::Autolock autolock(mLock);
James Dong4c238152010-09-01 18:48:35 -07002122 mDriftTimeUs = driftTimeUs;
James Dongb7208192010-08-02 19:13:40 -07002123}
2124
2125int64_t MPEG4Writer::getDriftTimeUs() {
2126 LOGV("getDriftTimeUs: %lld us", mDriftTimeUs);
2127 Mutex::Autolock autolock(mLock);
2128 return mDriftTimeUs;
2129}
2130
James Dong7755cdd2010-09-02 10:49:55 -07002131bool MPEG4Writer::useNalLengthFour() {
2132 return mUse4ByteNalLength;
2133}
2134
James Dongda8073c2010-07-30 17:41:22 -07002135void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) {
2136 LOGV("bufferChunk");
2137
James Dongda8073c2010-07-30 17:41:22 -07002138 Chunk chunk(this, timestampUs, mChunkSamples);
2139 mOwner->bufferChunk(chunk);
James Dong3300e962010-04-21 16:14:15 -07002140 mChunkSamples.clear();
Andreas Hubere46b7be2009-07-14 16:56:47 -07002141}
2142
Andreas Huber716582e2010-02-02 12:13:30 -08002143int64_t MPEG4Writer::Track::getDurationUs() const {
James Dong91b22a92010-08-05 10:46:13 -07002144 return mTrackDurationUs;
Andreas Hubere46b7be2009-07-14 16:56:47 -07002145}
2146
James Dong18244862010-05-11 14:57:02 -07002147int64_t MPEG4Writer::Track::getEstimatedTrackSizeBytes() const {
2148 return mEstimatedTrackSizeBytes;
2149}
2150
James Dong62948fa2010-08-19 13:52:47 -07002151status_t MPEG4Writer::Track::checkCodecSpecificData() const {
2152 const char *mime;
2153 CHECK(mMeta->findCString(kKeyMIMEType, &mime));
2154 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime) ||
2155 !strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) ||
2156 !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
2157 if (!mCodecSpecificData ||
2158 mCodecSpecificDataSize <= 0) {
James Dongb5024da2010-09-13 16:30:51 -07002159 LOGE("Missing codec specific data");
James Dong62948fa2010-08-19 13:52:47 -07002160 return ERROR_MALFORMED;
2161 }
2162 } else {
2163 if (mCodecSpecificData ||
2164 mCodecSpecificDataSize > 0) {
James Dongb5024da2010-09-13 16:30:51 -07002165 LOGE("Unexepected codec specific data found");
James Dong62948fa2010-08-19 13:52:47 -07002166 return ERROR_MALFORMED;
2167 }
2168 }
2169 return OK;
2170}
2171
James Dong39a0b212010-06-23 00:18:40 -07002172void MPEG4Writer::Track::writeTrackHeader(
2173 int32_t trackID, bool use32BitOffset) {
Andreas Hubere46b7be2009-07-14 16:56:47 -07002174 const char *mime;
2175 bool success = mMeta->findCString(kKeyMIMEType, &mime);
Andreas Huberb5ceb9e2009-08-26 14:48:20 -07002176 CHECK(success);
Andreas Hubere46b7be2009-07-14 16:56:47 -07002177
James Dong52d13f02010-07-02 11:39:06 -07002178 LOGV("%s track time scale: %d",
James Dongda8073c2010-07-30 17:41:22 -07002179 mIsAudio? "Audio": "Video", mTimeScale);
James Dong52d13f02010-07-02 11:39:06 -07002180
Andreas Hubere46b7be2009-07-14 16:56:47 -07002181 time_t now = time(NULL);
James Dong52d13f02010-07-02 11:39:06 -07002182 int32_t mvhdTimeScale = mOwner->getTimeScale();
2183 int64_t trakDurationUs = getDurationUs();
Andreas Hubere46b7be2009-07-14 16:56:47 -07002184
James Dongcbeebb12011-02-16 12:28:26 -08002185 // Compensate for small start time difference from different media tracks
2186 int64_t trackStartTimeOffsetUs = 0;
2187
Andreas Hubere46b7be2009-07-14 16:56:47 -07002188 mOwner->beginBox("trak");
2189
2190 mOwner->beginBox("tkhd");
James Dong39a0b212010-06-23 00:18:40 -07002191 // Flags = 7 to indicate that the track is enabled, and
2192 // part of the presentation
2193 mOwner->writeInt32(0x07); // version=0, flags=7
Andreas Hubere46b7be2009-07-14 16:56:47 -07002194 mOwner->writeInt32(now); // creation time
2195 mOwner->writeInt32(now); // modification time
2196 mOwner->writeInt32(trackID);
2197 mOwner->writeInt32(0); // reserved
James Dong52d13f02010-07-02 11:39:06 -07002198 int32_t tkhdDuration =
2199 (trakDurationUs * mvhdTimeScale + 5E5) / 1E6;
2200 mOwner->writeInt32(tkhdDuration); // in mvhd timescale
Andreas Hubere46b7be2009-07-14 16:56:47 -07002201 mOwner->writeInt32(0); // reserved
2202 mOwner->writeInt32(0); // reserved
2203 mOwner->writeInt16(0); // layer
2204 mOwner->writeInt16(0); // alternate group
James Dongda8073c2010-07-30 17:41:22 -07002205 mOwner->writeInt16(mIsAudio ? 0x100 : 0); // volume
Andreas Hubere46b7be2009-07-14 16:56:47 -07002206 mOwner->writeInt16(0); // reserved
2207
James Dongb9d7e012010-11-09 11:15:47 -08002208 mOwner->writeCompositionMatrix(mRotation); // matrix
Andreas Hubere46b7be2009-07-14 16:56:47 -07002209
James Dongda8073c2010-07-30 17:41:22 -07002210 if (mIsAudio) {
Andreas Hubere46b7be2009-07-14 16:56:47 -07002211 mOwner->writeInt32(0);
2212 mOwner->writeInt32(0);
2213 } else {
2214 int32_t width, height;
2215 bool success = mMeta->findInt32(kKeyWidth, &width);
2216 success = success && mMeta->findInt32(kKeyHeight, &height);
Andreas Huberb5ceb9e2009-08-26 14:48:20 -07002217 CHECK(success);
Andreas Hubere46b7be2009-07-14 16:56:47 -07002218
James Dongabed93a2010-04-22 17:27:04 -07002219 mOwner->writeInt32(width << 16); // 32-bit fixed-point value
2220 mOwner->writeInt32(height << 16); // 32-bit fixed-point value
Andreas Hubere46b7be2009-07-14 16:56:47 -07002221 }
2222 mOwner->endBox(); // tkhd
2223
James Dong36e573b2010-06-19 09:04:18 -07002224 int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
2225 if (mStartTimestampUs != moovStartTimeUs) {
James Dongcbeebb12011-02-16 12:28:26 -08002226 CHECK(mStartTimestampUs > moovStartTimeUs);
2227 trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
James Dong9db798d2010-05-13 11:47:36 -07002228 }
2229
Andreas Hubere46b7be2009-07-14 16:56:47 -07002230 mOwner->beginBox("mdia");
2231
2232 mOwner->beginBox("mdhd");
2233 mOwner->writeInt32(0); // version=0, flags=0
2234 mOwner->writeInt32(now); // creation time
2235 mOwner->writeInt32(now); // modification time
James Dong52d13f02010-07-02 11:39:06 -07002236 mOwner->writeInt32(mTimeScale); // media timescale
2237 int32_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6;
2238 mOwner->writeInt32(mdhdDuration); // use media timescale
James Dong39a0b212010-06-23 00:18:40 -07002239 // Language follows the three letter standard ISO-639-2/T
2240 // 'e', 'n', 'g' for "English", for instance.
2241 // Each character is packed as the difference between its ASCII value and 0x60.
2242 // For "English", these are 00101, 01110, 00111.
2243 // XXX: Where is the padding bit located: 0x15C7?
2244 mOwner->writeInt16(0); // language code
Andreas Hubere46b7be2009-07-14 16:56:47 -07002245 mOwner->writeInt16(0); // predefined
2246 mOwner->endBox();
2247
2248 mOwner->beginBox("hdlr");
2249 mOwner->writeInt32(0); // version=0, flags=0
James Dongabed93a2010-04-22 17:27:04 -07002250 mOwner->writeInt32(0); // component type: should be mhlr
James Dongda8073c2010-07-30 17:41:22 -07002251 mOwner->writeFourcc(mIsAudio ? "soun" : "vide"); // component subtype
Andreas Hubere46b7be2009-07-14 16:56:47 -07002252 mOwner->writeInt32(0); // reserved
2253 mOwner->writeInt32(0); // reserved
2254 mOwner->writeInt32(0); // reserved
James Dong39a0b212010-06-23 00:18:40 -07002255 // Removing "r" for the name string just makes the string 4 byte aligned
James Dongda8073c2010-07-30 17:41:22 -07002256 mOwner->writeCString(mIsAudio ? "SoundHandle": "VideoHandle"); // name
Andreas Hubere46b7be2009-07-14 16:56:47 -07002257 mOwner->endBox();
2258
2259 mOwner->beginBox("minf");
James Dongda8073c2010-07-30 17:41:22 -07002260 if (mIsAudio) {
Andreas Hubere46b7be2009-07-14 16:56:47 -07002261 mOwner->beginBox("smhd");
2262 mOwner->writeInt32(0); // version=0, flags=0
2263 mOwner->writeInt16(0); // balance
2264 mOwner->writeInt16(0); // reserved
2265 mOwner->endBox();
2266 } else {
2267 mOwner->beginBox("vmhd");
James Dong39a0b212010-06-23 00:18:40 -07002268 mOwner->writeInt32(0x01); // version=0, flags=1
Andreas Hubere46b7be2009-07-14 16:56:47 -07002269 mOwner->writeInt16(0); // graphics mode
2270 mOwner->writeInt16(0); // opcolor
2271 mOwner->writeInt16(0);
2272 mOwner->writeInt16(0);
2273 mOwner->endBox();
2274 }
James Dongabed93a2010-04-22 17:27:04 -07002275
2276 mOwner->beginBox("dinf");
2277 mOwner->beginBox("dref");
2278 mOwner->writeInt32(0); // version=0, flags=0
James Dong39a0b212010-06-23 00:18:40 -07002279 mOwner->writeInt32(1); // entry count (either url or urn)
2280 // The table index here refers to the sample description index
2281 // in the sample table entries.
James Dongabed93a2010-04-22 17:27:04 -07002282 mOwner->beginBox("url ");
James Dong39a0b212010-06-23 00:18:40 -07002283 mOwner->writeInt32(1); // version=0, flags=1 (self-contained)
James Dongabed93a2010-04-22 17:27:04 -07002284 mOwner->endBox(); // url
2285 mOwner->endBox(); // dref
2286 mOwner->endBox(); // dinf
2287
Andreas Hubere46b7be2009-07-14 16:56:47 -07002288 mOwner->beginBox("stbl");
2289
2290 mOwner->beginBox("stsd");
2291 mOwner->writeInt32(0); // version=0, flags=0
2292 mOwner->writeInt32(1); // entry count
James Dongda8073c2010-07-30 17:41:22 -07002293 if (mIsAudio) {
Andreas Huber033e6c32009-09-09 16:36:12 -07002294 const char *fourcc = NULL;
Andreas Hubere6c40962009-09-10 14:13:30 -07002295 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
Andreas Huber033e6c32009-09-09 16:36:12 -07002296 fourcc = "samr";
Andreas Hubere6c40962009-09-10 14:13:30 -07002297 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
Andreas Huber033e6c32009-09-09 16:36:12 -07002298 fourcc = "sawb";
James Dongabed93a2010-04-22 17:27:04 -07002299 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
2300 fourcc = "mp4a";
Andreas Huber033e6c32009-09-09 16:36:12 -07002301 } else {
2302 LOGE("Unknown mime type '%s'.", mime);
2303 CHECK(!"should not be here, unknown mime type.");
2304 }
2305
2306 mOwner->beginBox(fourcc); // audio format
Andreas Hubere46b7be2009-07-14 16:56:47 -07002307 mOwner->writeInt32(0); // reserved
2308 mOwner->writeInt16(0); // reserved
James Dongabed93a2010-04-22 17:27:04 -07002309 mOwner->writeInt16(0x1); // data ref index
Andreas Hubere46b7be2009-07-14 16:56:47 -07002310 mOwner->writeInt32(0); // reserved
2311 mOwner->writeInt32(0); // reserved
James Dongabed93a2010-04-22 17:27:04 -07002312 int32_t nChannels;
2313 CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels));
2314 mOwner->writeInt16(nChannels); // channel count
Andreas Hubere46b7be2009-07-14 16:56:47 -07002315 mOwner->writeInt16(16); // sample size
2316 mOwner->writeInt16(0); // predefined
2317 mOwner->writeInt16(0); // reserved
2318
2319 int32_t samplerate;
2320 bool success = mMeta->findInt32(kKeySampleRate, &samplerate);
Andreas Huberb5ceb9e2009-08-26 14:48:20 -07002321 CHECK(success);
Andreas Hubere46b7be2009-07-14 16:56:47 -07002322 mOwner->writeInt32(samplerate << 16);
James Dongabed93a2010-04-22 17:27:04 -07002323 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
2324 mOwner->beginBox("esds");
James Dongf3b78592010-08-08 08:56:48 -07002325 CHECK(mCodecSpecificData);
2326 CHECK(mCodecSpecificDataSize > 0);
James Dongabed93a2010-04-22 17:27:04 -07002327
Andreas Huber1df9a7b2010-10-20 14:29:41 -07002328 // Make sure all sizes encode to a single byte.
2329 CHECK(mCodecSpecificDataSize + 23 < 128);
2330
James Dongabed93a2010-04-22 17:27:04 -07002331 mOwner->writeInt32(0); // version=0, flags=0
2332 mOwner->writeInt8(0x03); // ES_DescrTag
2333 mOwner->writeInt8(23 + mCodecSpecificDataSize);
2334 mOwner->writeInt16(0x0000);// ES_ID
2335 mOwner->writeInt8(0x00);
2336
2337 mOwner->writeInt8(0x04); // DecoderConfigDescrTag
2338 mOwner->writeInt8(15 + mCodecSpecificDataSize);
2339 mOwner->writeInt8(0x40); // objectTypeIndication ISO/IEC 14492-2
2340 mOwner->writeInt8(0x15); // streamType AudioStream
2341
2342 mOwner->writeInt16(0x03); // XXX
2343 mOwner->writeInt8(0x00); // buffer size 24-bit
2344 mOwner->writeInt32(96000); // max bit rate
2345 mOwner->writeInt32(96000); // avg bit rate
2346
2347 mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
2348 mOwner->writeInt8(mCodecSpecificDataSize);
2349 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
2350
2351 static const uint8_t kData2[] = {
2352 0x06, // SLConfigDescriptorTag
2353 0x01,
2354 0x02
2355 };
2356 mOwner->write(kData2, sizeof(kData2));
2357
2358 mOwner->endBox(); // esds
James Dongc80218a2010-07-30 13:48:46 -07002359 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime) ||
2360 !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
2361 // 3gpp2 Spec AMRSampleEntry fields
2362 mOwner->beginBox("damr");
2363 mOwner->writeCString(" "); // vendor: 4 bytes
2364 mOwner->writeInt8(0); // decoder version
2365 mOwner->writeInt16(0x83FF); // mode set: all enabled
2366 mOwner->writeInt8(0); // mode change period
2367 mOwner->writeInt8(1); // frames per sample
2368 mOwner->endBox();
James Dongabed93a2010-04-22 17:27:04 -07002369 }
Andreas Hubere46b7be2009-07-14 16:56:47 -07002370 mOwner->endBox();
2371 } else {
Andreas Hubere6c40962009-09-10 14:13:30 -07002372 if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
Andreas Hubere46b7be2009-07-14 16:56:47 -07002373 mOwner->beginBox("mp4v");
Andreas Hubere6c40962009-09-10 14:13:30 -07002374 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
Andreas Hubere46b7be2009-07-14 16:56:47 -07002375 mOwner->beginBox("s263");
Andreas Huberea6a38c2009-11-16 15:43:38 -08002376 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
2377 mOwner->beginBox("avc1");
Andreas Hubere46b7be2009-07-14 16:56:47 -07002378 } else {
Andreas Huber033e6c32009-09-09 16:36:12 -07002379 LOGE("Unknown mime type '%s'.", mime);
Andreas Huberb5ceb9e2009-08-26 14:48:20 -07002380 CHECK(!"should not be here, unknown mime type.");
Andreas Hubere46b7be2009-07-14 16:56:47 -07002381 }
2382
2383 mOwner->writeInt32(0); // reserved
2384 mOwner->writeInt16(0); // reserved
James Dong39a0b212010-06-23 00:18:40 -07002385 mOwner->writeInt16(1); // data ref index
Andreas Hubere46b7be2009-07-14 16:56:47 -07002386 mOwner->writeInt16(0); // predefined
2387 mOwner->writeInt16(0); // reserved
2388 mOwner->writeInt32(0); // predefined
2389 mOwner->writeInt32(0); // predefined
2390 mOwner->writeInt32(0); // predefined
2391
2392 int32_t width, height;
2393 bool success = mMeta->findInt32(kKeyWidth, &width);
2394 success = success && mMeta->findInt32(kKeyHeight, &height);
Andreas Huberb5ceb9e2009-08-26 14:48:20 -07002395 CHECK(success);
Andreas Hubere46b7be2009-07-14 16:56:47 -07002396
2397 mOwner->writeInt16(width);
2398 mOwner->writeInt16(height);
2399 mOwner->writeInt32(0x480000); // horiz resolution
2400 mOwner->writeInt32(0x480000); // vert resolution
2401 mOwner->writeInt32(0); // reserved
2402 mOwner->writeInt16(1); // frame count
2403 mOwner->write(" ", 32);
2404 mOwner->writeInt16(0x18); // depth
2405 mOwner->writeInt16(-1); // predefined
2406
Andreas Huberb5ceb9e2009-08-26 14:48:20 -07002407 CHECK(23 + mCodecSpecificDataSize < 128);
Andreas Hubere46b7be2009-07-14 16:56:47 -07002408
Andreas Hubere6c40962009-09-10 14:13:30 -07002409 if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
James Dong7ae08a62010-08-11 15:58:59 -07002410 CHECK(mCodecSpecificData);
2411 CHECK(mCodecSpecificDataSize > 0);
Andreas Hubere46b7be2009-07-14 16:56:47 -07002412 mOwner->beginBox("esds");
2413
2414 mOwner->writeInt32(0); // version=0, flags=0
2415
2416 mOwner->writeInt8(0x03); // ES_DescrTag
2417 mOwner->writeInt8(23 + mCodecSpecificDataSize);
2418 mOwner->writeInt16(0x0000); // ES_ID
2419 mOwner->writeInt8(0x1f);
2420
2421 mOwner->writeInt8(0x04); // DecoderConfigDescrTag
2422 mOwner->writeInt8(15 + mCodecSpecificDataSize);
2423 mOwner->writeInt8(0x20); // objectTypeIndication ISO/IEC 14492-2
2424 mOwner->writeInt8(0x11); // streamType VisualStream
2425
2426 static const uint8_t kData[] = {
2427 0x01, 0x77, 0x00,
2428 0x00, 0x03, 0xe8, 0x00,
2429 0x00, 0x03, 0xe8, 0x00
2430 };
2431 mOwner->write(kData, sizeof(kData));
James Dongabed93a2010-04-22 17:27:04 -07002432
Andreas Hubere46b7be2009-07-14 16:56:47 -07002433 mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
2434
2435 mOwner->writeInt8(mCodecSpecificDataSize);
2436 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
2437
2438 static const uint8_t kData2[] = {
2439 0x06, // SLConfigDescriptorTag
2440 0x01,
2441 0x02
2442 };
2443 mOwner->write(kData2, sizeof(kData2));
2444
2445 mOwner->endBox(); // esds
Andreas Hubere6c40962009-09-10 14:13:30 -07002446 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
Andreas Hubere46b7be2009-07-14 16:56:47 -07002447 mOwner->beginBox("d263");
2448
2449 mOwner->writeInt32(0); // vendor
2450 mOwner->writeInt8(0); // decoder version
2451 mOwner->writeInt8(10); // level: 10
2452 mOwner->writeInt8(0); // profile: 0
2453
2454 mOwner->endBox(); // d263
Andreas Huberea6a38c2009-11-16 15:43:38 -08002455 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
James Dongf3b78592010-08-08 08:56:48 -07002456 CHECK(mCodecSpecificData);
Andreas Hubere7635932010-09-23 14:08:32 -07002457 CHECK(mCodecSpecificDataSize >= 5);
2458
2459 // Patch avcc's lengthSize field to match the number
2460 // of bytes we use to indicate the size of a nal unit.
2461 uint8_t *ptr = (uint8_t *)mCodecSpecificData;
2462 ptr[4] =
2463 (ptr[4] & 0xfc)
2464 | (mOwner->useNalLengthFour() ? 3 : 1);
2465
Andreas Huberea6a38c2009-11-16 15:43:38 -08002466 mOwner->beginBox("avcC");
2467 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
2468 mOwner->endBox(); // avcC
Andreas Hubere46b7be2009-07-14 16:56:47 -07002469 }
Andreas Huberea6a38c2009-11-16 15:43:38 -08002470
James Dong39a0b212010-06-23 00:18:40 -07002471 mOwner->beginBox("pasp");
2472 // This is useful if the pixel is not square
2473 mOwner->writeInt32(1 << 16); // hspacing
2474 mOwner->writeInt32(1 << 16); // vspacing
2475 mOwner->endBox(); // pasp
Andreas Huberea6a38c2009-11-16 15:43:38 -08002476 mOwner->endBox(); // mp4v, s263 or avc1
Andreas Hubere46b7be2009-07-14 16:56:47 -07002477 }
2478 mOwner->endBox(); // stsd
2479
2480 mOwner->beginBox("stts");
2481 mOwner->writeInt32(0); // version=0, flags=0
James Dongcb7e65c2010-09-02 11:19:11 -07002482 mOwner->writeInt32(mNumSttsTableEntries);
James Dongcbeebb12011-02-16 12:28:26 -08002483 int64_t prevTimestampUs = trackStartTimeOffsetUs;
James Dong2a4767e2010-04-20 11:50:11 -07002484 for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
2485 it != mSttsTableEntries.end(); ++it) {
2486 mOwner->writeInt32(it->sampleCount);
James Dongeff30e32010-08-13 14:16:26 -07002487
2488 // Make sure that we are calculating the sample duration the exactly
2489 // same way as we made decision on how to create stts entries.
2490 int64_t currTimestampUs = prevTimestampUs + it->sampleDurationUs;
2491 int32_t dur = ((currTimestampUs * mTimeScale + 500000LL) / 1000000LL -
2492 (prevTimestampUs * mTimeScale + 500000LL) / 1000000LL);
2493 prevTimestampUs += (it->sampleCount * it->sampleDurationUs);
2494
James Dong52d13f02010-07-02 11:39:06 -07002495 mOwner->writeInt32(dur);
Andreas Hubere46b7be2009-07-14 16:56:47 -07002496 }
Andreas Hubere46b7be2009-07-14 16:56:47 -07002497 mOwner->endBox(); // stts
2498
James Dongda8073c2010-07-30 17:41:22 -07002499 if (!mIsAudio) {
James Dongabed93a2010-04-22 17:27:04 -07002500 mOwner->beginBox("stss");
2501 mOwner->writeInt32(0); // version=0, flags=0
James Dongcb7e65c2010-09-02 11:19:11 -07002502 mOwner->writeInt32(mNumStssTableEntries); // number of sync frames
James Dongabed93a2010-04-22 17:27:04 -07002503 for (List<int32_t>::iterator it = mStssTableEntries.begin();
2504 it != mStssTableEntries.end(); ++it) {
2505 mOwner->writeInt32(*it);
2506 }
2507 mOwner->endBox(); // stss
2508 }
2509
Andreas Hubere46b7be2009-07-14 16:56:47 -07002510 mOwner->beginBox("stsz");
2511 mOwner->writeInt32(0); // version=0, flags=0
James Dong2a4767e2010-04-20 11:50:11 -07002512 if (mSamplesHaveSameSize) {
James Dong7e397842010-07-28 10:24:39 -07002513 List<size_t>::iterator it = mSampleSizes.begin();
2514 mOwner->writeInt32(*it); // default sample size
James Dong2a4767e2010-04-20 11:50:11 -07002515 } else {
2516 mOwner->writeInt32(0);
2517 }
James Donge991e5f2010-07-28 13:18:14 -07002518 mOwner->writeInt32(mNumSamples);
James Dong2a4767e2010-04-20 11:50:11 -07002519 if (!mSamplesHaveSameSize) {
James Dong7e397842010-07-28 10:24:39 -07002520 for (List<size_t>::iterator it = mSampleSizes.begin();
2521 it != mSampleSizes.end(); ++it) {
2522 mOwner->writeInt32(*it);
James Dong2a4767e2010-04-20 11:50:11 -07002523 }
Andreas Hubere46b7be2009-07-14 16:56:47 -07002524 }
2525 mOwner->endBox(); // stsz
2526
2527 mOwner->beginBox("stsc");
2528 mOwner->writeInt32(0); // version=0, flags=0
James Dongcb7e65c2010-09-02 11:19:11 -07002529 mOwner->writeInt32(mNumStscTableEntries);
James Dong3300e962010-04-21 16:14:15 -07002530 for (List<StscTableEntry>::iterator it = mStscTableEntries.begin();
2531 it != mStscTableEntries.end(); ++it) {
2532 mOwner->writeInt32(it->firstChunk);
2533 mOwner->writeInt32(it->samplesPerChunk);
2534 mOwner->writeInt32(it->sampleDescriptionId);
Andreas Hubere46b7be2009-07-14 16:56:47 -07002535 }
2536 mOwner->endBox(); // stsc
James Dong39a0b212010-06-23 00:18:40 -07002537 mOwner->beginBox(use32BitOffset? "stco": "co64");
Andreas Hubere46b7be2009-07-14 16:56:47 -07002538 mOwner->writeInt32(0); // version=0, flags=0
James Dongcb7e65c2010-09-02 11:19:11 -07002539 mOwner->writeInt32(mNumStcoTableEntries);
James Dongb1262a82010-11-16 14:04:54 -08002540 for (List<off64_t>::iterator it = mChunkOffsets.begin();
James Dong3300e962010-04-21 16:14:15 -07002541 it != mChunkOffsets.end(); ++it) {
James Dong39a0b212010-06-23 00:18:40 -07002542 if (use32BitOffset) {
2543 mOwner->writeInt32(static_cast<int32_t>(*it));
2544 } else {
2545 mOwner->writeInt64((*it));
2546 }
Andreas Hubere46b7be2009-07-14 16:56:47 -07002547 }
James Dong52d13f02010-07-02 11:39:06 -07002548 mOwner->endBox(); // stco or co64
Andreas Hubere46b7be2009-07-14 16:56:47 -07002549
2550 mOwner->endBox(); // stbl
James Dong39a0b212010-06-23 00:18:40 -07002551 mOwner->endBox(); // minf
Andreas Hubere46b7be2009-07-14 16:56:47 -07002552 mOwner->endBox(); // mdia
2553 mOwner->endBox(); // trak
2554}
2555
2556} // namespace android