Add haptic metadata for Ogg file.

Add a key "haptic" in Ogg file metadata, which indicating the haptic
channel count of the Ogg file. When it is present as a valid number,
OggExtractor will set haptic channel mask accordingly to support haptic
playback via audio. This is part of haptic playback support.

Bug: 111454766
Test: Manually test
Change-Id: I9f9ba22358b7c538382f23f24143057970f6d7ad
diff --git a/media/extractors/ogg/Android.bp b/media/extractors/ogg/Android.bp
index c6deb18..2600947 100644
--- a/media/extractors/ogg/Android.bp
+++ b/media/extractors/ogg/Android.bp
@@ -7,6 +7,10 @@
         "external/tremolo",
     ],
 
+    header_libs: [
+        "libaudio_system_headers",
+    ],
+
     shared_libs: [
         "liblog",
         "libmediaextractor",
diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp
index a52ccb1..cc2c792 100644
--- a/media/extractors/ogg/OggExtractor.cpp
+++ b/media/extractors/ogg/OggExtractor.cpp
@@ -34,6 +34,7 @@
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaDataBase.h>
 #include <media/stagefright/MetaDataUtils.h>
+#include <system/audio.h>
 #include <utils/String8.h>
 
 extern "C" {
@@ -133,6 +134,8 @@
 
     Vector<TOCEntry> mTableOfContents;
 
+    int32_t mHapticChannelCount;
+
     ssize_t readPage(off64_t offset, Page *page);
     status_t findNextPage(off64_t startOffset, off64_t *pageOffset);
 
@@ -163,6 +166,8 @@
 
     void buildTableOfContents();
 
+    void setChannelMask(int channelCount);
+
     MyOggExtractor(const MyOggExtractor &);
     MyOggExtractor &operator=(const MyOggExtractor &);
 };
@@ -310,7 +315,8 @@
       mMimeType(mimeType),
       mNumHeaders(numHeaders),
       mSeekPreRollUs(seekPreRollUs),
-      mFirstDataOffset(-1) {
+      mFirstDataOffset(-1),
+      mHapticChannelCount(0) {
     mCurrentPage.mNumSegments = 0;
 
     vorbis_info_init(&mVi);
@@ -1083,6 +1089,7 @@
     }
 
     parseFileMetaData();
+    setChannelMask(mChannelCount);
     return AMEDIA_OK;
 }
 
@@ -1157,6 +1164,7 @@
             }
 
             parseFileMetaData();
+            setChannelMask(mVi.channels);
             break;
         }
 
@@ -1192,6 +1200,29 @@
         parseVorbisComment(mFileMeta, comment, commentLength);
         //ALOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]);
     }
+
+    AMediaFormat_getInt32(mFileMeta, "haptic", &mHapticChannelCount);
+}
+
+void MyOggExtractor::setChannelMask(int channelCount) {
+    // Set channel mask according to channel count. When haptic channel count is found in
+    // file meta, set haptic channel mask to try haptic playback.
+    if (mHapticChannelCount > 0) {
+        const audio_channel_mask_t hapticChannelMask =
+                haptic_channel_mask_from_count(mHapticChannelCount);
+        const int32_t audioChannelCount = channelCount - mHapticChannelCount;
+        if (hapticChannelMask == AUDIO_CHANNEL_INVALID
+                || audioChannelCount <= 0 || audioChannelCount > FCC_8) {
+            ALOGE("Invalid haptic channel count found in metadata: %d", mHapticChannelCount);
+        } else {
+            const audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(
+                    audioChannelCount) | hapticChannelMask;
+            AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_CHANNEL_MASK, channelMask);
+        }
+    } else {
+        AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_CHANNEL_MASK,
+                audio_channel_out_mask_from_count(channelCount));
+    }
 }
 
 
diff --git a/media/libstagefright/MetaDataUtils.cpp b/media/libstagefright/MetaDataUtils.cpp
index a3259fd..dbc287e 100644
--- a/media/libstagefright/MetaDataUtils.cpp
+++ b/media/libstagefright/MetaDataUtils.cpp
@@ -308,6 +308,8 @@
 
 void parseVorbisComment(
         AMediaFormat *fileMeta, const char *comment, size_t commentLength) {
+    // Haptic tag is only kept here as it will only be used in extractor to generate channel mask.
+    const char* const haptic = "haptic";
     struct {
         const char *const mTag;
         const char *mKey;
@@ -328,6 +330,7 @@
         { "LYRICIST", AMEDIAFORMAT_KEY_LYRICIST },
         { "METADATA_BLOCK_PICTURE", AMEDIAFORMAT_KEY_ALBUMART },
         { "ANDROID_LOOP", AMEDIAFORMAT_KEY_LOOP },
+        { "ANDROID_HAPTIC", haptic },
     };
 
         for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) {
@@ -343,6 +346,15 @@
                     if (!strcasecmp(&comment[tagLen + 1], "true")) {
                         AMediaFormat_setInt32(fileMeta, AMEDIAFORMAT_KEY_LOOP, 1);
                     }
+                } else if (kMap[j].mKey == haptic) {
+                    char *end;
+                    errno = 0;
+                    const int hapticChannelCount = strtol(&comment[tagLen + 1], &end, 10);
+                    if (errno == 0) {
+                        AMediaFormat_setInt32(fileMeta, haptic, hapticChannelCount);
+                    } else {
+                        ALOGE("Error(%d) when parsing haptic channel count", errno);
+                    }
                 } else {
                     AMediaFormat_setString(fileMeta, kMap[j].mKey, &comment[tagLen + 1]);
                 }