/*****************************************************************
|
|    AP4 - dac4 Atoms
|
|    Copyright 2002-2018 Axiomatic Systems, LLC
|
|
|    This file is part of Bento4/AP4 (MP4 Atom Processing Library).
|
|    Unless you have obtained Bento4 under a difference license,
|    this version of Bento4 is Bento4|GPL.
|    Bento4|GPL is free software; you can redistribute it and/or modify
|    it under the terms of the GNU General Public License as published by
|    the Free Software Foundation; either version 2, or (at your option)
|    any later version.
|
|    Bento4|GPL is distributed in the hope that it will be useful,
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
|    GNU General Public License for more details.
|
|    You should have received a copy of the GNU General Public License
|    along with Bento4|GPL; see the file COPYING.  If not, write to the
|    Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|    02111-1307, USA.
|
****************************************************************/

/*----------------------------------------------------------------------
|   includes
+---------------------------------------------------------------------*/
#include "Ap4Dac4Atom.h"
#include "Ap4AtomFactory.h"
#include "Ap4Utils.h"
#include "Ap4Types.h"

#include <cmath>

/*----------------------------------------------------------------------
|   dynamic cast support
+---------------------------------------------------------------------*/
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_Dac4Atom)

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Create
+---------------------------------------------------------------------*/
AP4_Dac4Atom*
AP4_Dac4Atom::Create(AP4_Size size, AP4_ByteStream& stream)
{
    // read the raw bytes in a buffer
    unsigned int payload_size = size-AP4_ATOM_HEADER_SIZE;
    AP4_DataBuffer payload_data(payload_size);
    AP4_Result result = stream.Read(payload_data.UseData(), payload_size);
    if (AP4_FAILED(result)) return NULL;
    
    // check the version
    const AP4_UI08* payload = payload_data.GetData();
    return new AP4_Dac4Atom(size, payload);
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::AP4_Dac4Atom
+---------------------------------------------------------------------*/
AP4_Dac4Atom::AP4_Dac4Atom(AP4_UI32 size, const Ac4Dsi* ac4Dsi):
    AP4_Atom(AP4_ATOM_TYPE_DAC4, AP4_ATOM_HEADER_SIZE){
    AP4_BitWriter bits(size);
    bits.Write(ac4Dsi->ac4_dsi_version, 3);
    bits.Write(ac4Dsi->d.v1.bitstream_version, 7);
    bits.Write(ac4Dsi->d.v1.fs_index, 1);
    bits.Write(ac4Dsi->d.v1.frame_rate_index, 4);

    // IMS presentation shall has the legacy presentation
    unsigned int add_n_presentations = 0;
    for (unsigned int idx = 0; idx < ac4Dsi->d.v1.n_presentations; idx++){
        if (ac4Dsi->d.v1.presentations[idx].presentation_version == 2) { 
            add_n_presentations++; 
        }
    }
    
    // Assume the total presentation numbers is less than 512 after adding legacy presentation
    bits.Write(ac4Dsi->d.v1.n_presentations + add_n_presentations, 9);

    if (ac4Dsi->d.v1.bitstream_version > 1){
        bits.Write(ac4Dsi->d.v1.b_program_id, 1);
        if (ac4Dsi->d.v1.b_program_id == 1){
            bits.Write(ac4Dsi->d.v1.short_program_id, 16);
            bits.Write(ac4Dsi->d.v1.b_uuid, 1);
            if (ac4Dsi->d.v1.b_uuid == 1) {
                for (unsigned int idx = 0; idx < 16; idx ++) {
                    bits.Write(ac4Dsi->d.v1.program_uuid[idx], 8);
                }
            }
        }
    }
    // ac4_bitrate_dsi()
    AP4_Dac4Atom::Ac4Dsi::Ac4BitrateDsi ac4_bitrate_dsi = ac4Dsi->d.v1.ac4_bitrate_dsi;
    ac4_bitrate_dsi.WriteBitrateDsi(bits);

    AP4_ByteAlign(bits);
    for (unsigned int idx = 0; idx < ac4Dsi->d.v1.n_presentations; idx ++) {
        unsigned int default_pres_bytes = 36;   // random value
        AP4_Dac4Atom::Ac4Dsi::PresentationV1 &presentation = ac4Dsi->d.v1.presentations[idx];
        bits.Write(presentation.presentation_version, 8);
        bits.Write(default_pres_bytes, 8);      // pres_bytes, need to be updated later
        //TODO: if (pres_bytes == 255), shall not happen now. Need the memory move function for bits
        unsigned int pres_bytes_idx = bits.GetBitCount() / 8 - 1;
        if (ac4Dsi->d.v1.n_presentations != 1 && presentation.d.v1.b_presentation_id == 0 && presentation.d.v1.b_extended_presentation_id == 0) {
            fprintf(stderr, "WARN: Need presentation_id for multiple presnetaion signal. The presentation of Presentation Index (PI) is %d miss presentation_id.\n", idx + 1);
        }
        if (presentation.presentation_version == 0){
            // TODO: support presentation version = 0
        }else if (presentation.presentation_version == 1 || presentation.presentation_version == 2){
            presentation.WritePresentationV1Dsi(bits);
            Ap4_Ac4UpdatePresBytes(bits.GetData(), pres_bytes_idx, bits.GetBitCount()/8 - pres_bytes_idx - 1);
        } else {
            Ap4_Ac4UpdatePresBytes(bits.GetData(), pres_bytes_idx, 0);
        }

        //legacy presentation for IMS
        if (presentation.presentation_version == 2) {
            AP4_Dac4Atom::Ac4Dsi::PresentationV1 legacy_presentation = presentation;
            if (legacy_presentation.d.v1.b_presentation_id == 0 && legacy_presentation.d.v1.b_extended_presentation_id == 0) {
                fprintf(stderr, "WARN: Need presentation_id for IMS signal.\n");
            }
            legacy_presentation.presentation_version = 1;
            legacy_presentation.d.v1.b_pre_virtualized = 0;
            legacy_presentation.d.v1.dolby_atmos_indicator = 0;

            bits.Write(legacy_presentation.presentation_version, 8);
            bits.Write(default_pres_bytes, 8);    // pres_bytes, need to be updated later
            //TODO: if (pres_bytes == 255), shall not happen now
            unsigned int pres_bytes_idx = bits.GetBitCount() / 8 - 1;
            legacy_presentation.WritePresentationV1Dsi(bits);
            Ap4_Ac4UpdatePresBytes(bits.GetData(), pres_bytes_idx, bits.GetBitCount()/8 - pres_bytes_idx - 1);
        }
    }
    m_RawBytes.SetData(bits.GetData(), bits.GetBitCount() / 8);
    m_Size32 += m_RawBytes.GetDataSize();
    // clear the DSI
    AP4_SetMemory(&m_Dsi, 0, sizeof(m_Dsi));
    m_Dsi.ac4_dsi_version = -1;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::AP4_Dac4Atom
+---------------------------------------------------------------------*/
AP4_Dac4Atom::AP4_Dac4Atom(AP4_UI32 size, const AP4_UI08* payload) :
    AP4_Atom(AP4_ATOM_TYPE_DAC4, size)
{
    // clear the DSI
    AP4_SetMemory(&m_Dsi, 0, sizeof(m_Dsi));
    
    // make a copy of our configuration bytes
    unsigned int payload_size = size-AP4_ATOM_HEADER_SIZE;
    m_RawBytes.SetData(payload, payload_size);

    // sanity check
    if (payload_size < 11) return;
    
    // parse the `ac4_dsi` or `ac4_dsi_v1` bits
    AP4_BitReader bits(payload, payload_size);
    m_Dsi.ac4_dsi_version = bits.ReadBits(3);
    if (m_Dsi.ac4_dsi_version == 0) {
        m_Dsi.d.v0.bitstream_version = bits.ReadBits(7);
        m_Dsi.d.v0.fs_index          = bits.ReadBits(1);
        m_Dsi.d.v0.frame_rate_index  = bits.ReadBits(4);
        m_Dsi.d.v0.n_presentations   = bits.ReadBits(9);

        // fill in computed fields
        m_Dsi.d.v0.fs = m_Dsi.d.v0.fs_index == 0 ? 44100 : 48000;
    } else if (m_Dsi.ac4_dsi_version == 1) {
        m_Dsi.d.v1.bitstream_version = bits.ReadBits(7);
        m_Dsi.d.v1.fs_index          = bits.ReadBits(1);
        m_Dsi.d.v1.frame_rate_index  = bits.ReadBits(4);
        m_Dsi.d.v1.n_presentations   = bits.ReadBits(9);
        if (m_Dsi.d.v1.bitstream_version > 1) {
            if (bits.ReadBit()) {
                m_Dsi.d.v1.short_program_id = bits.ReadBits(16);
                if (bits.ReadBit()) {
                    for (unsigned int i = 0; i < 16; i ++) {
                        m_Dsi.d.v1.program_uuid[i] = bits.ReadBits(8);
                    }
                }
            }
        }
        m_Dsi.d.v1.ac4_bitrate_dsi.bit_rate_mode      = bits.ReadBits(2);
        m_Dsi.d.v1.ac4_bitrate_dsi.bit_rate           = bits.ReadBits(32);
        m_Dsi.d.v1.ac4_bitrate_dsi.bit_rate_precision = bits.ReadBits(32);

        // byte align
        if (bits.GetBitsRead() % 8) {
            bits.SkipBits(8 - (bits.GetBitsRead() % 8));
        }
        
        // parse the presentations
        m_Dsi.d.v1.presentations = new Ac4Dsi::PresentationV1[m_Dsi.d.v1.n_presentations];
        AP4_SetMemory(m_Dsi.d.v1.presentations, 0, m_Dsi.d.v1.n_presentations * sizeof(m_Dsi.d.v1.presentations[0]));
        for (unsigned int i = 0; i < m_Dsi.d.v1.n_presentations; i++) {
            Ac4Dsi::PresentationV1& presentation = m_Dsi.d.v1.presentations[i];
            
            presentation.presentation_version = bits.ReadBits(8);
            unsigned int pres_bytes           = bits.ReadBits(8);
            if (pres_bytes == 255) {
                pres_bytes += bits.ReadBits(16);
            }
            unsigned int bits_read_before_presentation = bits.GetBitsRead();
            if (presentation.presentation_version == 0) {
                presentation.d.v0.presentation_config = bits.ReadBits(5);
                //bool b_add_emdf_substreams = false;
                if (presentation.d.v0.presentation_config == 0x06) {
                    //b_add_emdf_substreams = true;
                } else {
                    presentation.d.v0.mdcompat = bits.ReadBits(3);
                    if (bits.ReadBit()) { // b_presentation_id
                        presentation.d.v0.presentation_id = bits.ReadBits(5);
                    }
                    presentation.d.v0.dsi_frame_rate_multiply_info = bits.ReadBits(2);
                    presentation.d.v0.presentation_emdf_version    = bits.ReadBits(5);
                    presentation.d.v0.presentation_key_id          = bits.ReadBits(10);
                    presentation.d.v0.presentation_channel_mask    = bits.ReadBits(24);
                }
                // some fields skipped
                if (bits.GetBitsRead() % 8) {
                    bits.SkipBits(8 - (bits.GetBitsRead() % 8));
                }
            } else if (presentation.presentation_version == 1 || presentation.presentation_version == 2) {
                presentation.d.v1.presentation_config_v1 = bits.ReadBits(5);
                if (presentation.d.v1.presentation_config_v1 == 0x06) {
                    presentation.d.v1.b_add_emdf_substreams = 1;
                } else {
                    presentation.d.v1.mdcompat = bits.ReadBits(3);
                    presentation.d.v1.b_presentation_id = bits.ReadBit();
                    if (presentation.d.v1.b_presentation_id) { 
                        presentation.d.v1.presentation_id = bits.ReadBits(5);
                    }
                    presentation.d.v1.dsi_frame_rate_multiply_info = bits.ReadBits(2);
                    presentation.d.v1.dsi_frame_rate_fraction_info = bits.ReadBits(2);
                    presentation.d.v1.presentation_emdf_version    = bits.ReadBits(5);
                    presentation.d.v1.presentation_key_id          = bits.ReadBits(10);
                    presentation.d.v1.b_presentation_channel_coded = bits.ReadBit();
                    if (presentation.d.v1.b_presentation_channel_coded) {
                        presentation.d.v1.dsi_presentation_ch_mode = bits.ReadBits(5);
                        if (presentation.d.v1.dsi_presentation_ch_mode == 11 ||
                            presentation.d.v1.dsi_presentation_ch_mode == 12 ||
                            presentation.d.v1.dsi_presentation_ch_mode == 13 ||
                            presentation.d.v1.dsi_presentation_ch_mode == 14) {
                            presentation.d.v1.pres_b_4_back_channels_present = bits.ReadBit();
                            presentation.d.v1.pres_top_channel_pairs         = bits.ReadBits(2);
                        }
                        presentation.d.v1.presentation_channel_mask_v1 = bits.ReadBits(24);
                    }else {
                        presentation.d.v1.presentation_channel_mask_v1 = 0x800000;
                    }
                    presentation.d.v1.b_presentation_core_differs = bits.ReadBit();
                    if (presentation.d.v1.b_presentation_core_differs) {
                        presentation.d.v1.b_presentation_core_channel_coded = bits.ReadBit();
                        if (presentation.d.v1.b_presentation_core_channel_coded) {
                            presentation.d.v1.dsi_presentation_channel_mode_core = bits.ReadBits(2);
                        }
                    }
                    presentation.d.v1.b_presentation_filter = bits.ReadBit();
                    if (presentation.d.v1.b_presentation_filter) {
                        presentation.d.v1.b_enable_presentation = bits.ReadBit();
                        presentation.d.v1.n_filter_bytes = bits.ReadBits(8);
                        for (int idx = 0; idx < presentation.d.v1.n_filter_bytes; idx ++) { bits.SkipBits(8); }
                    }
                    bool parse_substream_groups = true;
                    if (presentation.d.v1.presentation_config_v1 == 0x1f){
                        presentation.d.v1.n_substream_groups = 1;
                    } else {
                        presentation.d.v1.b_multi_pid = bits.ReadBit();
                        switch (presentation.d.v1.presentation_config_v1){
                            case 0:
                            case 1:
                            case 2:
                                presentation.d.v1.n_substream_groups = 2;
                                break;
                            case 3:
                            case 4:
                                presentation.d.v1.n_substream_groups = 3;
                                break;
                            case 5:
                                presentation.d.v1.n_substream_groups =  bits.ReadBits(3) + 2;
                                break;
                            default:
                                parse_substream_groups = false;
                                presentation.d.v1.n_skip_bytes = bits.ReadBits(7);
                                for (int idx = 0; idx < presentation.d.v1.n_skip_bytes; idx ++) { bits.SkipBits(8); }
                        }
                        
                    }
                    // Start to parse substream_groups
                    if (parse_substream_groups) {
                        presentation.d.v1.substream_groups = new Ac4Dsi::SubStreamGroupV1[presentation.d.v1.n_substream_groups];
                        AP4_SetMemory(presentation.d.v1.substream_groups, 0, presentation.d.v1.n_substream_groups * sizeof(presentation.d.v1.substream_groups[0]));
                        for (int cnt = 0; cnt < presentation.d.v1.n_substream_groups; cnt ++){
                             Ac4Dsi::SubStreamGroupV1& substreamgroup = presentation.d.v1.substream_groups[cnt];
                             substreamgroup.d.v1.b_substreams_present = bits.ReadBit();
                             substreamgroup.d.v1.b_hsf_ext            = bits.ReadBit();
                             substreamgroup.d.v1.b_channel_coded      = bits.ReadBit();
                             substreamgroup.d.v1.n_substreams         = bits.ReadBits(8);

                             substreamgroup.d.v1.substreams = new Ac4Dsi::SubStream[substreamgroup.d.v1.n_substreams];
                             AP4_SetMemory(substreamgroup.d.v1.substreams, 0, substreamgroup.d.v1.n_substreams * sizeof(substreamgroup.d.v1.substreams[0]));
                             for (int sus = 0; sus < substreamgroup.d.v1.n_substreams; sus ++){
                                 Ac4Dsi::SubStream& substream = substreamgroup.d.v1.substreams[sus];
                                 substream.dsi_sf_multiplier = bits.ReadBits(2);
                                 substream.b_substream_bitrate_indicator = bits.ReadBit();
                                 if (substream.b_substream_bitrate_indicator) {
                                    substream.substream_bitrate_indicator = bits.ReadBits(5);
                                 }
                                 if (substreamgroup.d.v1.b_channel_coded){
                                    substream.dsi_substream_channel_mask = bits.ReadBits(24);
                                 } else {
                                    substream.b_ajoc = bits.ReadBit();
                                    if (substream.b_ajoc) {
                                        substream.b_static_dmx = bits.ReadBit();
                                        if (substream.b_static_dmx == 0) {
                                            substream.n_dmx_objects_minus1 = bits.ReadBits(4);
                                        }
                                        substream.n_umx_objects_minus1 = bits.ReadBits(6);
                                    }
                                    substream.b_substream_contains_bed_objects = bits.ReadBit();
                                    substream.b_substream_contains_dynamic_objects = bits.ReadBit();
                                    substream.b_substream_contains_ISF_objects = bits.ReadBit();
                                    bits.SkipBit(); // reserved
                                }
                             }
                             substreamgroup.d.v1.b_content_type = bits.ReadBit();
                             if (substreamgroup.d.v1.b_content_type) {
                                 substreamgroup.d.v1.content_classifier = bits.ReadBits(3);
                                 substreamgroup.d.v1.b_language_indicator = bits.ReadBit();
                                 if (substreamgroup.d.v1.b_language_indicator) {
                                     substreamgroup.d.v1.n_language_tag_bytes = bits.ReadBits(6);
                                     for (int l = 0; l < substreamgroup.d.v1.n_language_tag_bytes; l++) {
                                         substreamgroup.d.v1.language_tag_bytes[l] = bits.ReadBits(8);
                                     }
                                 }
                             }
                        }
                    }
                    presentation.d.v1.b_pre_virtualized = bits.ReadBit();
                    presentation.d.v1.b_add_emdf_substreams = bits.ReadBit();
                }
                if (presentation.d.v1.b_add_emdf_substreams) {
                    presentation.d.v1.n_add_emdf_substreams = bits.ReadBits(7);
                    for (int j = 0; j < presentation.d.v1.n_add_emdf_substreams; j ++) {
                        presentation.d.v1.substream_emdf_version[j] = bits.ReadBits(5);
                        presentation.d.v1.substream_key_id[j] = bits.ReadBits(10);
                    }
                }
                presentation.d.v1.b_presentation_bitrate_info = bits.ReadBit();
                if (presentation.d.v1.b_presentation_bitrate_info) {
                    presentation.d.v1.ac4_bitrate_dsi.bit_rate_mode      = bits.ReadBits(2);
                    presentation.d.v1.ac4_bitrate_dsi.bit_rate           = bits.ReadBits(32);
                    presentation.d.v1.ac4_bitrate_dsi.bit_rate_precision = bits.ReadBits(32);
                }
                presentation.d.v1.b_alternative = bits.ReadBit();
                if (presentation.d.v1.b_alternative) {
                    // byte align
                    if (bits.GetBitsRead() % 8) {
                        bits.SkipBits(8 - (bits.GetBitsRead() % 8));
                    }

                    presentation.d.v1.alternative_info.name_len = bits.ReadBits(16);
                    for (int n = 0; n < presentation.d.v1.alternative_info.name_len; n++) { 
                        presentation.d.v1.alternative_info.presentation_name[n] = bits.ReadBits(8);
                    }
                    presentation.d.v1.alternative_info.n_targets = bits.ReadBits(5);
                    for (int t = 0; t < presentation.d.v1.alternative_info.n_targets; t++ ){
                        presentation.d.v1.alternative_info.target_md_compat[t] = bits.ReadBits(3);
                        presentation.d.v1.alternative_info.target_device_category[t] = bits.ReadBits(8); 
                    }
                }
                // byte align
                if (bits.GetBitsRead() % 8) {
                    bits.SkipBits(8 - (bits.GetBitsRead() % 8));
                }
                presentation.d.v1.de_indicator = bits.ReadBit();
                presentation.d.v1.dolby_atmos_indicator = bits.ReadBit();
                bits.SkipBits(4);
                presentation.d.v1.b_extended_presentation_id = bits.ReadBit();
                if (presentation.d.v1.b_extended_presentation_id){
                    presentation.d.v1.extended_presentation_id = bits.ReadBits(9);
                } else {
                    bits.SkipBit();
                }
            }
            unsigned int bits_read_after_presentation = bits.GetBitsRead();
            unsigned int presentation_bytes = (bits_read_after_presentation - bits_read_before_presentation) / 8;
            if (pres_bytes < presentation_bytes) {
                break;
            }
            unsigned int skip_bytes = pres_bytes - presentation_bytes;
            for (unsigned int skip = 0; skip < skip_bytes; skip++) {
                bits.SkipBits(8);
            }
        }
        
        // fill in computed fields
        m_Dsi.d.v1.fs = m_Dsi.d.v1.fs_index == 0 ? 44100 : 48000;
    } else {
        // not supported
        return;
    }
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::~AP4_Dac4Atom
+---------------------------------------------------------------------*/
AP4_Dac4Atom::~AP4_Dac4Atom()
{
    if (m_Dsi.ac4_dsi_version == 0) {
        //delete[] m_Dsi.d.v0.presentations;
    } else if (m_Dsi.ac4_dsi_version == 1) {
        for (int i = 0; i < m_Dsi.d.v1.n_presentations; i++) {
            for (int j = 0; j < m_Dsi.d.v1.presentations[i].d.v1.n_substream_groups; j++) {
                delete[] m_Dsi.d.v1.presentations[i].d.v1.substream_groups[j].d.v1.substreams;
            }
            delete[] m_Dsi.d.v1.presentations[i].d.v1.substream_groups;
            delete[] m_Dsi.d.v1.presentations[i].d.v1.substream_group_indexs;
        }
        delete[] m_Dsi.d.v1.presentations;
    }
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::WriteFields
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::WriteFields(AP4_ByteStream& stream)
{
    return stream.Write(m_RawBytes.GetData(), m_RawBytes.GetDataSize());
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::GetCodecString
+---------------------------------------------------------------------*/
void
AP4_Dac4Atom::GetCodecString(AP4_String& codec)
{
    AP4_UI08 bitstream_version    = 0;
    AP4_UI08 presentation_version = 0;
    AP4_UI08 mdcompat             = 0;
    if (m_Dsi.ac4_dsi_version == 0) {
        bitstream_version = m_Dsi.d.v0.bitstream_version;
    } else if (m_Dsi.ac4_dsi_version == 1) {
        bitstream_version = m_Dsi.d.v1.bitstream_version;
        if (m_Dsi.d.v1.n_presentations) {
            presentation_version = m_Dsi.d.v1.presentations[0].presentation_version;
            if (presentation_version == 0) {
                mdcompat = m_Dsi.d.v1.presentations[0].d.v0.mdcompat;
            } else if (presentation_version == 1 || presentation_version == 2) {
                mdcompat = m_Dsi.d.v1.presentations[0].d.v1.mdcompat;
                for (int idx = 0; idx < m_Dsi.d.v1.n_presentations; idx ++){
                    if (mdcompat > m_Dsi.d.v1.presentations[idx].d.v1.mdcompat) {
                        mdcompat = m_Dsi.d.v1.presentations[idx].d.v1.mdcompat;
                    }
                }   
            }
        }
    }
    char string[64];
    AP4_FormatString(string,
                     sizeof(string),
                     "ac-4.%02x.%02x.%02x",
                     bitstream_version,
                     presentation_version,
                     mdcompat);
    codec = string;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::InspectFields
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::InspectFields(AP4_AtomInspector& inspector)
{
    inspector.AddField("ac4_dsi_version",    m_Dsi.ac4_dsi_version);
    if (m_Dsi.ac4_dsi_version == 0) {
        inspector.AddField("bitstream_version",  m_Dsi.d.v0.bitstream_version);
        inspector.AddField("fs_index",           m_Dsi.d.v0.fs_index);
        inspector.AddField("fs",                 m_Dsi.d.v0.fs);
        inspector.AddField("frame_rate_index",   m_Dsi.d.v0.frame_rate_index);
    } else if (m_Dsi.ac4_dsi_version == 1) {
        inspector.AddField("bitstream_version",  m_Dsi.d.v1.bitstream_version);
        inspector.AddField("fs_index",           m_Dsi.d.v1.fs_index);
        inspector.AddField("fs",                 m_Dsi.d.v1.fs);
        inspector.AddField("frame_rate_index",   m_Dsi.d.v1.frame_rate_index);
        if (m_Dsi.d.v1.bitstream_version > 1) {
            inspector.AddField("short_program_id", m_Dsi.d.v1.short_program_id);
            inspector.AddField("program_uuid",     m_Dsi.d.v1.program_uuid, 16, AP4_AtomInspector::HINT_HEX);
        }
        inspector.AddField("bit_rate_mode",      m_Dsi.d.v1.ac4_bitrate_dsi.bit_rate_mode);
        inspector.AddField("bit_rate",           m_Dsi.d.v1.ac4_bitrate_dsi.bit_rate);
        inspector.AddField("bit_rate_precision", m_Dsi.d.v1.ac4_bitrate_dsi.bit_rate_precision);
        
        for (unsigned int i = 0; i < m_Dsi.d.v1.n_presentations; i++) {
            Ac4Dsi::PresentationV1& presentation = m_Dsi.d.v1.presentations[i];
            char field_name[64];
            AP4_FormatString(field_name, sizeof(field_name), "[%02d].presentation_version", i);
            inspector.AddField(field_name, presentation.presentation_version);
            if (presentation.presentation_version == 0) {
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].presentation_config", i);
                inspector.AddField(field_name, presentation.d.v0.presentation_config);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].mdcompat", i);
                inspector.AddField(field_name, presentation.d.v0.mdcompat);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].presentation_id", i);
                inspector.AddField(field_name, presentation.d.v0.presentation_id);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].dsi_frame_rate_multiply_info", i);
                inspector.AddField(field_name, presentation.d.v0.dsi_frame_rate_multiply_info);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].presentation_emdf_version", i);
                inspector.AddField(field_name, presentation.d.v0.presentation_emdf_version);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].presentation_key_id", i);
                inspector.AddField(field_name, presentation.d.v0.presentation_key_id);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].presentation_channel_mask", i);
                inspector.AddField(field_name, presentation.d.v0.presentation_channel_mask, AP4_AtomInspector::HINT_HEX);
            } else if (presentation.presentation_version == 1 || presentation.presentation_version == 2) {
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].presentation_config_v1", i);
                inspector.AddField(field_name, presentation.d.v1.presentation_config_v1);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].mdcompat", i);
                inspector.AddField(field_name, presentation.d.v1.mdcompat);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].presentation_id", i);
                inspector.AddField(field_name, presentation.d.v1.presentation_id);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].dsi_frame_rate_multiply_info", i);
                inspector.AddField(field_name, presentation.d.v1.dsi_frame_rate_multiply_info);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].dsi_frame_rate_fraction_info", i);
                inspector.AddField(field_name, presentation.d.v1.dsi_frame_rate_fraction_info);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].presentation_emdf_version", i);
                inspector.AddField(field_name, presentation.d.v1.presentation_emdf_version);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].presentation_key_id", i);
                inspector.AddField(field_name, presentation.d.v1.presentation_key_id);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].b_presentation_channel_coded", i);
                inspector.AddField(field_name, presentation.d.v1.b_presentation_channel_coded);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].dsi_presentation_ch_mode", i);
                inspector.AddField(field_name, presentation.d.v1.dsi_presentation_ch_mode);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].pres_b_4_back_channels_present", i);
                inspector.AddField(field_name, presentation.d.v1.pres_b_4_back_channels_present);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].pres_top_channel_pairs", i);
                inspector.AddField(field_name, presentation.d.v1.pres_top_channel_pairs);
                AP4_FormatString(field_name, sizeof(field_name), "[%02d].presentation_channel_mask_v1", i);
                inspector.AddField(field_name, presentation.d.v1.presentation_channel_mask_v1, AP4_AtomInspector::HINT_HEX);
            }
        }
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::Ac4BitrateDsi::WriteBitrateDsi
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::Ac4BitrateDsi::WriteBitrateDsi(AP4_BitWriter &bits)
{
    bits.Write(bit_rate_mode, 2);
    bits.Write(bit_rate, 32);
    bits.Write(bit_rate_precision, 32);
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::Ac4AlternativeInfo::WriteAlternativeInfo
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::Ac4Dsi::Ac4AlternativeInfo::WriteAlternativeInfo(AP4_BitWriter &bits)
{
    bits.Write(name_len, 16);
    for (unsigned int l = 0; l < name_len; l ++) {
        bits.Write(presentation_name[l], 8);
    }
    bits.Write(n_targets, 5);
    for (unsigned int t = 0; t < n_targets; t ++) {
        bits.Write(target_md_compat[t], 3);
        bits.Write(target_device_category[t], 8);
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::ParseSubstreamInfoChan
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::Ac4Dsi::SubStream::ParseSubstreamInfoChan(AP4_BitReader &bits, 
                                                        unsigned int  presentation_version, 
                                                        unsigned char defalut_presentation_flag,
                                                        unsigned int  fs_idx,
                                                        unsigned int  &speaker_index_mask,
                                                        unsigned int  frame_rate_factor,
                                                        unsigned int  b_substreams_present,
                                                        unsigned char &dolby_atmos_indicator)
{
    ch_mode = ParseChMode(bits, presentation_version, dolby_atmos_indicator); 
    int substreamSpeakerGroupIndexMask = AC4_SPEAKER_GROUP_INDEX_MASK_BY_CH_MODE[ch_mode];
    if ((ch_mode >= CH_MODE_7_0_4) && (ch_mode <= CH_MODE_9_1_4)) {
        if (!(b_4_back_channels_present = bits.ReadBit())) {    // b_4_back_channels_present false
            substreamSpeakerGroupIndexMask &= ~0x8;             // Remove back channels (Lb,Rb) from mask
        }
        if (!(b_centre_present = bits.ReadBit())) {             // b_centre_present false
            substreamSpeakerGroupIndexMask &= ~0x2;             // Remove centre channel (C) from mask
        }
        switch (top_channels_present = bits.ReadBits(2)) {      // top_channels_present
            case 0:
                substreamSpeakerGroupIndexMask &= ~0x30;        // Remove top channels (Tfl,Tfr,Tbl,Tbr) from mask
                break;
            case 1:
            case 2:
                substreamSpeakerGroupIndexMask &= ~0x30;        // Remove top channels (Tfl,Tfr,Tbl,Tbr) from mask
                substreamSpeakerGroupIndexMask |=  0x80;        // Add top channels (Tl, Tr) from mask;
                break;
        }
    }
    dsi_substream_channel_mask = substreamSpeakerGroupIndexMask;
    // Only combine channel masks of substream groups that are part of the first/default presentation
    if (defalut_presentation_flag) {
        speaker_index_mask |= substreamSpeakerGroupIndexMask;
    }

    ParseDsiSfMutiplier(bits, fs_idx);

    b_substream_bitrate_indicator = bits.ReadBit();
    if (b_substream_bitrate_indicator) {    // b_bitrate_info
        // bitrate_indicator()
        ParseBitrateIndicator(bits);
    }

    if (ch_mode >= CH_MODE_70_52 && ch_mode <= CH_MODE_71_322) {
        bits.ReadBit();                     // add_ch_base
    }
    for (unsigned int i = 0; i < frame_rate_factor; i++) {
        bits.ReadBit();                     // b_audio_ndot
    }

    ParseSubstreamIdxInfo(bits, b_substreams_present);

    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::ParseSubStreamInfoAjoc
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::Ac4Dsi::SubStream::ParseSubStreamInfoAjoc(AP4_BitReader &bits, 
                                                        unsigned int  &channel_count,
                                                        unsigned char defalut_presentation_flag,
                                                        unsigned int  fs_idx,
                                                        unsigned int  frame_rate_factor,
                                                        unsigned int  b_substreams_present)
{
    b_lfe = bits.ReadBit();     // b_lfe
    b_static_dmx = bits.ReadBit();
    if (b_static_dmx) {         // b_static_dmx
        if (defalut_presentation_flag) {
            channel_count += 5;
        }
    } else {
        n_dmx_objects_minus1 = bits.ReadBits(4);
        unsigned int nFullbandDmxSignals = n_dmx_objects_minus1 + 1;
        BedDynObjAssignment(bits, nFullbandDmxSignals, false);
        if (defalut_presentation_flag) {
            channel_count += nFullbandDmxSignals;
        }
    }
    if (bits.ReadBit()) {       // b_oamd_common_data_present
        // oamd_common_data()
        ParseOamdCommonData(bits);
    }
    n_umx_objects_minus1 = bits.ReadBits(4);
    int nFullbandUpmixSignals = n_umx_objects_minus1 + 1;
    if (nFullbandUpmixSignals == 16) {
        nFullbandUpmixSignals += AP4_Ac4VariableBits(bits, 3);
    }

    BedDynObjAssignment(bits, nFullbandUpmixSignals, true);
    ParseDsiSfMutiplier(bits, fs_idx);

    b_substream_bitrate_indicator = bits.ReadBit();
    if (b_substream_bitrate_indicator) {    // b_bitrate_info
        ParseBitrateIndicator(bits);
    }
    for (unsigned int i = 0; i< frame_rate_factor; i++) {
        bits.ReadBit();                     // b_audio_ndot
    }
    ParseSubstreamIdxInfo(bits, b_substreams_present);
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::ParseSubstreamInfoObj
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::Ac4Dsi::SubStream::ParseSubstreamInfoObj(AP4_BitReader &bits,
                                                       unsigned int  &channel_count,
                                                       unsigned char defalut_presentation_flag,
                                                       unsigned int  fs_idx,
                                                       unsigned int  frame_rate_factor,
                                                       unsigned int  b_substreams_present)
{
    int nObjectsCode = bits.ReadBits(3);
    if (defalut_presentation_flag) {
        switch (nObjectsCode) {
            case 0:
            case 1:
            case 2:
            case 3:
                channel_count += nObjectsCode;
                break;
            case 4:
                channel_count += 5;
                break;
            default:
                break;
                //TODO: Error
        }
    }
    if (bits.ReadBit()) {                       // b_dynamic_objects
        b_substream_contains_dynamic_objects = 1;
        unsigned int b_lfe = bits.ReadBit();    // b_lfe
        if (defalut_presentation_flag && b_lfe) { channel_count += 1; }
    }else {
        if (bits.ReadBit()) {                   // b_bed_objects
            b_substream_contains_bed_objects = 1;
            if (bits.ReadBit()) {               // b_bed_start
                if (bits.ReadBit()) {           // b_ch_assign_code
                    bits.ReadBits(3);           // bed_chan_assign_code
                }
                else {
                    if (bits.ReadBit()) {       // b_nonstd_bed_channel_assignment
                        bits.ReadBits(17);      // nonstd_bed_channel_assignment_mask
                    }
                    else {
                        bits.ReadBits(10);      // std_bed_channel_assignment_mask
                    }
                }
            }
        }else {
            if (bits.ReadBit()) {               // b_isf
                b_substream_contains_ISF_objects = 1;
                if (bits.ReadBit()) {           // b_isf_start
                    bits.ReadBits(3);           // isf_config
                }
            }else {
                int resBytes = bits.ReadBits(4);
                bits.ReadBits(resBytes * 8);
            }
        }
    }

    ParseDsiSfMutiplier(bits, fs_idx);

    b_substream_bitrate_indicator = bits.ReadBit();
    if (b_substream_bitrate_indicator) {        // b_bitrate_info
        ParseBitrateIndicator(bits);
    }
    for (unsigned int i = 0; i< frame_rate_factor; i++) {
        bits.ReadBit();                         // b_audio_ndot
    }
    ParseSubstreamIdxInfo(bits, b_substreams_present);
    return AP4_SUCCESS; 
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::WriteSubstreamDsi
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::SubStream::WriteSubstreamDsi(AP4_BitWriter &bits, unsigned char b_channel_coded)
{
    bits.Write(dsi_sf_multiplier, 2);
    bits.Write(b_substream_bitrate_indicator, 1);
    if (b_substream_bitrate_indicator == 1) {
        bits.Write(substream_bitrate_indicator, 5);
    }
    if (b_channel_coded == 1) {
        bits.Write(dsi_substream_channel_mask, 24);
    }else {
        bits.Write(b_ajoc, 1);
        if (b_ajoc == 1) {
            bits.Write(b_static_dmx, 1);
            if (b_static_dmx == 0) {
                bits.Write(n_dmx_objects_minus1, 4);
            }
            bits.Write(n_umx_objects_minus1, 6);
        }
        // substream composition information
        bits.Write(b_substream_contains_bed_objects, 1);
        bits.Write(b_substream_contains_dynamic_objects, 1);
        bits.Write(b_substream_contains_ISF_objects, 1);
        bits.Write(0, 1); //reserved bit
    }
    return AP4_SUCCESS; 
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::ParseChMode
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::SubStream::ParseChMode(AP4_BitReader &bits, int presentationVersion, unsigned char &dolby_atmos_indicator) 
{
    int channel_mode_code = 0;

    channel_mode_code = bits.ReadBit();
    if (channel_mode_code == 0) {   // Mono 0b0
        return CH_MODE_MONO;
    }
    channel_mode_code = (channel_mode_code << 1) | bits.ReadBit();
    if (channel_mode_code == 2) {   // Stereo  0b10
        return CH_MODE_STEREO;
    }
    channel_mode_code = (channel_mode_code << 2) | bits.ReadBits(2);
    switch (channel_mode_code) {
        case 12:                    // 3.0 0b1100
            return CH_MODE_3_0;
        case 13:                    // 5.0 0b1101
            return CH_MODE_5_0;
        case 14:                    // 5.1 0b1110
            return CH_MODE_5_1;
    }
    channel_mode_code = (channel_mode_code << 3) | bits.ReadBits(3);
    switch (channel_mode_code) {
        case 120:                   // 0b1111000
            if (presentationVersion == 2) { // IMS (all content)
                return CH_MODE_STEREO;
            }
            else {                  // 7.0: 3/4/0
                return CH_MODE_70_34;
            }
        case 121:                   // 0b1111001
            if (presentationVersion == 2) { // IMS (Atmos content)
                dolby_atmos_indicator |= 1;
                return CH_MODE_STEREO;
            }
            else {                  // 7.1: 3/4/0.1
                return CH_MODE_71_34;
            }
        case 122:                   // 7.0: 5/2/0   0b1111010
            return CH_MODE_70_52;
        case 123:                   // 7.1: 5/2/0.1 0b1111011
            return CH_MODE_71_52;
        case 124:                   // 7.0: 3/2/2   0b1111100
            return CH_MODE_70_322; 
        case 125:                   // 7.1: 3/2/2.1 0b1111101
            return CH_MODE_71_322;
    }
    channel_mode_code = (channel_mode_code << 1) | bits.ReadBit();
    switch (channel_mode_code) {
        case 252:                   // 7.0.4 0b11111100
            return CH_MODE_7_0_4;
        case 253:                   // 7.1.4 0b11111101
            return CH_MODE_7_1_4;
    }
    channel_mode_code = (channel_mode_code << 1) | bits.ReadBit();
    switch (channel_mode_code) {
        case 508:                   // 9.0.4 0b111111100
            return CH_MODE_9_0_4;
        case 509:                   // 9.1.4 0b111111101
            return CH_MODE_9_1_4;
        case 510:                   // 22.2 0b111111110
            return CH_MODE_22_2;
        case 511:                   // Reserved, escape value 0b111111111
        default:
            AP4_Ac4VariableBits(bits, 2);
            return CH_MODE_RESERVED;
    }
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::ParseDsiSfMutiplier
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::SubStream::ParseDsiSfMutiplier(AP4_BitReader &bits,  
                                                     unsigned int  fs_idx)
{
    if (fs_idx == 1) {
        if (bits.ReadBit()) {                       // b_sf_miultiplier
            // 96 kHz or 192 kHz
            dsi_sf_multiplier = bits.ReadBit() + 1; // sf_multiplier
        }else{
            // 48 kHz
            dsi_sf_multiplier = 0; 
        }
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::BedDynObjAssignment
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::Ac4Dsi::SubStream::BedDynObjAssignment(AP4_BitReader &bits, 
                                                     unsigned int  nSignals,
                                                     bool          is_upmix) 
{
    if (!bits.ReadBit()) {      // b_dyn_objects_only
        if (bits.ReadBit()) {   // b_isf
            unsigned char isf_config = bits.ReadBits(3); 
            if (is_upmix) {
                b_substream_contains_ISF_objects |= 1;
                if (nSignals > ObjNumFromIsfConfig(isf_config)) {
                    b_substream_contains_dynamic_objects |= 1;
                }
            }
        }else {
            if (bits.ReadBit()) {           // b_ch_assign_code
                unsigned char bed_chan_assign_code = bits.ReadBits(3); 
                if (is_upmix) {
                    b_substream_contains_bed_objects |= 1;
                    if (nSignals > BedNumFromAssignCode(bed_chan_assign_code)) {
                        b_substream_contains_dynamic_objects |= 1;
                    }
                }
            }else {
                if (bits.ReadBit()) {       // b_chan_assign_mask
                    if (bits.ReadBit()) {   // b_nonstd_bed_channel_assignment
                        unsigned int nonstd_bed_channel_assignment_mask = bits.ReadBits(17); 
                        if (is_upmix) {
                            unsigned int bed_num = BedNumFromNonStdMask(nonstd_bed_channel_assignment_mask);
                            if (bed_num > 0) { b_substream_contains_bed_objects |= 1; }
                            if (nSignals > bed_num) {
                                b_substream_contains_dynamic_objects |= 1;
                            }
                        }
                    }
                    else {
                        unsigned int std_bed_channel_assignment_mask = bits.ReadBits(10);
                        if (is_upmix) {
                            unsigned int bed_num = BedNumFromStdMask(std_bed_channel_assignment_mask);
                            if (bed_num > 0) { b_substream_contains_bed_objects |= 1; }
                            if (nSignals > bed_num) {
                                b_substream_contains_dynamic_objects |= 1;
                            }
                        }
                    }
                }else {
                    unsigned int nBedSignals = 0;
                    if (nSignals > 1) {
                        int bedChBits = (int) ceil(log((float)nSignals)/log((float)2));
                        nBedSignals = bits.ReadBits(bedChBits) + 1;
                    }
                    else {
                        nBedSignals = 1;
                    }
                    for (unsigned int b = 0; b < nBedSignals; b++) {
                        bits.ReadBits(4);   // nonstd_bed_channel_assignment
                    }
                    if (is_upmix) {
                        b_substream_contains_bed_objects |= 1;
                        if (nSignals > nBedSignals){ b_substream_contains_dynamic_objects |= 1; }
                    }
                }
            }
        }
    }else {
        if (is_upmix) {
            b_substream_contains_dynamic_objects |= 1;
            b_substream_contains_bed_objects     |= 0;
            b_substream_contains_ISF_objects     |= 0;
        }
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::ParseSubstreamIdxInfo
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::SubStream::ParseSubstreamIdxInfo(AP4_BitReader &bits, unsigned int b_substreams_present)
{
    if (b_substreams_present == 1) {
        if (bits.ReadBits(2) == 3) {    // substream_index
            AP4_Ac4VariableBits(bits, 2);
        }
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::ParseBitrateIndicator
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::SubStream::ParseBitrateIndicator(AP4_BitReader &bits)
{
    substream_bitrate_indicator = bits.ReadBits(3);
    if ((substream_bitrate_indicator & 0x1) == 1) { // bitrate_indicator
        substream_bitrate_indicator = (substream_bitrate_indicator << 2) + bits.ReadBits(2);
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::ParseOamdCommonData
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::Ac4Dsi::SubStream::ParseOamdCommonData(AP4_BitReader &bits)
{
    if (bits.ReadBit() == 0) {  // b_default_screen_size_ratio
        bits.ReadBits(5);       // master_screen_size_ratio_code
    }
    bits.ReadBit();             // b_bed_object_chan_distribute
    if (bits.ReadBit()) {       // b_additional_data
        int addDataBytes = bits.ReadBit() + 1;
        if (addDataBytes == 2) {
            addDataBytes += AP4_Ac4VariableBits(bits, 2);
        }
        unsigned int bits_used = Trim(bits);
        bits_used += BedRendeInfo(bits);
        bits.ReadBits(addDataBytes * 8 - bits_used);
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::Trim
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::Ac4Dsi::SubStream::Trim(AP4_BitReader &bits)
{
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::BedRendeInfo
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::Ac4Dsi::SubStream::BedRendeInfo(AP4_BitReader &bits)
{
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::ObjNumFromIsfConfig
+---------------------------------------------------------------------*/
AP4_UI32
AP4_Dac4Atom::Ac4Dsi::SubStream::ObjNumFromIsfConfig(unsigned char isf_config)
{
    unsigned int obj_num = 0;
    switch (isf_config){
        case 0: obj_num = 4 ; break;
        case 1: obj_num = 8 ; break;
        case 2: obj_num = 10; break;
        case 3: obj_num = 14; break;
        case 4: obj_num = 15; break;
        case 5: obj_num = 30; break;
        default: obj_num = 0;
    }
    return obj_num;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::BedNumFromAssignCode
+---------------------------------------------------------------------*/
AP4_UI32
AP4_Dac4Atom::Ac4Dsi::SubStream::BedNumFromAssignCode(unsigned char assign_code)
{
    unsigned int bed_num = 0;
    switch (assign_code){
        case 0: bed_num = 2 ; break;
        case 1: bed_num = 3 ; break;
        case 2: bed_num = 6 ; break;
        case 3: bed_num = 8 ; break;
        case 4: bed_num = 10; break;
        case 5: bed_num = 8 ; break;
        case 6: bed_num = 10; break;
        case 7: bed_num = 12; break;
        default: bed_num = 0;
    }
    return bed_num;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::BedNumFromNonStdMask
+---------------------------------------------------------------------*/
AP4_UI32
AP4_Dac4Atom::Ac4Dsi::SubStream::BedNumFromNonStdMask(unsigned int non_std_mask)
{
    unsigned int bed_num = 0;
    // Table 85: nonstd_bed_channel_assignment AC-4 part-2 v1.2.1
    for (unsigned int idx = 0; idx < 17; idx ++) {
        if ((non_std_mask >> idx) & 0x1){
            bed_num ++;
        }
    }
    return bed_num;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::BedNumFromStdMask
+---------------------------------------------------------------------*/
AP4_UI32
AP4_Dac4Atom::Ac4Dsi::SubStream::BedNumFromStdMask(unsigned int std_mask)
{
    unsigned int bed_num = 0;
    // Table 86 std_bed_channel_assignment_flag[] AC-4 part-2 v1.2.1
    for (unsigned int idx = 0; idx < 10; idx ++) {
        if ((std_mask >> idx) & 0x1){
            if ((idx == 1) || (idx == 2) || (idx == 9)) { bed_num ++;}
            else { bed_num += 2; }
        }
    }
    return bed_num;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStream::GetChModeCore
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::Ac4Dsi::SubStream::GetChModeCore(unsigned char b_channel_coded)
{
    int ch_mode_core = -1;
    if (b_channel_coded == 0 && 
        b_ajoc          == 1 && 
        b_static_dmx    == 1 && 
        b_lfe           == 0){
        ch_mode_core = 3 ;
    }else if (b_channel_coded == 0 && 
              b_ajoc          == 1 && 
              b_static_dmx    == 1 && 
              b_lfe           == 1){
              ch_mode_core = 4 ;
    }else if ((b_channel_coded == 1) && 
              (ch_mode == 11 || ch_mode == 13)){
              ch_mode_core = 5 ;
    }else if ((b_channel_coded == 1) && 
              (ch_mode == 12 || ch_mode == 14)){
              ch_mode_core = 6 ;
    }
    return ch_mode_core;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1::ParseSubstreamGroupInfo
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1::ParseSubstreamGroupInfo(AP4_BitReader& bits,
                                                                unsigned int   bitstream_version,
                                                                unsigned int   presentation_version,
                                                                unsigned char  defalut_presentation_flag,
                                                                unsigned int   frame_rate_factor,
                                                                unsigned int   fs_idx,
                                                                unsigned int&  channel_count,
                                                                unsigned int&  speaker_index_mask,
                                                                unsigned int&  b_obj_or_Ajoc)
{
    d.v1.b_substreams_present = bits.ReadBit();
    d.v1.b_hsf_ext = bits.ReadBit();
    if (bits.ReadBit()) {
        d.v1.n_substreams = 1;
    } else{
        d.v1.n_substreams = bits.ReadBits(2) + 2;
        if (d.v1.n_substreams == 5) {
            d.v1.n_substreams += AP4_Ac4VariableBits(bits, 2);
        }  
    }
    d.v1.substreams = new AP4_Dac4Atom::Ac4Dsi::SubStream[d.v1.n_substreams];
    AP4_SetMemory(d.v1.substreams, 0, d.v1.n_substreams * sizeof(d.v1.substreams[0]));
    d.v1.b_channel_coded = bits.ReadBit();
    if (d.v1.b_channel_coded) {
        for (unsigned int sus = 0; sus < d.v1.n_substreams; sus++) {
            AP4_Dac4Atom::Ac4Dsi::SubStream& substream = d.v1.substreams[sus];
            if (bitstream_version == 1) { 
                bits.ReadBit(); // sus_ver
            }

            // ac4_substream_info_chan()
            substream.ParseSubstreamInfoChan(bits,
                                             presentation_version, 
                                             defalut_presentation_flag,
                                             fs_idx,
                                             speaker_index_mask,
                                             frame_rate_factor,
                                             d.v1.b_substreams_present,
                                             d.v1.dolby_atmos_indicator);
            
            if (d.v1.b_hsf_ext) {
                // ac4_hsf_ext_substream_info()
                ParseHsfExtSubstreamInfo(bits);
            }
        }
    } else {     // b_channel_coded == 0
        b_obj_or_Ajoc = 1;
        if (bits.ReadBit()) {       // b_oamd_substream
                //oamd_substream_info()
            ParseOamdSubstreamInfo(bits);
        }
        for (int sus = 0; sus < d.v1.n_substreams; sus++) {
            AP4_Dac4Atom::Ac4Dsi::SubStream& substream = d.v1.substreams[sus];
            substream.b_ajoc = bits.ReadBit();
            unsigned int local_channel_count = 0;
            if (substream.b_ajoc) { // b_ajoc
                // ac4_substream_info_ajoc()
                substream.ParseSubStreamInfoAjoc(bits,
                                                 local_channel_count,
                                                 defalut_presentation_flag,
                                                 fs_idx,
                                                 frame_rate_factor,
                                                 d.v1.b_substreams_present);

                if (d.v1.b_hsf_ext) {
                    // ac4_hsf_ext_substream_info()
                    ParseHsfExtSubstreamInfo(bits);
                }
            }else {
                // ac4_substream_info_obj()
                substream.ParseSubstreamInfoObj(bits,
                                                local_channel_count,
                                                defalut_presentation_flag,
                                                fs_idx,
                                                frame_rate_factor,
                                                d.v1.b_substreams_present);

                if (d.v1.b_hsf_ext) {
                    // ac4_hsf_ext_substream_info()
                    ParseHsfExtSubstreamInfo(bits);
                }
            }
            if (channel_count < local_channel_count) { channel_count = local_channel_count;}
        }
    }

    d.v1.b_content_type = bits.ReadBit();
    if (d.v1.b_content_type) { 
        // content_type()
        ParseContentType(bits);
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1::WriteSubstreamGroupDsi
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1::WriteSubstreamGroupDsi(AP4_BitWriter &bits)
{
    bits.Write(d.v1.b_substreams_present, 1);
    bits.Write(d.v1.b_hsf_ext, 1);
    bits.Write(d.v1.b_channel_coded, 1);
    bits.Write(d.v1.n_substreams, 8);
    for (unsigned int sg = 0; sg < d.v1.n_substreams; sg++ ){
        d.v1.substreams[sg].WriteSubstreamDsi(bits, d.v1.b_channel_coded);
    }
    WriteContentType(bits);
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1::ParseOamdSubstreamInfo
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1::ParseOamdSubstreamInfo(AP4_BitReader &bits)
{
    bits.ReadBit();                     // b_oamd_ndot
    if (d.v1.b_substreams_present == 1) {
        if (bits.ReadBits(2) == 3) {    // substream_index
            AP4_Ac4VariableBits(bits, 2);
        }
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1::ParseHsfExtSubstreamInfo
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1::ParseHsfExtSubstreamInfo(AP4_BitReader &bits)
{
    if (d.v1.b_substreams_present == 1) {
        if (bits.ReadBits(2) == 3) {    // substream_index
            AP4_Ac4VariableBits(bits, 2);
        }
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1::ParseContentType
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1::ParseContentType(AP4_BitReader &bits)
{
    d.v1.content_classifier = bits.ReadBits(3); // content_classifier
    d.v1.b_language_indicator = bits.ReadBit();
    if (d.v1.b_language_indicator == 1) {       // b_language_indicator
        if (bits.ReadBit()) {                   // b_serialized_language_tag
            bits.ReadBits(17);                  // b_start_tag, language_tag_chunk
        }
        else {
            d.v1.n_language_tag_bytes= bits.ReadBits(6);
            for (unsigned int l = 0; l < d.v1.n_language_tag_bytes; l++){
                d.v1.language_tag_bytes[l] = bits.ReadBits(8);
            }
        }
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1::WriteContentType
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1::WriteContentType(AP4_BitWriter &bits)
{
    bits.Write(d.v1.b_content_type, 1);
    if (d.v1.b_content_type == 1){
        bits.Write(d.v1.content_classifier, 3);
        bits.Write(d.v1.b_language_indicator, 1);
        if (d.v1.b_language_indicator == 1){
            bits.Write(d.v1.n_language_tag_bytes, 6);
            for (unsigned int idx = 0; idx < d.v1.n_language_tag_bytes; idx++ ){
                bits.Write(d.v1.language_tag_bytes[idx], 8);
            }
        }
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::PresentationV1::Parse
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::PresentationV1::ParsePresentationV1Info(AP4_BitReader &bits, 
                                                              unsigned int  bitstream_version,
                                                              unsigned int  frame_rate_idx,
                                                              unsigned int  pres_idx,
                                                              unsigned int  &max_group_index,
                                                              unsigned int  **first_pres_sg_index,
                                                              unsigned int  &first_pres_sg_num)
{
    unsigned int *substreamGroupIndexes = new unsigned int[AP4_AC4_SUBSTREAM_GROUP_NUM];
    unsigned int b_single_substream_group = bits.ReadBit();
    if (b_single_substream_group != 1){
        d.v1.presentation_config_v1 = bits.ReadBits(3);
        if (d.v1.presentation_config_v1 == 7) {
            d.v1.presentation_config_v1 += AP4_Ac4VariableBits(bits, 2);
        }
    }else {
        d.v1.presentation_config_v1 = 0x1f;
    }

    // presentation_version()
    ParsePresentationVersion(bits, bitstream_version);

    AP4_Ac4EmdfInfo tmp_emdf_info;
    if (b_single_substream_group != 1 && d.v1.presentation_config_v1 == 6){
        d.v1.b_add_emdf_substreams = 1;
    }else{
        if (bitstream_version != 1){
            d.v1.mdcompat = bits.ReadBits(3);
        }
        d.v1.b_presentation_id = bits.ReadBit();
        if (d.v1.b_presentation_id) {
            d.v1.presentation_id = AP4_Ac4VariableBits(bits, 2);
        }
        // frame_rate_multiply_info()
        ParseDSIFrameRateMultiplyInfo(bits, frame_rate_idx);

        // frame_rate_fractions_info()
        ParseDSIFrameRateFractionsInfo(bits, frame_rate_idx);

        // emdf_info()
        ParseEmdInfo(bits, tmp_emdf_info);
        d.v1.presentation_emdf_version = tmp_emdf_info.emdf_version;
        d.v1.presentation_key_id = tmp_emdf_info.key_id;
           
        // b_presentation_filter
        d.v1.b_presentation_filter = bits.ReadBit();
        if (d.v1.b_presentation_filter == 1){
            d.v1.b_enable_presentation = bits.ReadBit();
        }
        if (b_single_substream_group == 1){
            substreamGroupIndexes[0] = ParseAc4SgiSpecifier(bits, bitstream_version);
            max_group_index = AP4_SetMaxGroupIndex(substreamGroupIndexes[0], max_group_index);
            d.v1.n_substream_groups = 1;
            d.v1.substream_group_indexs = substreamGroupIndexes;
        }else {
            d.v1.b_multi_pid = bits.ReadBit();
            switch (d.v1.presentation_config_v1){
                case 0: // M&E + D
                case 2: // Main + Assoc
                    substreamGroupIndexes[0] = ParseAc4SgiSpecifier(bits, bitstream_version);
                    substreamGroupIndexes[1] = ParseAc4SgiSpecifier(bits, bitstream_version);
                    max_group_index = AP4_SetMaxGroupIndex(substreamGroupIndexes[0], max_group_index);
                    max_group_index = AP4_SetMaxGroupIndex(substreamGroupIndexes[1], max_group_index);
                    d.v1.n_substream_groups = 2;
                    d.v1.substream_group_indexs = substreamGroupIndexes;
                    break;
                case 1: // Main + DE
                    // shall return same substream group index, need stream to verify
                    substreamGroupIndexes[0] = ParseAc4SgiSpecifier(bits, bitstream_version);
                    substreamGroupIndexes[1] = ParseAc4SgiSpecifier(bits, bitstream_version);
                    max_group_index = AP4_SetMaxGroupIndex(substreamGroupIndexes[0], max_group_index);
                    max_group_index = AP4_SetMaxGroupIndex(substreamGroupIndexes[1], max_group_index);
                    d.v1.n_substream_groups = 2;
                    d.v1.substream_group_indexs = substreamGroupIndexes;
                    break;
                case 3: // M&E + D + Assoc
                    substreamGroupIndexes[0] = ParseAc4SgiSpecifier(bits, bitstream_version);
                    substreamGroupIndexes[1] = ParseAc4SgiSpecifier(bits, bitstream_version);
                    substreamGroupIndexes[2] = ParseAc4SgiSpecifier(bits, bitstream_version);
                    max_group_index = AP4_SetMaxGroupIndex(substreamGroupIndexes[0], max_group_index);
                    max_group_index = AP4_SetMaxGroupIndex(substreamGroupIndexes[1], max_group_index);
                    max_group_index = AP4_SetMaxGroupIndex(substreamGroupIndexes[2], max_group_index);
                    d.v1.n_substream_groups = 3;
                    d.v1.substream_group_indexs = substreamGroupIndexes;
                    break;
                case 4: // Main + DE + Assoc
                    // shall return only two substream group index, need stream to verify
                    substreamGroupIndexes[0] = ParseAc4SgiSpecifier(bits, bitstream_version);
                    substreamGroupIndexes[1] = ParseAc4SgiSpecifier(bits, bitstream_version);
                    substreamGroupIndexes[2] = ParseAc4SgiSpecifier(bits, bitstream_version);
                    max_group_index = AP4_SetMaxGroupIndex(substreamGroupIndexes[0], max_group_index);
                    max_group_index = AP4_SetMaxGroupIndex(substreamGroupIndexes[1], max_group_index);
                    max_group_index = AP4_SetMaxGroupIndex(substreamGroupIndexes[2], max_group_index);
                    d.v1.n_substream_groups = 3;
                    d.v1.substream_group_indexs = substreamGroupIndexes;
                    break;
                case 5:
                    d.v1.n_substream_groups = bits.ReadBits(2) + 2;
                    if (d.v1.n_substream_groups == 5) {
                        d.v1.n_substream_groups += AP4_Ac4VariableBits(bits, 2);
                    }
                    delete[] substreamGroupIndexes;
                    substreamGroupIndexes = new unsigned int[d.v1.n_substream_groups];
                    for (unsigned int sg = 0; sg < d.v1.n_substream_groups; sg ++){
                        substreamGroupIndexes[sg] = ParseAc4SgiSpecifier(bits, bitstream_version);
                        max_group_index = AP4_SetMaxGroupIndex(substreamGroupIndexes[sg], max_group_index);
                    }
                    d.v1.substream_group_indexs = substreamGroupIndexes;
                    break;
                default:
                    // presentation_config_ext_info()
                    ParsePresentationConfigExtInfo(bits, bitstream_version);
                    break;
            }
        }
        // b_pre_virtualized
        d.v1.b_pre_virtualized     = bits.ReadBit();
        d.v1.b_add_emdf_substreams = bits.ReadBit();
        // ac4_presentation_substream_info()
        ParsePresentationSubstreamInfo(bits);
    }

    if (d.v1.b_add_emdf_substreams){
        d.v1.n_add_emdf_substreams = bits.ReadBits(2);
        if (d.v1.n_add_emdf_substreams == 0){
            d.v1.n_add_emdf_substreams = AP4_Ac4VariableBits(bits, 2) + 4;
        }
        for (unsigned int cnt = 0; cnt < d.v1.n_add_emdf_substreams; cnt ++){
            ParseEmdInfo(bits, tmp_emdf_info);
            d.v1.substream_emdf_version[cnt] = tmp_emdf_info.emdf_version;
            d.v1.substream_key_id[cnt]       = tmp_emdf_info.key_id;
        }
    }
    if (pres_idx == 0){
        *first_pres_sg_index = substreamGroupIndexes;
        first_pres_sg_num = d.v1.n_substream_groups;
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::PresentationV1::WritePresentationV1Dsi
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::PresentationV1::WritePresentationV1Dsi(AP4_BitWriter &bits)
{
    bits.Write(d.v1.presentation_config_v1, 5);
    if (d.v1.presentation_config_v1 == 0x06) {
        d.v1.b_add_emdf_substreams = 1;
    }else {
        bits.Write(d.v1.mdcompat, 3);
        bits.Write(d.v1.b_presentation_id, 1);
        if (d.v1.b_presentation_id == 1){
            bits.Write(d.v1.presentation_id, 5);
        }
        bits.Write(d.v1.dsi_frame_rate_multiply_info, 2);
        bits.Write(d.v1.dsi_frame_rate_fraction_info, 2);
        bits.Write(d.v1.presentation_emdf_version, 5);
        bits.Write(d.v1.presentation_key_id, 10);
        d.v1.b_presentation_channel_coded = ((GetPresentationChMode() == -1) ? 0: 1);
        bits.Write(d.v1.b_presentation_channel_coded, 1);
        if (d.v1.b_presentation_channel_coded == 1) {
            d.v1.dsi_presentation_ch_mode = GetPresentationChMode();
            bits.Write(d.v1.dsi_presentation_ch_mode, 5);
            if (d.v1.dsi_presentation_ch_mode >= 11 && d.v1.dsi_presentation_ch_mode <= 14){
                GetPresB4BackChannelsPresent();
                GetPresTopChannelPairs();
                bits.Write(d.v1.pres_b_4_back_channels_present, 1);
                bits.Write(d.v1.pres_top_channel_pairs, 2);
                if (d.v1.pres_top_channel_pairs) {
                    d.v1.dolby_atmos_indicator = 1;
                }
            }
            d.v1.presentation_channel_mask_v1 = GetPresentationChannelMask();
            bits.Write(d.v1.presentation_channel_mask_v1, 24);
        }
        int pres_ch_mode_core = GetBPresentationCoreDiffers();
        bits.Write(d.v1.b_presentation_core_differs = ((pres_ch_mode_core == -1) ? 0: 1), 1);
        if (d.v1.b_presentation_core_differs == 1) {
            bits.Write(d.v1.b_presentation_core_channel_coded = ((pres_ch_mode_core == -1) ? 0: 1), 1);
            if (d.v1.b_presentation_core_channel_coded == 1) {
                bits.Write(d.v1.dsi_presentation_channel_mode_core = (pres_ch_mode_core - 3), 2);
            }
        }
        bits.Write(d.v1.b_presentation_filter, 1);
        if (d.v1.b_presentation_filter == 1) {
            bits.Write(d.v1.b_enable_presentation, 1);
            // Encoders implemented according to the present document shall write the value 0
            d.v1.n_filter_bytes = 0; 
            bits.Write(d.v1.n_filter_bytes, 8);
            //TODO: filter_data if n_filter_bytes > 0
        }

        if (d.v1.presentation_config_v1 == 0x1f){
            d.v1.substream_groups[0].WriteSubstreamGroupDsi(bits);
        }else {
            bits.Write(d.v1.b_multi_pid, 1);
            if (d.v1.presentation_config_v1 <= 2){
                d.v1.substream_groups[0].WriteSubstreamGroupDsi(bits);
                d.v1.substream_groups[1].WriteSubstreamGroupDsi(bits);
            }
            if (d.v1.presentation_config_v1 >= 3 && d.v1.presentation_config_v1 <= 4){
                d.v1.substream_groups[0].WriteSubstreamGroupDsi(bits);
                d.v1.substream_groups[1].WriteSubstreamGroupDsi(bits);
                d.v1.substream_groups[2].WriteSubstreamGroupDsi(bits);
            }
            if (d.v1.presentation_config_v1 == 5) {
                bits.Write(d.v1.n_substream_groups - 2, 3);
                for (unsigned int sg = 0; sg < d.v1.n_substream_groups; sg ++) {
                    d.v1.substream_groups[sg].WriteSubstreamGroupDsi(bits);
                }
            }
            if (d.v1.presentation_config_v1 > 5) {
                d.v1.n_skip_bytes = 0;
                bits.Write(d.v1.n_skip_bytes, 7);
                //TODO: skip_data if n_skip_bytes > 0
            }
        }
        
        // IMS shall set the b_pre_virtualized = 1
        if (presentation_version == 2) {
            d.v1.b_pre_virtualized = 1;
        }
        bits.Write(d.v1.b_pre_virtualized, 1);
        bits.Write(d.v1.b_add_emdf_substreams, 1);
    }
    if (d.v1.b_add_emdf_substreams == 1){
        bits.Write(d.v1.n_add_emdf_substreams, 7);
        for (unsigned int j = 0; j < d.v1.n_add_emdf_substreams; j++){
            bits.Write(d.v1.substream_emdf_version[j], 5);
            bits.Write(d.v1.substream_key_id[j], 10);
        }
    }
    // TODO: Not implement, can't get the informaiton from TOC, need calcuate by the muxer
    bits.Write(d.v1.b_presentation_bitrate_info, 1);
    if (d.v1.b_presentation_bitrate_info == 1) {
        d.v1.ac4_bitrate_dsi.WriteBitrateDsi(bits);
    }

    bits.Write(d.v1.b_alternative, 1);
    if (d.v1.b_alternative == 1) {
        AP4_ByteAlign(bits);
        // TODO: Not implement, need the information from ac4_presentation_substream
        d.v1.alternative_info.WriteAlternativeInfo(bits);        
    }
    AP4_ByteAlign(bits);

    /*
     * TODO: Not implement, need the information from ac4_substream.
     * Currently just set the value to 1 according to Dolby's internal discussion.
     */
    d.v1.de_indicator = 1;
    bits.Write(d.v1.de_indicator, 1);

    bits.Write(d.v1.dolby_atmos_indicator, 1);
    bits.Write(0, 4);       //reserved bits

    if (d.v1.presentation_id > 31) {
        d.v1.b_extended_presentation_id = 1;
        d.v1.extended_presentation_id = d.v1.presentation_id;
    }
    bits.Write(d.v1.b_extended_presentation_id, 1);
    if (d.v1.b_extended_presentation_id == 1) {
        bits.Write(d.v1.extended_presentation_id, 9);
    }else {
        bits.Write(0, 1);   //reserved bit
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::PresentationV1::ParsePresentationVersion
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::PresentationV1::ParsePresentationVersion(AP4_BitReader& bits, unsigned int bitstream_version)
{
    presentation_version = 0;
    if (bitstream_version != 1){
        while(bits.ReadBit() == 1){
            presentation_version ++;
        }
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::PresentationV1::ParsePresentationConfigExtInfo
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::Ac4Dsi::PresentationV1::ParsePresentationConfigExtInfo(AP4_BitReader &bits, unsigned int bitstream_version)
{
    unsigned int nSkipBytes = bits.ReadBits(5);
    if (bits.ReadBit()) {  // b_more_skip_bytes
        nSkipBytes += (AP4_Ac4VariableBits(bits, 2) << 5);
    }
    if (bitstream_version == 1 && d.v1.presentation_config_v1 == 7) {
        // TODO: refer to chapte 6.2.1.5 - TS 103 190-2
    }
    bits.ReadBits(nSkipBytes * 8);
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::PresentationV1::ParseAc4SgiSpecifier
+---------------------------------------------------------------------*/
AP4_UI32 
AP4_Dac4Atom::Ac4Dsi::PresentationV1::ParseAc4SgiSpecifier(AP4_BitReader &bits, unsigned int bitstream_version) 
{
    if (bitstream_version  == 1) {
        // Error
        return 0;
    } else{
        unsigned int groupIndex = bits.ReadBits(3);
        if (groupIndex == 7) {
            groupIndex += AP4_Ac4VariableBits(bits, 2);
        }
        return groupIndex;
    }
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::PresentationV1::ParseDSIFrameRateMultiplyInfo
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::Ac4Dsi::PresentationV1::ParseDSIFrameRateMultiplyInfo(AP4_BitReader &bits, unsigned int frame_rate_idx)
{
    switch (frame_rate_idx) {
        case 2:
        case 3:
        case 4:
            if (bits.ReadBit()) {                               // b_multiplier
                unsigned int multiplier_bit = bits.ReadBit();   //multiplier_bit
                d.v1.dsi_frame_rate_multiply_info = (multiplier_bit == 0)? 1: 2;
            }
            else {
                d.v1.dsi_frame_rate_multiply_info = 0;
            }
            break;
        case 0:
        case 1:
        case 7:
        case 8:
        case 9:
            if (bits.ReadBit()) {
                d.v1.dsi_frame_rate_multiply_info = 1;
            }else {
                d.v1.dsi_frame_rate_multiply_info = 0;
            }
            break;
        default:
            d.v1.dsi_frame_rate_multiply_info = 0;
            break;
    }
    return AP4_SUCCESS;
}


/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::PresentationV1::ParseDSIFrameRateFractionsInfo
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::Ac4Dsi::PresentationV1::ParseDSIFrameRateFractionsInfo(AP4_BitReader &bits, unsigned int frame_rate_idx)
{
     if (frame_rate_idx >= 5 && frame_rate_idx <= 9) {
        if (bits.ReadBit() == 1) {      // b_frame_rate_fraction
            d.v1.dsi_frame_rate_fraction_info = 1;
        }else{
            d.v1.dsi_frame_rate_fraction_info = 0;
        }
    }else if (frame_rate_idx >= 10 && frame_rate_idx <= 12){
        if (bits.ReadBit() == 1) {      // b_frame_rate_fraction
            if (bits.ReadBit() == 1) {  // b_frame_rate_fraction_is_4
                d.v1.dsi_frame_rate_fraction_info = 2;
            } else {
                d.v1.dsi_frame_rate_fraction_info = 1;
            }
        
        }else{
            d.v1.dsi_frame_rate_fraction_info = 0;
        }
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::PresentationV1::ParseEmdInfo
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::Ac4Dsi::PresentationV1::ParseEmdInfo(AP4_BitReader &bits, AP4_Ac4EmdfInfo &emdf_info)
{
    emdf_info.emdf_version = bits.ReadBits(2);
    if (emdf_info.emdf_version == 3) { 
        emdf_info.emdf_version += AP4_Ac4VariableBits(bits, 2);
    }
    emdf_info.key_id = bits.ReadBits(3);
    if (emdf_info.key_id == 7) { 
        emdf_info.key_id += AP4_Ac4VariableBits(bits, 3);
    }
    emdf_info.b_emdf_payloads_substream_info = bits.ReadBit();
    if (emdf_info.b_emdf_payloads_substream_info == 1) {
        // emdf_payloads_substream_info ()
        if (bits.ReadBits(2) == 3) {    // substream_index
            AP4_Ac4VariableBits(bits, 2);
        }
    }
    emdf_info.protectionLengthPrimary   = bits.ReadBits(2);
    emdf_info.protectionLengthSecondary = bits.ReadBits(2);
    // protection_bits_primary
    switch (emdf_info.protectionLengthPrimary) { 
        case 1:
            emdf_info.protection_bits_primary[0] = bits.ReadBits(8);
            break;
        case 2:
            for (unsigned idx = 0; idx < 4; idx ++)  { emdf_info.protection_bits_primary[idx] = bits.ReadBits(8); }
            break;
        case 3:
            for (unsigned idx = 0; idx < 16; idx ++) { emdf_info.protection_bits_primary[idx] = bits.ReadBits(8); }
            break;
        default:
            // Error
            break;
    }
    // protection_bits_secondary
    switch (emdf_info.protectionLengthSecondary) { 
        case 0:
            break;
        case 1:
            emdf_info.protection_bits_Secondary[0] = bits.ReadBits(8);
            break;
        case 2:
            for (unsigned idx = 0; idx < 4; idx ++)  { emdf_info.protection_bits_Secondary[idx] = bits.ReadBits(8); }
            break;
        case 3:
            for (unsigned idx = 0; idx < 16; idx ++) { emdf_info.protection_bits_Secondary[idx] = bits.ReadBits(8); }
            break;
        default:
            // TODO: Error
            break;
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::PresentationV1::ParsePresentationSubstreamInfo
+---------------------------------------------------------------------*/
AP4_Result
AP4_Dac4Atom::Ac4Dsi::PresentationV1::ParsePresentationSubstreamInfo(AP4_BitReader &bits)
{
    d.v1.b_alternative = bits.ReadBit();
    /* unsigned int b_pres_ndot = */ bits.ReadBit();          // b_pres_ndot;
    unsigned int substream_index = bits.ReadBits(2);    //substream_index
    if (substream_index == 3){
        AP4_Ac4VariableBits(bits, 2);
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::PresentationV1::GetPresentationChMode
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::PresentationV1::GetPresentationChMode()
{
    int pres_ch_mode = -1;
    char b_obj_or_ajoc = 0;
    // TODO: n_substream_groups
    for (unsigned int sg = 0; sg < d.v1.n_substream_groups; sg++){
        AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1 &substream_group = d.v1.substream_groups[sg];
        unsigned int n_substreams = d.v1.substream_groups[sg].d.v1.n_substreams;
        for (unsigned int sus = 0; sus < n_substreams; sus++){
            AP4_Dac4Atom::Ac4Dsi::SubStream &substream = substream_group.d.v1.substreams[sus];
            if (substream_group.d.v1.b_channel_coded){
                pres_ch_mode = AP4_Ac4SuperSet(pres_ch_mode, substream.ch_mode);
            }else {
                b_obj_or_ajoc = 1;
            }
        }
    }
    if (b_obj_or_ajoc == 1) { pres_ch_mode = -1; }
    return pres_ch_mode;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::PresentationV1::GetPresentationChannelMask
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::PresentationV1::GetPresentationChannelMask()
{
    unsigned int channel_mask = 0;
    char b_obj_or_ajoc = 0;
    // TODO: n_substream_groups 
    for (unsigned int sg = 0; sg < d.v1.n_substream_groups; sg ++){
        AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1 &substream_group = d.v1.substream_groups[sg];
        unsigned int n_substreams = d.v1.substream_groups[sg].d.v1.n_substreams;
        for (unsigned int sus = 0; sus < n_substreams; sus ++){
            AP4_Dac4Atom::Ac4Dsi::SubStream &substream = substream_group.d.v1.substreams[sus];
            if (substream_group.d.v1.b_channel_coded){
                channel_mask |= substream.dsi_substream_channel_mask;
            }else {
                b_obj_or_ajoc = 1;
            }
        }
    }
    //Modify the mask for headphone presentation with b_pre_virtualized = 1
    /*
    if (presentation_version == 2 || d.v1.b_pre_virtualized == 1){
        if (channel_mask == 0x03) { channel_mask = 0x01;}
    }
    */
    // TODO: temporary solution according to Dolby's internal discussion
    if (channel_mask == 0x03) { channel_mask = 0x01;}

    // If one substream contains Tfl, Tfr, Tbl, Tbr, Tl and Tr shall be removed.
    if ((channel_mask & 0x30) && (channel_mask & 0x80))  { channel_mask &= ~0x80;}

    // objective channel mask
    if (b_obj_or_ajoc == 1) { channel_mask = 0x800000; }
    return channel_mask;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::PresentationV1::GetPresB4BackChannelsPresent
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::PresentationV1::GetPresB4BackChannelsPresent()
{
    for (unsigned int sg = 0; sg < d.v1.n_substream_groups; sg ++){
        AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1 &substream_group = d.v1.substream_groups[sg];
        for (unsigned int sus = 0; sus < substream_group.d.v1.n_substreams; sus ++){
            d.v1.pres_b_4_back_channels_present |=  substream_group.d.v1.substreams[sus].b_4_back_channels_present;
        }
    }
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::PresentationV1::GetPresTopChannelPairs
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::PresentationV1::GetPresTopChannelPairs()
{
    unsigned char tmp_pres_top_channel_pairs = 0;
    for (unsigned int sg = 0; sg < d.v1.n_substream_groups; sg ++){
        AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1 &substream_group = d.v1.substream_groups[sg];
        for (unsigned int sus = 0; sus < substream_group.d.v1.n_substreams; sus ++){
            if (tmp_pres_top_channel_pairs < substream_group.d.v1.substreams[sus].top_channels_present) {
                tmp_pres_top_channel_pairs = substream_group.d.v1.substreams[sus].top_channels_present;
            }
        }
    }
    switch (tmp_pres_top_channel_pairs){
        case 0: 
            d.v1.pres_top_channel_pairs = 0; break;
        case 1:
        case 2:
            d.v1.pres_top_channel_pairs = 1; break;
        case 3:
            d.v1.pres_top_channel_pairs = 2; break;
        default:
            d.v1.pres_top_channel_pairs = 0; break;
    }
    return AP4_SUCCESS;
}


/*----------------------------------------------------------------------
|   AP4_Dac4Atom::Ac4Dsi::PresentationV1::GetBPresentationCoreDiffers
+---------------------------------------------------------------------*/
AP4_Result 
AP4_Dac4Atom::Ac4Dsi::PresentationV1::GetBPresentationCoreDiffers()
{
    int pres_ch_mode_core = -1;
    char b_obj_or_ajoc_adaptive = 0;
    for (unsigned int sg = 0; sg < d.v1.n_substream_groups; sg ++){
        AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1 &substream_group = d.v1.substream_groups[sg];
        unsigned int n_substreams = d.v1.substream_groups[sg].d.v1.n_substreams;
        for (unsigned int sus = 0; sus < n_substreams; sus ++){
            AP4_Dac4Atom::Ac4Dsi::SubStream &substream = substream_group.d.v1.substreams[sus];
            if (substream_group.d.v1.b_channel_coded){
                pres_ch_mode_core = AP4_Ac4SuperSet(pres_ch_mode_core, substream.GetChModeCore(substream_group.d.v1.b_channel_coded));
            }else {
                if (substream.b_ajoc){
                    if (substream.b_static_dmx){
                        pres_ch_mode_core = AP4_Ac4SuperSet(pres_ch_mode_core, substream.GetChModeCore(substream_group.d.v1.b_channel_coded));
                    }else{
                        b_obj_or_ajoc_adaptive = 1;
                    }
                }else{
                    b_obj_or_ajoc_adaptive = 1;
                }
            }
        }
    }
    if (b_obj_or_ajoc_adaptive) {
        pres_ch_mode_core = -1;
    }
    if (pres_ch_mode_core == GetPresentationChMode()) {
        pres_ch_mode_core = -1;
    }
    return pres_ch_mode_core;
}
