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