blob: 316f6697484d032dd3f9d6c0c3f185067ed38c2e [file] [log] [blame]
Andreas Huber20111aa2009-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 Dong050b28a2010-04-22 17:27:04 -070017//#define LOG_NDEBUG 0
18#define LOG_TAG "MPEG4Writer"
19#include <utils/Log.h>
20
Andreas Huber20111aa2009-07-14 16:56:47 -070021#include <arpa/inet.h>
22
Andreas Huber20111aa2009-07-14 16:56:47 -070023#include <pthread.h>
James Donga6f61fc2010-10-07 20:20:59 -070024#include <sys/prctl.h>
Andreas Huber20111aa2009-07-14 16:56:47 -070025
James Dong43089da2012-02-09 11:53:57 -080026#include <media/stagefright/foundation/ADebug.h>
Andreas Huber20111aa2009-07-14 16:56:47 -070027#include <media/stagefright/MPEG4Writer.h>
28#include <media/stagefright/MediaBuffer.h>
29#include <media/stagefright/MetaData.h>
Andreas Huber18291bc2009-09-10 14:13:30 -070030#include <media/stagefright/MediaDefs.h>
Andreas Huber03b268e2010-03-19 11:43:15 -070031#include <media/stagefright/MediaErrors.h>
Andreas Huber20111aa2009-07-14 16:56:47 -070032#include <media/stagefright/MediaSource.h>
33#include <media/stagefright/Utils.h>
James Dongd599cd42010-05-11 14:57:02 -070034#include <media/mediarecorder.h>
James Dong07ec0192011-05-14 07:22:40 -070035#include <cutils/properties.h>
James Dong674ebd02010-11-18 20:59:13 -080036#include <sys/types.h>
37#include <sys/stat.h>
38#include <fcntl.h>
39#include <unistd.h>
Andreas Huber20111aa2009-07-14 16:56:47 -070040
Andreas Huber19c9a1e2010-07-01 08:19:52 -070041#include "include/ESDS.h"
42
Andreas Huber20111aa2009-07-14 16:56:47 -070043namespace android {
44
James Dong77e8ae92012-07-17 19:18:29 -070045static const int64_t kMinStreamableFileSizeInBytes = 5 * 1024 * 1024;
James Dong1f90c4b2010-09-02 11:19:11 -070046static const int64_t kMax32BitFileSize = 0x007fffffffLL;
James Dong3266b2c2010-08-06 00:29:03 -070047static const uint8_t kNalUnitTypeSeqParamSet = 0x07;
48static const uint8_t kNalUnitTypePicParamSet = 0x08;
James Dong70ccfd42011-05-06 12:19:04 -070049static const int64_t kInitialDelayTimeUs = 700000LL;
James Dong5b6a01e2010-10-21 17:58:14 -070050
Andreas Huber20111aa2009-07-14 16:56:47 -070051class MPEG4Writer::Track {
52public:
James Dongbc07bcc2011-03-18 11:25:41 -070053 Track(MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId);
James Dong8f5f2fc2010-07-02 11:39:06 -070054
Andreas Huber20111aa2009-07-14 16:56:47 -070055 ~Track();
56
James Dong93d6b102010-06-24 19:04:27 -070057 status_t start(MetaData *params);
James Dong37187912010-08-18 19:10:39 -070058 status_t stop();
59 status_t pause();
Andreas Huber25b13092009-09-09 16:36:12 -070060 bool reachedEOS();
Andreas Huber20111aa2009-07-14 16:56:47 -070061
Andreas Huber3b240d62010-02-02 12:13:30 -080062 int64_t getDurationUs() const;
James Dongd599cd42010-05-11 14:57:02 -070063 int64_t getEstimatedTrackSizeBytes() const;
James Dongb21c5642011-05-06 16:55:39 -070064 void writeTrackHeader(bool use32BitOffset = true);
James Dong1c9747a2010-07-30 17:41:22 -070065 void bufferChunk(int64_t timestampUs);
66 bool isAvc() const { return mIsAvc; }
67 bool isAudio() const { return mIsAudio; }
68 bool isMPEG4() const { return mIsMPEG4; }
James Dongc7fc37a2010-11-16 14:04:54 -080069 void addChunkOffset(off64_t offset);
James Dong70ccfd42011-05-06 12:19:04 -070070 int32_t getTrackId() const { return mTrackId; }
James Dongdedf4142010-08-18 03:32:26 -070071 status_t dump(int fd, const Vector<String16>& args) const;
Andreas Huber20111aa2009-07-14 16:56:47 -070072
73private:
James Dong000e1832012-02-06 23:46:37 -080074 enum {
75 kMaxCttsOffsetTimeUs = 1000000LL, // 1 second
James Dong8c460492012-05-15 10:43:30 -070076 kSampleArraySize = 1000,
James Dong000e1832012-02-06 23:46:37 -080077 };
78
James Dongc620cbd2012-07-27 17:14:44 -070079 // A helper class to handle faster write box with table entries
80 template<class TYPE>
81 struct ListTableEntries {
82 ListTableEntries(uint32_t elementCapacity, uint32_t entryCapacity)
83 : mElementCapacity(elementCapacity),
84 mEntryCapacity(entryCapacity),
85 mTotalNumTableEntries(0),
86 mNumValuesInCurrEntry(0),
87 mCurrTableEntriesElement(NULL) {
88 CHECK_GT(mElementCapacity, 0);
89 CHECK_GT(mEntryCapacity, 0);
90 }
91
92 // Free the allocated memory.
93 ~ListTableEntries() {
94 while (!mTableEntryList.empty()) {
95 typename List<TYPE *>::iterator it = mTableEntryList.begin();
96 delete[] (*it);
97 mTableEntryList.erase(it);
98 }
99 }
100
101 // Replace the value at the given position by the given value.
102 // There must be an existing value at the given position.
103 // @arg value must be in network byte order
104 // @arg pos location the value must be in.
105 void set(const TYPE& value, uint32_t pos) {
James Dong25f0d7b2012-07-30 18:06:12 -0700106 CHECK_LT(pos, mTotalNumTableEntries * mEntryCapacity);
James Dongc620cbd2012-07-27 17:14:44 -0700107
108 typename List<TYPE *>::iterator it = mTableEntryList.begin();
109 uint32_t iterations = (pos / (mElementCapacity * mEntryCapacity));
110 while (it != mTableEntryList.end() && iterations > 0) {
111 ++it;
112 --iterations;
113 }
114 CHECK(it != mTableEntryList.end());
115 CHECK_EQ(iterations, 0);
116
117 (*it)[(pos % (mElementCapacity * mEntryCapacity))] = value;
118 }
119
120 // Get the value at the given position by the given value.
121 // @arg value the retrieved value at the position in network byte order.
122 // @arg pos location the value must be in.
123 // @return true if a value is found.
124 bool get(TYPE& value, uint32_t pos) const {
James Dong25f0d7b2012-07-30 18:06:12 -0700125 if (pos >= mTotalNumTableEntries * mEntryCapacity) {
James Dongc620cbd2012-07-27 17:14:44 -0700126 return false;
127 }
128
129 typename List<TYPE *>::iterator it = mTableEntryList.begin();
130 uint32_t iterations = (pos / (mElementCapacity * mEntryCapacity));
131 while (it != mTableEntryList.end() && iterations > 0) {
132 ++it;
133 --iterations;
134 }
135 CHECK(it != mTableEntryList.end());
136 CHECK_EQ(iterations, 0);
137
138 value = (*it)[(pos % (mElementCapacity * mEntryCapacity))];
139 return true;
140 }
141
142 // Store a single value.
143 // @arg value must be in network byte order.
144 void add(const TYPE& value) {
145 CHECK_LT(mNumValuesInCurrEntry, mElementCapacity);
146 uint32_t nEntries = mTotalNumTableEntries % mElementCapacity;
147 uint32_t nValues = mNumValuesInCurrEntry % mEntryCapacity;
148 if (nEntries == 0 && nValues == 0) {
149 mCurrTableEntriesElement = new TYPE[mEntryCapacity * mElementCapacity];
150 CHECK(mCurrTableEntriesElement != NULL);
151 mTableEntryList.push_back(mCurrTableEntriesElement);
152 }
153
154 uint32_t pos = nEntries * mEntryCapacity + nValues;
155 mCurrTableEntriesElement[pos] = value;
156
157 ++mNumValuesInCurrEntry;
158 if ((mNumValuesInCurrEntry % mEntryCapacity) == 0) {
159 ++mTotalNumTableEntries;
160 mNumValuesInCurrEntry = 0;
161 }
162 }
163
164 // Write out the table entries:
165 // 1. the number of entries goes first
166 // 2. followed by the values in the table enties in order
167 // @arg writer the writer to actual write to the storage
168 void write(MPEG4Writer *writer) const {
169 CHECK_EQ(mNumValuesInCurrEntry % mEntryCapacity, 0);
170 uint32_t nEntries = mTotalNumTableEntries;
171 writer->writeInt32(nEntries);
172 for (typename List<TYPE *>::iterator it = mTableEntryList.begin();
173 it != mTableEntryList.end(); ++it) {
174 CHECK_GT(nEntries, 0);
175 if (nEntries >= mElementCapacity) {
176 writer->write(*it, sizeof(TYPE) * mEntryCapacity, mElementCapacity);
177 nEntries -= mElementCapacity;
178 } else {
179 writer->write(*it, sizeof(TYPE) * mEntryCapacity, nEntries);
180 break;
181 }
182 }
183 }
184
185 // Return the number of entries in the table.
186 uint32_t count() const { return mTotalNumTableEntries; }
187
188 private:
189 uint32_t mElementCapacity; // # entries in an element
190 uint32_t mEntryCapacity; // # of values in each entry
191 uint32_t mTotalNumTableEntries;
192 uint32_t mNumValuesInCurrEntry; // up to mEntryCapacity
193 TYPE *mCurrTableEntriesElement;
194 mutable List<TYPE *> mTableEntryList;
195
196 DISALLOW_EVIL_CONSTRUCTORS(ListTableEntries);
197 };
198
199
200
Andreas Huber20111aa2009-07-14 16:56:47 -0700201 MPEG4Writer *mOwner;
202 sp<MetaData> mMeta;
Andreas Huber693d2712009-08-14 14:37:10 -0700203 sp<MediaSource> mSource;
Andreas Huber20111aa2009-07-14 16:56:47 -0700204 volatile bool mDone;
James Donga7d1a2d2010-06-10 12:28:15 -0700205 volatile bool mPaused;
206 volatile bool mResumed;
James Dongeaae3842011-01-25 12:37:43 -0800207 volatile bool mStarted;
James Dong1c9747a2010-07-30 17:41:22 -0700208 bool mIsAvc;
209 bool mIsAudio;
210 bool mIsMPEG4;
James Dongbc07bcc2011-03-18 11:25:41 -0700211 int32_t mTrackId;
James Dongc5f0c712010-08-05 10:46:13 -0700212 int64_t mTrackDurationUs;
James Dong43ec1df2011-05-06 11:27:59 -0700213 int64_t mMaxChunkDurationUs;
James Donge2595312010-08-02 19:13:40 -0700214
James Donge2595312010-08-02 19:13:40 -0700215 bool mIsRealTimeRecording;
James Dongd599cd42010-05-11 14:57:02 -0700216 int64_t mEstimatedTrackSizeBytes;
James Dong1f90c4b2010-09-02 11:19:11 -0700217 int64_t mMdatSizeBytes;
James Dong8f5f2fc2010-07-02 11:39:06 -0700218 int32_t mTimeScale;
Andreas Huber20111aa2009-07-14 16:56:47 -0700219
220 pthread_t mThread;
221
James Dongbe83c9e2010-04-20 11:50:11 -0700222
James Dong13aec892010-04-21 16:14:15 -0700223 List<MediaBuffer *> mChunkSamples;
James Dong1f90c4b2010-09-02 11:19:11 -0700224
James Dongc620cbd2012-07-27 17:14:44 -0700225 bool mSamplesHaveSameSize;
226 ListTableEntries<uint32_t> *mStszTableEntries;
James Dong13aec892010-04-21 16:14:15 -0700227
James Dongc620cbd2012-07-27 17:14:44 -0700228 ListTableEntries<uint32_t> *mStcoTableEntries;
229 ListTableEntries<off64_t> *mCo64TableEntries;
230 ListTableEntries<uint32_t> *mStscTableEntries;
231 ListTableEntries<uint32_t> *mStssTableEntries;
232 ListTableEntries<uint32_t> *mSttsTableEntries;
233 ListTableEntries<uint32_t> *mCttsTableEntries;
James Dong13aec892010-04-21 16:14:15 -0700234
James Dong000e1832012-02-06 23:46:37 -0800235 int64_t mMinCttsOffsetTimeUs;
236 int64_t mMaxCttsOffsetTimeUs;
James Dong965e4232011-06-07 19:45:54 -0700237
James Dong3266b2c2010-08-06 00:29:03 -0700238 // Sequence parameter set or picture parameter set
239 struct AVCParamSet {
240 AVCParamSet(uint16_t length, const uint8_t *data)
241 : mLength(length), mData(data) {}
242
243 uint16_t mLength;
244 const uint8_t *mData;
245 };
246 List<AVCParamSet> mSeqParamSets;
247 List<AVCParamSet> mPicParamSets;
248 uint8_t mProfileIdc;
249 uint8_t mProfileCompatible;
250 uint8_t mLevelIdc;
251
Andreas Huber20111aa2009-07-14 16:56:47 -0700252 void *mCodecSpecificData;
253 size_t mCodecSpecificDataSize;
Andreas Huber548e3182010-04-09 14:25:46 -0700254 bool mGotAllCodecSpecificData;
James Dong93d6b102010-06-24 19:04:27 -0700255 bool mTrackingProgressStatus;
Andreas Huber20111aa2009-07-14 16:56:47 -0700256
Andreas Huber25b13092009-09-09 16:36:12 -0700257 bool mReachedEOS;
James Dong3c0131f2010-05-13 11:47:36 -0700258 int64_t mStartTimestampUs;
James Dong70ccfd42011-05-06 12:19:04 -0700259 int64_t mStartTimeRealUs;
260 int64_t mFirstSampleTimeRealUs;
James Dong93d6b102010-06-24 19:04:27 -0700261 int64_t mPreviousTrackTimeUs;
262 int64_t mTrackEveryTimeDurationUs;
Andreas Huber25b13092009-09-09 16:36:12 -0700263
James Dong872a4812010-10-03 10:59:26 -0700264 // Update the audio track's drift information.
265 void updateDriftTime(const sp<MetaData>& meta);
266
James Dong000e1832012-02-06 23:46:37 -0800267 int32_t getStartTimeOffsetScaledTime() const;
268
Andreas Huber20111aa2009-07-14 16:56:47 -0700269 static void *ThreadWrapper(void *me);
James Dong37187912010-08-18 19:10:39 -0700270 status_t threadEntry();
Andreas Huber20111aa2009-07-14 16:56:47 -0700271
James Dong3266b2c2010-08-06 00:29:03 -0700272 const uint8_t *parseParamSet(
273 const uint8_t *data, size_t length, int type, size_t *paramSetLen);
274
James Dongb21c5642011-05-06 16:55:39 -0700275 status_t makeAVCCodecSpecificData(const uint8_t *data, size_t size);
276 status_t copyAVCCodecSpecificData(const uint8_t *data, size_t size);
277 status_t parseAVCCodecSpecificData(const uint8_t *data, size_t size);
James Dong215381e2010-06-26 08:24:47 -0700278
279 // Track authoring progress status
James Dongfaed5cd2010-07-15 19:08:20 -0700280 void trackProgressStatus(int64_t timeUs, status_t err = OK);
James Dong93d6b102010-06-24 19:04:27 -0700281 void initTrackingProgressStatus(MetaData *params);
Andreas Huber03b268e2010-03-19 11:43:15 -0700282
Andreas Huber19c9a1e2010-07-01 08:19:52 -0700283 void getCodecSpecificDataFromInputFormatIfPossible();
284
James Dongc0598602010-08-13 14:16:26 -0700285 // Determine the track time scale
286 // If it is an audio track, try to use the sampling rate as
287 // the time scale; however, if user chooses the overwrite
288 // value, the user-supplied time scale will be used.
289 void setTimeScale();
290
James Dong690f5462010-08-19 13:52:47 -0700291 // Simple validation on the codec specific data
292 status_t checkCodecSpecificData() const;
James Dong13f62842010-11-09 11:15:47 -0800293 int32_t mRotation;
James Dong690f5462010-08-19 13:52:47 -0700294
James Dong1f90c4b2010-09-02 11:19:11 -0700295 void updateTrackSizeEstimate();
296 void addOneStscTableEntry(size_t chunkId, size_t sampleId);
297 void addOneStssTableEntry(size_t sampleId);
James Dong79761ab2011-06-06 19:00:40 -0700298
299 // Duration is time scale based
300 void addOneSttsTableEntry(size_t sampleCount, int32_t timescaledDur);
James Dong965e4232011-06-07 19:45:54 -0700301 void addOneCttsTableEntry(size_t sampleCount, int32_t timescaledDur);
James Dong45c254c2011-06-21 17:22:37 -0700302
303 bool isTrackMalFormed() const;
James Dong43ec1df2011-05-06 11:27:59 -0700304 void sendTrackSummary(bool hasMultipleTracks);
James Dong1f90c4b2010-09-02 11:19:11 -0700305
James Dongb21c5642011-05-06 16:55:39 -0700306 // Write the boxes
307 void writeStcoBox(bool use32BitOffset);
308 void writeStscBox();
309 void writeStszBox();
310 void writeStssBox();
311 void writeSttsBox();
James Dong965e4232011-06-07 19:45:54 -0700312 void writeCttsBox();
James Dongb21c5642011-05-06 16:55:39 -0700313 void writeD263Box();
314 void writePaspBox();
315 void writeAvccBox();
316 void writeUrlBox();
317 void writeDrefBox();
318 void writeDinfBox();
319 void writeDamrBox();
Johannes Carlssonefcdf182012-03-08 11:06:13 +0100320 void writeMdhdBox(uint32_t now);
James Dongb21c5642011-05-06 16:55:39 -0700321 void writeSmhdBox();
322 void writeVmhdBox();
323 void writeHdlrBox();
Johannes Carlssonefcdf182012-03-08 11:06:13 +0100324 void writeTkhdBox(uint32_t now);
James Dongb21c5642011-05-06 16:55:39 -0700325 void writeMp4aEsdsBox();
326 void writeMp4vEsdsBox();
327 void writeAudioFourCCBox();
328 void writeVideoFourCCBox();
329 void writeStblBox(bool use32BitOffset);
330
Andreas Huber20111aa2009-07-14 16:56:47 -0700331 Track(const Track &);
332 Track &operator=(const Track &);
333};
334
335MPEG4Writer::MPEG4Writer(const char *filename)
James Dong674ebd02010-11-18 20:59:13 -0800336 : mFd(-1),
337 mInitCheck(NO_INIT),
James Dongb4d53202010-09-02 10:49:55 -0700338 mUse4ByteNalLength(true),
James Dong1acfe862010-06-23 00:18:40 -0700339 mUse32BitOffset(true),
James Donga007e822010-10-04 16:41:53 -0700340 mIsFileSizeLimitExplicitlyRequested(false),
James Donga7d1a2d2010-06-10 12:28:15 -0700341 mPaused(false),
342 mStarted(false),
James Dong411ba422011-07-08 16:51:16 -0700343 mWriterThreadStarted(false),
Andreas Huber20111aa2009-07-14 16:56:47 -0700344 mOffset(0),
James Dong13aec892010-04-21 16:14:15 -0700345 mMdatOffset(0),
James Dong7837c172010-05-07 10:26:24 -0700346 mEstimatedMoovBoxSize(0),
James Dong07b1bb52011-05-11 19:09:25 -0700347 mInterleaveDurationUs(1000000),
348 mLatitudex10000(0),
349 mLongitudex10000(0),
James Dong86b7f472011-05-09 16:56:25 -0700350 mAreGeoTagsAvailable(false),
351 mStartTimeOffsetMs(-1) {
James Dong674ebd02010-11-18 20:59:13 -0800352
Nick Kralevichaf8e8aa2012-06-26 13:32:23 -0700353 mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
James Dong674ebd02010-11-18 20:59:13 -0800354 if (mFd >= 0) {
355 mInitCheck = OK;
356 }
Andreas Huber20111aa2009-07-14 16:56:47 -0700357}
358
Andreas Huber30ab6622009-11-16 15:43:38 -0800359MPEG4Writer::MPEG4Writer(int fd)
James Dong674ebd02010-11-18 20:59:13 -0800360 : mFd(dup(fd)),
361 mInitCheck(mFd < 0? NO_INIT: OK),
James Dongb4d53202010-09-02 10:49:55 -0700362 mUse4ByteNalLength(true),
James Dong1acfe862010-06-23 00:18:40 -0700363 mUse32BitOffset(true),
James Donga007e822010-10-04 16:41:53 -0700364 mIsFileSizeLimitExplicitlyRequested(false),
James Donga7d1a2d2010-06-10 12:28:15 -0700365 mPaused(false),
366 mStarted(false),
James Dong411ba422011-07-08 16:51:16 -0700367 mWriterThreadStarted(false),
Andreas Huber30ab6622009-11-16 15:43:38 -0800368 mOffset(0),
James Dong13aec892010-04-21 16:14:15 -0700369 mMdatOffset(0),
James Dong7837c172010-05-07 10:26:24 -0700370 mEstimatedMoovBoxSize(0),
James Dong07b1bb52011-05-11 19:09:25 -0700371 mInterleaveDurationUs(1000000),
372 mLatitudex10000(0),
373 mLongitudex10000(0),
James Dong86b7f472011-05-09 16:56:25 -0700374 mAreGeoTagsAvailable(false),
375 mStartTimeOffsetMs(-1) {
Andreas Huber30ab6622009-11-16 15:43:38 -0800376}
377
Andreas Huber20111aa2009-07-14 16:56:47 -0700378MPEG4Writer::~MPEG4Writer() {
James Dong8bcc65c2012-02-02 15:07:52 -0800379 reset();
Andreas Huber20111aa2009-07-14 16:56:47 -0700380
James Dong1f90c4b2010-09-02 11:19:11 -0700381 while (!mTracks.empty()) {
382 List<Track *>::iterator it = mTracks.begin();
Andreas Huber20111aa2009-07-14 16:56:47 -0700383 delete *it;
James Dong1f90c4b2010-09-02 11:19:11 -0700384 (*it) = NULL;
385 mTracks.erase(it);
Andreas Huber20111aa2009-07-14 16:56:47 -0700386 }
387 mTracks.clear();
388}
389
James Dongdedf4142010-08-18 03:32:26 -0700390status_t MPEG4Writer::dump(
391 int fd, const Vector<String16>& args) {
392 const size_t SIZE = 256;
393 char buffer[SIZE];
394 String8 result;
395 snprintf(buffer, SIZE, " MPEG4Writer %p\n", this);
396 result.append(buffer);
397 snprintf(buffer, SIZE, " mStarted: %s\n", mStarted? "true": "false");
398 result.append(buffer);
399 ::write(fd, result.string(), result.size());
400 for (List<Track *>::iterator it = mTracks.begin();
401 it != mTracks.end(); ++it) {
402 (*it)->dump(fd, args);
403 }
404 return OK;
405}
406
407status_t MPEG4Writer::Track::dump(
408 int fd, const Vector<String16>& args) const {
409 const size_t SIZE = 256;
410 char buffer[SIZE];
411 String8 result;
412 snprintf(buffer, SIZE, " %s track\n", mIsAudio? "Audio": "Video");
413 result.append(buffer);
414 snprintf(buffer, SIZE, " reached EOS: %s\n",
415 mReachedEOS? "true": "false");
416 result.append(buffer);
James Dongc620cbd2012-07-27 17:14:44 -0700417 snprintf(buffer, SIZE, " frames encoded : %d\n", mStszTableEntries->count());
James Dong13210f32012-07-19 17:12:51 -0700418 result.append(buffer);
419 snprintf(buffer, SIZE, " duration encoded : %lld us\n", mTrackDurationUs);
420 result.append(buffer);
James Dongdedf4142010-08-18 03:32:26 -0700421 ::write(fd, result.string(), result.size());
422 return OK;
423}
424
Andreas Huber2dce41a2010-01-25 15:30:31 -0800425status_t MPEG4Writer::addSource(const sp<MediaSource> &source) {
James Dongbc07bcc2011-03-18 11:25:41 -0700426 Mutex::Autolock l(mLock);
427 if (mStarted) {
Steve Block29357bc2012-01-06 19:20:56 +0000428 ALOGE("Attempt to add source AFTER recording is started");
James Dongbc07bcc2011-03-18 11:25:41 -0700429 return UNKNOWN_ERROR;
430 }
James Dong5883d532013-03-12 10:40:20 -0700431
432 // At most 2 tracks can be supported.
433 if (mTracks.size() >= 2) {
434 ALOGE("Too many tracks (%d) to add", mTracks.size());
435 return ERROR_UNSUPPORTED;
436 }
437
438 CHECK(source.get() != NULL);
439
440 // A track of type other than video or audio is not supported.
441 const char *mime;
442 source->getFormat()->findCString(kKeyMIMEType, &mime);
443 bool isAudio = !strncasecmp(mime, "audio/", 6);
444 bool isVideo = !strncasecmp(mime, "video/", 6);
445 if (!isAudio && !isVideo) {
446 ALOGE("Track (%s) other than video or audio is not supported",
447 mime);
448 return ERROR_UNSUPPORTED;
449 }
450
451 // At this point, we know the track to be added is either
452 // video or audio. Thus, we only need to check whether it
453 // is an audio track or not (if it is not, then it must be
454 // a video track).
455
456 // No more than one video or one audio track is supported.
457 for (List<Track*>::iterator it = mTracks.begin();
458 it != mTracks.end(); ++it) {
459 if ((*it)->isAudio() == isAudio) {
460 ALOGE("%s track already exists", isAudio? "Audio": "Video");
461 return ERROR_UNSUPPORTED;
462 }
463 }
464
465 // This is the first track of either audio or video.
466 // Go ahead to add the track.
James Dong219f1952012-08-07 16:54:22 -0700467 Track *track = new Track(this, source, 1 + mTracks.size());
Andreas Huber20111aa2009-07-14 16:56:47 -0700468 mTracks.push_back(track);
Andreas Huber2dce41a2010-01-25 15:30:31 -0800469
470 return OK;
Andreas Huber20111aa2009-07-14 16:56:47 -0700471}
472
James Dong93d6b102010-06-24 19:04:27 -0700473status_t MPEG4Writer::startTracks(MetaData *params) {
James Dong5883d532013-03-12 10:40:20 -0700474 if (mTracks.empty()) {
475 ALOGE("No source added");
476 return INVALID_OPERATION;
477 }
478
James Donga7d1a2d2010-06-10 12:28:15 -0700479 for (List<Track *>::iterator it = mTracks.begin();
480 it != mTracks.end(); ++it) {
James Dong93d6b102010-06-24 19:04:27 -0700481 status_t err = (*it)->start(params);
James Donga7d1a2d2010-06-10 12:28:15 -0700482
483 if (err != OK) {
484 for (List<Track *>::iterator it2 = mTracks.begin();
485 it2 != it; ++it2) {
486 (*it2)->stop();
487 }
488
489 return err;
490 }
491 }
492 return OK;
493}
494
James Dong2dec2b52010-06-20 08:20:54 -0700495int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) {
496 // This implementation is highly experimental/heurisitic.
497 //
498 // Statistical analysis shows that metadata usually accounts
499 // for a small portion of the total file size, usually < 0.6%.
James Dong2dec2b52010-06-20 08:20:54 -0700500
James Dong78a1a282010-10-19 21:28:47 -0700501 // The default MIN_MOOV_BOX_SIZE is set to 0.6% x 1MB / 2,
James Dong2dec2b52010-06-20 08:20:54 -0700502 // where 1MB is the common file size limit for MMS application.
James Dong78a1a282010-10-19 21:28:47 -0700503 // The default MAX _MOOV_BOX_SIZE value is based on about 3
James Dong2dec2b52010-06-20 08:20:54 -0700504 // minute video recording with a bit rate about 3 Mbps, because
505 // statistics also show that most of the video captured are going
506 // to be less than 3 minutes.
507
508 // If the estimation is wrong, we will pay the price of wasting
509 // some reserved space. This should not happen so often statistically.
510 static const int32_t factor = mUse32BitOffset? 1: 2;
James Dong78a1a282010-10-19 21:28:47 -0700511 static const int64_t MIN_MOOV_BOX_SIZE = 3 * 1024; // 3 KB
James Dong2dec2b52010-06-20 08:20:54 -0700512 static const int64_t MAX_MOOV_BOX_SIZE = (180 * 3000000 * 6LL / 8000);
513 int64_t size = MIN_MOOV_BOX_SIZE;
514
James Dong78a1a282010-10-19 21:28:47 -0700515 // Max file size limit is set
James Donga007e822010-10-04 16:41:53 -0700516 if (mMaxFileSizeLimitBytes != 0 && mIsFileSizeLimitExplicitlyRequested) {
James Dong78a1a282010-10-19 21:28:47 -0700517 size = mMaxFileSizeLimitBytes * 6 / 1000;
518 }
519
520 // Max file duration limit is set
521 if (mMaxFileDurationLimitUs != 0) {
522 if (bitRate > 0) {
523 int64_t size2 =
524 ((mMaxFileDurationLimitUs * bitRate * 6) / 1000 / 8000000);
525 if (mMaxFileSizeLimitBytes != 0 && mIsFileSizeLimitExplicitlyRequested) {
526 // When both file size and duration limits are set,
527 // we use the smaller limit of the two.
528 if (size > size2) {
529 size = size2;
530 }
531 } else {
532 // Only max file duration limit is set
533 size = size2;
534 }
James Dong2dec2b52010-06-20 08:20:54 -0700535 }
536 }
James Dong78a1a282010-10-19 21:28:47 -0700537
James Dong2dec2b52010-06-20 08:20:54 -0700538 if (size < MIN_MOOV_BOX_SIZE) {
539 size = MIN_MOOV_BOX_SIZE;
540 }
541
542 // Any long duration recording will be probably end up with
543 // non-streamable mp4 file.
544 if (size > MAX_MOOV_BOX_SIZE) {
545 size = MAX_MOOV_BOX_SIZE;
546 }
547
Steve Blockdf64d152012-01-04 20:05:49 +0000548 ALOGI("limits: %lld/%lld bytes/us, bit rate: %d bps and the estimated"
James Dong2dec2b52010-06-20 08:20:54 -0700549 " moov size %lld bytes",
550 mMaxFileSizeLimitBytes, mMaxFileDurationLimitUs, bitRate, size);
551 return factor * size;
552}
553
554status_t MPEG4Writer::start(MetaData *param) {
James Dong674ebd02010-11-18 20:59:13 -0800555 if (mInitCheck != OK) {
Andreas Huber25b13092009-09-09 16:36:12 -0700556 return UNKNOWN_ERROR;
Andreas Huber20111aa2009-07-14 16:56:47 -0700557 }
558
James Donga007e822010-10-04 16:41:53 -0700559 /*
560 * Check mMaxFileSizeLimitBytes at the beginning
561 * since mMaxFileSizeLimitBytes may be implicitly
562 * changed later for 32-bit file offset even if
563 * user does not ask to set it explicitly.
564 */
565 if (mMaxFileSizeLimitBytes != 0) {
566 mIsFileSizeLimitExplicitlyRequested = true;
567 }
568
James Dong2dec2b52010-06-20 08:20:54 -0700569 int32_t use64BitOffset;
570 if (param &&
571 param->findInt32(kKey64BitFileOffset, &use64BitOffset) &&
572 use64BitOffset) {
573 mUse32BitOffset = false;
574 }
575
James Dong1f90c4b2010-09-02 11:19:11 -0700576 if (mUse32BitOffset) {
577 // Implicit 32 bit file size limit
578 if (mMaxFileSizeLimitBytes == 0) {
579 mMaxFileSizeLimitBytes = kMax32BitFileSize;
580 }
581
582 // If file size is set to be larger than the 32 bit file
583 // size limit, treat it as an error.
584 if (mMaxFileSizeLimitBytes > kMax32BitFileSize) {
Steve Block5ff1dd52012-01-05 23:22:43 +0000585 ALOGW("32-bit file size limit (%lld bytes) too big. "
James Dongd2518e02010-09-08 15:13:36 -0700586 "It is changed to %lld bytes",
587 mMaxFileSizeLimitBytes, kMax32BitFileSize);
588 mMaxFileSizeLimitBytes = kMax32BitFileSize;
James Dong1f90c4b2010-09-02 11:19:11 -0700589 }
590 }
591
James Dongb4d53202010-09-02 10:49:55 -0700592 int32_t use2ByteNalLength;
593 if (param &&
594 param->findInt32(kKey2ByteNalLength, &use2ByteNalLength) &&
595 use2ByteNalLength) {
596 mUse4ByteNalLength = false;
James Dong2dec2b52010-06-20 08:20:54 -0700597 }
598
James Dong065d1af2010-06-23 16:51:39 -0700599 mStartTimestampUs = -1;
James Dong93d6b102010-06-24 19:04:27 -0700600
James Donga7d1a2d2010-06-10 12:28:15 -0700601 if (mStarted) {
602 if (mPaused) {
603 mPaused = false;
James Dong93d6b102010-06-24 19:04:27 -0700604 return startTracks(param);
James Donga7d1a2d2010-06-10 12:28:15 -0700605 }
606 return OK;
607 }
608
James Dong8f5f2fc2010-07-02 11:39:06 -0700609 if (!param ||
610 !param->findInt32(kKeyTimeScale, &mTimeScale)) {
611 mTimeScale = 1000;
612 }
James Dong43089da2012-02-09 11:53:57 -0800613 CHECK_GT(mTimeScale, 0);
Steve Block3856b092011-10-20 11:56:00 +0100614 ALOGV("movie time scale: %d", mTimeScale);
James Dong8f5f2fc2010-07-02 11:39:06 -0700615
James Dong77e8ae92012-07-17 19:18:29 -0700616 /*
617 * When the requested file size limit is small, the priority
618 * is to meet the file size limit requirement, rather than
James Dongb69d77c2012-12-13 18:58:38 -0800619 * to make the file streamable. mStreamableFile does not tell
620 * whether the actual recorded file is streamable or not.
James Dong77e8ae92012-07-17 19:18:29 -0700621 */
622 mStreamableFile =
623 (mMaxFileSizeLimitBytes != 0 &&
624 mMaxFileSizeLimitBytes >= kMinStreamableFileSizeInBytes);
625
James Dongb69d77c2012-12-13 18:58:38 -0800626 /*
627 * mWriteMoovBoxToMemory is true if the amount of data in moov box is
628 * smaller than the reserved free space at the beginning of a file, AND
629 * when the content of moov box is constructed. Note that video/audio
630 * frame data is always written to the file but not in the memory.
631 *
632 * Before stop()/reset() is called, mWriteMoovBoxToMemory is always
633 * false. When reset() is called at the end of a recording session,
634 * Moov box needs to be constructed.
635 *
636 * 1) Right before a moov box is constructed, mWriteMoovBoxToMemory
637 * to set to mStreamableFile so that if
638 * the file is intended to be streamable, it is set to true;
639 * otherwise, it is set to false. When the value is set to false,
640 * all the content of the moov box is written immediately to
641 * the end of the file. When the value is set to true, all the
642 * content of the moov box is written to an in-memory cache,
643 * mMoovBoxBuffer, util the following condition happens. Note
644 * that the size of the in-memory cache is the same as the
645 * reserved free space at the beginning of the file.
646 *
647 * 2) While the data of the moov box is written to an in-memory
648 * cache, the data size is checked against the reserved space.
649 * If the data size surpasses the reserved space, subsequent moov
650 * data could no longer be hold in the in-memory cache. This also
651 * indicates that the reserved space was too small. At this point,
652 * _all_ moov data must be written to the end of the file.
653 * mWriteMoovBoxToMemory must be set to false to direct the write
654 * to the file.
655 *
656 * 3) If the data size in moov box is smaller than the reserved
657 * space after moov box is completely constructed, the in-memory
658 * cache copy of the moov box is written to the reserved free
659 * space. Thus, immediately after the moov is completedly
660 * constructed, mWriteMoovBoxToMemory is always set to false.
661 */
662 mWriteMoovBoxToMemory = false;
James Dong7837c172010-05-07 10:26:24 -0700663 mMoovBoxBuffer = NULL;
664 mMoovBoxBufferOffset = 0;
665
James Dongb21c5642011-05-06 16:55:39 -0700666 writeFtypBox(param);
Andreas Huber20111aa2009-07-14 16:56:47 -0700667
James Dong7837c172010-05-07 10:26:24 -0700668 mFreeBoxOffset = mOffset;
Andreas Huber20111aa2009-07-14 16:56:47 -0700669
James Dong7837c172010-05-07 10:26:24 -0700670 if (mEstimatedMoovBoxSize == 0) {
James Dong2dec2b52010-06-20 08:20:54 -0700671 int32_t bitRate = -1;
672 if (param) {
673 param->findInt32(kKeyBitRate, &bitRate);
674 }
675 mEstimatedMoovBoxSize = estimateMoovBoxSize(bitRate);
James Dong7837c172010-05-07 10:26:24 -0700676 }
James Dong43089da2012-02-09 11:53:57 -0800677 CHECK_GE(mEstimatedMoovBoxSize, 8);
James Dong77e8ae92012-07-17 19:18:29 -0700678 if (mStreamableFile) {
679 // Reserve a 'free' box only for streamable file
680 lseek64(mFd, mFreeBoxOffset, SEEK_SET);
681 writeInt32(mEstimatedMoovBoxSize);
682 write("free", 4);
683 mMdatOffset = mFreeBoxOffset + mEstimatedMoovBoxSize;
684 } else {
685 mMdatOffset = mOffset;
686 }
James Dong7837c172010-05-07 10:26:24 -0700687
James Dong7837c172010-05-07 10:26:24 -0700688 mOffset = mMdatOffset;
James Dongc7fc37a2010-11-16 14:04:54 -0800689 lseek64(mFd, mMdatOffset, SEEK_SET);
James Dong1acfe862010-06-23 00:18:40 -0700690 if (mUse32BitOffset) {
691 write("????mdat", 8);
692 } else {
693 write("\x00\x00\x00\x01mdat????????", 16);
694 }
James Dong1c9747a2010-07-30 17:41:22 -0700695
696 status_t err = startWriterThread();
James Donga7d1a2d2010-06-10 12:28:15 -0700697 if (err != OK) {
698 return err;
699 }
James Dong1c9747a2010-07-30 17:41:22 -0700700
701 err = startTracks(param);
702 if (err != OK) {
703 return err;
704 }
705
James Donga7d1a2d2010-06-10 12:28:15 -0700706 mStarted = true;
707 return OK;
708}
709
James Dong1f90c4b2010-09-02 11:19:11 -0700710bool MPEG4Writer::use32BitFileOffset() const {
711 return mUse32BitOffset;
712}
713
James Dong37187912010-08-18 19:10:39 -0700714status_t MPEG4Writer::pause() {
James Dong674ebd02010-11-18 20:59:13 -0800715 if (mInitCheck != OK) {
James Dong37187912010-08-18 19:10:39 -0700716 return OK;
James Donga7d1a2d2010-06-10 12:28:15 -0700717 }
718 mPaused = true;
James Dong37187912010-08-18 19:10:39 -0700719 status_t err = OK;
Andreas Huber20111aa2009-07-14 16:56:47 -0700720 for (List<Track *>::iterator it = mTracks.begin();
721 it != mTracks.end(); ++it) {
James Dong37187912010-08-18 19:10:39 -0700722 status_t status = (*it)->pause();
723 if (status != OK) {
724 err = status;
725 }
Andreas Huber20111aa2009-07-14 16:56:47 -0700726 }
James Dong37187912010-08-18 19:10:39 -0700727 return err;
Andreas Huber20111aa2009-07-14 16:56:47 -0700728}
729
James Dong1c9747a2010-07-30 17:41:22 -0700730void MPEG4Writer::stopWriterThread() {
Steve Blockb8a80522011-12-20 16:23:08 +0000731 ALOGD("Stopping writer thread");
James Dong411ba422011-07-08 16:51:16 -0700732 if (!mWriterThreadStarted) {
733 return;
734 }
James Dong1c9747a2010-07-30 17:41:22 -0700735
736 {
737 Mutex::Autolock autolock(mLock);
738
739 mDone = true;
740 mChunkReadyCondition.signal();
741 }
742
743 void *dummy;
744 pthread_join(mThread, &dummy);
James Dong411ba422011-07-08 16:51:16 -0700745 mWriterThreadStarted = false;
Steve Blockb8a80522011-12-20 16:23:08 +0000746 ALOGD("Writer thread stopped");
James Dong1c9747a2010-07-30 17:41:22 -0700747}
748
James Dong13f62842010-11-09 11:15:47 -0800749/*
750 * MP4 file standard defines a composition matrix:
751 * | a b u |
752 * | c d v |
753 * | x y w |
754 *
755 * the element in the matrix is stored in the following
756 * order: {a, b, u, c, d, v, x, y, w},
757 * where a, b, c, d, x, and y is in 16.16 format, while
758 * u, v and w is in 2.30 format.
759 */
760void MPEG4Writer::writeCompositionMatrix(int degrees) {
Steve Block3856b092011-10-20 11:56:00 +0100761 ALOGV("writeCompositionMatrix");
James Dong13f62842010-11-09 11:15:47 -0800762 uint32_t a = 0x00010000;
763 uint32_t b = 0;
764 uint32_t c = 0;
765 uint32_t d = 0x00010000;
766 switch (degrees) {
767 case 0:
768 break;
769 case 90:
770 a = 0;
771 b = 0x00010000;
772 c = 0xFFFF0000;
773 d = 0;
774 break;
775 case 180:
776 a = 0xFFFF0000;
777 d = 0xFFFF0000;
778 break;
779 case 270:
780 a = 0;
781 b = 0xFFFF0000;
782 c = 0x00010000;
783 d = 0;
784 break;
785 default:
786 CHECK(!"Should never reach this unknown rotation");
787 break;
788 }
789
790 writeInt32(a); // a
791 writeInt32(b); // b
792 writeInt32(0); // u
793 writeInt32(c); // c
794 writeInt32(d); // d
795 writeInt32(0); // v
796 writeInt32(0); // x
797 writeInt32(0); // y
798 writeInt32(0x40000000); // w
799}
800
James Dong411ba422011-07-08 16:51:16 -0700801void MPEG4Writer::release() {
802 close(mFd);
803 mFd = -1;
804 mInitCheck = NO_INIT;
805 mStarted = false;
806}
James Dong13f62842010-11-09 11:15:47 -0800807
James Dong8bcc65c2012-02-02 15:07:52 -0800808status_t MPEG4Writer::reset() {
James Dong674ebd02010-11-18 20:59:13 -0800809 if (mInitCheck != OK) {
James Dong37187912010-08-18 19:10:39 -0700810 return OK;
James Dong411ba422011-07-08 16:51:16 -0700811 } else {
812 if (!mWriterThreadStarted ||
813 !mStarted) {
814 if (mWriterThreadStarted) {
815 stopWriterThread();
816 }
817 release();
818 return OK;
819 }
Andreas Huber20111aa2009-07-14 16:56:47 -0700820 }
821
James Dong37187912010-08-18 19:10:39 -0700822 status_t err = OK;
James Dong8f5f2fc2010-07-02 11:39:06 -0700823 int64_t maxDurationUs = 0;
James Dong65b3d762011-01-19 11:50:19 -0800824 int64_t minDurationUs = 0x7fffffffffffffffLL;
Andreas Huber20111aa2009-07-14 16:56:47 -0700825 for (List<Track *>::iterator it = mTracks.begin();
826 it != mTracks.end(); ++it) {
James Dong37187912010-08-18 19:10:39 -0700827 status_t status = (*it)->stop();
828 if (err == OK && status != OK) {
829 err = status;
830 }
Andreas Huber20111aa2009-07-14 16:56:47 -0700831
James Dong8f5f2fc2010-07-02 11:39:06 -0700832 int64_t durationUs = (*it)->getDurationUs();
833 if (durationUs > maxDurationUs) {
834 maxDurationUs = durationUs;
Andreas Huber20111aa2009-07-14 16:56:47 -0700835 }
James Dong65b3d762011-01-19 11:50:19 -0800836 if (durationUs < minDurationUs) {
837 minDurationUs = durationUs;
838 }
839 }
840
841 if (mTracks.size() > 1) {
Steve Blockb8a80522011-12-20 16:23:08 +0000842 ALOGD("Duration from tracks range is [%lld, %lld] us",
James Dong65b3d762011-01-19 11:50:19 -0800843 minDurationUs, maxDurationUs);
Andreas Huber20111aa2009-07-14 16:56:47 -0700844 }
845
James Dong1c9747a2010-07-30 17:41:22 -0700846 stopWriterThread();
James Dong7837c172010-05-07 10:26:24 -0700847
James Dong37187912010-08-18 19:10:39 -0700848 // Do not write out movie header on error.
849 if (err != OK) {
James Dong411ba422011-07-08 16:51:16 -0700850 release();
James Dong37187912010-08-18 19:10:39 -0700851 return err;
852 }
853
Andreas Huber20111aa2009-07-14 16:56:47 -0700854 // Fix up the size of the 'mdat' chunk.
James Dong1acfe862010-06-23 00:18:40 -0700855 if (mUse32BitOffset) {
James Dongc7fc37a2010-11-16 14:04:54 -0800856 lseek64(mFd, mMdatOffset, SEEK_SET);
James Dong1acfe862010-06-23 00:18:40 -0700857 int32_t size = htonl(static_cast<int32_t>(mOffset - mMdatOffset));
James Dongc7fc37a2010-11-16 14:04:54 -0800858 ::write(mFd, &size, 4);
James Dong1acfe862010-06-23 00:18:40 -0700859 } else {
James Dongc7fc37a2010-11-16 14:04:54 -0800860 lseek64(mFd, mMdatOffset + 8, SEEK_SET);
James Dong1acfe862010-06-23 00:18:40 -0700861 int64_t size = mOffset - mMdatOffset;
862 size = hton64(size);
James Dongc7fc37a2010-11-16 14:04:54 -0800863 ::write(mFd, &size, 8);
James Dong1acfe862010-06-23 00:18:40 -0700864 }
James Dongc7fc37a2010-11-16 14:04:54 -0800865 lseek64(mFd, mOffset, SEEK_SET);
Andreas Huber20111aa2009-07-14 16:56:47 -0700866
James Dongb69d77c2012-12-13 18:58:38 -0800867 // Construct moov box now
James Dong7837c172010-05-07 10:26:24 -0700868 mMoovBoxBufferOffset = 0;
James Dongb69d77c2012-12-13 18:58:38 -0800869 mWriteMoovBoxToMemory = mStreamableFile;
870 if (mWriteMoovBoxToMemory) {
871 // There is no need to allocate in-memory cache
872 // for moov box if the file is not streamable.
873
874 mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
875 CHECK(mMoovBoxBuffer != NULL);
876 }
James Dongb21c5642011-05-06 16:55:39 -0700877 writeMoovBox(maxDurationUs);
Andreas Huber20111aa2009-07-14 16:56:47 -0700878
James Dongb69d77c2012-12-13 18:58:38 -0800879 // mWriteMoovBoxToMemory could be set to false in
880 // MPEG4Writer::write() method
881 if (mWriteMoovBoxToMemory) {
882 mWriteMoovBoxToMemory = false;
883 // Content of the moov box is saved in the cache, and the in-memory
884 // moov box needs to be written to the file in a single shot.
885
James Dong43089da2012-02-09 11:53:57 -0800886 CHECK_LE(mMoovBoxBufferOffset + 8, mEstimatedMoovBoxSize);
James Dong7837c172010-05-07 10:26:24 -0700887
888 // Moov box
James Dongc7fc37a2010-11-16 14:04:54 -0800889 lseek64(mFd, mFreeBoxOffset, SEEK_SET);
James Dong7837c172010-05-07 10:26:24 -0700890 mOffset = mFreeBoxOffset;
James Dong674ebd02010-11-18 20:59:13 -0800891 write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset);
James Dong7837c172010-05-07 10:26:24 -0700892
893 // Free box
James Dongc7fc37a2010-11-16 14:04:54 -0800894 lseek64(mFd, mOffset, SEEK_SET);
James Dong7837c172010-05-07 10:26:24 -0700895 writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset);
896 write("free", 4);
James Dongb69d77c2012-12-13 18:58:38 -0800897 } else {
898 ALOGI("The mp4 file will not be streamable.");
899 }
James Dong7837c172010-05-07 10:26:24 -0700900
James Dongb69d77c2012-12-13 18:58:38 -0800901 // Free in-memory cache for moov box
902 if (mMoovBoxBuffer != NULL) {
James Dong7837c172010-05-07 10:26:24 -0700903 free(mMoovBoxBuffer);
904 mMoovBoxBuffer = NULL;
905 mMoovBoxBufferOffset = 0;
906 }
907
Andreas Huber0c891992009-08-26 14:48:20 -0700908 CHECK(mBoxes.empty());
Andreas Huber20111aa2009-07-14 16:56:47 -0700909
James Dong411ba422011-07-08 16:51:16 -0700910 release();
James Dong37187912010-08-18 19:10:39 -0700911 return err;
Andreas Huber20111aa2009-07-14 16:56:47 -0700912}
913
Johannes Carlssonefcdf182012-03-08 11:06:13 +0100914uint32_t MPEG4Writer::getMpeg4Time() {
James Dongb21c5642011-05-06 16:55:39 -0700915 time_t now = time(NULL);
Johannes Carlssonefcdf182012-03-08 11:06:13 +0100916 // MP4 file uses time counting seconds since midnight, Jan. 1, 1904
917 // while time function returns Unix epoch values which starts
918 // at 1970-01-01. Lets add the number of seconds between them
919 uint32_t mpeg4Time = now + (66 * 365 + 17) * (24 * 60 * 60);
920 return mpeg4Time;
921}
922
923void MPEG4Writer::writeMvhdBox(int64_t durationUs) {
924 uint32_t now = getMpeg4Time();
James Dongb21c5642011-05-06 16:55:39 -0700925 beginBox("mvhd");
926 writeInt32(0); // version=0, flags=0
927 writeInt32(now); // creation time
928 writeInt32(now); // modification time
929 writeInt32(mTimeScale); // mvhd timescale
930 int32_t duration = (durationUs * mTimeScale + 5E5) / 1E6;
931 writeInt32(duration);
932 writeInt32(0x10000); // rate: 1.0
933 writeInt16(0x100); // volume
934 writeInt16(0); // reserved
935 writeInt32(0); // reserved
936 writeInt32(0); // reserved
937 writeCompositionMatrix(0); // matrix
938 writeInt32(0); // predefined
939 writeInt32(0); // predefined
940 writeInt32(0); // predefined
941 writeInt32(0); // predefined
942 writeInt32(0); // predefined
943 writeInt32(0); // predefined
944 writeInt32(mTracks.size() + 1); // nextTrackID
945 endBox(); // mvhd
946}
947
948void MPEG4Writer::writeMoovBox(int64_t durationUs) {
949 beginBox("moov");
950 writeMvhdBox(durationUs);
James Dong07b1bb52011-05-11 19:09:25 -0700951 if (mAreGeoTagsAvailable) {
952 writeUdtaBox();
953 }
James Dongb21c5642011-05-06 16:55:39 -0700954 int32_t id = 1;
955 for (List<Track *>::iterator it = mTracks.begin();
956 it != mTracks.end(); ++it, ++id) {
957 (*it)->writeTrackHeader(mUse32BitOffset);
958 }
959 endBox(); // moov
960}
961
James Dong2cf9c502011-05-17 22:39:06 -0700962void MPEG4Writer::writeFtypBox(MetaData *param) {
James Dongb21c5642011-05-06 16:55:39 -0700963 beginBox("ftyp");
964
965 int32_t fileType;
966 if (param && param->findInt32(kKeyFileType, &fileType) &&
967 fileType != OUTPUT_FORMAT_MPEG_4) {
968 writeFourcc("3gp4");
969 } else {
970 writeFourcc("isom");
971 }
972
973 writeInt32(0);
974 writeFourcc("isom");
975 writeFourcc("3gp4");
976 endBox();
977}
978
James Dong07ec0192011-05-14 07:22:40 -0700979static bool isTestModeEnabled() {
980#if (PROPERTY_VALUE_MAX < 5)
981#error "PROPERTY_VALUE_MAX must be at least 5"
982#endif
983
984 // Test mode is enabled only if rw.media.record.test system
985 // property is enabled.
986 char value[PROPERTY_VALUE_MAX];
987 if (property_get("rw.media.record.test", value, NULL) &&
988 (!strcasecmp(value, "true") || !strcasecmp(value, "1"))) {
989 return true;
990 }
991 return false;
992}
993
James Dong70ccfd42011-05-06 12:19:04 -0700994void MPEG4Writer::sendSessionSummary() {
James Dong07ec0192011-05-14 07:22:40 -0700995 // Send session summary only if test mode is enabled
996 if (!isTestModeEnabled()) {
997 return;
998 }
999
James Dong70ccfd42011-05-06 12:19:04 -07001000 for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
1001 it != mChunkInfos.end(); ++it) {
1002 int trackNum = it->mTrack->getTrackId() << 28;
1003 notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
1004 trackNum | MEDIA_RECORDER_TRACK_INTER_CHUNK_TIME_MS,
1005 it->mMaxInterChunkDurUs);
1006 }
1007}
1008
James Dong13aec892010-04-21 16:14:15 -07001009status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) {
1010 mInterleaveDurationUs = durationUs;
1011 return OK;
1012}
Andreas Huber20111aa2009-07-14 16:56:47 -07001013
James Dong13aec892010-04-21 16:14:15 -07001014void MPEG4Writer::lock() {
1015 mLock.lock();
1016}
1017
1018void MPEG4Writer::unlock() {
1019 mLock.unlock();
1020}
1021
James Dongc7fc37a2010-11-16 14:04:54 -08001022off64_t MPEG4Writer::addSample_l(MediaBuffer *buffer) {
1023 off64_t old_offset = mOffset;
Andreas Huber20111aa2009-07-14 16:56:47 -07001024
James Dongc7fc37a2010-11-16 14:04:54 -08001025 ::write(mFd,
1026 (const uint8_t *)buffer->data() + buffer->range_offset(),
1027 buffer->range_length());
Andreas Huber20111aa2009-07-14 16:56:47 -07001028
1029 mOffset += buffer->range_length();
1030
1031 return old_offset;
1032}
1033
Andreas Huber03b268e2010-03-19 11:43:15 -07001034static void StripStartcode(MediaBuffer *buffer) {
1035 if (buffer->range_length() < 4) {
1036 return;
1037 }
1038
1039 const uint8_t *ptr =
1040 (const uint8_t *)buffer->data() + buffer->range_offset();
1041
1042 if (!memcmp(ptr, "\x00\x00\x00\x01", 4)) {
1043 buffer->set_range(
1044 buffer->range_offset() + 4, buffer->range_length() - 4);
1045 }
1046}
1047
James Dongc7fc37a2010-11-16 14:04:54 -08001048off64_t MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
1049 off64_t old_offset = mOffset;
Andreas Huber30ab6622009-11-16 15:43:38 -08001050
1051 size_t length = buffer->range_length();
Andreas Huber03b268e2010-03-19 11:43:15 -07001052
James Dongb4d53202010-09-02 10:49:55 -07001053 if (mUse4ByteNalLength) {
1054 uint8_t x = length >> 24;
James Dongc7fc37a2010-11-16 14:04:54 -08001055 ::write(mFd, &x, 1);
James Dongb4d53202010-09-02 10:49:55 -07001056 x = (length >> 16) & 0xff;
James Dongc7fc37a2010-11-16 14:04:54 -08001057 ::write(mFd, &x, 1);
James Dongb4d53202010-09-02 10:49:55 -07001058 x = (length >> 8) & 0xff;
James Dongc7fc37a2010-11-16 14:04:54 -08001059 ::write(mFd, &x, 1);
James Dongb4d53202010-09-02 10:49:55 -07001060 x = length & 0xff;
James Dongc7fc37a2010-11-16 14:04:54 -08001061 ::write(mFd, &x, 1);
Andreas Huber30ab6622009-11-16 15:43:38 -08001062
James Dongc7fc37a2010-11-16 14:04:54 -08001063 ::write(mFd,
1064 (const uint8_t *)buffer->data() + buffer->range_offset(),
1065 length);
1066
James Dongb4d53202010-09-02 10:49:55 -07001067 mOffset += length + 4;
1068 } else {
James Dong43089da2012-02-09 11:53:57 -08001069 CHECK_LT(length, 65536);
Andreas Huber30ab6622009-11-16 15:43:38 -08001070
James Dongb4d53202010-09-02 10:49:55 -07001071 uint8_t x = length >> 8;
James Dongc7fc37a2010-11-16 14:04:54 -08001072 ::write(mFd, &x, 1);
James Dongb4d53202010-09-02 10:49:55 -07001073 x = length & 0xff;
James Dongc7fc37a2010-11-16 14:04:54 -08001074 ::write(mFd, &x, 1);
1075 ::write(mFd, (const uint8_t *)buffer->data() + buffer->range_offset(), length);
James Dongb4d53202010-09-02 10:49:55 -07001076 mOffset += length + 2;
1077 }
Andreas Huber30ab6622009-11-16 15:43:38 -08001078
1079 return old_offset;
1080}
1081
James Dong7837c172010-05-07 10:26:24 -07001082size_t MPEG4Writer::write(
James Dong674ebd02010-11-18 20:59:13 -08001083 const void *ptr, size_t size, size_t nmemb) {
James Dong7837c172010-05-07 10:26:24 -07001084
1085 const size_t bytes = size * nmemb;
1086 if (mWriteMoovBoxToMemory) {
James Dongb69d77c2012-12-13 18:58:38 -08001087
James Dongc7fc37a2010-11-16 14:04:54 -08001088 off64_t moovBoxSize = 8 + mMoovBoxBufferOffset + bytes;
James Dong1acfe862010-06-23 00:18:40 -07001089 if (moovBoxSize > mEstimatedMoovBoxSize) {
James Dongb69d77c2012-12-13 18:58:38 -08001090 // The reserved moov box at the beginning of the file
1091 // is not big enough. Moov box should be written to
1092 // the end of the file from now on, but not to the
1093 // in-memory cache.
1094
1095 // We write partial moov box that is in the memory to
1096 // the file first.
James Dongc7fc37a2010-11-16 14:04:54 -08001097 for (List<off64_t>::iterator it = mBoxes.begin();
James Dong7837c172010-05-07 10:26:24 -07001098 it != mBoxes.end(); ++it) {
1099 (*it) += mOffset;
1100 }
James Dong674ebd02010-11-18 20:59:13 -08001101 lseek64(mFd, mOffset, SEEK_SET);
1102 ::write(mFd, mMoovBoxBuffer, mMoovBoxBufferOffset);
James Dongb69d77c2012-12-13 18:58:38 -08001103 ::write(mFd, ptr, bytes);
James Dong7837c172010-05-07 10:26:24 -07001104 mOffset += (bytes + mMoovBoxBufferOffset);
James Dongb69d77c2012-12-13 18:58:38 -08001105
1106 // All subsequent moov box content will be written
1107 // to the end of the file.
James Dong7837c172010-05-07 10:26:24 -07001108 mWriteMoovBoxToMemory = false;
James Dong7837c172010-05-07 10:26:24 -07001109 } else {
1110 memcpy(mMoovBoxBuffer + mMoovBoxBufferOffset, ptr, bytes);
1111 mMoovBoxBufferOffset += bytes;
1112 }
1113 } else {
James Dong674ebd02010-11-18 20:59:13 -08001114 ::write(mFd, ptr, size * nmemb);
James Dong7837c172010-05-07 10:26:24 -07001115 mOffset += bytes;
1116 }
1117 return bytes;
1118}
1119
Andreas Huber20111aa2009-07-14 16:56:47 -07001120void MPEG4Writer::beginBox(const char *fourcc) {
Andreas Huber0c891992009-08-26 14:48:20 -07001121 CHECK_EQ(strlen(fourcc), 4);
Andreas Huber20111aa2009-07-14 16:56:47 -07001122
James Dong7837c172010-05-07 10:26:24 -07001123 mBoxes.push_back(mWriteMoovBoxToMemory?
1124 mMoovBoxBufferOffset: mOffset);
Andreas Huber20111aa2009-07-14 16:56:47 -07001125
1126 writeInt32(0);
1127 writeFourcc(fourcc);
1128}
1129
1130void MPEG4Writer::endBox() {
Andreas Huber0c891992009-08-26 14:48:20 -07001131 CHECK(!mBoxes.empty());
Andreas Huber20111aa2009-07-14 16:56:47 -07001132
James Dongc7fc37a2010-11-16 14:04:54 -08001133 off64_t offset = *--mBoxes.end();
Andreas Huber20111aa2009-07-14 16:56:47 -07001134 mBoxes.erase(--mBoxes.end());
1135
James Dong7837c172010-05-07 10:26:24 -07001136 if (mWriteMoovBoxToMemory) {
1137 int32_t x = htonl(mMoovBoxBufferOffset - offset);
1138 memcpy(mMoovBoxBuffer + offset, &x, 4);
1139 } else {
James Dongc7fc37a2010-11-16 14:04:54 -08001140 lseek64(mFd, offset, SEEK_SET);
James Dong7837c172010-05-07 10:26:24 -07001141 writeInt32(mOffset - offset);
1142 mOffset -= 4;
James Dongc7fc37a2010-11-16 14:04:54 -08001143 lseek64(mFd, mOffset, SEEK_SET);
James Dong7837c172010-05-07 10:26:24 -07001144 }
Andreas Huber20111aa2009-07-14 16:56:47 -07001145}
1146
1147void MPEG4Writer::writeInt8(int8_t x) {
James Dong674ebd02010-11-18 20:59:13 -08001148 write(&x, 1, 1);
Andreas Huber20111aa2009-07-14 16:56:47 -07001149}
1150
1151void MPEG4Writer::writeInt16(int16_t x) {
1152 x = htons(x);
James Dong674ebd02010-11-18 20:59:13 -08001153 write(&x, 1, 2);
Andreas Huber20111aa2009-07-14 16:56:47 -07001154}
1155
1156void MPEG4Writer::writeInt32(int32_t x) {
1157 x = htonl(x);
James Dong674ebd02010-11-18 20:59:13 -08001158 write(&x, 1, 4);
Andreas Huber20111aa2009-07-14 16:56:47 -07001159}
1160
1161void MPEG4Writer::writeInt64(int64_t x) {
1162 x = hton64(x);
James Dong674ebd02010-11-18 20:59:13 -08001163 write(&x, 1, 8);
Andreas Huber20111aa2009-07-14 16:56:47 -07001164}
1165
1166void MPEG4Writer::writeCString(const char *s) {
1167 size_t n = strlen(s);
James Dong674ebd02010-11-18 20:59:13 -08001168 write(s, 1, n + 1);
Andreas Huber20111aa2009-07-14 16:56:47 -07001169}
1170
1171void MPEG4Writer::writeFourcc(const char *s) {
Andreas Huber0c891992009-08-26 14:48:20 -07001172 CHECK_EQ(strlen(s), 4);
James Dong674ebd02010-11-18 20:59:13 -08001173 write(s, 1, 4);
Andreas Huber20111aa2009-07-14 16:56:47 -07001174}
1175
James Dong07b1bb52011-05-11 19:09:25 -07001176
1177// Written in +/-DD.DDDD format
1178void MPEG4Writer::writeLatitude(int degreex10000) {
1179 bool isNegative = (degreex10000 < 0);
1180 char sign = isNegative? '-': '+';
1181
1182 // Handle the whole part
1183 char str[9];
1184 int wholePart = degreex10000 / 10000;
1185 if (wholePart == 0) {
1186 snprintf(str, 5, "%c%.2d.", sign, wholePart);
1187 } else {
1188 snprintf(str, 5, "%+.2d.", wholePart);
1189 }
1190
1191 // Handle the fractional part
1192 int fractionalPart = degreex10000 - (wholePart * 10000);
1193 if (fractionalPart < 0) {
1194 fractionalPart = -fractionalPart;
1195 }
1196 snprintf(&str[4], 5, "%.4d", fractionalPart);
1197
1198 // Do not write the null terminator
1199 write(str, 1, 8);
1200}
1201
1202// Written in +/- DDD.DDDD format
1203void MPEG4Writer::writeLongitude(int degreex10000) {
1204 bool isNegative = (degreex10000 < 0);
1205 char sign = isNegative? '-': '+';
1206
1207 // Handle the whole part
1208 char str[10];
1209 int wholePart = degreex10000 / 10000;
1210 if (wholePart == 0) {
1211 snprintf(str, 6, "%c%.3d.", sign, wholePart);
1212 } else {
1213 snprintf(str, 6, "%+.3d.", wholePart);
1214 }
1215
1216 // Handle the fractional part
1217 int fractionalPart = degreex10000 - (wholePart * 10000);
1218 if (fractionalPart < 0) {
1219 fractionalPart = -fractionalPart;
1220 }
1221 snprintf(&str[5], 5, "%.4d", fractionalPart);
1222
1223 // Do not write the null terminator
1224 write(str, 1, 9);
1225}
1226
1227/*
1228 * Geodata is stored according to ISO-6709 standard.
1229 * latitudex10000 is latitude in degrees times 10000, and
1230 * longitudex10000 is longitude in degrees times 10000.
1231 * The range for the latitude is in [-90, +90], and
1232 * The range for the longitude is in [-180, +180]
1233 */
1234status_t MPEG4Writer::setGeoData(int latitudex10000, int longitudex10000) {
1235 // Is latitude or longitude out of range?
1236 if (latitudex10000 < -900000 || latitudex10000 > 900000 ||
1237 longitudex10000 < -1800000 || longitudex10000 > 1800000) {
1238 return BAD_VALUE;
1239 }
1240
1241 mLatitudex10000 = latitudex10000;
1242 mLongitudex10000 = longitudex10000;
1243 mAreGeoTagsAvailable = true;
1244 return OK;
1245}
1246
Andreas Huber20111aa2009-07-14 16:56:47 -07001247void MPEG4Writer::write(const void *data, size_t size) {
James Dong674ebd02010-11-18 20:59:13 -08001248 write(data, 1, size);
Andreas Huber20111aa2009-07-14 16:56:47 -07001249}
1250
James Dong78a1a282010-10-19 21:28:47 -07001251bool MPEG4Writer::isFileStreamable() const {
1252 return mStreamableFile;
1253}
1254
James Dongd599cd42010-05-11 14:57:02 -07001255bool MPEG4Writer::exceedsFileSizeLimit() {
1256 // No limit
1257 if (mMaxFileSizeLimitBytes == 0) {
1258 return false;
1259 }
1260
James Dong956c5532010-05-14 15:45:22 -07001261 int64_t nTotalBytesEstimate = static_cast<int64_t>(mEstimatedMoovBoxSize);
James Dongd599cd42010-05-11 14:57:02 -07001262 for (List<Track *>::iterator it = mTracks.begin();
1263 it != mTracks.end(); ++it) {
1264 nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
1265 }
James Dong1f90c4b2010-09-02 11:19:11 -07001266
James Dong77e8ae92012-07-17 19:18:29 -07001267 if (!mStreamableFile) {
1268 // Add 1024 bytes as error tolerance
1269 return nTotalBytesEstimate + 1024 >= mMaxFileSizeLimitBytes;
1270 }
James Dongacd234b2010-11-30 18:18:08 -08001271 // Be conservative in the estimate: do not exceed 95% of
1272 // the target file limit. For small target file size limit, though,
1273 // this will not help.
1274 return (nTotalBytesEstimate >= (95 * mMaxFileSizeLimitBytes) / 100);
James Dongd599cd42010-05-11 14:57:02 -07001275}
1276
1277bool MPEG4Writer::exceedsFileDurationLimit() {
1278 // No limit
1279 if (mMaxFileDurationLimitUs == 0) {
1280 return false;
1281 }
1282
1283 for (List<Track *>::iterator it = mTracks.begin();
1284 it != mTracks.end(); ++it) {
1285 if ((*it)->getDurationUs() >= mMaxFileDurationLimitUs) {
1286 return true;
1287 }
1288 }
1289 return false;
1290}
1291
Andreas Huber25b13092009-09-09 16:36:12 -07001292bool MPEG4Writer::reachedEOS() {
1293 bool allDone = true;
1294 for (List<Track *>::iterator it = mTracks.begin();
1295 it != mTracks.end(); ++it) {
1296 if (!(*it)->reachedEOS()) {
1297 allDone = false;
1298 break;
1299 }
1300 }
1301
1302 return allDone;
1303}
1304
James Dongf60cafe2010-06-19 09:04:18 -07001305void MPEG4Writer::setStartTimestampUs(int64_t timeUs) {
Steve Blockdf64d152012-01-04 20:05:49 +00001306 ALOGI("setStartTimestampUs: %lld", timeUs);
James Dong43089da2012-02-09 11:53:57 -08001307 CHECK_GE(timeUs, 0ll);
James Dong3c0131f2010-05-13 11:47:36 -07001308 Mutex::Autolock autoLock(mLock);
James Dong065d1af2010-06-23 16:51:39 -07001309 if (mStartTimestampUs < 0 || mStartTimestampUs > timeUs) {
James Dongf60cafe2010-06-19 09:04:18 -07001310 mStartTimestampUs = timeUs;
Steve Blockdf64d152012-01-04 20:05:49 +00001311 ALOGI("Earliest track starting time: %lld", mStartTimestampUs);
James Dong3c0131f2010-05-13 11:47:36 -07001312 }
James Dong3c0131f2010-05-13 11:47:36 -07001313}
1314
James Dongf60cafe2010-06-19 09:04:18 -07001315int64_t MPEG4Writer::getStartTimestampUs() {
James Dong3c0131f2010-05-13 11:47:36 -07001316 Mutex::Autolock autoLock(mLock);
1317 return mStartTimestampUs;
1318}
1319
James Dong58ae9c52010-06-22 11:27:37 -07001320size_t MPEG4Writer::numTracks() {
1321 Mutex::Autolock autolock(mLock);
1322 return mTracks.size();
1323}
1324
Andreas Huber20111aa2009-07-14 16:56:47 -07001325////////////////////////////////////////////////////////////////////////////////
1326
1327MPEG4Writer::Track::Track(
James Dongbc07bcc2011-03-18 11:25:41 -07001328 MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId)
Andreas Huber20111aa2009-07-14 16:56:47 -07001329 : mOwner(owner),
Andreas Huber25b13092009-09-09 16:36:12 -07001330 mMeta(source->getFormat()),
Andreas Huber20111aa2009-07-14 16:56:47 -07001331 mSource(source),
1332 mDone(false),
James Donga7d1a2d2010-06-10 12:28:15 -07001333 mPaused(false),
1334 mResumed(false),
James Dongeaae3842011-01-25 12:37:43 -08001335 mStarted(false),
James Dongbc07bcc2011-03-18 11:25:41 -07001336 mTrackId(trackId),
James Dongc5f0c712010-08-05 10:46:13 -07001337 mTrackDurationUs(0),
James Dong956c5532010-05-14 15:45:22 -07001338 mEstimatedTrackSizeBytes(0),
James Dongbe83c9e2010-04-20 11:50:11 -07001339 mSamplesHaveSameSize(true),
James Dongc620cbd2012-07-27 17:14:44 -07001340 mStszTableEntries(new ListTableEntries<uint32_t>(1000, 1)),
1341 mStcoTableEntries(new ListTableEntries<uint32_t>(1000, 1)),
1342 mCo64TableEntries(new ListTableEntries<off64_t>(1000, 1)),
1343 mStscTableEntries(new ListTableEntries<uint32_t>(1000, 3)),
1344 mStssTableEntries(new ListTableEntries<uint32_t>(1000, 1)),
1345 mSttsTableEntries(new ListTableEntries<uint32_t>(1000, 2)),
1346 mCttsTableEntries(new ListTableEntries<uint32_t>(1000, 2)),
Andreas Huber20111aa2009-07-14 16:56:47 -07001347 mCodecSpecificData(NULL),
Andreas Huber25b13092009-09-09 16:36:12 -07001348 mCodecSpecificDataSize(0),
Andreas Huber548e3182010-04-09 14:25:46 -07001349 mGotAllCodecSpecificData(false),
James Dong13f62842010-11-09 11:15:47 -08001350 mReachedEOS(false),
1351 mRotation(0) {
Andreas Huber19c9a1e2010-07-01 08:19:52 -07001352 getCodecSpecificDataFromInputFormatIfPossible();
James Dong8f5f2fc2010-07-02 11:39:06 -07001353
James Dong1c9747a2010-07-30 17:41:22 -07001354 const char *mime;
1355 mMeta->findCString(kKeyMIMEType, &mime);
1356 mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
1357 mIsAudio = !strncasecmp(mime, "audio/", 6);
1358 mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
1359 !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
1360
James Dongc0598602010-08-13 14:16:26 -07001361 setTimeScale();
1362}
1363
James Dong1f90c4b2010-09-02 11:19:11 -07001364void MPEG4Writer::Track::updateTrackSizeEstimate() {
1365
James Dongc620cbd2012-07-27 17:14:44 -07001366 uint32_t stcoBoxCount = (mOwner->use32BitFileOffset()
1367 ? mStcoTableEntries->count()
1368 : mCo64TableEntries->count());
1369 int64_t stcoBoxSizeBytes = stcoBoxCount * 4;
1370 int64_t stszBoxSizeBytes = mSamplesHaveSameSize? 4: (mStszTableEntries->count() * 4);
James Dong1f90c4b2010-09-02 11:19:11 -07001371
James Dong78a1a282010-10-19 21:28:47 -07001372 mEstimatedTrackSizeBytes = mMdatSizeBytes; // media data size
1373 if (!mOwner->isFileStreamable()) {
1374 // Reserved free space is not large enough to hold
1375 // all meta data and thus wasted.
James Dongc620cbd2012-07-27 17:14:44 -07001376 mEstimatedTrackSizeBytes += mStscTableEntries->count() * 12 + // stsc box size
1377 mStssTableEntries->count() * 4 + // stss box size
1378 mSttsTableEntries->count() * 8 + // stts box size
1379 mCttsTableEntries->count() * 8 + // ctts box size
James Dong78a1a282010-10-19 21:28:47 -07001380 stcoBoxSizeBytes + // stco box size
1381 stszBoxSizeBytes; // stsz box size
1382 }
James Dong1f90c4b2010-09-02 11:19:11 -07001383}
1384
1385void MPEG4Writer::Track::addOneStscTableEntry(
1386 size_t chunkId, size_t sampleId) {
1387
James Dongc620cbd2012-07-27 17:14:44 -07001388 mStscTableEntries->add(htonl(chunkId));
1389 mStscTableEntries->add(htonl(sampleId));
1390 mStscTableEntries->add(htonl(1));
James Dong1f90c4b2010-09-02 11:19:11 -07001391}
1392
1393void MPEG4Writer::Track::addOneStssTableEntry(size_t sampleId) {
James Dongc620cbd2012-07-27 17:14:44 -07001394 mStssTableEntries->add(htonl(sampleId));
James Dong1f90c4b2010-09-02 11:19:11 -07001395}
1396
1397void MPEG4Writer::Track::addOneSttsTableEntry(
James Dong79761ab2011-06-06 19:00:40 -07001398 size_t sampleCount, int32_t duration) {
James Dong1f90c4b2010-09-02 11:19:11 -07001399
James Dong5a217fb2011-08-09 11:14:57 -07001400 if (duration == 0) {
Steve Block5ff1dd52012-01-05 23:22:43 +00001401 ALOGW("0-duration samples found: %d", sampleCount);
James Dong5a217fb2011-08-09 11:14:57 -07001402 }
James Dongc620cbd2012-07-27 17:14:44 -07001403 mSttsTableEntries->add(htonl(sampleCount));
1404 mSttsTableEntries->add(htonl(duration));
James Dong1f90c4b2010-09-02 11:19:11 -07001405}
1406
James Dong965e4232011-06-07 19:45:54 -07001407void MPEG4Writer::Track::addOneCttsTableEntry(
1408 size_t sampleCount, int32_t duration) {
1409
1410 if (mIsAudio) {
1411 return;
1412 }
James Dongc620cbd2012-07-27 17:14:44 -07001413 mCttsTableEntries->add(htonl(sampleCount));
1414 mCttsTableEntries->add(htonl(duration));
James Dong965e4232011-06-07 19:45:54 -07001415}
1416
James Dongc7fc37a2010-11-16 14:04:54 -08001417void MPEG4Writer::Track::addChunkOffset(off64_t offset) {
James Dongc620cbd2012-07-27 17:14:44 -07001418 if (mOwner->use32BitFileOffset()) {
1419 uint32_t value = offset;
1420 mStcoTableEntries->add(htonl(value));
1421 } else {
1422 mCo64TableEntries->add(hton64(offset));
1423 }
James Dong1f90c4b2010-09-02 11:19:11 -07001424}
1425
James Dongc0598602010-08-13 14:16:26 -07001426void MPEG4Writer::Track::setTimeScale() {
Steve Block3856b092011-10-20 11:56:00 +01001427 ALOGV("setTimeScale");
James Dongc0598602010-08-13 14:16:26 -07001428 // Default time scale
1429 mTimeScale = 90000;
1430
1431 if (mIsAudio) {
1432 // Use the sampling rate as the default time scale for audio track.
1433 int32_t sampleRate;
1434 bool success = mMeta->findInt32(kKeySampleRate, &sampleRate);
1435 CHECK(success);
1436 mTimeScale = sampleRate;
1437 }
1438
1439 // If someone would like to overwrite the timescale, use user-supplied value.
1440 int32_t timeScale;
1441 if (mMeta->findInt32(kKeyTimeScale, &timeScale)) {
1442 mTimeScale = timeScale;
1443 }
1444
James Dong43089da2012-02-09 11:53:57 -08001445 CHECK_GT(mTimeScale, 0);
Andreas Huber19c9a1e2010-07-01 08:19:52 -07001446}
1447
1448void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() {
1449 const char *mime;
1450 CHECK(mMeta->findCString(kKeyMIMEType, &mime));
1451
1452 if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
1453 uint32_t type;
1454 const void *data;
1455 size_t size;
1456 if (mMeta->findData(kKeyAVCC, &type, &data, &size)) {
1457 mCodecSpecificData = malloc(size);
1458 mCodecSpecificDataSize = size;
1459 memcpy(mCodecSpecificData, data, size);
1460 mGotAllCodecSpecificData = true;
1461 }
1462 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
1463 || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
1464 uint32_t type;
1465 const void *data;
1466 size_t size;
1467 if (mMeta->findData(kKeyESDS, &type, &data, &size)) {
1468 ESDS esds(data, size);
1469 if (esds.getCodecSpecificInfo(&data, &size) == OK) {
1470 mCodecSpecificData = malloc(size);
1471 mCodecSpecificDataSize = size;
1472 memcpy(mCodecSpecificData, data, size);
1473 mGotAllCodecSpecificData = true;
1474 }
1475 }
1476 }
Andreas Huber20111aa2009-07-14 16:56:47 -07001477}
1478
1479MPEG4Writer::Track::~Track() {
1480 stop();
1481
James Dongc620cbd2012-07-27 17:14:44 -07001482 delete mStszTableEntries;
1483 delete mStcoTableEntries;
1484 delete mCo64TableEntries;
1485 delete mStscTableEntries;
1486 delete mSttsTableEntries;
1487 delete mStssTableEntries;
1488 delete mCttsTableEntries;
1489
1490 mStszTableEntries = NULL;
1491 mStcoTableEntries = NULL;
1492 mCo64TableEntries = NULL;
1493 mStscTableEntries = NULL;
1494 mSttsTableEntries = NULL;
1495 mStssTableEntries = NULL;
1496 mCttsTableEntries = NULL;
1497
Andreas Huber20111aa2009-07-14 16:56:47 -07001498 if (mCodecSpecificData != NULL) {
1499 free(mCodecSpecificData);
1500 mCodecSpecificData = NULL;
1501 }
1502}
1503
James Dong93d6b102010-06-24 19:04:27 -07001504void MPEG4Writer::Track::initTrackingProgressStatus(MetaData *params) {
Steve Block3856b092011-10-20 11:56:00 +01001505 ALOGV("initTrackingProgressStatus");
James Dong93d6b102010-06-24 19:04:27 -07001506 mPreviousTrackTimeUs = -1;
1507 mTrackingProgressStatus = false;
1508 mTrackEveryTimeDurationUs = 0;
James Dong93d6b102010-06-24 19:04:27 -07001509 {
1510 int64_t timeUs;
1511 if (params && params->findInt64(kKeyTrackTimeStatus, &timeUs)) {
Steve Block3856b092011-10-20 11:56:00 +01001512 ALOGV("Receive request to track progress status for every %lld us", timeUs);
James Dong93d6b102010-06-24 19:04:27 -07001513 mTrackEveryTimeDurationUs = timeUs;
1514 mTrackingProgressStatus = true;
1515 }
1516 }
James Dong93d6b102010-06-24 19:04:27 -07001517}
1518
James Dong1c9747a2010-07-30 17:41:22 -07001519// static
1520void *MPEG4Writer::ThreadWrapper(void *me) {
Steve Block3856b092011-10-20 11:56:00 +01001521 ALOGV("ThreadWrapper: %p", me);
James Dong1c9747a2010-07-30 17:41:22 -07001522 MPEG4Writer *writer = static_cast<MPEG4Writer *>(me);
1523 writer->threadFunc();
1524 return NULL;
1525}
1526
1527void MPEG4Writer::bufferChunk(const Chunk& chunk) {
Steve Block3856b092011-10-20 11:56:00 +01001528 ALOGV("bufferChunk: %p", chunk.mTrack);
James Dong1c9747a2010-07-30 17:41:22 -07001529 Mutex::Autolock autolock(mLock);
1530 CHECK_EQ(mDone, false);
1531
1532 for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
1533 it != mChunkInfos.end(); ++it) {
1534
1535 if (chunk.mTrack == it->mTrack) { // Found owner
1536 it->mChunks.push_back(chunk);
1537 mChunkReadyCondition.signal();
1538 return;
1539 }
1540 }
1541
James Dong43089da2012-02-09 11:53:57 -08001542 CHECK(!"Received a chunk for a unknown track");
James Dong1c9747a2010-07-30 17:41:22 -07001543}
1544
James Dongfcac8fa2011-02-09 14:00:55 -08001545void MPEG4Writer::writeChunkToFile(Chunk* chunk) {
Steve Block3856b092011-10-20 11:56:00 +01001546 ALOGV("writeChunkToFile: %lld from %s track",
Pannag Sanketi5410afc2011-08-26 19:17:20 -07001547 chunk->mTimeStampUs, chunk->mTrack->isAudio()? "audio": "video");
James Dong1c9747a2010-07-30 17:41:22 -07001548
James Dongfcac8fa2011-02-09 14:00:55 -08001549 int32_t isFirstSample = true;
1550 while (!chunk->mSamples.empty()) {
1551 List<MediaBuffer *>::iterator it = chunk->mSamples.begin();
James Dong1c9747a2010-07-30 17:41:22 -07001552
James Dongfcac8fa2011-02-09 14:00:55 -08001553 off64_t offset = chunk->mTrack->isAvc()
1554 ? addLengthPrefixedSample_l(*it)
1555 : addSample_l(*it);
1556
1557 if (isFirstSample) {
1558 chunk->mTrack->addChunkOffset(offset);
1559 isFirstSample = false;
James Dong1c9747a2010-07-30 17:41:22 -07001560 }
James Dong1c9747a2010-07-30 17:41:22 -07001561
James Dong1c9747a2010-07-30 17:41:22 -07001562 (*it)->release();
1563 (*it) = NULL;
James Dongfcac8fa2011-02-09 14:00:55 -08001564 chunk->mSamples.erase(it);
James Dong1c9747a2010-07-30 17:41:22 -07001565 }
James Dongfcac8fa2011-02-09 14:00:55 -08001566 chunk->mSamples.clear();
James Dong1c9747a2010-07-30 17:41:22 -07001567}
1568
James Dongfcac8fa2011-02-09 14:00:55 -08001569void MPEG4Writer::writeAllChunks() {
Steve Block3856b092011-10-20 11:56:00 +01001570 ALOGV("writeAllChunks");
James Dong1c9747a2010-07-30 17:41:22 -07001571 size_t outstandingChunks = 0;
James Dong70ccfd42011-05-06 12:19:04 -07001572 Chunk chunk;
1573 while (findChunkToWrite(&chunk)) {
James Donge9f6d052011-06-09 15:31:39 -07001574 writeChunkToFile(&chunk);
James Dong70ccfd42011-05-06 12:19:04 -07001575 ++outstandingChunks;
James Dong1c9747a2010-07-30 17:41:22 -07001576 }
James Dong70ccfd42011-05-06 12:19:04 -07001577
1578 sendSessionSummary();
1579
James Dong1c9747a2010-07-30 17:41:22 -07001580 mChunkInfos.clear();
Steve Blockb8a80522011-12-20 16:23:08 +00001581 ALOGD("%d chunks are written in the last batch", outstandingChunks);
James Dong1c9747a2010-07-30 17:41:22 -07001582}
1583
James Dongfcac8fa2011-02-09 14:00:55 -08001584bool MPEG4Writer::findChunkToWrite(Chunk *chunk) {
Steve Block3856b092011-10-20 11:56:00 +01001585 ALOGV("findChunkToWrite");
James Dong1c9747a2010-07-30 17:41:22 -07001586
James Dong1c9747a2010-07-30 17:41:22 -07001587 int64_t minTimestampUs = 0x7FFFFFFFFFFFFFFFLL;
1588 Track *track = NULL;
1589 for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
1590 it != mChunkInfos.end(); ++it) {
1591 if (!it->mChunks.empty()) {
1592 List<Chunk>::iterator chunkIt = it->mChunks.begin();
1593 if (chunkIt->mTimeStampUs < minTimestampUs) {
1594 minTimestampUs = chunkIt->mTimeStampUs;
1595 track = it->mTrack;
1596 }
1597 }
1598 }
1599
1600 if (track == NULL) {
Steve Block3856b092011-10-20 11:56:00 +01001601 ALOGV("Nothing to be written after all");
James Dongfcac8fa2011-02-09 14:00:55 -08001602 return false;
James Dong1c9747a2010-07-30 17:41:22 -07001603 }
1604
1605 if (mIsFirstChunk) {
1606 mIsFirstChunk = false;
1607 }
James Dongfcac8fa2011-02-09 14:00:55 -08001608
James Dong1c9747a2010-07-30 17:41:22 -07001609 for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
1610 it != mChunkInfos.end(); ++it) {
1611 if (it->mTrack == track) {
James Dongfcac8fa2011-02-09 14:00:55 -08001612 *chunk = *(it->mChunks.begin());
1613 it->mChunks.erase(it->mChunks.begin());
1614 CHECK_EQ(chunk->mTrack, track);
James Dong70ccfd42011-05-06 12:19:04 -07001615
1616 int64_t interChunkTimeUs =
1617 chunk->mTimeStampUs - it->mPrevChunkTimestampUs;
1618 if (interChunkTimeUs > it->mPrevChunkTimestampUs) {
1619 it->mMaxInterChunkDurUs = interChunkTimeUs;
1620 }
1621
James Dongfcac8fa2011-02-09 14:00:55 -08001622 return true;
James Dong1c9747a2010-07-30 17:41:22 -07001623 }
1624 }
James Dongfcac8fa2011-02-09 14:00:55 -08001625
1626 return false;
James Dong1c9747a2010-07-30 17:41:22 -07001627}
1628
1629void MPEG4Writer::threadFunc() {
Steve Block3856b092011-10-20 11:56:00 +01001630 ALOGV("threadFunc");
James Dong1c9747a2010-07-30 17:41:22 -07001631
James Donga6f61fc2010-10-07 20:20:59 -07001632 prctl(PR_SET_NAME, (unsigned long)"MPEG4Writer", 0, 0, 0);
James Dongfcac8fa2011-02-09 14:00:55 -08001633
1634 Mutex::Autolock autoLock(mLock);
James Dong1c9747a2010-07-30 17:41:22 -07001635 while (!mDone) {
James Dongfcac8fa2011-02-09 14:00:55 -08001636 Chunk chunk;
1637 bool chunkFound = false;
1638
1639 while (!mDone && !(chunkFound = findChunkToWrite(&chunk))) {
James Dong1c9747a2010-07-30 17:41:22 -07001640 mChunkReadyCondition.wait(mLock);
James Dongfcac8fa2011-02-09 14:00:55 -08001641 }
1642
1643 // Actual write without holding the lock in order to
1644 // reduce the blocking time for media track threads.
1645 if (chunkFound) {
1646 mLock.unlock();
1647 writeChunkToFile(&chunk);
1648 mLock.lock();
James Dong1c9747a2010-07-30 17:41:22 -07001649 }
1650 }
1651
James Dongfcac8fa2011-02-09 14:00:55 -08001652 writeAllChunks();
James Dong1c9747a2010-07-30 17:41:22 -07001653}
1654
1655status_t MPEG4Writer::startWriterThread() {
Steve Block3856b092011-10-20 11:56:00 +01001656 ALOGV("startWriterThread");
James Dong1c9747a2010-07-30 17:41:22 -07001657
1658 mDone = false;
1659 mIsFirstChunk = true;
James Donge2595312010-08-02 19:13:40 -07001660 mDriftTimeUs = 0;
James Dong1c9747a2010-07-30 17:41:22 -07001661 for (List<Track *>::iterator it = mTracks.begin();
1662 it != mTracks.end(); ++it) {
1663 ChunkInfo info;
1664 info.mTrack = *it;
James Dong70ccfd42011-05-06 12:19:04 -07001665 info.mPrevChunkTimestampUs = 0;
1666 info.mMaxInterChunkDurUs = 0;
James Dong1c9747a2010-07-30 17:41:22 -07001667 mChunkInfos.push_back(info);
1668 }
1669
1670 pthread_attr_t attr;
1671 pthread_attr_init(&attr);
1672 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1673 pthread_create(&mThread, &attr, ThreadWrapper, this);
1674 pthread_attr_destroy(&attr);
James Dong411ba422011-07-08 16:51:16 -07001675 mWriterThreadStarted = true;
James Dong1c9747a2010-07-30 17:41:22 -07001676 return OK;
1677}
1678
James Dong1f90c4b2010-09-02 11:19:11 -07001679
James Dong93d6b102010-06-24 19:04:27 -07001680status_t MPEG4Writer::Track::start(MetaData *params) {
James Donga7d1a2d2010-06-10 12:28:15 -07001681 if (!mDone && mPaused) {
1682 mPaused = false;
1683 mResumed = true;
1684 return OK;
1685 }
Andreas Huber25b13092009-09-09 16:36:12 -07001686
James Dong93d6b102010-06-24 19:04:27 -07001687 int64_t startTimeUs;
Andreas Huber19c9a1e2010-07-01 08:19:52 -07001688 if (params == NULL || !params->findInt64(kKeyTime, &startTimeUs)) {
1689 startTimeUs = 0;
1690 }
James Dong70ccfd42011-05-06 12:19:04 -07001691 mStartTimeRealUs = startTimeUs;
Andreas Huber19c9a1e2010-07-01 08:19:52 -07001692
James Dong13f62842010-11-09 11:15:47 -08001693 int32_t rotationDegrees;
1694 if (!mIsAudio && params && params->findInt32(kKeyRotation, &rotationDegrees)) {
1695 mRotation = rotationDegrees;
1696 }
1697
James Dong5b6a01e2010-10-21 17:58:14 -07001698 mIsRealTimeRecording = true;
James Donge2595312010-08-02 19:13:40 -07001699 {
1700 int32_t isNotRealTime;
1701 if (params && params->findInt32(kKeyNotRealTime, &isNotRealTime)) {
1702 mIsRealTimeRecording = (isNotRealTime == 0);
1703 }
1704 }
1705
James Dong93d6b102010-06-24 19:04:27 -07001706 initTrackingProgressStatus(params);
1707
James Dongf60cafe2010-06-19 09:04:18 -07001708 sp<MetaData> meta = new MetaData;
James Donga4726132011-02-16 12:28:26 -08001709 if (mIsRealTimeRecording && mOwner->numTracks() > 1) {
1710 /*
1711 * This extra delay of accepting incoming audio/video signals
1712 * helps to align a/v start time at the beginning of a recording
1713 * session, and it also helps eliminate the "recording" sound for
1714 * camcorder applications.
1715 *
James Dong86b7f472011-05-09 16:56:25 -07001716 * If client does not set the start time offset, we fall back to
1717 * use the default initial delay value.
James Donga4726132011-02-16 12:28:26 -08001718 */
James Dong86b7f472011-05-09 16:56:25 -07001719 int64_t startTimeOffsetUs = mOwner->getStartTimeOffsetMs() * 1000LL;
1720 if (startTimeOffsetUs < 0) { // Start time offset was not set
1721 startTimeOffsetUs = kInitialDelayTimeUs;
1722 }
1723 startTimeUs += startTimeOffsetUs;
Steve Blockdf64d152012-01-04 20:05:49 +00001724 ALOGI("Start time offset: %lld us", startTimeOffsetUs);
James Donga4726132011-02-16 12:28:26 -08001725 }
1726
James Dongf60cafe2010-06-19 09:04:18 -07001727 meta->setInt64(kKeyTime, startTimeUs);
James Donga4726132011-02-16 12:28:26 -08001728
James Dongf60cafe2010-06-19 09:04:18 -07001729 status_t err = mSource->start(meta.get());
Andreas Huber25b13092009-09-09 16:36:12 -07001730 if (err != OK) {
1731 mDone = mReachedEOS = true;
1732 return err;
1733 }
Andreas Huber20111aa2009-07-14 16:56:47 -07001734
1735 pthread_attr_t attr;
1736 pthread_attr_init(&attr);
1737 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1738
1739 mDone = false;
James Dongeaae3842011-01-25 12:37:43 -08001740 mStarted = true;
James Dongc5f0c712010-08-05 10:46:13 -07001741 mTrackDurationUs = 0;
Andreas Huber25b13092009-09-09 16:36:12 -07001742 mReachedEOS = false;
James Dong956c5532010-05-14 15:45:22 -07001743 mEstimatedTrackSizeBytes = 0;
James Dong1f90c4b2010-09-02 11:19:11 -07001744 mMdatSizeBytes = 0;
James Dong43ec1df2011-05-06 11:27:59 -07001745 mMaxChunkDurationUs = 0;
Andreas Huber20111aa2009-07-14 16:56:47 -07001746
Andreas Huber25b13092009-09-09 16:36:12 -07001747 pthread_create(&mThread, &attr, ThreadWrapper, this);
Andreas Huber20111aa2009-07-14 16:56:47 -07001748 pthread_attr_destroy(&attr);
Andreas Huber25b13092009-09-09 16:36:12 -07001749
1750 return OK;
Andreas Huber20111aa2009-07-14 16:56:47 -07001751}
1752
James Dong37187912010-08-18 19:10:39 -07001753status_t MPEG4Writer::Track::pause() {
James Donga7d1a2d2010-06-10 12:28:15 -07001754 mPaused = true;
James Dong37187912010-08-18 19:10:39 -07001755 return OK;
James Donga7d1a2d2010-06-10 12:28:15 -07001756}
1757
James Dong37187912010-08-18 19:10:39 -07001758status_t MPEG4Writer::Track::stop() {
Steve Blockb8a80522011-12-20 16:23:08 +00001759 ALOGD("Stopping %s track", mIsAudio? "Audio": "Video");
James Dongeaae3842011-01-25 12:37:43 -08001760 if (!mStarted) {
Steve Block29357bc2012-01-06 19:20:56 +00001761 ALOGE("Stop() called but track is not started");
James Dongeaae3842011-01-25 12:37:43 -08001762 return ERROR_END_OF_STREAM;
1763 }
1764
Andreas Huber20111aa2009-07-14 16:56:47 -07001765 if (mDone) {
James Dong37187912010-08-18 19:10:39 -07001766 return OK;
Andreas Huber20111aa2009-07-14 16:56:47 -07001767 }
Andreas Huber20111aa2009-07-14 16:56:47 -07001768 mDone = true;
1769
1770 void *dummy;
1771 pthread_join(mThread, &dummy);
1772
James Dong37187912010-08-18 19:10:39 -07001773 status_t err = (status_t) dummy;
1774
Steve Blockb8a80522011-12-20 16:23:08 +00001775 ALOGD("Stopping %s track source", mIsAudio? "Audio": "Video");
James Dong37187912010-08-18 19:10:39 -07001776 {
1777 status_t status = mSource->stop();
1778 if (err == OK && status != OK && status != ERROR_END_OF_STREAM) {
1779 err = status;
1780 }
1781 }
1782
Steve Blockb8a80522011-12-20 16:23:08 +00001783 ALOGD("%s track stopped", mIsAudio? "Audio": "Video");
James Dong37187912010-08-18 19:10:39 -07001784 return err;
Andreas Huber20111aa2009-07-14 16:56:47 -07001785}
1786
Andreas Huber25b13092009-09-09 16:36:12 -07001787bool MPEG4Writer::Track::reachedEOS() {
1788 return mReachedEOS;
1789}
1790
Andreas Huber20111aa2009-07-14 16:56:47 -07001791// static
1792void *MPEG4Writer::Track::ThreadWrapper(void *me) {
1793 Track *track = static_cast<Track *>(me);
1794
James Dong37187912010-08-18 19:10:39 -07001795 status_t err = track->threadEntry();
1796 return (void *) err;
Andreas Huber20111aa2009-07-14 16:56:47 -07001797}
1798
James Dong3266b2c2010-08-06 00:29:03 -07001799static void getNalUnitType(uint8_t byte, uint8_t* type) {
Steve Block3856b092011-10-20 11:56:00 +01001800 ALOGV("getNalUnitType: %d", byte);
James Dong3266b2c2010-08-06 00:29:03 -07001801
1802 // nal_unit_type: 5-bit unsigned integer
1803 *type = (byte & 0x1F);
1804}
1805
1806static const uint8_t *findNextStartCode(
1807 const uint8_t *data, size_t length) {
1808
Steve Block3856b092011-10-20 11:56:00 +01001809 ALOGV("findNextStartCode: %p %d", data, length);
James Dong3266b2c2010-08-06 00:29:03 -07001810
1811 size_t bytesLeft = length;
1812 while (bytesLeft > 4 &&
1813 memcmp("\x00\x00\x00\x01", &data[length - bytesLeft], 4)) {
1814 --bytesLeft;
1815 }
1816 if (bytesLeft <= 4) {
1817 bytesLeft = 0; // Last parameter set
1818 }
1819 return &data[length - bytesLeft];
1820}
1821
1822const uint8_t *MPEG4Writer::Track::parseParamSet(
1823 const uint8_t *data, size_t length, int type, size_t *paramSetLen) {
1824
Steve Block3856b092011-10-20 11:56:00 +01001825 ALOGV("parseParamSet");
James Dong3266b2c2010-08-06 00:29:03 -07001826 CHECK(type == kNalUnitTypeSeqParamSet ||
1827 type == kNalUnitTypePicParamSet);
1828
1829 const uint8_t *nextStartCode = findNextStartCode(data, length);
1830 *paramSetLen = nextStartCode - data;
1831 if (*paramSetLen == 0) {
Steve Block29357bc2012-01-06 19:20:56 +00001832 ALOGE("Param set is malformed, since its length is 0");
James Dong3266b2c2010-08-06 00:29:03 -07001833 return NULL;
1834 }
1835
1836 AVCParamSet paramSet(*paramSetLen, data);
1837 if (type == kNalUnitTypeSeqParamSet) {
1838 if (*paramSetLen < 4) {
Steve Block29357bc2012-01-06 19:20:56 +00001839 ALOGE("Seq parameter set malformed");
James Dong3266b2c2010-08-06 00:29:03 -07001840 return NULL;
1841 }
1842 if (mSeqParamSets.empty()) {
1843 mProfileIdc = data[1];
1844 mProfileCompatible = data[2];
1845 mLevelIdc = data[3];
1846 } else {
1847 if (mProfileIdc != data[1] ||
1848 mProfileCompatible != data[2] ||
1849 mLevelIdc != data[3]) {
Steve Block29357bc2012-01-06 19:20:56 +00001850 ALOGE("Inconsistent profile/level found in seq parameter sets");
James Dong3266b2c2010-08-06 00:29:03 -07001851 return NULL;
1852 }
1853 }
1854 mSeqParamSets.push_back(paramSet);
1855 } else {
1856 mPicParamSets.push_back(paramSet);
1857 }
1858 return nextStartCode;
1859}
1860
1861status_t MPEG4Writer::Track::copyAVCCodecSpecificData(
1862 const uint8_t *data, size_t size) {
Steve Block3856b092011-10-20 11:56:00 +01001863 ALOGV("copyAVCCodecSpecificData");
James Dong3266b2c2010-08-06 00:29:03 -07001864
1865 // 2 bytes for each of the parameter set length field
1866 // plus the 7 bytes for the header
1867 if (size < 4 + 7) {
Steve Block29357bc2012-01-06 19:20:56 +00001868 ALOGE("Codec specific data length too short: %d", size);
James Dong3266b2c2010-08-06 00:29:03 -07001869 return ERROR_MALFORMED;
1870 }
1871
1872 mCodecSpecificDataSize = size;
1873 mCodecSpecificData = malloc(size);
1874 memcpy(mCodecSpecificData, data, size);
1875 return OK;
1876}
1877
1878status_t MPEG4Writer::Track::parseAVCCodecSpecificData(
1879 const uint8_t *data, size_t size) {
1880
Steve Block3856b092011-10-20 11:56:00 +01001881 ALOGV("parseAVCCodecSpecificData");
James Dong3266b2c2010-08-06 00:29:03 -07001882 // Data starts with a start code.
1883 // SPS and PPS are separated with start codes.
1884 // Also, SPS must come before PPS
1885 uint8_t type = kNalUnitTypeSeqParamSet;
1886 bool gotSps = false;
1887 bool gotPps = false;
1888 const uint8_t *tmp = data;
1889 const uint8_t *nextStartCode = data;
1890 size_t bytesLeft = size;
1891 size_t paramSetLen = 0;
1892 mCodecSpecificDataSize = 0;
1893 while (bytesLeft > 4 && !memcmp("\x00\x00\x00\x01", tmp, 4)) {
1894 getNalUnitType(*(tmp + 4), &type);
1895 if (type == kNalUnitTypeSeqParamSet) {
1896 if (gotPps) {
Steve Block29357bc2012-01-06 19:20:56 +00001897 ALOGE("SPS must come before PPS");
James Dong3266b2c2010-08-06 00:29:03 -07001898 return ERROR_MALFORMED;
1899 }
1900 if (!gotSps) {
1901 gotSps = true;
1902 }
1903 nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, &paramSetLen);
1904 } else if (type == kNalUnitTypePicParamSet) {
1905 if (!gotSps) {
Steve Block29357bc2012-01-06 19:20:56 +00001906 ALOGE("SPS must come before PPS");
James Dong3266b2c2010-08-06 00:29:03 -07001907 return ERROR_MALFORMED;
1908 }
1909 if (!gotPps) {
1910 gotPps = true;
1911 }
1912 nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, &paramSetLen);
1913 } else {
Steve Block29357bc2012-01-06 19:20:56 +00001914 ALOGE("Only SPS and PPS Nal units are expected");
James Dong3266b2c2010-08-06 00:29:03 -07001915 return ERROR_MALFORMED;
1916 }
1917
1918 if (nextStartCode == NULL) {
1919 return ERROR_MALFORMED;
1920 }
1921
1922 // Move on to find the next parameter set
1923 bytesLeft -= nextStartCode - tmp;
1924 tmp = nextStartCode;
1925 mCodecSpecificDataSize += (2 + paramSetLen);
1926 }
1927
1928 {
1929 // Check on the number of seq parameter sets
1930 size_t nSeqParamSets = mSeqParamSets.size();
1931 if (nSeqParamSets == 0) {
Steve Block29357bc2012-01-06 19:20:56 +00001932 ALOGE("Cound not find sequence parameter set");
James Dong3266b2c2010-08-06 00:29:03 -07001933 return ERROR_MALFORMED;
1934 }
1935
1936 if (nSeqParamSets > 0x1F) {
Steve Block29357bc2012-01-06 19:20:56 +00001937 ALOGE("Too many seq parameter sets (%d) found", nSeqParamSets);
James Dong3266b2c2010-08-06 00:29:03 -07001938 return ERROR_MALFORMED;
1939 }
1940 }
1941
1942 {
1943 // Check on the number of pic parameter sets
1944 size_t nPicParamSets = mPicParamSets.size();
1945 if (nPicParamSets == 0) {
Steve Block29357bc2012-01-06 19:20:56 +00001946 ALOGE("Cound not find picture parameter set");
James Dong3266b2c2010-08-06 00:29:03 -07001947 return ERROR_MALFORMED;
1948 }
1949 if (nPicParamSets > 0xFF) {
Steve Block29357bc2012-01-06 19:20:56 +00001950 ALOGE("Too many pic parameter sets (%d) found", nPicParamSets);
James Dong3266b2c2010-08-06 00:29:03 -07001951 return ERROR_MALFORMED;
1952 }
1953 }
Dandawate Saket1374edd2011-07-11 19:12:57 -07001954// FIXME:
1955// Add chromat_format_idc, bit depth values, etc for AVC/h264 high profile and above
1956// and remove #if 0
1957#if 0
James Dong3266b2c2010-08-06 00:29:03 -07001958 {
1959 // Check on the profiles
1960 // These profiles requires additional parameter set extensions
1961 if (mProfileIdc == 100 || mProfileIdc == 110 ||
1962 mProfileIdc == 122 || mProfileIdc == 144) {
Steve Block29357bc2012-01-06 19:20:56 +00001963 ALOGE("Sorry, no support for profile_idc: %d!", mProfileIdc);
James Dong3266b2c2010-08-06 00:29:03 -07001964 return BAD_VALUE;
1965 }
1966 }
Dandawate Saket1374edd2011-07-11 19:12:57 -07001967#endif
James Dong3266b2c2010-08-06 00:29:03 -07001968 return OK;
1969}
Andreas Huber548e3182010-04-09 14:25:46 -07001970
Andreas Huber03b268e2010-03-19 11:43:15 -07001971status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
1972 const uint8_t *data, size_t size) {
Andreas Huber548e3182010-04-09 14:25:46 -07001973
Andreas Huber03b268e2010-03-19 11:43:15 -07001974 if (mCodecSpecificData != NULL) {
Steve Block29357bc2012-01-06 19:20:56 +00001975 ALOGE("Already have codec specific data");
Andreas Huber03b268e2010-03-19 11:43:15 -07001976 return ERROR_MALFORMED;
1977 }
1978
James Dong3266b2c2010-08-06 00:29:03 -07001979 if (size < 4) {
Steve Block29357bc2012-01-06 19:20:56 +00001980 ALOGE("Codec specific data length too short: %d", size);
Andreas Huber03b268e2010-03-19 11:43:15 -07001981 return ERROR_MALFORMED;
1982 }
1983
James Dong3266b2c2010-08-06 00:29:03 -07001984 // Data is in the form of AVCCodecSpecificData
1985 if (memcmp("\x00\x00\x00\x01", data, 4)) {
1986 return copyAVCCodecSpecificData(data, size);
Andreas Huber03b268e2010-03-19 11:43:15 -07001987 }
1988
James Dong3266b2c2010-08-06 00:29:03 -07001989 if (parseAVCCodecSpecificData(data, size) != OK) {
Andreas Huber03b268e2010-03-19 11:43:15 -07001990 return ERROR_MALFORMED;
1991 }
1992
James Dong3266b2c2010-08-06 00:29:03 -07001993 // ISO 14496-15: AVC file format
1994 mCodecSpecificDataSize += 7; // 7 more bytes in the header
Andreas Huber03b268e2010-03-19 11:43:15 -07001995 mCodecSpecificData = malloc(mCodecSpecificDataSize);
1996 uint8_t *header = (uint8_t *)mCodecSpecificData;
James Dong3266b2c2010-08-06 00:29:03 -07001997 header[0] = 1; // version
1998 header[1] = mProfileIdc; // profile indication
1999 header[2] = mProfileCompatible; // profile compatibility
2000 header[3] = mLevelIdc;
Andreas Huber03b268e2010-03-19 11:43:15 -07002001
James Dong3266b2c2010-08-06 00:29:03 -07002002 // 6-bit '111111' followed by 2-bit to lengthSizeMinuusOne
James Dongb4d53202010-09-02 10:49:55 -07002003 if (mOwner->useNalLengthFour()) {
2004 header[4] = 0xfc | 3; // length size == 4 bytes
2005 } else {
2006 header[4] = 0xfc | 1; // length size == 2 bytes
2007 }
Andreas Huber03b268e2010-03-19 11:43:15 -07002008
James Dong3266b2c2010-08-06 00:29:03 -07002009 // 3-bit '111' followed by 5-bit numSequenceParameterSets
2010 int nSequenceParamSets = mSeqParamSets.size();
2011 header[5] = 0xe0 | nSequenceParamSets;
2012 header += 6;
2013 for (List<AVCParamSet>::iterator it = mSeqParamSets.begin();
2014 it != mSeqParamSets.end(); ++it) {
2015 // 16-bit sequence parameter set length
2016 uint16_t seqParamSetLength = it->mLength;
2017 header[0] = seqParamSetLength >> 8;
2018 header[1] = seqParamSetLength & 0xff;
2019
2020 // SPS NAL unit (sequence parameter length bytes)
2021 memcpy(&header[2], it->mData, seqParamSetLength);
2022 header += (2 + seqParamSetLength);
2023 }
2024
2025 // 8-bit nPictureParameterSets
2026 int nPictureParamSets = mPicParamSets.size();
2027 header[0] = nPictureParamSets;
2028 header += 1;
2029 for (List<AVCParamSet>::iterator it = mPicParamSets.begin();
2030 it != mPicParamSets.end(); ++it) {
2031 // 16-bit picture parameter set length
2032 uint16_t picParamSetLength = it->mLength;
2033 header[0] = picParamSetLength >> 8;
2034 header[1] = picParamSetLength & 0xff;
2035
2036 // PPS Nal unit (picture parameter set length bytes)
2037 memcpy(&header[2], it->mData, picParamSetLength);
2038 header += (2 + picParamSetLength);
2039 }
Andreas Huber03b268e2010-03-19 11:43:15 -07002040
2041 return OK;
2042}
2043
James Dong872a4812010-10-03 10:59:26 -07002044/*
James Dong872a4812010-10-03 10:59:26 -07002045 * Updates the drift time from the audio track so that
2046 * the video track can get the updated drift time information
2047 * from the file writer. The fluctuation of the drift time of the audio
2048 * encoding path is smoothed out with a simple filter by giving a larger
2049 * weight to more recently drift time. The filter coefficients, 0.5 and 0.5,
2050 * are heuristically determined.
2051 */
2052void MPEG4Writer::Track::updateDriftTime(const sp<MetaData>& meta) {
2053 int64_t driftTimeUs = 0;
2054 if (meta->findInt64(kKeyDriftTime, &driftTimeUs)) {
2055 int64_t prevDriftTimeUs = mOwner->getDriftTimeUs();
2056 int64_t timeUs = (driftTimeUs + prevDriftTimeUs) >> 1;
2057 mOwner->setDriftTimeUs(timeUs);
2058 }
2059}
2060
James Dong37187912010-08-18 19:10:39 -07002061status_t MPEG4Writer::Track::threadEntry() {
Andreas Huber30ab6622009-11-16 15:43:38 -08002062 int32_t count = 0;
James Dong13aec892010-04-21 16:14:15 -07002063 const int64_t interleaveDurationUs = mOwner->interleaveDuration();
James Dong43ec1df2011-05-06 11:27:59 -07002064 const bool hasMultipleTracks = (mOwner->numTracks() > 1);
James Dong13aec892010-04-21 16:14:15 -07002065 int64_t chunkTimestampUs = 0;
2066 int32_t nChunks = 0;
2067 int32_t nZeroLengthFrames = 0;
James Dong965e4232011-06-07 19:45:54 -07002068 int64_t lastTimestampUs = 0; // Previous sample time stamp
James Dong965e4232011-06-07 19:45:54 -07002069 int64_t lastDurationUs = 0; // Between the previous two samples
2070 int64_t currDurationTicks = 0; // Timescale based ticks
2071 int64_t lastDurationTicks = 0; // Timescale based ticks
2072 int32_t sampleCount = 1; // Sample count in the current stts table entry
James Dong000e1832012-02-06 23:46:37 -08002073 uint32_t previousSampleSize = 0; // Size of the previous sample
James Donga7d1a2d2010-06-10 12:28:15 -07002074 int64_t previousPausedDurationUs = 0;
James Dong965e4232011-06-07 19:45:54 -07002075 int64_t timestampUs = 0;
James Dong000e1832012-02-06 23:46:37 -08002076 int64_t cttsOffsetTimeUs = 0;
2077 int64_t currCttsOffsetTimeTicks = 0; // Timescale based ticks
2078 int64_t lastCttsOffsetTimeTicks = -1; // Timescale based ticks
James Dong43089da2012-02-09 11:53:57 -08002079 int32_t cttsSampleCount = 0; // Sample count in the current ctts table entry
James Dongc620cbd2012-07-27 17:14:44 -07002080 uint32_t lastSamplesPerChunk = 0;
James Donge2595312010-08-02 19:13:40 -07002081
James Donga6f61fc2010-10-07 20:20:59 -07002082 if (mIsAudio) {
2083 prctl(PR_SET_NAME, (unsigned long)"AudioTrackEncoding", 0, 0, 0);
2084 } else {
2085 prctl(PR_SET_NAME, (unsigned long)"VideoTrackEncoding", 0, 0, 0);
2086 }
Glenn Kasten86106f82011-06-14 10:35:34 -07002087 androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
James Dong985f8382010-12-07 14:37:27 -08002088
James Dongd07139e2010-06-07 14:41:41 -07002089 sp<MetaData> meta_data;
Andreas Huber20111aa2009-07-14 16:56:47 -07002090
James Dong93d6b102010-06-24 19:04:27 -07002091 status_t err = OK;
Andreas Huber20111aa2009-07-14 16:56:47 -07002092 MediaBuffer *buffer;
James Dong93d6b102010-06-24 19:04:27 -07002093 while (!mDone && (err = mSource->read(&buffer)) == OK) {
Andreas Huber20111aa2009-07-14 16:56:47 -07002094 if (buffer->range_length() == 0) {
2095 buffer->release();
2096 buffer = NULL;
James Dong13aec892010-04-21 16:14:15 -07002097 ++nZeroLengthFrames;
Andreas Huber20111aa2009-07-14 16:56:47 -07002098 continue;
2099 }
2100
James Donga7d1a2d2010-06-10 12:28:15 -07002101 // If the codec specific data has not been received yet, delay pause.
2102 // After the codec specific data is received, discard what we received
2103 // when the track is to be paused.
2104 if (mPaused && !mResumed) {
2105 buffer->release();
2106 buffer = NULL;
2107 continue;
2108 }
2109
Andreas Huber30ab6622009-11-16 15:43:38 -08002110 ++count;
2111
Andreas Huber03b268e2010-03-19 11:43:15 -07002112 int32_t isCodecConfig;
2113 if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecConfig)
2114 && isCodecConfig) {
Andreas Huber548e3182010-04-09 14:25:46 -07002115 CHECK(!mGotAllCodecSpecificData);
2116
James Dong1c9747a2010-07-30 17:41:22 -07002117 if (mIsAvc) {
Andreas Huber03b268e2010-03-19 11:43:15 -07002118 status_t err = makeAVCCodecSpecificData(
2119 (const uint8_t *)buffer->data()
2120 + buffer->range_offset(),
2121 buffer->range_length());
James Dong43089da2012-02-09 11:53:57 -08002122 CHECK_EQ((status_t)OK, err);
James Dong1c9747a2010-07-30 17:41:22 -07002123 } else if (mIsMPEG4) {
Andreas Huber03b268e2010-03-19 11:43:15 -07002124 mCodecSpecificDataSize = buffer->range_length();
2125 mCodecSpecificData = malloc(mCodecSpecificDataSize);
2126 memcpy(mCodecSpecificData,
2127 (const uint8_t *)buffer->data()
2128 + buffer->range_offset(),
2129 buffer->range_length());
Andreas Huber30ab6622009-11-16 15:43:38 -08002130 }
2131
2132 buffer->release();
2133 buffer = NULL;
2134
Andreas Huber548e3182010-04-09 14:25:46 -07002135 mGotAllCodecSpecificData = true;
Andreas Huber30ab6622009-11-16 15:43:38 -08002136 continue;
James Donga7d1a2d2010-06-10 12:28:15 -07002137 }
2138
James Dongd07139e2010-06-07 14:41:41 -07002139 // Make a deep copy of the MediaBuffer and Metadata and release
2140 // the original as soon as we can
2141 MediaBuffer *copy = new MediaBuffer(buffer->range_length());
2142 memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(),
2143 buffer->range_length());
2144 copy->set_range(0, buffer->range_length());
2145 meta_data = new MetaData(*buffer->meta_data().get());
2146 buffer->release();
2147 buffer = NULL;
2148
James Dong1c9747a2010-07-30 17:41:22 -07002149 if (mIsAvc) StripStartcode(copy);
James Donge136c3b2010-05-20 17:55:52 -07002150
James Dongb4d53202010-09-02 10:49:55 -07002151 size_t sampleSize = copy->range_length();
2152 if (mIsAvc) {
2153 if (mOwner->useNalLengthFour()) {
2154 sampleSize += 4;
2155 } else {
2156 sampleSize += 2;
2157 }
2158 }
James Dong050b28a2010-04-22 17:27:04 -07002159
James Dongd599cd42010-05-11 14:57:02 -07002160 // Max file size or duration handling
James Dong1f90c4b2010-09-02 11:19:11 -07002161 mMdatSizeBytes += sampleSize;
2162 updateTrackSizeEstimate();
2163
James Dongd599cd42010-05-11 14:57:02 -07002164 if (mOwner->exceedsFileSizeLimit()) {
James Dongd599cd42010-05-11 14:57:02 -07002165 mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
2166 break;
2167 }
2168 if (mOwner->exceedsFileDurationLimit()) {
James Dongd599cd42010-05-11 14:57:02 -07002169 mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
2170 break;
2171 }
2172
James Dong050b28a2010-04-22 17:27:04 -07002173
James Dongd07139e2010-06-07 14:41:41 -07002174 int32_t isSync = false;
2175 meta_data->findInt32(kKeyIsSyncFrame, &isSync);
James Dongd07139e2010-06-07 14:41:41 -07002176 CHECK(meta_data->findInt64(kKeyTime, &timestampUs));
2177
2178////////////////////////////////////////////////////////////////////////////////
James Dongc620cbd2012-07-27 17:14:44 -07002179 if (mStszTableEntries->count() == 0) {
James Dong70ccfd42011-05-06 12:19:04 -07002180 mFirstSampleTimeRealUs = systemTime() / 1000;
James Dongf60cafe2010-06-19 09:04:18 -07002181 mStartTimestampUs = timestampUs;
2182 mOwner->setStartTimestampUs(mStartTimestampUs);
James Dong8428af52010-09-24 10:01:29 -07002183 previousPausedDurationUs = mStartTimestampUs;
James Dong3c0131f2010-05-13 11:47:36 -07002184 }
Andreas Huber20111aa2009-07-14 16:56:47 -07002185
James Donga7d1a2d2010-06-10 12:28:15 -07002186 if (mResumed) {
James Dong8428af52010-09-24 10:01:29 -07002187 int64_t durExcludingEarlierPausesUs = timestampUs - previousPausedDurationUs;
James Dong43089da2012-02-09 11:53:57 -08002188 CHECK_GE(durExcludingEarlierPausesUs, 0ll);
James Dong8428af52010-09-24 10:01:29 -07002189 int64_t pausedDurationUs = durExcludingEarlierPausesUs - mTrackDurationUs;
James Dong43089da2012-02-09 11:53:57 -08002190 CHECK_GE(pausedDurationUs, lastDurationUs);
James Dong8428af52010-09-24 10:01:29 -07002191 previousPausedDurationUs += pausedDurationUs - lastDurationUs;
James Donga7d1a2d2010-06-10 12:28:15 -07002192 mResumed = false;
2193 }
2194
2195 timestampUs -= previousPausedDurationUs;
James Dong43089da2012-02-09 11:53:57 -08002196 CHECK_GE(timestampUs, 0ll);
James Dong000e1832012-02-06 23:46:37 -08002197 if (!mIsAudio) {
James Dong965e4232011-06-07 19:45:54 -07002198 /*
2199 * Composition time: timestampUs
2200 * Decoding time: decodingTimeUs
James Dong000e1832012-02-06 23:46:37 -08002201 * Composition time offset = composition time - decoding time
James Dong965e4232011-06-07 19:45:54 -07002202 */
2203 int64_t decodingTimeUs;
2204 CHECK(meta_data->findInt64(kKeyDecodingTime, &decodingTimeUs));
2205 decodingTimeUs -= previousPausedDurationUs;
James Dong000e1832012-02-06 23:46:37 -08002206 cttsOffsetTimeUs =
2207 timestampUs + kMaxCttsOffsetTimeUs - decodingTimeUs;
James Dong43089da2012-02-09 11:53:57 -08002208 CHECK_GE(cttsOffsetTimeUs, 0ll);
James Dong965e4232011-06-07 19:45:54 -07002209 timestampUs = decodingTimeUs;
James Dong000e1832012-02-06 23:46:37 -08002210 ALOGV("decoding time: %lld and ctts offset time: %lld",
2211 timestampUs, cttsOffsetTimeUs);
2212
2213 // Update ctts box table if necessary
2214 currCttsOffsetTimeTicks =
2215 (cttsOffsetTimeUs * mTimeScale + 500000LL) / 1000000LL;
James Dong43089da2012-02-09 11:53:57 -08002216 CHECK_LE(currCttsOffsetTimeTicks, 0x0FFFFFFFFLL);
James Dongc620cbd2012-07-27 17:14:44 -07002217 if (mStszTableEntries->count() == 0) {
James Dong43089da2012-02-09 11:53:57 -08002218 // Force the first ctts table entry to have one single entry
2219 // so that we can do adjustment for the initial track start
2220 // time offset easily in writeCttsBox().
2221 lastCttsOffsetTimeTicks = currCttsOffsetTimeTicks;
2222 addOneCttsTableEntry(1, currCttsOffsetTimeTicks);
2223 cttsSampleCount = 0; // No sample in ctts box is pending
2224 } else {
2225 if (currCttsOffsetTimeTicks != lastCttsOffsetTimeTicks) {
2226 addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
2227 lastCttsOffsetTimeTicks = currCttsOffsetTimeTicks;
2228 cttsSampleCount = 1; // One sample in ctts box is pending
2229 } else {
2230 ++cttsSampleCount;
2231 }
2232 }
James Dong000e1832012-02-06 23:46:37 -08002233
2234 // Update ctts time offset range
James Dongc620cbd2012-07-27 17:14:44 -07002235 if (mStszTableEntries->count() == 0) {
James Dong000e1832012-02-06 23:46:37 -08002236 mMinCttsOffsetTimeUs = currCttsOffsetTimeTicks;
2237 mMaxCttsOffsetTimeUs = currCttsOffsetTimeTicks;
2238 } else {
2239 if (currCttsOffsetTimeTicks > mMaxCttsOffsetTimeUs) {
2240 mMaxCttsOffsetTimeUs = currCttsOffsetTimeTicks;
2241 } else if (currCttsOffsetTimeTicks < mMinCttsOffsetTimeUs) {
2242 mMinCttsOffsetTimeUs = currCttsOffsetTimeTicks;
2243 }
2244 }
2245
James Dong965e4232011-06-07 19:45:54 -07002246 }
James Dong872a4812010-10-03 10:59:26 -07002247
James Dong872a4812010-10-03 10:59:26 -07002248 if (mIsRealTimeRecording) {
2249 if (mIsAudio) {
2250 updateDriftTime(meta_data);
James Donge2595312010-08-02 19:13:40 -07002251 }
2252 }
James Dong872a4812010-10-03 10:59:26 -07002253
James Dong43089da2012-02-09 11:53:57 -08002254 CHECK_GE(timestampUs, 0ll);
Steve Block3856b092011-10-20 11:56:00 +01002255 ALOGV("%s media time stamp: %lld and previous paused duration %lld",
James Dong8428af52010-09-24 10:01:29 -07002256 mIsAudio? "Audio": "Video", timestampUs, previousPausedDurationUs);
James Dongc5f0c712010-08-05 10:46:13 -07002257 if (timestampUs > mTrackDurationUs) {
2258 mTrackDurationUs = timestampUs;
Andreas Huber3b240d62010-02-02 12:13:30 -08002259 }
2260
James Dong5a217fb2011-08-09 11:14:57 -07002261 // We need to use the time scale based ticks, rather than the
2262 // timestamp itself to determine whether we have to use a new
2263 // stts entry, since we may have rounding errors.
2264 // The calculation is intended to reduce the accumulated
2265 // rounding errors.
2266 currDurationTicks =
2267 ((timestampUs * mTimeScale + 500000LL) / 1000000LL -
2268 (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
James Dongc620cbd2012-07-27 17:14:44 -07002269 if (currDurationTicks < 0ll) {
2270 ALOGE("timestampUs %lld < lastTimestampUs %lld for %s track",
2271 timestampUs, lastTimestampUs, mIsAudio? "Audio": "Video");
2272 return UNKNOWN_ERROR;
James Dong8c460492012-05-15 10:43:30 -07002273 }
2274
James Dongc620cbd2012-07-27 17:14:44 -07002275 mStszTableEntries->add(htonl(sampleSize));
2276 if (mStszTableEntries->count() > 2) {
James Dongc0598602010-08-13 14:16:26 -07002277
James Donga4726132011-02-16 12:28:26 -08002278 // Force the first sample to have its own stts entry so that
2279 // we can adjust its value later to maintain the A/V sync.
James Dongc620cbd2012-07-27 17:14:44 -07002280 if (mStszTableEntries->count() == 3 || currDurationTicks != lastDurationTicks) {
James Dong79761ab2011-06-06 19:00:40 -07002281 addOneSttsTableEntry(sampleCount, lastDurationTicks);
James Dongbe83c9e2010-04-20 11:50:11 -07002282 sampleCount = 1;
2283 } else {
2284 ++sampleCount;
2285 }
James Dong965e4232011-06-07 19:45:54 -07002286
James Dongbe83c9e2010-04-20 11:50:11 -07002287 }
2288 if (mSamplesHaveSameSize) {
James Dongc620cbd2012-07-27 17:14:44 -07002289 if (mStszTableEntries->count() >= 2 && previousSampleSize != sampleSize) {
James Dongbe83c9e2010-04-20 11:50:11 -07002290 mSamplesHaveSameSize = false;
2291 }
James Dong8644c142010-07-28 10:24:39 -07002292 previousSampleSize = sampleSize;
James Dongbe83c9e2010-04-20 11:50:11 -07002293 }
Steve Block3856b092011-10-20 11:56:00 +01002294 ALOGV("%s timestampUs/lastTimestampUs: %lld/%lld",
James Donga4726132011-02-16 12:28:26 -08002295 mIsAudio? "Audio": "Video", timestampUs, lastTimestampUs);
James Dong8644c142010-07-28 10:24:39 -07002296 lastDurationUs = timestampUs - lastTimestampUs;
James Dongc0598602010-08-13 14:16:26 -07002297 lastDurationTicks = currDurationTicks;
James Dong8644c142010-07-28 10:24:39 -07002298 lastTimestampUs = timestampUs;
Andreas Huber20111aa2009-07-14 16:56:47 -07002299
James Dongd07139e2010-06-07 14:41:41 -07002300 if (isSync != 0) {
James Dongc620cbd2012-07-27 17:14:44 -07002301 addOneStssTableEntry(mStszTableEntries->count());
James Dongd07139e2010-06-07 14:41:41 -07002302 }
2303
James Dong93d6b102010-06-24 19:04:27 -07002304 if (mTrackingProgressStatus) {
2305 if (mPreviousTrackTimeUs <= 0) {
2306 mPreviousTrackTimeUs = mStartTimestampUs;
2307 }
James Dongfaed5cd2010-07-15 19:08:20 -07002308 trackProgressStatus(timestampUs);
James Dong93d6b102010-06-24 19:04:27 -07002309 }
James Dong43ec1df2011-05-06 11:27:59 -07002310 if (!hasMultipleTracks) {
James Dongc7fc37a2010-11-16 14:04:54 -08002311 off64_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy)
James Dong58ae9c52010-06-22 11:27:37 -07002312 : mOwner->addSample_l(copy);
James Dongc620cbd2012-07-27 17:14:44 -07002313
2314 uint32_t count = (mOwner->use32BitFileOffset()
2315 ? mStcoTableEntries->count()
2316 : mCo64TableEntries->count());
2317
2318 if (count == 0) {
James Dong1f90c4b2010-09-02 11:19:11 -07002319 addChunkOffset(offset);
James Dong58ae9c52010-06-22 11:27:37 -07002320 }
2321 copy->release();
2322 copy = NULL;
2323 continue;
2324 }
James Dong13aec892010-04-21 16:14:15 -07002325
2326 mChunkSamples.push_back(copy);
2327 if (interleaveDurationUs == 0) {
James Dong1f90c4b2010-09-02 11:19:11 -07002328 addOneStscTableEntry(++nChunks, 1);
James Dong1c9747a2010-07-30 17:41:22 -07002329 bufferChunk(timestampUs);
James Dong13aec892010-04-21 16:14:15 -07002330 } else {
2331 if (chunkTimestampUs == 0) {
2332 chunkTimestampUs = timestampUs;
2333 } else {
James Dong43ec1df2011-05-06 11:27:59 -07002334 int64_t chunkDurationUs = timestampUs - chunkTimestampUs;
2335 if (chunkDurationUs > interleaveDurationUs) {
2336 if (chunkDurationUs > mMaxChunkDurationUs) {
2337 mMaxChunkDurationUs = chunkDurationUs;
2338 }
James Dong13aec892010-04-21 16:14:15 -07002339 ++nChunks;
2340 if (nChunks == 1 || // First chunk
James Dongc620cbd2012-07-27 17:14:44 -07002341 lastSamplesPerChunk != mChunkSamples.size()) {
2342 lastSamplesPerChunk = mChunkSamples.size();
2343 addOneStscTableEntry(nChunks, lastSamplesPerChunk);
James Dong13aec892010-04-21 16:14:15 -07002344 }
James Dong1c9747a2010-07-30 17:41:22 -07002345 bufferChunk(timestampUs);
James Dong13aec892010-04-21 16:14:15 -07002346 chunkTimestampUs = timestampUs;
2347 }
2348 }
2349 }
2350
Andreas Huber20111aa2009-07-14 16:56:47 -07002351 }
Andreas Huber25b13092009-09-09 16:36:12 -07002352
James Dong45c254c2011-06-21 17:22:37 -07002353 if (isTrackMalFormed()) {
James Dong690f5462010-08-19 13:52:47 -07002354 err = ERROR_MALFORMED;
James Dongf0ce2fb2010-05-14 11:48:00 -07002355 }
James Dong45c254c2011-06-21 17:22:37 -07002356
James Dongbc07bcc2011-03-18 11:25:41 -07002357 mOwner->trackProgressStatus(mTrackId, -1, err);
James Dongbe83c9e2010-04-20 11:50:11 -07002358
James Dong13aec892010-04-21 16:14:15 -07002359 // Last chunk
James Dong43ec1df2011-05-06 11:27:59 -07002360 if (!hasMultipleTracks) {
James Dongc620cbd2012-07-27 17:14:44 -07002361 addOneStscTableEntry(1, mStszTableEntries->count());
James Dong58ae9c52010-06-22 11:27:37 -07002362 } else if (!mChunkSamples.empty()) {
James Dong1f90c4b2010-09-02 11:19:11 -07002363 addOneStscTableEntry(++nChunks, mChunkSamples.size());
James Dong1c9747a2010-07-30 17:41:22 -07002364 bufferChunk(timestampUs);
James Dong13aec892010-04-21 16:14:15 -07002365 }
2366
James Dongbe83c9e2010-04-20 11:50:11 -07002367 // We don't really know how long the last frame lasts, since
2368 // there is no frame time after it, just repeat the previous
2369 // frame's duration.
James Dongc620cbd2012-07-27 17:14:44 -07002370 if (mStszTableEntries->count() == 1) {
James Dong8f5f2fc2010-07-02 11:39:06 -07002371 lastDurationUs = 0; // A single sample's duration
James Dong79761ab2011-06-06 19:00:40 -07002372 lastDurationTicks = 0;
James Dongbe83c9e2010-04-20 11:50:11 -07002373 } else {
2374 ++sampleCount; // Count for the last sample
2375 }
James Donga4726132011-02-16 12:28:26 -08002376
James Dongc620cbd2012-07-27 17:14:44 -07002377 if (mStszTableEntries->count() <= 2) {
James Dong79761ab2011-06-06 19:00:40 -07002378 addOneSttsTableEntry(1, lastDurationTicks);
James Donga4726132011-02-16 12:28:26 -08002379 if (sampleCount - 1 > 0) {
James Dong79761ab2011-06-06 19:00:40 -07002380 addOneSttsTableEntry(sampleCount - 1, lastDurationTicks);
James Donga4726132011-02-16 12:28:26 -08002381 }
2382 } else {
James Dong79761ab2011-06-06 19:00:40 -07002383 addOneSttsTableEntry(sampleCount, lastDurationTicks);
James Donga4726132011-02-16 12:28:26 -08002384 }
2385
James Dong43089da2012-02-09 11:53:57 -08002386 // The last ctts box may not have been written yet, and this
2387 // is to make sure that we write out the last ctts box.
2388 if (currCttsOffsetTimeTicks == lastCttsOffsetTimeTicks) {
2389 if (cttsSampleCount > 0) {
2390 addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
2391 }
2392 }
2393
James Dongc5f0c712010-08-05 10:46:13 -07002394 mTrackDurationUs += lastDurationUs;
Andreas Huber25b13092009-09-09 16:36:12 -07002395 mReachedEOS = true;
James Dong43ec1df2011-05-06 11:27:59 -07002396
2397 sendTrackSummary(hasMultipleTracks);
2398
Steve Blockdf64d152012-01-04 20:05:49 +00002399 ALOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s",
James Dongc620cbd2012-07-27 17:14:44 -07002400 count, nZeroLengthFrames, mStszTableEntries->count(), mIsAudio? "audio": "video");
James Dong872a4812010-10-03 10:59:26 -07002401 if (mIsAudio) {
Steve Blockdf64d152012-01-04 20:05:49 +00002402 ALOGI("Audio track drift time: %lld us", mOwner->getDriftTimeUs());
James Dong872a4812010-10-03 10:59:26 -07002403 }
James Dong365a9632010-06-04 13:59:27 -07002404
James Dong37187912010-08-18 19:10:39 -07002405 if (err == ERROR_END_OF_STREAM) {
2406 return OK;
2407 }
2408 return err;
James Dong365a9632010-06-04 13:59:27 -07002409}
2410
James Dong45c254c2011-06-21 17:22:37 -07002411bool MPEG4Writer::Track::isTrackMalFormed() const {
James Dongc620cbd2012-07-27 17:14:44 -07002412 if (mStszTableEntries->count() == 0) { // no samples written
Steve Block29357bc2012-01-06 19:20:56 +00002413 ALOGE("The number of recorded samples is 0");
James Dong45c254c2011-06-21 17:22:37 -07002414 return true;
2415 }
2416
James Dongc620cbd2012-07-27 17:14:44 -07002417 if (!mIsAudio && mStssTableEntries->count() == 0) { // no sync frames for video
Steve Block29357bc2012-01-06 19:20:56 +00002418 ALOGE("There are no sync frames for video track");
James Dong45c254c2011-06-21 17:22:37 -07002419 return true;
2420 }
2421
2422 if (OK != checkCodecSpecificData()) { // no codec specific data
2423 return true;
2424 }
2425
2426 return false;
2427}
2428
James Dong43ec1df2011-05-06 11:27:59 -07002429void MPEG4Writer::Track::sendTrackSummary(bool hasMultipleTracks) {
James Dong07ec0192011-05-14 07:22:40 -07002430
2431 // Send track summary only if test mode is enabled.
2432 if (!isTestModeEnabled()) {
2433 return;
2434 }
2435
James Dong43ec1df2011-05-06 11:27:59 -07002436 int trackNum = (mTrackId << 28);
2437
2438 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
2439 trackNum | MEDIA_RECORDER_TRACK_INFO_TYPE,
2440 mIsAudio? 0: 1);
2441
2442 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
2443 trackNum | MEDIA_RECORDER_TRACK_INFO_DURATION_MS,
2444 mTrackDurationUs / 1000);
2445
2446 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
2447 trackNum | MEDIA_RECORDER_TRACK_INFO_ENCODED_FRAMES,
James Dongc620cbd2012-07-27 17:14:44 -07002448 mStszTableEntries->count());
James Dong43ec1df2011-05-06 11:27:59 -07002449
James Dong86b7f472011-05-09 16:56:25 -07002450 {
2451 // The system delay time excluding the requested initial delay that
2452 // is used to eliminate the recording sound.
2453 int64_t startTimeOffsetUs = mOwner->getStartTimeOffsetMs() * 1000LL;
2454 if (startTimeOffsetUs < 0) { // Start time offset was not set
2455 startTimeOffsetUs = kInitialDelayTimeUs;
2456 }
2457 int64_t initialDelayUs =
2458 mFirstSampleTimeRealUs - mStartTimeRealUs - startTimeOffsetUs;
2459
2460 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
James Dong70ccfd42011-05-06 12:19:04 -07002461 trackNum | MEDIA_RECORDER_TRACK_INFO_INITIAL_DELAY_MS,
2462 (initialDelayUs) / 1000);
James Dong86b7f472011-05-09 16:56:25 -07002463 }
James Dong70ccfd42011-05-06 12:19:04 -07002464
James Dong07ec0192011-05-14 07:22:40 -07002465 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
2466 trackNum | MEDIA_RECORDER_TRACK_INFO_DATA_KBYTES,
2467 mMdatSizeBytes / 1024);
2468
James Dong43ec1df2011-05-06 11:27:59 -07002469 if (hasMultipleTracks) {
2470 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
2471 trackNum | MEDIA_RECORDER_TRACK_INFO_MAX_CHUNK_DUR_MS,
2472 mMaxChunkDurationUs / 1000);
James Dong70ccfd42011-05-06 12:19:04 -07002473
2474 int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
2475 if (mStartTimestampUs != moovStartTimeUs) {
2476 int64_t startTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
2477 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
2478 trackNum | MEDIA_RECORDER_TRACK_INFO_START_OFFSET_MS,
2479 startTimeOffsetUs / 1000);
2480 }
James Dong43ec1df2011-05-06 11:27:59 -07002481 }
2482}
2483
James Dongfaed5cd2010-07-15 19:08:20 -07002484void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) {
Steve Block3856b092011-10-20 11:56:00 +01002485 ALOGV("trackProgressStatus: %lld us", timeUs);
James Dongc620cbd2012-07-27 17:14:44 -07002486
James Dong215381e2010-06-26 08:24:47 -07002487 if (mTrackEveryTimeDurationUs > 0 &&
2488 timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
Steve Block3856b092011-10-20 11:56:00 +01002489 ALOGV("Fire time tracking progress status at %lld us", timeUs);
James Dongbc07bcc2011-03-18 11:25:41 -07002490 mOwner->trackProgressStatus(mTrackId, timeUs - mPreviousTrackTimeUs, err);
James Dong93d6b102010-06-24 19:04:27 -07002491 mPreviousTrackTimeUs = timeUs;
2492 }
2493}
2494
James Dongfaed5cd2010-07-15 19:08:20 -07002495void MPEG4Writer::trackProgressStatus(
James Dongbc07bcc2011-03-18 11:25:41 -07002496 size_t trackId, int64_t timeUs, status_t err) {
James Dongfaed5cd2010-07-15 19:08:20 -07002497 Mutex::Autolock lock(mLock);
James Dongbc07bcc2011-03-18 11:25:41 -07002498 int32_t trackNum = (trackId << 28);
James Dongfaed5cd2010-07-15 19:08:20 -07002499
2500 // Error notification
2501 // Do not consider ERROR_END_OF_STREAM an error
2502 if (err != OK && err != ERROR_END_OF_STREAM) {
James Dongbc07bcc2011-03-18 11:25:41 -07002503 notify(MEDIA_RECORDER_TRACK_EVENT_ERROR,
2504 trackNum | MEDIA_RECORDER_TRACK_ERROR_GENERAL,
James Dongfaed5cd2010-07-15 19:08:20 -07002505 err);
2506 return;
2507 }
2508
2509 if (timeUs == -1) {
2510 // Send completion notification
James Dongbc07bcc2011-03-18 11:25:41 -07002511 notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
2512 trackNum | MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS,
James Dongfaed5cd2010-07-15 19:08:20 -07002513 err);
James Dongfaed5cd2010-07-15 19:08:20 -07002514 } else {
2515 // Send progress status
James Dongbc07bcc2011-03-18 11:25:41 -07002516 notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
2517 trackNum | MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME,
James Dongfaed5cd2010-07-15 19:08:20 -07002518 timeUs / 1000);
2519 }
2520}
2521
James Dongd707fcb2010-09-01 18:48:35 -07002522void MPEG4Writer::setDriftTimeUs(int64_t driftTimeUs) {
Steve Block3856b092011-10-20 11:56:00 +01002523 ALOGV("setDriftTimeUs: %lld us", driftTimeUs);
James Donge2595312010-08-02 19:13:40 -07002524 Mutex::Autolock autolock(mLock);
James Dongd707fcb2010-09-01 18:48:35 -07002525 mDriftTimeUs = driftTimeUs;
James Donge2595312010-08-02 19:13:40 -07002526}
2527
2528int64_t MPEG4Writer::getDriftTimeUs() {
Steve Block3856b092011-10-20 11:56:00 +01002529 ALOGV("getDriftTimeUs: %lld us", mDriftTimeUs);
James Donge2595312010-08-02 19:13:40 -07002530 Mutex::Autolock autolock(mLock);
2531 return mDriftTimeUs;
2532}
2533
James Dongb4d53202010-09-02 10:49:55 -07002534bool MPEG4Writer::useNalLengthFour() {
2535 return mUse4ByteNalLength;
2536}
2537
James Dong1c9747a2010-07-30 17:41:22 -07002538void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) {
Steve Block3856b092011-10-20 11:56:00 +01002539 ALOGV("bufferChunk");
James Dong1c9747a2010-07-30 17:41:22 -07002540
James Dong1c9747a2010-07-30 17:41:22 -07002541 Chunk chunk(this, timestampUs, mChunkSamples);
2542 mOwner->bufferChunk(chunk);
James Dong13aec892010-04-21 16:14:15 -07002543 mChunkSamples.clear();
Andreas Huber20111aa2009-07-14 16:56:47 -07002544}
2545
Andreas Huber3b240d62010-02-02 12:13:30 -08002546int64_t MPEG4Writer::Track::getDurationUs() const {
James Dongc5f0c712010-08-05 10:46:13 -07002547 return mTrackDurationUs;
Andreas Huber20111aa2009-07-14 16:56:47 -07002548}
2549
James Dongd599cd42010-05-11 14:57:02 -07002550int64_t MPEG4Writer::Track::getEstimatedTrackSizeBytes() const {
2551 return mEstimatedTrackSizeBytes;
2552}
2553
James Dong690f5462010-08-19 13:52:47 -07002554status_t MPEG4Writer::Track::checkCodecSpecificData() const {
2555 const char *mime;
2556 CHECK(mMeta->findCString(kKeyMIMEType, &mime));
2557 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime) ||
2558 !strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) ||
2559 !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
2560 if (!mCodecSpecificData ||
2561 mCodecSpecificDataSize <= 0) {
Steve Block29357bc2012-01-06 19:20:56 +00002562 ALOGE("Missing codec specific data");
James Dong690f5462010-08-19 13:52:47 -07002563 return ERROR_MALFORMED;
2564 }
2565 } else {
2566 if (mCodecSpecificData ||
2567 mCodecSpecificDataSize > 0) {
Steve Block29357bc2012-01-06 19:20:56 +00002568 ALOGE("Unexepected codec specific data found");
James Dong690f5462010-08-19 13:52:47 -07002569 return ERROR_MALFORMED;
2570 }
2571 }
2572 return OK;
2573}
2574
James Dongb21c5642011-05-06 16:55:39 -07002575void MPEG4Writer::Track::writeTrackHeader(bool use32BitOffset) {
Andreas Huber20111aa2009-07-14 16:56:47 -07002576
Steve Block3856b092011-10-20 11:56:00 +01002577 ALOGV("%s track time scale: %d",
James Dong1c9747a2010-07-30 17:41:22 -07002578 mIsAudio? "Audio": "Video", mTimeScale);
James Dong8f5f2fc2010-07-02 11:39:06 -07002579
Johannes Carlssonefcdf182012-03-08 11:06:13 +01002580 uint32_t now = getMpeg4Time();
James Dongb21c5642011-05-06 16:55:39 -07002581 mOwner->beginBox("trak");
2582 writeTkhdBox(now);
2583 mOwner->beginBox("mdia");
2584 writeMdhdBox(now);
2585 writeHdlrBox();
2586 mOwner->beginBox("minf");
2587 if (mIsAudio) {
2588 writeSmhdBox();
2589 } else {
2590 writeVmhdBox();
2591 }
2592 writeDinfBox();
2593 writeStblBox(use32BitOffset);
2594 mOwner->endBox(); // minf
2595 mOwner->endBox(); // mdia
2596 mOwner->endBox(); // trak
2597}
Andreas Huber20111aa2009-07-14 16:56:47 -07002598
James Dongb21c5642011-05-06 16:55:39 -07002599void MPEG4Writer::Track::writeStblBox(bool use32BitOffset) {
2600 mOwner->beginBox("stbl");
2601 mOwner->beginBox("stsd");
2602 mOwner->writeInt32(0); // version=0, flags=0
2603 mOwner->writeInt32(1); // entry count
2604 if (mIsAudio) {
2605 writeAudioFourCCBox();
2606 } else {
2607 writeVideoFourCCBox();
2608 }
2609 mOwner->endBox(); // stsd
2610 writeSttsBox();
James Dong965e4232011-06-07 19:45:54 -07002611 writeCttsBox();
James Dongb21c5642011-05-06 16:55:39 -07002612 if (!mIsAudio) {
2613 writeStssBox();
2614 }
2615 writeStszBox();
2616 writeStscBox();
2617 writeStcoBox(use32BitOffset);
2618 mOwner->endBox(); // stbl
2619}
2620
2621void MPEG4Writer::Track::writeVideoFourCCBox() {
2622 const char *mime;
2623 bool success = mMeta->findCString(kKeyMIMEType, &mime);
2624 CHECK(success);
2625 if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
2626 mOwner->beginBox("mp4v");
2627 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
2628 mOwner->beginBox("s263");
2629 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
2630 mOwner->beginBox("avc1");
2631 } else {
Steve Block29357bc2012-01-06 19:20:56 +00002632 ALOGE("Unknown mime type '%s'.", mime);
James Dongb21c5642011-05-06 16:55:39 -07002633 CHECK(!"should not be here, unknown mime type.");
2634 }
2635
2636 mOwner->writeInt32(0); // reserved
2637 mOwner->writeInt16(0); // reserved
2638 mOwner->writeInt16(1); // data ref index
2639 mOwner->writeInt16(0); // predefined
2640 mOwner->writeInt16(0); // reserved
2641 mOwner->writeInt32(0); // predefined
2642 mOwner->writeInt32(0); // predefined
2643 mOwner->writeInt32(0); // predefined
2644
2645 int32_t width, height;
2646 success = mMeta->findInt32(kKeyWidth, &width);
2647 success = success && mMeta->findInt32(kKeyHeight, &height);
2648 CHECK(success);
2649
2650 mOwner->writeInt16(width);
2651 mOwner->writeInt16(height);
2652 mOwner->writeInt32(0x480000); // horiz resolution
2653 mOwner->writeInt32(0x480000); // vert resolution
2654 mOwner->writeInt32(0); // reserved
2655 mOwner->writeInt16(1); // frame count
Martin Storsjo89b28bf2013-01-30 18:45:30 +02002656 mOwner->writeInt8(0); // compressor string length
2657 mOwner->write(" ", 31);
James Dongb21c5642011-05-06 16:55:39 -07002658 mOwner->writeInt16(0x18); // depth
2659 mOwner->writeInt16(-1); // predefined
2660
James Dong43089da2012-02-09 11:53:57 -08002661 CHECK_LT(23 + mCodecSpecificDataSize, 128);
James Dongb21c5642011-05-06 16:55:39 -07002662
2663 if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
2664 writeMp4vEsdsBox();
2665 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
2666 writeD263Box();
2667 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
2668 writeAvccBox();
2669 }
2670
2671 writePaspBox();
2672 mOwner->endBox(); // mp4v, s263 or avc1
2673}
2674
2675void MPEG4Writer::Track::writeAudioFourCCBox() {
2676 const char *mime;
2677 bool success = mMeta->findCString(kKeyMIMEType, &mime);
2678 CHECK(success);
2679 const char *fourcc = NULL;
2680 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
2681 fourcc = "samr";
2682 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
2683 fourcc = "sawb";
2684 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
2685 fourcc = "mp4a";
2686 } else {
Steve Block29357bc2012-01-06 19:20:56 +00002687 ALOGE("Unknown mime type '%s'.", mime);
James Dongb21c5642011-05-06 16:55:39 -07002688 CHECK(!"should not be here, unknown mime type.");
2689 }
2690
2691 mOwner->beginBox(fourcc); // audio format
2692 mOwner->writeInt32(0); // reserved
2693 mOwner->writeInt16(0); // reserved
2694 mOwner->writeInt16(0x1); // data ref index
2695 mOwner->writeInt32(0); // reserved
2696 mOwner->writeInt32(0); // reserved
2697 int32_t nChannels;
2698 CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels));
2699 mOwner->writeInt16(nChannels); // channel count
2700 mOwner->writeInt16(16); // sample size
2701 mOwner->writeInt16(0); // predefined
2702 mOwner->writeInt16(0); // reserved
2703
2704 int32_t samplerate;
2705 success = mMeta->findInt32(kKeySampleRate, &samplerate);
2706 CHECK(success);
2707 mOwner->writeInt32(samplerate << 16);
2708 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
2709 writeMp4aEsdsBox();
2710 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime) ||
2711 !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
2712 writeDamrBox();
2713 }
2714 mOwner->endBox();
2715}
2716
2717void MPEG4Writer::Track::writeMp4aEsdsBox() {
2718 mOwner->beginBox("esds");
2719 CHECK(mCodecSpecificData);
James Dong43089da2012-02-09 11:53:57 -08002720 CHECK_GT(mCodecSpecificDataSize, 0);
James Dongb21c5642011-05-06 16:55:39 -07002721
2722 // Make sure all sizes encode to a single byte.
James Dong43089da2012-02-09 11:53:57 -08002723 CHECK_LT(mCodecSpecificDataSize + 23, 128);
James Dongb21c5642011-05-06 16:55:39 -07002724
2725 mOwner->writeInt32(0); // version=0, flags=0
2726 mOwner->writeInt8(0x03); // ES_DescrTag
2727 mOwner->writeInt8(23 + mCodecSpecificDataSize);
2728 mOwner->writeInt16(0x0000);// ES_ID
2729 mOwner->writeInt8(0x00);
2730
2731 mOwner->writeInt8(0x04); // DecoderConfigDescrTag
2732 mOwner->writeInt8(15 + mCodecSpecificDataSize);
2733 mOwner->writeInt8(0x40); // objectTypeIndication ISO/IEC 14492-2
2734 mOwner->writeInt8(0x15); // streamType AudioStream
2735
2736 mOwner->writeInt16(0x03); // XXX
2737 mOwner->writeInt8(0x00); // buffer size 24-bit
2738 mOwner->writeInt32(96000); // max bit rate
2739 mOwner->writeInt32(96000); // avg bit rate
2740
2741 mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
2742 mOwner->writeInt8(mCodecSpecificDataSize);
2743 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
2744
2745 static const uint8_t kData2[] = {
2746 0x06, // SLConfigDescriptorTag
2747 0x01,
2748 0x02
2749 };
2750 mOwner->write(kData2, sizeof(kData2));
2751
2752 mOwner->endBox(); // esds
2753}
2754
2755void MPEG4Writer::Track::writeMp4vEsdsBox() {
2756 CHECK(mCodecSpecificData);
James Dong43089da2012-02-09 11:53:57 -08002757 CHECK_GT(mCodecSpecificDataSize, 0);
James Dongb21c5642011-05-06 16:55:39 -07002758 mOwner->beginBox("esds");
2759
2760 mOwner->writeInt32(0); // version=0, flags=0
2761
2762 mOwner->writeInt8(0x03); // ES_DescrTag
2763 mOwner->writeInt8(23 + mCodecSpecificDataSize);
2764 mOwner->writeInt16(0x0000); // ES_ID
2765 mOwner->writeInt8(0x1f);
2766
2767 mOwner->writeInt8(0x04); // DecoderConfigDescrTag
2768 mOwner->writeInt8(15 + mCodecSpecificDataSize);
2769 mOwner->writeInt8(0x20); // objectTypeIndication ISO/IEC 14492-2
2770 mOwner->writeInt8(0x11); // streamType VisualStream
2771
2772 static const uint8_t kData[] = {
2773 0x01, 0x77, 0x00,
2774 0x00, 0x03, 0xe8, 0x00,
2775 0x00, 0x03, 0xe8, 0x00
2776 };
2777 mOwner->write(kData, sizeof(kData));
2778
2779 mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
2780
2781 mOwner->writeInt8(mCodecSpecificDataSize);
2782 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
2783
2784 static const uint8_t kData2[] = {
2785 0x06, // SLConfigDescriptorTag
2786 0x01,
2787 0x02
2788 };
2789 mOwner->write(kData2, sizeof(kData2));
2790
2791 mOwner->endBox(); // esds
2792}
2793
Johannes Carlssonefcdf182012-03-08 11:06:13 +01002794void MPEG4Writer::Track::writeTkhdBox(uint32_t now) {
James Dongb21c5642011-05-06 16:55:39 -07002795 mOwner->beginBox("tkhd");
2796 // Flags = 7 to indicate that the track is enabled, and
2797 // part of the presentation
2798 mOwner->writeInt32(0x07); // version=0, flags=7
2799 mOwner->writeInt32(now); // creation time
2800 mOwner->writeInt32(now); // modification time
James Dong219f1952012-08-07 16:54:22 -07002801 mOwner->writeInt32(mTrackId); // track id starts with 1
James Dongb21c5642011-05-06 16:55:39 -07002802 mOwner->writeInt32(0); // reserved
2803 int64_t trakDurationUs = getDurationUs();
2804 int32_t mvhdTimeScale = mOwner->getTimeScale();
2805 int32_t tkhdDuration =
2806 (trakDurationUs * mvhdTimeScale + 5E5) / 1E6;
2807 mOwner->writeInt32(tkhdDuration); // in mvhd timescale
2808 mOwner->writeInt32(0); // reserved
2809 mOwner->writeInt32(0); // reserved
2810 mOwner->writeInt16(0); // layer
2811 mOwner->writeInt16(0); // alternate group
2812 mOwner->writeInt16(mIsAudio ? 0x100 : 0); // volume
2813 mOwner->writeInt16(0); // reserved
2814
2815 mOwner->writeCompositionMatrix(mRotation); // matrix
2816
2817 if (mIsAudio) {
2818 mOwner->writeInt32(0);
2819 mOwner->writeInt32(0);
2820 } else {
2821 int32_t width, height;
2822 bool success = mMeta->findInt32(kKeyWidth, &width);
2823 success = success && mMeta->findInt32(kKeyHeight, &height);
2824 CHECK(success);
2825
2826 mOwner->writeInt32(width << 16); // 32-bit fixed-point value
2827 mOwner->writeInt32(height << 16); // 32-bit fixed-point value
2828 }
2829 mOwner->endBox(); // tkhd
2830}
2831
2832void MPEG4Writer::Track::writeVmhdBox() {
2833 mOwner->beginBox("vmhd");
2834 mOwner->writeInt32(0x01); // version=0, flags=1
2835 mOwner->writeInt16(0); // graphics mode
2836 mOwner->writeInt16(0); // opcolor
2837 mOwner->writeInt16(0);
2838 mOwner->writeInt16(0);
2839 mOwner->endBox();
2840}
2841
2842void MPEG4Writer::Track::writeSmhdBox() {
2843 mOwner->beginBox("smhd");
2844 mOwner->writeInt32(0); // version=0, flags=0
2845 mOwner->writeInt16(0); // balance
2846 mOwner->writeInt16(0); // reserved
2847 mOwner->endBox();
2848}
2849
2850void MPEG4Writer::Track::writeHdlrBox() {
2851 mOwner->beginBox("hdlr");
2852 mOwner->writeInt32(0); // version=0, flags=0
2853 mOwner->writeInt32(0); // component type: should be mhlr
2854 mOwner->writeFourcc(mIsAudio ? "soun" : "vide"); // component subtype
2855 mOwner->writeInt32(0); // reserved
2856 mOwner->writeInt32(0); // reserved
2857 mOwner->writeInt32(0); // reserved
2858 // Removing "r" for the name string just makes the string 4 byte aligned
2859 mOwner->writeCString(mIsAudio ? "SoundHandle": "VideoHandle"); // name
2860 mOwner->endBox();
2861}
2862
Johannes Carlssonefcdf182012-03-08 11:06:13 +01002863void MPEG4Writer::Track::writeMdhdBox(uint32_t now) {
James Dongb21c5642011-05-06 16:55:39 -07002864 int64_t trakDurationUs = getDurationUs();
2865 mOwner->beginBox("mdhd");
2866 mOwner->writeInt32(0); // version=0, flags=0
2867 mOwner->writeInt32(now); // creation time
2868 mOwner->writeInt32(now); // modification time
2869 mOwner->writeInt32(mTimeScale); // media timescale
2870 int32_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6;
2871 mOwner->writeInt32(mdhdDuration); // use media timescale
2872 // Language follows the three letter standard ISO-639-2/T
2873 // 'e', 'n', 'g' for "English", for instance.
2874 // Each character is packed as the difference between its ASCII value and 0x60.
2875 // For "English", these are 00101, 01110, 00111.
2876 // XXX: Where is the padding bit located: 0x15C7?
2877 mOwner->writeInt16(0); // language code
2878 mOwner->writeInt16(0); // predefined
2879 mOwner->endBox();
2880}
2881
2882void MPEG4Writer::Track::writeDamrBox() {
2883 // 3gpp2 Spec AMRSampleEntry fields
2884 mOwner->beginBox("damr");
2885 mOwner->writeCString(" "); // vendor: 4 bytes
2886 mOwner->writeInt8(0); // decoder version
2887 mOwner->writeInt16(0x83FF); // mode set: all enabled
2888 mOwner->writeInt8(0); // mode change period
2889 mOwner->writeInt8(1); // frames per sample
2890 mOwner->endBox();
2891}
2892
2893void MPEG4Writer::Track::writeUrlBox() {
2894 // The table index here refers to the sample description index
2895 // in the sample table entries.
2896 mOwner->beginBox("url ");
2897 mOwner->writeInt32(1); // version=0, flags=1 (self-contained)
2898 mOwner->endBox(); // url
2899}
2900
2901void MPEG4Writer::Track::writeDrefBox() {
2902 mOwner->beginBox("dref");
2903 mOwner->writeInt32(0); // version=0, flags=0
2904 mOwner->writeInt32(1); // entry count (either url or urn)
2905 writeUrlBox();
2906 mOwner->endBox(); // dref
2907}
2908
2909void MPEG4Writer::Track::writeDinfBox() {
2910 mOwner->beginBox("dinf");
2911 writeDrefBox();
2912 mOwner->endBox(); // dinf
2913}
2914
2915void MPEG4Writer::Track::writeAvccBox() {
2916 CHECK(mCodecSpecificData);
James Dong43089da2012-02-09 11:53:57 -08002917 CHECK_GE(mCodecSpecificDataSize, 5);
James Dongb21c5642011-05-06 16:55:39 -07002918
2919 // Patch avcc's lengthSize field to match the number
2920 // of bytes we use to indicate the size of a nal unit.
2921 uint8_t *ptr = (uint8_t *)mCodecSpecificData;
2922 ptr[4] = (ptr[4] & 0xfc) | (mOwner->useNalLengthFour() ? 3 : 1);
2923 mOwner->beginBox("avcC");
2924 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
2925 mOwner->endBox(); // avcC
2926}
2927
2928void MPEG4Writer::Track::writeD263Box() {
2929 mOwner->beginBox("d263");
2930 mOwner->writeInt32(0); // vendor
2931 mOwner->writeInt8(0); // decoder version
2932 mOwner->writeInt8(10); // level: 10
2933 mOwner->writeInt8(0); // profile: 0
2934 mOwner->endBox(); // d263
2935}
2936
2937// This is useful if the pixel is not square
2938void MPEG4Writer::Track::writePaspBox() {
2939 mOwner->beginBox("pasp");
2940 mOwner->writeInt32(1 << 16); // hspacing
2941 mOwner->writeInt32(1 << 16); // vspacing
2942 mOwner->endBox(); // pasp
2943}
2944
James Dong000e1832012-02-06 23:46:37 -08002945int32_t MPEG4Writer::Track::getStartTimeOffsetScaledTime() const {
James Donga4726132011-02-16 12:28:26 -08002946 int64_t trackStartTimeOffsetUs = 0;
James Dongb21c5642011-05-06 16:55:39 -07002947 int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
2948 if (mStartTimestampUs != moovStartTimeUs) {
James Dong43089da2012-02-09 11:53:57 -08002949 CHECK_GT(mStartTimestampUs, moovStartTimeUs);
James Dongb21c5642011-05-06 16:55:39 -07002950 trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
2951 }
James Dong000e1832012-02-06 23:46:37 -08002952 return (trackStartTimeOffsetUs * mTimeScale + 500000LL) / 1000000LL;
2953}
2954
2955void MPEG4Writer::Track::writeSttsBox() {
2956 mOwner->beginBox("stts");
2957 mOwner->writeInt32(0); // version=0, flags=0
James Dongc620cbd2012-07-27 17:14:44 -07002958 uint32_t duration;
2959 CHECK(mSttsTableEntries->get(duration, 1));
2960 duration = htonl(duration); // Back to host byte order
2961 mSttsTableEntries->set(htonl(duration + getStartTimeOffsetScaledTime()), 1);
2962 mSttsTableEntries->write(mOwner);
James Dongb21c5642011-05-06 16:55:39 -07002963 mOwner->endBox(); // stts
2964}
Andreas Huber20111aa2009-07-14 16:56:47 -07002965
James Dong965e4232011-06-07 19:45:54 -07002966void MPEG4Writer::Track::writeCttsBox() {
2967 if (mIsAudio) { // ctts is not for audio
2968 return;
2969 }
2970
James Dong000e1832012-02-06 23:46:37 -08002971 // There is no B frame at all
2972 if (mMinCttsOffsetTimeUs == mMaxCttsOffsetTimeUs) {
2973 return;
2974 }
2975
James Dong965e4232011-06-07 19:45:54 -07002976 // Do not write ctts box when there is no need to have it.
James Dongc620cbd2012-07-27 17:14:44 -07002977 if (mCttsTableEntries->count() == 0) {
James Dong965e4232011-06-07 19:45:54 -07002978 return;
2979 }
2980
James Dongc620cbd2012-07-27 17:14:44 -07002981 ALOGV("ctts box has %d entries with range [%lld, %lld]",
2982 mCttsTableEntries->count(), mMinCttsOffsetTimeUs, mMaxCttsOffsetTimeUs);
James Dong965e4232011-06-07 19:45:54 -07002983
2984 mOwner->beginBox("ctts");
James Dong000e1832012-02-06 23:46:37 -08002985 mOwner->writeInt32(0); // version=0, flags=0
James Dongc620cbd2012-07-27 17:14:44 -07002986 uint32_t duration;
2987 CHECK(mCttsTableEntries->get(duration, 1));
2988 duration = htonl(duration); // Back host byte order
2989 mCttsTableEntries->set(htonl(duration + getStartTimeOffsetScaledTime() - mMinCttsOffsetTimeUs), 1);
2990 mCttsTableEntries->write(mOwner);
James Dong965e4232011-06-07 19:45:54 -07002991 mOwner->endBox(); // ctts
2992}
2993
James Dongb21c5642011-05-06 16:55:39 -07002994void MPEG4Writer::Track::writeStssBox() {
2995 mOwner->beginBox("stss");
2996 mOwner->writeInt32(0); // version=0, flags=0
James Dongc620cbd2012-07-27 17:14:44 -07002997 mStssTableEntries->write(mOwner);
James Dongb21c5642011-05-06 16:55:39 -07002998 mOwner->endBox(); // stss
2999}
Andreas Huber20111aa2009-07-14 16:56:47 -07003000
James Dongb21c5642011-05-06 16:55:39 -07003001void MPEG4Writer::Track::writeStszBox() {
3002 mOwner->beginBox("stsz");
3003 mOwner->writeInt32(0); // version=0, flags=0
James Dongc620cbd2012-07-27 17:14:44 -07003004 mOwner->writeInt32(0);
3005 mStszTableEntries->write(mOwner);
James Dongb21c5642011-05-06 16:55:39 -07003006 mOwner->endBox(); // stsz
3007}
Andreas Huber20111aa2009-07-14 16:56:47 -07003008
James Dongb21c5642011-05-06 16:55:39 -07003009void MPEG4Writer::Track::writeStscBox() {
3010 mOwner->beginBox("stsc");
3011 mOwner->writeInt32(0); // version=0, flags=0
James Dongc620cbd2012-07-27 17:14:44 -07003012 mStscTableEntries->write(mOwner);
James Dongb21c5642011-05-06 16:55:39 -07003013 mOwner->endBox(); // stsc
3014}
James Dong3c0131f2010-05-13 11:47:36 -07003015
James Dongb21c5642011-05-06 16:55:39 -07003016void MPEG4Writer::Track::writeStcoBox(bool use32BitOffset) {
3017 mOwner->beginBox(use32BitOffset? "stco": "co64");
3018 mOwner->writeInt32(0); // version=0, flags=0
James Dongc620cbd2012-07-27 17:14:44 -07003019 if (use32BitOffset) {
3020 mStcoTableEntries->write(mOwner);
3021 } else {
3022 mCo64TableEntries->write(mOwner);
James Dongb21c5642011-05-06 16:55:39 -07003023 }
3024 mOwner->endBox(); // stco or co64
Andreas Huber20111aa2009-07-14 16:56:47 -07003025}
3026
James Dong07b1bb52011-05-11 19:09:25 -07003027void MPEG4Writer::writeUdtaBox() {
3028 beginBox("udta");
3029 writeGeoDataBox();
3030 endBox();
3031}
3032
3033/*
3034 * Geodata is stored according to ISO-6709 standard.
3035 */
3036void MPEG4Writer::writeGeoDataBox() {
3037 beginBox("\xA9xyz");
3038 /*
3039 * For historical reasons, any user data start
3040 * with "\0xA9", must be followed by its assoicated
3041 * language code.
James Dong432ec372011-06-03 17:09:26 -07003042 * 0x0012: text string length
3043 * 0x15c7: lang (locale) code: en
James Dong07b1bb52011-05-11 19:09:25 -07003044 */
3045 writeInt32(0x001215c7);
3046 writeLatitude(mLatitudex10000);
3047 writeLongitude(mLongitudex10000);
3048 writeInt8(0x2F);
3049 endBox();
3050}
3051
Andreas Huber20111aa2009-07-14 16:56:47 -07003052} // namespace android