heif: fix rotation info writing

bug: 63633199

Change-Id: I7095c87e109f979a6df9b137e6c961b3e2314237
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index cfbbcb2..c0f260f 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -1962,6 +1962,17 @@
         return;
     }
 
+    // Rotation angle in HEIF is CCW, framework angle is CW.
+    int32_t heifRotation = 0;
+    switch(mRotation) {
+        case 90: heifRotation = 3; break;
+        case 180: heifRotation = 2; break;
+        case 270: heifRotation = 1; break;
+        default: break; // don't set if invalid
+    }
+
+    bool hasGrid = (mNumTiles > 1);
+
     if (mProperties.empty()) {
         mProperties.push_back(mOwner->addProperty_l({
             .type = FOURCC('h', 'v', 'c', 'C'),
@@ -1970,22 +1981,29 @@
 
         mProperties.push_back(mOwner->addProperty_l({
             .type = FOURCC('i', 's', 'p', 'e'),
-            .width = (mNumTiles > 1) ? mGridWidth : mWidth,
-            .height = (mNumTiles > 1) ? mGridHeight : mHeight,
+            .width = hasGrid ? mGridWidth : mWidth,
+            .height = hasGrid ? mGridHeight : mHeight,
         }));
+
+        if (!hasGrid && heifRotation > 0) {
+            mProperties.push_back(mOwner->addProperty_l({
+                .type = FOURCC('i', 'r', 'o', 't'),
+                .rotation = heifRotation,
+            }));
+        }
     }
 
     uint16_t itemId = mOwner->addItem_l({
         .itemType = "hvc1",
-        .isPrimary = (mNumTiles > 1) ? false : (mIsPrimary != 0),
-        .isHidden = (mNumTiles > 1),
+        .isPrimary = hasGrid ? false : (mIsPrimary != 0),
+        .isHidden = hasGrid,
         .offset = (uint32_t)offset,
         .size = (uint32_t)size,
         .properties = mProperties,
     });
 
     mTileIndex++;
-    if (mNumTiles > 1) {
+    if (hasGrid) {
         mDimgRefs.push_back(itemId);
 
         if (mTileIndex == mNumTiles) {
@@ -1995,6 +2013,12 @@
                 .width = mWidth,
                 .height = mHeight,
             }));
+            if (heifRotation > 0) {
+                mProperties.push_back(mOwner->addProperty_l({
+                    .type = FOURCC('i', 'r', 'o', 't'),
+                    .rotation = heifRotation,
+                }));
+            }
             mOwner->addItem_l({
                 .itemType = "grid",
                 .isPrimary = (mIsPrimary != 0),
@@ -2305,7 +2329,8 @@
     mStartTimeRealUs = startTimeUs;
 
     int32_t rotationDegrees;
-    if (mIsVideo && params && params->findInt32(kKeyRotation, &rotationDegrees)) {
+    if ((mIsVideo || mIsHeic) && params &&
+            params->findInt32(kKeyRotation, &rotationDegrees)) {
         mRotation = rotationDegrees;
     }
 
@@ -3430,16 +3455,36 @@
 
 int32_t MPEG4Writer::Track::getMetaSizeIncrease() const {
     CHECK(mIsHeic);
-    return    20                           // 1. 'ispe' property
-            + (8 + mCodecSpecificDataSize) // 2. 'hvcC' property
-            + (20                          // 3. extra 'ispe'
-            + (8 + 2 + 2 + mNumTiles * 2)  // 4. 'dimg' ref
-            + 12)                          // 5. ImageGrid in 'idat' (worst case)
-            * (mNumTiles > 1)              // -  (3~5: applicable only if grid)
-            + (16                          // 6. increase to 'iloc'
-            + 21                           // 7. increase to 'iinf'
-            + (3 + 2 * 2))                 // 8. increase to 'ipma' (worst case)
-            * (mNumTiles + 1);             // -  (6~8: are per-item)
+
+    int32_t grid = (mNumTiles > 1);
+
+    // 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.
+
+    // increase to ipco
+    int32_t increase = 20 * (grid + 1)              // 'ispe' property
+                     + (8 + mCodecSpecificDataSize) // 'hvcC' property
+                     + 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 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);
+
+    // adjust to ipma:
+    // if rotation is present and only one tile, it could ref 3 properties
+    if (!grid) {
+        increase += 2;
+    }
+
+    return increase;
 }
 
 status_t MPEG4Writer::Track::checkCodecSpecificData() const {
@@ -4267,24 +4312,38 @@
         numProperties = 32767;
     }
     for (size_t propIndex = 0; propIndex < numProperties; propIndex++) {
-        if (mProperties[propIndex].type == FOURCC('h', 'v', 'c', 'C')) {
-            beginBox("hvcC");
-            sp<ABuffer> hvcc = mProperties[propIndex].hvcc;
-            // Patch avcc's lengthSize field to match the number
-            // of bytes we use to indicate the size of a nal unit.
-            uint8_t *ptr = (uint8_t *)hvcc->data();
-            ptr[21] = (ptr[21] & 0xfc) | (useNalLengthFour() ? 3 : 1);
-            write(hvcc->data(), hvcc->size());
-            endBox();
-        } else if (mProperties[propIndex].type == FOURCC('i', 's', 'p', 'e')) {
-            beginBox("ispe");
-            writeInt32(0); // Version = 0, Flags = 0
-            writeInt32(mProperties[propIndex].width);
-            writeInt32(mProperties[propIndex].height);
-            endBox();
-        } else {
-            ALOGW("Skipping unrecognized property: type 0x%08x",
-                    mProperties[propIndex].type);
+        switch (mProperties[propIndex].type) {
+            case FOURCC('h', 'v', 'c', 'C'):
+            {
+                beginBox("hvcC");
+                sp<ABuffer> hvcc = mProperties[propIndex].hvcc;
+                // Patch avcc's lengthSize field to match the number
+                // of bytes we use to indicate the size of a nal unit.
+                uint8_t *ptr = (uint8_t *)hvcc->data();
+                ptr[21] = (ptr[21] & 0xfc) | (useNalLengthFour() ? 3 : 1);
+                write(hvcc->data(), hvcc->size());
+                endBox();
+                break;
+            }
+            case FOURCC('i', 's', 'p', 'e'):
+            {
+                beginBox("ispe");
+                writeInt32(0); // Version = 0, Flags = 0
+                writeInt32(mProperties[propIndex].width);
+                writeInt32(mProperties[propIndex].height);
+                endBox();
+                break;
+            }
+            case FOURCC('i', 'r', 'o', 't'):
+            {
+                beginBox("irot");
+                writeInt8(mProperties[propIndex].rotation);
+                endBox();
+                break;
+            }
+            default:
+                ALOGW("Skipping unrecognized property: type 0x%08x",
+                        mProperties[propIndex].type);
         }
     }
     endBox();
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 2a062cc..7b41362 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -195,6 +195,7 @@
         uint32_t type;
         int32_t width;
         int32_t height;
+        int32_t rotation;
         sp<ABuffer> hvcc;
     } ItemProperty;