blob: dfee2813bd4774e5298b53ec30c78ff71062512b [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>
Andreas Hubere46b7be2009-07-14 16:56:47 -070025
26#include <media/stagefright/MPEG4Writer.h>
27#include <media/stagefright/MediaBuffer.h>
28#include <media/stagefright/MetaData.h>
Andreas Huberb5ceb9e2009-08-26 14:48:20 -070029#include <media/stagefright/MediaDebug.h>
Andreas Hubere6c40962009-09-10 14:13:30 -070030#include <media/stagefright/MediaDefs.h>
Andreas Huber71c27d92010-03-19 11:43:15 -070031#include <media/stagefright/MediaErrors.h>
Andreas Hubere46b7be2009-07-14 16:56:47 -070032#include <media/stagefright/MediaSource.h>
33#include <media/stagefright/Utils.h>
James Dong18244862010-05-11 14:57:02 -070034#include <media/mediarecorder.h>
James Dong0f32fb32011-05-14 07:22:40 -070035#include <cutils/properties.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 Dong3aea0372011-05-06 12:19:04 -070048static const int64_t kInitialDelayTimeUs = 700000LL;
James Dong89a01042010-10-21 17:58:14 -070049
Andreas Hubere46b7be2009-07-14 16:56:47 -070050class MPEG4Writer::Track {
51public:
James Dong7fc8b4f2011-03-18 11:25:41 -070052 Track(MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId);
James Dong52d13f02010-07-02 11:39:06 -070053
Andreas Hubere46b7be2009-07-14 16:56:47 -070054 ~Track();
55
James Dong09936ed2010-06-24 19:04:27 -070056 status_t start(MetaData *params);
James Dongd0366622010-08-18 19:10:39 -070057 status_t stop();
58 status_t pause();
Andreas Huber033e6c32009-09-09 16:36:12 -070059 bool reachedEOS();
Andreas Hubere46b7be2009-07-14 16:56:47 -070060
Andreas Huber716582e2010-02-02 12:13:30 -080061 int64_t getDurationUs() const;
James Dong18244862010-05-11 14:57:02 -070062 int64_t getEstimatedTrackSizeBytes() const;
James Dong7a6cea42011-05-06 16:55:39 -070063 void writeTrackHeader(bool use32BitOffset = true);
James Dongda8073c2010-07-30 17:41:22 -070064 void bufferChunk(int64_t timestampUs);
65 bool isAvc() const { return mIsAvc; }
66 bool isAudio() const { return mIsAudio; }
67 bool isMPEG4() const { return mIsMPEG4; }
James Dongb1262a82010-11-16 14:04:54 -080068 void addChunkOffset(off64_t offset);
James Dong3aea0372011-05-06 12:19:04 -070069 int32_t getTrackId() const { return mTrackId; }
James Dong3f51fa72010-08-18 03:32:26 -070070 status_t dump(int fd, const Vector<String16>& args) const;
Andreas Hubere46b7be2009-07-14 16:56:47 -070071
72private:
73 MPEG4Writer *mOwner;
74 sp<MetaData> mMeta;
Andreas Huberbe06d262009-08-14 14:37:10 -070075 sp<MediaSource> mSource;
Andreas Hubere46b7be2009-07-14 16:56:47 -070076 volatile bool mDone;
James Dong08c74732010-06-10 12:28:15 -070077 volatile bool mPaused;
78 volatile bool mResumed;
James Dongd7ef5b62011-01-25 12:37:43 -080079 volatile bool mStarted;
James Dongda8073c2010-07-30 17:41:22 -070080 bool mIsAvc;
81 bool mIsAudio;
82 bool mIsMPEG4;
James Dong7fc8b4f2011-03-18 11:25:41 -070083 int32_t mTrackId;
James Dong91b22a92010-08-05 10:46:13 -070084 int64_t mTrackDurationUs;
James Donged742302011-05-06 11:27:59 -070085 int64_t mMaxChunkDurationUs;
James Dongb7208192010-08-02 19:13:40 -070086
James Dongb7208192010-08-02 19:13:40 -070087 bool mIsRealTimeRecording;
88 int64_t mMaxTimeStampUs;
James Dong18244862010-05-11 14:57:02 -070089 int64_t mEstimatedTrackSizeBytes;
James Dongcb7e65c2010-09-02 11:19:11 -070090 int64_t mMdatSizeBytes;
James Dong52d13f02010-07-02 11:39:06 -070091 int32_t mTimeScale;
Andreas Hubere46b7be2009-07-14 16:56:47 -070092
93 pthread_t mThread;
94
James Donge991e5f2010-07-28 13:18:14 -070095 // mNumSamples is used to track how many samples in mSampleSizes List.
96 // This is to reduce the cost associated with mSampleSizes.size() call,
97 // since it is O(n). Ideally, the fix should be in List class.
98 size_t mNumSamples;
James Dong7e397842010-07-28 10:24:39 -070099 List<size_t> mSampleSizes;
James Dong2a4767e2010-04-20 11:50:11 -0700100 bool mSamplesHaveSameSize;
101
James Dong3300e962010-04-21 16:14:15 -0700102 List<MediaBuffer *> mChunkSamples;
James Dongcb7e65c2010-09-02 11:19:11 -0700103
104 size_t mNumStcoTableEntries;
James Dongb1262a82010-11-16 14:04:54 -0800105 List<off64_t> mChunkOffsets;
James Dong3300e962010-04-21 16:14:15 -0700106
James Dongcb7e65c2010-09-02 11:19:11 -0700107 size_t mNumStscTableEntries;
James Dong3300e962010-04-21 16:14:15 -0700108 struct StscTableEntry {
109
110 StscTableEntry(uint32_t chunk, uint32_t samples, uint32_t id)
111 : firstChunk(chunk),
112 samplesPerChunk(samples),
113 sampleDescriptionId(id) {}
114
115 uint32_t firstChunk;
116 uint32_t samplesPerChunk;
117 uint32_t sampleDescriptionId;
118 };
119 List<StscTableEntry> mStscTableEntries;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700120
James Dongcb7e65c2010-09-02 11:19:11 -0700121 size_t mNumStssTableEntries;
James Dongabed93a2010-04-22 17:27:04 -0700122 List<int32_t> mStssTableEntries;
123
James Dong2a4767e2010-04-20 11:50:11 -0700124 struct SttsTableEntry {
125
James Donga7ea9f92011-06-06 19:00:40 -0700126 SttsTableEntry(uint32_t count, uint32_t duration)
127 : sampleCount(count), sampleDuration(duration) {}
James Dong2a4767e2010-04-20 11:50:11 -0700128
129 uint32_t sampleCount;
James Donga7ea9f92011-06-06 19:00:40 -0700130 uint32_t sampleDuration; // time scale based
James Dong2a4767e2010-04-20 11:50:11 -0700131 };
James Dong4108b1ed2011-06-07 19:45:54 -0700132 size_t mNumSttsTableEntries;
James Dong2a4767e2010-04-20 11:50:11 -0700133 List<SttsTableEntry> mSttsTableEntries;
134
James Dong4108b1ed2011-06-07 19:45:54 -0700135 struct CttsTableEntry {
136 CttsTableEntry(uint32_t count, int32_t timescaledDur)
137 : sampleCount(count), sampleDuration(timescaledDur) {}
138
139 uint32_t sampleCount;
140 int32_t sampleDuration; // time scale based
141 };
142 bool mHasNegativeCttsDeltaDuration;
143 size_t mNumCttsTableEntries;
144 List<CttsTableEntry> mCttsTableEntries;
145
James Dong481e05e2010-08-06 00:29:03 -0700146 // Sequence parameter set or picture parameter set
147 struct AVCParamSet {
148 AVCParamSet(uint16_t length, const uint8_t *data)
149 : mLength(length), mData(data) {}
150
151 uint16_t mLength;
152 const uint8_t *mData;
153 };
154 List<AVCParamSet> mSeqParamSets;
155 List<AVCParamSet> mPicParamSets;
156 uint8_t mProfileIdc;
157 uint8_t mProfileCompatible;
158 uint8_t mLevelIdc;
159
Andreas Hubere46b7be2009-07-14 16:56:47 -0700160 void *mCodecSpecificData;
161 size_t mCodecSpecificDataSize;
Andreas Huberf4e5baa2010-04-09 14:25:46 -0700162 bool mGotAllCodecSpecificData;
James Dong09936ed2010-06-24 19:04:27 -0700163 bool mTrackingProgressStatus;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700164
Andreas Huber033e6c32009-09-09 16:36:12 -0700165 bool mReachedEOS;
James Dong9db798d2010-05-13 11:47:36 -0700166 int64_t mStartTimestampUs;
James Dong3aea0372011-05-06 12:19:04 -0700167 int64_t mStartTimeRealUs;
168 int64_t mFirstSampleTimeRealUs;
James Dong09936ed2010-06-24 19:04:27 -0700169 int64_t mPreviousTrackTimeUs;
170 int64_t mTrackEveryTimeDurationUs;
Andreas Huber033e6c32009-09-09 16:36:12 -0700171
James Dongacee8e72010-10-03 10:59:26 -0700172 // Update the audio track's drift information.
173 void updateDriftTime(const sp<MetaData>& meta);
174
Andreas Hubere46b7be2009-07-14 16:56:47 -0700175 static void *ThreadWrapper(void *me);
James Dongd0366622010-08-18 19:10:39 -0700176 status_t threadEntry();
Andreas Hubere46b7be2009-07-14 16:56:47 -0700177
James Dong481e05e2010-08-06 00:29:03 -0700178 const uint8_t *parseParamSet(
179 const uint8_t *data, size_t length, int type, size_t *paramSetLen);
180
James Dong7a6cea42011-05-06 16:55:39 -0700181 status_t makeAVCCodecSpecificData(const uint8_t *data, size_t size);
182 status_t copyAVCCodecSpecificData(const uint8_t *data, size_t size);
183 status_t parseAVCCodecSpecificData(const uint8_t *data, size_t size);
James Dongd4760c22010-06-26 08:24:47 -0700184
185 // Track authoring progress status
James Dong85edea72010-07-15 19:08:20 -0700186 void trackProgressStatus(int64_t timeUs, status_t err = OK);
James Dong09936ed2010-06-24 19:04:27 -0700187 void initTrackingProgressStatus(MetaData *params);
Andreas Huber71c27d92010-03-19 11:43:15 -0700188
Andreas Huber45bac572010-07-01 08:19:52 -0700189 void getCodecSpecificDataFromInputFormatIfPossible();
190
James Dongeff30e32010-08-13 14:16:26 -0700191 // Determine the track time scale
192 // If it is an audio track, try to use the sampling rate as
193 // the time scale; however, if user chooses the overwrite
194 // value, the user-supplied time scale will be used.
195 void setTimeScale();
196
James Dong62948fa2010-08-19 13:52:47 -0700197 // Simple validation on the codec specific data
198 status_t checkCodecSpecificData() const;
James Dongb9d7e012010-11-09 11:15:47 -0800199 int32_t mRotation;
James Dong62948fa2010-08-19 13:52:47 -0700200
James Dongcb7e65c2010-09-02 11:19:11 -0700201 void updateTrackSizeEstimate();
202 void addOneStscTableEntry(size_t chunkId, size_t sampleId);
203 void addOneStssTableEntry(size_t sampleId);
James Donga7ea9f92011-06-06 19:00:40 -0700204
205 // Duration is time scale based
206 void addOneSttsTableEntry(size_t sampleCount, int32_t timescaledDur);
James Dong4108b1ed2011-06-07 19:45:54 -0700207 void addOneCttsTableEntry(size_t sampleCount, int32_t timescaledDur);
James Dong839ba2c2011-06-21 17:22:37 -0700208
209 bool isTrackMalFormed() const;
James Donged742302011-05-06 11:27:59 -0700210 void sendTrackSummary(bool hasMultipleTracks);
James Dongcb7e65c2010-09-02 11:19:11 -0700211
James Dong7a6cea42011-05-06 16:55:39 -0700212 // Write the boxes
213 void writeStcoBox(bool use32BitOffset);
214 void writeStscBox();
215 void writeStszBox();
216 void writeStssBox();
217 void writeSttsBox();
James Dong4108b1ed2011-06-07 19:45:54 -0700218 void writeCttsBox();
James Dong7a6cea42011-05-06 16:55:39 -0700219 void writeD263Box();
220 void writePaspBox();
221 void writeAvccBox();
222 void writeUrlBox();
223 void writeDrefBox();
224 void writeDinfBox();
225 void writeDamrBox();
226 void writeMdhdBox(time_t now);
227 void writeSmhdBox();
228 void writeVmhdBox();
229 void writeHdlrBox();
230 void writeTkhdBox(time_t now);
231 void writeMp4aEsdsBox();
232 void writeMp4vEsdsBox();
233 void writeAudioFourCCBox();
234 void writeVideoFourCCBox();
235 void writeStblBox(bool use32BitOffset);
236
Andreas Hubere46b7be2009-07-14 16:56:47 -0700237 Track(const Track &);
238 Track &operator=(const Track &);
239};
240
241MPEG4Writer::MPEG4Writer(const char *filename)
James Dong2747e0e2010-11-18 20:59:13 -0800242 : mFd(-1),
243 mInitCheck(NO_INIT),
James Dong7755cdd2010-09-02 10:49:55 -0700244 mUse4ByteNalLength(true),
James Dong39a0b212010-06-23 00:18:40 -0700245 mUse32BitOffset(true),
James Dong6a9e39a2010-10-04 16:41:53 -0700246 mIsFileSizeLimitExplicitlyRequested(false),
James Dong08c74732010-06-10 12:28:15 -0700247 mPaused(false),
248 mStarted(false),
James Dong0e27fce2011-07-08 16:51:16 -0700249 mWriterThreadStarted(false),
Andreas Hubere46b7be2009-07-14 16:56:47 -0700250 mOffset(0),
James Dong3300e962010-04-21 16:14:15 -0700251 mMdatOffset(0),
James Dongb5e74232010-05-07 10:26:24 -0700252 mEstimatedMoovBoxSize(0),
James Dong987ab482011-05-11 19:09:25 -0700253 mInterleaveDurationUs(1000000),
254 mLatitudex10000(0),
255 mLongitudex10000(0),
James Dong13a33162011-05-09 16:56:25 -0700256 mAreGeoTagsAvailable(false),
257 mStartTimeOffsetMs(-1) {
James Dong2747e0e2010-11-18 20:59:13 -0800258
James Dongf84433f2011-03-21 14:26:18 -0700259 mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR);
James Dong2747e0e2010-11-18 20:59:13 -0800260 if (mFd >= 0) {
261 mInitCheck = OK;
262 }
Andreas Hubere46b7be2009-07-14 16:56:47 -0700263}
264
Andreas Huberea6a38c2009-11-16 15:43:38 -0800265MPEG4Writer::MPEG4Writer(int fd)
James Dong2747e0e2010-11-18 20:59:13 -0800266 : mFd(dup(fd)),
267 mInitCheck(mFd < 0? NO_INIT: OK),
James Dong7755cdd2010-09-02 10:49:55 -0700268 mUse4ByteNalLength(true),
James Dong39a0b212010-06-23 00:18:40 -0700269 mUse32BitOffset(true),
James Dong6a9e39a2010-10-04 16:41:53 -0700270 mIsFileSizeLimitExplicitlyRequested(false),
James Dong08c74732010-06-10 12:28:15 -0700271 mPaused(false),
272 mStarted(false),
James Dong0e27fce2011-07-08 16:51:16 -0700273 mWriterThreadStarted(false),
Andreas Huberea6a38c2009-11-16 15:43:38 -0800274 mOffset(0),
James Dong3300e962010-04-21 16:14:15 -0700275 mMdatOffset(0),
James Dongb5e74232010-05-07 10:26:24 -0700276 mEstimatedMoovBoxSize(0),
James Dong987ab482011-05-11 19:09:25 -0700277 mInterleaveDurationUs(1000000),
278 mLatitudex10000(0),
279 mLongitudex10000(0),
James Dong13a33162011-05-09 16:56:25 -0700280 mAreGeoTagsAvailable(false),
281 mStartTimeOffsetMs(-1) {
Andreas Huberea6a38c2009-11-16 15:43:38 -0800282}
283
Andreas Hubere46b7be2009-07-14 16:56:47 -0700284MPEG4Writer::~MPEG4Writer() {
285 stop();
286
James Dongcb7e65c2010-09-02 11:19:11 -0700287 while (!mTracks.empty()) {
288 List<Track *>::iterator it = mTracks.begin();
Andreas Hubere46b7be2009-07-14 16:56:47 -0700289 delete *it;
James Dongcb7e65c2010-09-02 11:19:11 -0700290 (*it) = NULL;
291 mTracks.erase(it);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700292 }
293 mTracks.clear();
294}
295
James Dong3f51fa72010-08-18 03:32:26 -0700296status_t MPEG4Writer::dump(
297 int fd, const Vector<String16>& args) {
298 const size_t SIZE = 256;
299 char buffer[SIZE];
300 String8 result;
301 snprintf(buffer, SIZE, " MPEG4Writer %p\n", this);
302 result.append(buffer);
303 snprintf(buffer, SIZE, " mStarted: %s\n", mStarted? "true": "false");
304 result.append(buffer);
305 ::write(fd, result.string(), result.size());
306 for (List<Track *>::iterator it = mTracks.begin();
307 it != mTracks.end(); ++it) {
308 (*it)->dump(fd, args);
309 }
310 return OK;
311}
312
313status_t MPEG4Writer::Track::dump(
314 int fd, const Vector<String16>& args) const {
315 const size_t SIZE = 256;
316 char buffer[SIZE];
317 String8 result;
318 snprintf(buffer, SIZE, " %s track\n", mIsAudio? "Audio": "Video");
319 result.append(buffer);
320 snprintf(buffer, SIZE, " reached EOS: %s\n",
321 mReachedEOS? "true": "false");
322 result.append(buffer);
323 ::write(fd, result.string(), result.size());
324 return OK;
325}
326
Andreas Huber996dddf2010-01-25 15:30:31 -0800327status_t MPEG4Writer::addSource(const sp<MediaSource> &source) {
James Dong7fc8b4f2011-03-18 11:25:41 -0700328 Mutex::Autolock l(mLock);
329 if (mStarted) {
330 LOGE("Attempt to add source AFTER recording is started");
331 return UNKNOWN_ERROR;
332 }
333 Track *track = new Track(this, source, mTracks.size());
Andreas Hubere46b7be2009-07-14 16:56:47 -0700334 mTracks.push_back(track);
Andreas Huber996dddf2010-01-25 15:30:31 -0800335
336 return OK;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700337}
338
James Dong09936ed2010-06-24 19:04:27 -0700339status_t MPEG4Writer::startTracks(MetaData *params) {
James Dong08c74732010-06-10 12:28:15 -0700340 for (List<Track *>::iterator it = mTracks.begin();
341 it != mTracks.end(); ++it) {
James Dong09936ed2010-06-24 19:04:27 -0700342 status_t err = (*it)->start(params);
James Dong08c74732010-06-10 12:28:15 -0700343
344 if (err != OK) {
345 for (List<Track *>::iterator it2 = mTracks.begin();
346 it2 != it; ++it2) {
347 (*it2)->stop();
348 }
349
350 return err;
351 }
352 }
353 return OK;
354}
355
James Dong6feaa462010-06-20 08:20:54 -0700356int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) {
357 // This implementation is highly experimental/heurisitic.
358 //
359 // Statistical analysis shows that metadata usually accounts
360 // for a small portion of the total file size, usually < 0.6%.
James Dong6feaa462010-06-20 08:20:54 -0700361
James Dong22b37fa2010-10-19 21:28:47 -0700362 // The default MIN_MOOV_BOX_SIZE is set to 0.6% x 1MB / 2,
James Dong6feaa462010-06-20 08:20:54 -0700363 // where 1MB is the common file size limit for MMS application.
James Dong22b37fa2010-10-19 21:28:47 -0700364 // The default MAX _MOOV_BOX_SIZE value is based on about 3
James Dong6feaa462010-06-20 08:20:54 -0700365 // minute video recording with a bit rate about 3 Mbps, because
366 // statistics also show that most of the video captured are going
367 // to be less than 3 minutes.
368
369 // If the estimation is wrong, we will pay the price of wasting
370 // some reserved space. This should not happen so often statistically.
371 static const int32_t factor = mUse32BitOffset? 1: 2;
James Dong22b37fa2010-10-19 21:28:47 -0700372 static const int64_t MIN_MOOV_BOX_SIZE = 3 * 1024; // 3 KB
James Dong6feaa462010-06-20 08:20:54 -0700373 static const int64_t MAX_MOOV_BOX_SIZE = (180 * 3000000 * 6LL / 8000);
374 int64_t size = MIN_MOOV_BOX_SIZE;
375
James Dong22b37fa2010-10-19 21:28:47 -0700376 // Max file size limit is set
James Dong6a9e39a2010-10-04 16:41:53 -0700377 if (mMaxFileSizeLimitBytes != 0 && mIsFileSizeLimitExplicitlyRequested) {
James Dong22b37fa2010-10-19 21:28:47 -0700378 size = mMaxFileSizeLimitBytes * 6 / 1000;
379 }
380
381 // Max file duration limit is set
382 if (mMaxFileDurationLimitUs != 0) {
383 if (bitRate > 0) {
384 int64_t size2 =
385 ((mMaxFileDurationLimitUs * bitRate * 6) / 1000 / 8000000);
386 if (mMaxFileSizeLimitBytes != 0 && mIsFileSizeLimitExplicitlyRequested) {
387 // When both file size and duration limits are set,
388 // we use the smaller limit of the two.
389 if (size > size2) {
390 size = size2;
391 }
392 } else {
393 // Only max file duration limit is set
394 size = size2;
395 }
James Dong6feaa462010-06-20 08:20:54 -0700396 }
397 }
James Dong22b37fa2010-10-19 21:28:47 -0700398
James Dong6feaa462010-06-20 08:20:54 -0700399 if (size < MIN_MOOV_BOX_SIZE) {
400 size = MIN_MOOV_BOX_SIZE;
401 }
402
403 // Any long duration recording will be probably end up with
404 // non-streamable mp4 file.
405 if (size > MAX_MOOV_BOX_SIZE) {
406 size = MAX_MOOV_BOX_SIZE;
407 }
408
James Dong6a9e39a2010-10-04 16:41:53 -0700409 LOGI("limits: %lld/%lld bytes/us, bit rate: %d bps and the estimated"
James Dong6feaa462010-06-20 08:20:54 -0700410 " moov size %lld bytes",
411 mMaxFileSizeLimitBytes, mMaxFileDurationLimitUs, bitRate, size);
412 return factor * size;
413}
414
415status_t MPEG4Writer::start(MetaData *param) {
James Dong2747e0e2010-11-18 20:59:13 -0800416 if (mInitCheck != OK) {
Andreas Huber033e6c32009-09-09 16:36:12 -0700417 return UNKNOWN_ERROR;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700418 }
419
James Dong6a9e39a2010-10-04 16:41:53 -0700420 /*
421 * Check mMaxFileSizeLimitBytes at the beginning
422 * since mMaxFileSizeLimitBytes may be implicitly
423 * changed later for 32-bit file offset even if
424 * user does not ask to set it explicitly.
425 */
426 if (mMaxFileSizeLimitBytes != 0) {
427 mIsFileSizeLimitExplicitlyRequested = true;
428 }
429
James Dong6feaa462010-06-20 08:20:54 -0700430 int32_t use64BitOffset;
431 if (param &&
432 param->findInt32(kKey64BitFileOffset, &use64BitOffset) &&
433 use64BitOffset) {
434 mUse32BitOffset = false;
435 }
436
James Dongcb7e65c2010-09-02 11:19:11 -0700437 if (mUse32BitOffset) {
438 // Implicit 32 bit file size limit
439 if (mMaxFileSizeLimitBytes == 0) {
440 mMaxFileSizeLimitBytes = kMax32BitFileSize;
441 }
442
443 // If file size is set to be larger than the 32 bit file
444 // size limit, treat it as an error.
445 if (mMaxFileSizeLimitBytes > kMax32BitFileSize) {
James Dongacee8e72010-10-03 10:59:26 -0700446 LOGW("32-bit file size limit (%lld bytes) too big. "
James Donga4fb8162010-09-08 15:13:36 -0700447 "It is changed to %lld bytes",
448 mMaxFileSizeLimitBytes, kMax32BitFileSize);
449 mMaxFileSizeLimitBytes = kMax32BitFileSize;
James Dongcb7e65c2010-09-02 11:19:11 -0700450 }
451 }
452
James Dong7755cdd2010-09-02 10:49:55 -0700453 int32_t use2ByteNalLength;
454 if (param &&
455 param->findInt32(kKey2ByteNalLength, &use2ByteNalLength) &&
456 use2ByteNalLength) {
457 mUse4ByteNalLength = false;
James Dong6feaa462010-06-20 08:20:54 -0700458 }
459
James Dong438e4f62010-06-23 16:51:39 -0700460 mStartTimestampUs = -1;
James Dong09936ed2010-06-24 19:04:27 -0700461
James Dong08c74732010-06-10 12:28:15 -0700462 if (mStarted) {
463 if (mPaused) {
464 mPaused = false;
James Dong09936ed2010-06-24 19:04:27 -0700465 return startTracks(param);
James Dong08c74732010-06-10 12:28:15 -0700466 }
467 return OK;
468 }
469
James Dong52d13f02010-07-02 11:39:06 -0700470 if (!param ||
471 !param->findInt32(kKeyTimeScale, &mTimeScale)) {
472 mTimeScale = 1000;
473 }
474 CHECK(mTimeScale > 0);
Steve Block71f2cf12011-10-20 11:56:00 +0100475 ALOGV("movie time scale: %d", mTimeScale);
James Dong52d13f02010-07-02 11:39:06 -0700476
James Dongb5e74232010-05-07 10:26:24 -0700477 mStreamableFile = true;
478 mWriteMoovBoxToMemory = false;
479 mMoovBoxBuffer = NULL;
480 mMoovBoxBufferOffset = 0;
481
James Dong7a6cea42011-05-06 16:55:39 -0700482 writeFtypBox(param);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700483
James Dongb5e74232010-05-07 10:26:24 -0700484 mFreeBoxOffset = mOffset;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700485
James Dongb5e74232010-05-07 10:26:24 -0700486 if (mEstimatedMoovBoxSize == 0) {
James Dong6feaa462010-06-20 08:20:54 -0700487 int32_t bitRate = -1;
488 if (param) {
489 param->findInt32(kKeyBitRate, &bitRate);
490 }
491 mEstimatedMoovBoxSize = estimateMoovBoxSize(bitRate);
James Dongb5e74232010-05-07 10:26:24 -0700492 }
493 CHECK(mEstimatedMoovBoxSize >= 8);
James Dongb1262a82010-11-16 14:04:54 -0800494 lseek64(mFd, mFreeBoxOffset, SEEK_SET);
James Dongb5e74232010-05-07 10:26:24 -0700495 writeInt32(mEstimatedMoovBoxSize);
496 write("free", 4);
497
498 mMdatOffset = mFreeBoxOffset + mEstimatedMoovBoxSize;
499 mOffset = mMdatOffset;
James Dongb1262a82010-11-16 14:04:54 -0800500 lseek64(mFd, mMdatOffset, SEEK_SET);
James Dong39a0b212010-06-23 00:18:40 -0700501 if (mUse32BitOffset) {
502 write("????mdat", 8);
503 } else {
504 write("\x00\x00\x00\x01mdat????????", 16);
505 }
James Dongda8073c2010-07-30 17:41:22 -0700506
507 status_t err = startWriterThread();
James Dong08c74732010-06-10 12:28:15 -0700508 if (err != OK) {
509 return err;
510 }
James Dongda8073c2010-07-30 17:41:22 -0700511
512 err = startTracks(param);
513 if (err != OK) {
514 return err;
515 }
516
James Dong08c74732010-06-10 12:28:15 -0700517 mStarted = true;
518 return OK;
519}
520
James Dongcb7e65c2010-09-02 11:19:11 -0700521bool MPEG4Writer::use32BitFileOffset() const {
522 return mUse32BitOffset;
523}
524
James Dongd0366622010-08-18 19:10:39 -0700525status_t MPEG4Writer::pause() {
James Dong2747e0e2010-11-18 20:59:13 -0800526 if (mInitCheck != OK) {
James Dongd0366622010-08-18 19:10:39 -0700527 return OK;
James Dong08c74732010-06-10 12:28:15 -0700528 }
529 mPaused = true;
James Dongd0366622010-08-18 19:10:39 -0700530 status_t err = OK;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700531 for (List<Track *>::iterator it = mTracks.begin();
532 it != mTracks.end(); ++it) {
James Dongd0366622010-08-18 19:10:39 -0700533 status_t status = (*it)->pause();
534 if (status != OK) {
535 err = status;
536 }
Andreas Hubere46b7be2009-07-14 16:56:47 -0700537 }
James Dongd0366622010-08-18 19:10:39 -0700538 return err;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700539}
540
James Dongda8073c2010-07-30 17:41:22 -0700541void MPEG4Writer::stopWriterThread() {
James Dong42338ff2010-10-29 16:01:06 -0700542 LOGD("Stopping writer thread");
James Dong0e27fce2011-07-08 16:51:16 -0700543 if (!mWriterThreadStarted) {
544 return;
545 }
James Dongda8073c2010-07-30 17:41:22 -0700546
547 {
548 Mutex::Autolock autolock(mLock);
549
550 mDone = true;
551 mChunkReadyCondition.signal();
552 }
553
554 void *dummy;
555 pthread_join(mThread, &dummy);
James Dong0e27fce2011-07-08 16:51:16 -0700556 mWriterThreadStarted = false;
James Dong42338ff2010-10-29 16:01:06 -0700557 LOGD("Writer thread stopped");
James Dongda8073c2010-07-30 17:41:22 -0700558}
559
James Dongb9d7e012010-11-09 11:15:47 -0800560/*
561 * MP4 file standard defines a composition matrix:
562 * | a b u |
563 * | c d v |
564 * | x y w |
565 *
566 * the element in the matrix is stored in the following
567 * order: {a, b, u, c, d, v, x, y, w},
568 * where a, b, c, d, x, and y is in 16.16 format, while
569 * u, v and w is in 2.30 format.
570 */
571void MPEG4Writer::writeCompositionMatrix(int degrees) {
Steve Block71f2cf12011-10-20 11:56:00 +0100572 ALOGV("writeCompositionMatrix");
James Dongb9d7e012010-11-09 11:15:47 -0800573 uint32_t a = 0x00010000;
574 uint32_t b = 0;
575 uint32_t c = 0;
576 uint32_t d = 0x00010000;
577 switch (degrees) {
578 case 0:
579 break;
580 case 90:
581 a = 0;
582 b = 0x00010000;
583 c = 0xFFFF0000;
584 d = 0;
585 break;
586 case 180:
587 a = 0xFFFF0000;
588 d = 0xFFFF0000;
589 break;
590 case 270:
591 a = 0;
592 b = 0xFFFF0000;
593 c = 0x00010000;
594 d = 0;
595 break;
596 default:
597 CHECK(!"Should never reach this unknown rotation");
598 break;
599 }
600
601 writeInt32(a); // a
602 writeInt32(b); // b
603 writeInt32(0); // u
604 writeInt32(c); // c
605 writeInt32(d); // d
606 writeInt32(0); // v
607 writeInt32(0); // x
608 writeInt32(0); // y
609 writeInt32(0x40000000); // w
610}
611
James Dong0e27fce2011-07-08 16:51:16 -0700612void MPEG4Writer::release() {
613 close(mFd);
614 mFd = -1;
615 mInitCheck = NO_INIT;
616 mStarted = false;
617}
James Dongb9d7e012010-11-09 11:15:47 -0800618
James Dongd0366622010-08-18 19:10:39 -0700619status_t MPEG4Writer::stop() {
James Dong2747e0e2010-11-18 20:59:13 -0800620 if (mInitCheck != OK) {
James Dongd0366622010-08-18 19:10:39 -0700621 return OK;
James Dong0e27fce2011-07-08 16:51:16 -0700622 } else {
623 if (!mWriterThreadStarted ||
624 !mStarted) {
625 if (mWriterThreadStarted) {
626 stopWriterThread();
627 }
628 release();
629 return OK;
630 }
Andreas Hubere46b7be2009-07-14 16:56:47 -0700631 }
632
James Dongd0366622010-08-18 19:10:39 -0700633 status_t err = OK;
James Dong52d13f02010-07-02 11:39:06 -0700634 int64_t maxDurationUs = 0;
James Dongda580762011-01-19 11:50:19 -0800635 int64_t minDurationUs = 0x7fffffffffffffffLL;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700636 for (List<Track *>::iterator it = mTracks.begin();
637 it != mTracks.end(); ++it) {
James Dongd0366622010-08-18 19:10:39 -0700638 status_t status = (*it)->stop();
639 if (err == OK && status != OK) {
640 err = status;
641 }
Andreas Hubere46b7be2009-07-14 16:56:47 -0700642
James Dong52d13f02010-07-02 11:39:06 -0700643 int64_t durationUs = (*it)->getDurationUs();
644 if (durationUs > maxDurationUs) {
645 maxDurationUs = durationUs;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700646 }
James Dongda580762011-01-19 11:50:19 -0800647 if (durationUs < minDurationUs) {
648 minDurationUs = durationUs;
649 }
650 }
651
652 if (mTracks.size() > 1) {
653 LOGD("Duration from tracks range is [%lld, %lld] us",
654 minDurationUs, maxDurationUs);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700655 }
656
James Dongda8073c2010-07-30 17:41:22 -0700657 stopWriterThread();
James Dongb5e74232010-05-07 10:26:24 -0700658
James Dongd0366622010-08-18 19:10:39 -0700659 // Do not write out movie header on error.
660 if (err != OK) {
James Dong0e27fce2011-07-08 16:51:16 -0700661 release();
James Dongd0366622010-08-18 19:10:39 -0700662 return err;
663 }
664
Andreas Hubere46b7be2009-07-14 16:56:47 -0700665 // Fix up the size of the 'mdat' chunk.
James Dong39a0b212010-06-23 00:18:40 -0700666 if (mUse32BitOffset) {
James Dongb1262a82010-11-16 14:04:54 -0800667 lseek64(mFd, mMdatOffset, SEEK_SET);
James Dong39a0b212010-06-23 00:18:40 -0700668 int32_t size = htonl(static_cast<int32_t>(mOffset - mMdatOffset));
James Dongb1262a82010-11-16 14:04:54 -0800669 ::write(mFd, &size, 4);
James Dong39a0b212010-06-23 00:18:40 -0700670 } else {
James Dongb1262a82010-11-16 14:04:54 -0800671 lseek64(mFd, mMdatOffset + 8, SEEK_SET);
James Dong39a0b212010-06-23 00:18:40 -0700672 int64_t size = mOffset - mMdatOffset;
673 size = hton64(size);
James Dongb1262a82010-11-16 14:04:54 -0800674 ::write(mFd, &size, 8);
James Dong39a0b212010-06-23 00:18:40 -0700675 }
James Dongb1262a82010-11-16 14:04:54 -0800676 lseek64(mFd, mOffset, SEEK_SET);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700677
James Dongb1262a82010-11-16 14:04:54 -0800678 const off64_t moovOffset = mOffset;
James Dongb5e74232010-05-07 10:26:24 -0700679 mWriteMoovBoxToMemory = true;
680 mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
681 mMoovBoxBufferOffset = 0;
682 CHECK(mMoovBoxBuffer != NULL);
James Dong7a6cea42011-05-06 16:55:39 -0700683 writeMoovBox(maxDurationUs);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700684
James Dongb5e74232010-05-07 10:26:24 -0700685 mWriteMoovBoxToMemory = false;
686 if (mStreamableFile) {
687 CHECK(mMoovBoxBufferOffset + 8 <= mEstimatedMoovBoxSize);
688
689 // Moov box
James Dongb1262a82010-11-16 14:04:54 -0800690 lseek64(mFd, mFreeBoxOffset, SEEK_SET);
James Dongb5e74232010-05-07 10:26:24 -0700691 mOffset = mFreeBoxOffset;
James Dong2747e0e2010-11-18 20:59:13 -0800692 write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset);
James Dongb5e74232010-05-07 10:26:24 -0700693
694 // Free box
James Dongb1262a82010-11-16 14:04:54 -0800695 lseek64(mFd, mOffset, SEEK_SET);
James Dongb5e74232010-05-07 10:26:24 -0700696 writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset);
697 write("free", 4);
698
699 // Free temp memory
700 free(mMoovBoxBuffer);
701 mMoovBoxBuffer = NULL;
702 mMoovBoxBufferOffset = 0;
James Dong6feaa462010-06-20 08:20:54 -0700703 } else {
704 LOGI("The mp4 file will not be streamable.");
James Dongb5e74232010-05-07 10:26:24 -0700705 }
706
Andreas Huberb5ceb9e2009-08-26 14:48:20 -0700707 CHECK(mBoxes.empty());
Andreas Hubere46b7be2009-07-14 16:56:47 -0700708
James Dong0e27fce2011-07-08 16:51:16 -0700709 release();
James Dongd0366622010-08-18 19:10:39 -0700710 return err;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700711}
712
James Dong7a6cea42011-05-06 16:55:39 -0700713void MPEG4Writer::writeMvhdBox(int64_t durationUs) {
714 time_t now = time(NULL);
715 beginBox("mvhd");
716 writeInt32(0); // version=0, flags=0
717 writeInt32(now); // creation time
718 writeInt32(now); // modification time
719 writeInt32(mTimeScale); // mvhd timescale
720 int32_t duration = (durationUs * mTimeScale + 5E5) / 1E6;
721 writeInt32(duration);
722 writeInt32(0x10000); // rate: 1.0
723 writeInt16(0x100); // volume
724 writeInt16(0); // reserved
725 writeInt32(0); // reserved
726 writeInt32(0); // reserved
727 writeCompositionMatrix(0); // matrix
728 writeInt32(0); // predefined
729 writeInt32(0); // predefined
730 writeInt32(0); // predefined
731 writeInt32(0); // predefined
732 writeInt32(0); // predefined
733 writeInt32(0); // predefined
734 writeInt32(mTracks.size() + 1); // nextTrackID
735 endBox(); // mvhd
736}
737
738void MPEG4Writer::writeMoovBox(int64_t durationUs) {
739 beginBox("moov");
740 writeMvhdBox(durationUs);
James Dong987ab482011-05-11 19:09:25 -0700741 if (mAreGeoTagsAvailable) {
742 writeUdtaBox();
743 }
James Dong7a6cea42011-05-06 16:55:39 -0700744 int32_t id = 1;
745 for (List<Track *>::iterator it = mTracks.begin();
746 it != mTracks.end(); ++it, ++id) {
747 (*it)->writeTrackHeader(mUse32BitOffset);
748 }
749 endBox(); // moov
750}
751
James Dongf84e4a62011-05-17 22:39:06 -0700752void MPEG4Writer::writeFtypBox(MetaData *param) {
James Dong7a6cea42011-05-06 16:55:39 -0700753 beginBox("ftyp");
754
755 int32_t fileType;
756 if (param && param->findInt32(kKeyFileType, &fileType) &&
757 fileType != OUTPUT_FORMAT_MPEG_4) {
758 writeFourcc("3gp4");
759 } else {
760 writeFourcc("isom");
761 }
762
763 writeInt32(0);
764 writeFourcc("isom");
765 writeFourcc("3gp4");
766 endBox();
767}
768
James Dong0f32fb32011-05-14 07:22:40 -0700769static bool isTestModeEnabled() {
770#if (PROPERTY_VALUE_MAX < 5)
771#error "PROPERTY_VALUE_MAX must be at least 5"
772#endif
773
774 // Test mode is enabled only if rw.media.record.test system
775 // property is enabled.
776 char value[PROPERTY_VALUE_MAX];
777 if (property_get("rw.media.record.test", value, NULL) &&
778 (!strcasecmp(value, "true") || !strcasecmp(value, "1"))) {
779 return true;
780 }
781 return false;
782}
783
James Dong3aea0372011-05-06 12:19:04 -0700784void MPEG4Writer::sendSessionSummary() {
James Dong0f32fb32011-05-14 07:22:40 -0700785 // Send session summary only if test mode is enabled
786 if (!isTestModeEnabled()) {
787 return;
788 }
789
James Dong3aea0372011-05-06 12:19:04 -0700790 for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
791 it != mChunkInfos.end(); ++it) {
792 int trackNum = it->mTrack->getTrackId() << 28;
793 notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
794 trackNum | MEDIA_RECORDER_TRACK_INTER_CHUNK_TIME_MS,
795 it->mMaxInterChunkDurUs);
796 }
797}
798
James Dong3300e962010-04-21 16:14:15 -0700799status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) {
800 mInterleaveDurationUs = durationUs;
801 return OK;
802}
Andreas Hubere46b7be2009-07-14 16:56:47 -0700803
James Dong3300e962010-04-21 16:14:15 -0700804void MPEG4Writer::lock() {
805 mLock.lock();
806}
807
808void MPEG4Writer::unlock() {
809 mLock.unlock();
810}
811
James Dongb1262a82010-11-16 14:04:54 -0800812off64_t MPEG4Writer::addSample_l(MediaBuffer *buffer) {
813 off64_t old_offset = mOffset;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700814
James Dongb1262a82010-11-16 14:04:54 -0800815 ::write(mFd,
816 (const uint8_t *)buffer->data() + buffer->range_offset(),
817 buffer->range_length());
Andreas Hubere46b7be2009-07-14 16:56:47 -0700818
819 mOffset += buffer->range_length();
820
821 return old_offset;
822}
823
Andreas Huber71c27d92010-03-19 11:43:15 -0700824static void StripStartcode(MediaBuffer *buffer) {
825 if (buffer->range_length() < 4) {
826 return;
827 }
828
829 const uint8_t *ptr =
830 (const uint8_t *)buffer->data() + buffer->range_offset();
831
832 if (!memcmp(ptr, "\x00\x00\x00\x01", 4)) {
833 buffer->set_range(
834 buffer->range_offset() + 4, buffer->range_length() - 4);
835 }
836}
837
James Dongb1262a82010-11-16 14:04:54 -0800838off64_t MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
839 off64_t old_offset = mOffset;
Andreas Huberea6a38c2009-11-16 15:43:38 -0800840
841 size_t length = buffer->range_length();
Andreas Huber71c27d92010-03-19 11:43:15 -0700842
James Dong7755cdd2010-09-02 10:49:55 -0700843 if (mUse4ByteNalLength) {
844 uint8_t x = length >> 24;
James Dongb1262a82010-11-16 14:04:54 -0800845 ::write(mFd, &x, 1);
James Dong7755cdd2010-09-02 10:49:55 -0700846 x = (length >> 16) & 0xff;
James Dongb1262a82010-11-16 14:04:54 -0800847 ::write(mFd, &x, 1);
James Dong7755cdd2010-09-02 10:49:55 -0700848 x = (length >> 8) & 0xff;
James Dongb1262a82010-11-16 14:04:54 -0800849 ::write(mFd, &x, 1);
James Dong7755cdd2010-09-02 10:49:55 -0700850 x = length & 0xff;
James Dongb1262a82010-11-16 14:04:54 -0800851 ::write(mFd, &x, 1);
Andreas Huberea6a38c2009-11-16 15:43:38 -0800852
James Dongb1262a82010-11-16 14:04:54 -0800853 ::write(mFd,
854 (const uint8_t *)buffer->data() + buffer->range_offset(),
855 length);
856
James Dong7755cdd2010-09-02 10:49:55 -0700857 mOffset += length + 4;
858 } else {
859 CHECK(length < 65536);
Andreas Huberea6a38c2009-11-16 15:43:38 -0800860
James Dong7755cdd2010-09-02 10:49:55 -0700861 uint8_t x = length >> 8;
James Dongb1262a82010-11-16 14:04:54 -0800862 ::write(mFd, &x, 1);
James Dong7755cdd2010-09-02 10:49:55 -0700863 x = length & 0xff;
James Dongb1262a82010-11-16 14:04:54 -0800864 ::write(mFd, &x, 1);
865 ::write(mFd, (const uint8_t *)buffer->data() + buffer->range_offset(), length);
James Dong7755cdd2010-09-02 10:49:55 -0700866 mOffset += length + 2;
867 }
Andreas Huberea6a38c2009-11-16 15:43:38 -0800868
869 return old_offset;
870}
871
James Dongb5e74232010-05-07 10:26:24 -0700872size_t MPEG4Writer::write(
James Dong2747e0e2010-11-18 20:59:13 -0800873 const void *ptr, size_t size, size_t nmemb) {
James Dongb5e74232010-05-07 10:26:24 -0700874
875 const size_t bytes = size * nmemb;
876 if (mWriteMoovBoxToMemory) {
James Dong2747e0e2010-11-18 20:59:13 -0800877 // This happens only when we write the moov box at the end of
878 // recording, not for each output video/audio frame we receive.
James Dongb1262a82010-11-16 14:04:54 -0800879 off64_t moovBoxSize = 8 + mMoovBoxBufferOffset + bytes;
James Dong39a0b212010-06-23 00:18:40 -0700880 if (moovBoxSize > mEstimatedMoovBoxSize) {
James Dongb1262a82010-11-16 14:04:54 -0800881 for (List<off64_t>::iterator it = mBoxes.begin();
James Dongb5e74232010-05-07 10:26:24 -0700882 it != mBoxes.end(); ++it) {
883 (*it) += mOffset;
884 }
James Dong2747e0e2010-11-18 20:59:13 -0800885 lseek64(mFd, mOffset, SEEK_SET);
886 ::write(mFd, mMoovBoxBuffer, mMoovBoxBufferOffset);
887 ::write(mFd, ptr, size * nmemb);
James Dongb5e74232010-05-07 10:26:24 -0700888 mOffset += (bytes + mMoovBoxBufferOffset);
889 free(mMoovBoxBuffer);
890 mMoovBoxBuffer = NULL;
891 mMoovBoxBufferOffset = 0;
892 mWriteMoovBoxToMemory = false;
893 mStreamableFile = false;
894 } else {
895 memcpy(mMoovBoxBuffer + mMoovBoxBufferOffset, ptr, bytes);
896 mMoovBoxBufferOffset += bytes;
897 }
898 } else {
James Dong2747e0e2010-11-18 20:59:13 -0800899 ::write(mFd, ptr, size * nmemb);
James Dongb5e74232010-05-07 10:26:24 -0700900 mOffset += bytes;
901 }
902 return bytes;
903}
904
Andreas Hubere46b7be2009-07-14 16:56:47 -0700905void MPEG4Writer::beginBox(const char *fourcc) {
Andreas Huberb5ceb9e2009-08-26 14:48:20 -0700906 CHECK_EQ(strlen(fourcc), 4);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700907
James Dongb5e74232010-05-07 10:26:24 -0700908 mBoxes.push_back(mWriteMoovBoxToMemory?
909 mMoovBoxBufferOffset: mOffset);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700910
911 writeInt32(0);
912 writeFourcc(fourcc);
913}
914
915void MPEG4Writer::endBox() {
Andreas Huberb5ceb9e2009-08-26 14:48:20 -0700916 CHECK(!mBoxes.empty());
Andreas Hubere46b7be2009-07-14 16:56:47 -0700917
James Dongb1262a82010-11-16 14:04:54 -0800918 off64_t offset = *--mBoxes.end();
Andreas Hubere46b7be2009-07-14 16:56:47 -0700919 mBoxes.erase(--mBoxes.end());
920
James Dongb5e74232010-05-07 10:26:24 -0700921 if (mWriteMoovBoxToMemory) {
922 int32_t x = htonl(mMoovBoxBufferOffset - offset);
923 memcpy(mMoovBoxBuffer + offset, &x, 4);
924 } else {
James Dongb1262a82010-11-16 14:04:54 -0800925 lseek64(mFd, offset, SEEK_SET);
James Dongb5e74232010-05-07 10:26:24 -0700926 writeInt32(mOffset - offset);
927 mOffset -= 4;
James Dongb1262a82010-11-16 14:04:54 -0800928 lseek64(mFd, mOffset, SEEK_SET);
James Dongb5e74232010-05-07 10:26:24 -0700929 }
Andreas Hubere46b7be2009-07-14 16:56:47 -0700930}
931
932void MPEG4Writer::writeInt8(int8_t x) {
James Dong2747e0e2010-11-18 20:59:13 -0800933 write(&x, 1, 1);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700934}
935
936void MPEG4Writer::writeInt16(int16_t x) {
937 x = htons(x);
James Dong2747e0e2010-11-18 20:59:13 -0800938 write(&x, 1, 2);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700939}
940
941void MPEG4Writer::writeInt32(int32_t x) {
942 x = htonl(x);
James Dong2747e0e2010-11-18 20:59:13 -0800943 write(&x, 1, 4);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700944}
945
946void MPEG4Writer::writeInt64(int64_t x) {
947 x = hton64(x);
James Dong2747e0e2010-11-18 20:59:13 -0800948 write(&x, 1, 8);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700949}
950
951void MPEG4Writer::writeCString(const char *s) {
952 size_t n = strlen(s);
James Dong2747e0e2010-11-18 20:59:13 -0800953 write(s, 1, n + 1);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700954}
955
956void MPEG4Writer::writeFourcc(const char *s) {
Andreas Huberb5ceb9e2009-08-26 14:48:20 -0700957 CHECK_EQ(strlen(s), 4);
James Dong2747e0e2010-11-18 20:59:13 -0800958 write(s, 1, 4);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700959}
960
James Dong987ab482011-05-11 19:09:25 -0700961
962// Written in +/-DD.DDDD format
963void MPEG4Writer::writeLatitude(int degreex10000) {
964 bool isNegative = (degreex10000 < 0);
965 char sign = isNegative? '-': '+';
966
967 // Handle the whole part
968 char str[9];
969 int wholePart = degreex10000 / 10000;
970 if (wholePart == 0) {
971 snprintf(str, 5, "%c%.2d.", sign, wholePart);
972 } else {
973 snprintf(str, 5, "%+.2d.", wholePart);
974 }
975
976 // Handle the fractional part
977 int fractionalPart = degreex10000 - (wholePart * 10000);
978 if (fractionalPart < 0) {
979 fractionalPart = -fractionalPart;
980 }
981 snprintf(&str[4], 5, "%.4d", fractionalPart);
982
983 // Do not write the null terminator
984 write(str, 1, 8);
985}
986
987// Written in +/- DDD.DDDD format
988void MPEG4Writer::writeLongitude(int degreex10000) {
989 bool isNegative = (degreex10000 < 0);
990 char sign = isNegative? '-': '+';
991
992 // Handle the whole part
993 char str[10];
994 int wholePart = degreex10000 / 10000;
995 if (wholePart == 0) {
996 snprintf(str, 6, "%c%.3d.", sign, wholePart);
997 } else {
998 snprintf(str, 6, "%+.3d.", wholePart);
999 }
1000
1001 // Handle the fractional part
1002 int fractionalPart = degreex10000 - (wholePart * 10000);
1003 if (fractionalPart < 0) {
1004 fractionalPart = -fractionalPart;
1005 }
1006 snprintf(&str[5], 5, "%.4d", fractionalPart);
1007
1008 // Do not write the null terminator
1009 write(str, 1, 9);
1010}
1011
1012/*
1013 * Geodata is stored according to ISO-6709 standard.
1014 * latitudex10000 is latitude in degrees times 10000, and
1015 * longitudex10000 is longitude in degrees times 10000.
1016 * The range for the latitude is in [-90, +90], and
1017 * The range for the longitude is in [-180, +180]
1018 */
1019status_t MPEG4Writer::setGeoData(int latitudex10000, int longitudex10000) {
1020 // Is latitude or longitude out of range?
1021 if (latitudex10000 < -900000 || latitudex10000 > 900000 ||
1022 longitudex10000 < -1800000 || longitudex10000 > 1800000) {
1023 return BAD_VALUE;
1024 }
1025
1026 mLatitudex10000 = latitudex10000;
1027 mLongitudex10000 = longitudex10000;
1028 mAreGeoTagsAvailable = true;
1029 return OK;
1030}
1031
Andreas Hubere46b7be2009-07-14 16:56:47 -07001032void MPEG4Writer::write(const void *data, size_t size) {
James Dong2747e0e2010-11-18 20:59:13 -08001033 write(data, 1, size);
Andreas Hubere46b7be2009-07-14 16:56:47 -07001034}
1035
James Dong22b37fa2010-10-19 21:28:47 -07001036bool MPEG4Writer::isFileStreamable() const {
1037 return mStreamableFile;
1038}
1039
James Dong18244862010-05-11 14:57:02 -07001040bool MPEG4Writer::exceedsFileSizeLimit() {
1041 // No limit
1042 if (mMaxFileSizeLimitBytes == 0) {
1043 return false;
1044 }
1045
James Dong17299ab2010-05-14 15:45:22 -07001046 int64_t nTotalBytesEstimate = static_cast<int64_t>(mEstimatedMoovBoxSize);
James Dong18244862010-05-11 14:57:02 -07001047 for (List<Track *>::iterator it = mTracks.begin();
1048 it != mTracks.end(); ++it) {
1049 nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
1050 }
James Dongcb7e65c2010-09-02 11:19:11 -07001051
James Dong5cdcf162010-11-30 18:18:08 -08001052 // Be conservative in the estimate: do not exceed 95% of
1053 // the target file limit. For small target file size limit, though,
1054 // this will not help.
1055 return (nTotalBytesEstimate >= (95 * mMaxFileSizeLimitBytes) / 100);
James Dong18244862010-05-11 14:57:02 -07001056}
1057
1058bool MPEG4Writer::exceedsFileDurationLimit() {
1059 // No limit
1060 if (mMaxFileDurationLimitUs == 0) {
1061 return false;
1062 }
1063
1064 for (List<Track *>::iterator it = mTracks.begin();
1065 it != mTracks.end(); ++it) {
1066 if ((*it)->getDurationUs() >= mMaxFileDurationLimitUs) {
1067 return true;
1068 }
1069 }
1070 return false;
1071}
1072
Andreas Huber033e6c32009-09-09 16:36:12 -07001073bool MPEG4Writer::reachedEOS() {
1074 bool allDone = true;
1075 for (List<Track *>::iterator it = mTracks.begin();
1076 it != mTracks.end(); ++it) {
1077 if (!(*it)->reachedEOS()) {
1078 allDone = false;
1079 break;
1080 }
1081 }
1082
1083 return allDone;
1084}
1085
James Dong36e573b2010-06-19 09:04:18 -07001086void MPEG4Writer::setStartTimestampUs(int64_t timeUs) {
1087 LOGI("setStartTimestampUs: %lld", timeUs);
1088 CHECK(timeUs >= 0);
James Dong9db798d2010-05-13 11:47:36 -07001089 Mutex::Autolock autoLock(mLock);
James Dong438e4f62010-06-23 16:51:39 -07001090 if (mStartTimestampUs < 0 || mStartTimestampUs > timeUs) {
James Dong36e573b2010-06-19 09:04:18 -07001091 mStartTimestampUs = timeUs;
1092 LOGI("Earliest track starting time: %lld", mStartTimestampUs);
James Dong9db798d2010-05-13 11:47:36 -07001093 }
James Dong9db798d2010-05-13 11:47:36 -07001094}
1095
James Dong36e573b2010-06-19 09:04:18 -07001096int64_t MPEG4Writer::getStartTimestampUs() {
James Dong9db798d2010-05-13 11:47:36 -07001097 Mutex::Autolock autoLock(mLock);
1098 return mStartTimestampUs;
1099}
1100
James Dongb54a91782010-06-22 11:27:37 -07001101size_t MPEG4Writer::numTracks() {
1102 Mutex::Autolock autolock(mLock);
1103 return mTracks.size();
1104}
1105
Andreas Hubere46b7be2009-07-14 16:56:47 -07001106////////////////////////////////////////////////////////////////////////////////
1107
1108MPEG4Writer::Track::Track(
James Dong7fc8b4f2011-03-18 11:25:41 -07001109 MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId)
Andreas Hubere46b7be2009-07-14 16:56:47 -07001110 : mOwner(owner),
Andreas Huber033e6c32009-09-09 16:36:12 -07001111 mMeta(source->getFormat()),
Andreas Hubere46b7be2009-07-14 16:56:47 -07001112 mSource(source),
1113 mDone(false),
James Dong08c74732010-06-10 12:28:15 -07001114 mPaused(false),
1115 mResumed(false),
James Dongd7ef5b62011-01-25 12:37:43 -08001116 mStarted(false),
James Dong7fc8b4f2011-03-18 11:25:41 -07001117 mTrackId(trackId),
James Dong91b22a92010-08-05 10:46:13 -07001118 mTrackDurationUs(0),
James Dong17299ab2010-05-14 15:45:22 -07001119 mEstimatedTrackSizeBytes(0),
James Dong2a4767e2010-04-20 11:50:11 -07001120 mSamplesHaveSameSize(true),
Andreas Hubere46b7be2009-07-14 16:56:47 -07001121 mCodecSpecificData(NULL),
Andreas Huber033e6c32009-09-09 16:36:12 -07001122 mCodecSpecificDataSize(0),
Andreas Huberf4e5baa2010-04-09 14:25:46 -07001123 mGotAllCodecSpecificData(false),
James Dongb9d7e012010-11-09 11:15:47 -08001124 mReachedEOS(false),
1125 mRotation(0) {
Andreas Huber45bac572010-07-01 08:19:52 -07001126 getCodecSpecificDataFromInputFormatIfPossible();
James Dong52d13f02010-07-02 11:39:06 -07001127
James Dongda8073c2010-07-30 17:41:22 -07001128 const char *mime;
1129 mMeta->findCString(kKeyMIMEType, &mime);
1130 mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
1131 mIsAudio = !strncasecmp(mime, "audio/", 6);
1132 mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
1133 !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
1134
James Dongeff30e32010-08-13 14:16:26 -07001135 setTimeScale();
1136}
1137
James Dongcb7e65c2010-09-02 11:19:11 -07001138void MPEG4Writer::Track::updateTrackSizeEstimate() {
1139
1140 int64_t stcoBoxSizeBytes = mOwner->use32BitFileOffset()
1141 ? mNumStcoTableEntries * 4
1142 : mNumStcoTableEntries * 8;
1143
1144 int64_t stszBoxSizeBytes = mSamplesHaveSameSize? 4: (mNumSamples * 4);
1145
James Dong22b37fa2010-10-19 21:28:47 -07001146 mEstimatedTrackSizeBytes = mMdatSizeBytes; // media data size
1147 if (!mOwner->isFileStreamable()) {
1148 // Reserved free space is not large enough to hold
1149 // all meta data and thus wasted.
1150 mEstimatedTrackSizeBytes += mNumStscTableEntries * 12 + // stsc box size
1151 mNumStssTableEntries * 4 + // stss box size
1152 mNumSttsTableEntries * 8 + // stts box size
James Dong4108b1ed2011-06-07 19:45:54 -07001153 mNumCttsTableEntries * 8 + // ctts box size
James Dong22b37fa2010-10-19 21:28:47 -07001154 stcoBoxSizeBytes + // stco box size
1155 stszBoxSizeBytes; // stsz box size
1156 }
James Dongcb7e65c2010-09-02 11:19:11 -07001157}
1158
1159void MPEG4Writer::Track::addOneStscTableEntry(
1160 size_t chunkId, size_t sampleId) {
1161
1162 StscTableEntry stscEntry(chunkId, sampleId, 1);
1163 mStscTableEntries.push_back(stscEntry);
1164 ++mNumStscTableEntries;
1165}
1166
1167void MPEG4Writer::Track::addOneStssTableEntry(size_t sampleId) {
1168 mStssTableEntries.push_back(sampleId);
1169 ++mNumStssTableEntries;
1170}
1171
1172void MPEG4Writer::Track::addOneSttsTableEntry(
James Donga7ea9f92011-06-06 19:00:40 -07001173 size_t sampleCount, int32_t duration) {
James Dongcb7e65c2010-09-02 11:19:11 -07001174
James Dong2c8e8502011-08-09 11:14:57 -07001175 if (duration == 0) {
Pannag Sanketi5cfcfb22011-08-26 19:17:20 -07001176 LOGW("0-duration samples found: %d", sampleCount);
James Dong2c8e8502011-08-09 11:14:57 -07001177 }
James Donga7ea9f92011-06-06 19:00:40 -07001178 SttsTableEntry sttsEntry(sampleCount, duration);
James Dongcb7e65c2010-09-02 11:19:11 -07001179 mSttsTableEntries.push_back(sttsEntry);
1180 ++mNumSttsTableEntries;
1181}
1182
James Dong4108b1ed2011-06-07 19:45:54 -07001183void MPEG4Writer::Track::addOneCttsTableEntry(
1184 size_t sampleCount, int32_t duration) {
1185
1186 if (mIsAudio) {
1187 return;
1188 }
1189 if (duration < 0 && !mHasNegativeCttsDeltaDuration) {
1190 mHasNegativeCttsDeltaDuration = true;
1191 }
1192 CttsTableEntry cttsEntry(sampleCount, duration);
1193 mCttsTableEntries.push_back(cttsEntry);
1194 ++mNumCttsTableEntries;
1195}
1196
James Dongb1262a82010-11-16 14:04:54 -08001197void MPEG4Writer::Track::addChunkOffset(off64_t offset) {
James Dongcb7e65c2010-09-02 11:19:11 -07001198 ++mNumStcoTableEntries;
1199 mChunkOffsets.push_back(offset);
1200}
1201
James Dongeff30e32010-08-13 14:16:26 -07001202void MPEG4Writer::Track::setTimeScale() {
Steve Block71f2cf12011-10-20 11:56:00 +01001203 ALOGV("setTimeScale");
James Dongeff30e32010-08-13 14:16:26 -07001204 // Default time scale
1205 mTimeScale = 90000;
1206
1207 if (mIsAudio) {
1208 // Use the sampling rate as the default time scale for audio track.
1209 int32_t sampleRate;
1210 bool success = mMeta->findInt32(kKeySampleRate, &sampleRate);
1211 CHECK(success);
1212 mTimeScale = sampleRate;
1213 }
1214
1215 // If someone would like to overwrite the timescale, use user-supplied value.
1216 int32_t timeScale;
1217 if (mMeta->findInt32(kKeyTimeScale, &timeScale)) {
1218 mTimeScale = timeScale;
1219 }
1220
James Dong52d13f02010-07-02 11:39:06 -07001221 CHECK(mTimeScale > 0);
Andreas Huber45bac572010-07-01 08:19:52 -07001222}
1223
1224void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() {
1225 const char *mime;
1226 CHECK(mMeta->findCString(kKeyMIMEType, &mime));
1227
1228 if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
1229 uint32_t type;
1230 const void *data;
1231 size_t size;
1232 if (mMeta->findData(kKeyAVCC, &type, &data, &size)) {
1233 mCodecSpecificData = malloc(size);
1234 mCodecSpecificDataSize = size;
1235 memcpy(mCodecSpecificData, data, size);
1236 mGotAllCodecSpecificData = true;
1237 }
1238 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
1239 || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
1240 uint32_t type;
1241 const void *data;
1242 size_t size;
1243 if (mMeta->findData(kKeyESDS, &type, &data, &size)) {
1244 ESDS esds(data, size);
1245 if (esds.getCodecSpecificInfo(&data, &size) == OK) {
1246 mCodecSpecificData = malloc(size);
1247 mCodecSpecificDataSize = size;
1248 memcpy(mCodecSpecificData, data, size);
1249 mGotAllCodecSpecificData = true;
1250 }
1251 }
1252 }
Andreas Hubere46b7be2009-07-14 16:56:47 -07001253}
1254
1255MPEG4Writer::Track::~Track() {
1256 stop();
1257
1258 if (mCodecSpecificData != NULL) {
1259 free(mCodecSpecificData);
1260 mCodecSpecificData = NULL;
1261 }
1262}
1263
James Dong09936ed2010-06-24 19:04:27 -07001264void MPEG4Writer::Track::initTrackingProgressStatus(MetaData *params) {
Steve Block71f2cf12011-10-20 11:56:00 +01001265 ALOGV("initTrackingProgressStatus");
James Dong09936ed2010-06-24 19:04:27 -07001266 mPreviousTrackTimeUs = -1;
1267 mTrackingProgressStatus = false;
1268 mTrackEveryTimeDurationUs = 0;
James Dong09936ed2010-06-24 19:04:27 -07001269 {
1270 int64_t timeUs;
1271 if (params && params->findInt64(kKeyTrackTimeStatus, &timeUs)) {
Steve Block71f2cf12011-10-20 11:56:00 +01001272 ALOGV("Receive request to track progress status for every %lld us", timeUs);
James Dong09936ed2010-06-24 19:04:27 -07001273 mTrackEveryTimeDurationUs = timeUs;
1274 mTrackingProgressStatus = true;
1275 }
1276 }
James Dong09936ed2010-06-24 19:04:27 -07001277}
1278
James Dongda8073c2010-07-30 17:41:22 -07001279// static
1280void *MPEG4Writer::ThreadWrapper(void *me) {
Steve Block71f2cf12011-10-20 11:56:00 +01001281 ALOGV("ThreadWrapper: %p", me);
James Dongda8073c2010-07-30 17:41:22 -07001282 MPEG4Writer *writer = static_cast<MPEG4Writer *>(me);
1283 writer->threadFunc();
1284 return NULL;
1285}
1286
1287void MPEG4Writer::bufferChunk(const Chunk& chunk) {
Steve Block71f2cf12011-10-20 11:56:00 +01001288 ALOGV("bufferChunk: %p", chunk.mTrack);
James Dongda8073c2010-07-30 17:41:22 -07001289 Mutex::Autolock autolock(mLock);
1290 CHECK_EQ(mDone, false);
1291
1292 for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
1293 it != mChunkInfos.end(); ++it) {
1294
1295 if (chunk.mTrack == it->mTrack) { // Found owner
1296 it->mChunks.push_back(chunk);
1297 mChunkReadyCondition.signal();
1298 return;
1299 }
1300 }
1301
1302 CHECK("Received a chunk for a unknown track" == 0);
1303}
1304
James Dongf6a2bff2011-02-09 14:00:55 -08001305void MPEG4Writer::writeChunkToFile(Chunk* chunk) {
Steve Block71f2cf12011-10-20 11:56:00 +01001306 ALOGV("writeChunkToFile: %lld from %s track",
Pannag Sanketi5cfcfb22011-08-26 19:17:20 -07001307 chunk->mTimeStampUs, chunk->mTrack->isAudio()? "audio": "video");
James Dongda8073c2010-07-30 17:41:22 -07001308
James Dongf6a2bff2011-02-09 14:00:55 -08001309 int32_t isFirstSample = true;
1310 while (!chunk->mSamples.empty()) {
1311 List<MediaBuffer *>::iterator it = chunk->mSamples.begin();
James Dongda8073c2010-07-30 17:41:22 -07001312
James Dongf6a2bff2011-02-09 14:00:55 -08001313 off64_t offset = chunk->mTrack->isAvc()
1314 ? addLengthPrefixedSample_l(*it)
1315 : addSample_l(*it);
1316
1317 if (isFirstSample) {
1318 chunk->mTrack->addChunkOffset(offset);
1319 isFirstSample = false;
James Dongda8073c2010-07-30 17:41:22 -07001320 }
James Dongda8073c2010-07-30 17:41:22 -07001321
James Dongda8073c2010-07-30 17:41:22 -07001322 (*it)->release();
1323 (*it) = NULL;
James Dongf6a2bff2011-02-09 14:00:55 -08001324 chunk->mSamples.erase(it);
James Dongda8073c2010-07-30 17:41:22 -07001325 }
James Dongf6a2bff2011-02-09 14:00:55 -08001326 chunk->mSamples.clear();
James Dongda8073c2010-07-30 17:41:22 -07001327}
1328
James Dongf6a2bff2011-02-09 14:00:55 -08001329void MPEG4Writer::writeAllChunks() {
Steve Block71f2cf12011-10-20 11:56:00 +01001330 ALOGV("writeAllChunks");
James Dongda8073c2010-07-30 17:41:22 -07001331 size_t outstandingChunks = 0;
James Dong3aea0372011-05-06 12:19:04 -07001332 Chunk chunk;
1333 while (findChunkToWrite(&chunk)) {
James Donga81fcad2011-06-09 15:31:39 -07001334 writeChunkToFile(&chunk);
James Dong3aea0372011-05-06 12:19:04 -07001335 ++outstandingChunks;
James Dongda8073c2010-07-30 17:41:22 -07001336 }
James Dong3aea0372011-05-06 12:19:04 -07001337
1338 sendSessionSummary();
1339
James Dongda8073c2010-07-30 17:41:22 -07001340 mChunkInfos.clear();
1341 LOGD("%d chunks are written in the last batch", outstandingChunks);
1342}
1343
James Dongf6a2bff2011-02-09 14:00:55 -08001344bool MPEG4Writer::findChunkToWrite(Chunk *chunk) {
Steve Block71f2cf12011-10-20 11:56:00 +01001345 ALOGV("findChunkToWrite");
James Dongda8073c2010-07-30 17:41:22 -07001346
James Dongda8073c2010-07-30 17:41:22 -07001347 int64_t minTimestampUs = 0x7FFFFFFFFFFFFFFFLL;
1348 Track *track = NULL;
1349 for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
1350 it != mChunkInfos.end(); ++it) {
1351 if (!it->mChunks.empty()) {
1352 List<Chunk>::iterator chunkIt = it->mChunks.begin();
1353 if (chunkIt->mTimeStampUs < minTimestampUs) {
1354 minTimestampUs = chunkIt->mTimeStampUs;
1355 track = it->mTrack;
1356 }
1357 }
1358 }
1359
1360 if (track == NULL) {
Steve Block71f2cf12011-10-20 11:56:00 +01001361 ALOGV("Nothing to be written after all");
James Dongf6a2bff2011-02-09 14:00:55 -08001362 return false;
James Dongda8073c2010-07-30 17:41:22 -07001363 }
1364
1365 if (mIsFirstChunk) {
1366 mIsFirstChunk = false;
1367 }
James Dongf6a2bff2011-02-09 14:00:55 -08001368
James Dongda8073c2010-07-30 17:41:22 -07001369 for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
1370 it != mChunkInfos.end(); ++it) {
1371 if (it->mTrack == track) {
James Dongf6a2bff2011-02-09 14:00:55 -08001372 *chunk = *(it->mChunks.begin());
1373 it->mChunks.erase(it->mChunks.begin());
1374 CHECK_EQ(chunk->mTrack, track);
James Dong3aea0372011-05-06 12:19:04 -07001375
1376 int64_t interChunkTimeUs =
1377 chunk->mTimeStampUs - it->mPrevChunkTimestampUs;
1378 if (interChunkTimeUs > it->mPrevChunkTimestampUs) {
1379 it->mMaxInterChunkDurUs = interChunkTimeUs;
1380 }
1381
James Dongf6a2bff2011-02-09 14:00:55 -08001382 return true;
James Dongda8073c2010-07-30 17:41:22 -07001383 }
1384 }
James Dongf6a2bff2011-02-09 14:00:55 -08001385
1386 return false;
James Dongda8073c2010-07-30 17:41:22 -07001387}
1388
1389void MPEG4Writer::threadFunc() {
Steve Block71f2cf12011-10-20 11:56:00 +01001390 ALOGV("threadFunc");
James Dongda8073c2010-07-30 17:41:22 -07001391
James Dongc67acb22010-10-07 20:20:59 -07001392 prctl(PR_SET_NAME, (unsigned long)"MPEG4Writer", 0, 0, 0);
James Dongf6a2bff2011-02-09 14:00:55 -08001393
1394 Mutex::Autolock autoLock(mLock);
James Dongda8073c2010-07-30 17:41:22 -07001395 while (!mDone) {
James Dongf6a2bff2011-02-09 14:00:55 -08001396 Chunk chunk;
1397 bool chunkFound = false;
1398
1399 while (!mDone && !(chunkFound = findChunkToWrite(&chunk))) {
James Dongda8073c2010-07-30 17:41:22 -07001400 mChunkReadyCondition.wait(mLock);
James Dongf6a2bff2011-02-09 14:00:55 -08001401 }
1402
1403 // Actual write without holding the lock in order to
1404 // reduce the blocking time for media track threads.
1405 if (chunkFound) {
1406 mLock.unlock();
1407 writeChunkToFile(&chunk);
1408 mLock.lock();
James Dongda8073c2010-07-30 17:41:22 -07001409 }
1410 }
1411
James Dongf6a2bff2011-02-09 14:00:55 -08001412 writeAllChunks();
James Dongda8073c2010-07-30 17:41:22 -07001413}
1414
1415status_t MPEG4Writer::startWriterThread() {
Steve Block71f2cf12011-10-20 11:56:00 +01001416 ALOGV("startWriterThread");
James Dongda8073c2010-07-30 17:41:22 -07001417
1418 mDone = false;
1419 mIsFirstChunk = true;
James Dongb7208192010-08-02 19:13:40 -07001420 mDriftTimeUs = 0;
James Dongda8073c2010-07-30 17:41:22 -07001421 for (List<Track *>::iterator it = mTracks.begin();
1422 it != mTracks.end(); ++it) {
1423 ChunkInfo info;
1424 info.mTrack = *it;
James Dong3aea0372011-05-06 12:19:04 -07001425 info.mPrevChunkTimestampUs = 0;
1426 info.mMaxInterChunkDurUs = 0;
James Dongda8073c2010-07-30 17:41:22 -07001427 mChunkInfos.push_back(info);
1428 }
1429
1430 pthread_attr_t attr;
1431 pthread_attr_init(&attr);
1432 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1433 pthread_create(&mThread, &attr, ThreadWrapper, this);
1434 pthread_attr_destroy(&attr);
James Dong0e27fce2011-07-08 16:51:16 -07001435 mWriterThreadStarted = true;
James Dongda8073c2010-07-30 17:41:22 -07001436 return OK;
1437}
1438
James Dongcb7e65c2010-09-02 11:19:11 -07001439
James Dong09936ed2010-06-24 19:04:27 -07001440status_t MPEG4Writer::Track::start(MetaData *params) {
James Dong08c74732010-06-10 12:28:15 -07001441 if (!mDone && mPaused) {
1442 mPaused = false;
1443 mResumed = true;
1444 return OK;
1445 }
Andreas Huber033e6c32009-09-09 16:36:12 -07001446
James Dong09936ed2010-06-24 19:04:27 -07001447 int64_t startTimeUs;
Andreas Huber45bac572010-07-01 08:19:52 -07001448 if (params == NULL || !params->findInt64(kKeyTime, &startTimeUs)) {
1449 startTimeUs = 0;
1450 }
James Dong3aea0372011-05-06 12:19:04 -07001451 mStartTimeRealUs = startTimeUs;
Andreas Huber45bac572010-07-01 08:19:52 -07001452
James Dongb9d7e012010-11-09 11:15:47 -08001453 int32_t rotationDegrees;
1454 if (!mIsAudio && params && params->findInt32(kKeyRotation, &rotationDegrees)) {
1455 mRotation = rotationDegrees;
1456 }
1457
James Dong89a01042010-10-21 17:58:14 -07001458 mIsRealTimeRecording = true;
James Dongb7208192010-08-02 19:13:40 -07001459 {
1460 int32_t isNotRealTime;
1461 if (params && params->findInt32(kKeyNotRealTime, &isNotRealTime)) {
1462 mIsRealTimeRecording = (isNotRealTime == 0);
1463 }
1464 }
1465
James Dong09936ed2010-06-24 19:04:27 -07001466 initTrackingProgressStatus(params);
1467
James Dong36e573b2010-06-19 09:04:18 -07001468 sp<MetaData> meta = new MetaData;
James Dongcbeebb12011-02-16 12:28:26 -08001469 if (mIsRealTimeRecording && mOwner->numTracks() > 1) {
1470 /*
1471 * This extra delay of accepting incoming audio/video signals
1472 * helps to align a/v start time at the beginning of a recording
1473 * session, and it also helps eliminate the "recording" sound for
1474 * camcorder applications.
1475 *
James Dong13a33162011-05-09 16:56:25 -07001476 * If client does not set the start time offset, we fall back to
1477 * use the default initial delay value.
James Dongcbeebb12011-02-16 12:28:26 -08001478 */
James Dong13a33162011-05-09 16:56:25 -07001479 int64_t startTimeOffsetUs = mOwner->getStartTimeOffsetMs() * 1000LL;
1480 if (startTimeOffsetUs < 0) { // Start time offset was not set
1481 startTimeOffsetUs = kInitialDelayTimeUs;
1482 }
1483 startTimeUs += startTimeOffsetUs;
1484 LOGI("Start time offset: %lld us", startTimeOffsetUs);
James Dongcbeebb12011-02-16 12:28:26 -08001485 }
1486
James Dong36e573b2010-06-19 09:04:18 -07001487 meta->setInt64(kKeyTime, startTimeUs);
James Dongcbeebb12011-02-16 12:28:26 -08001488
James Dong36e573b2010-06-19 09:04:18 -07001489 status_t err = mSource->start(meta.get());
Andreas Huber033e6c32009-09-09 16:36:12 -07001490 if (err != OK) {
1491 mDone = mReachedEOS = true;
1492 return err;
1493 }
Andreas Hubere46b7be2009-07-14 16:56:47 -07001494
1495 pthread_attr_t attr;
1496 pthread_attr_init(&attr);
1497 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1498
1499 mDone = false;
James Dongd7ef5b62011-01-25 12:37:43 -08001500 mStarted = true;
James Dong91b22a92010-08-05 10:46:13 -07001501 mTrackDurationUs = 0;
Andreas Huber033e6c32009-09-09 16:36:12 -07001502 mReachedEOS = false;
James Dong17299ab2010-05-14 15:45:22 -07001503 mEstimatedTrackSizeBytes = 0;
James Dongcb7e65c2010-09-02 11:19:11 -07001504 mNumStcoTableEntries = 0;
1505 mNumStssTableEntries = 0;
1506 mNumStscTableEntries = 0;
1507 mNumSttsTableEntries = 0;
James Dong4108b1ed2011-06-07 19:45:54 -07001508 mNumCttsTableEntries = 0;
James Dongcb7e65c2010-09-02 11:19:11 -07001509 mMdatSizeBytes = 0;
James Donge67bb482011-06-03 16:56:03 -07001510
James Donged742302011-05-06 11:27:59 -07001511 mMaxChunkDurationUs = 0;
James Dong4108b1ed2011-06-07 19:45:54 -07001512 mHasNegativeCttsDeltaDuration = false;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001513
Andreas Huber033e6c32009-09-09 16:36:12 -07001514 pthread_create(&mThread, &attr, ThreadWrapper, this);
Andreas Hubere46b7be2009-07-14 16:56:47 -07001515 pthread_attr_destroy(&attr);
Andreas Huber033e6c32009-09-09 16:36:12 -07001516
1517 return OK;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001518}
1519
James Dongd0366622010-08-18 19:10:39 -07001520status_t MPEG4Writer::Track::pause() {
James Dong08c74732010-06-10 12:28:15 -07001521 mPaused = true;
James Dongd0366622010-08-18 19:10:39 -07001522 return OK;
James Dong08c74732010-06-10 12:28:15 -07001523}
1524
James Dongd0366622010-08-18 19:10:39 -07001525status_t MPEG4Writer::Track::stop() {
James Dong42338ff2010-10-29 16:01:06 -07001526 LOGD("Stopping %s track", mIsAudio? "Audio": "Video");
James Dongd7ef5b62011-01-25 12:37:43 -08001527 if (!mStarted) {
1528 LOGE("Stop() called but track is not started");
1529 return ERROR_END_OF_STREAM;
1530 }
1531
Andreas Hubere46b7be2009-07-14 16:56:47 -07001532 if (mDone) {
James Dongd0366622010-08-18 19:10:39 -07001533 return OK;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001534 }
Andreas Hubere46b7be2009-07-14 16:56:47 -07001535 mDone = true;
1536
1537 void *dummy;
1538 pthread_join(mThread, &dummy);
1539
James Dongd0366622010-08-18 19:10:39 -07001540 status_t err = (status_t) dummy;
1541
James Dong42338ff2010-10-29 16:01:06 -07001542 LOGD("Stopping %s track source", mIsAudio? "Audio": "Video");
James Dongd0366622010-08-18 19:10:39 -07001543 {
1544 status_t status = mSource->stop();
1545 if (err == OK && status != OK && status != ERROR_END_OF_STREAM) {
1546 err = status;
1547 }
1548 }
1549
James Dong42338ff2010-10-29 16:01:06 -07001550 LOGD("%s track stopped", mIsAudio? "Audio": "Video");
James Dongd0366622010-08-18 19:10:39 -07001551 return err;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001552}
1553
Andreas Huber033e6c32009-09-09 16:36:12 -07001554bool MPEG4Writer::Track::reachedEOS() {
1555 return mReachedEOS;
1556}
1557
Andreas Hubere46b7be2009-07-14 16:56:47 -07001558// static
1559void *MPEG4Writer::Track::ThreadWrapper(void *me) {
1560 Track *track = static_cast<Track *>(me);
1561
James Dongd0366622010-08-18 19:10:39 -07001562 status_t err = track->threadEntry();
1563 return (void *) err;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001564}
1565
James Dong481e05e2010-08-06 00:29:03 -07001566static void getNalUnitType(uint8_t byte, uint8_t* type) {
Steve Block71f2cf12011-10-20 11:56:00 +01001567 ALOGV("getNalUnitType: %d", byte);
James Dong481e05e2010-08-06 00:29:03 -07001568
1569 // nal_unit_type: 5-bit unsigned integer
1570 *type = (byte & 0x1F);
1571}
1572
1573static const uint8_t *findNextStartCode(
1574 const uint8_t *data, size_t length) {
1575
Steve Block71f2cf12011-10-20 11:56:00 +01001576 ALOGV("findNextStartCode: %p %d", data, length);
James Dong481e05e2010-08-06 00:29:03 -07001577
1578 size_t bytesLeft = length;
1579 while (bytesLeft > 4 &&
1580 memcmp("\x00\x00\x00\x01", &data[length - bytesLeft], 4)) {
1581 --bytesLeft;
1582 }
1583 if (bytesLeft <= 4) {
1584 bytesLeft = 0; // Last parameter set
1585 }
1586 return &data[length - bytesLeft];
1587}
1588
1589const uint8_t *MPEG4Writer::Track::parseParamSet(
1590 const uint8_t *data, size_t length, int type, size_t *paramSetLen) {
1591
Steve Block71f2cf12011-10-20 11:56:00 +01001592 ALOGV("parseParamSet");
James Dong481e05e2010-08-06 00:29:03 -07001593 CHECK(type == kNalUnitTypeSeqParamSet ||
1594 type == kNalUnitTypePicParamSet);
1595
1596 const uint8_t *nextStartCode = findNextStartCode(data, length);
1597 *paramSetLen = nextStartCode - data;
1598 if (*paramSetLen == 0) {
1599 LOGE("Param set is malformed, since its length is 0");
1600 return NULL;
1601 }
1602
1603 AVCParamSet paramSet(*paramSetLen, data);
1604 if (type == kNalUnitTypeSeqParamSet) {
1605 if (*paramSetLen < 4) {
1606 LOGE("Seq parameter set malformed");
1607 return NULL;
1608 }
1609 if (mSeqParamSets.empty()) {
1610 mProfileIdc = data[1];
1611 mProfileCompatible = data[2];
1612 mLevelIdc = data[3];
1613 } else {
1614 if (mProfileIdc != data[1] ||
1615 mProfileCompatible != data[2] ||
1616 mLevelIdc != data[3]) {
1617 LOGE("Inconsistent profile/level found in seq parameter sets");
1618 return NULL;
1619 }
1620 }
1621 mSeqParamSets.push_back(paramSet);
1622 } else {
1623 mPicParamSets.push_back(paramSet);
1624 }
1625 return nextStartCode;
1626}
1627
1628status_t MPEG4Writer::Track::copyAVCCodecSpecificData(
1629 const uint8_t *data, size_t size) {
Steve Block71f2cf12011-10-20 11:56:00 +01001630 ALOGV("copyAVCCodecSpecificData");
James Dong481e05e2010-08-06 00:29:03 -07001631
1632 // 2 bytes for each of the parameter set length field
1633 // plus the 7 bytes for the header
1634 if (size < 4 + 7) {
1635 LOGE("Codec specific data length too short: %d", size);
1636 return ERROR_MALFORMED;
1637 }
1638
1639 mCodecSpecificDataSize = size;
1640 mCodecSpecificData = malloc(size);
1641 memcpy(mCodecSpecificData, data, size);
1642 return OK;
1643}
1644
1645status_t MPEG4Writer::Track::parseAVCCodecSpecificData(
1646 const uint8_t *data, size_t size) {
1647
Steve Block71f2cf12011-10-20 11:56:00 +01001648 ALOGV("parseAVCCodecSpecificData");
James Dong481e05e2010-08-06 00:29:03 -07001649 // Data starts with a start code.
1650 // SPS and PPS are separated with start codes.
1651 // Also, SPS must come before PPS
1652 uint8_t type = kNalUnitTypeSeqParamSet;
1653 bool gotSps = false;
1654 bool gotPps = false;
1655 const uint8_t *tmp = data;
1656 const uint8_t *nextStartCode = data;
1657 size_t bytesLeft = size;
1658 size_t paramSetLen = 0;
1659 mCodecSpecificDataSize = 0;
1660 while (bytesLeft > 4 && !memcmp("\x00\x00\x00\x01", tmp, 4)) {
1661 getNalUnitType(*(tmp + 4), &type);
1662 if (type == kNalUnitTypeSeqParamSet) {
1663 if (gotPps) {
1664 LOGE("SPS must come before PPS");
1665 return ERROR_MALFORMED;
1666 }
1667 if (!gotSps) {
1668 gotSps = true;
1669 }
1670 nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, &paramSetLen);
1671 } else if (type == kNalUnitTypePicParamSet) {
1672 if (!gotSps) {
1673 LOGE("SPS must come before PPS");
1674 return ERROR_MALFORMED;
1675 }
1676 if (!gotPps) {
1677 gotPps = true;
1678 }
1679 nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, &paramSetLen);
1680 } else {
1681 LOGE("Only SPS and PPS Nal units are expected");
1682 return ERROR_MALFORMED;
1683 }
1684
1685 if (nextStartCode == NULL) {
1686 return ERROR_MALFORMED;
1687 }
1688
1689 // Move on to find the next parameter set
1690 bytesLeft -= nextStartCode - tmp;
1691 tmp = nextStartCode;
1692 mCodecSpecificDataSize += (2 + paramSetLen);
1693 }
1694
1695 {
1696 // Check on the number of seq parameter sets
1697 size_t nSeqParamSets = mSeqParamSets.size();
1698 if (nSeqParamSets == 0) {
1699 LOGE("Cound not find sequence parameter set");
1700 return ERROR_MALFORMED;
1701 }
1702
1703 if (nSeqParamSets > 0x1F) {
1704 LOGE("Too many seq parameter sets (%d) found", nSeqParamSets);
1705 return ERROR_MALFORMED;
1706 }
1707 }
1708
1709 {
1710 // Check on the number of pic parameter sets
1711 size_t nPicParamSets = mPicParamSets.size();
1712 if (nPicParamSets == 0) {
1713 LOGE("Cound not find picture parameter set");
1714 return ERROR_MALFORMED;
1715 }
1716 if (nPicParamSets > 0xFF) {
1717 LOGE("Too many pic parameter sets (%d) found", nPicParamSets);
1718 return ERROR_MALFORMED;
1719 }
1720 }
Dandawate Sakete641dc52011-07-11 19:12:57 -07001721// FIXME:
1722// Add chromat_format_idc, bit depth values, etc for AVC/h264 high profile and above
1723// and remove #if 0
1724#if 0
James Dong481e05e2010-08-06 00:29:03 -07001725 {
1726 // Check on the profiles
1727 // These profiles requires additional parameter set extensions
1728 if (mProfileIdc == 100 || mProfileIdc == 110 ||
1729 mProfileIdc == 122 || mProfileIdc == 144) {
1730 LOGE("Sorry, no support for profile_idc: %d!", mProfileIdc);
1731 return BAD_VALUE;
1732 }
1733 }
Dandawate Sakete641dc52011-07-11 19:12:57 -07001734#endif
James Dong481e05e2010-08-06 00:29:03 -07001735 return OK;
1736}
Andreas Huberf4e5baa2010-04-09 14:25:46 -07001737
Andreas Huber71c27d92010-03-19 11:43:15 -07001738status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
1739 const uint8_t *data, size_t size) {
Andreas Huberf4e5baa2010-04-09 14:25:46 -07001740
Andreas Huber71c27d92010-03-19 11:43:15 -07001741 if (mCodecSpecificData != NULL) {
Andreas Huberf4e5baa2010-04-09 14:25:46 -07001742 LOGE("Already have codec specific data");
Andreas Huber71c27d92010-03-19 11:43:15 -07001743 return ERROR_MALFORMED;
1744 }
1745
James Dong481e05e2010-08-06 00:29:03 -07001746 if (size < 4) {
1747 LOGE("Codec specific data length too short: %d", size);
Andreas Huber71c27d92010-03-19 11:43:15 -07001748 return ERROR_MALFORMED;
1749 }
1750
James Dong481e05e2010-08-06 00:29:03 -07001751 // Data is in the form of AVCCodecSpecificData
1752 if (memcmp("\x00\x00\x00\x01", data, 4)) {
1753 return copyAVCCodecSpecificData(data, size);
Andreas Huber71c27d92010-03-19 11:43:15 -07001754 }
1755
James Dong481e05e2010-08-06 00:29:03 -07001756 if (parseAVCCodecSpecificData(data, size) != OK) {
Andreas Huber71c27d92010-03-19 11:43:15 -07001757 return ERROR_MALFORMED;
1758 }
1759
James Dong481e05e2010-08-06 00:29:03 -07001760 // ISO 14496-15: AVC file format
1761 mCodecSpecificDataSize += 7; // 7 more bytes in the header
Andreas Huber71c27d92010-03-19 11:43:15 -07001762 mCodecSpecificData = malloc(mCodecSpecificDataSize);
1763 uint8_t *header = (uint8_t *)mCodecSpecificData;
James Dong481e05e2010-08-06 00:29:03 -07001764 header[0] = 1; // version
1765 header[1] = mProfileIdc; // profile indication
1766 header[2] = mProfileCompatible; // profile compatibility
1767 header[3] = mLevelIdc;
Andreas Huber71c27d92010-03-19 11:43:15 -07001768
James Dong481e05e2010-08-06 00:29:03 -07001769 // 6-bit '111111' followed by 2-bit to lengthSizeMinuusOne
James Dong7755cdd2010-09-02 10:49:55 -07001770 if (mOwner->useNalLengthFour()) {
1771 header[4] = 0xfc | 3; // length size == 4 bytes
1772 } else {
1773 header[4] = 0xfc | 1; // length size == 2 bytes
1774 }
Andreas Huber71c27d92010-03-19 11:43:15 -07001775
James Dong481e05e2010-08-06 00:29:03 -07001776 // 3-bit '111' followed by 5-bit numSequenceParameterSets
1777 int nSequenceParamSets = mSeqParamSets.size();
1778 header[5] = 0xe0 | nSequenceParamSets;
1779 header += 6;
1780 for (List<AVCParamSet>::iterator it = mSeqParamSets.begin();
1781 it != mSeqParamSets.end(); ++it) {
1782 // 16-bit sequence parameter set length
1783 uint16_t seqParamSetLength = it->mLength;
1784 header[0] = seqParamSetLength >> 8;
1785 header[1] = seqParamSetLength & 0xff;
1786
1787 // SPS NAL unit (sequence parameter length bytes)
1788 memcpy(&header[2], it->mData, seqParamSetLength);
1789 header += (2 + seqParamSetLength);
1790 }
1791
1792 // 8-bit nPictureParameterSets
1793 int nPictureParamSets = mPicParamSets.size();
1794 header[0] = nPictureParamSets;
1795 header += 1;
1796 for (List<AVCParamSet>::iterator it = mPicParamSets.begin();
1797 it != mPicParamSets.end(); ++it) {
1798 // 16-bit picture parameter set length
1799 uint16_t picParamSetLength = it->mLength;
1800 header[0] = picParamSetLength >> 8;
1801 header[1] = picParamSetLength & 0xff;
1802
1803 // PPS Nal unit (picture parameter set length bytes)
1804 memcpy(&header[2], it->mData, picParamSetLength);
1805 header += (2 + picParamSetLength);
1806 }
Andreas Huber71c27d92010-03-19 11:43:15 -07001807
1808 return OK;
1809}
1810
James Dongacee8e72010-10-03 10:59:26 -07001811/*
James Dongacee8e72010-10-03 10:59:26 -07001812 * Updates the drift time from the audio track so that
1813 * the video track can get the updated drift time information
1814 * from the file writer. The fluctuation of the drift time of the audio
1815 * encoding path is smoothed out with a simple filter by giving a larger
1816 * weight to more recently drift time. The filter coefficients, 0.5 and 0.5,
1817 * are heuristically determined.
1818 */
1819void MPEG4Writer::Track::updateDriftTime(const sp<MetaData>& meta) {
1820 int64_t driftTimeUs = 0;
1821 if (meta->findInt64(kKeyDriftTime, &driftTimeUs)) {
1822 int64_t prevDriftTimeUs = mOwner->getDriftTimeUs();
1823 int64_t timeUs = (driftTimeUs + prevDriftTimeUs) >> 1;
1824 mOwner->setDriftTimeUs(timeUs);
1825 }
1826}
1827
James Dongd0366622010-08-18 19:10:39 -07001828status_t MPEG4Writer::Track::threadEntry() {
Andreas Huberea6a38c2009-11-16 15:43:38 -08001829 int32_t count = 0;
James Dong3300e962010-04-21 16:14:15 -07001830 const int64_t interleaveDurationUs = mOwner->interleaveDuration();
James Donged742302011-05-06 11:27:59 -07001831 const bool hasMultipleTracks = (mOwner->numTracks() > 1);
James Dong3300e962010-04-21 16:14:15 -07001832 int64_t chunkTimestampUs = 0;
1833 int32_t nChunks = 0;
1834 int32_t nZeroLengthFrames = 0;
James Dong4108b1ed2011-06-07 19:45:54 -07001835 int64_t lastTimestampUs = 0; // Previous sample time stamp
1836 int64_t lastCttsTimeUs = 0; // Previous sample time stamp
1837 int64_t lastDurationUs = 0; // Between the previous two samples
1838 int64_t currDurationTicks = 0; // Timescale based ticks
1839 int64_t lastDurationTicks = 0; // Timescale based ticks
1840 int32_t sampleCount = 1; // Sample count in the current stts table entry
1841 int64_t currCttsDurTicks = 0; // Timescale based ticks
1842 int64_t lastCttsDurTicks = 0; // Timescale based ticks
1843 int32_t cttsSampleCount = 1; // Sample count in the current ctts table entry
1844 uint32_t previousSampleSize = 0; // Size of the previous sample
James Dong08c74732010-06-10 12:28:15 -07001845 int64_t previousPausedDurationUs = 0;
James Dong4108b1ed2011-06-07 19:45:54 -07001846 int64_t timestampUs = 0;
1847 int64_t cttsDeltaTimeUs = 0;
James Dongbf8116f2011-06-17 15:13:42 -07001848 bool hasBFrames = false;
James Dongb7208192010-08-02 19:13:40 -07001849
James Dongbf8116f2011-06-17 15:13:42 -07001850#if 1
1851 // XXX: Samsung's video encoder's output buffer timestamp
1852 // is not correct. see bug 4724339
1853 char value[PROPERTY_VALUE_MAX];
1854 if (property_get("rw.media.record.hasb", value, NULL) &&
1855 (!strcasecmp(value, "true") || !strcasecmp(value, "1"))) {
1856 hasBFrames = true;
1857 }
1858#endif
James Dongc67acb22010-10-07 20:20:59 -07001859 if (mIsAudio) {
1860 prctl(PR_SET_NAME, (unsigned long)"AudioTrackEncoding", 0, 0, 0);
1861 } else {
1862 prctl(PR_SET_NAME, (unsigned long)"VideoTrackEncoding", 0, 0, 0);
1863 }
Glenn Kasten1d24aaa2011-06-14 10:35:34 -07001864 androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
James Dongfc8b7c92010-12-07 14:37:27 -08001865
James Dong4f501f02010-06-07 14:41:41 -07001866 sp<MetaData> meta_data;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001867
James Donge991e5f2010-07-28 13:18:14 -07001868 mNumSamples = 0;
James Dong09936ed2010-06-24 19:04:27 -07001869 status_t err = OK;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001870 MediaBuffer *buffer;
James Dong09936ed2010-06-24 19:04:27 -07001871 while (!mDone && (err = mSource->read(&buffer)) == OK) {
Andreas Hubere46b7be2009-07-14 16:56:47 -07001872 if (buffer->range_length() == 0) {
1873 buffer->release();
1874 buffer = NULL;
James Dong3300e962010-04-21 16:14:15 -07001875 ++nZeroLengthFrames;
Andreas Hubere46b7be2009-07-14 16:56:47 -07001876 continue;
1877 }
1878
James Dong08c74732010-06-10 12:28:15 -07001879 // If the codec specific data has not been received yet, delay pause.
1880 // After the codec specific data is received, discard what we received
1881 // when the track is to be paused.
1882 if (mPaused && !mResumed) {
1883 buffer->release();
1884 buffer = NULL;
1885 continue;
1886 }
1887
Andreas Huberea6a38c2009-11-16 15:43:38 -08001888 ++count;
1889
Andreas Huber71c27d92010-03-19 11:43:15 -07001890 int32_t isCodecConfig;
1891 if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecConfig)
1892 && isCodecConfig) {
Andreas Huberf4e5baa2010-04-09 14:25:46 -07001893 CHECK(!mGotAllCodecSpecificData);
1894
James Dongda8073c2010-07-30 17:41:22 -07001895 if (mIsAvc) {
Andreas Huber71c27d92010-03-19 11:43:15 -07001896 status_t err = makeAVCCodecSpecificData(
1897 (const uint8_t *)buffer->data()
1898 + buffer->range_offset(),
1899 buffer->range_length());
James Dong2a4767e2010-04-20 11:50:11 -07001900 CHECK_EQ(OK, err);
James Dongda8073c2010-07-30 17:41:22 -07001901 } else if (mIsMPEG4) {
Andreas Huber71c27d92010-03-19 11:43:15 -07001902 mCodecSpecificDataSize = buffer->range_length();
1903 mCodecSpecificData = malloc(mCodecSpecificDataSize);
1904 memcpy(mCodecSpecificData,
1905 (const uint8_t *)buffer->data()
1906 + buffer->range_offset(),
1907 buffer->range_length());
Andreas Huberea6a38c2009-11-16 15:43:38 -08001908 }
1909
1910 buffer->release();
1911 buffer = NULL;
1912
Andreas Huberf4e5baa2010-04-09 14:25:46 -07001913 mGotAllCodecSpecificData = true;
Andreas Huberea6a38c2009-11-16 15:43:38 -08001914 continue;
James Dong08c74732010-06-10 12:28:15 -07001915 }
1916
James Dong4f501f02010-06-07 14:41:41 -07001917 // Make a deep copy of the MediaBuffer and Metadata and release
1918 // the original as soon as we can
1919 MediaBuffer *copy = new MediaBuffer(buffer->range_length());
1920 memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(),
1921 buffer->range_length());
1922 copy->set_range(0, buffer->range_length());
1923 meta_data = new MetaData(*buffer->meta_data().get());
1924 buffer->release();
1925 buffer = NULL;
1926
James Dongda8073c2010-07-30 17:41:22 -07001927 if (mIsAvc) StripStartcode(copy);
James Dongc6161722010-05-20 17:55:52 -07001928
James Dong7755cdd2010-09-02 10:49:55 -07001929 size_t sampleSize = copy->range_length();
1930 if (mIsAvc) {
1931 if (mOwner->useNalLengthFour()) {
1932 sampleSize += 4;
1933 } else {
1934 sampleSize += 2;
1935 }
1936 }
James Dongabed93a2010-04-22 17:27:04 -07001937
James Dong18244862010-05-11 14:57:02 -07001938 // Max file size or duration handling
James Dongcb7e65c2010-09-02 11:19:11 -07001939 mMdatSizeBytes += sampleSize;
1940 updateTrackSizeEstimate();
1941
James Dong18244862010-05-11 14:57:02 -07001942 if (mOwner->exceedsFileSizeLimit()) {
James Dong18244862010-05-11 14:57:02 -07001943 mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
1944 break;
1945 }
1946 if (mOwner->exceedsFileDurationLimit()) {
James Dong18244862010-05-11 14:57:02 -07001947 mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
1948 break;
1949 }
1950
James Dongabed93a2010-04-22 17:27:04 -07001951
James Dong4f501f02010-06-07 14:41:41 -07001952 int32_t isSync = false;
1953 meta_data->findInt32(kKeyIsSyncFrame, &isSync);
James Dong4f501f02010-06-07 14:41:41 -07001954 CHECK(meta_data->findInt64(kKeyTime, &timestampUs));
1955
1956////////////////////////////////////////////////////////////////////////////////
James Dong3aea0372011-05-06 12:19:04 -07001957 if (mNumSamples == 0) {
1958 mFirstSampleTimeRealUs = systemTime() / 1000;
James Dong36e573b2010-06-19 09:04:18 -07001959 mStartTimestampUs = timestampUs;
1960 mOwner->setStartTimestampUs(mStartTimestampUs);
James Dongdacebe62010-09-24 10:01:29 -07001961 previousPausedDurationUs = mStartTimestampUs;
James Dong9db798d2010-05-13 11:47:36 -07001962 }
Andreas Hubere46b7be2009-07-14 16:56:47 -07001963
James Dong08c74732010-06-10 12:28:15 -07001964 if (mResumed) {
James Dongdacebe62010-09-24 10:01:29 -07001965 int64_t durExcludingEarlierPausesUs = timestampUs - previousPausedDurationUs;
1966 CHECK(durExcludingEarlierPausesUs >= 0);
1967 int64_t pausedDurationUs = durExcludingEarlierPausesUs - mTrackDurationUs;
1968 CHECK(pausedDurationUs >= lastDurationUs);
1969 previousPausedDurationUs += pausedDurationUs - lastDurationUs;
James Dong08c74732010-06-10 12:28:15 -07001970 mResumed = false;
1971 }
1972
1973 timestampUs -= previousPausedDurationUs;
James Dongdacebe62010-09-24 10:01:29 -07001974 CHECK(timestampUs >= 0);
James Dongbf8116f2011-06-17 15:13:42 -07001975 if (!mIsAudio && hasBFrames) {
James Dong4108b1ed2011-06-07 19:45:54 -07001976 /*
1977 * Composition time: timestampUs
1978 * Decoding time: decodingTimeUs
1979 * Composition time delta = composition time - decoding time
1980 *
1981 * We save picture decoding time stamp delta in stts table entries,
1982 * and composition time delta duration in ctts table entries.
1983 */
1984 int64_t decodingTimeUs;
1985 CHECK(meta_data->findInt64(kKeyDecodingTime, &decodingTimeUs));
1986 decodingTimeUs -= previousPausedDurationUs;
1987 int64_t timeUs = decodingTimeUs;
1988 cttsDeltaTimeUs = timestampUs - decodingTimeUs;
1989 timestampUs = decodingTimeUs;
Steve Block71f2cf12011-10-20 11:56:00 +01001990 ALOGV("decoding time: %lld and ctts delta time: %lld",
James Dong4108b1ed2011-06-07 19:45:54 -07001991 timestampUs, cttsDeltaTimeUs);
1992 }
James Dongacee8e72010-10-03 10:59:26 -07001993
James Dongacee8e72010-10-03 10:59:26 -07001994 if (mIsRealTimeRecording) {
1995 if (mIsAudio) {
1996 updateDriftTime(meta_data);
James Dongb7208192010-08-02 19:13:40 -07001997 }
1998 }
James Dongacee8e72010-10-03 10:59:26 -07001999
James Dongb7208192010-08-02 19:13:40 -07002000 CHECK(timestampUs >= 0);
Steve Block71f2cf12011-10-20 11:56:00 +01002001 ALOGV("%s media time stamp: %lld and previous paused duration %lld",
James Dongdacebe62010-09-24 10:01:29 -07002002 mIsAudio? "Audio": "Video", timestampUs, previousPausedDurationUs);
James Dong91b22a92010-08-05 10:46:13 -07002003 if (timestampUs > mTrackDurationUs) {
2004 mTrackDurationUs = timestampUs;
Andreas Huber716582e2010-02-02 12:13:30 -08002005 }
2006
James Dong2c8e8502011-08-09 11:14:57 -07002007 // We need to use the time scale based ticks, rather than the
2008 // timestamp itself to determine whether we have to use a new
2009 // stts entry, since we may have rounding errors.
2010 // The calculation is intended to reduce the accumulated
2011 // rounding errors.
2012 currDurationTicks =
2013 ((timestampUs * mTimeScale + 500000LL) / 1000000LL -
2014 (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
2015
James Dong7e397842010-07-28 10:24:39 -07002016 mSampleSizes.push_back(sampleSize);
James Donge991e5f2010-07-28 13:18:14 -07002017 ++mNumSamples;
2018 if (mNumSamples > 2) {
James Dongeff30e32010-08-13 14:16:26 -07002019
James Dongcbeebb12011-02-16 12:28:26 -08002020 // Force the first sample to have its own stts entry so that
2021 // we can adjust its value later to maintain the A/V sync.
2022 if (mNumSamples == 3 || currDurationTicks != lastDurationTicks) {
Steve Block71f2cf12011-10-20 11:56:00 +01002023 ALOGV("%s lastDurationUs: %lld us, currDurationTicks: %lld us",
James Dongcbeebb12011-02-16 12:28:26 -08002024 mIsAudio? "Audio": "Video", lastDurationUs, currDurationTicks);
James Donga7ea9f92011-06-06 19:00:40 -07002025 addOneSttsTableEntry(sampleCount, lastDurationTicks);
James Dong2a4767e2010-04-20 11:50:11 -07002026 sampleCount = 1;
2027 } else {
2028 ++sampleCount;
2029 }
James Dong4108b1ed2011-06-07 19:45:54 -07002030
2031 if (!mIsAudio) {
2032 currCttsDurTicks =
2033 ((cttsDeltaTimeUs * mTimeScale + 500000LL) / 1000000LL -
2034 (lastCttsTimeUs * mTimeScale + 500000LL) / 1000000LL);
2035 if (currCttsDurTicks != lastCttsDurTicks) {
2036 addOneCttsTableEntry(cttsSampleCount, lastCttsDurTicks);
2037 cttsSampleCount = 1;
2038 } else {
2039 ++cttsSampleCount;
2040 }
2041 }
James Dong2a4767e2010-04-20 11:50:11 -07002042 }
2043 if (mSamplesHaveSameSize) {
James Donge991e5f2010-07-28 13:18:14 -07002044 if (mNumSamples >= 2 && previousSampleSize != sampleSize) {
James Dong2a4767e2010-04-20 11:50:11 -07002045 mSamplesHaveSameSize = false;
2046 }
James Dong7e397842010-07-28 10:24:39 -07002047 previousSampleSize = sampleSize;
James Dong2a4767e2010-04-20 11:50:11 -07002048 }
Steve Block71f2cf12011-10-20 11:56:00 +01002049 ALOGV("%s timestampUs/lastTimestampUs: %lld/%lld",
James Dongcbeebb12011-02-16 12:28:26 -08002050 mIsAudio? "Audio": "Video", timestampUs, lastTimestampUs);
James Dong7e397842010-07-28 10:24:39 -07002051 lastDurationUs = timestampUs - lastTimestampUs;
James Dongeff30e32010-08-13 14:16:26 -07002052 lastDurationTicks = currDurationTicks;
James Dong7e397842010-07-28 10:24:39 -07002053 lastTimestampUs = timestampUs;
Andreas Hubere46b7be2009-07-14 16:56:47 -07002054
James Dong4108b1ed2011-06-07 19:45:54 -07002055 if (!mIsAudio) {
2056 lastCttsDurTicks = currCttsDurTicks;
2057 lastCttsTimeUs = cttsDeltaTimeUs;
2058 }
2059
James Dong4f501f02010-06-07 14:41:41 -07002060 if (isSync != 0) {
James Dongcb7e65c2010-09-02 11:19:11 -07002061 addOneStssTableEntry(mNumSamples);
James Dong4f501f02010-06-07 14:41:41 -07002062 }
2063
James Dong09936ed2010-06-24 19:04:27 -07002064 if (mTrackingProgressStatus) {
2065 if (mPreviousTrackTimeUs <= 0) {
2066 mPreviousTrackTimeUs = mStartTimestampUs;
2067 }
James Dong85edea72010-07-15 19:08:20 -07002068 trackProgressStatus(timestampUs);
James Dong09936ed2010-06-24 19:04:27 -07002069 }
James Donged742302011-05-06 11:27:59 -07002070 if (!hasMultipleTracks) {
James Dongb1262a82010-11-16 14:04:54 -08002071 off64_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy)
James Dongb54a91782010-06-22 11:27:37 -07002072 : mOwner->addSample_l(copy);
2073 if (mChunkOffsets.empty()) {
James Dongcb7e65c2010-09-02 11:19:11 -07002074 addChunkOffset(offset);
James Dongb54a91782010-06-22 11:27:37 -07002075 }
2076 copy->release();
2077 copy = NULL;
2078 continue;
2079 }
James Dong3300e962010-04-21 16:14:15 -07002080
2081 mChunkSamples.push_back(copy);
2082 if (interleaveDurationUs == 0) {
James Dongcb7e65c2010-09-02 11:19:11 -07002083 addOneStscTableEntry(++nChunks, 1);
James Dongda8073c2010-07-30 17:41:22 -07002084 bufferChunk(timestampUs);
James Dong3300e962010-04-21 16:14:15 -07002085 } else {
2086 if (chunkTimestampUs == 0) {
2087 chunkTimestampUs = timestampUs;
2088 } else {
James Donged742302011-05-06 11:27:59 -07002089 int64_t chunkDurationUs = timestampUs - chunkTimestampUs;
2090 if (chunkDurationUs > interleaveDurationUs) {
2091 if (chunkDurationUs > mMaxChunkDurationUs) {
2092 mMaxChunkDurationUs = chunkDurationUs;
2093 }
James Dong3300e962010-04-21 16:14:15 -07002094 ++nChunks;
2095 if (nChunks == 1 || // First chunk
2096 (--(mStscTableEntries.end()))->samplesPerChunk !=
2097 mChunkSamples.size()) {
James Dongcb7e65c2010-09-02 11:19:11 -07002098 addOneStscTableEntry(nChunks, mChunkSamples.size());
James Dong3300e962010-04-21 16:14:15 -07002099 }
James Dongda8073c2010-07-30 17:41:22 -07002100 bufferChunk(timestampUs);
James Dong3300e962010-04-21 16:14:15 -07002101 chunkTimestampUs = timestampUs;
2102 }
2103 }
2104 }
2105
Andreas Hubere46b7be2009-07-14 16:56:47 -07002106 }
Andreas Huber033e6c32009-09-09 16:36:12 -07002107
James Dong839ba2c2011-06-21 17:22:37 -07002108 if (isTrackMalFormed()) {
James Dong62948fa2010-08-19 13:52:47 -07002109 err = ERROR_MALFORMED;
James Dong68510e62010-05-14 11:48:00 -07002110 }
James Dong839ba2c2011-06-21 17:22:37 -07002111
James Dong7fc8b4f2011-03-18 11:25:41 -07002112 mOwner->trackProgressStatus(mTrackId, -1, err);
James Dong2a4767e2010-04-20 11:50:11 -07002113
James Dong3300e962010-04-21 16:14:15 -07002114 // Last chunk
James Donged742302011-05-06 11:27:59 -07002115 if (!hasMultipleTracks) {
James Dongcb7e65c2010-09-02 11:19:11 -07002116 addOneStscTableEntry(1, mNumSamples);
James Dongb54a91782010-06-22 11:27:37 -07002117 } else if (!mChunkSamples.empty()) {
James Dongcb7e65c2010-09-02 11:19:11 -07002118 addOneStscTableEntry(++nChunks, mChunkSamples.size());
James Dongda8073c2010-07-30 17:41:22 -07002119 bufferChunk(timestampUs);
James Dong3300e962010-04-21 16:14:15 -07002120 }
2121
James Dong2a4767e2010-04-20 11:50:11 -07002122 // We don't really know how long the last frame lasts, since
2123 // there is no frame time after it, just repeat the previous
2124 // frame's duration.
James Donge991e5f2010-07-28 13:18:14 -07002125 if (mNumSamples == 1) {
James Dong52d13f02010-07-02 11:39:06 -07002126 lastDurationUs = 0; // A single sample's duration
James Donga7ea9f92011-06-06 19:00:40 -07002127 lastDurationTicks = 0;
James Dong4108b1ed2011-06-07 19:45:54 -07002128 lastCttsDurTicks = 0;
James Dong2a4767e2010-04-20 11:50:11 -07002129 } else {
2130 ++sampleCount; // Count for the last sample
James Dong4108b1ed2011-06-07 19:45:54 -07002131 ++cttsSampleCount;
James Dong2a4767e2010-04-20 11:50:11 -07002132 }
James Dongcbeebb12011-02-16 12:28:26 -08002133
2134 if (mNumSamples <= 2) {
James Donga7ea9f92011-06-06 19:00:40 -07002135 addOneSttsTableEntry(1, lastDurationTicks);
James Dongcbeebb12011-02-16 12:28:26 -08002136 if (sampleCount - 1 > 0) {
James Donga7ea9f92011-06-06 19:00:40 -07002137 addOneSttsTableEntry(sampleCount - 1, lastDurationTicks);
James Dongcbeebb12011-02-16 12:28:26 -08002138 }
2139 } else {
James Donga7ea9f92011-06-06 19:00:40 -07002140 addOneSttsTableEntry(sampleCount, lastDurationTicks);
James Dongcbeebb12011-02-16 12:28:26 -08002141 }
2142
James Dong4108b1ed2011-06-07 19:45:54 -07002143 addOneCttsTableEntry(cttsSampleCount, lastCttsDurTicks);
James Dong91b22a92010-08-05 10:46:13 -07002144 mTrackDurationUs += lastDurationUs;
Andreas Huber033e6c32009-09-09 16:36:12 -07002145 mReachedEOS = true;
James Donged742302011-05-06 11:27:59 -07002146
2147 sendTrackSummary(hasMultipleTracks);
2148
James Dongcb7e65c2010-09-02 11:19:11 -07002149 LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s",
2150 count, nZeroLengthFrames, mNumSamples, mIsAudio? "audio": "video");
James Dongacee8e72010-10-03 10:59:26 -07002151 if (mIsAudio) {
2152 LOGI("Audio track drift time: %lld us", mOwner->getDriftTimeUs());
2153 }
James Dongdae9fd32010-06-04 13:59:27 -07002154
James Dongd0366622010-08-18 19:10:39 -07002155 if (err == ERROR_END_OF_STREAM) {
2156 return OK;
2157 }
2158 return err;
James Dongdae9fd32010-06-04 13:59:27 -07002159}
2160
James Dong839ba2c2011-06-21 17:22:37 -07002161bool MPEG4Writer::Track::isTrackMalFormed() const {
2162 if (mSampleSizes.empty()) { // no samples written
2163 LOGE("The number of recorded samples is 0");
2164 return true;
2165 }
2166
2167 if (!mIsAudio && mNumStssTableEntries == 0) { // no sync frames for video
2168 LOGE("There are no sync frames for video track");
2169 return true;
2170 }
2171
2172 if (OK != checkCodecSpecificData()) { // no codec specific data
2173 return true;
2174 }
2175
2176 return false;
2177}
2178
James Donged742302011-05-06 11:27:59 -07002179void MPEG4Writer::Track::sendTrackSummary(bool hasMultipleTracks) {
James Dong0f32fb32011-05-14 07:22:40 -07002180
2181 // Send track summary only if test mode is enabled.
2182 if (!isTestModeEnabled()) {
2183 return;
2184 }
2185
James Donged742302011-05-06 11:27:59 -07002186 int trackNum = (mTrackId << 28);
2187
2188 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
2189 trackNum | MEDIA_RECORDER_TRACK_INFO_TYPE,
2190 mIsAudio? 0: 1);
2191
2192 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
2193 trackNum | MEDIA_RECORDER_TRACK_INFO_DURATION_MS,
2194 mTrackDurationUs / 1000);
2195
2196 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
2197 trackNum | MEDIA_RECORDER_TRACK_INFO_ENCODED_FRAMES,
2198 mNumSamples);
2199
James Dong13a33162011-05-09 16:56:25 -07002200 {
2201 // The system delay time excluding the requested initial delay that
2202 // is used to eliminate the recording sound.
2203 int64_t startTimeOffsetUs = mOwner->getStartTimeOffsetMs() * 1000LL;
2204 if (startTimeOffsetUs < 0) { // Start time offset was not set
2205 startTimeOffsetUs = kInitialDelayTimeUs;
2206 }
2207 int64_t initialDelayUs =
2208 mFirstSampleTimeRealUs - mStartTimeRealUs - startTimeOffsetUs;
2209
2210 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
James Dong3aea0372011-05-06 12:19:04 -07002211 trackNum | MEDIA_RECORDER_TRACK_INFO_INITIAL_DELAY_MS,
2212 (initialDelayUs) / 1000);
James Dong13a33162011-05-09 16:56:25 -07002213 }
James Dong3aea0372011-05-06 12:19:04 -07002214
James Dong0f32fb32011-05-14 07:22:40 -07002215 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
2216 trackNum | MEDIA_RECORDER_TRACK_INFO_DATA_KBYTES,
2217 mMdatSizeBytes / 1024);
2218
James Donged742302011-05-06 11:27:59 -07002219 if (hasMultipleTracks) {
2220 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
2221 trackNum | MEDIA_RECORDER_TRACK_INFO_MAX_CHUNK_DUR_MS,
2222 mMaxChunkDurationUs / 1000);
James Dong3aea0372011-05-06 12:19:04 -07002223
2224 int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
2225 if (mStartTimestampUs != moovStartTimeUs) {
2226 int64_t startTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
2227 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
2228 trackNum | MEDIA_RECORDER_TRACK_INFO_START_OFFSET_MS,
2229 startTimeOffsetUs / 1000);
2230 }
James Donged742302011-05-06 11:27:59 -07002231 }
2232}
2233
James Dong85edea72010-07-15 19:08:20 -07002234void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) {
Steve Block71f2cf12011-10-20 11:56:00 +01002235 ALOGV("trackProgressStatus: %lld us", timeUs);
James Dongd4760c22010-06-26 08:24:47 -07002236 if (mTrackEveryTimeDurationUs > 0 &&
2237 timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
Steve Block71f2cf12011-10-20 11:56:00 +01002238 ALOGV("Fire time tracking progress status at %lld us", timeUs);
James Dong7fc8b4f2011-03-18 11:25:41 -07002239 mOwner->trackProgressStatus(mTrackId, timeUs - mPreviousTrackTimeUs, err);
James Dong09936ed2010-06-24 19:04:27 -07002240 mPreviousTrackTimeUs = timeUs;
2241 }
2242}
2243
James Dong85edea72010-07-15 19:08:20 -07002244void MPEG4Writer::trackProgressStatus(
James Dong7fc8b4f2011-03-18 11:25:41 -07002245 size_t trackId, int64_t timeUs, status_t err) {
James Dong85edea72010-07-15 19:08:20 -07002246 Mutex::Autolock lock(mLock);
James Dong7fc8b4f2011-03-18 11:25:41 -07002247 int32_t trackNum = (trackId << 28);
James Dong85edea72010-07-15 19:08:20 -07002248
2249 // Error notification
2250 // Do not consider ERROR_END_OF_STREAM an error
2251 if (err != OK && err != ERROR_END_OF_STREAM) {
James Dong7fc8b4f2011-03-18 11:25:41 -07002252 notify(MEDIA_RECORDER_TRACK_EVENT_ERROR,
2253 trackNum | MEDIA_RECORDER_TRACK_ERROR_GENERAL,
James Dong85edea72010-07-15 19:08:20 -07002254 err);
2255 return;
2256 }
2257
2258 if (timeUs == -1) {
2259 // Send completion notification
James Dong7fc8b4f2011-03-18 11:25:41 -07002260 notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
2261 trackNum | MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS,
James Dong85edea72010-07-15 19:08:20 -07002262 err);
James Dong85edea72010-07-15 19:08:20 -07002263 } else {
2264 // Send progress status
James Dong7fc8b4f2011-03-18 11:25:41 -07002265 notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
2266 trackNum | MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME,
James Dong85edea72010-07-15 19:08:20 -07002267 timeUs / 1000);
2268 }
2269}
2270
James Dong4c238152010-09-01 18:48:35 -07002271void MPEG4Writer::setDriftTimeUs(int64_t driftTimeUs) {
Steve Block71f2cf12011-10-20 11:56:00 +01002272 ALOGV("setDriftTimeUs: %lld us", driftTimeUs);
James Dongb7208192010-08-02 19:13:40 -07002273 Mutex::Autolock autolock(mLock);
James Dong4c238152010-09-01 18:48:35 -07002274 mDriftTimeUs = driftTimeUs;
James Dongb7208192010-08-02 19:13:40 -07002275}
2276
2277int64_t MPEG4Writer::getDriftTimeUs() {
Steve Block71f2cf12011-10-20 11:56:00 +01002278 ALOGV("getDriftTimeUs: %lld us", mDriftTimeUs);
James Dongb7208192010-08-02 19:13:40 -07002279 Mutex::Autolock autolock(mLock);
2280 return mDriftTimeUs;
2281}
2282
James Dong7755cdd2010-09-02 10:49:55 -07002283bool MPEG4Writer::useNalLengthFour() {
2284 return mUse4ByteNalLength;
2285}
2286
James Dongda8073c2010-07-30 17:41:22 -07002287void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) {
Steve Block71f2cf12011-10-20 11:56:00 +01002288 ALOGV("bufferChunk");
James Dongda8073c2010-07-30 17:41:22 -07002289
James Dongda8073c2010-07-30 17:41:22 -07002290 Chunk chunk(this, timestampUs, mChunkSamples);
2291 mOwner->bufferChunk(chunk);
James Dong3300e962010-04-21 16:14:15 -07002292 mChunkSamples.clear();
Andreas Hubere46b7be2009-07-14 16:56:47 -07002293}
2294
Andreas Huber716582e2010-02-02 12:13:30 -08002295int64_t MPEG4Writer::Track::getDurationUs() const {
James Dong91b22a92010-08-05 10:46:13 -07002296 return mTrackDurationUs;
Andreas Hubere46b7be2009-07-14 16:56:47 -07002297}
2298
James Dong18244862010-05-11 14:57:02 -07002299int64_t MPEG4Writer::Track::getEstimatedTrackSizeBytes() const {
2300 return mEstimatedTrackSizeBytes;
2301}
2302
James Dong62948fa2010-08-19 13:52:47 -07002303status_t MPEG4Writer::Track::checkCodecSpecificData() const {
2304 const char *mime;
2305 CHECK(mMeta->findCString(kKeyMIMEType, &mime));
2306 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime) ||
2307 !strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) ||
2308 !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
2309 if (!mCodecSpecificData ||
2310 mCodecSpecificDataSize <= 0) {
James Dongb5024da2010-09-13 16:30:51 -07002311 LOGE("Missing codec specific data");
James Dong62948fa2010-08-19 13:52:47 -07002312 return ERROR_MALFORMED;
2313 }
2314 } else {
2315 if (mCodecSpecificData ||
2316 mCodecSpecificDataSize > 0) {
James Dongb5024da2010-09-13 16:30:51 -07002317 LOGE("Unexepected codec specific data found");
James Dong62948fa2010-08-19 13:52:47 -07002318 return ERROR_MALFORMED;
2319 }
2320 }
2321 return OK;
2322}
2323
James Dong7a6cea42011-05-06 16:55:39 -07002324void MPEG4Writer::Track::writeTrackHeader(bool use32BitOffset) {
Andreas Hubere46b7be2009-07-14 16:56:47 -07002325
Steve Block71f2cf12011-10-20 11:56:00 +01002326 ALOGV("%s track time scale: %d",
James Dongda8073c2010-07-30 17:41:22 -07002327 mIsAudio? "Audio": "Video", mTimeScale);
James Dong52d13f02010-07-02 11:39:06 -07002328
Andreas Hubere46b7be2009-07-14 16:56:47 -07002329 time_t now = time(NULL);
James Dong7a6cea42011-05-06 16:55:39 -07002330 mOwner->beginBox("trak");
2331 writeTkhdBox(now);
2332 mOwner->beginBox("mdia");
2333 writeMdhdBox(now);
2334 writeHdlrBox();
2335 mOwner->beginBox("minf");
2336 if (mIsAudio) {
2337 writeSmhdBox();
2338 } else {
2339 writeVmhdBox();
2340 }
2341 writeDinfBox();
2342 writeStblBox(use32BitOffset);
2343 mOwner->endBox(); // minf
2344 mOwner->endBox(); // mdia
2345 mOwner->endBox(); // trak
2346}
Andreas Hubere46b7be2009-07-14 16:56:47 -07002347
James Dong7a6cea42011-05-06 16:55:39 -07002348void MPEG4Writer::Track::writeStblBox(bool use32BitOffset) {
2349 mOwner->beginBox("stbl");
2350 mOwner->beginBox("stsd");
2351 mOwner->writeInt32(0); // version=0, flags=0
2352 mOwner->writeInt32(1); // entry count
2353 if (mIsAudio) {
2354 writeAudioFourCCBox();
2355 } else {
2356 writeVideoFourCCBox();
2357 }
2358 mOwner->endBox(); // stsd
2359 writeSttsBox();
James Dong4108b1ed2011-06-07 19:45:54 -07002360 writeCttsBox();
James Dong7a6cea42011-05-06 16:55:39 -07002361 if (!mIsAudio) {
2362 writeStssBox();
2363 }
2364 writeStszBox();
2365 writeStscBox();
2366 writeStcoBox(use32BitOffset);
2367 mOwner->endBox(); // stbl
2368}
2369
2370void MPEG4Writer::Track::writeVideoFourCCBox() {
2371 const char *mime;
2372 bool success = mMeta->findCString(kKeyMIMEType, &mime);
2373 CHECK(success);
2374 if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
2375 mOwner->beginBox("mp4v");
2376 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
2377 mOwner->beginBox("s263");
2378 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
2379 mOwner->beginBox("avc1");
2380 } else {
2381 LOGE("Unknown mime type '%s'.", mime);
2382 CHECK(!"should not be here, unknown mime type.");
2383 }
2384
2385 mOwner->writeInt32(0); // reserved
2386 mOwner->writeInt16(0); // reserved
2387 mOwner->writeInt16(1); // data ref index
2388 mOwner->writeInt16(0); // predefined
2389 mOwner->writeInt16(0); // reserved
2390 mOwner->writeInt32(0); // predefined
2391 mOwner->writeInt32(0); // predefined
2392 mOwner->writeInt32(0); // predefined
2393
2394 int32_t width, height;
2395 success = mMeta->findInt32(kKeyWidth, &width);
2396 success = success && mMeta->findInt32(kKeyHeight, &height);
2397 CHECK(success);
2398
2399 mOwner->writeInt16(width);
2400 mOwner->writeInt16(height);
2401 mOwner->writeInt32(0x480000); // horiz resolution
2402 mOwner->writeInt32(0x480000); // vert resolution
2403 mOwner->writeInt32(0); // reserved
2404 mOwner->writeInt16(1); // frame count
2405 mOwner->write(" ", 32);
2406 mOwner->writeInt16(0x18); // depth
2407 mOwner->writeInt16(-1); // predefined
2408
2409 CHECK(23 + mCodecSpecificDataSize < 128);
2410
2411 if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
2412 writeMp4vEsdsBox();
2413 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
2414 writeD263Box();
2415 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
2416 writeAvccBox();
2417 }
2418
2419 writePaspBox();
2420 mOwner->endBox(); // mp4v, s263 or avc1
2421}
2422
2423void MPEG4Writer::Track::writeAudioFourCCBox() {
2424 const char *mime;
2425 bool success = mMeta->findCString(kKeyMIMEType, &mime);
2426 CHECK(success);
2427 const char *fourcc = NULL;
2428 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
2429 fourcc = "samr";
2430 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
2431 fourcc = "sawb";
2432 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
2433 fourcc = "mp4a";
2434 } else {
2435 LOGE("Unknown mime type '%s'.", mime);
2436 CHECK(!"should not be here, unknown mime type.");
2437 }
2438
2439 mOwner->beginBox(fourcc); // audio format
2440 mOwner->writeInt32(0); // reserved
2441 mOwner->writeInt16(0); // reserved
2442 mOwner->writeInt16(0x1); // data ref index
2443 mOwner->writeInt32(0); // reserved
2444 mOwner->writeInt32(0); // reserved
2445 int32_t nChannels;
2446 CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels));
2447 mOwner->writeInt16(nChannels); // channel count
2448 mOwner->writeInt16(16); // sample size
2449 mOwner->writeInt16(0); // predefined
2450 mOwner->writeInt16(0); // reserved
2451
2452 int32_t samplerate;
2453 success = mMeta->findInt32(kKeySampleRate, &samplerate);
2454 CHECK(success);
2455 mOwner->writeInt32(samplerate << 16);
2456 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
2457 writeMp4aEsdsBox();
2458 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime) ||
2459 !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
2460 writeDamrBox();
2461 }
2462 mOwner->endBox();
2463}
2464
2465void MPEG4Writer::Track::writeMp4aEsdsBox() {
2466 mOwner->beginBox("esds");
2467 CHECK(mCodecSpecificData);
2468 CHECK(mCodecSpecificDataSize > 0);
2469
2470 // Make sure all sizes encode to a single byte.
2471 CHECK(mCodecSpecificDataSize + 23 < 128);
2472
2473 mOwner->writeInt32(0); // version=0, flags=0
2474 mOwner->writeInt8(0x03); // ES_DescrTag
2475 mOwner->writeInt8(23 + mCodecSpecificDataSize);
2476 mOwner->writeInt16(0x0000);// ES_ID
2477 mOwner->writeInt8(0x00);
2478
2479 mOwner->writeInt8(0x04); // DecoderConfigDescrTag
2480 mOwner->writeInt8(15 + mCodecSpecificDataSize);
2481 mOwner->writeInt8(0x40); // objectTypeIndication ISO/IEC 14492-2
2482 mOwner->writeInt8(0x15); // streamType AudioStream
2483
2484 mOwner->writeInt16(0x03); // XXX
2485 mOwner->writeInt8(0x00); // buffer size 24-bit
2486 mOwner->writeInt32(96000); // max bit rate
2487 mOwner->writeInt32(96000); // avg bit rate
2488
2489 mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
2490 mOwner->writeInt8(mCodecSpecificDataSize);
2491 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
2492
2493 static const uint8_t kData2[] = {
2494 0x06, // SLConfigDescriptorTag
2495 0x01,
2496 0x02
2497 };
2498 mOwner->write(kData2, sizeof(kData2));
2499
2500 mOwner->endBox(); // esds
2501}
2502
2503void MPEG4Writer::Track::writeMp4vEsdsBox() {
2504 CHECK(mCodecSpecificData);
2505 CHECK(mCodecSpecificDataSize > 0);
2506 mOwner->beginBox("esds");
2507
2508 mOwner->writeInt32(0); // version=0, flags=0
2509
2510 mOwner->writeInt8(0x03); // ES_DescrTag
2511 mOwner->writeInt8(23 + mCodecSpecificDataSize);
2512 mOwner->writeInt16(0x0000); // ES_ID
2513 mOwner->writeInt8(0x1f);
2514
2515 mOwner->writeInt8(0x04); // DecoderConfigDescrTag
2516 mOwner->writeInt8(15 + mCodecSpecificDataSize);
2517 mOwner->writeInt8(0x20); // objectTypeIndication ISO/IEC 14492-2
2518 mOwner->writeInt8(0x11); // streamType VisualStream
2519
2520 static const uint8_t kData[] = {
2521 0x01, 0x77, 0x00,
2522 0x00, 0x03, 0xe8, 0x00,
2523 0x00, 0x03, 0xe8, 0x00
2524 };
2525 mOwner->write(kData, sizeof(kData));
2526
2527 mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
2528
2529 mOwner->writeInt8(mCodecSpecificDataSize);
2530 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
2531
2532 static const uint8_t kData2[] = {
2533 0x06, // SLConfigDescriptorTag
2534 0x01,
2535 0x02
2536 };
2537 mOwner->write(kData2, sizeof(kData2));
2538
2539 mOwner->endBox(); // esds
2540}
2541
2542void MPEG4Writer::Track::writeTkhdBox(time_t now) {
2543 mOwner->beginBox("tkhd");
2544 // Flags = 7 to indicate that the track is enabled, and
2545 // part of the presentation
2546 mOwner->writeInt32(0x07); // version=0, flags=7
2547 mOwner->writeInt32(now); // creation time
2548 mOwner->writeInt32(now); // modification time
James Dongd2952962011-05-13 14:41:21 -07002549 mOwner->writeInt32(mTrackId + 1); // track id starts with 1
James Dong7a6cea42011-05-06 16:55:39 -07002550 mOwner->writeInt32(0); // reserved
2551 int64_t trakDurationUs = getDurationUs();
2552 int32_t mvhdTimeScale = mOwner->getTimeScale();
2553 int32_t tkhdDuration =
2554 (trakDurationUs * mvhdTimeScale + 5E5) / 1E6;
2555 mOwner->writeInt32(tkhdDuration); // in mvhd timescale
2556 mOwner->writeInt32(0); // reserved
2557 mOwner->writeInt32(0); // reserved
2558 mOwner->writeInt16(0); // layer
2559 mOwner->writeInt16(0); // alternate group
2560 mOwner->writeInt16(mIsAudio ? 0x100 : 0); // volume
2561 mOwner->writeInt16(0); // reserved
2562
2563 mOwner->writeCompositionMatrix(mRotation); // matrix
2564
2565 if (mIsAudio) {
2566 mOwner->writeInt32(0);
2567 mOwner->writeInt32(0);
2568 } else {
2569 int32_t width, height;
2570 bool success = mMeta->findInt32(kKeyWidth, &width);
2571 success = success && mMeta->findInt32(kKeyHeight, &height);
2572 CHECK(success);
2573
2574 mOwner->writeInt32(width << 16); // 32-bit fixed-point value
2575 mOwner->writeInt32(height << 16); // 32-bit fixed-point value
2576 }
2577 mOwner->endBox(); // tkhd
2578}
2579
2580void MPEG4Writer::Track::writeVmhdBox() {
2581 mOwner->beginBox("vmhd");
2582 mOwner->writeInt32(0x01); // version=0, flags=1
2583 mOwner->writeInt16(0); // graphics mode
2584 mOwner->writeInt16(0); // opcolor
2585 mOwner->writeInt16(0);
2586 mOwner->writeInt16(0);
2587 mOwner->endBox();
2588}
2589
2590void MPEG4Writer::Track::writeSmhdBox() {
2591 mOwner->beginBox("smhd");
2592 mOwner->writeInt32(0); // version=0, flags=0
2593 mOwner->writeInt16(0); // balance
2594 mOwner->writeInt16(0); // reserved
2595 mOwner->endBox();
2596}
2597
2598void MPEG4Writer::Track::writeHdlrBox() {
2599 mOwner->beginBox("hdlr");
2600 mOwner->writeInt32(0); // version=0, flags=0
2601 mOwner->writeInt32(0); // component type: should be mhlr
2602 mOwner->writeFourcc(mIsAudio ? "soun" : "vide"); // component subtype
2603 mOwner->writeInt32(0); // reserved
2604 mOwner->writeInt32(0); // reserved
2605 mOwner->writeInt32(0); // reserved
2606 // Removing "r" for the name string just makes the string 4 byte aligned
2607 mOwner->writeCString(mIsAudio ? "SoundHandle": "VideoHandle"); // name
2608 mOwner->endBox();
2609}
2610
2611void MPEG4Writer::Track::writeMdhdBox(time_t now) {
2612 int64_t trakDurationUs = getDurationUs();
2613 mOwner->beginBox("mdhd");
2614 mOwner->writeInt32(0); // version=0, flags=0
2615 mOwner->writeInt32(now); // creation time
2616 mOwner->writeInt32(now); // modification time
2617 mOwner->writeInt32(mTimeScale); // media timescale
2618 int32_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6;
2619 mOwner->writeInt32(mdhdDuration); // use media timescale
2620 // Language follows the three letter standard ISO-639-2/T
2621 // 'e', 'n', 'g' for "English", for instance.
2622 // Each character is packed as the difference between its ASCII value and 0x60.
2623 // For "English", these are 00101, 01110, 00111.
2624 // XXX: Where is the padding bit located: 0x15C7?
2625 mOwner->writeInt16(0); // language code
2626 mOwner->writeInt16(0); // predefined
2627 mOwner->endBox();
2628}
2629
2630void MPEG4Writer::Track::writeDamrBox() {
2631 // 3gpp2 Spec AMRSampleEntry fields
2632 mOwner->beginBox("damr");
2633 mOwner->writeCString(" "); // vendor: 4 bytes
2634 mOwner->writeInt8(0); // decoder version
2635 mOwner->writeInt16(0x83FF); // mode set: all enabled
2636 mOwner->writeInt8(0); // mode change period
2637 mOwner->writeInt8(1); // frames per sample
2638 mOwner->endBox();
2639}
2640
2641void MPEG4Writer::Track::writeUrlBox() {
2642 // The table index here refers to the sample description index
2643 // in the sample table entries.
2644 mOwner->beginBox("url ");
2645 mOwner->writeInt32(1); // version=0, flags=1 (self-contained)
2646 mOwner->endBox(); // url
2647}
2648
2649void MPEG4Writer::Track::writeDrefBox() {
2650 mOwner->beginBox("dref");
2651 mOwner->writeInt32(0); // version=0, flags=0
2652 mOwner->writeInt32(1); // entry count (either url or urn)
2653 writeUrlBox();
2654 mOwner->endBox(); // dref
2655}
2656
2657void MPEG4Writer::Track::writeDinfBox() {
2658 mOwner->beginBox("dinf");
2659 writeDrefBox();
2660 mOwner->endBox(); // dinf
2661}
2662
2663void MPEG4Writer::Track::writeAvccBox() {
2664 CHECK(mCodecSpecificData);
2665 CHECK(mCodecSpecificDataSize >= 5);
2666
2667 // Patch avcc's lengthSize field to match the number
2668 // of bytes we use to indicate the size of a nal unit.
2669 uint8_t *ptr = (uint8_t *)mCodecSpecificData;
2670 ptr[4] = (ptr[4] & 0xfc) | (mOwner->useNalLengthFour() ? 3 : 1);
2671 mOwner->beginBox("avcC");
2672 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
2673 mOwner->endBox(); // avcC
2674}
2675
2676void MPEG4Writer::Track::writeD263Box() {
2677 mOwner->beginBox("d263");
2678 mOwner->writeInt32(0); // vendor
2679 mOwner->writeInt8(0); // decoder version
2680 mOwner->writeInt8(10); // level: 10
2681 mOwner->writeInt8(0); // profile: 0
2682 mOwner->endBox(); // d263
2683}
2684
2685// This is useful if the pixel is not square
2686void MPEG4Writer::Track::writePaspBox() {
2687 mOwner->beginBox("pasp");
2688 mOwner->writeInt32(1 << 16); // hspacing
2689 mOwner->writeInt32(1 << 16); // vspacing
2690 mOwner->endBox(); // pasp
2691}
2692
2693void MPEG4Writer::Track::writeSttsBox() {
2694 mOwner->beginBox("stts");
2695 mOwner->writeInt32(0); // version=0, flags=0
2696 mOwner->writeInt32(mNumSttsTableEntries);
James Dong3aea0372011-05-06 12:19:04 -07002697
James Dongcbeebb12011-02-16 12:28:26 -08002698 // Compensate for small start time difference from different media tracks
2699 int64_t trackStartTimeOffsetUs = 0;
James Dong7a6cea42011-05-06 16:55:39 -07002700 int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
2701 if (mStartTimestampUs != moovStartTimeUs) {
2702 CHECK(mStartTimestampUs > moovStartTimeUs);
2703 trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
2704 }
James Donga7ea9f92011-06-06 19:00:40 -07002705 List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
2706 CHECK(it != mSttsTableEntries.end() && it->sampleCount == 1);
2707 mOwner->writeInt32(it->sampleCount);
2708 int32_t dur = (trackStartTimeOffsetUs * mTimeScale + 500000LL) / 1000000LL;
2709 mOwner->writeInt32(dur + it->sampleDuration);
2710
James Dong4108b1ed2011-06-07 19:45:54 -07002711 int64_t totalCount = 1;
James Donga7ea9f92011-06-06 19:00:40 -07002712 while (++it != mSttsTableEntries.end()) {
James Dong7a6cea42011-05-06 16:55:39 -07002713 mOwner->writeInt32(it->sampleCount);
James Donga7ea9f92011-06-06 19:00:40 -07002714 mOwner->writeInt32(it->sampleDuration);
James Dong4108b1ed2011-06-07 19:45:54 -07002715 totalCount += it->sampleCount;
James Dong7a6cea42011-05-06 16:55:39 -07002716 }
James Dong4108b1ed2011-06-07 19:45:54 -07002717 CHECK(totalCount == mNumSamples);
James Dong7a6cea42011-05-06 16:55:39 -07002718 mOwner->endBox(); // stts
2719}
Andreas Hubere46b7be2009-07-14 16:56:47 -07002720
James Dong4108b1ed2011-06-07 19:45:54 -07002721void MPEG4Writer::Track::writeCttsBox() {
2722 if (mIsAudio) { // ctts is not for audio
2723 return;
2724 }
2725
2726 // Do not write ctts box when there is no need to have it.
2727 if ((mNumCttsTableEntries == 1 &&
2728 mCttsTableEntries.begin()->sampleDuration == 0) ||
2729 mNumCttsTableEntries == 0) {
2730 return;
2731 }
2732
Steve Block71f2cf12011-10-20 11:56:00 +01002733 ALOGV("ctts box has %d entries", mNumCttsTableEntries);
James Dong4108b1ed2011-06-07 19:45:54 -07002734
2735 mOwner->beginBox("ctts");
2736 if (mHasNegativeCttsDeltaDuration) {
2737 mOwner->writeInt32(0x00010000); // version=1, flags=0
2738 } else {
2739 mOwner->writeInt32(0); // version=0, flags=0
2740 }
2741 mOwner->writeInt32(mNumCttsTableEntries);
2742
2743 int64_t totalCount = 0;
2744 for (List<CttsTableEntry>::iterator it = mCttsTableEntries.begin();
2745 it != mCttsTableEntries.end(); ++it) {
2746 mOwner->writeInt32(it->sampleCount);
2747 mOwner->writeInt32(it->sampleDuration);
2748 totalCount += it->sampleCount;
2749 }
2750 CHECK(totalCount == mNumSamples);
2751 mOwner->endBox(); // ctts
2752}
2753
James Dong7a6cea42011-05-06 16:55:39 -07002754void MPEG4Writer::Track::writeStssBox() {
2755 mOwner->beginBox("stss");
2756 mOwner->writeInt32(0); // version=0, flags=0
2757 mOwner->writeInt32(mNumStssTableEntries); // number of sync frames
2758 for (List<int32_t>::iterator it = mStssTableEntries.begin();
2759 it != mStssTableEntries.end(); ++it) {
2760 mOwner->writeInt32(*it);
2761 }
2762 mOwner->endBox(); // stss
2763}
Andreas Hubere46b7be2009-07-14 16:56:47 -07002764
James Dong7a6cea42011-05-06 16:55:39 -07002765void MPEG4Writer::Track::writeStszBox() {
2766 mOwner->beginBox("stsz");
2767 mOwner->writeInt32(0); // version=0, flags=0
2768 if (mSamplesHaveSameSize) {
2769 List<size_t>::iterator it = mSampleSizes.begin();
2770 mOwner->writeInt32(*it); // default sample size
2771 } else {
2772 mOwner->writeInt32(0);
2773 }
2774 mOwner->writeInt32(mNumSamples);
2775 if (!mSamplesHaveSameSize) {
2776 for (List<size_t>::iterator it = mSampleSizes.begin();
2777 it != mSampleSizes.end(); ++it) {
2778 mOwner->writeInt32(*it);
Andreas Hubere46b7be2009-07-14 16:56:47 -07002779 }
James Dong7a6cea42011-05-06 16:55:39 -07002780 }
2781 mOwner->endBox(); // stsz
2782}
Andreas Hubere46b7be2009-07-14 16:56:47 -07002783
James Dong7a6cea42011-05-06 16:55:39 -07002784void MPEG4Writer::Track::writeStscBox() {
2785 mOwner->beginBox("stsc");
2786 mOwner->writeInt32(0); // version=0, flags=0
2787 mOwner->writeInt32(mNumStscTableEntries);
2788 for (List<StscTableEntry>::iterator it = mStscTableEntries.begin();
2789 it != mStscTableEntries.end(); ++it) {
2790 mOwner->writeInt32(it->firstChunk);
2791 mOwner->writeInt32(it->samplesPerChunk);
2792 mOwner->writeInt32(it->sampleDescriptionId);
2793 }
2794 mOwner->endBox(); // stsc
2795}
James Dong9db798d2010-05-13 11:47:36 -07002796
James Dong7a6cea42011-05-06 16:55:39 -07002797void MPEG4Writer::Track::writeStcoBox(bool use32BitOffset) {
2798 mOwner->beginBox(use32BitOffset? "stco": "co64");
2799 mOwner->writeInt32(0); // version=0, flags=0
2800 mOwner->writeInt32(mNumStcoTableEntries);
2801 for (List<off64_t>::iterator it = mChunkOffsets.begin();
2802 it != mChunkOffsets.end(); ++it) {
2803 if (use32BitOffset) {
2804 mOwner->writeInt32(static_cast<int32_t>(*it));
2805 } else {
2806 mOwner->writeInt64((*it));
2807 }
2808 }
2809 mOwner->endBox(); // stco or co64
Andreas Hubere46b7be2009-07-14 16:56:47 -07002810}
2811
James Dong987ab482011-05-11 19:09:25 -07002812void MPEG4Writer::writeUdtaBox() {
2813 beginBox("udta");
2814 writeGeoDataBox();
2815 endBox();
2816}
2817
2818/*
2819 * Geodata is stored according to ISO-6709 standard.
2820 */
2821void MPEG4Writer::writeGeoDataBox() {
2822 beginBox("\xA9xyz");
2823 /*
2824 * For historical reasons, any user data start
2825 * with "\0xA9", must be followed by its assoicated
2826 * language code.
James Dong0741f002011-06-03 17:09:26 -07002827 * 0x0012: text string length
2828 * 0x15c7: lang (locale) code: en
James Dong987ab482011-05-11 19:09:25 -07002829 */
2830 writeInt32(0x001215c7);
2831 writeLatitude(mLatitudex10000);
2832 writeLongitude(mLongitudex10000);
2833 writeInt8(0x2F);
2834 endBox();
2835}
2836
Andreas Hubere46b7be2009-07-14 16:56:47 -07002837} // namespace android