Extract AudioPresentations from AC4 in MP4 and TS

Translates MPEG-4 AC4 DSI AC4Presentations to AudioPresentation.
Parses MPEG2-TS audio preselection descriptors from MPEG2-TS.
Adds AudioPresentationInfo interface to native MediaExtractor.

Test: Manually test
Bug: 119312182
Change-Id: I61286b38543e114aeaef331aa013bc2d2d7626c3
diff --git a/include/media/AudioPresentationInfo.h b/include/media/AudioPresentationInfo.h
deleted file mode 100644
index e91a992..0000000
--- a/include/media/AudioPresentationInfo.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef AUDIO_PRESENTATION_INFO_H_
-#define AUDIO_PRESENTATION_INFO_H_
-
-#include <sstream>
-#include <stdint.h>
-
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
-
-namespace android {
-
-enum mastering_indication {
-    MASTERING_NOT_INDICATED,
-    MASTERED_FOR_STEREO,
-    MASTERED_FOR_SURROUND,
-    MASTERED_FOR_3D,
-    MASTERED_FOR_HEADPHONE,
-};
-
-struct AudioPresentation : public RefBase {
-    int32_t mPresentationId;
-    int32_t mProgramId;
-    KeyedVector<String8, String8> mLabels;
-    String8 mLanguage;
-    int32_t mMasteringIndication;
-    bool mAudioDescriptionAvailable;
-    bool mSpokenSubtitlesAvailable;
-    bool mDialogueEnhancementAvailable;
-
-    AudioPresentation() {
-        mPresentationId = -1;
-        mProgramId = -1;
-        mLanguage = "";
-        mMasteringIndication = MASTERING_NOT_INDICATED;
-        mAudioDescriptionAvailable = false;
-        mSpokenSubtitlesAvailable = false;
-        mDialogueEnhancementAvailable = false;
-    }
-};
-
-typedef Vector<sp<AudioPresentation>> AudioPresentations;
-
-class AudioPresentationInfo : public RefBase {
- public:
-    AudioPresentationInfo();
-
-    ~AudioPresentationInfo();
-
-    void addPresentation(sp<AudioPresentation> presentation);
-
-    size_t countPresentations() const;
-
-    const sp<AudioPresentation> getPresentation(size_t index) const;
-
- private:
-    AudioPresentations mPresentations;
-};
-
-}  // namespace android
-
-#endif  // AUDIO_PRESENTATION_INFO_H_
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index c3bdc92..337ff2a 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -38,6 +38,7 @@
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AudioPresentationInfo.h>
 #include <media/stagefright/foundation/AUtils.h>
 #include <media/stagefright/foundation/ByteUtils.h>
 #include <media/stagefright/foundation/ColorUtils.h>
@@ -2753,6 +2754,75 @@
     AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_AC4);
     AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, channelCount);
     AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sampleRate);
+
+    AudioPresentationCollection presentations;
+    // translate the AC4 presentation information to audio presentations for this track
+    AC4DSIParser::AC4Presentations ac4Presentations = parser.getPresentations();
+    if (!ac4Presentations.empty()) {
+        for (const auto& ac4Presentation : ac4Presentations) {
+            auto& presentation = ac4Presentation.second;
+            if (!presentation.mEnabled) {
+                continue;
+            }
+            AudioPresentationV1 ap;
+            ap.mPresentationId = presentation.mGroupIndex;
+            ap.mProgramId = presentation.mProgramID;
+            ap.mLanguage = presentation.mLanguage;
+            if (presentation.mPreVirtualized) {
+                ap.mMasteringIndication = MASTERED_FOR_HEADPHONE;
+            } else {
+                switch (presentation.mChannelMode) {
+                    case AC4Parser::AC4Presentation::kChannelMode_Mono:
+                    case AC4Parser::AC4Presentation::kChannelMode_Stereo:
+                        ap.mMasteringIndication = MASTERED_FOR_STEREO;
+                        break;
+                    case AC4Parser::AC4Presentation::kChannelMode_3_0:
+                    case AC4Parser::AC4Presentation::kChannelMode_5_0:
+                    case AC4Parser::AC4Presentation::kChannelMode_5_1:
+                    case AC4Parser::AC4Presentation::kChannelMode_7_0_34:
+                    case AC4Parser::AC4Presentation::kChannelMode_7_1_34:
+                    case AC4Parser::AC4Presentation::kChannelMode_7_0_52:
+                    case AC4Parser::AC4Presentation::kChannelMode_7_1_52:
+                        ap.mMasteringIndication = MASTERED_FOR_SURROUND;
+                        break;
+                    case AC4Parser::AC4Presentation::kChannelMode_7_0_322:
+                    case AC4Parser::AC4Presentation::kChannelMode_7_1_322:
+                    case AC4Parser::AC4Presentation::kChannelMode_7_0_4:
+                    case AC4Parser::AC4Presentation::kChannelMode_7_1_4:
+                    case AC4Parser::AC4Presentation::kChannelMode_9_0_4:
+                    case AC4Parser::AC4Presentation::kChannelMode_9_1_4:
+                    case AC4Parser::AC4Presentation::kChannelMode_22_2:
+                        ap.mMasteringIndication = MASTERED_FOR_3D;
+                        break;
+                    default:
+                        ALOGE("Invalid channel mode in AC4 presentation");
+                        return ERROR_MALFORMED;
+                }
+            }
+
+            ap.mAudioDescriptionAvailable = (presentation.mContentClassifier ==
+                    AC4Parser::AC4Presentation::kVisuallyImpaired);
+            ap.mSpokenSubtitlesAvailable = (presentation.mContentClassifier ==
+                    AC4Parser::AC4Presentation::kVoiceOver);
+            ap.mDialogueEnhancementAvailable = presentation.mHasDialogEnhancements;
+            if (!ap.mLanguage.empty()) {
+                ap.mLabels.emplace(ap.mLanguage, presentation.mDescription);
+            }
+            presentations.push_back(std::move(ap));
+        }
+    }
+
+    if (presentations.empty()) {
+        // Clear audio presentation info in metadata.
+        AMediaFormat_setBuffer(
+                mLastTrack->meta, AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO, nullptr, 0);
+    } else {
+        std::ostringstream outStream(std::ios::out);
+        serializeAudioPresentations(presentations, &outStream);
+        AMediaFormat_setBuffer(
+                mLastTrack->meta, AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO,
+                outStream.str().data(), outStream.str().size());
+    }
     return OK;
 }
 
diff --git a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
index 72efcdf..9f2deda 100644
--- a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
+++ b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
@@ -225,6 +225,9 @@
 
     // Key for ALAC Magic Cookie
     kKeyAlacMagicCookie  = 'almc', // raw data
+
+    // AC-4 AudioPresentationInfo
+    kKeyAudioPresentationInfo = 'audP',  // raw data
 };
 
 enum {
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index d96d358..55b0862 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -96,7 +96,6 @@
         "AHierarchicalStateMachine.cpp",
         "AMRWriter.cpp",
         "AudioPlayer.cpp",
-        "AudioPresentationInfo.cpp",
         "AudioSource.cpp",
         "BufferImpl.cpp",
         "CallbackDataSource.cpp",
diff --git a/media/libstagefright/AudioPresentationInfo.cpp b/media/libstagefright/AudioPresentationInfo.cpp
deleted file mode 100644
index 86e1859..0000000
--- a/media/libstagefright/AudioPresentationInfo.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// #define LOG_NDEBUG 0
-#define LOG_TAG "AudioPresentationInfo"
-
-#include <media/AudioPresentationInfo.h>
-
-namespace android {
-
-AudioPresentationInfo::AudioPresentationInfo() {
-}
-
-AudioPresentationInfo::~AudioPresentationInfo() {
-    mPresentations.clear();
-}
-
-void AudioPresentationInfo::addPresentation(sp<AudioPresentation> presentation) {
-    mPresentations.push(presentation);
-}
-
-size_t AudioPresentationInfo::countPresentations() const {
-    return mPresentations.size();
-}
-
-// Returns an AudioPresentation for the given valid index
-// index must be >=0 and < countPresentations()
-const sp<AudioPresentation> AudioPresentationInfo::getPresentation(size_t index) const {
-    return mPresentations[index];
-}
-
-}  // namespace android
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index f94648c..f5178dd 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -794,4 +794,32 @@
     return false;
 }
 
+// Return OK if we have received an audio presentation info.
+// Return ERROR_UNSUPPORTED if the track has no audio presentation.
+// Return INVALID_OPERATION if audio presentation metadata version does not match.
+status_t NuMediaExtractor::getAudioPresentations(
+        size_t trackIndex, AudioPresentationCollection *presentations) const {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mImpl == NULL) {
+        return -EINVAL;
+    }
+
+    if (trackIndex >= mImpl->countTracks()) {
+        return -ERANGE;
+    }
+
+    sp<MetaData> meta = mImpl->getTrackMetaData(trackIndex);
+
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (meta != NULL && meta->findData(kKeyAudioPresentationInfo, &type, &data, &size)) {
+        std::istringstream inStream(std::string(static_cast<const char*>(data), size));
+        return deserializeAudioPresentations(&inStream, presentations);
+    }
+    ALOGE("Source does not contain any audio presentation");
+    return ERROR_UNSUPPORTED;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 4aee9d5..53c32b2 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -615,6 +615,7 @@
 static std::vector<std::pair<const char *, uint32_t>> bufferMappings {
     {
         { "albumart", kKeyAlbumArt },
+        { "audio-presentation-info", kKeyAudioPresentationInfo },
         { "pssh", kKeyPssh },
         { "crypto-iv", kKeyCryptoIV },
         { "crypto-key", kKeyCryptoKey },
diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp
index 5b7961d..861528e 100644
--- a/media/libstagefright/foundation/Android.bp
+++ b/media/libstagefright/foundation/Android.bp
@@ -61,6 +61,7 @@
         "AMessage.cpp",
         "AString.cpp",
         "AStringUtils.cpp",
+        "AudioPresentationInfo.cpp",
         "ByteUtils.cpp",
         "ColorUtils.cpp",
         "MediaDefs.cpp",
diff --git a/media/libstagefright/foundation/AudioPresentationInfo.cpp b/media/libstagefright/foundation/AudioPresentationInfo.cpp
new file mode 100644
index 0000000..4b8e969
--- /dev/null
+++ b/media/libstagefright/foundation/AudioPresentationInfo.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "AudioPresentationInfo"
+
+#include <vector>
+
+#include "AudioPresentationInfo.h"
+
+#include <utils/Log.h>
+
+namespace android {
+
+void serializeAudioPresentations(const AudioPresentationCollection& presentations,
+        std::ostream* serializedOutput) {
+    uint32_t numPresentations = presentations.size();
+    serializedOutput->write(reinterpret_cast<char*>(&numPresentations), sizeof(numPresentations));
+    for (const auto& ap : presentations) {
+        if (ap.mVersion == PRESENTATION_VERSION_1) {
+            serializedOutput->write(
+                    const_cast<char*>(reinterpret_cast<const char*>(&ap.mVersion)),
+                    sizeof(ap.mVersion));
+            serializedOutput->write(
+                    const_cast<char*>(reinterpret_cast<const char*>(&ap.mPresentationId)),
+                    sizeof(ap.mPresentationId));
+            serializedOutput->write(
+                    const_cast<char*>(reinterpret_cast<const char*>(&ap.mProgramId)),
+                    sizeof(ap.mProgramId));
+
+            uint32_t numLabels = ap.mLabels.size();
+            serializedOutput->write(
+                    const_cast<char*>(reinterpret_cast<const char*>(&numLabels)),
+                    sizeof(numLabels));
+            for (const auto& label : ap.mLabels) {
+                uint32_t labelKeySize = label.first.size();
+                serializedOutput->write(
+                        const_cast<char*>(reinterpret_cast<const char*>(&labelKeySize)),
+                        sizeof(labelKeySize));
+                serializedOutput->write(label.first.c_str(), labelKeySize);
+
+                uint32_t labelValSize = label.second.size();
+                serializedOutput->write(
+                        const_cast<char*>(reinterpret_cast<const char*>(&labelValSize)),
+                        sizeof(labelValSize));
+                serializedOutput->write(label.second.c_str(), labelValSize);
+            }
+
+            uint32_t langSize = ap.mLanguage.size();
+            serializedOutput->write(
+                    const_cast<char*>(reinterpret_cast<const char*>(&langSize)),
+                    sizeof(langSize));
+            serializedOutput->write(ap.mLanguage.c_str(), langSize);
+
+            serializedOutput->write(
+                    const_cast<char*>(reinterpret_cast<const char*>(&ap.mMasteringIndication)),
+                    sizeof(ap.mMasteringIndication));
+            serializedOutput->write(
+                    const_cast<char*>(reinterpret_cast<const char*>(&ap.mAudioDescriptionAvailable)),
+                    sizeof(ap.mAudioDescriptionAvailable));
+            serializedOutput->write(
+                    const_cast<char*>(reinterpret_cast<const char*>(&ap.mSpokenSubtitlesAvailable)),
+                    sizeof(ap.mSpokenSubtitlesAvailable));
+            serializedOutput->write(
+                    const_cast<char*>(reinterpret_cast<const char*>(&ap.mDialogueEnhancementAvailable)),
+                    sizeof(ap.mDialogueEnhancementAvailable));
+        }
+    }
+}
+
+status_t deserializeAudioPresentations(std::istream* serializedInput,
+        AudioPresentationCollection *presentations) {
+    uint32_t numPresentations;
+    serializedInput->read(reinterpret_cast<char*>(&numPresentations), sizeof(numPresentations));
+    for (uint32_t i = 0; i < numPresentations; ++i) {
+        uint32_t version;
+        serializedInput->read(reinterpret_cast<char*>(&version), sizeof(version));
+        if (version == PRESENTATION_VERSION_1) {
+            AudioPresentationV1 ap;
+            serializedInput->read(
+                    reinterpret_cast<char*>(&ap.mPresentationId),
+                    sizeof(ap.mPresentationId));
+            serializedInput->read(reinterpret_cast<char*>(&ap.mProgramId), sizeof(ap.mProgramId));
+
+            uint32_t numLabels;
+            serializedInput->read(reinterpret_cast<char*>(&numLabels), sizeof(numLabels));
+            for (uint32_t j = 0; j < numLabels; ++j) {
+                uint32_t labelKeySize;
+                serializedInput->read(reinterpret_cast<char*>(&labelKeySize), sizeof(labelKeySize));
+                std::vector<char> labelKey(labelKeySize);
+                serializedInput->read(labelKey.data(), labelKeySize);
+
+                uint32_t labelValSize;
+                serializedInput->read(reinterpret_cast<char*>(&labelValSize), sizeof(labelValSize));
+                std::vector<char> labelVal(labelValSize);
+                serializedInput->read(labelVal.data(), labelValSize);
+                ap.mLabels.emplace(
+                        std::string(reinterpret_cast<char*>(labelKey.data()), labelKeySize),
+                        std::string(reinterpret_cast<char*>(labelVal.data()), labelValSize));
+            }
+            uint32_t languageSize;
+            serializedInput->read(reinterpret_cast<char*>(&languageSize), sizeof(languageSize));
+            std::vector<char> language(languageSize);
+            serializedInput->read(language.data(), languageSize);
+            ap.mLanguage = std::string(reinterpret_cast<char*>(language.data()), languageSize);
+            serializedInput->read(reinterpret_cast<char*>(&ap.mMasteringIndication),
+                                 sizeof(ap.mMasteringIndication));
+            serializedInput->read(reinterpret_cast<char*>(&ap.mAudioDescriptionAvailable),
+                                 sizeof(ap.mAudioDescriptionAvailable));
+            serializedInput->read(reinterpret_cast<char*>(&ap.mSpokenSubtitlesAvailable),
+                                 sizeof(ap.mSpokenSubtitlesAvailable));
+            serializedInput->read(reinterpret_cast<char*>(&ap.mDialogueEnhancementAvailable),
+                                 sizeof(ap.mDialogueEnhancementAvailable));
+            presentations->push_back(std::move(ap));
+        } else {
+            ALOGE("Audio presentation info version is not supported");
+            return INVALID_OPERATION;
+        }
+    }
+    return OK;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/AudioPresentationInfo.h b/media/libstagefright/foundation/include/media/stagefright/foundation/AudioPresentationInfo.h
new file mode 100644
index 0000000..4bd4d9f
--- /dev/null
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/AudioPresentationInfo.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AUDIO_PRESENTATION_INFO_H_
+#define AUDIO_PRESENTATION_INFO_H_
+
+#include <map>
+#include <sstream>
+#include <stdint.h>
+#include <vector>
+
+#include <utils/Errors.h>
+
+namespace android {
+
+enum AudioPresentationVersion {
+    PRESENTATION_VERSION_UNDEFINED = 0,
+    PRESENTATION_VERSION_1,
+};
+
+enum MasteringIndication {
+    MASTERING_NOT_INDICATED,
+    MASTERED_FOR_STEREO,
+    MASTERED_FOR_SURROUND,
+    MASTERED_FOR_3D,
+    MASTERED_FOR_HEADPHONE,
+};
+
+struct AudioPresentation {
+    uint32_t mVersion = PRESENTATION_VERSION_UNDEFINED;
+    int32_t mPresentationId = -1;
+    int32_t mProgramId = -1;
+    std::map<std::string, std::string> mLabels;
+    std::string mLanguage;
+    MasteringIndication mMasteringIndication = MASTERING_NOT_INDICATED;
+    bool mAudioDescriptionAvailable = false;
+    bool mSpokenSubtitlesAvailable = false;
+    bool mDialogueEnhancementAvailable = false;
+};
+
+struct AudioPresentationV1 : public AudioPresentation {
+    AudioPresentationV1() {
+        mVersion = PRESENTATION_VERSION_1;
+    }
+};
+
+typedef std::vector<AudioPresentation> AudioPresentationCollection;
+
+void serializeAudioPresentations(const AudioPresentationCollection& presentations,
+                                               std::ostream* serializedOutput);
+status_t deserializeAudioPresentations(std::istream* serializedInput,
+                                                AudioPresentationCollection *presentations);
+}  // namespace android
+
+#endif  // AUDIO_PRESENTATION_INFO_H_
diff --git a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
index 641ccfa..8dc8d38 100644
--- a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
@@ -20,6 +20,7 @@
 #include <list>
 #include <media/mediaplayer.h>
 #include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AudioPresentationInfo.h>
 #include <media/IMediaExtractor.h>
 #include <media/MediaSource.h>
 #include <utils/Errors.h>
@@ -95,6 +96,9 @@
 
     bool getCachedDuration(int64_t *durationUs, bool *eos) const;
 
+    status_t getAudioPresentations(size_t trackIdx,
+            AudioPresentationCollection *presentations) const;
+
 protected:
     virtual ~NuMediaExtractor();
 
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index aa4a4db..cf93fcf 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -210,6 +210,7 @@
     sp<ABuffer> mDescrambledBuffer;
     List<SubSampleInfo> mSubSamples;
     sp<IDescrambler> mDescrambler;
+    AudioPresentationCollection mAudioPresentations;
 
     // Flush accumulated payload if necessary --- i.e. at EOS or at the start of
     // another payload. event is set if the flushed payload is PES with a sync
@@ -525,6 +526,8 @@
 
         CADescriptor streamCA;
         info.mTypeExt = EXT_DESCRIPTOR_DVB_RESERVED_MAX;
+
+        info.mAudioPresentations.clear();
         bool hasStreamCA = false;
         while (ES_info_length > 2 && infoBytesRemaining >= 0) {
             unsigned descriptor_tag = br->getBits(8);
@@ -549,12 +552,94 @@
                        descriptor_tag == DESCRIPTOR_DVB_EXTENSION && descriptor_length >= 1) {
                 unsigned descTagExt = br->getBits(8);
                 ALOGV("      tag_ext = 0x%02x", descTagExt);
-                if (descTagExt == EXT_DESCRIPTOR_DVB_AC4) {
-                    info.mTypeExt = EXT_DESCRIPTOR_DVB_AC4;
-                }
                 ES_info_length -= descriptor_length;
                 descriptor_length--;
-                br->skipBits(descriptor_length * 8);
+                // The AC4 descriptor is used in the PSI PMT to identify streams which carry AC4
+                // audio.
+                if (descTagExt == EXT_DESCRIPTOR_DVB_AC4) {
+                    info.mTypeExt = EXT_DESCRIPTOR_DVB_AC4;
+                    br->skipBits(descriptor_length * 8);
+                } else if (descTagExt == EXT_DESCRIPTOR_DVB_AUDIO_PRESELECTION &&
+                           descriptor_length >= 1) {
+                    // DVB BlueBook A038 Table 110
+                    unsigned num_preselections = br->getBits(5);
+                    br->skipBits(3);  // reserved
+                    for (unsigned i = 0; i < num_preselections; ++i) {
+                        if (br->numBitsLeft() < 16) {
+                            ALOGE("Not enough data left in bitreader!");
+                            return ERROR_MALFORMED;
+                        }
+                        AudioPresentationV1 ap;
+                        ap.mPresentationId = br->getBits(5);  // preselection_id
+
+                        // audio_rendering_indication
+                        ap.mMasteringIndication = static_cast<MasteringIndication>(br->getBits(3));
+                        ap.mAudioDescriptionAvailable = (br->getBits(1) == 1);
+                        ap.mSpokenSubtitlesAvailable = (br->getBits(1) == 1);
+                        ap.mDialogueEnhancementAvailable = (br->getBits(1) == 1);
+
+                        bool interactivity_enabled = (br->getBits(1) == 1);
+                        MY_LOGV("      interactivity_enabled = %d", interactivity_enabled);
+
+                        bool language_code_present = (br->getBits(1) == 1);
+                        bool text_label_present = (br->getBits(1) == 1);
+
+                        bool multi_stream_info_present = (br->getBits(1) == 1);
+                        bool future_extension = (br->getBits(1) == 1);
+                        if (language_code_present) {
+                            if (br->numBitsLeft() < 24) {
+                                ALOGE("Not enough data left in bitreader!");
+                                return ERROR_MALFORMED;
+                            }
+                            char language[4];
+                            language[0] = br->getBits(8);
+                            language[1] = br->getBits(8);
+                            language[2] = br->getBits(8);
+                            language[3] = 0;
+                            ap.mLanguage = String8(language);
+                        }
+
+                        // This maps the presentation id to the message id in the
+                        // EXT_DESCRIPTOR_DVB_MESSAGE so that we can get the presentation label.
+                        if (text_label_present) {
+                            if (br->numBitsLeft() < 8) {
+                                ALOGE("Not enough data left in bitreader!");
+                                return ERROR_MALFORMED;
+                            }
+                            unsigned message_id = br->getBits(8);
+                            MY_LOGV("      message_id = %u", message_id);
+                        }
+
+                        if (multi_stream_info_present) {
+                            if (br->numBitsLeft() < 8) {
+                                ALOGE("Not enough data left in bitreader!");
+                                return ERROR_MALFORMED;
+                            }
+                            unsigned num_aux_components = br->getBits(3);
+                            br->skipBits(5);  // reserved
+                            if (br->numBitsLeft() < (num_aux_components * 8)) {
+                                ALOGE("Not enough data left in bitreader!");
+                                return ERROR_MALFORMED;
+                            }
+                            br->skipBits(num_aux_components * 8);  // component_tag
+                        }
+                        if (future_extension) {
+                            if (br->numBitsLeft() < 8) {
+                                return ERROR_MALFORMED;
+                            }
+                            br->skipBits(3);  // reserved
+                            unsigned future_extension_length = br->getBits(5);
+                            if (br->numBitsLeft() < (future_extension_length * 8)) {
+                                ALOGE("Not enough data left in bitreader!");
+                                return ERROR_MALFORMED;
+                            }
+                            br->skipBits(future_extension_length * 8);  // future_extension_byte
+                        }
+                        info.mAudioPresentations.push_back(std::move(ap));
+                    }
+                } else {
+                    br->skipBits(descriptor_length * 8);
+                }
             } else {
                 ES_info_length -= descriptor_length;
                 br->skipBits(descriptor_length * 8);
@@ -754,7 +839,8 @@
       mEOSReached(false),
       mPrevPTS(0),
       mQueue(NULL),
-      mScrambled(info.mCADescriptor.mSystemID >= 0) {
+      mScrambled(info.mCADescriptor.mSystemID >= 0),
+      mAudioPresentations(info.mAudioPresentations) {
     mSampleEncrypted =
             mStreamType == STREAMTYPE_H264_ENCRYPTED ||
             mStreamType == STREAMTYPE_AAC_ENCRYPTED  ||
@@ -1633,6 +1719,7 @@
                 }
                 mSource = new AnotherPacketSource(meta);
                 mSource->queueAccessUnit(accessUnit);
+                mSource->convertAudioPresentationInfoToMetadata(mAudioPresentations);
                 ALOGV("onPayloadData: created AnotherPacketSource PID 0x%08x of type 0x%02x",
                         mElementaryPID, mStreamType);
             }
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 9ece21e..0ff2d7e 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -23,6 +23,7 @@
 #include <media/MediaSource.h>
 #include <media/stagefright/foundation/ABase.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AudioPresentationInfo.h>
 #include <utils/KeyedVector.h>
 #include <utils/Vector.h>
 #include <utils/RefBase.h>
@@ -163,7 +164,7 @@
     };
 
     enum {
-        // From ISO/IEC 13818-1: 2007 (E), Table 2-29
+        // From ISO/IEC 13818-1: 2007 (E), Table 2-45
         DESCRIPTOR_CA                   = 0x09,
 
         // DVB BlueBook A038 Table 12
@@ -172,8 +173,9 @@
 
     // DVB BlueBook A038 Table 109
     enum {
-        EXT_DESCRIPTOR_DVB_AC4              = 0x15,
-        EXT_DESCRIPTOR_DVB_RESERVED_MAX     = 0x7F,
+        EXT_DESCRIPTOR_DVB_AC4                  = 0x15,
+        EXT_DESCRIPTOR_DVB_AUDIO_PRESELECTION   = 0x19,
+        EXT_DESCRIPTOR_DVB_RESERVED_MAX         = 0x7F,
     };
 
 protected:
@@ -196,6 +198,7 @@
         unsigned mTypeExt;
         unsigned mPID;
         CADescriptor mCADescriptor;
+        AudioPresentationCollection mAudioPresentations;
     };
 
     sp<CasManager> mCasManager;
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 9e154a3..e2c5031 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -697,4 +697,23 @@
     return firstMeta;
 }
 
+void AnotherPacketSource::convertAudioPresentationInfoToMetadata(
+        const AudioPresentationCollection& presentations) {
+    sp<MetaData> meta = getFormat();
+    if (meta == NULL) {
+        return;
+    }
+    if (presentations.empty()) {
+        // Clear audio presentation info in metadata.
+        Mutex::Autolock autoLock(mLock);
+        meta->remove(kKeyAudioPresentationInfo);
+    } else {
+        std::ostringstream outStream(std::ios::out);
+        serializeAudioPresentations(presentations, &outStream);
+        Mutex::Autolock autoLock(mLock);
+        meta->setData(kKeyAudioPresentationInfo, MetaData::TYPE_NONE,
+                outStream.str().data(), outStream.str().size());
+    }
+}
+
 }  // namespace android
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index f4a6acb..57a6c33 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -85,6 +85,8 @@
     void trimBuffersAfterMeta(const sp<AMessage> &meta);
     sp<AMessage> trimBuffersBeforeMeta(const sp<AMessage> &meta);
 
+    void convertAudioPresentationInfoToMetadata(const AudioPresentationCollection &presentations);
+
 protected:
     virtual ~AnotherPacketSource();
 
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index b282ed8..bf9725c 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -272,6 +272,7 @@
 EXPORT const char* AMEDIAFORMAT_KEY_ALBUMART = "albumart";
 EXPORT const char* AMEDIAFORMAT_KEY_ALBUMARTIST = "albumartist";
 EXPORT const char* AMEDIAFORMAT_KEY_ARTIST = "artist";
+EXPORT const char* AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO = "audio-presentation-info";
 EXPORT const char* AMEDIAFORMAT_KEY_AUDIO_SESSION_ID = "audio-session-id";
 EXPORT const char* AMEDIAFORMAT_KEY_AUTHOR = "author";
 EXPORT const char* AMEDIAFORMAT_KEY_BITRATE_MODE = "bitrate-mode";
diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h
index 89cfd5e..658cbac 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -180,6 +180,7 @@
 extern const char* AMEDIAFORMAT_KEY_ALBUMART __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_ALBUMARTIST __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_ARTIST __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_AUTHOR __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_BITS_PER_SAMPLE __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_CDTRACKNUMBER __INTRODUCED_IN(29);