Write Exif data block for HEIF files

Pass in Exif data block as a muxer data sample on each
image track. Add support in MPEG4Writer to write it out.

Allow grid to be used even when there is only 1 tile,
as some encoders can't encode very small images, using
grid allows them to encode in a supported size then apply
a crop to final size.

Also be more strict on esitmating the file level meta
size, since we now have to also reserve for Exif items.

bug: 79476308

Change-Id: I4b7d3af18cfb55a52f7218d9e7b6ad6f85f34343
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index b6787af..ca9deab 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -506,7 +506,7 @@
 
         ImageItem &derivedImage = itemIdToItemMap.editValueAt(itemIndex);
         if (!derivedImage.dimgRefs.empty()) {
-            ALOGW("dimgRefs if not clean!");
+            ALOGW("dimgRefs not clean!");
         }
         derivedImage.dimgRefs.appendVector(mRefs);
 
@@ -1490,6 +1490,17 @@
 
     const ImageItem *image = &mItemIdToItemMap[itemIndex];
 
+    ssize_t tileItemIndex = -1;
+    if (image->isGrid()) {
+        if (image->dimgRefs.empty()) {
+            return NULL;
+        }
+        tileItemIndex = mItemIdToItemMap.indexOfKey(image->dimgRefs[0]);
+        if (tileItemIndex < 0) {
+            return NULL;
+        }
+    }
+
     sp<MetaData> meta = new MetaData;
     meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
 
@@ -1530,10 +1541,6 @@
     }
 
     if (image->isGrid()) {
-        ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(image->dimgRefs[0]);
-        if (tileItemIndex < 0) {
-            return NULL;
-        }
         meta->setInt32(kKeyGridRows, image->rows);
         meta->setInt32(kKeyGridCols, image->columns);
 
diff --git a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
index f1b7806..6ad2441 100644
--- a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
+++ b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
@@ -70,6 +70,7 @@
     kKeyWantsNALFragments = 'NALf',
     kKeyIsSyncFrame       = 'sync',  // int32_t (bool)
     kKeyIsCodecConfig     = 'conf',  // int32_t (bool)
+    kKeyIsMuxerData       = 'muxd',  // int32_t (bool)
     kKeyTime              = 'time',  // int64_t (usecs)
     kKeyDecodingTime      = 'decT',  // int64_t (decoding timestamp in usecs)
     kKeyNTPTime           = 'ntpT',  // uint64_t (ntp-timestamp)
@@ -220,6 +221,7 @@
     kKeyFrameCount       = 'nfrm', // int32_t, total number of frame in video track
     kKeyExifOffset       = 'exof', // int64_t, Exif data offset
     kKeyExifSize         = 'exsz', // int64_t, Exif data size
+    kKeyIsExif           = 'exif', // bool (int32_t) buffer contains exif data block
 };
 
 enum {
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 550a99c..6ff3d78 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -83,6 +83,9 @@
 static const char kMetaKey_TemporalLayerCount[] = "com.android.video.temporal_layers_count";
 
 static const int kTimestampDebugCount = 10;
+static const int kItemIdBase = 10000;
+static const char kExifHeader[] = {'E', 'x', 'i', 'f', '\0', '\0'};
+static const int32_t kTiffHeaderOffset = htonl(sizeof(kExifHeader));
 
 static const uint8_t kMandatoryHevcNalUnitTypes[3] = {
     kHevcNalUnitTypeVps,
@@ -112,7 +115,7 @@
 
     int64_t getDurationUs() const;
     int64_t getEstimatedTrackSizeBytes() const;
-    int32_t getMetaSizeIncrease() const;
+    int32_t getMetaSizeIncrease(int32_t angle, int32_t trackCount) const;
     void writeTrackHeader(bool use32BitOffset = true);
     int64_t getMinCttsOffsetTimeUs();
     void bufferChunk(int64_t timestampUs);
@@ -122,8 +125,10 @@
     bool isAudio() const { return mIsAudio; }
     bool isMPEG4() const { return mIsMPEG4; }
     bool usePrefix() const { return mIsAvc || mIsHevc || mIsHeic; }
+    bool isExifData(const MediaBufferBase *buffer) const;
     void addChunkOffset(off64_t offset);
-    void addItemOffsetAndSize(off64_t offset, size_t size);
+    void addItemOffsetAndSize(off64_t offset, size_t size, bool isExif);
+    void flushItemRefs();
     int32_t getTrackId() const { return mTrackId; }
     status_t dump(int fd, const Vector<String16>& args) const;
     static const char *getFourCCForMime(const char *mime);
@@ -355,7 +360,9 @@
     int32_t mRotation;
 
     Vector<uint16_t> mProperties;
-    Vector<uint16_t> mDimgRefs;
+    ItemRefs mDimgRefs;
+    ItemRefs mCdscRefs;
+    uint16_t mImageItemId;
     int32_t mIsPrimary;
     int32_t mWidth, mHeight;
     int32_t mTileWidth, mTileHeight;
@@ -499,6 +506,7 @@
     mPrimaryItemId = 0;
     mAssociationEntryCount = 0;
     mNumGrids = 0;
+    mHasRefs = false;
 
     // Following variables only need to be set for the first recording session.
     // And they will stay the same for all the recording sessions.
@@ -680,7 +688,12 @@
 #endif
 }
 
-int64_t MPEG4Writer::estimateFileLevelMetaSize() {
+int64_t MPEG4Writer::estimateFileLevelMetaSize(MetaData *params) {
+    int32_t rotation;
+    if (!params || !params->findInt32(kKeyRotation, &rotation)) {
+        rotation = 0;
+    }
+
     // base meta size
     int64_t metaSize =     12  // meta fullbox header
                          + 33  // hdlr box
@@ -695,7 +708,7 @@
     for (List<Track *>::iterator it = mTracks.begin();
          it != mTracks.end(); ++it) {
         if ((*it)->isHeic()) {
-            metaSize += (*it)->getMetaSizeIncrease();
+            metaSize += (*it)->getMetaSizeIncrease(rotation, mTracks.size());
         }
     }
 
@@ -900,7 +913,7 @@
     if (mInMemoryCacheSize == 0) {
         int32_t bitRate = -1;
         if (mHasFileLevelMeta) {
-            mInMemoryCacheSize += estimateFileLevelMetaSize();
+            mInMemoryCacheSize += estimateFileLevelMetaSize(param);
         }
         if (mHasMoovBox) {
             if (param) {
@@ -1344,12 +1357,17 @@
 }
 
 off64_t MPEG4Writer::addSample_l(
-        MediaBuffer *buffer, bool usePrefix, size_t *bytesWritten) {
+        MediaBuffer *buffer, bool usePrefix, bool isExif, size_t *bytesWritten) {
     off64_t old_offset = mOffset;
 
     if (usePrefix) {
         addMultipleLengthPrefixedSamples_l(buffer);
     } else {
+        if (isExif) {
+            ::write(mFd, &kTiffHeaderOffset, 4); // exif_tiff_header_offset field
+            mOffset += 4;
+        }
+
         ::write(mFd,
               (const uint8_t *)buffer->data() + buffer->range_offset(),
               buffer->range_length());
@@ -1767,6 +1785,9 @@
       mReachedEOS(false),
       mStartTimestampUs(-1),
       mRotation(0),
+      mDimgRefs("dimg"),
+      mCdscRefs("cdsc"),
+      mImageItemId(0),
       mIsPrimary(0),
       mWidth(0),
       mHeight(0),
@@ -1933,6 +1954,13 @@
     return OK;
 }
 
+bool MPEG4Writer::Track::isExifData(const MediaBufferBase *buffer) const {
+    return mIsHeic
+            && (buffer->range_length() > sizeof(kExifHeader))
+            && !memcmp((uint8_t *)buffer->data() + buffer->range_offset(),
+                    kExifHeader, sizeof(kExifHeader));
+}
+
 void MPEG4Writer::Track::addChunkOffset(off64_t offset) {
     CHECK(!mIsHeic);
     if (mOwner->use32BitFileOffset()) {
@@ -1943,7 +1971,7 @@
     }
 }
 
-void MPEG4Writer::Track::addItemOffsetAndSize(off64_t offset, size_t size) {
+void MPEG4Writer::Track::addItemOffsetAndSize(off64_t offset, size_t size, bool isExif) {
     CHECK(mIsHeic);
 
     if (offset > UINT32_MAX || size > UINT32_MAX) {
@@ -1954,6 +1982,18 @@
     if (mIsMalformed) {
         return;
     }
+
+    if (isExif) {
+         mCdscRefs.value.push_back(mOwner->addItem_l({
+            .itemType = "Exif",
+            .isPrimary = false,
+            .isHidden = false,
+            .offset = (uint32_t)offset,
+            .size = (uint32_t)size,
+        }));
+        return;
+    }
+
     if (mTileIndex >= mNumTiles) {
         ALOGW("Ignoring excess tiles!");
         return;
@@ -1968,7 +2008,7 @@
         default: break; // don't set if invalid
     }
 
-    bool hasGrid = (mNumTiles > 1);
+    bool hasGrid = (mTileWidth > 0);
 
     if (mProperties.empty()) {
         mProperties.push_back(mOwner->addProperty_l({
@@ -1990,18 +2030,16 @@
         }
     }
 
-    uint16_t itemId = mOwner->addItem_l({
-        .itemType = "hvc1",
-        .isPrimary = hasGrid ? false : (mIsPrimary != 0),
-        .isHidden = hasGrid,
-        .offset = (uint32_t)offset,
-        .size = (uint32_t)size,
-        .properties = mProperties,
-    });
-
     mTileIndex++;
     if (hasGrid) {
-        mDimgRefs.push_back(itemId);
+        mDimgRefs.value.push_back(mOwner->addItem_l({
+            .itemType = "hvc1",
+            .isPrimary = false,
+            .isHidden = true,
+            .offset = (uint32_t)offset,
+            .size = (uint32_t)size,
+            .properties = mProperties,
+        }));
 
         if (mTileIndex == mNumTiles) {
             mProperties.clear();
@@ -2016,7 +2054,7 @@
                     .rotation = heifRotation,
                 }));
             }
-            mOwner->addItem_l({
+            mImageItemId = mOwner->addItem_l({
                 .itemType = "grid",
                 .isPrimary = (mIsPrimary != 0),
                 .isHidden = false,
@@ -2025,9 +2063,31 @@
                 .width = (uint32_t)mWidth,
                 .height = (uint32_t)mHeight,
                 .properties = mProperties,
-                .dimgRefs = mDimgRefs,
             });
         }
+    } else {
+        mImageItemId = mOwner->addItem_l({
+            .itemType = "hvc1",
+            .isPrimary = (mIsPrimary != 0),
+            .isHidden = false,
+            .offset = (uint32_t)offset,
+            .size = (uint32_t)size,
+            .properties = mProperties,
+        });
+    }
+}
+
+// Flush out the item refs for this track. Note that it must be called after the
+// writer thread has stopped, because there might be pending items in the last
+// few chunks written by the writer thread (as opposed to the track). In particular,
+// it affects the 'dimg' refs for tiled image, as we only have the refs after the
+// last tile sample is written.
+void MPEG4Writer::Track::flushItemRefs() {
+    CHECK(mIsHeic);
+
+    if (mImageItemId > 0) {
+        mOwner->addRefs_l(mImageItemId, mDimgRefs);
+        mOwner->addRefs_l(mImageItemId, mCdscRefs);
     }
 }
 
@@ -2174,15 +2234,20 @@
         chunk->mTimeStampUs, chunk->mTrack->getTrackType());
 
     int32_t isFirstSample = true;
-    bool usePrefix = chunk->mTrack->usePrefix();
     while (!chunk->mSamples.empty()) {
         List<MediaBuffer *>::iterator it = chunk->mSamples.begin();
 
+        int32_t isExif;
+        if (!(*it)->meta_data().findInt32(kKeyIsExif, &isExif)) {
+            isExif = 0;
+        }
+        bool usePrefix = chunk->mTrack->usePrefix() && !isExif;
+
         size_t bytesWritten;
-        off64_t offset = addSample_l(*it, usePrefix, &bytesWritten);
+        off64_t offset = addSample_l(*it, usePrefix, isExif, &bytesWritten);
 
         if (chunk->mTrack->isHeic()) {
-            chunk->mTrack->addItemOffsetAndSize(offset, bytesWritten);
+            chunk->mTrack->addItemOffsetAndSize(offset, bytesWritten, isExif);
         } else if (isFirstSample) {
             chunk->mTrack->addChunkOffset(offset);
             isFirstSample = false;
@@ -2904,6 +2969,19 @@
             break;
         }
 
+        bool isExif = false;
+        int32_t isMuxerData;
+        if (buffer->meta_data().findInt32(kKeyIsMuxerData, &isMuxerData) && isMuxerData) {
+            // We only support one type of muxer data, which is Exif data block.
+            isExif = isExifData(buffer);
+            if (!isExif) {
+                ALOGW("Ignoring bad Exif data block");
+                buffer->release();
+                buffer = NULL;
+                continue;
+            }
+        }
+
         ++nActualFrames;
 
         // Make a deep copy of the MediaBuffer and Metadata and release
@@ -2916,10 +2994,15 @@
         buffer->release();
         buffer = NULL;
 
-        if (usePrefix()) StripStartcode(copy);
+        if (isExif) {
+            copy->meta_data().setInt32(kKeyIsExif, 1);
+        }
+        bool usePrefix = this->usePrefix() && !isExif;
+
+        if (usePrefix) StripStartcode(copy);
 
         size_t sampleSize = copy->range_length();
-        if (usePrefix()) {
+        if (usePrefix) {
             if (mOwner->useNalLengthFour()) {
                 sampleSize += 4;
             } else {
@@ -3185,10 +3268,10 @@
         }
         if (!hasMultipleTracks) {
             size_t bytesWritten;
-            off64_t offset = mOwner->addSample_l(copy, usePrefix(), &bytesWritten);
+            off64_t offset = mOwner->addSample_l(copy, usePrefix, isExif, &bytesWritten);
 
             if (mIsHeic) {
-                addItemOffsetAndSize(offset, bytesWritten);
+                addItemOffsetAndSize(offset, bytesWritten, isExif);
             } else {
                 uint32_t count = (mOwner->use32BitFileOffset()
                             ? mStcoTableEntries->count()
@@ -3450,10 +3533,12 @@
     return mEstimatedTrackSizeBytes;
 }
 
-int32_t MPEG4Writer::Track::getMetaSizeIncrease() const {
+int32_t MPEG4Writer::Track::getMetaSizeIncrease(
+        int32_t angle, int32_t trackCount) const {
     CHECK(mIsHeic);
 
-    int32_t grid = (mNumTiles > 1);
+    int32_t grid = (mTileWidth > 0);
+    int32_t rotate = (angle > 0);
 
     // Note that the rotation angle is in the file meta, and we don't have
     // it until start, so here the calculation has to assume rotation.
@@ -3461,25 +3546,34 @@
     // increase to ipco
     int32_t increase = 20 * (grid + 1)              // 'ispe' property
                      + (8 + mCodecSpecificDataSize) // 'hvcC' property
-                     + 9;                           // 'irot' property (worst case)
+                     ;
+
+    if (rotate) {
+        increase += 9;                              // 'irot' property (worst case)
+    }
 
     // increase to iref and idat
     if (grid) {
-        increase += (8 + 2 + 2 + mNumTiles * 2)  // 'dimg' in iref
-                  + 12;                          // ImageGrid in 'idat' (worst case)
+        increase += (12 + mNumTiles * 2)            // 'dimg' in iref
+                  + 12;                             // ImageGrid in 'idat' (worst case)
     }
 
-    // increase to iloc, iinf and ipma
-    increase += (16             // increase to 'iloc'
-              + 21              // increase to 'iinf'
-              + (3 + 2 * 2))    // increase to 'ipma' (worst case, 2 props x 2 bytes)
-              * (mNumTiles + grid);
+    increase += (12 + 2);                           // 'cdsc' in iref
 
-    // adjust to ipma:
-    // if rotation is present and only one tile, it could ref 3 properties
-    if (!grid) {
-        increase += 2;
-    }
+    // increase to iloc, iinf
+    increase += (16                                 // increase to 'iloc'
+              + 21)                                 // increase to 'iinf'
+              * (mNumTiles + grid + 1);             // "+1" is for 'Exif'
+
+    // When total # of properties is > 127, the properties id becomes 2-byte.
+    // We write 4 properties at most for each image (2x'ispe', 1x'hvcC', 1x'irot').
+    // Set the threshold to be 30.
+    int32_t propBytes = trackCount > 30 ? 2 : 1;
+
+    // increase to ipma
+    increase += (3 + 2 * propBytes) * mNumTiles     // 'ispe' + 'hvcC'
+             + grid * (3 + propBytes)               // 'ispe' for grid
+             + rotate * propBytes;                  // 'irot' (either on grid or tile)
 
     return increase;
 }
@@ -4239,7 +4333,7 @@
     writeInt16((uint16_t)itemCount);
     for (size_t i = 0; i < itemCount; i++) {
         writeInfeBox(mItems[i].itemId, mItems[i].itemType,
-                mItems[i].isHidden ? 1 : 0);
+                (mItems[i].isImage() && mItems[i].isHidden) ? 1 : 0);
     }
 
     endBox();
@@ -4274,21 +4368,21 @@
     writeInt32(0);          // Version = 0, Flags = 0
     {
         for (size_t i = 0; i < mItems.size(); i++) {
-            if (!mItems[i].isGrid()) {
-                continue;
+            for (size_t r = 0; r < mItems[i].refsList.size(); r++) {
+                const ItemRefs &refs = mItems[i].refsList[r];
+                beginBox(refs.key);
+                writeInt16(mItems[i].itemId);
+                size_t refCount = refs.value.size();
+                if (refCount > 65535) {
+                    ALOGW("too many entries in %s", refs.key);
+                    refCount = 65535;
+                }
+                writeInt16((uint16_t)refCount);
+                for (size_t refIndex = 0; refIndex < refCount; refIndex++) {
+                    writeInt16(refs.value[refIndex]);
+                }
+                endBox();
             }
-            beginBox("dimg");
-            writeInt16(mItems[i].itemId);
-            size_t refCount = mItems[i].dimgRefs.size();
-            if (refCount > 65535) {
-                ALOGW("too many entries in dimg");
-                refCount = 65535;
-            }
-            writeInt16((uint16_t)refCount);
-            for (size_t refIndex = 0; refIndex < refCount; refIndex++) {
-                writeInt16(mItems[i].dimgRefs[refIndex]);
-            }
-            endBox();
         }
     }
     endBox();
@@ -4384,32 +4478,45 @@
 }
 
 void MPEG4Writer::writeFileLevelMetaBox() {
-    if (mItems.empty()) {
-        ALOGE("no valid item was found");
-        return;
-    }
-
     // patch up the mPrimaryItemId and count items with prop associations
     uint16_t firstVisibleItemId = 0;
+    uint16_t firstImageItemId = 0;
     for (size_t index = 0; index < mItems.size(); index++) {
+        if (!mItems[index].isImage()) continue;
+
         if (mItems[index].isPrimary) {
             mPrimaryItemId = mItems[index].itemId;
-        } else if (!firstVisibleItemId && !mItems[index].isHidden) {
+        }
+        if (!firstImageItemId) {
+            firstImageItemId = mItems[index].itemId;
+        }
+        if (!firstVisibleItemId && !mItems[index].isHidden) {
             firstVisibleItemId = mItems[index].itemId;
         }
-
         if (!mItems[index].properties.empty()) {
             mAssociationEntryCount++;
         }
     }
 
+    if (!firstImageItemId) {
+        ALOGE("no valid image was found");
+        return;
+    }
+
     if (mPrimaryItemId == 0) {
         if (firstVisibleItemId > 0) {
-            ALOGW("didn't find primary, using first visible item");
+            ALOGW("didn't find primary, using first visible image");
             mPrimaryItemId = firstVisibleItemId;
         } else {
-            ALOGW("no primary and no visible item, using first item");
-            mPrimaryItemId = mItems[0].itemId;
+            ALOGW("no primary and no visible item, using first image");
+            mPrimaryItemId = firstImageItemId;
+        }
+    }
+
+    for (List<Track *>::iterator it = mTracks.begin();
+        it != mTracks.end(); ++it) {
+        if ((*it)->isHeic()) {
+            (*it)->flushItemRefs();
         }
     }
 
@@ -4422,6 +4529,8 @@
     writeIprpBox();
     if (mNumGrids > 0) {
         writeIdatBox();
+    }
+    if (mHasRefs) {
         writeIrefBox();
     }
     endBox();
@@ -4445,8 +4554,8 @@
     size_t index = mItems.size();
     mItems.push_back(info);
 
-    // make the item id start at 10000
-    mItems.editItemAt(index).itemId = index + 10000;
+    // make the item id start at kItemIdBase
+    mItems.editItemAt(index).itemId = index + kItemIdBase;
 
 #if (LOG_NDEBUG==0)
     if (!info.properties.empty()) {
@@ -4464,6 +4573,20 @@
     return mItems[index].itemId;
 }
 
+void MPEG4Writer::addRefs_l(uint16_t itemId, const ItemRefs &refs) {
+    if (refs.value.empty()) {
+        return;
+    }
+    if (itemId < kItemIdBase) {
+        ALOGW("itemId shouldn't be smaller than kItemIdBase");
+        return;
+    }
+
+    size_t index = itemId - kItemIdBase;
+    mItems.editItemAt(index).refsList.push_back(refs);
+    mHasRefs = true;
+}
+
 /*
  * Geodata is stored according to ISO-6709 standard.
  */
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index 23e543d..98f59b5 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -190,6 +190,10 @@
         sampleMetaData.setInt32(kKeyIsSyncFrame, true);
     }
 
+    if (flags & MediaCodec::BUFFER_FLAG_MUXER_DATA) {
+        sampleMetaData.setInt32(kKeyIsMuxerData, 1);
+    }
+
     sp<MediaAdapter> currentTrack = mTrackList[trackIndex];
     // This pushBuffer will wait until the mediaBuffer is consumed.
     return currentTrack->pushBuffer(mediaBuffer);
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 7b41362..f18940d 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -132,7 +132,7 @@
     status_t startTracks(MetaData *params);
     size_t numTracks();
     int64_t estimateMoovBoxSize(int32_t bitRate);
-    int64_t estimateFileLevelMetaSize();
+    int64_t estimateFileLevelMetaSize(MetaData *params);
     void writeCachedBoxToFile(const char *type);
 
     struct Chunk {
@@ -167,8 +167,10 @@
     Condition       mChunkReadyCondition;   // Signal that chunks are available
 
     // HEIF writing
+    typedef key_value_pair_t< const char *, Vector<uint16_t> > ItemRefs;
     typedef struct _ItemInfo {
         bool isGrid() const { return !strcmp("grid", itemType); }
+        bool isImage() const { return !strcmp("hvc1", itemType) || isGrid(); }
         const char *itemType;
         uint16_t itemId;
         bool isPrimary;
@@ -188,7 +190,7 @@
             };
         };
         Vector<uint16_t> properties;
-        Vector<uint16_t> dimgRefs;
+        Vector<ItemRefs> refsList;
     } ItemInfo;
 
     typedef struct _ItemProperty {
@@ -204,6 +206,7 @@
     uint32_t mPrimaryItemId;
     uint32_t mAssociationEntryCount;
     uint32_t mNumGrids;
+    bool mHasRefs;
     Vector<ItemInfo> mItems;
     Vector<ItemProperty> mProperties;
 
@@ -252,11 +255,12 @@
     void initInternal(int fd, bool isFirstSession);
 
     // Acquire lock before calling these methods
-    off64_t addSample_l(MediaBuffer *buffer, bool usePrefix, size_t *bytesWritten);
+    off64_t addSample_l(MediaBuffer *buffer, bool usePrefix, bool isExif, size_t *bytesWritten);
     void addLengthPrefixedSample_l(MediaBuffer *buffer);
     void addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer);
     uint16_t addProperty_l(const ItemProperty &);
     uint16_t addItem_l(const ItemInfo &);
+    void addRefs_l(uint16_t itemId, const ItemRefs &);
 
     bool exceedsFileSizeLimit();
     bool use32BitFileOffset() const;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 67808f1..ad02004 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -61,9 +61,11 @@
     };
 
     enum BufferFlags {
-        BUFFER_FLAG_SYNCFRAME   = 1,
-        BUFFER_FLAG_CODECCONFIG = 2,
-        BUFFER_FLAG_EOS         = 4,
+        BUFFER_FLAG_SYNCFRAME     = 1,
+        BUFFER_FLAG_CODECCONFIG   = 2,
+        BUFFER_FLAG_EOS           = 4,
+        BUFFER_FLAG_PARTIAL_FRAME = 8,
+        BUFFER_FLAG_MUXER_DATA    = 16,
     };
 
     enum {