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