stagefright: Add AC4 support in MediaExtractor for MP4/TS

* Add AC4 mime type

* Add AC-4 support to TS extractor
** Extract PES private data and AC4 descriptor.
** Dequeue and parse AC-4 access unit and sync frame
** Validate AC4 elementary stream header and parse sampling rate.

*  Add AC-4 support to MPEG4Extractor
** Parse AC-4 sample entry in MP4 and set AC-4 MIME
** Add AC-4 sync header to MPEG4 encapsulated AC-4 raw frame

*  Add AC4 DSI Parser
** In order to populate the AudioPresentation objects we need to
   extract the AC4 presentation information from the dsi that is
   written to the MPEG4 file during muxing.

Change-Id: If84c24ca475cabf3e0d2bdf3e4850aeeb185a0de
Signed-off-by: Previr Rangroo <prang@dolby.com>
diff --git a/media/extractors/mp4/AC4Parser.cpp b/media/extractors/mp4/AC4Parser.cpp
new file mode 100644
index 0000000..167d474
--- /dev/null
+++ b/media/extractors/mp4/AC4Parser.cpp
@@ -0,0 +1,624 @@
+/*
+ * 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 "AC4Parser"
+
+#include <inttypes.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+#include "AC4Parser.h"
+
+#define BOOLSTR(a)  ((a)?"true":"false")
+#define BYTE_ALIGN mBitReader.skipBits(mBitReader.numBitsLeft() % 8)
+#define CHECK_BITS_LEFT(n) if (mBitReader.numBitsLeft() < n) {return false;}
+
+namespace android {
+
+AC4Parser::AC4Parser() {
+}
+
+AC4DSIParser::AC4DSIParser(ABitReader &br)
+    : mBitReader(br){
+
+    mDSISize = mBitReader.numBitsLeft();
+}
+
+// ETSI TS 103 190-2 V1.1.1 (2015-09) Table 79: channel_mode
+static const char *ChannelModes[] = {
+    "mono",
+    "stereo",
+    "3.0",
+    "5.0",
+    "5.1",
+    "7.0 (3/4/0)",
+    "7.1 (3/4/0.1)",
+    "7.0 (5/2/0)",
+    "7.1 (5/2/0.1)",
+    "7.0 (3/2/2)",
+    "7.1 (3/2/2.1)",
+    "7.0.4",
+    "7.1.4",
+    "9.0.4",
+    "9.1.4",
+    "22.2"
+};
+
+static const char* ContentClassifier[] = {
+    "Complete Main",
+    "Music and Effects",
+    "Visually Impaired",
+    "Hearing Impaired",
+    "Dialog",
+    "Commentary",
+    "Emergency",
+    "Voice Over"
+};
+
+bool AC4DSIParser::parseLanguageTag(uint32_t presentationID, uint32_t substreamID){
+    CHECK_BITS_LEFT(6);
+    uint32_t n_language_tag_bytes = mBitReader.getBits(6);
+    if (n_language_tag_bytes < 2 || n_language_tag_bytes >= 42) {
+        return false;
+    }
+    CHECK_BITS_LEFT(n_language_tag_bytes * 8);
+    char language_tag_bytes[42]; // TS 103 190 part 1 4.3.3.8.7
+    for (uint32_t i = 0; i < n_language_tag_bytes; i++) {
+        language_tag_bytes[i] = (char)mBitReader.getBits(8);
+    }
+    language_tag_bytes[n_language_tag_bytes] = 0;
+    ALOGV("%u.%u: language_tag = %s\n", presentationID, substreamID, language_tag_bytes);
+
+    std::string language(language_tag_bytes, n_language_tag_bytes);
+    mPresentations[presentationID].mLanguage = language;
+
+    return true;
+}
+
+// TS 103 190-1 v1.2.1 E.5 and TS 103 190-2 v1.1.1 E.9
+bool AC4DSIParser::parseSubstreamDSI(uint32_t presentationID, uint32_t substreamID){
+    CHECK_BITS_LEFT(5);
+    uint32_t channel_mode = mBitReader.getBits(5);
+    CHECK_BITS_LEFT(2);
+    uint32_t dsi_sf_multiplier = mBitReader.getBits(2);
+    CHECK_BITS_LEFT(1);
+    bool b_substream_bitrate_indicator = (mBitReader.getBits(1) == 1);
+    ALOGV("%u.%u: channel_mode = %u (%s)\n", presentationID, substreamID, channel_mode,
+    channel_mode < NELEM(ChannelModes) ? ChannelModes[channel_mode] : "reserved");
+    ALOGV("%u.%u: dsi_sf_multiplier = %u\n", presentationID,
+        substreamID, dsi_sf_multiplier);
+    ALOGV("%u.%u: b_substream_bitrate_indicator = %s\n", presentationID,
+        substreamID, BOOLSTR(b_substream_bitrate_indicator));
+
+    if (b_substream_bitrate_indicator) {
+        CHECK_BITS_LEFT(5);
+        uint32_t substream_bitrate_indicator = mBitReader.getBits(5);
+        ALOGV("%u.%u: substream_bitrate_indicator = %u\n", presentationID, substreamID,
+            substream_bitrate_indicator);
+    }
+    if (channel_mode >= 7 && channel_mode <= 10) {
+        CHECK_BITS_LEFT(1);
+        uint32_t add_ch_base = mBitReader.getBits(1);
+        ALOGV("%u.%u: add_ch_base = %u\n", presentationID, substreamID, add_ch_base);
+    }
+    CHECK_BITS_LEFT(1);
+    bool b_content_type = (mBitReader.getBits(1) == 1);
+    ALOGV("%u.%u: b_content_type = %s\n", presentationID, substreamID, BOOLSTR(b_content_type));
+    if (b_content_type) {
+        CHECK_BITS_LEFT(3);
+        uint32_t content_classifier = mBitReader.getBits(3);
+        ALOGV("%u.%u: content_classifier = %u (%s)\n", presentationID, substreamID,
+            content_classifier, ContentClassifier[content_classifier]);
+
+        // For streams based on TS 103 190 part 1 the presentation level channel_mode doesn't
+        // exist and so we use the channel_mode from either the CM or M&E substream
+        // (they are mutually exclusive)
+        if (mPresentations[presentationID].mChannelMode == -1 &&
+            (content_classifier == 0 || content_classifier == 1)) {
+            mPresentations[presentationID].mChannelMode = channel_mode;
+        }
+        mPresentations[presentationID].mContentClassifier = content_classifier;
+        CHECK_BITS_LEFT(1);
+        bool b_language_indicator = (mBitReader.getBits(1) == 1);
+        ALOGV("%u.%u: b_language_indicator = %s\n", presentationID, substreamID,
+            BOOLSTR(b_language_indicator));
+        if (b_language_indicator) {
+            if (!parseLanguageTag(presentationID, substreamID)) {
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+// ETSI TS 103 190-2 v1.1.1 section E.11
+bool AC4DSIParser::parseSubstreamGroupDSI(uint32_t presentationID, uint32_t groupID)
+{
+    CHECK_BITS_LEFT(1);
+    bool b_substreams_present = (mBitReader.getBits(1) == 1);
+    CHECK_BITS_LEFT(1);
+    bool b_hsf_ext = (mBitReader.getBits(1) == 1);
+    CHECK_BITS_LEFT(1);
+    bool b_channel_coded = (mBitReader.getBits(1) == 1);
+    CHECK_BITS_LEFT(8);
+    uint32_t n_substreams = mBitReader.getBits(8);
+    ALOGV("%u.%u: b_substreams_present = %s\n", presentationID, groupID,
+        BOOLSTR(b_substreams_present));
+    ALOGV("%u.%u: b_hsf_ext = %s\n", presentationID, groupID, BOOLSTR(b_hsf_ext));
+    ALOGV("%u.%u: b_channel_coded = %s\n", presentationID, groupID, BOOLSTR(b_channel_coded));
+    ALOGV("%u.%u: n_substreams = %u\n", presentationID, groupID, n_substreams);
+
+    for (uint32_t i = 0; i < n_substreams; i++) {
+        CHECK_BITS_LEFT(2);
+        uint32_t dsi_sf_multiplier = mBitReader.getBits(2);
+        CHECK_BITS_LEFT(1);
+        bool b_substream_bitrate_indicator = (mBitReader.getBits(1) == 1);
+        ALOGV("%u.%u.%u: dsi_sf_multiplier = %u\n", presentationID, groupID, i, dsi_sf_multiplier);
+        ALOGV("%u.%u.%u: b_substream_bitrate_indicator = %s\n", presentationID, groupID, i,
+            BOOLSTR(b_substream_bitrate_indicator));
+
+        if (b_substream_bitrate_indicator) {
+            CHECK_BITS_LEFT(5);
+            uint32_t substream_bitrate_indicator = mBitReader.getBits(5);
+            ALOGV("%u.%u.%u: substream_bitrate_indicator = %u\n", presentationID, groupID, i,
+                substream_bitrate_indicator);
+        }
+        if (b_channel_coded) {
+            CHECK_BITS_LEFT(24);
+            uint32_t dsi_substream_channel_mask = mBitReader.getBits(24);
+            ALOGV("%u.%u.%u: dsi_substream_channel_mask = 0x%06x\n", presentationID, groupID, i,
+                dsi_substream_channel_mask);
+        } else {
+            CHECK_BITS_LEFT(1);
+            bool b_ajoc = (mBitReader.getBits(1) == 1);
+            ALOGV("%u.%u.%u: b_ajoc = %s\n", presentationID, groupID, i, BOOLSTR(b_ajoc));
+            if (b_ajoc) {
+                CHECK_BITS_LEFT(1);
+                bool b_static_dmx = (mBitReader.getBits(1) == 1);
+                ALOGV("%u.%u.%u: b_static_dmx = %s\n", presentationID, groupID, i,
+                    BOOLSTR(b_static_dmx));
+                if (!b_static_dmx) {
+                    CHECK_BITS_LEFT(4);
+                    uint32_t n_dmx_objects_minus1 = mBitReader.getBits(4);
+                    ALOGV("%u.%u.%u: n_dmx_objects_minus1 = %u\n", presentationID, groupID, i,
+                        n_dmx_objects_minus1);
+                }
+                CHECK_BITS_LEFT(6);
+                uint32_t n_umx_objects_minus1 = mBitReader.getBits(6);
+                ALOGV("%u.%u.%u: n_umx_objects_minus1 = %u\n", presentationID, groupID, i,
+                    n_umx_objects_minus1);
+            }
+            CHECK_BITS_LEFT(4);
+            mBitReader.skipBits(4); // objects_assignment_mask
+        }
+    }
+
+    CHECK_BITS_LEFT(1);
+    bool b_content_type = (mBitReader.getBits(1) == 1);
+    ALOGV("%u.%u: b_content_type = %s\n", presentationID, groupID, BOOLSTR(b_content_type));
+    if (b_content_type) {
+        CHECK_BITS_LEFT(3);
+        uint32_t content_classifier = mBitReader.getBits(3);
+        ALOGV("%u.%u: content_classifier = %s (%u)\n", presentationID, groupID,
+            ContentClassifier[content_classifier], content_classifier);
+
+        mPresentations[presentationID].mContentClassifier = content_classifier;
+
+        CHECK_BITS_LEFT(1);
+        bool b_language_indicator = (mBitReader.getBits(1) == 1);
+        ALOGV("%u.%u: b_language_indicator = %s\n", presentationID, groupID,
+            BOOLSTR(b_language_indicator));
+
+        if (b_language_indicator) {
+            if (!parseLanguageTag(presentationID, groupID)) {
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+bool AC4DSIParser::parseBitrateDsi() {
+    CHECK_BITS_LEFT(2 + 32 + 32);
+    mBitReader.skipBits(2); // bit_rate_mode
+    mBitReader.skipBits(32); // bit_rate
+    mBitReader.skipBits(32); // bit_rate_precision
+
+    return true;
+}
+
+// TS 103 190-1 section E.4 (ac4_dsi) and TS 103 190-2 section E.6 (ac4_dsi_v1)
+bool AC4DSIParser::parse() {
+    CHECK_BITS_LEFT(3);
+    uint32_t ac4_dsi_version = mBitReader.getBits(3);
+    if (ac4_dsi_version > 1) {
+        ALOGE("error while parsing ac-4 dsi: only versions 0 and 1 are supported");
+        return false;
+    }
+
+    CHECK_BITS_LEFT(7 + 1 + 4 + 9);
+    uint32_t bitstream_version = mBitReader.getBits(7);
+    mBitReader.skipBits(1); // fs_index
+    mBitReader.skipBits(4); // frame_rate_index
+    uint32_t n_presentations = mBitReader.getBits(9);
+
+    int32_t short_program_id = -1;
+    if (bitstream_version > 1) {
+        if (ac4_dsi_version == 0){
+            ALOGE("invalid ac4 dsi");
+            return false;
+        }
+        CHECK_BITS_LEFT(1);
+        bool b_program_id = (mBitReader.getBits(1) == 1);
+        if (b_program_id) {
+            CHECK_BITS_LEFT(16 + 1);
+            short_program_id = mBitReader.getBits(16);
+            bool b_uuid = (mBitReader.getBits(1) == 1);
+            if (b_uuid) {
+                const uint32_t kAC4UUIDSizeInBytes = 16;
+                char program_uuid[kAC4UUIDSizeInBytes];
+                CHECK_BITS_LEFT(kAC4UUIDSizeInBytes * 8);
+                for (uint32_t i = 0; i < kAC4UUIDSizeInBytes; i++) {
+                    program_uuid[i] = (char)(mBitReader.getBits(8));
+                }
+                ALOGV("UUID = %s", program_uuid);
+            }
+        }
+    }
+
+    if (ac4_dsi_version == 1) {
+        if (!parseBitrateDsi()) {
+            return false;
+        }
+        BYTE_ALIGN;
+    }
+
+    for (uint32_t presentation = 0; presentation < n_presentations; presentation++) {
+        mPresentations[presentation].mProgramID = short_program_id;
+        // known as b_single_substream in ac4_dsi_version 0
+        bool b_single_substream_group = false;
+        uint32_t presentation_config = 0, presentation_version = 0;
+        uint32_t pres_bytes = 0;
+
+        if (ac4_dsi_version == 0) {
+            CHECK_BITS_LEFT(1 + 5 + 5);
+            b_single_substream_group = (mBitReader.getBits(1) == 1);
+            presentation_config = mBitReader.getBits(5);
+            presentation_version = mBitReader.getBits(5);
+        } else if (ac4_dsi_version == 1) {
+            CHECK_BITS_LEFT(8 + 8);
+            presentation_version = mBitReader.getBits(8);
+            pres_bytes = mBitReader.getBits(8);
+            if (pres_bytes == 0xff) {
+                CHECK_BITS_LEFT(16);
+                pres_bytes += mBitReader.getBits(16);
+            }
+            ALOGV("%u: pres_bytes = %u\n", presentation, pres_bytes);
+            if (presentation_version > 1) {
+                CHECK_BITS_LEFT(pres_bytes * 8);
+                mBitReader.skipBits(pres_bytes * 8);
+                continue;
+            }
+            // ac4_presentation_v0_dsi() and ac4_presentation_v1_dsi() both
+            // start with a presentation_config of 5 bits
+            CHECK_BITS_LEFT(5);
+            presentation_config = mBitReader.getBits(5);
+            b_single_substream_group = (presentation_config == 0x1f);
+        }
+
+        static const char *PresentationConfig[] = {
+            "Music&Effects + Dialog",
+            "Main + DE",
+            "Main + Associate",
+            "Music&Effects + Dialog + Associate",
+            "Main + DE + Associate",
+            "Arbitrary substream groups",
+            "EMDF only"
+        };
+        ALOGV("%u: b_single_substream/group = %s\n", presentation,
+            BOOLSTR(b_single_substream_group));
+        ALOGV("%u: presentation_version = %u\n", presentation, presentation_version);
+        ALOGV("%u: presentation_config = %u (%s)\n", presentation, presentation_config,
+            (presentation_config >= NELEM(PresentationConfig) ?
+            "reserved" : PresentationConfig[presentation_config]));
+
+        /* record a marker, less the size of the presentation_config */
+        uint64_t start = (mDSISize - mBitReader.numBitsLeft()) / 8;
+
+        bool b_add_emdf_substreams = false;
+        if (!b_single_substream_group && presentation_config == 6) {
+            b_add_emdf_substreams = true;
+            ALOGV("%u: b_add_emdf_substreams = %s\n", presentation, BOOLSTR(b_add_emdf_substreams));
+        } else {
+            CHECK_BITS_LEFT(3 + 1);
+            uint32_t mdcompat = mBitReader.getBits(3);
+            ALOGV("%u: mdcompat = %d\n", presentation, mdcompat);
+
+            bool b_presentation_group_index = (mBitReader.getBits(1) == 1);
+            ALOGV("%u: b_presentation_group_index = %s\n", presentation,
+                BOOLSTR(b_presentation_group_index));
+            if (b_presentation_group_index) {
+                CHECK_BITS_LEFT(5);
+                mPresentations[presentation].mGroupIndex = mBitReader.getBits(5);
+                ALOGV("%u: presentation_group_index = %d\n", presentation,
+                    mPresentations[presentation].mGroupIndex);
+            }
+            CHECK_BITS_LEFT(2);
+            uint32_t dsi_frame_rate_multiply_info = mBitReader.getBits(2);
+            ALOGV("%u: dsi_frame_rate_multiply_info = %d\n", presentation,
+                dsi_frame_rate_multiply_info);
+            if (ac4_dsi_version == 1 && presentation_version == 1) {
+                CHECK_BITS_LEFT(2);
+                uint32_t dsi_frame_rate_fraction_info = mBitReader.getBits(2);
+                ALOGV("%u: dsi_frame_rate_fraction_info = %d\n", presentation,
+                    dsi_frame_rate_fraction_info);
+            }
+            CHECK_BITS_LEFT(5 + 10);
+            uint32_t presentation_emdf_version = mBitReader.getBits(5);
+            uint32_t presentation_key_id = mBitReader.getBits(10);
+            ALOGV("%u: presentation_emdf_version = %d\n", presentation, presentation_emdf_version);
+            ALOGV("%u: presentation_key_id = %d\n", presentation, presentation_key_id);
+
+            if (ac4_dsi_version == 1) {
+                bool b_presentation_channel_coded = false;
+                if (presentation_version == 0) {
+                    b_presentation_channel_coded = true;
+                } else {
+                    CHECK_BITS_LEFT(1);
+                    b_presentation_channel_coded = (mBitReader.getBits(1) == 1);
+                }
+                ALOGV("%u: b_presentation_channel_coded = %s\n", presentation,
+                    BOOLSTR(b_presentation_channel_coded));
+                if (b_presentation_channel_coded) {
+                    if (presentation_version == 1) {
+                        CHECK_BITS_LEFT(5);
+                        uint32_t dsi_presentation_ch_mode = mBitReader.getBits(5);
+                        mPresentations[presentation].mChannelMode = dsi_presentation_ch_mode;
+                        ALOGV("%u: dsi_presentation_ch_mode = %d (%s)\n", presentation,
+                            dsi_presentation_ch_mode,
+                            dsi_presentation_ch_mode < NELEM(ChannelModes) ?
+                            ChannelModes[dsi_presentation_ch_mode] : "reserved");
+
+                        if (dsi_presentation_ch_mode >= 11 && dsi_presentation_ch_mode <= 14) {
+                            CHECK_BITS_LEFT(1 + 2);
+                            uint32_t pres_b_4_back_channels_present = mBitReader.getBits(1);
+                            uint32_t pres_top_channel_pairs = mBitReader.getBits(2);
+                            ALOGV("%u: pres_b_4_back_channels_present = %s\n", presentation,
+                                BOOLSTR(pres_b_4_back_channels_present));
+                            ALOGV("%u: pres_top_channel_pairs = %d\n", presentation,
+                                pres_top_channel_pairs);
+                        }
+                    }
+                    // presentation_channel_mask in ac4_presentation_v0_dsi()
+                    CHECK_BITS_LEFT(24);
+                    uint32_t presentation_channel_mask_v1 = mBitReader.getBits(24);
+                    ALOGV("%u: presentation_channel_mask_v1 = 0x%06x\n", presentation,
+                        presentation_channel_mask_v1);
+                }
+                if (presentation_version == 1) {
+                    CHECK_BITS_LEFT(1);
+                    bool b_presentation_core_differs = (mBitReader.getBits(1) == 1);
+                    ALOGV("%u: b_presentation_core_differs = %s\n", presentation,
+                        BOOLSTR(b_presentation_core_differs));
+                    if (b_presentation_core_differs) {
+                        CHECK_BITS_LEFT(1);
+                        bool b_presentation_core_channel_coded = (mBitReader.getBits(1) == 1);
+                        if (b_presentation_core_channel_coded) {
+                            CHECK_BITS_LEFT(2);
+                            mBitReader.skipBits(2); // dsi_presentation_channel_mode_core
+                        }
+                    }
+                    CHECK_BITS_LEFT(1);
+                    bool b_presentation_filter = (mBitReader.getBits(1) == 1);
+                    ALOGV("%u: b_presentation_filter = %s\n", presentation,
+                        BOOLSTR(b_presentation_filter));
+                    if (b_presentation_filter) {
+                        CHECK_BITS_LEFT(1 + 8);
+                        bool b_enable_presentation = (mBitReader.getBits(1) == 1);
+                        if (!b_enable_presentation) {
+                            mPresentations[presentation].mEnabled = false;
+                        }
+                        ALOGV("%u: b_enable_presentation = %s\n", presentation,
+                            BOOLSTR(b_enable_presentation));
+                        uint32_t n_filter_bytes = mBitReader.getBits(8);
+                        CHECK_BITS_LEFT(n_filter_bytes * 8);
+                        for (uint32_t i = 0; i < n_filter_bytes; i++) {
+                            mBitReader.skipBits(8); // filter_data
+                        }
+                    }
+                }
+            } /* ac4_dsi_version == 1 */
+
+            if (b_single_substream_group) {
+                if (presentation_version == 0) {
+                    if (!parseSubstreamDSI(presentation, 0)) {
+                        return false;
+                    }
+                } else {
+                    if (!parseSubstreamGroupDSI(presentation, 0)) {
+                        return false;
+                    }
+                }
+            } else {
+                if (ac4_dsi_version == 1) {
+                    CHECK_BITS_LEFT(1);
+                    bool b_multi_pid = (mBitReader.getBits(1) == 1);
+                    ALOGV("%u: b_multi_pid = %s\n", presentation, BOOLSTR(b_multi_pid));
+                } else {
+                    CHECK_BITS_LEFT(1);
+                    bool b_hsf_ext = (mBitReader.getBits(1) == 1);
+                    ALOGV("%u: b_hsf_ext = %s\n", presentation, BOOLSTR(b_hsf_ext));
+                }
+                switch (presentation_config) {
+                case 0:
+                case 1:
+                case 2:
+                    if (presentation_version == 0) {
+                        if (!parseSubstreamDSI(presentation, 0)) {
+                            return false;
+                        }
+                        if (!parseSubstreamDSI(presentation, 1)) {
+                            return false;
+                        }
+                    } else {
+                        if (!parseSubstreamGroupDSI(presentation, 0)) {
+                            return false;
+                        }
+                        if (!parseSubstreamGroupDSI(presentation, 1)) {
+                            return false;
+                        }
+                    }
+                    break;
+                case 3:
+                case 4:
+                    if (presentation_version == 0) {
+                        if (!parseSubstreamDSI(presentation, 0)) {
+                            return false;
+                        }
+                        if (!parseSubstreamDSI(presentation, 1)) {
+                            return false;
+                        }
+                        if (!parseSubstreamDSI(presentation, 2)) {
+                            return false;
+                        }
+                    } else {
+                        if (!parseSubstreamGroupDSI(presentation, 0)) {
+                            return false;
+                        }
+                        if (!parseSubstreamGroupDSI(presentation, 1)) {
+                            return false;
+                        }
+                        if (!parseSubstreamGroupDSI(presentation, 2)) {
+                            return false;
+                        }
+                    }
+                    break;
+                case 5:
+                    if (presentation_version == 0) {
+                        if (!parseSubstreamDSI(presentation, 0)) {
+                            return false;
+                        }
+                    } else {
+                        CHECK_BITS_LEFT(3);
+                        uint32_t n_substream_groups_minus2 = mBitReader.getBits(3);
+                        ALOGV("%u: n_substream_groups_minus2 = %d\n", presentation,
+                            n_substream_groups_minus2);
+                        for (uint32_t sg = 0; sg < n_substream_groups_minus2 + 2; sg++) {
+                            if (!parseSubstreamGroupDSI(presentation, sg)) {
+                                return false;
+                            }
+                        }
+                    }
+                    break;
+                default:
+                    CHECK_BITS_LEFT(7);
+                    uint32_t n_skip_bytes = mBitReader.getBits(7);
+                    CHECK_BITS_LEFT(n_skip_bytes * 8)
+                    for (uint32_t j = 0; j < n_skip_bytes; j++) {
+                        mBitReader.getBits(8);
+                    }
+                    break;
+                }
+                CHECK_BITS_LEFT(1 + 1);
+                bool b_pre_virtualized = (mBitReader.getBits(1) == 1);
+                mPresentations[presentation].mPreVirtualized = b_pre_virtualized;
+                b_add_emdf_substreams = (mBitReader.getBits(1) == 1);
+                ALOGV("%u: b_pre_virtualized = %s\n", presentation, BOOLSTR(b_pre_virtualized));
+                ALOGV("%u: b_add_emdf_substreams = %s\n", presentation,
+                    BOOLSTR(b_add_emdf_substreams));
+            }
+        }
+        if (b_add_emdf_substreams) {
+            CHECK_BITS_LEFT(7);
+            uint32_t n_add_emdf_substreams = mBitReader.getBits(7);
+            for (uint32_t j = 0; j < n_add_emdf_substreams; j++) {
+                CHECK_BITS_LEFT(5 + 10);
+                uint32_t substream_emdf_version = mBitReader.getBits(5);
+                uint32_t substream_key_id = mBitReader.getBits(10);
+                ALOGV("%u: emdf_substream[%d]: version=%d, key_id=%d\n", presentation, j,
+                    substream_emdf_version, substream_key_id);
+            }
+        }
+
+        bool b_presentation_bitrate_info = false;
+        if (presentation_version > 0) {
+            CHECK_BITS_LEFT(1);
+            b_presentation_bitrate_info = (mBitReader.getBits(1) == 1);
+        }
+
+        ALOGV("b_presentation_bitrate_info = %s\n", BOOLSTR(b_presentation_bitrate_info));
+        if (b_presentation_bitrate_info) {
+            if (!parseBitrateDsi()) {
+                return false;
+            }
+        }
+
+        if (presentation_version > 0) {
+            CHECK_BITS_LEFT(1);
+            bool b_alternative = (mBitReader.getBits(1) == 1);
+            ALOGV("b_alternative = %s\n", BOOLSTR(b_alternative));
+            if (b_alternative) {
+                BYTE_ALIGN;
+                CHECK_BITS_LEFT(16);
+                uint32_t name_len = mBitReader.getBits(16);
+                char* presentation_name = new char[name_len+1];
+                CHECK_BITS_LEFT(name_len * 8);
+                for (uint32_t i = 0; i < name_len; i++) {
+                    presentation_name[i] = (char)(mBitReader.getBits(8));
+                }
+                presentation_name[name_len] = '\0';
+                std::string description(presentation_name, name_len);
+                mPresentations[presentation].mDescription = description;
+                CHECK_BITS_LEFT(5);
+                uint32_t n_targets = mBitReader.getBits(5);
+                CHECK_BITS_LEFT(n_targets * (3 + 8));
+                for (uint32_t i = 0; i < n_targets; i++){
+                    mBitReader.skipBits(3); // target_md_compat
+                    mBitReader.skipBits(8); // target_device_category
+                }
+            }
+        }
+
+        BYTE_ALIGN;
+
+        if (ac4_dsi_version == 1) {
+            uint64_t end = (mDSISize - mBitReader.numBitsLeft()) / 8;
+            if (mBitReader.numBitsLeft() % 8 != 0) {
+                end += 1;
+            }
+
+            uint64_t presentation_bytes = end - start;
+            uint64_t skip_bytes = pres_bytes - presentation_bytes;
+            ALOGV("skipping = %" PRIu64 " bytes", skip_bytes);
+            CHECK_BITS_LEFT(skip_bytes * 8);
+            mBitReader.skipBits(skip_bytes * 8);
+        }
+
+        // we should know this or something is probably wrong
+        // with the bitstream (or we don't support it)
+        if (mPresentations[presentation].mChannelMode == -1){
+            ALOGE("could not determing channel mode of presentation %d", presentation);
+            return false;
+        }
+    } /* each presentation */
+
+    return true;
+}
+
+};
diff --git a/media/extractors/mp4/AC4Parser.h b/media/extractors/mp4/AC4Parser.h
new file mode 100644
index 0000000..73b6e31
--- /dev/null
+++ b/media/extractors/mp4/AC4Parser.h
@@ -0,0 +1,109 @@
+/*
+ * 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 AC4_PARSER_H_
+#define AC4_PARSER_H_
+
+#include <cstdint>
+#include <map>
+#include <string>
+
+#include <media/stagefright/foundation/ABitReader.h>
+
+namespace android {
+
+class AC4Parser {
+public:
+    AC4Parser();
+    virtual ~AC4Parser() { }
+
+    virtual bool parse() = 0;
+
+    struct AC4Presentation {
+        int32_t mChannelMode = -1;
+        int32_t mProgramID = -1;
+        int32_t mGroupIndex = -1;
+
+        // TS 103 190-1 v1.2.1 4.3.3.8.1
+        enum ContentClassifiers {
+            kCompleteMain,
+            kMusicAndEffects,
+            kVisuallyImpaired,
+            kHearingImpaired,
+            kDialog,
+            kCommentary,
+            kEmergency,
+            kVoiceOver
+        };
+
+        uint32_t mContentClassifier = kCompleteMain;
+
+        // ETSI TS 103 190-2 V1.1.1 (2015-09) Table 79: channel_mode
+        enum InputChannelMode {
+            kChannelMode_Mono,
+            kChannelMode_Stereo,
+            kChannelMode_3_0,
+            kChannelMode_5_0,
+            kChannelMode_5_1,
+            kChannelMode_7_0_34,
+            kChannelMode_7_1_34,
+            kChannelMode_7_0_52,
+            kChannelMode_7_1_52,
+            kChannelMode_7_0_322,
+            kChannelMode_7_1_322,
+            kChannelMode_7_0_4,
+            kChannelMode_7_1_4,
+            kChannelMode_9_0_4,
+            kChannelMode_9_1_4,
+            kChannelMode_22_2,
+            kChannelMode_Reserved,
+        };
+
+        bool mHasDialogEnhancements = false;
+        bool mPreVirtualized = false;
+        bool mEnabled = true;
+
+        std::string mLanguage;
+        std::string mDescription;
+    };
+    typedef std::map<uint32_t, AC4Presentation> AC4Presentations;
+
+    const AC4Presentations& getPresentations() const { return mPresentations; }
+
+protected:
+    AC4Presentations mPresentations;
+};
+
+class AC4DSIParser: public AC4Parser {
+public:
+    explicit AC4DSIParser(ABitReader &br);
+    virtual ~AC4DSIParser() { }
+
+    bool parse();
+
+private:
+    bool parseSubstreamDSI(uint32_t presentationID, uint32_t substreamID);
+    bool parseSubstreamGroupDSI(uint32_t presentationID, uint32_t groupID);
+    bool parseLanguageTag(uint32_t presentationID, uint32_t substreamID);
+    bool parseBitrateDsi();
+
+    uint64_t mDSISize;
+    ABitReader& mBitReader;
+};
+
+};
+
+#endif  // AC4_PARSER_H_
diff --git a/media/extractors/mp4/Android.bp b/media/extractors/mp4/Android.bp
index 3fe2336..0b6e75a 100644
--- a/media/extractors/mp4/Android.bp
+++ b/media/extractors/mp4/Android.bp
@@ -2,6 +2,7 @@
     name: "libmp4extractor_defaults",
 
     srcs: [
+        "AC4Parser.cpp",
         "ItemTable.cpp",
         "MPEG4Extractor.cpp",
         "SampleIterator.cpp",
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 99f32d5..bbc735e 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -26,6 +26,7 @@
 
 #include <utils/Log.h>
 
+#include "AC4Parser.h"
 #include "MPEG4Extractor.h"
 #include "SampleTable.h"
 #include "ItemTable.h"
@@ -123,6 +124,8 @@
 
     bool mIsAVC;
     bool mIsHEVC;
+    bool mIsAC4;
+
     size_t mNALLengthSize;
 
     bool mStarted;
@@ -320,6 +323,8 @@
         case FOURCC('h', 'v', 'c', '1'):
         case FOURCC('h', 'e', 'v', '1'):
             return MEDIA_MIMETYPE_VIDEO_HEVC;
+        case FOURCC('a', 'c', '-', '4'):
+            return MEDIA_MIMETYPE_AUDIO_AC4;
         default:
             CHECK(!"should not be here.");
             return NULL;
@@ -2326,6 +2331,12 @@
             return parseAC3SampleEntry(data_offset);
         }
 
+        case FOURCC('a', 'c', '-', '4'):
+        {
+            *offset += chunk_size;
+            return parseAC4SampleEntry(data_offset);
+        }
+
         case FOURCC('f', 't', 'y', 'p'):
         {
             if (chunk_data_size < 8 || depth != 0) {
@@ -2397,6 +2408,84 @@
     return OK;
 }
 
+status_t MPEG4Extractor::parseAC4SampleEntry(off64_t offset) {
+    // skip 16 bytes:
+    //  + 6-byte reserved,
+    //  + 2-byte data reference index,
+    //  + 8-byte reserved
+    offset += 16;
+    uint16_t channelCount;
+    if (!mDataSource->getUInt16(offset, &channelCount)) {
+        ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read channel count");
+        return ERROR_MALFORMED;
+    }
+    // skip 8 bytes:
+    //  + 2-byte channelCount,
+    //  + 2-byte sample size,
+    //  + 4-byte reserved
+    offset += 8;
+    uint16_t sampleRate;
+    if (!mDataSource->getUInt16(offset, &sampleRate)) {
+        ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read sample rate");
+        return ERROR_MALFORMED;
+    }
+
+    // skip 4 bytes:
+    //  + 2-byte sampleRate,
+    //  + 2-byte reserved
+    offset += 4;
+
+    if (mLastTrack == NULL) {
+        return ERROR_MALFORMED;
+    }
+    mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC4);
+    mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
+    mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
+    return parseAC4SpecificBox(offset);
+}
+
+status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) {
+    uint32_t size;
+    // + 4-byte size
+    // + 4-byte type
+    // + 3-byte payload
+    const uint32_t kAC4MinimumBoxSize = 4 + 4 + 3;
+    if (!mDataSource->getUInt32(offset, &size) || size < kAC4MinimumBoxSize) {
+        ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read specific box size");
+        return ERROR_MALFORMED;
+    }
+
+    // + 4-byte size
+    offset += 4;
+    uint32_t type;
+    if (!mDataSource->getUInt32(offset, &type) || type != FOURCC('d', 'a', 'c', '4')) {
+        ALOGE("MPEG4Extractor: error while reading ac-4 specific block: header not dac4");
+        return ERROR_MALFORMED;
+    }
+
+    // + 4-byte type
+    offset += 4;
+    // at least for AC4 DSI v1 this is big enough
+    const uint32_t kAC4SpecificBoxPayloadSize = 256;
+    uint8_t chunk[kAC4SpecificBoxPayloadSize];
+    ssize_t dsiSize = size - 8; // size of box - size and type fields
+    if (dsiSize >= (ssize_t)kAC4SpecificBoxPayloadSize ||
+        mDataSource->readAt(offset, chunk, dsiSize) != dsiSize) {
+        ALOGE("MPEG4Extractor: error while reading ac-4 specific block: bitstream fields");
+        return ERROR_MALFORMED;
+    }
+    // + size-byte payload
+    offset += dsiSize;
+    ABitReader br(chunk, dsiSize);
+    AC4DSIParser parser(br);
+    if (!parser.parse()){
+        ALOGE("MPEG4Extractor: error while parsing ac-4 specific block");
+        return ERROR_MALFORMED;
+    }
+
+    return OK;
+}
+
 status_t MPEG4Extractor::parseAC3SampleEntry(off64_t offset) {
     // skip 16 bytes:
     //  + 6-byte reserved,
@@ -3745,6 +3834,7 @@
       mCurrentSampleInfoOffsets(NULL),
       mIsAVC(false),
       mIsHEVC(false),
+      mIsAC4(false),
       mNALLengthSize(0),
       mStarted(false),
       mGroup(NULL),
@@ -3775,6 +3865,7 @@
     mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
     mIsHEVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) ||
               !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
+    mIsAC4 = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4);
 
     if (mIsAVC) {
         uint32_t type;
@@ -4672,7 +4763,7 @@
         }
     }
 
-    if ((!mIsAVC && !mIsHEVC) || mWantsNALFragments) {
+    if ((!mIsAVC && !mIsHEVC && !mIsAC4) || mWantsNALFragments) {
         if (newBuffer) {
             ssize_t num_bytes_read =
                 mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
@@ -4704,13 +4795,20 @@
             ++mCurrentSampleIndex;
         }
 
-        if (!mIsAVC && !mIsHEVC) {
+        if (!mIsAVC && !mIsHEVC && !mIsAC4) {
             *out = mBuffer;
             mBuffer = NULL;
 
             return OK;
         }
 
+        if (mIsAC4) {
+            mBuffer->release();
+            mBuffer = NULL;
+
+            return ERROR_IO;
+        }
+
         // Each NAL unit is split up into its constituent fragments and
         // each one of them returned in its own buffer.
 
@@ -4749,6 +4847,58 @@
         *out = clone;
 
         return OK;
+    } else if (mIsAC4) {
+        CHECK(mBuffer != NULL);
+        // Make sure there is enough space to write the sync header and the raw frame
+        if (mBuffer->range_length() < (7 + size)) {
+            mBuffer->release();
+            mBuffer = NULL;
+
+            return ERROR_IO;
+        }
+
+        uint8_t *dstData = (uint8_t *)mBuffer->data();
+        size_t dstOffset = 0;
+        // Add AC-4 sync header to MPEG4 encapsulated AC-4 raw frame
+        // AC40 sync word, meaning no CRC at the end of the frame
+        dstData[dstOffset++] = 0xAC;
+        dstData[dstOffset++] = 0x40;
+        dstData[dstOffset++] = 0xFF;
+        dstData[dstOffset++] = 0xFF;
+        dstData[dstOffset++] = (uint8_t)((size >> 16) & 0xFF);
+        dstData[dstOffset++] = (uint8_t)((size >> 8) & 0xFF);
+        dstData[dstOffset++] = (uint8_t)((size >> 0) & 0xFF);
+
+        ssize_t numBytesRead = mDataSource->readAt(offset, dstData + dstOffset, size);
+        if (numBytesRead != (ssize_t)size) {
+            mBuffer->release();
+            mBuffer = NULL;
+
+            return ERROR_IO;
+        }
+
+        mBuffer->set_range(0, dstOffset + size);
+        mBuffer->meta_data().clear();
+        mBuffer->meta_data().setInt64(
+                kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
+        mBuffer->meta_data().setInt64(
+                kKeyDuration, ((int64_t)stts * 1000000) / mTimescale);
+
+        if (targetSampleTimeUs >= 0) {
+            mBuffer->meta_data().setInt64(
+                    kKeyTargetTime, targetSampleTimeUs);
+        }
+
+        if (isSyncSample) {
+            mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
+        }
+
+        ++mCurrentSampleIndex;
+
+        *out = mBuffer;
+        mBuffer = NULL;
+
+        return OK;
     } else {
         // Whole NAL units are returned but each fragment is prefixed by
         // the start code (0x00 00 00 01).
@@ -5191,6 +5341,8 @@
 
         return OK;
     }
+
+    return OK;
 }
 
 MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix(
diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h
index 831f120..6148334 100644
--- a/media/extractors/mp4/MPEG4Extractor.h
+++ b/media/extractors/mp4/MPEG4Extractor.h
@@ -140,6 +140,8 @@
 
     status_t parseAC3SampleEntry(off64_t offset);
     status_t parseAC3SpecificBox(off64_t offset, uint16_t sampleRate);
+    status_t parseAC4SampleEntry(off64_t offset);
+    status_t parseAC4SpecificBox(off64_t offset);
 
     MPEG4Extractor(const MPEG4Extractor &);
     MPEG4Extractor &operator=(const MPEG4Extractor &);