1 /*
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
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "MPEG4Writer"
19
20 #include <algorithm>
21
22 #include <arpa/inet.h>
23 #include <fcntl.h>
24 #include <inttypes.h>
25 #include <pthread.h>
26 #include <sys/prctl.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30
31 #include <utils/Log.h>
32
33 #include <functional>
34
35 #include <media/MediaSource.h>
36 #include <media/stagefright/foundation/ADebug.h>
37 #include <media/stagefright/foundation/AMessage.h>
38 #include <media/stagefright/foundation/AUtils.h>
39 #include <media/stagefright/foundation/ByteUtils.h>
40 #include <media/stagefright/foundation/ColorUtils.h>
41 #include <media/stagefright/foundation/avc_utils.h>
42 #include <media/stagefright/MPEG4Writer.h>
43 #include <media/stagefright/MediaBuffer.h>
44 #include <media/stagefright/MetaData.h>
45 #include <media/stagefright/MediaDefs.h>
46 #include <media/stagefright/MediaErrors.h>
47 #include <media/stagefright/Utils.h>
48 #include <media/mediarecorder.h>
49 #include <cutils/properties.h>
50
51 #include "include/ESDS.h"
52 #include "include/HevcUtils.h"
53
54 #ifndef __predict_false
55 #define __predict_false(exp) __builtin_expect((exp) != 0, 0)
56 #endif
57
58 #define WARN_UNLESS(condition, message, ...) \
59 ( (__predict_false(condition)) ? false : ({ \
60 ALOGW("Condition %s failed " message, #condition, ##__VA_ARGS__); \
61 true; \
62 }))
63
64 namespace android {
65
66 static const int64_t kMinStreamableFileSizeInBytes = 5 * 1024 * 1024;
67 static const int64_t kMax32BitFileSize = 0x00ffffffffLL; // 2^32-1 : max FAT32
68 // filesystem file size
69 // used by most SD cards
70 static const uint8_t kNalUnitTypeSeqParamSet = 0x07;
71 static const uint8_t kNalUnitTypePicParamSet = 0x08;
72 static const int64_t kInitialDelayTimeUs = 700000LL;
73 static const int64_t kMaxMetadataSize = 0x4000000LL; // 64MB max per-frame metadata size
74 static const int64_t kMaxCttsOffsetTimeUs = 30 * 60 * 1000000LL; // 30 minutes
75 static const size_t kESDSScratchBufferSize = 10; // kMaxAtomSize in Mpeg4Extractor 64MB
76
77 static const char kMetaKey_Version[] = "com.android.version";
78 static const char kMetaKey_Manufacturer[] = "com.android.manufacturer";
79 static const char kMetaKey_Model[] = "com.android.model";
80
81 #ifdef SHOW_BUILD
82 static const char kMetaKey_Build[] = "com.android.build";
83 #endif
84 static const char kMetaKey_CaptureFps[] = "com.android.capture.fps";
85 static const char kMetaKey_TemporalLayerCount[] = "com.android.video.temporal_layers_count";
86
87 static const int kTimestampDebugCount = 10;
88 static const int kItemIdBase = 10000;
89 static const char kExifHeader[] = {'E', 'x', 'i', 'f', '\0', '\0'};
90 static const uint8_t kExifApp1Marker[] = {'E', 'x', 'i', 'f', 0xff, 0xe1};
91
92 static const uint8_t kMandatoryHevcNalUnitTypes[3] = {
93 kHevcNalUnitTypeVps,
94 kHevcNalUnitTypeSps,
95 kHevcNalUnitTypePps,
96 };
97 static const uint8_t kHevcNalUnitTypes[5] = {
98 kHevcNalUnitTypeVps,
99 kHevcNalUnitTypeSps,
100 kHevcNalUnitTypePps,
101 kHevcNalUnitTypePrefixSei,
102 kHevcNalUnitTypeSuffixSei,
103 };
104 /* uncomment to include build in meta */
105 //#define SHOW_MODEL_BUILD 1
106
107 class MPEG4Writer::Track {
108 public:
109 Track(MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId);
110
111 ~Track();
112
113 status_t start(MetaData *params);
114 status_t stop(bool stopSource = true);
115 status_t pause();
116 bool reachedEOS();
117
118 int64_t getDurationUs() const;
119 int64_t getEstimatedTrackSizeBytes() const;
120 int32_t getMetaSizeIncrease(int32_t angle, int32_t trackCount) const;
121 void writeTrackHeader(bool use32BitOffset = true);
122 int64_t getMinCttsOffsetTimeUs();
123 void bufferChunk(int64_t timestampUs);
isAvc() const124 bool isAvc() const { return mIsAvc; }
isHevc() const125 bool isHevc() const { return mIsHevc; }
isHeic() const126 bool isHeic() const { return mIsHeic; }
isAudio() const127 bool isAudio() const { return mIsAudio; }
isMPEG4() const128 bool isMPEG4() const { return mIsMPEG4; }
usePrefix() const129 bool usePrefix() const { return mIsAvc || mIsHevc || mIsHeic; }
130 bool isExifData(MediaBufferBase *buffer, uint32_t *tiffHdrOffset) const;
131 void addChunkOffset(off64_t offset);
132 void addItemOffsetAndSize(off64_t offset, size_t size, bool isExif);
133 void flushItemRefs();
getTrackId() const134 int32_t getTrackId() const { return mTrackId; }
135 status_t dump(int fd, const Vector<String16>& args) const;
136 static const char *getFourCCForMime(const char *mime);
137 const char *getTrackType() const;
138 void resetInternal();
139
140 private:
141 // A helper class to handle faster write box with table entries
142 template<class TYPE, unsigned ENTRY_SIZE>
143 // ENTRY_SIZE: # of values in each entry
144 struct ListTableEntries {
145 static_assert(ENTRY_SIZE > 0, "ENTRY_SIZE must be positive");
ListTableEntriesandroid::MPEG4Writer::Track::ListTableEntries146 ListTableEntries(uint32_t elementCapacity)
147 : mElementCapacity(elementCapacity),
148 mTotalNumTableEntries(0),
149 mNumValuesInCurrEntry(0),
150 mCurrTableEntriesElement(NULL) {
151 CHECK_GT(mElementCapacity, 0u);
152 // Ensure no integer overflow on allocation in add().
153 CHECK_LT(ENTRY_SIZE, UINT32_MAX / mElementCapacity);
154 }
155
156 // Free the allocated memory.
~ListTableEntriesandroid::MPEG4Writer::Track::ListTableEntries157 ~ListTableEntries() {
158 while (!mTableEntryList.empty()) {
159 typename List<TYPE *>::iterator it = mTableEntryList.begin();
160 delete[] (*it);
161 mTableEntryList.erase(it);
162 }
163 }
164
165 // Replace the value at the given position by the given value.
166 // There must be an existing value at the given position.
167 // @arg value must be in network byte order
168 // @arg pos location the value must be in.
setandroid::MPEG4Writer::Track::ListTableEntries169 void set(const TYPE& value, uint32_t pos) {
170 CHECK_LT(pos, mTotalNumTableEntries * ENTRY_SIZE);
171
172 typename List<TYPE *>::iterator it = mTableEntryList.begin();
173 uint32_t iterations = (pos / (mElementCapacity * ENTRY_SIZE));
174 while (it != mTableEntryList.end() && iterations > 0) {
175 ++it;
176 --iterations;
177 }
178 CHECK(it != mTableEntryList.end());
179 CHECK_EQ(iterations, 0u);
180
181 (*it)[(pos % (mElementCapacity * ENTRY_SIZE))] = value;
182 }
183
184 // Get the value at the given position by the given value.
185 // @arg value the retrieved value at the position in network byte order.
186 // @arg pos location the value must be in.
187 // @return true if a value is found.
getandroid::MPEG4Writer::Track::ListTableEntries188 bool get(TYPE& value, uint32_t pos) const {
189 if (pos >= mTotalNumTableEntries * ENTRY_SIZE) {
190 return false;
191 }
192
193 typename List<TYPE *>::iterator it = mTableEntryList.begin();
194 uint32_t iterations = (pos / (mElementCapacity * ENTRY_SIZE));
195 while (it != mTableEntryList.end() && iterations > 0) {
196 ++it;
197 --iterations;
198 }
199 CHECK(it != mTableEntryList.end());
200 CHECK_EQ(iterations, 0u);
201
202 value = (*it)[(pos % (mElementCapacity * ENTRY_SIZE))];
203 return true;
204 }
205
206 // adjusts all values by |adjust(value)|
adjustEntriesandroid::MPEG4Writer::Track::ListTableEntries207 void adjustEntries(
208 std::function<void(size_t /* ix */, TYPE(& /* entry */)[ENTRY_SIZE])> update) {
209 size_t nEntries = mTotalNumTableEntries + mNumValuesInCurrEntry / ENTRY_SIZE;
210 size_t ix = 0;
211 for (TYPE *entryArray : mTableEntryList) {
212 size_t num = std::min(nEntries, (size_t)mElementCapacity);
213 for (size_t i = 0; i < num; ++i) {
214 update(ix++, (TYPE(&)[ENTRY_SIZE])(*entryArray));
215 entryArray += ENTRY_SIZE;
216 }
217 nEntries -= num;
218 }
219 }
220
221 // Store a single value.
222 // @arg value must be in network byte order.
addandroid::MPEG4Writer::Track::ListTableEntries223 void add(const TYPE& value) {
224 CHECK_LT(mNumValuesInCurrEntry, mElementCapacity);
225 uint32_t nEntries = mTotalNumTableEntries % mElementCapacity;
226 uint32_t nValues = mNumValuesInCurrEntry % ENTRY_SIZE;
227 if (nEntries == 0 && nValues == 0) {
228 mCurrTableEntriesElement = new TYPE[ENTRY_SIZE * mElementCapacity];
229 CHECK(mCurrTableEntriesElement != NULL);
230 mTableEntryList.push_back(mCurrTableEntriesElement);
231 }
232
233 uint32_t pos = nEntries * ENTRY_SIZE + nValues;
234 mCurrTableEntriesElement[pos] = value;
235
236 ++mNumValuesInCurrEntry;
237 if ((mNumValuesInCurrEntry % ENTRY_SIZE) == 0) {
238 ++mTotalNumTableEntries;
239 mNumValuesInCurrEntry = 0;
240 }
241 }
242
243 // Write out the table entries:
244 // 1. the number of entries goes first
245 // 2. followed by the values in the table enties in order
246 // @arg writer the writer to actual write to the storage
writeandroid::MPEG4Writer::Track::ListTableEntries247 void write(MPEG4Writer *writer) const {
248 CHECK_EQ(mNumValuesInCurrEntry % ENTRY_SIZE, 0u);
249 uint32_t nEntries = mTotalNumTableEntries;
250 writer->writeInt32(nEntries);
251 for (typename List<TYPE *>::iterator it = mTableEntryList.begin();
252 it != mTableEntryList.end(); ++it) {
253 CHECK_GT(nEntries, 0u);
254 if (nEntries >= mElementCapacity) {
255 writer->write(*it, sizeof(TYPE) * ENTRY_SIZE, mElementCapacity);
256 nEntries -= mElementCapacity;
257 } else {
258 writer->write(*it, sizeof(TYPE) * ENTRY_SIZE, nEntries);
259 break;
260 }
261 }
262 }
263
264 // Return the number of entries in the table.
countandroid::MPEG4Writer::Track::ListTableEntries265 uint32_t count() const { return mTotalNumTableEntries; }
266
267 private:
268 uint32_t mElementCapacity; // # entries in an element
269 uint32_t mTotalNumTableEntries;
270 uint32_t mNumValuesInCurrEntry; // up to ENTRY_SIZE
271 TYPE *mCurrTableEntriesElement;
272 mutable List<TYPE *> mTableEntryList;
273
274 DISALLOW_EVIL_CONSTRUCTORS(ListTableEntries);
275 };
276
277
278
279 MPEG4Writer *mOwner;
280 sp<MetaData> mMeta;
281 sp<MediaSource> mSource;
282 volatile bool mDone;
283 volatile bool mPaused;
284 volatile bool mResumed;
285 volatile bool mStarted;
286 bool mIsAvc;
287 bool mIsHevc;
288 bool mIsAudio;
289 bool mIsVideo;
290 bool mIsHeic;
291 bool mIsMPEG4;
292 bool mGotStartKeyFrame;
293 bool mIsMalformed;
294 int32_t mTrackId;
295 int64_t mTrackDurationUs;
296 int64_t mMaxChunkDurationUs;
297 int64_t mLastDecodingTimeUs;
298
299 int64_t mEstimatedTrackSizeBytes;
300 int64_t mMdatSizeBytes;
301 int32_t mTimeScale;
302
303 pthread_t mThread;
304
305
306 List<MediaBuffer *> mChunkSamples;
307
308 bool mSamplesHaveSameSize;
309 ListTableEntries<uint32_t, 1> *mStszTableEntries;
310
311 ListTableEntries<uint32_t, 1> *mStcoTableEntries;
312 ListTableEntries<off64_t, 1> *mCo64TableEntries;
313 ListTableEntries<uint32_t, 3> *mStscTableEntries;
314 ListTableEntries<uint32_t, 1> *mStssTableEntries;
315 ListTableEntries<uint32_t, 2> *mSttsTableEntries;
316 ListTableEntries<uint32_t, 2> *mCttsTableEntries;
317 ListTableEntries<uint32_t, 3> *mElstTableEntries; // 3columns: segDuration, mediaTime, mediaRate
318
319 int64_t mMinCttsOffsetTimeUs;
320 int64_t mMinCttsOffsetTicks;
321 int64_t mMaxCttsOffsetTicks;
322
323 // Save the last 10 frames' timestamp and frame type for debug.
324 struct TimestampDebugHelperEntry {
325 int64_t pts;
326 int64_t dts;
327 std::string frameType;
328 };
329
330 std::list<TimestampDebugHelperEntry> mTimestampDebugHelper;
331
332 // Sequence parameter set or picture parameter set
333 struct AVCParamSet {
AVCParamSetandroid::MPEG4Writer::Track::AVCParamSet334 AVCParamSet(uint16_t length, const uint8_t *data)
335 : mLength(length), mData(data) {}
336
337 uint16_t mLength;
338 const uint8_t *mData;
339 };
340 List<AVCParamSet> mSeqParamSets;
341 List<AVCParamSet> mPicParamSets;
342 uint8_t mProfileIdc;
343 uint8_t mProfileCompatible;
344 uint8_t mLevelIdc;
345
346 void *mCodecSpecificData;
347 size_t mCodecSpecificDataSize;
348 bool mGotAllCodecSpecificData;
349 bool mTrackingProgressStatus;
350
351 bool mReachedEOS;
352 int64_t mStartTimestampUs;
353 int64_t mStartTimeRealUs;
354 int64_t mFirstSampleTimeRealUs;
355 int64_t mPreviousTrackTimeUs;
356 int64_t mTrackEveryTimeDurationUs;
357
358 int32_t mRotation;
359
360 Vector<uint16_t> mProperties;
361 ItemRefs mDimgRefs;
362 Vector<uint16_t> mExifList;
363 uint16_t mImageItemId;
364 int32_t mIsPrimary;
365 int32_t mWidth, mHeight;
366 int32_t mTileWidth, mTileHeight;
367 int32_t mGridRows, mGridCols;
368 size_t mNumTiles, mTileIndex;
369
370 // Update the audio track's drift information.
371 void updateDriftTime(const sp<MetaData>& meta);
372
373 void dumpTimeStamps();
374
375 int64_t getStartTimeOffsetTimeUs() const;
376 int32_t getStartTimeOffsetScaledTime() const;
377
378 static void *ThreadWrapper(void *me);
379 status_t threadEntry();
380
381 const uint8_t *parseParamSet(
382 const uint8_t *data, size_t length, int type, size_t *paramSetLen);
383
384 status_t copyCodecSpecificData(const uint8_t *data, size_t size, size_t minLength = 0);
385
386 status_t makeAVCCodecSpecificData(const uint8_t *data, size_t size);
387 status_t copyAVCCodecSpecificData(const uint8_t *data, size_t size);
388 status_t parseAVCCodecSpecificData(const uint8_t *data, size_t size);
389
390 status_t makeHEVCCodecSpecificData(const uint8_t *data, size_t size);
391 status_t copyHEVCCodecSpecificData(const uint8_t *data, size_t size);
392 status_t parseHEVCCodecSpecificData(
393 const uint8_t *data, size_t size, HevcParameterSets ¶mSets);
394
395 // Track authoring progress status
396 void trackProgressStatus(int64_t timeUs, status_t err = OK);
397 void initTrackingProgressStatus(MetaData *params);
398
399 void getCodecSpecificDataFromInputFormatIfPossible();
400
401 // Determine the track time scale
402 // If it is an audio track, try to use the sampling rate as
403 // the time scale; however, if user chooses the overwrite
404 // value, the user-supplied time scale will be used.
405 void setTimeScale();
406
407 // Simple validation on the codec specific data
408 status_t checkCodecSpecificData() const;
409
410 void updateTrackSizeEstimate();
411 void addOneStscTableEntry(size_t chunkId, size_t sampleId);
412 void addOneStssTableEntry(size_t sampleId);
413
414 // Duration is time scale based
415 void addOneSttsTableEntry(size_t sampleCount, int32_t timescaledDur);
416 void addOneCttsTableEntry(size_t sampleCount, int32_t timescaledDur);
417 void addOneElstTableEntry(uint32_t segmentDuration, int32_t mediaTime,
418 int16_t mediaRate, int16_t mediaRateFraction);
419
420 bool isTrackMalFormed() const;
421 void sendTrackSummary(bool hasMultipleTracks);
422
423 // Write the boxes
424 void writeStcoBox(bool use32BitOffset);
425 void writeStscBox();
426 void writeStszBox();
427 void writeStssBox();
428 void writeSttsBox();
429 void writeCttsBox();
430 void writeD263Box();
431 void writePaspBox();
432 void writeAvccBox();
433 void writeHvccBox();
434 void writeUrlBox();
435 void writeDrefBox();
436 void writeDinfBox();
437 void writeDamrBox();
438 void writeMdhdBox(uint32_t now);
439 void writeSmhdBox();
440 void writeVmhdBox();
441 void writeNmhdBox();
442 void writeHdlrBox();
443 void writeTkhdBox(uint32_t now);
444 void writeColrBox();
445 void writeMp4aEsdsBox();
446 void writeMp4vEsdsBox();
447 void writeAudioFourCCBox();
448 void writeVideoFourCCBox();
449 void writeMetadataFourCCBox();
450 void writeStblBox(bool use32BitOffset);
451 void writeEdtsBox();
452
453 Track(const Track &);
454 Track &operator=(const Track &);
455 };
456
MPEG4Writer(int fd)457 MPEG4Writer::MPEG4Writer(int fd) {
458 initInternal(dup(fd), true /*isFirstSession*/);
459 }
460
~MPEG4Writer()461 MPEG4Writer::~MPEG4Writer() {
462 reset();
463
464 while (!mTracks.empty()) {
465 List<Track *>::iterator it = mTracks.begin();
466 delete *it;
467 (*it) = NULL;
468 mTracks.erase(it);
469 }
470 mTracks.clear();
471
472 if (mNextFd != -1) {
473 close(mNextFd);
474 }
475 }
476
initInternal(int fd,bool isFirstSession)477 void MPEG4Writer::initInternal(int fd, bool isFirstSession) {
478 ALOGV("initInternal");
479 mFd = fd;
480 mNextFd = -1;
481 mInitCheck = mFd < 0? NO_INIT: OK;
482
483 mInterleaveDurationUs = 1000000;
484
485 mStartTimestampUs = -1LL;
486 mStartTimeOffsetMs = -1;
487 mStartTimeOffsetBFramesUs = 0;
488 mPaused = false;
489 mStarted = false;
490 mWriterThreadStarted = false;
491 mSendNotify = false;
492
493 // Reset following variables for all the sessions and they will be
494 // initialized in start(MetaData *param).
495 mIsRealTimeRecording = true;
496 mUse4ByteNalLength = true;
497 mUse32BitOffset = true;
498 mOffset = 0;
499 mMdatOffset = 0;
500 mInMemoryCache = NULL;
501 mInMemoryCacheOffset = 0;
502 mInMemoryCacheSize = 0;
503 mWriteBoxToMemory = false;
504 mFreeBoxOffset = 0;
505 mStreamableFile = false;
506 mTimeScale = -1;
507 mHasFileLevelMeta = false;
508 mPrimaryItemId = 0;
509 mAssociationEntryCount = 0;
510 mNumGrids = 0;
511 mHasRefs = false;
512
513 // Following variables only need to be set for the first recording session.
514 // And they will stay the same for all the recording sessions.
515 if (isFirstSession) {
516 mMoovExtraSize = 0;
517 mHasMoovBox = false;
518 mMetaKeys = new AMessage();
519 addDeviceMeta();
520 mLatitudex10000 = 0;
521 mLongitudex10000 = 0;
522 mAreGeoTagsAvailable = false;
523 mSwitchPending = false;
524 mIsFileSizeLimitExplicitlyRequested = false;
525 }
526
527 // Verify mFd is seekable
528 off64_t off = lseek64(mFd, 0, SEEK_SET);
529 if (off < 0) {
530 ALOGE("cannot seek mFd: %s (%d) %lld", strerror(errno), errno, (long long)mFd);
531 release();
532 }
533 for (List<Track *>::iterator it = mTracks.begin();
534 it != mTracks.end(); ++it) {
535 (*it)->resetInternal();
536 }
537 }
538
dump(int fd,const Vector<String16> & args)539 status_t MPEG4Writer::dump(
540 int fd, const Vector<String16>& args) {
541 const size_t SIZE = 256;
542 char buffer[SIZE];
543 String8 result;
544 snprintf(buffer, SIZE, " MPEG4Writer %p\n", this);
545 result.append(buffer);
546 snprintf(buffer, SIZE, " mStarted: %s\n", mStarted? "true": "false");
547 result.append(buffer);
548 ::write(fd, result.string(), result.size());
549 for (List<Track *>::iterator it = mTracks.begin();
550 it != mTracks.end(); ++it) {
551 (*it)->dump(fd, args);
552 }
553 return OK;
554 }
555
dump(int fd,const Vector<String16> &) const556 status_t MPEG4Writer::Track::dump(
557 int fd, const Vector<String16>& /* args */) const {
558 const size_t SIZE = 256;
559 char buffer[SIZE];
560 String8 result;
561 snprintf(buffer, SIZE, " %s track\n", getTrackType());
562 result.append(buffer);
563 snprintf(buffer, SIZE, " reached EOS: %s\n",
564 mReachedEOS? "true": "false");
565 result.append(buffer);
566 snprintf(buffer, SIZE, " frames encoded : %d\n", mStszTableEntries->count());
567 result.append(buffer);
568 snprintf(buffer, SIZE, " duration encoded : %" PRId64 " us\n", mTrackDurationUs);
569 result.append(buffer);
570 ::write(fd, result.string(), result.size());
571 return OK;
572 }
573
574 // static
getFourCCForMime(const char * mime)575 const char *MPEG4Writer::Track::getFourCCForMime(const char *mime) {
576 if (mime == NULL) {
577 return NULL;
578 }
579 if (!strncasecmp(mime, "audio/", 6)) {
580 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
581 return "samr";
582 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
583 return "sawb";
584 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
585 return "mp4a";
586 }
587 } else if (!strncasecmp(mime, "video/", 6)) {
588 if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
589 return "mp4v";
590 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
591 return "s263";
592 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
593 return "avc1";
594 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) {
595 return "hvc1";
596 }
597 } else if (!strncasecmp(mime, "application/", 12)) {
598 return "mett";
599 } else if (!strcasecmp(MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, mime)) {
600 return "heic";
601 } else {
602 ALOGE("Track (%s) other than video/audio/metadata is not supported", mime);
603 }
604 return NULL;
605 }
606
addSource(const sp<MediaSource> & source)607 status_t MPEG4Writer::addSource(const sp<MediaSource> &source) {
608 Mutex::Autolock l(mLock);
609 if (mStarted) {
610 ALOGE("Attempt to add source AFTER recording is started");
611 return UNKNOWN_ERROR;
612 }
613
614 CHECK(source.get() != NULL);
615
616 const char *mime;
617 source->getFormat()->findCString(kKeyMIMEType, &mime);
618
619 if (Track::getFourCCForMime(mime) == NULL) {
620 ALOGE("Unsupported mime '%s'", mime);
621 return ERROR_UNSUPPORTED;
622 }
623
624 // This is a metadata track or the first track of either audio or video
625 // Go ahead to add the track.
626 Track *track = new Track(this, source, 1 + mTracks.size());
627 mTracks.push_back(track);
628
629 mHasMoovBox |= !track->isHeic();
630 mHasFileLevelMeta |= track->isHeic();
631
632 return OK;
633 }
634
startTracks(MetaData * params)635 status_t MPEG4Writer::startTracks(MetaData *params) {
636 if (mTracks.empty()) {
637 ALOGE("No source added");
638 return INVALID_OPERATION;
639 }
640
641 for (List<Track *>::iterator it = mTracks.begin();
642 it != mTracks.end(); ++it) {
643 status_t err = (*it)->start(params);
644
645 if (err != OK) {
646 for (List<Track *>::iterator it2 = mTracks.begin();
647 it2 != it; ++it2) {
648 (*it2)->stop();
649 }
650
651 return err;
652 }
653 }
654 return OK;
655 }
656
addDeviceMeta()657 void MPEG4Writer::addDeviceMeta() {
658 // add device info and estimate space in 'moov'
659 char val[PROPERTY_VALUE_MAX];
660 size_t n;
661 // meta size is estimated by adding up the following:
662 // - meta header structures, which occur only once (total 66 bytes)
663 // - size for each key, which consists of a fixed header (32 bytes),
664 // plus key length and data length.
665 mMoovExtraSize += 66;
666 if (property_get("ro.build.version.release", val, NULL)
667 && (n = strlen(val)) > 0) {
668 mMetaKeys->setString(kMetaKey_Version, val, n + 1);
669 mMoovExtraSize += sizeof(kMetaKey_Version) + n + 32;
670 }
671
672 if (property_get_bool("media.recorder.show_manufacturer_and_model", false)) {
673 if (property_get("ro.product.manufacturer", val, NULL)
674 && (n = strlen(val)) > 0) {
675 mMetaKeys->setString(kMetaKey_Manufacturer, val, n + 1);
676 mMoovExtraSize += sizeof(kMetaKey_Manufacturer) + n + 32;
677 }
678 if (property_get("ro.product.model", val, NULL)
679 && (n = strlen(val)) > 0) {
680 mMetaKeys->setString(kMetaKey_Model, val, n + 1);
681 mMoovExtraSize += sizeof(kMetaKey_Model) + n + 32;
682 }
683 }
684 #ifdef SHOW_MODEL_BUILD
685 if (property_get("ro.build.display.id", val, NULL)
686 && (n = strlen(val)) > 0) {
687 mMetaKeys->setString(kMetaKey_Build, val, n + 1);
688 mMoovExtraSize += sizeof(kMetaKey_Build) + n + 32;
689 }
690 #endif
691 }
692
estimateFileLevelMetaSize(MetaData * params)693 int64_t MPEG4Writer::estimateFileLevelMetaSize(MetaData *params) {
694 int32_t rotation;
695 if (!params || !params->findInt32(kKeyRotation, &rotation)) {
696 rotation = 0;
697 }
698
699 // base meta size
700 int64_t metaSize = 12 // meta fullbox header
701 + 33 // hdlr box
702 + 14 // pitm box
703 + 16 // iloc box (fixed size portion)
704 + 14 // iinf box (fixed size portion)
705 + 32 // iprp box (fixed size protion)
706 + 8 // idat box (when empty)
707 + 12 // iref box (when empty)
708 ;
709
710 for (List<Track *>::iterator it = mTracks.begin();
711 it != mTracks.end(); ++it) {
712 if ((*it)->isHeic()) {
713 metaSize += (*it)->getMetaSizeIncrease(rotation, mTracks.size());
714 }
715 }
716
717 ALOGV("estimated meta size: %lld", (long long) metaSize);
718
719 // Need at least 8-byte padding at the end, otherwise the left-over
720 // freebox may become malformed
721 return metaSize + 8;
722 }
723
estimateMoovBoxSize(int32_t bitRate)724 int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) {
725 // This implementation is highly experimental/heurisitic.
726 //
727 // Statistical analysis shows that metadata usually accounts
728 // for a small portion of the total file size, usually < 0.6%.
729
730 // The default MIN_MOOV_BOX_SIZE is set to 0.6% x 1MB / 2,
731 // where 1MB is the common file size limit for MMS application.
732 // The default MAX _MOOV_BOX_SIZE value is based on about 3
733 // minute video recording with a bit rate about 3 Mbps, because
734 // statistics also show that most of the video captured are going
735 // to be less than 3 minutes.
736
737 // If the estimation is wrong, we will pay the price of wasting
738 // some reserved space. This should not happen so often statistically.
739 static const int32_t factor = mUse32BitOffset? 1: 2;
740 static const int64_t MIN_MOOV_BOX_SIZE = 3 * 1024; // 3 KB
741 static const int64_t MAX_MOOV_BOX_SIZE = (180 * 3000000 * 6LL / 8000);
742 int64_t size = MIN_MOOV_BOX_SIZE;
743
744 // Max file size limit is set
745 if (mMaxFileSizeLimitBytes != 0 && mIsFileSizeLimitExplicitlyRequested) {
746 size = mMaxFileSizeLimitBytes * 6 / 1000;
747 }
748
749 // Max file duration limit is set
750 if (mMaxFileDurationLimitUs != 0) {
751 if (bitRate > 0) {
752 int64_t size2 =
753 ((mMaxFileDurationLimitUs / 1000) * bitRate * 6) / 8000000;
754 if (mMaxFileSizeLimitBytes != 0 && mIsFileSizeLimitExplicitlyRequested) {
755 // When both file size and duration limits are set,
756 // we use the smaller limit of the two.
757 if (size > size2) {
758 size = size2;
759 }
760 } else {
761 // Only max file duration limit is set
762 size = size2;
763 }
764 }
765 }
766
767 if (size < MIN_MOOV_BOX_SIZE) {
768 size = MIN_MOOV_BOX_SIZE;
769 }
770
771 // Any long duration recording will be probably end up with
772 // non-streamable mp4 file.
773 if (size > MAX_MOOV_BOX_SIZE) {
774 size = MAX_MOOV_BOX_SIZE;
775 }
776
777 // Account for the extra stuff (Geo, meta keys, etc.)
778 size += mMoovExtraSize;
779
780 ALOGI("limits: %" PRId64 "/%" PRId64 " bytes/us, bit rate: %d bps and the"
781 " estimated moov size %" PRId64 " bytes",
782 mMaxFileSizeLimitBytes, mMaxFileDurationLimitUs, bitRate, size);
783
784 int64_t estimatedSize = factor * size;
785 CHECK_GE(estimatedSize, 8);
786
787 return estimatedSize;
788 }
789
start(MetaData * param)790 status_t MPEG4Writer::start(MetaData *param) {
791 if (mInitCheck != OK) {
792 return UNKNOWN_ERROR;
793 }
794 mStartMeta = param;
795
796 /*
797 * Check mMaxFileSizeLimitBytes at the beginning
798 * since mMaxFileSizeLimitBytes may be implicitly
799 * changed later for 32-bit file offset even if
800 * user does not ask to set it explicitly.
801 */
802 if (mMaxFileSizeLimitBytes != 0) {
803 mIsFileSizeLimitExplicitlyRequested = true;
804 }
805
806 int32_t use64BitOffset;
807 if (param &&
808 param->findInt32(kKey64BitFileOffset, &use64BitOffset) &&
809 use64BitOffset) {
810 mUse32BitOffset = false;
811 }
812
813 if (mUse32BitOffset) {
814 // Implicit 32 bit file size limit
815 if (mMaxFileSizeLimitBytes == 0) {
816 mMaxFileSizeLimitBytes = kMax32BitFileSize;
817 }
818
819 // If file size is set to be larger than the 32 bit file
820 // size limit, treat it as an error.
821 if (mMaxFileSizeLimitBytes > kMax32BitFileSize) {
822 ALOGW("32-bit file size limit (%" PRId64 " bytes) too big. "
823 "It is changed to %" PRId64 " bytes",
824 mMaxFileSizeLimitBytes, kMax32BitFileSize);
825 mMaxFileSizeLimitBytes = kMax32BitFileSize;
826 }
827 }
828
829 int32_t use2ByteNalLength;
830 if (param &&
831 param->findInt32(kKey2ByteNalLength, &use2ByteNalLength) &&
832 use2ByteNalLength) {
833 mUse4ByteNalLength = false;
834 }
835
836 int32_t isRealTimeRecording;
837 if (param && param->findInt32(kKeyRealTimeRecording, &isRealTimeRecording)) {
838 mIsRealTimeRecording = isRealTimeRecording;
839 }
840
841 mStartTimestampUs = -1;
842
843 if (mStarted) {
844 if (mPaused) {
845 mPaused = false;
846 return startTracks(param);
847 }
848 return OK;
849 }
850
851 if (!param ||
852 !param->findInt32(kKeyTimeScale, &mTimeScale)) {
853 mTimeScale = 1000;
854 }
855 CHECK_GT(mTimeScale, 0);
856 ALOGV("movie time scale: %d", mTimeScale);
857
858 /*
859 * When the requested file size limit is small, the priority
860 * is to meet the file size limit requirement, rather than
861 * to make the file streamable. mStreamableFile does not tell
862 * whether the actual recorded file is streamable or not.
863 */
864 mStreamableFile =
865 (mMaxFileSizeLimitBytes != 0 &&
866 mMaxFileSizeLimitBytes >= kMinStreamableFileSizeInBytes);
867
868 /*
869 * mWriteBoxToMemory is true if the amount of data in a file-level meta or
870 * moov box is smaller than the reserved free space at the beginning of a
871 * file, AND when the content of the box is constructed. Note that video/
872 * audio frame data is always written to the file but not in the memory.
873 *
874 * Before stop()/reset() is called, mWriteBoxToMemory is always
875 * false. When reset() is called at the end of a recording session,
876 * file-level meta and/or moov box needs to be constructed.
877 *
878 * 1) Right before the box is constructed, mWriteBoxToMemory to set to
879 * mStreamableFile so that if the file is intended to be streamable, it
880 * is set to true; otherwise, it is set to false. When the value is set
881 * to false, all the content of that box is written immediately to
882 * the end of the file. When the value is set to true, all the
883 * content of that box is written to an in-memory cache,
884 * mInMemoryCache, util the following condition happens. Note
885 * that the size of the in-memory cache is the same as the
886 * reserved free space at the beginning of the file.
887 *
888 * 2) While the data of the box is written to an in-memory
889 * cache, the data size is checked against the reserved space.
890 * If the data size surpasses the reserved space, subsequent box data
891 * could no longer be hold in the in-memory cache. This also
892 * indicates that the reserved space was too small. At this point,
893 * _all_ subsequent box data must be written to the end of the file.
894 * mWriteBoxToMemory must be set to false to direct the write
895 * to the file.
896 *
897 * 3) If the data size in the box is smaller than the reserved
898 * space after the box is completely constructed, the in-memory
899 * cache copy of the box is written to the reserved free space.
900 * mWriteBoxToMemory is always set to false after all boxes that
901 * using the in-memory cache have been constructed.
902 */
903 mWriteBoxToMemory = false;
904 mInMemoryCache = NULL;
905 mInMemoryCacheOffset = 0;
906
907
908 ALOGV("muxer starting: mHasMoovBox %d, mHasFileLevelMeta %d",
909 mHasMoovBox, mHasFileLevelMeta);
910
911 writeFtypBox(param);
912
913 mFreeBoxOffset = mOffset;
914
915 if (mInMemoryCacheSize == 0) {
916 int32_t bitRate = -1;
917 if (mHasFileLevelMeta) {
918 mInMemoryCacheSize += estimateFileLevelMetaSize(param);
919 }
920 if (mHasMoovBox) {
921 if (param) {
922 param->findInt32(kKeyBitRate, &bitRate);
923 }
924 mInMemoryCacheSize += estimateMoovBoxSize(bitRate);
925 }
926 }
927 if (mStreamableFile) {
928 // Reserve a 'free' box only for streamable file
929 lseek64(mFd, mFreeBoxOffset, SEEK_SET);
930 writeInt32(mInMemoryCacheSize);
931 write("free", 4);
932 mMdatOffset = mFreeBoxOffset + mInMemoryCacheSize;
933 } else {
934 mMdatOffset = mOffset;
935 }
936
937 mOffset = mMdatOffset;
938 lseek64(mFd, mMdatOffset, SEEK_SET);
939 if (mUse32BitOffset) {
940 write("????mdat", 8);
941 } else {
942 write("\x00\x00\x00\x01mdat????????", 16);
943 }
944
945 status_t err = startWriterThread();
946 if (err != OK) {
947 return err;
948 }
949
950 err = startTracks(param);
951 if (err != OK) {
952 return err;
953 }
954
955 mStarted = true;
956 return OK;
957 }
958
use32BitFileOffset() const959 bool MPEG4Writer::use32BitFileOffset() const {
960 return mUse32BitOffset;
961 }
962
pause()963 status_t MPEG4Writer::pause() {
964 ALOGW("MPEG4Writer: pause is not supported");
965 return ERROR_UNSUPPORTED;
966 }
967
stopWriterThread()968 void MPEG4Writer::stopWriterThread() {
969 ALOGD("Stopping writer thread");
970 if (!mWriterThreadStarted) {
971 return;
972 }
973
974 {
975 Mutex::Autolock autolock(mLock);
976
977 mDone = true;
978 mChunkReadyCondition.signal();
979 }
980
981 void *dummy;
982 pthread_join(mThread, &dummy);
983 mWriterThreadStarted = false;
984 ALOGD("Writer thread stopped");
985 }
986
987 /*
988 * MP4 file standard defines a composition matrix:
989 * | a b u |
990 * | c d v |
991 * | x y w |
992 *
993 * the element in the matrix is stored in the following
994 * order: {a, b, u, c, d, v, x, y, w},
995 * where a, b, c, d, x, and y is in 16.16 format, while
996 * u, v and w is in 2.30 format.
997 */
writeCompositionMatrix(int degrees)998 void MPEG4Writer::writeCompositionMatrix(int degrees) {
999 ALOGV("writeCompositionMatrix");
1000 uint32_t a = 0x00010000;
1001 uint32_t b = 0;
1002 uint32_t c = 0;
1003 uint32_t d = 0x00010000;
1004 switch (degrees) {
1005 case 0:
1006 break;
1007 case 90:
1008 a = 0;
1009 b = 0x00010000;
1010 c = 0xFFFF0000;
1011 d = 0;
1012 break;
1013 case 180:
1014 a = 0xFFFF0000;
1015 d = 0xFFFF0000;
1016 break;
1017 case 270:
1018 a = 0;
1019 b = 0xFFFF0000;
1020 c = 0x00010000;
1021 d = 0;
1022 break;
1023 default:
1024 CHECK(!"Should never reach this unknown rotation");
1025 break;
1026 }
1027
1028 writeInt32(a); // a
1029 writeInt32(b); // b
1030 writeInt32(0); // u
1031 writeInt32(c); // c
1032 writeInt32(d); // d
1033 writeInt32(0); // v
1034 writeInt32(0); // x
1035 writeInt32(0); // y
1036 writeInt32(0x40000000); // w
1037 }
1038
release()1039 void MPEG4Writer::release() {
1040 close(mFd);
1041 mFd = -1;
1042 if (mNextFd != -1) {
1043 close(mNextFd);
1044 mNextFd = -1;
1045 }
1046 mInitCheck = NO_INIT;
1047 mStarted = false;
1048 free(mInMemoryCache);
1049 mInMemoryCache = NULL;
1050 }
1051
finishCurrentSession()1052 void MPEG4Writer::finishCurrentSession() {
1053 reset(false /* stopSource */);
1054 }
1055
switchFd()1056 status_t MPEG4Writer::switchFd() {
1057 ALOGV("switchFd");
1058 Mutex::Autolock l(mLock);
1059 if (mSwitchPending) {
1060 return OK;
1061 }
1062
1063 if (mNextFd == -1) {
1064 ALOGW("No FileDescripter for next recording");
1065 return INVALID_OPERATION;
1066 }
1067
1068 mSwitchPending = true;
1069 sp<AMessage> msg = new AMessage(kWhatSwitch, mReflector);
1070 status_t err = msg->post();
1071
1072 return err;
1073 }
1074
reset(bool stopSource)1075 status_t MPEG4Writer::reset(bool stopSource) {
1076 if (mInitCheck != OK) {
1077 return OK;
1078 } else {
1079 if (!mWriterThreadStarted ||
1080 !mStarted) {
1081 if (mWriterThreadStarted) {
1082 stopWriterThread();
1083 }
1084 release();
1085 return OK;
1086 }
1087 }
1088
1089 status_t err = OK;
1090 int64_t maxDurationUs = 0;
1091 int64_t minDurationUs = 0x7fffffffffffffffLL;
1092 int32_t nonImageTrackCount = 0;
1093 for (List<Track *>::iterator it = mTracks.begin();
1094 it != mTracks.end(); ++it) {
1095 status_t status = (*it)->stop(stopSource);
1096 if (err == OK && status != OK) {
1097 err = status;
1098 }
1099
1100 // skip image tracks
1101 if ((*it)->isHeic()) continue;
1102 nonImageTrackCount++;
1103
1104 int64_t durationUs = (*it)->getDurationUs();
1105 if (durationUs > maxDurationUs) {
1106 maxDurationUs = durationUs;
1107 }
1108 if (durationUs < minDurationUs) {
1109 minDurationUs = durationUs;
1110 }
1111 }
1112
1113 if (nonImageTrackCount > 1) {
1114 ALOGD("Duration from tracks range is [%" PRId64 ", %" PRId64 "] us",
1115 minDurationUs, maxDurationUs);
1116 }
1117
1118 stopWriterThread();
1119
1120 // Do not write out movie header on error.
1121 if (err != OK) {
1122 release();
1123 return err;
1124 }
1125
1126 // Fix up the size of the 'mdat' chunk.
1127 if (mUse32BitOffset) {
1128 lseek64(mFd, mMdatOffset, SEEK_SET);
1129 uint32_t size = htonl(static_cast<uint32_t>(mOffset - mMdatOffset));
1130 ::write(mFd, &size, 4);
1131 } else {
1132 lseek64(mFd, mMdatOffset + 8, SEEK_SET);
1133 uint64_t size = mOffset - mMdatOffset;
1134 size = hton64(size);
1135 ::write(mFd, &size, 8);
1136 }
1137 lseek64(mFd, mOffset, SEEK_SET);
1138
1139 // Construct file-level meta and moov box now
1140 mInMemoryCacheOffset = 0;
1141 mWriteBoxToMemory = mStreamableFile;
1142 if (mWriteBoxToMemory) {
1143 // There is no need to allocate in-memory cache
1144 // if the file is not streamable.
1145
1146 mInMemoryCache = (uint8_t *) malloc(mInMemoryCacheSize);
1147 CHECK(mInMemoryCache != NULL);
1148 }
1149
1150 if (mHasFileLevelMeta) {
1151 writeFileLevelMetaBox();
1152 if (mWriteBoxToMemory) {
1153 writeCachedBoxToFile("meta");
1154 } else {
1155 ALOGI("The file meta box is written at the end.");
1156 }
1157 }
1158
1159 if (mHasMoovBox) {
1160 writeMoovBox(maxDurationUs);
1161 // mWriteBoxToMemory could be set to false in
1162 // MPEG4Writer::write() method
1163 if (mWriteBoxToMemory) {
1164 writeCachedBoxToFile("moov");
1165 } else {
1166 ALOGI("The mp4 file will not be streamable.");
1167 }
1168 }
1169 mWriteBoxToMemory = false;
1170
1171 // Free in-memory cache for box writing
1172 if (mInMemoryCache != NULL) {
1173 free(mInMemoryCache);
1174 mInMemoryCache = NULL;
1175 mInMemoryCacheOffset = 0;
1176 }
1177
1178 CHECK(mBoxes.empty());
1179
1180 release();
1181 return err;
1182 }
1183
1184 /*
1185 * Writes currently cached box into file.
1186 *
1187 * Must be called while mWriteBoxToMemory is true, and will not modify
1188 * mWriteBoxToMemory. After the call, remaining cache size will be
1189 * reduced and buffer offset will be set to the beginning of the cache.
1190 */
writeCachedBoxToFile(const char * type)1191 void MPEG4Writer::writeCachedBoxToFile(const char *type) {
1192 CHECK(mWriteBoxToMemory);
1193
1194 mWriteBoxToMemory = false;
1195 // Content of the box is saved in the cache, and the in-memory
1196 // box needs to be written to the file in a single shot.
1197
1198 CHECK_LE(mInMemoryCacheOffset + 8, mInMemoryCacheSize);
1199
1200 // Cached box
1201 lseek64(mFd, mFreeBoxOffset, SEEK_SET);
1202 mOffset = mFreeBoxOffset;
1203 write(mInMemoryCache, 1, mInMemoryCacheOffset);
1204
1205 // Free box
1206 lseek64(mFd, mOffset, SEEK_SET);
1207 mFreeBoxOffset = mOffset;
1208 writeInt32(mInMemoryCacheSize - mInMemoryCacheOffset);
1209 write("free", 4);
1210
1211 // Rewind buffering to the beginning, and restore mWriteBoxToMemory flag
1212 mInMemoryCacheSize -= mInMemoryCacheOffset;
1213 mInMemoryCacheOffset = 0;
1214 mWriteBoxToMemory = true;
1215
1216 ALOGV("dumped out %s box, estimated size remaining %lld",
1217 type, (long long)mInMemoryCacheSize);
1218 }
1219
getMpeg4Time()1220 uint32_t MPEG4Writer::getMpeg4Time() {
1221 time_t now = time(NULL);
1222 // MP4 file uses time counting seconds since midnight, Jan. 1, 1904
1223 // while time function returns Unix epoch values which starts
1224 // at 1970-01-01. Lets add the number of seconds between them
1225 static const uint32_t delta = (66 * 365 + 17) * (24 * 60 * 60);
1226 if (now < 0 || uint32_t(now) > UINT32_MAX - delta) {
1227 return 0;
1228 }
1229 uint32_t mpeg4Time = uint32_t(now) + delta;
1230 return mpeg4Time;
1231 }
1232
writeMvhdBox(int64_t durationUs)1233 void MPEG4Writer::writeMvhdBox(int64_t durationUs) {
1234 uint32_t now = getMpeg4Time();
1235 beginBox("mvhd");
1236 writeInt32(0); // version=0, flags=0
1237 writeInt32(now); // creation time
1238 writeInt32(now); // modification time
1239 writeInt32(mTimeScale); // mvhd timescale
1240 int32_t duration = (durationUs * mTimeScale + 5E5) / 1E6;
1241 writeInt32(duration);
1242 writeInt32(0x10000); // rate: 1.0
1243 writeInt16(0x100); // volume
1244 writeInt16(0); // reserved
1245 writeInt32(0); // reserved
1246 writeInt32(0); // reserved
1247 writeCompositionMatrix(0); // matrix
1248 writeInt32(0); // predefined
1249 writeInt32(0); // predefined
1250 writeInt32(0); // predefined
1251 writeInt32(0); // predefined
1252 writeInt32(0); // predefined
1253 writeInt32(0); // predefined
1254 writeInt32(mTracks.size() + 1); // nextTrackID
1255 endBox(); // mvhd
1256 }
1257
writeMoovBox(int64_t durationUs)1258 void MPEG4Writer::writeMoovBox(int64_t durationUs) {
1259 beginBox("moov");
1260 writeMvhdBox(durationUs);
1261 if (mAreGeoTagsAvailable) {
1262 writeUdtaBox();
1263 }
1264 writeMoovLevelMetaBox();
1265 // Loop through all the tracks to get the global time offset if there is
1266 // any ctts table appears in a video track.
1267 int64_t minCttsOffsetTimeUs = kMaxCttsOffsetTimeUs;
1268 for (List<Track *>::iterator it = mTracks.begin();
1269 it != mTracks.end(); ++it) {
1270 if (!(*it)->isHeic()) {
1271 minCttsOffsetTimeUs =
1272 std::min(minCttsOffsetTimeUs, (*it)->getMinCttsOffsetTimeUs());
1273 }
1274 }
1275 ALOGI("Ajust the moov start time from %lld us -> %lld us",
1276 (long long)mStartTimestampUs,
1277 (long long)(mStartTimestampUs + minCttsOffsetTimeUs - kMaxCttsOffsetTimeUs));
1278 // Adjust the global start time.
1279 mStartTimestampUs += minCttsOffsetTimeUs - kMaxCttsOffsetTimeUs;
1280
1281 // Add mStartTimeOffsetBFramesUs(-ve or zero) to the duration of first entry in STTS.
1282 mStartTimeOffsetBFramesUs = minCttsOffsetTimeUs - kMaxCttsOffsetTimeUs;
1283 ALOGV("mStartTimeOffsetBFramesUs :%" PRId32, mStartTimeOffsetBFramesUs);
1284
1285 for (List<Track *>::iterator it = mTracks.begin();
1286 it != mTracks.end(); ++it) {
1287 if (!(*it)->isHeic()) {
1288 (*it)->writeTrackHeader(mUse32BitOffset);
1289 }
1290 }
1291 endBox(); // moov
1292 }
1293
writeFtypBox(MetaData * param)1294 void MPEG4Writer::writeFtypBox(MetaData *param) {
1295 beginBox("ftyp");
1296
1297 int32_t fileType;
1298 if (!param || !param->findInt32(kKeyFileType, &fileType)) {
1299 fileType = OUTPUT_FORMAT_MPEG_4;
1300 }
1301 if (fileType != OUTPUT_FORMAT_MPEG_4 && fileType != OUTPUT_FORMAT_HEIF) {
1302 writeFourcc("3gp4");
1303 writeInt32(0);
1304 writeFourcc("isom");
1305 writeFourcc("3gp4");
1306 } else {
1307 // Only write "heic" as major brand if the client specified HEIF
1308 // AND we indeed receive some image heic tracks.
1309 if (fileType == OUTPUT_FORMAT_HEIF && mHasFileLevelMeta) {
1310 writeFourcc("heic");
1311 } else {
1312 writeFourcc("mp42");
1313 }
1314 writeInt32(0);
1315 if (mHasFileLevelMeta) {
1316 writeFourcc("mif1");
1317 writeFourcc("heic");
1318 }
1319 if (mHasMoovBox) {
1320 writeFourcc("isom");
1321 writeFourcc("mp42");
1322 }
1323 }
1324
1325 endBox();
1326 }
1327
isTestModeEnabled()1328 static bool isTestModeEnabled() {
1329 #if (PROPERTY_VALUE_MAX < 5)
1330 #error "PROPERTY_VALUE_MAX must be at least 5"
1331 #endif
1332
1333 // Test mode is enabled only if rw.media.record.test system
1334 // property is enabled.
1335 if (property_get_bool("rw.media.record.test", false)) {
1336 return true;
1337 }
1338 return false;
1339 }
1340
sendSessionSummary()1341 void MPEG4Writer::sendSessionSummary() {
1342 // Send session summary only if test mode is enabled
1343 if (!isTestModeEnabled()) {
1344 return;
1345 }
1346
1347 for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
1348 it != mChunkInfos.end(); ++it) {
1349 int trackNum = it->mTrack->getTrackId() << 28;
1350 notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
1351 trackNum | MEDIA_RECORDER_TRACK_INTER_CHUNK_TIME_MS,
1352 it->mMaxInterChunkDurUs);
1353 }
1354 }
1355
setInterleaveDuration(uint32_t durationUs)1356 status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) {
1357 mInterleaveDurationUs = durationUs;
1358 return OK;
1359 }
1360
lock()1361 void MPEG4Writer::lock() {
1362 mLock.lock();
1363 }
1364
unlock()1365 void MPEG4Writer::unlock() {
1366 mLock.unlock();
1367 }
1368
addSample_l(MediaBuffer * buffer,bool usePrefix,uint32_t tiffHdrOffset,size_t * bytesWritten)1369 off64_t MPEG4Writer::addSample_l(
1370 MediaBuffer *buffer, bool usePrefix,
1371 uint32_t tiffHdrOffset, size_t *bytesWritten) {
1372 off64_t old_offset = mOffset;
1373
1374 if (usePrefix) {
1375 addMultipleLengthPrefixedSamples_l(buffer);
1376 } else {
1377 if (tiffHdrOffset > 0) {
1378 tiffHdrOffset = htonl(tiffHdrOffset);
1379 ::write(mFd, &tiffHdrOffset, 4); // exif_tiff_header_offset field
1380 mOffset += 4;
1381 }
1382
1383 ::write(mFd,
1384 (const uint8_t *)buffer->data() + buffer->range_offset(),
1385 buffer->range_length());
1386
1387 mOffset += buffer->range_length();
1388 }
1389
1390 *bytesWritten = mOffset - old_offset;
1391 return old_offset;
1392 }
1393
StripStartcode(MediaBuffer * buffer)1394 static void StripStartcode(MediaBuffer *buffer) {
1395 if (buffer->range_length() < 4) {
1396 return;
1397 }
1398
1399 const uint8_t *ptr =
1400 (const uint8_t *)buffer->data() + buffer->range_offset();
1401
1402 if (!memcmp(ptr, "\x00\x00\x00\x01", 4)) {
1403 buffer->set_range(
1404 buffer->range_offset() + 4, buffer->range_length() - 4);
1405 }
1406 }
1407
addMultipleLengthPrefixedSamples_l(MediaBuffer * buffer)1408 void MPEG4Writer::addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer) {
1409 const uint8_t *dataStart = (const uint8_t *)buffer->data() + buffer->range_offset();
1410 const uint8_t *currentNalStart = dataStart;
1411 const uint8_t *nextNalStart;
1412 const uint8_t *data = dataStart;
1413 size_t nextNalSize;
1414 size_t searchSize = buffer->range_length();
1415
1416 while (getNextNALUnit(&data, &searchSize, &nextNalStart,
1417 &nextNalSize, true) == OK) {
1418 size_t currentNalSize = nextNalStart - currentNalStart - 4 /* strip start-code */;
1419 MediaBuffer *nalBuf = new MediaBuffer((void *)currentNalStart, currentNalSize);
1420 addLengthPrefixedSample_l(nalBuf);
1421 nalBuf->release();
1422
1423 currentNalStart = nextNalStart;
1424 }
1425
1426 size_t currentNalOffset = currentNalStart - dataStart;
1427 buffer->set_range(buffer->range_offset() + currentNalOffset,
1428 buffer->range_length() - currentNalOffset);
1429 addLengthPrefixedSample_l(buffer);
1430 }
1431
addLengthPrefixedSample_l(MediaBuffer * buffer)1432 void MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
1433 size_t length = buffer->range_length();
1434
1435 if (mUse4ByteNalLength) {
1436 uint8_t x = length >> 24;
1437 ::write(mFd, &x, 1);
1438 x = (length >> 16) & 0xff;
1439 ::write(mFd, &x, 1);
1440 x = (length >> 8) & 0xff;
1441 ::write(mFd, &x, 1);
1442 x = length & 0xff;
1443 ::write(mFd, &x, 1);
1444
1445 ::write(mFd,
1446 (const uint8_t *)buffer->data() + buffer->range_offset(),
1447 length);
1448
1449 mOffset += length + 4;
1450 } else {
1451 CHECK_LT(length, 65536u);
1452
1453 uint8_t x = length >> 8;
1454 ::write(mFd, &x, 1);
1455 x = length & 0xff;
1456 ::write(mFd, &x, 1);
1457 ::write(mFd, (const uint8_t *)buffer->data() + buffer->range_offset(), length);
1458 mOffset += length + 2;
1459 }
1460 }
1461
write(const void * ptr,size_t size,size_t nmemb)1462 size_t MPEG4Writer::write(
1463 const void *ptr, size_t size, size_t nmemb) {
1464
1465 const size_t bytes = size * nmemb;
1466 if (mWriteBoxToMemory) {
1467
1468 off64_t boxSize = 8 + mInMemoryCacheOffset + bytes;
1469 if (boxSize > mInMemoryCacheSize) {
1470 // The reserved free space at the beginning of the file is not big
1471 // enough. Boxes should be written to the end of the file from now
1472 // on, but not to the in-memory cache.
1473
1474 // We write partial box that is in the memory to the file first.
1475 for (List<off64_t>::iterator it = mBoxes.begin();
1476 it != mBoxes.end(); ++it) {
1477 (*it) += mOffset;
1478 }
1479 lseek64(mFd, mOffset, SEEK_SET);
1480 ::write(mFd, mInMemoryCache, mInMemoryCacheOffset);
1481 ::write(mFd, ptr, bytes);
1482 mOffset += (bytes + mInMemoryCacheOffset);
1483
1484 // All subsequent boxes will be written to the end of the file.
1485 mWriteBoxToMemory = false;
1486 } else {
1487 memcpy(mInMemoryCache + mInMemoryCacheOffset, ptr, bytes);
1488 mInMemoryCacheOffset += bytes;
1489 }
1490 } else {
1491 ::write(mFd, ptr, size * nmemb);
1492 mOffset += bytes;
1493 }
1494 return bytes;
1495 }
1496
beginBox(uint32_t id)1497 void MPEG4Writer::beginBox(uint32_t id) {
1498 mBoxes.push_back(mWriteBoxToMemory?
1499 mInMemoryCacheOffset: mOffset);
1500
1501 writeInt32(0);
1502 writeInt32(id);
1503 }
1504
beginBox(const char * fourcc)1505 void MPEG4Writer::beginBox(const char *fourcc) {
1506 CHECK_EQ(strlen(fourcc), 4u);
1507
1508 mBoxes.push_back(mWriteBoxToMemory?
1509 mInMemoryCacheOffset: mOffset);
1510
1511 writeInt32(0);
1512 writeFourcc(fourcc);
1513 }
1514
endBox()1515 void MPEG4Writer::endBox() {
1516 CHECK(!mBoxes.empty());
1517
1518 off64_t offset = *--mBoxes.end();
1519 mBoxes.erase(--mBoxes.end());
1520
1521 if (mWriteBoxToMemory) {
1522 int32_t x = htonl(mInMemoryCacheOffset - offset);
1523 memcpy(mInMemoryCache + offset, &x, 4);
1524 } else {
1525 lseek64(mFd, offset, SEEK_SET);
1526 writeInt32(mOffset - offset);
1527 mOffset -= 4;
1528 lseek64(mFd, mOffset, SEEK_SET);
1529 }
1530 }
1531
writeInt8(int8_t x)1532 void MPEG4Writer::writeInt8(int8_t x) {
1533 write(&x, 1, 1);
1534 }
1535
writeInt16(int16_t x)1536 void MPEG4Writer::writeInt16(int16_t x) {
1537 x = htons(x);
1538 write(&x, 1, 2);
1539 }
1540
writeInt32(int32_t x)1541 void MPEG4Writer::writeInt32(int32_t x) {
1542 x = htonl(x);
1543 write(&x, 1, 4);
1544 }
1545
writeInt64(int64_t x)1546 void MPEG4Writer::writeInt64(int64_t x) {
1547 x = hton64(x);
1548 write(&x, 1, 8);
1549 }
1550
writeCString(const char * s)1551 void MPEG4Writer::writeCString(const char *s) {
1552 size_t n = strlen(s);
1553 write(s, 1, n + 1);
1554 }
1555
writeFourcc(const char * s)1556 void MPEG4Writer::writeFourcc(const char *s) {
1557 CHECK_EQ(strlen(s), 4u);
1558 write(s, 1, 4);
1559 }
1560
1561
1562 // Written in +/-DD.DDDD format
writeLatitude(int degreex10000)1563 void MPEG4Writer::writeLatitude(int degreex10000) {
1564 bool isNegative = (degreex10000 < 0);
1565 char sign = isNegative? '-': '+';
1566
1567 // Handle the whole part
1568 char str[9];
1569 int wholePart = degreex10000 / 10000;
1570 if (wholePart == 0) {
1571 snprintf(str, 5, "%c%.2d.", sign, wholePart);
1572 } else {
1573 snprintf(str, 5, "%+.2d.", wholePart);
1574 }
1575
1576 // Handle the fractional part
1577 int fractionalPart = degreex10000 - (wholePart * 10000);
1578 if (fractionalPart < 0) {
1579 fractionalPart = -fractionalPart;
1580 }
1581 snprintf(&str[4], 5, "%.4d", fractionalPart);
1582
1583 // Do not write the null terminator
1584 write(str, 1, 8);
1585 }
1586
1587 // Written in +/- DDD.DDDD format
writeLongitude(int degreex10000)1588 void MPEG4Writer::writeLongitude(int degreex10000) {
1589 bool isNegative = (degreex10000 < 0);
1590 char sign = isNegative? '-': '+';
1591
1592 // Handle the whole part
1593 char str[10];
1594 int wholePart = degreex10000 / 10000;
1595 if (wholePart == 0) {
1596 snprintf(str, 6, "%c%.3d.", sign, wholePart);
1597 } else {
1598 snprintf(str, 6, "%+.3d.", wholePart);
1599 }
1600
1601 // Handle the fractional part
1602 int fractionalPart = degreex10000 - (wholePart * 10000);
1603 if (fractionalPart < 0) {
1604 fractionalPart = -fractionalPart;
1605 }
1606 snprintf(&str[5], 5, "%.4d", fractionalPart);
1607
1608 // Do not write the null terminator
1609 write(str, 1, 9);
1610 }
1611
1612 /*
1613 * Geodata is stored according to ISO-6709 standard.
1614 * latitudex10000 is latitude in degrees times 10000, and
1615 * longitudex10000 is longitude in degrees times 10000.
1616 * The range for the latitude is in [-90, +90], and
1617 * The range for the longitude is in [-180, +180]
1618 */
setGeoData(int latitudex10000,int longitudex10000)1619 status_t MPEG4Writer::setGeoData(int latitudex10000, int longitudex10000) {
1620 // Is latitude or longitude out of range?
1621 if (latitudex10000 < -900000 || latitudex10000 > 900000 ||
1622 longitudex10000 < -1800000 || longitudex10000 > 1800000) {
1623 return BAD_VALUE;
1624 }
1625
1626 mLatitudex10000 = latitudex10000;
1627 mLongitudex10000 = longitudex10000;
1628 mAreGeoTagsAvailable = true;
1629 mMoovExtraSize += 30;
1630 return OK;
1631 }
1632
setCaptureRate(float captureFps)1633 status_t MPEG4Writer::setCaptureRate(float captureFps) {
1634 if (captureFps <= 0.0f) {
1635 return BAD_VALUE;
1636 }
1637
1638 mMetaKeys->setFloat(kMetaKey_CaptureFps, captureFps);
1639 mMoovExtraSize += sizeof(kMetaKey_CaptureFps) + 4 + 32;
1640
1641 return OK;
1642 }
1643
setTemporalLayerCount(uint32_t layerCount)1644 status_t MPEG4Writer::setTemporalLayerCount(uint32_t layerCount) {
1645 if (layerCount > 9) {
1646 return BAD_VALUE;
1647 }
1648
1649 if (layerCount > 0) {
1650 mMetaKeys->setInt32(kMetaKey_TemporalLayerCount, layerCount);
1651 mMoovExtraSize += sizeof(kMetaKey_TemporalLayerCount) + 4 + 32;
1652 }
1653
1654 return OK;
1655 }
1656
notifyApproachingLimit()1657 void MPEG4Writer::notifyApproachingLimit() {
1658 Mutex::Autolock autolock(mLock);
1659 // Only notify once.
1660 if (mSendNotify) {
1661 return;
1662 }
1663 ALOGW("Recorded file size is approaching limit %" PRId64 "bytes",
1664 mMaxFileSizeLimitBytes);
1665 notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING, 0);
1666 mSendNotify = true;
1667 }
1668
write(const void * data,size_t size)1669 void MPEG4Writer::write(const void *data, size_t size) {
1670 write(data, 1, size);
1671 }
1672
isFileStreamable() const1673 bool MPEG4Writer::isFileStreamable() const {
1674 return mStreamableFile;
1675 }
1676
exceedsFileSizeLimit()1677 bool MPEG4Writer::exceedsFileSizeLimit() {
1678 // No limit
1679 if (mMaxFileSizeLimitBytes == 0) {
1680 return false;
1681 }
1682 int64_t nTotalBytesEstimate = static_cast<int64_t>(mInMemoryCacheSize);
1683 for (List<Track *>::iterator it = mTracks.begin();
1684 it != mTracks.end(); ++it) {
1685 nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
1686 }
1687
1688 if (!mStreamableFile) {
1689 // Add 1024 bytes as error tolerance
1690 return nTotalBytesEstimate + 1024 >= mMaxFileSizeLimitBytes;
1691 }
1692
1693 // Be conservative in the estimate: do not exceed 95% of
1694 // the target file limit. For small target file size limit, though,
1695 // this will not help.
1696 return (nTotalBytesEstimate >= (95 * mMaxFileSizeLimitBytes) / 100);
1697 }
1698
approachingFileSizeLimit()1699 bool MPEG4Writer::approachingFileSizeLimit() {
1700 // No limit
1701 if (mMaxFileSizeLimitBytes == 0) {
1702 return false;
1703 }
1704
1705 int64_t nTotalBytesEstimate = static_cast<int64_t>(mInMemoryCacheSize);
1706 for (List<Track *>::iterator it = mTracks.begin();
1707 it != mTracks.end(); ++it) {
1708 nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
1709 }
1710
1711 if (!mStreamableFile) {
1712 // Add 1024 bytes as error tolerance
1713 return nTotalBytesEstimate + 1024 >= (90 * mMaxFileSizeLimitBytes) / 100;
1714 }
1715
1716 return (nTotalBytesEstimate >= (90 * mMaxFileSizeLimitBytes) / 100);
1717 }
1718
exceedsFileDurationLimit()1719 bool MPEG4Writer::exceedsFileDurationLimit() {
1720 // No limit
1721 if (mMaxFileDurationLimitUs == 0) {
1722 return false;
1723 }
1724
1725 for (List<Track *>::iterator it = mTracks.begin();
1726 it != mTracks.end(); ++it) {
1727 if (!(*it)->isHeic() && (*it)->getDurationUs() >= mMaxFileDurationLimitUs) {
1728 return true;
1729 }
1730 }
1731 return false;
1732 }
1733
reachedEOS()1734 bool MPEG4Writer::reachedEOS() {
1735 bool allDone = true;
1736 for (List<Track *>::iterator it = mTracks.begin();
1737 it != mTracks.end(); ++it) {
1738 if (!(*it)->reachedEOS()) {
1739 allDone = false;
1740 break;
1741 }
1742 }
1743
1744 return allDone;
1745 }
1746
setStartTimestampUs(int64_t timeUs)1747 void MPEG4Writer::setStartTimestampUs(int64_t timeUs) {
1748 ALOGI("setStartTimestampUs: %" PRId64, timeUs);
1749 CHECK_GE(timeUs, 0LL);
1750 Mutex::Autolock autoLock(mLock);
1751 if (mStartTimestampUs < 0 || mStartTimestampUs > timeUs) {
1752 mStartTimestampUs = timeUs;
1753 ALOGI("Earliest track starting time: %" PRId64, mStartTimestampUs);
1754 }
1755 }
1756
getStartTimestampUs()1757 int64_t MPEG4Writer::getStartTimestampUs() {
1758 Mutex::Autolock autoLock(mLock);
1759 return mStartTimestampUs;
1760 }
1761
getStartTimeOffsetBFramesUs()1762 int32_t MPEG4Writer::getStartTimeOffsetBFramesUs() {
1763 Mutex::Autolock autoLock(mLock);
1764 return mStartTimeOffsetBFramesUs;
1765 }
1766
numTracks()1767 size_t MPEG4Writer::numTracks() {
1768 Mutex::Autolock autolock(mLock);
1769 return mTracks.size();
1770 }
1771
1772 ////////////////////////////////////////////////////////////////////////////////
1773
Track(MPEG4Writer * owner,const sp<MediaSource> & source,size_t trackId)1774 MPEG4Writer::Track::Track(
1775 MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId)
1776 : mOwner(owner),
1777 mMeta(source->getFormat()),
1778 mSource(source),
1779 mDone(false),
1780 mPaused(false),
1781 mResumed(false),
1782 mStarted(false),
1783 mGotStartKeyFrame(false),
1784 mIsMalformed(false),
1785 mTrackId(trackId),
1786 mTrackDurationUs(0),
1787 mEstimatedTrackSizeBytes(0),
1788 mSamplesHaveSameSize(true),
1789 mStszTableEntries(new ListTableEntries<uint32_t, 1>(1000)),
1790 mStcoTableEntries(new ListTableEntries<uint32_t, 1>(1000)),
1791 mCo64TableEntries(new ListTableEntries<off64_t, 1>(1000)),
1792 mStscTableEntries(new ListTableEntries<uint32_t, 3>(1000)),
1793 mStssTableEntries(new ListTableEntries<uint32_t, 1>(1000)),
1794 mSttsTableEntries(new ListTableEntries<uint32_t, 2>(1000)),
1795 mCttsTableEntries(new ListTableEntries<uint32_t, 2>(1000)),
1796 mElstTableEntries(new ListTableEntries<uint32_t, 3>(3)), // Reserve 3 rows, a row has 3 items
1797 mMinCttsOffsetTimeUs(0),
1798 mMinCttsOffsetTicks(0),
1799 mMaxCttsOffsetTicks(0),
1800 mCodecSpecificData(NULL),
1801 mCodecSpecificDataSize(0),
1802 mGotAllCodecSpecificData(false),
1803 mReachedEOS(false),
1804 mStartTimestampUs(-1),
1805 mRotation(0),
1806 mDimgRefs("dimg"),
1807 mImageItemId(0),
1808 mIsPrimary(0),
1809 mWidth(0),
1810 mHeight(0),
1811 mTileWidth(0),
1812 mTileHeight(0),
1813 mGridRows(0),
1814 mGridCols(0),
1815 mNumTiles(1),
1816 mTileIndex(0) {
1817 getCodecSpecificDataFromInputFormatIfPossible();
1818
1819 const char *mime;
1820 mMeta->findCString(kKeyMIMEType, &mime);
1821 mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
1822 mIsHevc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC);
1823 mIsAudio = !strncasecmp(mime, "audio/", 6);
1824 mIsVideo = !strncasecmp(mime, "video/", 6);
1825 mIsHeic = !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
1826 mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
1827 !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
1828
1829 // store temporal layer count
1830 if (mIsVideo) {
1831 int32_t count;
1832 if (mMeta->findInt32(kKeyTemporalLayerCount, &count) && count > 1) {
1833 mOwner->setTemporalLayerCount(count);
1834 }
1835 }
1836
1837 if (!mIsHeic) {
1838 setTimeScale();
1839 } else {
1840 CHECK(mMeta->findInt32(kKeyWidth, &mWidth) && (mWidth > 0));
1841 CHECK(mMeta->findInt32(kKeyHeight, &mHeight) && (mHeight > 0));
1842
1843 int32_t tileWidth, tileHeight, gridRows, gridCols;
1844 if (mMeta->findInt32(kKeyTileWidth, &tileWidth) && (tileWidth > 0) &&
1845 mMeta->findInt32(kKeyTileHeight, &tileHeight) && (tileHeight > 0) &&
1846 mMeta->findInt32(kKeyGridRows, &gridRows) && (gridRows > 0) &&
1847 mMeta->findInt32(kKeyGridCols, &gridCols) && (gridCols > 0)) {
1848 mTileWidth = tileWidth;
1849 mTileHeight = tileHeight;
1850 mGridRows = gridRows;
1851 mGridCols = gridCols;
1852 mNumTiles = gridRows * gridCols;
1853 }
1854 if (!mMeta->findInt32(kKeyTrackIsDefault, &mIsPrimary)) {
1855 mIsPrimary = false;
1856 }
1857 }
1858 }
1859
1860 // Clear all the internal states except the CSD data.
resetInternal()1861 void MPEG4Writer::Track::resetInternal() {
1862 mDone = false;
1863 mPaused = false;
1864 mResumed = false;
1865 mStarted = false;
1866 mGotStartKeyFrame = false;
1867 mIsMalformed = false;
1868 mTrackDurationUs = 0;
1869 mEstimatedTrackSizeBytes = 0;
1870 mSamplesHaveSameSize = 0;
1871 if (mStszTableEntries != NULL) {
1872 delete mStszTableEntries;
1873 mStszTableEntries = new ListTableEntries<uint32_t, 1>(1000);
1874 }
1875 if (mStcoTableEntries != NULL) {
1876 delete mStcoTableEntries;
1877 mStcoTableEntries = new ListTableEntries<uint32_t, 1>(1000);
1878 }
1879 if (mCo64TableEntries != NULL) {
1880 delete mCo64TableEntries;
1881 mCo64TableEntries = new ListTableEntries<off64_t, 1>(1000);
1882 }
1883 if (mStscTableEntries != NULL) {
1884 delete mStscTableEntries;
1885 mStscTableEntries = new ListTableEntries<uint32_t, 3>(1000);
1886 }
1887 if (mStssTableEntries != NULL) {
1888 delete mStssTableEntries;
1889 mStssTableEntries = new ListTableEntries<uint32_t, 1>(1000);
1890 }
1891 if (mSttsTableEntries != NULL) {
1892 delete mSttsTableEntries;
1893 mSttsTableEntries = new ListTableEntries<uint32_t, 2>(1000);
1894 }
1895 if (mCttsTableEntries != NULL) {
1896 delete mCttsTableEntries;
1897 mCttsTableEntries = new ListTableEntries<uint32_t, 2>(1000);
1898 }
1899 if (mElstTableEntries != NULL) {
1900 delete mElstTableEntries;
1901 mElstTableEntries = new ListTableEntries<uint32_t, 3>(3);
1902 }
1903 mReachedEOS = false;
1904 }
1905
updateTrackSizeEstimate()1906 void MPEG4Writer::Track::updateTrackSizeEstimate() {
1907 mEstimatedTrackSizeBytes = mMdatSizeBytes; // media data size
1908
1909 if (!isHeic() && !mOwner->isFileStreamable()) {
1910 uint32_t stcoBoxCount = (mOwner->use32BitFileOffset()
1911 ? mStcoTableEntries->count()
1912 : mCo64TableEntries->count());
1913 int64_t stcoBoxSizeBytes = stcoBoxCount * 4;
1914 int64_t stszBoxSizeBytes = mSamplesHaveSameSize? 4: (mStszTableEntries->count() * 4);
1915
1916 // Reserved free space is not large enough to hold
1917 // all meta data and thus wasted.
1918 mEstimatedTrackSizeBytes += mStscTableEntries->count() * 12 + // stsc box size
1919 mStssTableEntries->count() * 4 + // stss box size
1920 mSttsTableEntries->count() * 8 + // stts box size
1921 mCttsTableEntries->count() * 8 + // ctts box size
1922 mElstTableEntries->count() * 12 + // elst box size
1923 stcoBoxSizeBytes + // stco box size
1924 stszBoxSizeBytes; // stsz box size
1925 }
1926 }
1927
addOneStscTableEntry(size_t chunkId,size_t sampleId)1928 void MPEG4Writer::Track::addOneStscTableEntry(
1929 size_t chunkId, size_t sampleId) {
1930 mStscTableEntries->add(htonl(chunkId));
1931 mStscTableEntries->add(htonl(sampleId));
1932 mStscTableEntries->add(htonl(1));
1933 }
1934
addOneStssTableEntry(size_t sampleId)1935 void MPEG4Writer::Track::addOneStssTableEntry(size_t sampleId) {
1936 mStssTableEntries->add(htonl(sampleId));
1937 }
1938
addOneSttsTableEntry(size_t sampleCount,int32_t duration)1939 void MPEG4Writer::Track::addOneSttsTableEntry(
1940 size_t sampleCount, int32_t duration) {
1941
1942 if (duration == 0) {
1943 ALOGW("0-duration samples found: %zu", sampleCount);
1944 }
1945 mSttsTableEntries->add(htonl(sampleCount));
1946 mSttsTableEntries->add(htonl(duration));
1947 }
1948
addOneCttsTableEntry(size_t sampleCount,int32_t duration)1949 void MPEG4Writer::Track::addOneCttsTableEntry(
1950 size_t sampleCount, int32_t duration) {
1951
1952 if (!mIsVideo) {
1953 return;
1954 }
1955 mCttsTableEntries->add(htonl(sampleCount));
1956 mCttsTableEntries->add(htonl(duration));
1957 }
1958
addOneElstTableEntry(uint32_t segmentDuration,int32_t mediaTime,int16_t mediaRate,int16_t mediaRateFraction)1959 void MPEG4Writer::Track::addOneElstTableEntry(
1960 uint32_t segmentDuration, int32_t mediaTime, int16_t mediaRate, int16_t mediaRateFraction) {
1961 ALOGV("segmentDuration:%u, mediaTime:%d", segmentDuration, mediaTime);
1962 ALOGV("mediaRate :%" PRId16 ", mediaRateFraction :%" PRId16 ", Ored %u", mediaRate,
1963 mediaRateFraction, ((((uint32_t)mediaRate) << 16) | ((uint32_t)mediaRateFraction)));
1964 mElstTableEntries->add(htonl(segmentDuration));
1965 mElstTableEntries->add(htonl(mediaTime));
1966 mElstTableEntries->add(htonl((((uint32_t)mediaRate) << 16) | (uint32_t)mediaRateFraction));
1967 }
1968
setNextFd(int fd)1969 status_t MPEG4Writer::setNextFd(int fd) {
1970 ALOGV("addNextFd");
1971 Mutex::Autolock l(mLock);
1972 if (mLooper == NULL) {
1973 mReflector = new AHandlerReflector<MPEG4Writer>(this);
1974 mLooper = new ALooper;
1975 mLooper->registerHandler(mReflector);
1976 mLooper->start();
1977 }
1978
1979 if (mNextFd != -1) {
1980 // No need to set a new FD yet.
1981 return INVALID_OPERATION;
1982 }
1983 mNextFd = dup(fd);
1984 return OK;
1985 }
1986
isExifData(MediaBufferBase * buffer,uint32_t * tiffHdrOffset) const1987 bool MPEG4Writer::Track::isExifData(
1988 MediaBufferBase *buffer, uint32_t *tiffHdrOffset) const {
1989 if (!mIsHeic) {
1990 return false;
1991 }
1992
1993 // Exif block starting with 'Exif\0\0'
1994 size_t length = buffer->range_length();
1995 uint8_t *data = (uint8_t *)buffer->data() + buffer->range_offset();
1996 if ((length > sizeof(kExifHeader))
1997 && !memcmp(data, kExifHeader, sizeof(kExifHeader))) {
1998 *tiffHdrOffset = sizeof(kExifHeader);
1999 return true;
2000 }
2001
2002 // Exif block starting with fourcc 'Exif' followed by APP1 marker
2003 if ((length > sizeof(kExifApp1Marker) + 2 + sizeof(kExifHeader))
2004 && !memcmp(data, kExifApp1Marker, sizeof(kExifApp1Marker))
2005 && !memcmp(data + sizeof(kExifApp1Marker) + 2, kExifHeader, sizeof(kExifHeader))) {
2006 // skip 'Exif' fourcc
2007 buffer->set_range(4, buffer->range_length() - 4);
2008
2009 // 2-byte APP1 + 2-byte size followed by kExifHeader
2010 *tiffHdrOffset = 2 + 2 + sizeof(kExifHeader);
2011 return true;
2012 }
2013
2014 return false;
2015 }
2016
addChunkOffset(off64_t offset)2017 void MPEG4Writer::Track::addChunkOffset(off64_t offset) {
2018 CHECK(!mIsHeic);
2019 if (mOwner->use32BitFileOffset()) {
2020 uint32_t value = offset;
2021 mStcoTableEntries->add(htonl(value));
2022 } else {
2023 mCo64TableEntries->add(hton64(offset));
2024 }
2025 }
2026
addItemOffsetAndSize(off64_t offset,size_t size,bool isExif)2027 void MPEG4Writer::Track::addItemOffsetAndSize(off64_t offset, size_t size, bool isExif) {
2028 CHECK(mIsHeic);
2029
2030 if (offset > UINT32_MAX || size > UINT32_MAX) {
2031 ALOGE("offset or size is out of range: %lld, %lld",
2032 (long long) offset, (long long) size);
2033 mIsMalformed = true;
2034 }
2035 if (mIsMalformed) {
2036 return;
2037 }
2038
2039 if (isExif) {
2040 mExifList.push_back(mOwner->addItem_l({
2041 .itemType = "Exif",
2042 .isPrimary = false,
2043 .isHidden = false,
2044 .offset = (uint32_t)offset,
2045 .size = (uint32_t)size,
2046 }));
2047 return;
2048 }
2049
2050 if (mTileIndex >= mNumTiles) {
2051 ALOGW("Ignoring excess tiles!");
2052 return;
2053 }
2054
2055 // Rotation angle in HEIF is CCW, framework angle is CW.
2056 int32_t heifRotation = 0;
2057 switch(mRotation) {
2058 case 90: heifRotation = 3; break;
2059 case 180: heifRotation = 2; break;
2060 case 270: heifRotation = 1; break;
2061 default: break; // don't set if invalid
2062 }
2063
2064 bool hasGrid = (mTileWidth > 0);
2065
2066 if (mProperties.empty()) {
2067 mProperties.push_back(mOwner->addProperty_l({
2068 .type = FOURCC('h', 'v', 'c', 'C'),
2069 .hvcc = ABuffer::CreateAsCopy(mCodecSpecificData, mCodecSpecificDataSize)
2070 }));
2071
2072 mProperties.push_back(mOwner->addProperty_l({
2073 .type = FOURCC('i', 's', 'p', 'e'),
2074 .width = hasGrid ? mTileWidth : mWidth,
2075 .height = hasGrid ? mTileHeight : mHeight,
2076 }));
2077
2078 if (!hasGrid && heifRotation > 0) {
2079 mProperties.push_back(mOwner->addProperty_l({
2080 .type = FOURCC('i', 'r', 'o', 't'),
2081 .rotation = heifRotation,
2082 }));
2083 }
2084 }
2085
2086 mTileIndex++;
2087 if (hasGrid) {
2088 mDimgRefs.value.push_back(mOwner->addItem_l({
2089 .itemType = "hvc1",
2090 .isPrimary = false,
2091 .isHidden = true,
2092 .offset = (uint32_t)offset,
2093 .size = (uint32_t)size,
2094 .properties = mProperties,
2095 }));
2096
2097 if (mTileIndex == mNumTiles) {
2098 mProperties.clear();
2099 mProperties.push_back(mOwner->addProperty_l({
2100 .type = FOURCC('i', 's', 'p', 'e'),
2101 .width = mWidth,
2102 .height = mHeight,
2103 }));
2104 if (heifRotation > 0) {
2105 mProperties.push_back(mOwner->addProperty_l({
2106 .type = FOURCC('i', 'r', 'o', 't'),
2107 .rotation = heifRotation,
2108 }));
2109 }
2110 mImageItemId = mOwner->addItem_l({
2111 .itemType = "grid",
2112 .isPrimary = (mIsPrimary != 0),
2113 .isHidden = false,
2114 .rows = (uint32_t)mGridRows,
2115 .cols = (uint32_t)mGridCols,
2116 .width = (uint32_t)mWidth,
2117 .height = (uint32_t)mHeight,
2118 .properties = mProperties,
2119 });
2120 }
2121 } else {
2122 mImageItemId = mOwner->addItem_l({
2123 .itemType = "hvc1",
2124 .isPrimary = (mIsPrimary != 0),
2125 .isHidden = false,
2126 .offset = (uint32_t)offset,
2127 .size = (uint32_t)size,
2128 .properties = mProperties,
2129 });
2130 }
2131 }
2132
2133 // Flush out the item refs for this track. Note that it must be called after the
2134 // writer thread has stopped, because there might be pending items in the last
2135 // few chunks written by the writer thread (as opposed to the track). In particular,
2136 // it affects the 'dimg' refs for tiled image, as we only have the refs after the
2137 // last tile sample is written.
flushItemRefs()2138 void MPEG4Writer::Track::flushItemRefs() {
2139 CHECK(mIsHeic);
2140
2141 if (mImageItemId > 0) {
2142 mOwner->addRefs_l(mImageItemId, mDimgRefs);
2143
2144 if (!mExifList.empty()) {
2145 // The "cdsc" ref is from the metadata/exif item to the image item.
2146 // So the refs all contain the image item.
2147 ItemRefs cdscRefs("cdsc");
2148 cdscRefs.value.push_back(mImageItemId);
2149 for (uint16_t exifItem : mExifList) {
2150 mOwner->addRefs_l(exifItem, cdscRefs);
2151 }
2152 }
2153 }
2154 }
2155
setTimeScale()2156 void MPEG4Writer::Track::setTimeScale() {
2157 ALOGV("setTimeScale");
2158 // Default time scale
2159 mTimeScale = 90000;
2160
2161 if (mIsAudio) {
2162 // Use the sampling rate as the default time scale for audio track.
2163 int32_t sampleRate;
2164 bool success = mMeta->findInt32(kKeySampleRate, &sampleRate);
2165 CHECK(success);
2166 mTimeScale = sampleRate;
2167 }
2168
2169 // If someone would like to overwrite the timescale, use user-supplied value.
2170 int32_t timeScale;
2171 if (mMeta->findInt32(kKeyTimeScale, &timeScale)) {
2172 mTimeScale = timeScale;
2173 }
2174
2175 CHECK_GT(mTimeScale, 0);
2176 }
2177
onMessageReceived(const sp<AMessage> & msg)2178 void MPEG4Writer::onMessageReceived(const sp<AMessage> &msg) {
2179 switch (msg->what()) {
2180 case kWhatSwitch:
2181 {
2182 mLock.lock();
2183 int fd = mNextFd;
2184 mNextFd = -1;
2185 mLock.unlock();
2186 finishCurrentSession();
2187 initInternal(fd, false /*isFirstSession*/);
2188 start(mStartMeta.get());
2189 mSwitchPending = false;
2190 notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED, 0);
2191 break;
2192 }
2193 default:
2194 TRESPASS();
2195 }
2196 }
2197
getCodecSpecificDataFromInputFormatIfPossible()2198 void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() {
2199 const char *mime;
2200
2201 CHECK(mMeta->findCString(kKeyMIMEType, &mime));
2202
2203 uint32_t type;
2204 const void *data = NULL;
2205 size_t size = 0;
2206 if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
2207 mMeta->findData(kKeyAVCC, &type, &data, &size);
2208 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) ||
2209 !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) {
2210 mMeta->findData(kKeyHVCC, &type, &data, &size);
2211 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
2212 || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
2213 if (mMeta->findData(kKeyESDS, &type, &data, &size)) {
2214 ESDS esds(data, size);
2215 if (esds.getCodecSpecificInfo(&data, &size) == OK &&
2216 data != NULL &&
2217 copyCodecSpecificData((uint8_t*)data, size) == OK) {
2218 mGotAllCodecSpecificData = true;
2219 }
2220 return;
2221 }
2222 }
2223 if (data != NULL && copyCodecSpecificData((uint8_t *)data, size) == OK) {
2224 mGotAllCodecSpecificData = true;
2225 }
2226 }
2227
~Track()2228 MPEG4Writer::Track::~Track() {
2229 stop();
2230
2231 delete mStszTableEntries;
2232 delete mStcoTableEntries;
2233 delete mCo64TableEntries;
2234 delete mStscTableEntries;
2235 delete mSttsTableEntries;
2236 delete mStssTableEntries;
2237 delete mCttsTableEntries;
2238 delete mElstTableEntries;
2239
2240 mStszTableEntries = NULL;
2241 mStcoTableEntries = NULL;
2242 mCo64TableEntries = NULL;
2243 mStscTableEntries = NULL;
2244 mSttsTableEntries = NULL;
2245 mStssTableEntries = NULL;
2246 mCttsTableEntries = NULL;
2247 mElstTableEntries = NULL;
2248
2249 if (mCodecSpecificData != NULL) {
2250 free(mCodecSpecificData);
2251 mCodecSpecificData = NULL;
2252 }
2253 }
2254
initTrackingProgressStatus(MetaData * params)2255 void MPEG4Writer::Track::initTrackingProgressStatus(MetaData *params) {
2256 ALOGV("initTrackingProgressStatus");
2257 mPreviousTrackTimeUs = -1;
2258 mTrackingProgressStatus = false;
2259 mTrackEveryTimeDurationUs = 0;
2260 {
2261 int64_t timeUs;
2262 if (params && params->findInt64(kKeyTrackTimeStatus, &timeUs)) {
2263 ALOGV("Receive request to track progress status for every %" PRId64 " us", timeUs);
2264 mTrackEveryTimeDurationUs = timeUs;
2265 mTrackingProgressStatus = true;
2266 }
2267 }
2268 }
2269
2270 // static
ThreadWrapper(void * me)2271 void *MPEG4Writer::ThreadWrapper(void *me) {
2272 ALOGV("ThreadWrapper: %p", me);
2273 MPEG4Writer *writer = static_cast<MPEG4Writer *>(me);
2274 writer->threadFunc();
2275 return NULL;
2276 }
2277
bufferChunk(const Chunk & chunk)2278 void MPEG4Writer::bufferChunk(const Chunk& chunk) {
2279 ALOGV("bufferChunk: %p", chunk.mTrack);
2280 Mutex::Autolock autolock(mLock);
2281 CHECK_EQ(mDone, false);
2282
2283 for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
2284 it != mChunkInfos.end(); ++it) {
2285
2286 if (chunk.mTrack == it->mTrack) { // Found owner
2287 it->mChunks.push_back(chunk);
2288 mChunkReadyCondition.signal();
2289 return;
2290 }
2291 }
2292
2293 CHECK(!"Received a chunk for a unknown track");
2294 }
2295
writeChunkToFile(Chunk * chunk)2296 void MPEG4Writer::writeChunkToFile(Chunk* chunk) {
2297 ALOGV("writeChunkToFile: %" PRId64 " from %s track",
2298 chunk->mTimeStampUs, chunk->mTrack->getTrackType());
2299
2300 int32_t isFirstSample = true;
2301 while (!chunk->mSamples.empty()) {
2302 List<MediaBuffer *>::iterator it = chunk->mSamples.begin();
2303
2304 uint32_t tiffHdrOffset;
2305 if (!(*it)->meta_data().findInt32(
2306 kKeyExifTiffOffset, (int32_t*)&tiffHdrOffset)) {
2307 tiffHdrOffset = 0;
2308 }
2309 bool isExif = (tiffHdrOffset > 0);
2310 bool usePrefix = chunk->mTrack->usePrefix() && !isExif;
2311
2312 size_t bytesWritten;
2313 off64_t offset = addSample_l(*it, usePrefix, tiffHdrOffset, &bytesWritten);
2314
2315 if (chunk->mTrack->isHeic()) {
2316 chunk->mTrack->addItemOffsetAndSize(offset, bytesWritten, isExif);
2317 } else if (isFirstSample) {
2318 chunk->mTrack->addChunkOffset(offset);
2319 isFirstSample = false;
2320 }
2321
2322 (*it)->release();
2323 (*it) = NULL;
2324 chunk->mSamples.erase(it);
2325 }
2326 chunk->mSamples.clear();
2327 }
2328
writeAllChunks()2329 void MPEG4Writer::writeAllChunks() {
2330 ALOGV("writeAllChunks");
2331 size_t outstandingChunks = 0;
2332 Chunk chunk;
2333 while (findChunkToWrite(&chunk)) {
2334 writeChunkToFile(&chunk);
2335 ++outstandingChunks;
2336 }
2337
2338 sendSessionSummary();
2339
2340 mChunkInfos.clear();
2341 ALOGD("%zu chunks are written in the last batch", outstandingChunks);
2342 }
2343
findChunkToWrite(Chunk * chunk)2344 bool MPEG4Writer::findChunkToWrite(Chunk *chunk) {
2345 ALOGV("findChunkToWrite");
2346
2347 int64_t minTimestampUs = 0x7FFFFFFFFFFFFFFFLL;
2348 Track *track = NULL;
2349 for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
2350 it != mChunkInfos.end(); ++it) {
2351 if (!it->mChunks.empty()) {
2352 List<Chunk>::iterator chunkIt = it->mChunks.begin();
2353 if (chunkIt->mTimeStampUs < minTimestampUs) {
2354 minTimestampUs = chunkIt->mTimeStampUs;
2355 track = it->mTrack;
2356 }
2357 }
2358 }
2359
2360 if (track == NULL) {
2361 ALOGV("Nothing to be written after all");
2362 return false;
2363 }
2364
2365 if (mIsFirstChunk) {
2366 mIsFirstChunk = false;
2367 }
2368
2369 for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
2370 it != mChunkInfos.end(); ++it) {
2371 if (it->mTrack == track) {
2372 *chunk = *(it->mChunks.begin());
2373 it->mChunks.erase(it->mChunks.begin());
2374 CHECK_EQ(chunk->mTrack, track);
2375
2376 int64_t interChunkTimeUs =
2377 chunk->mTimeStampUs - it->mPrevChunkTimestampUs;
2378 if (interChunkTimeUs > it->mPrevChunkTimestampUs) {
2379 it->mMaxInterChunkDurUs = interChunkTimeUs;
2380 }
2381
2382 return true;
2383 }
2384 }
2385
2386 return false;
2387 }
2388
threadFunc()2389 void MPEG4Writer::threadFunc() {
2390 ALOGV("threadFunc");
2391
2392 prctl(PR_SET_NAME, (unsigned long)"MPEG4Writer", 0, 0, 0);
2393
2394 Mutex::Autolock autoLock(mLock);
2395 while (!mDone) {
2396 Chunk chunk;
2397 bool chunkFound = false;
2398
2399 while (!mDone && !(chunkFound = findChunkToWrite(&chunk))) {
2400 mChunkReadyCondition.wait(mLock);
2401 }
2402
2403 // In real time recording mode, write without holding the lock in order
2404 // to reduce the blocking time for media track threads.
2405 // Otherwise, hold the lock until the existing chunks get written to the
2406 // file.
2407 if (chunkFound) {
2408 if (mIsRealTimeRecording) {
2409 mLock.unlock();
2410 }
2411 writeChunkToFile(&chunk);
2412 if (mIsRealTimeRecording) {
2413 mLock.lock();
2414 }
2415 }
2416 }
2417
2418 writeAllChunks();
2419 }
2420
startWriterThread()2421 status_t MPEG4Writer::startWriterThread() {
2422 ALOGV("startWriterThread");
2423
2424 mDone = false;
2425 mIsFirstChunk = true;
2426 mDriftTimeUs = 0;
2427 for (List<Track *>::iterator it = mTracks.begin();
2428 it != mTracks.end(); ++it) {
2429 ChunkInfo info;
2430 info.mTrack = *it;
2431 info.mPrevChunkTimestampUs = 0;
2432 info.mMaxInterChunkDurUs = 0;
2433 mChunkInfos.push_back(info);
2434 }
2435
2436 pthread_attr_t attr;
2437 pthread_attr_init(&attr);
2438 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
2439 pthread_create(&mThread, &attr, ThreadWrapper, this);
2440 pthread_attr_destroy(&attr);
2441 mWriterThreadStarted = true;
2442 return OK;
2443 }
2444
2445
start(MetaData * params)2446 status_t MPEG4Writer::Track::start(MetaData *params) {
2447 if (!mDone && mPaused) {
2448 mPaused = false;
2449 mResumed = true;
2450 return OK;
2451 }
2452
2453 int64_t startTimeUs;
2454 if (params == NULL || !params->findInt64(kKeyTime, &startTimeUs)) {
2455 startTimeUs = 0;
2456 }
2457 mStartTimeRealUs = startTimeUs;
2458
2459 int32_t rotationDegrees;
2460 if ((mIsVideo || mIsHeic) && params &&
2461 params->findInt32(kKeyRotation, &rotationDegrees)) {
2462 mRotation = rotationDegrees;
2463 }
2464
2465 initTrackingProgressStatus(params);
2466
2467 sp<MetaData> meta = new MetaData;
2468 if (mOwner->isRealTimeRecording() && mOwner->numTracks() > 1) {
2469 /*
2470 * This extra delay of accepting incoming audio/video signals
2471 * helps to align a/v start time at the beginning of a recording
2472 * session, and it also helps eliminate the "recording" sound for
2473 * camcorder applications.
2474 *
2475 * If client does not set the start time offset, we fall back to
2476 * use the default initial delay value.
2477 */
2478 int64_t startTimeOffsetUs = mOwner->getStartTimeOffsetMs() * 1000LL;
2479 if (startTimeOffsetUs < 0) { // Start time offset was not set
2480 startTimeOffsetUs = kInitialDelayTimeUs;
2481 }
2482 startTimeUs += startTimeOffsetUs;
2483 ALOGI("Start time offset: %" PRId64 " us", startTimeOffsetUs);
2484 }
2485
2486 meta->setInt64(kKeyTime, startTimeUs);
2487
2488 status_t err = mSource->start(meta.get());
2489 if (err != OK) {
2490 mDone = mReachedEOS = true;
2491 return err;
2492 }
2493
2494 pthread_attr_t attr;
2495 pthread_attr_init(&attr);
2496 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
2497
2498 mDone = false;
2499 mStarted = true;
2500 mTrackDurationUs = 0;
2501 mReachedEOS = false;
2502 mEstimatedTrackSizeBytes = 0;
2503 mMdatSizeBytes = 0;
2504 mMaxChunkDurationUs = 0;
2505 mLastDecodingTimeUs = -1;
2506
2507 pthread_create(&mThread, &attr, ThreadWrapper, this);
2508 pthread_attr_destroy(&attr);
2509
2510 return OK;
2511 }
2512
pause()2513 status_t MPEG4Writer::Track::pause() {
2514 mPaused = true;
2515 return OK;
2516 }
2517
stop(bool stopSource)2518 status_t MPEG4Writer::Track::stop(bool stopSource) {
2519 ALOGD("%s track stopping. %s source", getTrackType(), stopSource ? "Stop" : "Not Stop");
2520 if (!mStarted) {
2521 ALOGE("Stop() called but track is not started");
2522 return ERROR_END_OF_STREAM;
2523 }
2524
2525 if (mDone) {
2526 return OK;
2527 }
2528
2529 if (stopSource) {
2530 ALOGD("%s track source stopping", getTrackType());
2531 mSource->stop();
2532 ALOGD("%s track source stopped", getTrackType());
2533 }
2534
2535 // Set mDone to be true after sucessfully stop mSource as mSource may be still outputting
2536 // buffers to the writer.
2537 mDone = true;
2538
2539 void *dummy;
2540 pthread_join(mThread, &dummy);
2541 status_t err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
2542
2543 ALOGD("%s track stopped. %s source", getTrackType(), stopSource ? "Stop" : "Not Stop");
2544 return err;
2545 }
2546
reachedEOS()2547 bool MPEG4Writer::Track::reachedEOS() {
2548 return mReachedEOS;
2549 }
2550
2551 // static
ThreadWrapper(void * me)2552 void *MPEG4Writer::Track::ThreadWrapper(void *me) {
2553 Track *track = static_cast<Track *>(me);
2554
2555 status_t err = track->threadEntry();
2556 return (void *)(uintptr_t)err;
2557 }
2558
getNalUnitType(uint8_t byte,uint8_t * type)2559 static void getNalUnitType(uint8_t byte, uint8_t* type) {
2560 ALOGV("getNalUnitType: %d", byte);
2561
2562 // nal_unit_type: 5-bit unsigned integer
2563 *type = (byte & 0x1F);
2564 }
2565
parseParamSet(const uint8_t * data,size_t length,int type,size_t * paramSetLen)2566 const uint8_t *MPEG4Writer::Track::parseParamSet(
2567 const uint8_t *data, size_t length, int type, size_t *paramSetLen) {
2568
2569 ALOGV("parseParamSet");
2570 CHECK(type == kNalUnitTypeSeqParamSet ||
2571 type == kNalUnitTypePicParamSet);
2572
2573 const uint8_t *nextStartCode = findNextNalStartCode(data, length);
2574 *paramSetLen = nextStartCode - data;
2575 if (*paramSetLen == 0) {
2576 ALOGE("Param set is malformed, since its length is 0");
2577 return NULL;
2578 }
2579
2580 AVCParamSet paramSet(*paramSetLen, data);
2581 if (type == kNalUnitTypeSeqParamSet) {
2582 if (*paramSetLen < 4) {
2583 ALOGE("Seq parameter set malformed");
2584 return NULL;
2585 }
2586 if (mSeqParamSets.empty()) {
2587 mProfileIdc = data[1];
2588 mProfileCompatible = data[2];
2589 mLevelIdc = data[3];
2590 } else {
2591 if (mProfileIdc != data[1] ||
2592 mProfileCompatible != data[2] ||
2593 mLevelIdc != data[3]) {
2594 // COULD DO: set profile/level to the lowest required to support all SPSs
2595 ALOGE("Inconsistent profile/level found in seq parameter sets");
2596 return NULL;
2597 }
2598 }
2599 mSeqParamSets.push_back(paramSet);
2600 } else {
2601 mPicParamSets.push_back(paramSet);
2602 }
2603 return nextStartCode;
2604 }
2605
copyAVCCodecSpecificData(const uint8_t * data,size_t size)2606 status_t MPEG4Writer::Track::copyAVCCodecSpecificData(
2607 const uint8_t *data, size_t size) {
2608 ALOGV("copyAVCCodecSpecificData");
2609
2610 // 2 bytes for each of the parameter set length field
2611 // plus the 7 bytes for the header
2612 return copyCodecSpecificData(data, size, 4 + 7);
2613 }
2614
copyHEVCCodecSpecificData(const uint8_t * data,size_t size)2615 status_t MPEG4Writer::Track::copyHEVCCodecSpecificData(
2616 const uint8_t *data, size_t size) {
2617 ALOGV("copyHEVCCodecSpecificData");
2618
2619 // Min length of HEVC CSD is 23. (ISO/IEC 14496-15:2014 Chapter 8.3.3.1.2)
2620 return copyCodecSpecificData(data, size, 23);
2621 }
2622
copyCodecSpecificData(const uint8_t * data,size_t size,size_t minLength)2623 status_t MPEG4Writer::Track::copyCodecSpecificData(
2624 const uint8_t *data, size_t size, size_t minLength) {
2625 if (size < minLength) {
2626 ALOGE("Codec specific data length too short: %zu", size);
2627 return ERROR_MALFORMED;
2628 }
2629
2630 mCodecSpecificData = malloc(size);
2631 if (mCodecSpecificData == NULL) {
2632 ALOGE("Failed allocating codec specific data");
2633 return NO_MEMORY;
2634 }
2635 mCodecSpecificDataSize = size;
2636 memcpy(mCodecSpecificData, data, size);
2637 return OK;
2638 }
2639
parseAVCCodecSpecificData(const uint8_t * data,size_t size)2640 status_t MPEG4Writer::Track::parseAVCCodecSpecificData(
2641 const uint8_t *data, size_t size) {
2642
2643 ALOGV("parseAVCCodecSpecificData");
2644 // Data starts with a start code.
2645 // SPS and PPS are separated with start codes.
2646 // Also, SPS must come before PPS
2647 uint8_t type = kNalUnitTypeSeqParamSet;
2648 bool gotSps = false;
2649 bool gotPps = false;
2650 const uint8_t *tmp = data;
2651 const uint8_t *nextStartCode = data;
2652 size_t bytesLeft = size;
2653 size_t paramSetLen = 0;
2654 mCodecSpecificDataSize = 0;
2655 while (bytesLeft > 4 && !memcmp("\x00\x00\x00\x01", tmp, 4)) {
2656 getNalUnitType(*(tmp + 4), &type);
2657 if (type == kNalUnitTypeSeqParamSet) {
2658 if (gotPps) {
2659 ALOGE("SPS must come before PPS");
2660 return ERROR_MALFORMED;
2661 }
2662 if (!gotSps) {
2663 gotSps = true;
2664 }
2665 nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, ¶mSetLen);
2666 } else if (type == kNalUnitTypePicParamSet) {
2667 if (!gotSps) {
2668 ALOGE("SPS must come before PPS");
2669 return ERROR_MALFORMED;
2670 }
2671 if (!gotPps) {
2672 gotPps = true;
2673 }
2674 nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, ¶mSetLen);
2675 } else {
2676 ALOGE("Only SPS and PPS Nal units are expected");
2677 return ERROR_MALFORMED;
2678 }
2679
2680 if (nextStartCode == NULL) {
2681 return ERROR_MALFORMED;
2682 }
2683
2684 // Move on to find the next parameter set
2685 bytesLeft -= nextStartCode - tmp;
2686 tmp = nextStartCode;
2687 mCodecSpecificDataSize += (2 + paramSetLen);
2688 }
2689
2690 {
2691 // Check on the number of seq parameter sets
2692 size_t nSeqParamSets = mSeqParamSets.size();
2693 if (nSeqParamSets == 0) {
2694 ALOGE("Cound not find sequence parameter set");
2695 return ERROR_MALFORMED;
2696 }
2697
2698 if (nSeqParamSets > 0x1F) {
2699 ALOGE("Too many seq parameter sets (%zu) found", nSeqParamSets);
2700 return ERROR_MALFORMED;
2701 }
2702 }
2703
2704 {
2705 // Check on the number of pic parameter sets
2706 size_t nPicParamSets = mPicParamSets.size();
2707 if (nPicParamSets == 0) {
2708 ALOGE("Cound not find picture parameter set");
2709 return ERROR_MALFORMED;
2710 }
2711 if (nPicParamSets > 0xFF) {
2712 ALOGE("Too many pic parameter sets (%zd) found", nPicParamSets);
2713 return ERROR_MALFORMED;
2714 }
2715 }
2716 // FIXME:
2717 // Add chromat_format_idc, bit depth values, etc for AVC/h264 high profile and above
2718 // and remove #if 0
2719 #if 0
2720 {
2721 // Check on the profiles
2722 // These profiles requires additional parameter set extensions
2723 if (mProfileIdc == 100 || mProfileIdc == 110 ||
2724 mProfileIdc == 122 || mProfileIdc == 144) {
2725 ALOGE("Sorry, no support for profile_idc: %d!", mProfileIdc);
2726 return BAD_VALUE;
2727 }
2728 }
2729 #endif
2730 return OK;
2731 }
2732
makeAVCCodecSpecificData(const uint8_t * data,size_t size)2733 status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
2734 const uint8_t *data, size_t size) {
2735
2736 if (mCodecSpecificData != NULL) {
2737 ALOGE("Already have codec specific data");
2738 return ERROR_MALFORMED;
2739 }
2740
2741 if (size < 4) {
2742 ALOGE("Codec specific data length too short: %zu", size);
2743 return ERROR_MALFORMED;
2744 }
2745
2746 // Data is in the form of AVCCodecSpecificData
2747 if (memcmp("\x00\x00\x00\x01", data, 4)) {
2748 return copyAVCCodecSpecificData(data, size);
2749 }
2750
2751 if (parseAVCCodecSpecificData(data, size) != OK) {
2752 return ERROR_MALFORMED;
2753 }
2754
2755 // ISO 14496-15: AVC file format
2756 mCodecSpecificDataSize += 7; // 7 more bytes in the header
2757 mCodecSpecificData = malloc(mCodecSpecificDataSize);
2758 if (mCodecSpecificData == NULL) {
2759 mCodecSpecificDataSize = 0;
2760 ALOGE("Failed allocating codec specific data");
2761 return NO_MEMORY;
2762 }
2763 uint8_t *header = (uint8_t *)mCodecSpecificData;
2764 header[0] = 1; // version
2765 header[1] = mProfileIdc; // profile indication
2766 header[2] = mProfileCompatible; // profile compatibility
2767 header[3] = mLevelIdc;
2768
2769 // 6-bit '111111' followed by 2-bit to lengthSizeMinuusOne
2770 if (mOwner->useNalLengthFour()) {
2771 header[4] = 0xfc | 3; // length size == 4 bytes
2772 } else {
2773 header[4] = 0xfc | 1; // length size == 2 bytes
2774 }
2775
2776 // 3-bit '111' followed by 5-bit numSequenceParameterSets
2777 int nSequenceParamSets = mSeqParamSets.size();
2778 header[5] = 0xe0 | nSequenceParamSets;
2779 header += 6;
2780 for (List<AVCParamSet>::iterator it = mSeqParamSets.begin();
2781 it != mSeqParamSets.end(); ++it) {
2782 // 16-bit sequence parameter set length
2783 uint16_t seqParamSetLength = it->mLength;
2784 header[0] = seqParamSetLength >> 8;
2785 header[1] = seqParamSetLength & 0xff;
2786
2787 // SPS NAL unit (sequence parameter length bytes)
2788 memcpy(&header[2], it->mData, seqParamSetLength);
2789 header += (2 + seqParamSetLength);
2790 }
2791
2792 // 8-bit nPictureParameterSets
2793 int nPictureParamSets = mPicParamSets.size();
2794 header[0] = nPictureParamSets;
2795 header += 1;
2796 for (List<AVCParamSet>::iterator it = mPicParamSets.begin();
2797 it != mPicParamSets.end(); ++it) {
2798 // 16-bit picture parameter set length
2799 uint16_t picParamSetLength = it->mLength;
2800 header[0] = picParamSetLength >> 8;
2801 header[1] = picParamSetLength & 0xff;
2802
2803 // PPS Nal unit (picture parameter set length bytes)
2804 memcpy(&header[2], it->mData, picParamSetLength);
2805 header += (2 + picParamSetLength);
2806 }
2807
2808 return OK;
2809 }
2810
2811
parseHEVCCodecSpecificData(const uint8_t * data,size_t size,HevcParameterSets & paramSets)2812 status_t MPEG4Writer::Track::parseHEVCCodecSpecificData(
2813 const uint8_t *data, size_t size, HevcParameterSets ¶mSets) {
2814
2815 ALOGV("parseHEVCCodecSpecificData");
2816 const uint8_t *tmp = data;
2817 const uint8_t *nextStartCode = data;
2818 size_t bytesLeft = size;
2819 while (bytesLeft > 4 && !memcmp("\x00\x00\x00\x01", tmp, 4)) {
2820 nextStartCode = findNextNalStartCode(tmp + 4, bytesLeft - 4);
2821 status_t err = paramSets.addNalUnit(tmp + 4, (nextStartCode - tmp) - 4);
2822 if (err != OK) {
2823 return ERROR_MALFORMED;
2824 }
2825
2826 // Move on to find the next parameter set
2827 bytesLeft -= nextStartCode - tmp;
2828 tmp = nextStartCode;
2829 }
2830
2831 size_t csdSize = 23;
2832 const size_t numNalUnits = paramSets.getNumNalUnits();
2833 for (size_t i = 0; i < ARRAY_SIZE(kMandatoryHevcNalUnitTypes); ++i) {
2834 int type = kMandatoryHevcNalUnitTypes[i];
2835 size_t numParamSets = paramSets.getNumNalUnitsOfType(type);
2836 if (numParamSets == 0) {
2837 ALOGE("Cound not find NAL unit of type %d", type);
2838 return ERROR_MALFORMED;
2839 }
2840 }
2841 for (size_t i = 0; i < ARRAY_SIZE(kHevcNalUnitTypes); ++i) {
2842 int type = kHevcNalUnitTypes[i];
2843 size_t numParamSets = paramSets.getNumNalUnitsOfType(type);
2844 if (numParamSets > 0xffff) {
2845 ALOGE("Too many seq parameter sets (%zu) found", numParamSets);
2846 return ERROR_MALFORMED;
2847 }
2848 csdSize += 3;
2849 for (size_t j = 0; j < numNalUnits; ++j) {
2850 if (paramSets.getType(j) != type) {
2851 continue;
2852 }
2853 csdSize += 2 + paramSets.getSize(j);
2854 }
2855 }
2856 mCodecSpecificDataSize = csdSize;
2857 return OK;
2858 }
2859
makeHEVCCodecSpecificData(const uint8_t * data,size_t size)2860 status_t MPEG4Writer::Track::makeHEVCCodecSpecificData(
2861 const uint8_t *data, size_t size) {
2862
2863 if (mCodecSpecificData != NULL) {
2864 ALOGE("Already have codec specific data");
2865 return ERROR_MALFORMED;
2866 }
2867
2868 if (size < 4) {
2869 ALOGE("Codec specific data length too short: %zu", size);
2870 return ERROR_MALFORMED;
2871 }
2872
2873 // Data is in the form of HEVCCodecSpecificData
2874 if (memcmp("\x00\x00\x00\x01", data, 4)) {
2875 return copyHEVCCodecSpecificData(data, size);
2876 }
2877
2878 HevcParameterSets paramSets;
2879 if (parseHEVCCodecSpecificData(data, size, paramSets) != OK) {
2880 ALOGE("failed parsing codec specific data");
2881 return ERROR_MALFORMED;
2882 }
2883
2884 mCodecSpecificData = malloc(mCodecSpecificDataSize);
2885 if (mCodecSpecificData == NULL) {
2886 mCodecSpecificDataSize = 0;
2887 ALOGE("Failed allocating codec specific data");
2888 return NO_MEMORY;
2889 }
2890 status_t err = paramSets.makeHvcc((uint8_t *)mCodecSpecificData,
2891 &mCodecSpecificDataSize, mOwner->useNalLengthFour() ? 4 : 2);
2892 if (err != OK) {
2893 ALOGE("failed constructing HVCC atom");
2894 return err;
2895 }
2896
2897 return OK;
2898 }
2899
2900 /*
2901 * Updates the drift time from the audio track so that
2902 * the video track can get the updated drift time information
2903 * from the file writer. The fluctuation of the drift time of the audio
2904 * encoding path is smoothed out with a simple filter by giving a larger
2905 * weight to more recently drift time. The filter coefficients, 0.5 and 0.5,
2906 * are heuristically determined.
2907 */
updateDriftTime(const sp<MetaData> & meta)2908 void MPEG4Writer::Track::updateDriftTime(const sp<MetaData>& meta) {
2909 int64_t driftTimeUs = 0;
2910 if (meta->findInt64(kKeyDriftTime, &driftTimeUs)) {
2911 int64_t prevDriftTimeUs = mOwner->getDriftTimeUs();
2912 int64_t timeUs = (driftTimeUs + prevDriftTimeUs) >> 1;
2913 mOwner->setDriftTimeUs(timeUs);
2914 }
2915 }
2916
dumpTimeStamps()2917 void MPEG4Writer::Track::dumpTimeStamps() {
2918 if (!mTimestampDebugHelper.empty()) {
2919 std::string timeStampString = "Dumping " + std::string(getTrackType()) + " track's last " +
2920 std::to_string(mTimestampDebugHelper.size()) +
2921 " frames' timestamps(pts, dts) and frame type : ";
2922 for (const TimestampDebugHelperEntry& entry : mTimestampDebugHelper) {
2923 timeStampString += "\n(" + std::to_string(entry.pts) + "us, " +
2924 std::to_string(entry.dts) + "us " + entry.frameType + ") ";
2925 }
2926 ALOGE("%s", timeStampString.c_str());
2927 } else {
2928 ALOGE("0 frames to dump timeStamps in %s track ", getTrackType());
2929 }
2930 }
2931
threadEntry()2932 status_t MPEG4Writer::Track::threadEntry() {
2933 int32_t count = 0;
2934 const int64_t interleaveDurationUs = mOwner->interleaveDuration();
2935 const bool hasMultipleTracks = (mOwner->numTracks() > 1);
2936 int64_t chunkTimestampUs = 0;
2937 int32_t nChunks = 0;
2938 int32_t nActualFrames = 0; // frames containing non-CSD data (non-0 length)
2939 int32_t nZeroLengthFrames = 0;
2940 int64_t lastTimestampUs = 0; // Previous sample time stamp
2941 int64_t lastDurationUs = 0; // Between the previous two samples
2942 int64_t currDurationTicks = 0; // Timescale based ticks
2943 int64_t lastDurationTicks = 0; // Timescale based ticks
2944 int32_t sampleCount = 1; // Sample count in the current stts table entry
2945 uint32_t previousSampleSize = 0; // Size of the previous sample
2946 int64_t previousPausedDurationUs = 0;
2947 int64_t timestampUs = 0;
2948 int64_t cttsOffsetTimeUs = 0;
2949 int64_t currCttsOffsetTimeTicks = 0; // Timescale based ticks
2950 int64_t lastCttsOffsetTimeTicks = -1; // Timescale based ticks
2951 int32_t cttsSampleCount = 0; // Sample count in the current ctts table entry
2952 uint32_t lastSamplesPerChunk = 0;
2953
2954 if (mIsAudio) {
2955 prctl(PR_SET_NAME, (unsigned long)"AudioTrackEncoding", 0, 0, 0);
2956 } else if (mIsVideo) {
2957 prctl(PR_SET_NAME, (unsigned long)"VideoTrackEncoding", 0, 0, 0);
2958 } else {
2959 prctl(PR_SET_NAME, (unsigned long)"MetadataTrackEncoding", 0, 0, 0);
2960 }
2961
2962 if (mOwner->isRealTimeRecording()) {
2963 androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
2964 }
2965
2966 sp<MetaData> meta_data;
2967
2968 status_t err = OK;
2969 MediaBufferBase *buffer;
2970 const char *trackName = getTrackType();
2971 while (!mDone && (err = mSource->read(&buffer)) == OK) {
2972 if (buffer->range_length() == 0) {
2973 buffer->release();
2974 buffer = NULL;
2975 ++nZeroLengthFrames;
2976 continue;
2977 }
2978
2979 // If the codec specific data has not been received yet, delay pause.
2980 // After the codec specific data is received, discard what we received
2981 // when the track is to be paused.
2982 if (mPaused && !mResumed) {
2983 buffer->release();
2984 buffer = NULL;
2985 continue;
2986 }
2987
2988 ++count;
2989
2990 int32_t isCodecConfig;
2991 if (buffer->meta_data().findInt32(kKeyIsCodecConfig, &isCodecConfig)
2992 && isCodecConfig) {
2993 // if config format (at track addition) already had CSD, keep that
2994 // UNLESS we have not received any frames yet.
2995 // TODO: for now the entire CSD has to come in one frame for encoders, even though
2996 // they need to be spread out for decoders.
2997 if (mGotAllCodecSpecificData && nActualFrames > 0) {
2998 ALOGI("ignoring additional CSD for video track after first frame");
2999 } else {
3000 mMeta = mSource->getFormat(); // get output format after format change
3001 status_t err;
3002 if (mIsAvc) {
3003 err = makeAVCCodecSpecificData(
3004 (const uint8_t *)buffer->data()
3005 + buffer->range_offset(),
3006 buffer->range_length());
3007 } else if (mIsHevc || mIsHeic) {
3008 err = makeHEVCCodecSpecificData(
3009 (const uint8_t *)buffer->data()
3010 + buffer->range_offset(),
3011 buffer->range_length());
3012 } else if (mIsMPEG4) {
3013 copyCodecSpecificData((const uint8_t *)buffer->data() + buffer->range_offset(),
3014 buffer->range_length());
3015 }
3016 }
3017
3018 buffer->release();
3019 buffer = NULL;
3020 if (OK != err) {
3021 mSource->stop();
3022 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_ERROR,
3023 mTrackId | MEDIA_RECORDER_TRACK_ERROR_GENERAL, err);
3024 break;
3025 }
3026
3027 mGotAllCodecSpecificData = true;
3028 continue;
3029 }
3030
3031 // Per-frame metadata sample's size must be smaller than max allowed.
3032 if (!mIsVideo && !mIsAudio && !mIsHeic &&
3033 buffer->range_length() >= kMaxMetadataSize) {
3034 ALOGW("Buffer size is %zu. Maximum metadata buffer size is %lld for %s track",
3035 buffer->range_length(), (long long)kMaxMetadataSize, trackName);
3036 buffer->release();
3037 mSource->stop();
3038 mIsMalformed = true;
3039 break;
3040 }
3041
3042 bool isExif = false;
3043 uint32_t tiffHdrOffset = 0;
3044 int32_t isMuxerData;
3045 if (buffer->meta_data().findInt32(kKeyIsMuxerData, &isMuxerData) && isMuxerData) {
3046 // We only support one type of muxer data, which is Exif data block.
3047 isExif = isExifData(buffer, &tiffHdrOffset);
3048 if (!isExif) {
3049 ALOGW("Ignoring bad Exif data block");
3050 buffer->release();
3051 buffer = NULL;
3052 continue;
3053 }
3054 }
3055
3056 ++nActualFrames;
3057
3058 // Make a deep copy of the MediaBuffer and Metadata and release
3059 // the original as soon as we can
3060 MediaBuffer *copy = new MediaBuffer(buffer->range_length());
3061 memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(),
3062 buffer->range_length());
3063 copy->set_range(0, buffer->range_length());
3064 meta_data = new MetaData(buffer->meta_data());
3065 buffer->release();
3066 buffer = NULL;
3067
3068 if (isExif) {
3069 copy->meta_data().setInt32(kKeyExifTiffOffset, tiffHdrOffset);
3070 }
3071 bool usePrefix = this->usePrefix() && !isExif;
3072
3073 if (usePrefix) StripStartcode(copy);
3074
3075 size_t sampleSize = copy->range_length();
3076 if (usePrefix) {
3077 if (mOwner->useNalLengthFour()) {
3078 sampleSize += 4;
3079 } else {
3080 sampleSize += 2;
3081 }
3082 }
3083
3084 // Max file size or duration handling
3085 mMdatSizeBytes += sampleSize;
3086 updateTrackSizeEstimate();
3087
3088 if (mOwner->exceedsFileSizeLimit()) {
3089 if (mOwner->switchFd() != OK) {
3090 ALOGW("Recorded file size exceeds limit %" PRId64 "bytes",
3091 mOwner->mMaxFileSizeLimitBytes);
3092 mSource->stop();
3093 mOwner->notify(
3094 MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
3095 } else {
3096 ALOGV("%s Current recorded file size exceeds limit %" PRId64 "bytes. Switching output",
3097 getTrackType(), mOwner->mMaxFileSizeLimitBytes);
3098 }
3099 copy->release();
3100 break;
3101 }
3102
3103 if (mOwner->exceedsFileDurationLimit()) {
3104 ALOGW("Recorded file duration exceeds limit %" PRId64 "microseconds",
3105 mOwner->mMaxFileDurationLimitUs);
3106 mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
3107 copy->release();
3108 mSource->stop();
3109 break;
3110 }
3111
3112 if (mOwner->approachingFileSizeLimit()) {
3113 mOwner->notifyApproachingLimit();
3114 }
3115
3116 int32_t isSync = false;
3117 meta_data->findInt32(kKeyIsSyncFrame, &isSync);
3118 CHECK(meta_data->findInt64(kKeyTime, ×tampUs));
3119
3120 // For video, skip the first several non-key frames until getting the first key frame.
3121 if (mIsVideo && !mGotStartKeyFrame && !isSync) {
3122 ALOGD("Video skip non-key frame");
3123 copy->release();
3124 continue;
3125 }
3126 if (mIsVideo && isSync) {
3127 mGotStartKeyFrame = true;
3128 }
3129 ////////////////////////////////////////////////////////////////////////////////
3130
3131 if (!mIsHeic) {
3132 if (mStszTableEntries->count() == 0) {
3133 mFirstSampleTimeRealUs = systemTime() / 1000;
3134 mOwner->setStartTimestampUs(timestampUs);
3135 mStartTimestampUs = timestampUs;
3136 previousPausedDurationUs = mStartTimestampUs;
3137 }
3138
3139 if (mResumed) {
3140 int64_t durExcludingEarlierPausesUs = timestampUs - previousPausedDurationUs;
3141 if (WARN_UNLESS(durExcludingEarlierPausesUs >= 0LL, "for %s track", trackName)) {
3142 copy->release();
3143 mSource->stop();
3144 mIsMalformed = true;
3145 break;
3146 }
3147
3148 int64_t pausedDurationUs = durExcludingEarlierPausesUs - mTrackDurationUs;
3149 if (WARN_UNLESS(pausedDurationUs >= lastDurationUs, "for %s track", trackName)) {
3150 copy->release();
3151 mSource->stop();
3152 mIsMalformed = true;
3153 break;
3154 }
3155
3156 previousPausedDurationUs += pausedDurationUs - lastDurationUs;
3157 mResumed = false;
3158 }
3159 TimestampDebugHelperEntry timestampDebugEntry;
3160 timestampUs -= previousPausedDurationUs;
3161 timestampDebugEntry.pts = timestampUs;
3162 if (WARN_UNLESS(timestampUs >= 0LL, "for %s track", trackName)) {
3163 copy->release();
3164 mSource->stop();
3165 mIsMalformed = true;
3166 break;
3167 }
3168
3169 if (mIsVideo) {
3170 /*
3171 * Composition time: timestampUs
3172 * Decoding time: decodingTimeUs
3173 * Composition time offset = composition time - decoding time
3174 */
3175 int64_t decodingTimeUs;
3176 CHECK(meta_data->findInt64(kKeyDecodingTime, &decodingTimeUs));
3177 decodingTimeUs -= previousPausedDurationUs;
3178
3179 // ensure non-negative, monotonic decoding time
3180 if (mLastDecodingTimeUs < 0) {
3181 decodingTimeUs = std::max((int64_t)0, decodingTimeUs);
3182 } else {
3183 // increase decoding time by at least the larger vaule of 1 tick and
3184 // 0.1 milliseconds. This needs to take into account the possible
3185 // delta adjustment in DurationTicks in below.
3186 decodingTimeUs = std::max(mLastDecodingTimeUs +
3187 std::max(100, divUp(1000000, mTimeScale)), decodingTimeUs);
3188 }
3189
3190 mLastDecodingTimeUs = decodingTimeUs;
3191 timestampDebugEntry.dts = decodingTimeUs;
3192 timestampDebugEntry.frameType = isSync ? "Key frame" : "Non-Key frame";
3193 // Insert the timestamp into the mTimestampDebugHelper
3194 if (mTimestampDebugHelper.size() >= kTimestampDebugCount) {
3195 mTimestampDebugHelper.pop_front();
3196 }
3197 mTimestampDebugHelper.push_back(timestampDebugEntry);
3198
3199 cttsOffsetTimeUs =
3200 timestampUs + kMaxCttsOffsetTimeUs - decodingTimeUs;
3201 if (WARN_UNLESS(cttsOffsetTimeUs >= 0LL, "for %s track", trackName)) {
3202 copy->release();
3203 mSource->stop();
3204 mIsMalformed = true;
3205 break;
3206 }
3207
3208 timestampUs = decodingTimeUs;
3209 ALOGV("decoding time: %" PRId64 " and ctts offset time: %" PRId64,
3210 timestampUs, cttsOffsetTimeUs);
3211
3212 // Update ctts box table if necessary
3213 currCttsOffsetTimeTicks =
3214 (cttsOffsetTimeUs * mTimeScale + 500000LL) / 1000000LL;
3215 if (WARN_UNLESS(currCttsOffsetTimeTicks <= 0x0FFFFFFFFLL, "for %s track", trackName)) {
3216 copy->release();
3217 mSource->stop();
3218 mIsMalformed = true;
3219 break;
3220 }
3221
3222 if (mStszTableEntries->count() == 0) {
3223 // Force the first ctts table entry to have one single entry
3224 // so that we can do adjustment for the initial track start
3225 // time offset easily in writeCttsBox().
3226 lastCttsOffsetTimeTicks = currCttsOffsetTimeTicks;
3227 addOneCttsTableEntry(1, currCttsOffsetTimeTicks);
3228 cttsSampleCount = 0; // No sample in ctts box is pending
3229 } else {
3230 if (currCttsOffsetTimeTicks != lastCttsOffsetTimeTicks) {
3231 addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
3232 lastCttsOffsetTimeTicks = currCttsOffsetTimeTicks;
3233 cttsSampleCount = 1; // One sample in ctts box is pending
3234 } else {
3235 ++cttsSampleCount;
3236 }
3237 }
3238
3239 // Update ctts time offset range
3240 if (mStszTableEntries->count() == 0) {
3241 mMinCttsOffsetTicks = currCttsOffsetTimeTicks;
3242 mMaxCttsOffsetTicks = currCttsOffsetTimeTicks;
3243 } else {
3244 if (currCttsOffsetTimeTicks > mMaxCttsOffsetTicks) {
3245 mMaxCttsOffsetTicks = currCttsOffsetTimeTicks;
3246 } else if (currCttsOffsetTimeTicks < mMinCttsOffsetTicks) {
3247 mMinCttsOffsetTicks = currCttsOffsetTimeTicks;
3248 mMinCttsOffsetTimeUs = cttsOffsetTimeUs;
3249 }
3250 }
3251 }
3252
3253 if (mOwner->isRealTimeRecording()) {
3254 if (mIsAudio) {
3255 updateDriftTime(meta_data);
3256 }
3257 }
3258
3259 if (WARN_UNLESS(timestampUs >= 0LL, "for %s track", trackName)) {
3260 copy->release();
3261 mSource->stop();
3262 mIsMalformed = true;
3263 break;
3264 }
3265
3266 ALOGV("%s media time stamp: %" PRId64 " and previous paused duration %" PRId64,
3267 trackName, timestampUs, previousPausedDurationUs);
3268 if (timestampUs > mTrackDurationUs) {
3269 mTrackDurationUs = timestampUs;
3270 }
3271
3272 // We need to use the time scale based ticks, rather than the
3273 // timestamp itself to determine whether we have to use a new
3274 // stts entry, since we may have rounding errors.
3275 // The calculation is intended to reduce the accumulated
3276 // rounding errors.
3277 currDurationTicks =
3278 ((timestampUs * mTimeScale + 500000LL) / 1000000LL -
3279 (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
3280 if (currDurationTicks < 0LL) {
3281 ALOGE("do not support out of order frames (timestamp: %lld < last: %lld for %s track",
3282 (long long)timestampUs, (long long)lastTimestampUs, trackName);
3283 copy->release();
3284 mSource->stop();
3285 mIsMalformed = true;
3286 break;
3287 }
3288
3289 // if the duration is different for this sample, see if it is close enough to the previous
3290 // duration that we can fudge it and use the same value, to avoid filling the stts table
3291 // with lots of near-identical entries.
3292 // "close enough" here means that the current duration needs to be adjusted by less
3293 // than 0.1 milliseconds
3294 if (lastDurationTicks && (currDurationTicks != lastDurationTicks)) {
3295 int64_t deltaUs = ((lastDurationTicks - currDurationTicks) * 1000000LL
3296 + (mTimeScale / 2)) / mTimeScale;
3297 if (deltaUs > -100 && deltaUs < 100) {
3298 // use previous ticks, and adjust timestamp as if it was actually that number
3299 // of ticks
3300 currDurationTicks = lastDurationTicks;
3301 timestampUs += deltaUs;
3302 }
3303 }
3304 mStszTableEntries->add(htonl(sampleSize));
3305 if (mStszTableEntries->count() > 2) {
3306
3307 // Force the first sample to have its own stts entry so that
3308 // we can adjust its value later to maintain the A/V sync.
3309 if (mStszTableEntries->count() == 3 || currDurationTicks != lastDurationTicks) {
3310 addOneSttsTableEntry(sampleCount, lastDurationTicks);
3311 sampleCount = 1;
3312 } else {
3313 ++sampleCount;
3314 }
3315
3316 }
3317 if (mSamplesHaveSameSize) {
3318 if (mStszTableEntries->count() >= 2 && previousSampleSize != sampleSize) {
3319 mSamplesHaveSameSize = false;
3320 }
3321 previousSampleSize = sampleSize;
3322 }
3323 ALOGV("%s timestampUs/lastTimestampUs: %" PRId64 "/%" PRId64,
3324 trackName, timestampUs, lastTimestampUs);
3325 lastDurationUs = timestampUs - lastTimestampUs;
3326 lastDurationTicks = currDurationTicks;
3327 lastTimestampUs = timestampUs;
3328
3329 if (isSync != 0) {
3330 addOneStssTableEntry(mStszTableEntries->count());
3331 }
3332
3333 if (mTrackingProgressStatus) {
3334 if (mPreviousTrackTimeUs <= 0) {
3335 mPreviousTrackTimeUs = mStartTimestampUs;
3336 }
3337 trackProgressStatus(timestampUs);
3338 }
3339 }
3340 if (!hasMultipleTracks) {
3341 size_t bytesWritten;
3342 off64_t offset = mOwner->addSample_l(
3343 copy, usePrefix, tiffHdrOffset, &bytesWritten);
3344
3345 if (mIsHeic) {
3346 addItemOffsetAndSize(offset, bytesWritten, isExif);
3347 } else {
3348 uint32_t count = (mOwner->use32BitFileOffset()
3349 ? mStcoTableEntries->count()
3350 : mCo64TableEntries->count());
3351
3352 if (count == 0) {
3353 addChunkOffset(offset);
3354 }
3355 }
3356 copy->release();
3357 copy = NULL;
3358 continue;
3359 }
3360
3361 mChunkSamples.push_back(copy);
3362 if (mIsHeic) {
3363 bufferChunk(0 /*timestampUs*/);
3364 ++nChunks;
3365 } else if (interleaveDurationUs == 0) {
3366 addOneStscTableEntry(++nChunks, 1);
3367 bufferChunk(timestampUs);
3368 } else {
3369 if (chunkTimestampUs == 0) {
3370 chunkTimestampUs = timestampUs;
3371 } else {
3372 int64_t chunkDurationUs = timestampUs - chunkTimestampUs;
3373 if (chunkDurationUs > interleaveDurationUs) {
3374 if (chunkDurationUs > mMaxChunkDurationUs) {
3375 mMaxChunkDurationUs = chunkDurationUs;
3376 }
3377 ++nChunks;
3378 if (nChunks == 1 || // First chunk
3379 lastSamplesPerChunk != mChunkSamples.size()) {
3380 lastSamplesPerChunk = mChunkSamples.size();
3381 addOneStscTableEntry(nChunks, lastSamplesPerChunk);
3382 }
3383 bufferChunk(timestampUs);
3384 chunkTimestampUs = timestampUs;
3385 }
3386 }
3387 }
3388
3389 }
3390
3391 if (isTrackMalFormed()) {
3392 dumpTimeStamps();
3393 err = ERROR_MALFORMED;
3394 }
3395
3396 mOwner->trackProgressStatus(mTrackId, -1, err);
3397
3398 if (mIsHeic) {
3399 if (!mChunkSamples.empty()) {
3400 bufferChunk(0);
3401 ++nChunks;
3402 }
3403 } else {
3404 // Last chunk
3405 if (!hasMultipleTracks) {
3406 addOneStscTableEntry(1, mStszTableEntries->count());
3407 } else if (!mChunkSamples.empty()) {
3408 addOneStscTableEntry(++nChunks, mChunkSamples.size());
3409 bufferChunk(timestampUs);
3410 }
3411
3412 // We don't really know how long the last frame lasts, since
3413 // there is no frame time after it, just repeat the previous
3414 // frame's duration.
3415 if (mStszTableEntries->count() == 1) {
3416 lastDurationUs = 0; // A single sample's duration
3417 lastDurationTicks = 0;
3418 } else {
3419 ++sampleCount; // Count for the last sample
3420 }
3421
3422 if (mStszTableEntries->count() <= 2) {
3423 addOneSttsTableEntry(1, lastDurationTicks);
3424 if (sampleCount - 1 > 0) {
3425 addOneSttsTableEntry(sampleCount - 1, lastDurationTicks);
3426 }
3427 } else {
3428 addOneSttsTableEntry(sampleCount, lastDurationTicks);
3429 }
3430
3431 // The last ctts box may not have been written yet, and this
3432 // is to make sure that we write out the last ctts box.
3433 if (currCttsOffsetTimeTicks == lastCttsOffsetTimeTicks) {
3434 if (cttsSampleCount > 0) {
3435 addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
3436 }
3437 }
3438
3439 mTrackDurationUs += lastDurationUs;
3440 }
3441 mReachedEOS = true;
3442
3443 sendTrackSummary(hasMultipleTracks);
3444
3445 ALOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s",
3446 count, nZeroLengthFrames, mStszTableEntries->count(), trackName);
3447 if (mIsAudio) {
3448 ALOGI("Audio track drift time: %" PRId64 " us", mOwner->getDriftTimeUs());
3449 }
3450
3451 if (err == ERROR_END_OF_STREAM) {
3452 return OK;
3453 }
3454 return err;
3455 }
3456
isTrackMalFormed() const3457 bool MPEG4Writer::Track::isTrackMalFormed() const {
3458 if (mIsMalformed) {
3459 return true;
3460 }
3461
3462 if (!mIsHeic && mStszTableEntries->count() == 0) { // no samples written
3463 ALOGE("The number of recorded samples is 0");
3464 return true;
3465 }
3466
3467 if (mIsVideo && mStssTableEntries->count() == 0) { // no sync frames for video
3468 ALOGE("There are no sync frames for video track");
3469 return true;
3470 }
3471
3472 if (OK != checkCodecSpecificData()) { // no codec specific data
3473 return true;
3474 }
3475
3476 return false;
3477 }
3478
sendTrackSummary(bool hasMultipleTracks)3479 void MPEG4Writer::Track::sendTrackSummary(bool hasMultipleTracks) {
3480
3481 // Send track summary only if test mode is enabled.
3482 if (!isTestModeEnabled()) {
3483 return;
3484 }
3485
3486 int trackNum = (mTrackId << 28);
3487
3488 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
3489 trackNum | MEDIA_RECORDER_TRACK_INFO_TYPE,
3490 mIsAudio ? 0: 1);
3491
3492 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
3493 trackNum | MEDIA_RECORDER_TRACK_INFO_DURATION_MS,
3494 mTrackDurationUs / 1000);
3495
3496 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
3497 trackNum | MEDIA_RECORDER_TRACK_INFO_ENCODED_FRAMES,
3498 mStszTableEntries->count());
3499
3500 {
3501 // The system delay time excluding the requested initial delay that
3502 // is used to eliminate the recording sound.
3503 int64_t startTimeOffsetUs = mOwner->getStartTimeOffsetMs() * 1000LL;
3504 if (startTimeOffsetUs < 0) { // Start time offset was not set
3505 startTimeOffsetUs = kInitialDelayTimeUs;
3506 }
3507 int64_t initialDelayUs =
3508 mFirstSampleTimeRealUs - mStartTimeRealUs - startTimeOffsetUs;
3509
3510 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
3511 trackNum | MEDIA_RECORDER_TRACK_INFO_INITIAL_DELAY_MS,
3512 (initialDelayUs) / 1000);
3513 }
3514
3515 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
3516 trackNum | MEDIA_RECORDER_TRACK_INFO_DATA_KBYTES,
3517 mMdatSizeBytes / 1024);
3518
3519 if (hasMultipleTracks) {
3520 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
3521 trackNum | MEDIA_RECORDER_TRACK_INFO_MAX_CHUNK_DUR_MS,
3522 mMaxChunkDurationUs / 1000);
3523
3524 int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
3525 if (mStartTimestampUs != moovStartTimeUs) {
3526 int64_t startTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
3527 mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
3528 trackNum | MEDIA_RECORDER_TRACK_INFO_START_OFFSET_MS,
3529 startTimeOffsetUs / 1000);
3530 }
3531 }
3532 }
3533
trackProgressStatus(int64_t timeUs,status_t err)3534 void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) {
3535 ALOGV("trackProgressStatus: %" PRId64 " us", timeUs);
3536
3537 if (mTrackEveryTimeDurationUs > 0 &&
3538 timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
3539 ALOGV("Fire time tracking progress status at %" PRId64 " us", timeUs);
3540 mOwner->trackProgressStatus(mTrackId, timeUs - mPreviousTrackTimeUs, err);
3541 mPreviousTrackTimeUs = timeUs;
3542 }
3543 }
3544
trackProgressStatus(size_t trackId,int64_t timeUs,status_t err)3545 void MPEG4Writer::trackProgressStatus(
3546 size_t trackId, int64_t timeUs, status_t err) {
3547 Mutex::Autolock lock(mLock);
3548 int32_t trackNum = (trackId << 28);
3549
3550 // Error notification
3551 // Do not consider ERROR_END_OF_STREAM an error
3552 if (err != OK && err != ERROR_END_OF_STREAM) {
3553 notify(MEDIA_RECORDER_TRACK_EVENT_ERROR,
3554 trackNum | MEDIA_RECORDER_TRACK_ERROR_GENERAL,
3555 err);
3556 return;
3557 }
3558
3559 if (timeUs == -1) {
3560 // Send completion notification
3561 notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
3562 trackNum | MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS,
3563 err);
3564 } else {
3565 // Send progress status
3566 notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
3567 trackNum | MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME,
3568 timeUs / 1000);
3569 }
3570 }
3571
setDriftTimeUs(int64_t driftTimeUs)3572 void MPEG4Writer::setDriftTimeUs(int64_t driftTimeUs) {
3573 ALOGV("setDriftTimeUs: %" PRId64 " us", driftTimeUs);
3574 Mutex::Autolock autolock(mLock);
3575 mDriftTimeUs = driftTimeUs;
3576 }
3577
getDriftTimeUs()3578 int64_t MPEG4Writer::getDriftTimeUs() {
3579 ALOGV("getDriftTimeUs: %" PRId64 " us", mDriftTimeUs);
3580 Mutex::Autolock autolock(mLock);
3581 return mDriftTimeUs;
3582 }
3583
isRealTimeRecording() const3584 bool MPEG4Writer::isRealTimeRecording() const {
3585 return mIsRealTimeRecording;
3586 }
3587
useNalLengthFour()3588 bool MPEG4Writer::useNalLengthFour() {
3589 return mUse4ByteNalLength;
3590 }
3591
bufferChunk(int64_t timestampUs)3592 void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) {
3593 ALOGV("bufferChunk");
3594
3595 Chunk chunk(this, timestampUs, mChunkSamples);
3596 mOwner->bufferChunk(chunk);
3597 mChunkSamples.clear();
3598 }
3599
getDurationUs() const3600 int64_t MPEG4Writer::Track::getDurationUs() const {
3601 return mTrackDurationUs + getStartTimeOffsetTimeUs() + mOwner->getStartTimeOffsetBFramesUs();
3602 }
3603
getEstimatedTrackSizeBytes() const3604 int64_t MPEG4Writer::Track::getEstimatedTrackSizeBytes() const {
3605 return mEstimatedTrackSizeBytes;
3606 }
3607
getMetaSizeIncrease(int32_t angle,int32_t trackCount) const3608 int32_t MPEG4Writer::Track::getMetaSizeIncrease(
3609 int32_t angle, int32_t trackCount) const {
3610 CHECK(mIsHeic);
3611
3612 int32_t grid = (mTileWidth > 0);
3613 int32_t rotate = (angle > 0);
3614
3615 // Note that the rotation angle is in the file meta, and we don't have
3616 // it until start, so here the calculation has to assume rotation.
3617
3618 // increase to ipco
3619 int32_t increase = 20 * (grid + 1) // 'ispe' property
3620 + (8 + mCodecSpecificDataSize) // 'hvcC' property
3621 ;
3622
3623 if (rotate) {
3624 increase += 9; // 'irot' property (worst case)
3625 }
3626
3627 // increase to iref and idat
3628 if (grid) {
3629 increase += (12 + mNumTiles * 2) // 'dimg' in iref
3630 + 12; // ImageGrid in 'idat' (worst case)
3631 }
3632
3633 increase += (12 + 2); // 'cdsc' in iref
3634
3635 // increase to iloc, iinf
3636 increase += (16 // increase to 'iloc'
3637 + 21) // increase to 'iinf'
3638 * (mNumTiles + grid + 1); // "+1" is for 'Exif'
3639
3640 // When total # of properties is > 127, the properties id becomes 2-byte.
3641 // We write 4 properties at most for each image (2x'ispe', 1x'hvcC', 1x'irot').
3642 // Set the threshold to be 30.
3643 int32_t propBytes = trackCount > 30 ? 2 : 1;
3644
3645 // increase to ipma
3646 increase += (3 + 2 * propBytes) * mNumTiles // 'ispe' + 'hvcC'
3647 + grid * (3 + propBytes) // 'ispe' for grid
3648 + rotate * propBytes; // 'irot' (either on grid or tile)
3649
3650 return increase;
3651 }
3652
checkCodecSpecificData() const3653 status_t MPEG4Writer::Track::checkCodecSpecificData() const {
3654 const char *mime;
3655 CHECK(mMeta->findCString(kKeyMIMEType, &mime));
3656 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime) ||
3657 !strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) ||
3658 !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime) ||
3659 !strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime) ||
3660 !strcasecmp(MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, mime)) {
3661 if (!mCodecSpecificData ||
3662 mCodecSpecificDataSize <= 0) {
3663 ALOGE("Missing codec specific data");
3664 return ERROR_MALFORMED;
3665 }
3666 } else {
3667 if (mCodecSpecificData ||
3668 mCodecSpecificDataSize > 0) {
3669 ALOGE("Unexepected codec specific data found");
3670 return ERROR_MALFORMED;
3671 }
3672 }
3673 return OK;
3674 }
3675
getTrackType() const3676 const char *MPEG4Writer::Track::getTrackType() const {
3677 return mIsAudio ? "Audio" :
3678 mIsVideo ? "Video" :
3679 mIsHeic ? "Image" :
3680 "Metadata";
3681 }
3682
writeTrackHeader(bool use32BitOffset)3683 void MPEG4Writer::Track::writeTrackHeader(bool use32BitOffset) {
3684 uint32_t now = getMpeg4Time();
3685 mOwner->beginBox("trak");
3686 writeTkhdBox(now);
3687 writeEdtsBox();
3688 mOwner->beginBox("mdia");
3689 writeMdhdBox(now);
3690 writeHdlrBox();
3691 mOwner->beginBox("minf");
3692 if (mIsAudio) {
3693 writeSmhdBox();
3694 } else if (mIsVideo) {
3695 writeVmhdBox();
3696 } else {
3697 writeNmhdBox();
3698 }
3699 writeDinfBox();
3700 writeStblBox(use32BitOffset);
3701 mOwner->endBox(); // minf
3702 mOwner->endBox(); // mdia
3703 mOwner->endBox(); // trak
3704 }
3705
getMinCttsOffsetTimeUs()3706 int64_t MPEG4Writer::Track::getMinCttsOffsetTimeUs() {
3707 // For video tracks with ctts table, this should return the minimum ctts
3708 // offset in the table. For non-video tracks or video tracks without ctts
3709 // table, this will return kMaxCttsOffsetTimeUs.
3710 if (mMinCttsOffsetTicks == mMaxCttsOffsetTicks) {
3711 return kMaxCttsOffsetTimeUs;
3712 }
3713 return mMinCttsOffsetTimeUs;
3714 }
3715
writeStblBox(bool use32BitOffset)3716 void MPEG4Writer::Track::writeStblBox(bool use32BitOffset) {
3717 mOwner->beginBox("stbl");
3718 mOwner->beginBox("stsd");
3719 mOwner->writeInt32(0); // version=0, flags=0
3720 mOwner->writeInt32(1); // entry count
3721 if (mIsAudio) {
3722 writeAudioFourCCBox();
3723 } else if (mIsVideo) {
3724 writeVideoFourCCBox();
3725 } else {
3726 writeMetadataFourCCBox();
3727 }
3728 mOwner->endBox(); // stsd
3729 writeSttsBox();
3730 if (mIsVideo) {
3731 writeCttsBox();
3732 writeStssBox();
3733 }
3734 writeStszBox();
3735 writeStscBox();
3736 writeStcoBox(use32BitOffset);
3737 mOwner->endBox(); // stbl
3738 }
3739
writeMetadataFourCCBox()3740 void MPEG4Writer::Track::writeMetadataFourCCBox() {
3741 const char *mime;
3742 bool success = mMeta->findCString(kKeyMIMEType, &mime);
3743 CHECK(success);
3744 const char *fourcc = getFourCCForMime(mime);
3745 if (fourcc == NULL) {
3746 ALOGE("Unknown mime type '%s'.", mime);
3747 TRESPASS();
3748 }
3749 mOwner->beginBox(fourcc); // TextMetaDataSampleEntry
3750
3751 // HACK to make the metadata track compliant with the ISO standard.
3752 //
3753 // Metadata track is added from API 26 and the original implementation does not
3754 // fully followed the TextMetaDataSampleEntry specified in ISO/IEC 14496-12-2015
3755 // in that only the mime_format is written out. content_encoding and
3756 // data_reference_index have not been written out. This leads to the failure
3757 // when some MP4 parser tries to parse the metadata track according to the
3758 // standard. The hack here will make the metadata track compliant with the
3759 // standard while still maintaining backwards compatibility. This would enable
3760 // Android versions before API 29 to be able to read out the standard compliant
3761 // Metadata track generated with Android API 29 and upward. The trick is based
3762 // on the fact that the Metadata track must start with prefix “application/” and
3763 // those missing fields are not used in Android's Metadata track. By writting
3764 // out the mime_format twice, the first mime_format will be used to fill out the
3765 // missing reserved, data_reference_index and content encoding fields. On the
3766 // parser side, the extracter before API 29 will read out the first mime_format
3767 // correctly and drop the second mime_format. The extractor from API 29 will
3768 // check if the reserved, data_reference_index and content encoding are filled
3769 // with “application” to detect if this is a standard compliant metadata track
3770 // and read out the data accordingly.
3771 mOwner->writeCString(mime);
3772
3773 mOwner->writeCString(mime); // metadata mime_format
3774 mOwner->endBox(); // mett
3775 }
3776
writeVideoFourCCBox()3777 void MPEG4Writer::Track::writeVideoFourCCBox() {
3778 const char *mime;
3779 bool success = mMeta->findCString(kKeyMIMEType, &mime);
3780 CHECK(success);
3781 const char *fourcc = getFourCCForMime(mime);
3782 if (fourcc == NULL) {
3783 ALOGE("Unknown mime type '%s'.", mime);
3784 TRESPASS();
3785 }
3786
3787 mOwner->beginBox(fourcc); // video format
3788 mOwner->writeInt32(0); // reserved
3789 mOwner->writeInt16(0); // reserved
3790 mOwner->writeInt16(1); // data ref index
3791 mOwner->writeInt16(0); // predefined
3792 mOwner->writeInt16(0); // reserved
3793 mOwner->writeInt32(0); // predefined
3794 mOwner->writeInt32(0); // predefined
3795 mOwner->writeInt32(0); // predefined
3796
3797 int32_t width, height;
3798 success = mMeta->findInt32(kKeyWidth, &width);
3799 success = success && mMeta->findInt32(kKeyHeight, &height);
3800 CHECK(success);
3801
3802 mOwner->writeInt16(width);
3803 mOwner->writeInt16(height);
3804 mOwner->writeInt32(0x480000); // horiz resolution
3805 mOwner->writeInt32(0x480000); // vert resolution
3806 mOwner->writeInt32(0); // reserved
3807 mOwner->writeInt16(1); // frame count
3808 mOwner->writeInt8(0); // compressor string length
3809 mOwner->write(" ", 31);
3810 mOwner->writeInt16(0x18); // depth
3811 mOwner->writeInt16(-1); // predefined
3812
3813 if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
3814 writeMp4vEsdsBox();
3815 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
3816 writeD263Box();
3817 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
3818 writeAvccBox();
3819 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) {
3820 writeHvccBox();
3821 }
3822
3823 writePaspBox();
3824 writeColrBox();
3825 mOwner->endBox(); // mp4v, s263 or avc1
3826 }
3827
writeColrBox()3828 void MPEG4Writer::Track::writeColrBox() {
3829 ColorAspects aspects;
3830 memset(&aspects, 0, sizeof(aspects));
3831 // TRICKY: using | instead of || because we want to execute all findInt32-s
3832 if (mMeta->findInt32(kKeyColorPrimaries, (int32_t*)&aspects.mPrimaries)
3833 | mMeta->findInt32(kKeyTransferFunction, (int32_t*)&aspects.mTransfer)
3834 | mMeta->findInt32(kKeyColorMatrix, (int32_t*)&aspects.mMatrixCoeffs)
3835 | mMeta->findInt32(kKeyColorRange, (int32_t*)&aspects.mRange)) {
3836 int32_t primaries, transfer, coeffs;
3837 bool fullRange;
3838 ColorUtils::convertCodecColorAspectsToIsoAspects(
3839 aspects, &primaries, &transfer, &coeffs, &fullRange);
3840 mOwner->beginBox("colr");
3841 mOwner->writeFourcc("nclx");
3842 mOwner->writeInt16(primaries);
3843 mOwner->writeInt16(transfer);
3844 mOwner->writeInt16(coeffs);
3845 mOwner->writeInt8(int8_t(fullRange ? 0x80 : 0x0));
3846 mOwner->endBox(); // colr
3847 }
3848 }
3849
writeAudioFourCCBox()3850 void MPEG4Writer::Track::writeAudioFourCCBox() {
3851 const char *mime;
3852 bool success = mMeta->findCString(kKeyMIMEType, &mime);
3853 CHECK(success);
3854 const char *fourcc = getFourCCForMime(mime);
3855 if (fourcc == NULL) {
3856 ALOGE("Unknown mime type '%s'.", mime);
3857 TRESPASS();
3858 }
3859
3860 mOwner->beginBox(fourcc); // audio format
3861 mOwner->writeInt32(0); // reserved
3862 mOwner->writeInt16(0); // reserved
3863 mOwner->writeInt16(0x1); // data ref index
3864 mOwner->writeInt32(0); // reserved
3865 mOwner->writeInt32(0); // reserved
3866 int32_t nChannels;
3867 CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels));
3868 mOwner->writeInt16(nChannels); // channel count
3869 mOwner->writeInt16(16); // sample size
3870 mOwner->writeInt16(0); // predefined
3871 mOwner->writeInt16(0); // reserved
3872
3873 int32_t samplerate;
3874 success = mMeta->findInt32(kKeySampleRate, &samplerate);
3875 CHECK(success);
3876 mOwner->writeInt32(samplerate << 16);
3877 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
3878 writeMp4aEsdsBox();
3879 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime) ||
3880 !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
3881 writeDamrBox();
3882 }
3883 mOwner->endBox();
3884 }
3885
generateEsdsSize(size_t dataLength,size_t * sizeGenerated,uint8_t * buffer)3886 static void generateEsdsSize(size_t dataLength, size_t* sizeGenerated, uint8_t* buffer) {
3887 size_t offset = 0, cur = 0;
3888 size_t more = 0x00;
3889 *sizeGenerated = 0;
3890 /* Start with the LSB(7 bits) of dataLength and build the byte sequence upto MSB.
3891 * Continuation flag(most significant bit) will be set on the first N-1 bytes.
3892 */
3893 do {
3894 buffer[cur++] = (dataLength & 0x7f) | more;
3895 dataLength >>= 7;
3896 more = 0x80;
3897 ++(*sizeGenerated);
3898 } while (dataLength > 0u);
3899 --cur;
3900 // Reverse the newly formed byte sequence.
3901 while (cur > offset) {
3902 uint8_t tmp = buffer[cur];
3903 buffer[cur--] = buffer[offset];
3904 buffer[offset++] = tmp;
3905 }
3906 }
3907
writeMp4aEsdsBox()3908 void MPEG4Writer::Track::writeMp4aEsdsBox() {
3909 CHECK(mCodecSpecificData);
3910 CHECK_GT(mCodecSpecificDataSize, 0u);
3911
3912 uint8_t sizeESDBuffer[kESDSScratchBufferSize];
3913 uint8_t sizeDCDBuffer[kESDSScratchBufferSize];
3914 uint8_t sizeDSIBuffer[kESDSScratchBufferSize];
3915 size_t sizeESD = 0;
3916 size_t sizeDCD = 0;
3917 size_t sizeDSI = 0;
3918 generateEsdsSize(mCodecSpecificDataSize, &sizeDSI, sizeDSIBuffer);
3919 generateEsdsSize(mCodecSpecificDataSize + sizeDSI + 14, &sizeDCD, sizeDCDBuffer);
3920 generateEsdsSize(mCodecSpecificDataSize + sizeDSI + sizeDCD + 21, &sizeESD, sizeESDBuffer);
3921
3922 mOwner->beginBox("esds");
3923
3924 mOwner->writeInt32(0); // version=0, flags=0
3925 mOwner->writeInt8(0x03); // ES_DescrTag
3926 mOwner->write(sizeESDBuffer, sizeESD);
3927 mOwner->writeInt16(0x0000);// ES_ID
3928 mOwner->writeInt8(0x00);
3929
3930 mOwner->writeInt8(0x04); // DecoderConfigDescrTag
3931 mOwner->write(sizeDCDBuffer, sizeDCD);
3932 mOwner->writeInt8(0x40); // objectTypeIndication ISO/IEC 14492-2
3933 mOwner->writeInt8(0x15); // streamType AudioStream
3934
3935 mOwner->writeInt16(0x03); // XXX
3936 mOwner->writeInt8(0x00); // buffer size 24-bit (0x300)
3937
3938 int32_t avgBitrate = 0;
3939 (void)mMeta->findInt32(kKeyBitRate, &avgBitrate);
3940 int32_t maxBitrate = 0;
3941 (void)mMeta->findInt32(kKeyMaxBitRate, &maxBitrate);
3942 mOwner->writeInt32(maxBitrate);
3943 mOwner->writeInt32(avgBitrate);
3944
3945 mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
3946 mOwner->write(sizeDSIBuffer, sizeDSI);
3947 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
3948
3949 static const uint8_t kData2[] = {
3950 0x06, // SLConfigDescriptorTag
3951 0x01,
3952 0x02
3953 };
3954 mOwner->write(kData2, sizeof(kData2));
3955
3956 mOwner->endBox(); // esds
3957 }
3958
writeMp4vEsdsBox()3959 void MPEG4Writer::Track::writeMp4vEsdsBox() {
3960 CHECK(mCodecSpecificData);
3961 CHECK_GT(mCodecSpecificDataSize, 0u);
3962
3963 uint8_t sizeESDBuffer[kESDSScratchBufferSize];
3964 uint8_t sizeDCDBuffer[kESDSScratchBufferSize];
3965 uint8_t sizeDSIBuffer[kESDSScratchBufferSize];
3966 size_t sizeESD = 0;
3967 size_t sizeDCD = 0;
3968 size_t sizeDSI = 0;
3969 generateEsdsSize(mCodecSpecificDataSize, &sizeDSI, sizeDSIBuffer);
3970 generateEsdsSize(mCodecSpecificDataSize + sizeDSI + 14, &sizeDCD, sizeDCDBuffer);
3971 generateEsdsSize(mCodecSpecificDataSize + sizeDSI + sizeDCD + 21, &sizeESD, sizeESDBuffer);
3972
3973 mOwner->beginBox("esds");
3974
3975 mOwner->writeInt32(0); // version=0, flags=0
3976
3977 mOwner->writeInt8(0x03); // ES_DescrTag
3978 mOwner->write(sizeESDBuffer, sizeESD);
3979 mOwner->writeInt16(0x0000); // ES_ID
3980 mOwner->writeInt8(0x1f);
3981
3982 mOwner->writeInt8(0x04); // DecoderConfigDescrTag
3983 mOwner->write(sizeDCDBuffer, sizeDCD);
3984 mOwner->writeInt8(0x20); // objectTypeIndication ISO/IEC 14492-2
3985 mOwner->writeInt8(0x11); // streamType VisualStream
3986
3987 static const uint8_t kData[] = {
3988 0x01, 0x77, 0x00, // buffer size 96000 bytes
3989 };
3990 mOwner->write(kData, sizeof(kData));
3991
3992 int32_t avgBitrate = 0;
3993 (void)mMeta->findInt32(kKeyBitRate, &avgBitrate);
3994 int32_t maxBitrate = 0;
3995 (void)mMeta->findInt32(kKeyMaxBitRate, &maxBitrate);
3996 mOwner->writeInt32(maxBitrate);
3997 mOwner->writeInt32(avgBitrate);
3998
3999 mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
4000
4001 mOwner->write(sizeDSIBuffer, sizeDSI);
4002 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
4003
4004 static const uint8_t kData2[] = {
4005 0x06, // SLConfigDescriptorTag
4006 0x01,
4007 0x02
4008 };
4009 mOwner->write(kData2, sizeof(kData2));
4010
4011 mOwner->endBox(); // esds
4012 }
4013
writeTkhdBox(uint32_t now)4014 void MPEG4Writer::Track::writeTkhdBox(uint32_t now) {
4015 mOwner->beginBox("tkhd");
4016 // Flags = 7 to indicate that the track is enabled, and
4017 // part of the presentation
4018 mOwner->writeInt32(0x07); // version=0, flags=7
4019 mOwner->writeInt32(now); // creation time
4020 mOwner->writeInt32(now); // modification time
4021 mOwner->writeInt32(mTrackId); // track id starts with 1
4022 mOwner->writeInt32(0); // reserved
4023 int64_t trakDurationUs = getDurationUs();
4024 int32_t mvhdTimeScale = mOwner->getTimeScale();
4025 int32_t tkhdDuration =
4026 (trakDurationUs * mvhdTimeScale + 5E5) / 1E6;
4027 mOwner->writeInt32(tkhdDuration); // in mvhd timescale
4028 mOwner->writeInt32(0); // reserved
4029 mOwner->writeInt32(0); // reserved
4030 mOwner->writeInt16(0); // layer
4031 mOwner->writeInt16(0); // alternate group
4032 mOwner->writeInt16(mIsAudio ? 0x100 : 0); // volume
4033 mOwner->writeInt16(0); // reserved
4034
4035 mOwner->writeCompositionMatrix(mRotation); // matrix
4036
4037 if (!mIsVideo) {
4038 mOwner->writeInt32(0);
4039 mOwner->writeInt32(0);
4040 } else {
4041 int32_t width, height;
4042 bool success = mMeta->findInt32(kKeyDisplayWidth, &width);
4043 success = success && mMeta->findInt32(kKeyDisplayHeight, &height);
4044
4045 // Use width/height if display width/height are not present.
4046 if (!success) {
4047 success = mMeta->findInt32(kKeyWidth, &width);
4048 success = success && mMeta->findInt32(kKeyHeight, &height);
4049 }
4050 CHECK(success);
4051
4052 mOwner->writeInt32(width << 16); // 32-bit fixed-point value
4053 mOwner->writeInt32(height << 16); // 32-bit fixed-point value
4054 }
4055 mOwner->endBox(); // tkhd
4056 }
4057
writeVmhdBox()4058 void MPEG4Writer::Track::writeVmhdBox() {
4059 mOwner->beginBox("vmhd");
4060 mOwner->writeInt32(0x01); // version=0, flags=1
4061 mOwner->writeInt16(0); // graphics mode
4062 mOwner->writeInt16(0); // opcolor
4063 mOwner->writeInt16(0);
4064 mOwner->writeInt16(0);
4065 mOwner->endBox();
4066 }
4067
writeSmhdBox()4068 void MPEG4Writer::Track::writeSmhdBox() {
4069 mOwner->beginBox("smhd");
4070 mOwner->writeInt32(0); // version=0, flags=0
4071 mOwner->writeInt16(0); // balance
4072 mOwner->writeInt16(0); // reserved
4073 mOwner->endBox();
4074 }
4075
writeNmhdBox()4076 void MPEG4Writer::Track::writeNmhdBox() {
4077 mOwner->beginBox("nmhd");
4078 mOwner->writeInt32(0); // version=0, flags=0
4079 mOwner->endBox();
4080 }
4081
writeHdlrBox()4082 void MPEG4Writer::Track::writeHdlrBox() {
4083 mOwner->beginBox("hdlr");
4084 mOwner->writeInt32(0); // version=0, flags=0
4085 mOwner->writeInt32(0); // component type: should be mhlr
4086 mOwner->writeFourcc(mIsAudio ? "soun" : (mIsVideo ? "vide" : "meta")); // component subtype
4087 mOwner->writeInt32(0); // reserved
4088 mOwner->writeInt32(0); // reserved
4089 mOwner->writeInt32(0); // reserved
4090 // Removing "r" for the name string just makes the string 4 byte aligned
4091 mOwner->writeCString(mIsAudio ? "SoundHandle": (mIsVideo ? "VideoHandle" : "MetadHandle"));
4092 mOwner->endBox();
4093 }
4094
writeEdtsBox()4095 void MPEG4Writer::Track::writeEdtsBox(){
4096 ALOGV("%s : getStartTimeOffsetTimeUs of track:%" PRId64 " us", getTrackType(),
4097 getStartTimeOffsetTimeUs());
4098
4099 // Prepone video playback.
4100 if (mMinCttsOffsetTicks != mMaxCttsOffsetTicks) {
4101 int32_t mvhdTimeScale = mOwner->getTimeScale();
4102 uint32_t tkhdDuration = (getDurationUs() * mvhdTimeScale + 5E5) / 1E6;
4103 int64_t mediaTime = ((kMaxCttsOffsetTimeUs - getMinCttsOffsetTimeUs())
4104 * mTimeScale + 5E5) / 1E6;
4105 if (tkhdDuration > 0 && mediaTime > 0) {
4106 addOneElstTableEntry(tkhdDuration, mediaTime, 1, 0);
4107 }
4108 }
4109
4110 if (mElstTableEntries->count() == 0) {
4111 return;
4112 }
4113
4114 mOwner->beginBox("edts");
4115 mOwner->beginBox("elst");
4116 mOwner->writeInt32(0); // version=0, flags=0
4117 mElstTableEntries->write(mOwner);
4118 mOwner->endBox(); // elst;
4119 mOwner->endBox(); // edts
4120 }
4121
writeMdhdBox(uint32_t now)4122 void MPEG4Writer::Track::writeMdhdBox(uint32_t now) {
4123 int64_t trakDurationUs = getDurationUs();
4124 int64_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6;
4125 mOwner->beginBox("mdhd");
4126
4127 if (mdhdDuration > UINT32_MAX) {
4128 mOwner->writeInt32((1 << 24)); // version=1, flags=0
4129 mOwner->writeInt64((int64_t)now); // creation time
4130 mOwner->writeInt64((int64_t)now); // modification time
4131 mOwner->writeInt32(mTimeScale); // media timescale
4132 mOwner->writeInt64(mdhdDuration); // media timescale
4133 } else {
4134 mOwner->writeInt32(0); // version=0, flags=0
4135 mOwner->writeInt32(now); // creation time
4136 mOwner->writeInt32(now); // modification time
4137 mOwner->writeInt32(mTimeScale); // media timescale
4138 mOwner->writeInt32((int32_t)mdhdDuration); // use media timescale
4139 }
4140 // Language follows the three letter standard ISO-639-2/T
4141 // 'e', 'n', 'g' for "English", for instance.
4142 // Each character is packed as the difference between its ASCII value and 0x60.
4143 // For "English", these are 00101, 01110, 00111.
4144 // XXX: Where is the padding bit located: 0x15C7?
4145 const char *lang = NULL;
4146 int16_t langCode = 0;
4147 if (mMeta->findCString(kKeyMediaLanguage, &lang) && lang && strnlen(lang, 3) > 2) {
4148 langCode = ((lang[0] & 0x1f) << 10) | ((lang[1] & 0x1f) << 5) | (lang[2] & 0x1f);
4149 }
4150 mOwner->writeInt16(langCode); // language code
4151 mOwner->writeInt16(0); // predefined
4152 mOwner->endBox();
4153 }
4154
writeDamrBox()4155 void MPEG4Writer::Track::writeDamrBox() {
4156 // 3gpp2 Spec AMRSampleEntry fields
4157 mOwner->beginBox("damr");
4158 mOwner->writeCString(" "); // vendor: 4 bytes
4159 mOwner->writeInt8(0); // decoder version
4160 mOwner->writeInt16(0x83FF); // mode set: all enabled
4161 mOwner->writeInt8(0); // mode change period
4162 mOwner->writeInt8(1); // frames per sample
4163 mOwner->endBox();
4164 }
4165
writeUrlBox()4166 void MPEG4Writer::Track::writeUrlBox() {
4167 // The table index here refers to the sample description index
4168 // in the sample table entries.
4169 mOwner->beginBox("url ");
4170 mOwner->writeInt32(1); // version=0, flags=1 (self-contained)
4171 mOwner->endBox(); // url
4172 }
4173
writeDrefBox()4174 void MPEG4Writer::Track::writeDrefBox() {
4175 mOwner->beginBox("dref");
4176 mOwner->writeInt32(0); // version=0, flags=0
4177 mOwner->writeInt32(1); // entry count (either url or urn)
4178 writeUrlBox();
4179 mOwner->endBox(); // dref
4180 }
4181
writeDinfBox()4182 void MPEG4Writer::Track::writeDinfBox() {
4183 mOwner->beginBox("dinf");
4184 writeDrefBox();
4185 mOwner->endBox(); // dinf
4186 }
4187
writeAvccBox()4188 void MPEG4Writer::Track::writeAvccBox() {
4189 CHECK(mCodecSpecificData);
4190 CHECK_GE(mCodecSpecificDataSize, 5u);
4191
4192 // Patch avcc's lengthSize field to match the number
4193 // of bytes we use to indicate the size of a nal unit.
4194 uint8_t *ptr = (uint8_t *)mCodecSpecificData;
4195 ptr[4] = (ptr[4] & 0xfc) | (mOwner->useNalLengthFour() ? 3 : 1);
4196 mOwner->beginBox("avcC");
4197 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
4198 mOwner->endBox(); // avcC
4199 }
4200
4201
writeHvccBox()4202 void MPEG4Writer::Track::writeHvccBox() {
4203 CHECK(mCodecSpecificData);
4204 CHECK_GE(mCodecSpecificDataSize, 5u);
4205
4206 // Patch avcc's lengthSize field to match the number
4207 // of bytes we use to indicate the size of a nal unit.
4208 uint8_t *ptr = (uint8_t *)mCodecSpecificData;
4209 ptr[21] = (ptr[21] & 0xfc) | (mOwner->useNalLengthFour() ? 3 : 1);
4210 mOwner->beginBox("hvcC");
4211 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
4212 mOwner->endBox(); // hvcC
4213 }
4214
writeD263Box()4215 void MPEG4Writer::Track::writeD263Box() {
4216 mOwner->beginBox("d263");
4217 mOwner->writeInt32(0); // vendor
4218 mOwner->writeInt8(0); // decoder version
4219 mOwner->writeInt8(10); // level: 10
4220 mOwner->writeInt8(0); // profile: 0
4221 mOwner->endBox(); // d263
4222 }
4223
4224 // This is useful if the pixel is not square
writePaspBox()4225 void MPEG4Writer::Track::writePaspBox() {
4226 mOwner->beginBox("pasp");
4227 mOwner->writeInt32(1 << 16); // hspacing
4228 mOwner->writeInt32(1 << 16); // vspacing
4229 mOwner->endBox(); // pasp
4230 }
4231
getStartTimeOffsetTimeUs() const4232 int64_t MPEG4Writer::Track::getStartTimeOffsetTimeUs() const {
4233 int64_t trackStartTimeOffsetUs = 0;
4234 int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
4235 if (mStartTimestampUs != -1 && mStartTimestampUs != moovStartTimeUs) {
4236 CHECK_GT(mStartTimestampUs, moovStartTimeUs);
4237 trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
4238 }
4239 return trackStartTimeOffsetUs;
4240 }
4241
getStartTimeOffsetScaledTime() const4242 int32_t MPEG4Writer::Track::getStartTimeOffsetScaledTime() const {
4243 return (getStartTimeOffsetTimeUs() * mTimeScale + 500000LL) / 1000000LL;
4244 }
4245
writeSttsBox()4246 void MPEG4Writer::Track::writeSttsBox() {
4247 mOwner->beginBox("stts");
4248 mOwner->writeInt32(0); // version=0, flags=0
4249 if (mMinCttsOffsetTicks == mMaxCttsOffsetTicks) {
4250 // For non-vdeio tracks or video tracks without ctts table,
4251 // adjust duration of first sample for tracks to account for
4252 // first sample not starting at the media start time.
4253 // TODO: consider signaling this using some offset
4254 // as this is not quite correct.
4255 uint32_t duration;
4256 CHECK(mSttsTableEntries->get(duration, 1));
4257 duration = htonl(duration); // Back to host byte order
4258 int32_t startTimeOffsetScaled = (((getStartTimeOffsetTimeUs() +
4259 mOwner->getStartTimeOffsetBFramesUs()) * mTimeScale) + 500000LL) / 1000000LL;
4260 mSttsTableEntries->set(htonl((int32_t)duration + startTimeOffsetScaled), 1);
4261 }
4262 mSttsTableEntries->write(mOwner);
4263 mOwner->endBox(); // stts
4264 }
4265
writeCttsBox()4266 void MPEG4Writer::Track::writeCttsBox() {
4267 // There is no B frame at all
4268 if (mMinCttsOffsetTicks == mMaxCttsOffsetTicks) {
4269 return;
4270 }
4271
4272 // Do not write ctts box when there is no need to have it.
4273 if (mCttsTableEntries->count() == 0) {
4274 return;
4275 }
4276
4277 ALOGV("ctts box has %d entries with range [%" PRId64 ", %" PRId64 "]",
4278 mCttsTableEntries->count(), mMinCttsOffsetTicks, mMaxCttsOffsetTicks);
4279
4280 mOwner->beginBox("ctts");
4281 mOwner->writeInt32(0); // version=0, flags=0
4282 int64_t deltaTimeUs = kMaxCttsOffsetTimeUs - getStartTimeOffsetTimeUs();
4283 int64_t delta = (deltaTimeUs * mTimeScale + 500000LL) / 1000000LL;
4284 mCttsTableEntries->adjustEntries([delta](size_t /* ix */, uint32_t (&value)[2]) {
4285 // entries are <count, ctts> pairs; adjust only ctts
4286 uint32_t duration = htonl(value[1]); // back to host byte order
4287 // Prevent overflow and underflow
4288 if (delta > duration) {
4289 duration = 0;
4290 } else if (delta < 0 && UINT32_MAX + delta < duration) {
4291 duration = UINT32_MAX;
4292 } else {
4293 duration -= delta;
4294 }
4295 value[1] = htonl(duration);
4296 });
4297 mCttsTableEntries->write(mOwner);
4298 mOwner->endBox(); // ctts
4299 }
4300
writeStssBox()4301 void MPEG4Writer::Track::writeStssBox() {
4302 mOwner->beginBox("stss");
4303 mOwner->writeInt32(0); // version=0, flags=0
4304 mStssTableEntries->write(mOwner);
4305 mOwner->endBox(); // stss
4306 }
4307
writeStszBox()4308 void MPEG4Writer::Track::writeStszBox() {
4309 mOwner->beginBox("stsz");
4310 mOwner->writeInt32(0); // version=0, flags=0
4311 mOwner->writeInt32(0);
4312 mStszTableEntries->write(mOwner);
4313 mOwner->endBox(); // stsz
4314 }
4315
writeStscBox()4316 void MPEG4Writer::Track::writeStscBox() {
4317 mOwner->beginBox("stsc");
4318 mOwner->writeInt32(0); // version=0, flags=0
4319 mStscTableEntries->write(mOwner);
4320 mOwner->endBox(); // stsc
4321 }
4322
writeStcoBox(bool use32BitOffset)4323 void MPEG4Writer::Track::writeStcoBox(bool use32BitOffset) {
4324 mOwner->beginBox(use32BitOffset? "stco": "co64");
4325 mOwner->writeInt32(0); // version=0, flags=0
4326 if (use32BitOffset) {
4327 mStcoTableEntries->write(mOwner);
4328 } else {
4329 mCo64TableEntries->write(mOwner);
4330 }
4331 mOwner->endBox(); // stco or co64
4332 }
4333
writeUdtaBox()4334 void MPEG4Writer::writeUdtaBox() {
4335 beginBox("udta");
4336 writeGeoDataBox();
4337 endBox();
4338 }
4339
writeHdlr(const char * handlerType)4340 void MPEG4Writer::writeHdlr(const char *handlerType) {
4341 beginBox("hdlr");
4342 writeInt32(0); // Version, Flags
4343 writeInt32(0); // Predefined
4344 writeFourcc(handlerType);
4345 writeInt32(0); // Reserved[0]
4346 writeInt32(0); // Reserved[1]
4347 writeInt32(0); // Reserved[2]
4348 writeInt8(0); // Name (empty)
4349 endBox();
4350 }
4351
writeKeys()4352 void MPEG4Writer::writeKeys() {
4353 size_t count = mMetaKeys->countEntries();
4354
4355 beginBox("keys");
4356 writeInt32(0); // Version, Flags
4357 writeInt32(count); // Entry_count
4358 for (size_t i = 0; i < count; i++) {
4359 AMessage::Type type;
4360 const char *key = mMetaKeys->getEntryNameAt(i, &type);
4361 size_t n = strlen(key);
4362 writeInt32(n + 8);
4363 writeFourcc("mdta");
4364 write(key, n); // write without the \0
4365 }
4366 endBox();
4367 }
4368
writeIlst()4369 void MPEG4Writer::writeIlst() {
4370 size_t count = mMetaKeys->countEntries();
4371
4372 beginBox("ilst");
4373 for (size_t i = 0; i < count; i++) {
4374 beginBox(i + 1); // key id (1-based)
4375 beginBox("data");
4376 AMessage::Type type;
4377 const char *key = mMetaKeys->getEntryNameAt(i, &type);
4378 switch (type) {
4379 case AMessage::kTypeString:
4380 {
4381 AString val;
4382 CHECK(mMetaKeys->findString(key, &val));
4383 writeInt32(1); // type = UTF8
4384 writeInt32(0); // default country/language
4385 write(val.c_str(), strlen(val.c_str())); // write without \0
4386 break;
4387 }
4388
4389 case AMessage::kTypeFloat:
4390 {
4391 float val;
4392 CHECK(mMetaKeys->findFloat(key, &val));
4393 writeInt32(23); // type = float32
4394 writeInt32(0); // default country/language
4395 writeInt32(*reinterpret_cast<int32_t *>(&val));
4396 break;
4397 }
4398
4399 case AMessage::kTypeInt32:
4400 {
4401 int32_t val;
4402 CHECK(mMetaKeys->findInt32(key, &val));
4403 writeInt32(67); // type = signed int32
4404 writeInt32(0); // default country/language
4405 writeInt32(val);
4406 break;
4407 }
4408
4409 default:
4410 {
4411 ALOGW("Unsupported key type, writing 0 instead");
4412 writeInt32(77); // type = unsigned int32
4413 writeInt32(0); // default country/language
4414 writeInt32(0);
4415 break;
4416 }
4417 }
4418 endBox(); // data
4419 endBox(); // key id
4420 }
4421 endBox(); // ilst
4422 }
4423
writeMoovLevelMetaBox()4424 void MPEG4Writer::writeMoovLevelMetaBox() {
4425 size_t count = mMetaKeys->countEntries();
4426 if (count == 0) {
4427 return;
4428 }
4429
4430 beginBox("meta");
4431 writeHdlr("mdta");
4432 writeKeys();
4433 writeIlst();
4434 endBox();
4435 }
4436
writeIlocBox()4437 void MPEG4Writer::writeIlocBox() {
4438 beginBox("iloc");
4439 // Use version 1 to allow construction method 1 that refers to
4440 // data in idat box inside meta box.
4441 writeInt32(0x01000000); // Version = 1, Flags = 0
4442 writeInt16(0x4400); // offset_size = length_size = 4
4443 // base_offset_size = index_size = 0
4444
4445 // 16-bit item_count
4446 size_t itemCount = mItems.size();
4447 if (itemCount > 65535) {
4448 ALOGW("Dropping excess items: itemCount %zu", itemCount);
4449 itemCount = 65535;
4450 }
4451 writeInt16((uint16_t)itemCount);
4452
4453 for (size_t i = 0; i < itemCount; i++) {
4454 writeInt16(mItems[i].itemId);
4455 bool isGrid = mItems[i].isGrid();
4456
4457 writeInt16(isGrid ? 1 : 0); // construction_method
4458 writeInt16(0); // data_reference_index = 0
4459 writeInt16(1); // extent_count = 1
4460
4461 if (isGrid) {
4462 // offset into the 'idat' box
4463 writeInt32(mNumGrids++ * 8);
4464 writeInt32(8);
4465 } else {
4466 writeInt32(mItems[i].offset);
4467 writeInt32(mItems[i].size);
4468 }
4469 }
4470 endBox();
4471 }
4472
writeInfeBox(uint16_t itemId,const char * itemType,uint32_t flags)4473 void MPEG4Writer::writeInfeBox(
4474 uint16_t itemId, const char *itemType, uint32_t flags) {
4475 beginBox("infe");
4476 writeInt32(0x02000000 | flags); // Version = 2, Flags = 0
4477 writeInt16(itemId);
4478 writeInt16(0); //item_protection_index = 0
4479 writeFourcc(itemType);
4480 writeCString(""); // item_name
4481 endBox();
4482 }
4483
writeIinfBox()4484 void MPEG4Writer::writeIinfBox() {
4485 beginBox("iinf");
4486 writeInt32(0); // Version = 0, Flags = 0
4487
4488 // 16-bit item_count
4489 size_t itemCount = mItems.size();
4490 if (itemCount > 65535) {
4491 ALOGW("Dropping excess items: itemCount %zu", itemCount);
4492 itemCount = 65535;
4493 }
4494
4495 writeInt16((uint16_t)itemCount);
4496 for (size_t i = 0; i < itemCount; i++) {
4497 writeInfeBox(mItems[i].itemId, mItems[i].itemType,
4498 (mItems[i].isImage() && mItems[i].isHidden) ? 1 : 0);
4499 }
4500
4501 endBox();
4502 }
4503
writeIdatBox()4504 void MPEG4Writer::writeIdatBox() {
4505 beginBox("idat");
4506
4507 for (size_t i = 0; i < mItems.size(); i++) {
4508 if (mItems[i].isGrid()) {
4509 writeInt8(0); // version
4510 // flags == 1 means 32-bit width,height
4511 int8_t flags = (mItems[i].width > 65535 || mItems[i].height > 65535);
4512 writeInt8(flags);
4513 writeInt8(mItems[i].rows - 1);
4514 writeInt8(mItems[i].cols - 1);
4515 if (flags) {
4516 writeInt32(mItems[i].width);
4517 writeInt32(mItems[i].height);
4518 } else {
4519 writeInt16((uint16_t)mItems[i].width);
4520 writeInt16((uint16_t)mItems[i].height);
4521 }
4522 }
4523 }
4524
4525 endBox();
4526 }
4527
writeIrefBox()4528 void MPEG4Writer::writeIrefBox() {
4529 beginBox("iref");
4530 writeInt32(0); // Version = 0, Flags = 0
4531 {
4532 for (size_t i = 0; i < mItems.size(); i++) {
4533 for (size_t r = 0; r < mItems[i].refsList.size(); r++) {
4534 const ItemRefs &refs = mItems[i].refsList[r];
4535 beginBox(refs.key);
4536 writeInt16(mItems[i].itemId);
4537 size_t refCount = refs.value.size();
4538 if (refCount > 65535) {
4539 ALOGW("too many entries in %s", refs.key);
4540 refCount = 65535;
4541 }
4542 writeInt16((uint16_t)refCount);
4543 for (size_t refIndex = 0; refIndex < refCount; refIndex++) {
4544 writeInt16(refs.value[refIndex]);
4545 }
4546 endBox();
4547 }
4548 }
4549 }
4550 endBox();
4551 }
4552
writePitmBox()4553 void MPEG4Writer::writePitmBox() {
4554 beginBox("pitm");
4555 writeInt32(0); // Version = 0, Flags = 0
4556 writeInt16(mPrimaryItemId);
4557 endBox();
4558 }
4559
writeIpcoBox()4560 void MPEG4Writer::writeIpcoBox() {
4561 beginBox("ipco");
4562 size_t numProperties = mProperties.size();
4563 if (numProperties > 32767) {
4564 ALOGW("Dropping excess properties: numProperties %zu", numProperties);
4565 numProperties = 32767;
4566 }
4567 for (size_t propIndex = 0; propIndex < numProperties; propIndex++) {
4568 switch (mProperties[propIndex].type) {
4569 case FOURCC('h', 'v', 'c', 'C'):
4570 {
4571 beginBox("hvcC");
4572 sp<ABuffer> hvcc = mProperties[propIndex].hvcc;
4573 // Patch avcc's lengthSize field to match the number
4574 // of bytes we use to indicate the size of a nal unit.
4575 uint8_t *ptr = (uint8_t *)hvcc->data();
4576 ptr[21] = (ptr[21] & 0xfc) | (useNalLengthFour() ? 3 : 1);
4577 write(hvcc->data(), hvcc->size());
4578 endBox();
4579 break;
4580 }
4581 case FOURCC('i', 's', 'p', 'e'):
4582 {
4583 beginBox("ispe");
4584 writeInt32(0); // Version = 0, Flags = 0
4585 writeInt32(mProperties[propIndex].width);
4586 writeInt32(mProperties[propIndex].height);
4587 endBox();
4588 break;
4589 }
4590 case FOURCC('i', 'r', 'o', 't'):
4591 {
4592 beginBox("irot");
4593 writeInt8(mProperties[propIndex].rotation);
4594 endBox();
4595 break;
4596 }
4597 default:
4598 ALOGW("Skipping unrecognized property: type 0x%08x",
4599 mProperties[propIndex].type);
4600 }
4601 }
4602 endBox();
4603 }
4604
writeIpmaBox()4605 void MPEG4Writer::writeIpmaBox() {
4606 beginBox("ipma");
4607 uint32_t flags = (mProperties.size() > 127) ? 1 : 0;
4608 writeInt32(flags); // Version = 0
4609
4610 writeInt32(mAssociationEntryCount);
4611 for (size_t itemIndex = 0; itemIndex < mItems.size(); itemIndex++) {
4612 const Vector<uint16_t> &properties = mItems[itemIndex].properties;
4613 if (properties.empty()) {
4614 continue;
4615 }
4616 writeInt16(mItems[itemIndex].itemId);
4617
4618 size_t entryCount = properties.size();
4619 if (entryCount > 255) {
4620 ALOGW("Dropping excess associations: entryCount %zu", entryCount);
4621 entryCount = 255;
4622 }
4623 writeInt8((uint8_t)entryCount);
4624 for (size_t propIndex = 0; propIndex < entryCount; propIndex++) {
4625 if (flags & 1) {
4626 writeInt16((1 << 15) | properties[propIndex]);
4627 } else {
4628 writeInt8((1 << 7) | properties[propIndex]);
4629 }
4630 }
4631 }
4632 endBox();
4633 }
4634
writeIprpBox()4635 void MPEG4Writer::writeIprpBox() {
4636 beginBox("iprp");
4637 writeIpcoBox();
4638 writeIpmaBox();
4639 endBox();
4640 }
4641
writeFileLevelMetaBox()4642 void MPEG4Writer::writeFileLevelMetaBox() {
4643 // patch up the mPrimaryItemId and count items with prop associations
4644 uint16_t firstVisibleItemId = 0;
4645 uint16_t firstImageItemId = 0;
4646 for (size_t index = 0; index < mItems.size(); index++) {
4647 if (!mItems[index].isImage()) continue;
4648
4649 if (mItems[index].isPrimary) {
4650 mPrimaryItemId = mItems[index].itemId;
4651 }
4652 if (!firstImageItemId) {
4653 firstImageItemId = mItems[index].itemId;
4654 }
4655 if (!firstVisibleItemId && !mItems[index].isHidden) {
4656 firstVisibleItemId = mItems[index].itemId;
4657 }
4658 if (!mItems[index].properties.empty()) {
4659 mAssociationEntryCount++;
4660 }
4661 }
4662
4663 if (!firstImageItemId) {
4664 ALOGE("no valid image was found");
4665 return;
4666 }
4667
4668 if (mPrimaryItemId == 0) {
4669 if (firstVisibleItemId > 0) {
4670 ALOGW("didn't find primary, using first visible image");
4671 mPrimaryItemId = firstVisibleItemId;
4672 } else {
4673 ALOGW("no primary and no visible item, using first image");
4674 mPrimaryItemId = firstImageItemId;
4675 }
4676 }
4677
4678 for (List<Track *>::iterator it = mTracks.begin();
4679 it != mTracks.end(); ++it) {
4680 if ((*it)->isHeic()) {
4681 (*it)->flushItemRefs();
4682 }
4683 }
4684
4685 beginBox("meta");
4686 writeInt32(0); // Version = 0, Flags = 0
4687 writeHdlr("pict");
4688 writeIlocBox();
4689 writeIinfBox();
4690 writePitmBox();
4691 writeIprpBox();
4692 if (mNumGrids > 0) {
4693 writeIdatBox();
4694 }
4695 if (mHasRefs) {
4696 writeIrefBox();
4697 }
4698 endBox();
4699 }
4700
addProperty_l(const ItemProperty & prop)4701 uint16_t MPEG4Writer::addProperty_l(const ItemProperty &prop) {
4702 char typeStr[5];
4703 MakeFourCCString(prop.type, typeStr);
4704 ALOGV("addProperty_l: %s", typeStr);
4705
4706 mProperties.push_back(prop);
4707
4708 // returning 1-based property index
4709 return mProperties.size();
4710 }
4711
addItem_l(const ItemInfo & info)4712 uint16_t MPEG4Writer::addItem_l(const ItemInfo &info) {
4713 ALOGV("addItem_l: type %s, offset %u, size %u",
4714 info.itemType, info.offset, info.size);
4715
4716 size_t index = mItems.size();
4717 mItems.push_back(info);
4718
4719 // make the item id start at kItemIdBase
4720 mItems.editItemAt(index).itemId = index + kItemIdBase;
4721
4722 #if (LOG_NDEBUG==0)
4723 if (!info.properties.empty()) {
4724 AString str;
4725 for (size_t i = 0; i < info.properties.size(); i++) {
4726 if (i > 0) {
4727 str.append(", ");
4728 }
4729 str.append(info.properties[i]);
4730 }
4731 ALOGV("addItem_l: id %d, properties: %s", mItems[index].itemId, str.c_str());
4732 }
4733 #endif // (LOG_NDEBUG==0)
4734
4735 return mItems[index].itemId;
4736 }
4737
addRefs_l(uint16_t itemId,const ItemRefs & refs)4738 void MPEG4Writer::addRefs_l(uint16_t itemId, const ItemRefs &refs) {
4739 if (refs.value.empty()) {
4740 return;
4741 }
4742 if (itemId < kItemIdBase) {
4743 ALOGW("itemId shouldn't be smaller than kItemIdBase");
4744 return;
4745 }
4746
4747 size_t index = itemId - kItemIdBase;
4748 mItems.editItemAt(index).refsList.push_back(refs);
4749 mHasRefs = true;
4750 }
4751
4752 /*
4753 * Geodata is stored according to ISO-6709 standard.
4754 */
writeGeoDataBox()4755 void MPEG4Writer::writeGeoDataBox() {
4756 beginBox("\xA9xyz");
4757 /*
4758 * For historical reasons, any user data start
4759 * with "\0xA9", must be followed by its assoicated
4760 * language code.
4761 * 0x0012: text string length
4762 * 0x15c7: lang (locale) code: en
4763 */
4764 writeInt32(0x001215c7);
4765 writeLatitude(mLatitudex10000);
4766 writeLongitude(mLongitudex10000);
4767 writeInt8(0x2F);
4768 endBox();
4769 }
4770
4771 } // namespace android
4772