/*****************************************************************
|
|    AP4 - Sample Description Objects
|
|    Copyright 2002-2008 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 "Ap4SampleDescription.h"
#include "Ap4EsDescriptor.h"
#include "Ap4SLConfigDescriptor.h"
#include "Ap4SampleEntry.h"
#include "Ap4AvccAtom.h"
#include "Ap4HvccAtom.h"
#include "Ap4VpccAtom.h"
#include "Ap4Av1cAtom.h"
#include "Ap4Utils.h"
#include "Ap4Mp4AudioInfo.h"

/*----------------------------------------------------------------------
|   dynamic cast support
+---------------------------------------------------------------------*/
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_SampleDescription)
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_UnknownSampleDescription)
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_AudioSampleDescription)
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_VideoSampleDescription)
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_GenericAudioSampleDescription)
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_GenericVideoSampleDescription)
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_MpegSampleDescription)
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_MpegAudioSampleDescription)
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_MpegVideoSampleDescription)
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_MpegSystemSampleDescription)
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_AvcSampleDescription)
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_HevcSampleDescription)
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_Av1SampleDescription)
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_SubtitleSampleDescription)
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_Ac3SampleDescription)
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_Eac3SampleDescription)
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_Ac4SampleDescription)

/*----------------------------------------------------------------------
|  AP4_GetFormatName
+---------------------------------------------------------------------*/
const char*
AP4_GetFormatName(AP4_UI32 format)
{
    switch (format) {
        case AP4_SAMPLE_FORMAT_MP4A: return "MPEG-4 Audio";
        case AP4_SAMPLE_FORMAT_MP4V: return "MPEG-4 Video";
        case AP4_SAMPLE_FORMAT_MP4S: return "MPEG-4 Systems";
        case AP4_SAMPLE_FORMAT_ALAC: return "Apple Lossless Audio";
        case AP4_SAMPLE_FORMAT_AVC1: return "H.264";
        case AP4_SAMPLE_FORMAT_AVC2: return "H.264";
        case AP4_SAMPLE_FORMAT_AVC3: return "H.264";
        case AP4_SAMPLE_FORMAT_AVC4: return "H.264";
        case AP4_SAMPLE_FORMAT_DVAV: return "Dolby Vision (H.264)";
        case AP4_SAMPLE_FORMAT_DVA1: return "Dolby Vision (H.264)";
        case AP4_SAMPLE_FORMAT_HEV1: return "H.265";
        case AP4_SAMPLE_FORMAT_HVC1: return "H.265";
        case AP4_SAMPLE_FORMAT_DVH1: return "Dolby Vision (H.265)";
        case AP4_SAMPLE_FORMAT_DVHE: return "Dolby Vision (H.265)";
        case AP4_SAMPLE_FORMAT_AV01: return "AV1";
        case AP4_SAMPLE_FORMAT_OVC1: return "VC-1";
        case AP4_SAMPLE_FORMAT_OWMA: return "WMA";
        case AP4_SAMPLE_FORMAT_AC_3: return "Dolby Digital (AC-3)";
        case AP4_SAMPLE_FORMAT_EC_3: return "Dolby Digital Plus (Enhanced AC-3)";
        case AP4_SAMPLE_FORMAT_AC_4: return "Dolby AC-4";
        case AP4_SAMPLE_FORMAT_DTSC: return "DTS";
        case AP4_SAMPLE_FORMAT_DTSH: return "DTS-HD";
        case AP4_SAMPLE_FORMAT_DTSL: return "DTS-HD Lossless";
        case AP4_SAMPLE_FORMAT_DTSE: return "DTS Low Bitrate";
        case AP4_SAMPLE_FORMAT_AVCP: return "Advanced Video Coding Parameters";
        case AP4_SAMPLE_FORMAT_DRAC: return "Dirac";
        case AP4_SAMPLE_FORMAT_DRA1: return "DRA Audio";
        case AP4_SAMPLE_FORMAT_G726: return "G726";
        case AP4_SAMPLE_FORMAT_MJP2: return "Motion JPEG 2000";
        case AP4_SAMPLE_FORMAT_OKSD: return "OMA Keys";
        case AP4_SAMPLE_FORMAT_RAW_: return "Uncompressed Audio";
        case AP4_SAMPLE_FORMAT_RTP_: return "RTP Hints";
        case AP4_SAMPLE_FORMAT_S263: return "H.263";
        case AP4_SAMPLE_FORMAT_SAMR: return "Narrowband AMR";
        case AP4_SAMPLE_FORMAT_SAWB: return "Wideband AMR";
        case AP4_SAMPLE_FORMAT_SAWP: return "Extended AMR";
        case AP4_SAMPLE_FORMAT_SEVC: return "EVRC Voice";
        case AP4_SAMPLE_FORMAT_SQCP: return "13K Voice";
        case AP4_SAMPLE_FORMAT_SRTP: return "SRTP Hints";
        case AP4_SAMPLE_FORMAT_SSMV: return "SMV Voice";
        case AP4_SAMPLE_FORMAT_TEXT: return "Textual Metadata";
        case AP4_SAMPLE_FORMAT_TWOS: return "Uncompressed 16-bit Audio";
        case AP4_SAMPLE_FORMAT_TX3G: return "Timed Text";
        case AP4_SAMPLE_FORMAT_VC_1: return "SMPTE VC-1";
        case AP4_SAMPLE_FORMAT_XML_: return "XML Metadata";
        case AP4_SAMPLE_FORMAT_STPP: return "Timed Text";
        case AP4_SAMPLE_FORMAT_VP8:  return "VP8";
        case AP4_SAMPLE_FORMAT_VP9:  return "VP9";
        case AP4_SAMPLE_FORMAT_VP10: return "VP10";
        default: return NULL;
    }
}

/*----------------------------------------------------------------------
|   AP4_SampleDescription::AP4_SampleDescription
+---------------------------------------------------------------------*/
AP4_SampleDescription::AP4_SampleDescription(Type            type,
                                             AP4_UI32        format,
                                             AP4_AtomParent* details) :
    m_Type(type), m_Format(format)
{
    if (details) {
        for (AP4_List<AP4_Atom>::Item* item = details->GetChildren().FirstItem();
             item;
             item = item->GetNext()) {
            AP4_Atom* atom = item->GetData();
            if (atom) {
                AP4_Atom* clone = atom->Clone();
                if (clone) m_Details.AddChild(clone);
            }
        }
    }
}

/*----------------------------------------------------------------------
|   AP4_SampleDescription::Clone
+---------------------------------------------------------------------*/
AP4_SampleDescription*
AP4_SampleDescription::Clone(AP4_Result* result)
{
    if (result) *result = AP4_SUCCESS;
    AP4_Atom* atom = ToAtom();
    if (atom == NULL) {
        if (result) *result = AP4_FAILURE;
        return NULL;
    }
    
    // serialize the atom to a buffer
    AP4_MemoryByteStream* mbs = new AP4_MemoryByteStream((AP4_UI32)atom->GetSize());
    atom->Write(*mbs);
    delete atom;
    atom = NULL;
    
    // parse the buffer back to an atom
    mbs->Seek(0);
    AP4_AtomFactory* factory = new AP4_AtomFactory();
    factory->PushContext(AP4_ATOM_TYPE_STSD);
    AP4_Atom* atom_clone = NULL;
    AP4_Result lresult = factory->CreateAtomFromStream(*mbs, atom_clone);
    factory->PopContext();
    delete factory;
    if (result) *result = lresult;
    mbs->Release();
    if (AP4_FAILED(lresult)) return NULL;
    
    // convert the atom clone to a sample description
    AP4_SampleEntry* sample_entry = AP4_DYNAMIC_CAST(AP4_SampleEntry, atom_clone);
    if (sample_entry == NULL) {
        if (result) *result = AP4_ERROR_INTERNAL;
        delete atom_clone;
        return NULL;
    }
    
    AP4_SampleDescription* clone = sample_entry->ToSampleDescription();
    if (clone == NULL) {
        if (result) *result = AP4_ERROR_INTERNAL;
    }
    
    delete atom_clone;
    return clone;
}

/*----------------------------------------------------------------------
|   AP4_SampleDescription::ToAtom
+---------------------------------------------------------------------*/
AP4_Atom*
AP4_SampleDescription::ToAtom() const
{
    return new AP4_SampleEntry(m_Format);
}

/*----------------------------------------------------------------------
|   AP4_SampleDescription::GetCodecString
+---------------------------------------------------------------------*/
AP4_Result
AP4_SampleDescription::GetCodecString(AP4_String& codec)
{
    char coding[5];
    AP4_FormatFourChars(coding, m_Format);
    codec.Assign(coding, 4);
    
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_UnknownSampleDescription::AP4_UnknownSampleDescription
+---------------------------------------------------------------------*/
AP4_UnknownSampleDescription::AP4_UnknownSampleDescription(AP4_Atom* atom) :
    AP4_SampleDescription(AP4_SampleDescription::TYPE_UNKNOWN, 
                          atom->GetType(),
                          NULL),
    m_Atom(atom->Clone())
{
}

/*----------------------------------------------------------------------
|   AP4_UnknownSampleDescription::~AP4_UnknownSampleDescription
+---------------------------------------------------------------------*/
AP4_UnknownSampleDescription::~AP4_UnknownSampleDescription()
{
    delete m_Atom;
}

/*----------------------------------------------------------------------
|   AP4_UnknownSampleDescription::Clone
+---------------------------------------------------------------------*/
AP4_SampleDescription*
AP4_UnknownSampleDescription::Clone(AP4_Result* result)
{
    AP4_Atom* atom_clone = NULL;
    if (m_Atom) {
        atom_clone = m_Atom->Clone();
    }
    if (atom_clone == NULL) {
        if (result) *result = AP4_FAILURE;
        return NULL;
    }
    if (result) *result = AP4_SUCCESS;
    return new AP4_UnknownSampleDescription(atom_clone);
}

/*----------------------------------------------------------------------
|   AP4_UnknownSampleDescription::ToAtom
+---------------------------------------------------------------------*/
AP4_Atom* 
AP4_UnknownSampleDescription::ToAtom() const
{
    if (m_Atom) {
        return m_Atom->Clone();
    } else {
        return NULL;
    }
}

/*----------------------------------------------------------------------
|   AP4_GenericAudioSampleDescription
+---------------------------------------------------------------------*/
AP4_Atom*
AP4_GenericAudioSampleDescription::ToAtom() const
{
    AP4_AudioSampleEntry* sample_entry = new AP4_AudioSampleEntry(m_Format,
                                                                  m_SampleRate<<16,
                                                                  m_SampleSize,
                                                                  m_ChannelCount);
    AP4_AtomParent& details = const_cast<AP4_AtomParent&>(m_Details);
    for (AP4_List<AP4_Atom>::Item* item = details.GetChildren().FirstItem();
                                   item;
                                   item = item->GetNext()) {
        AP4_Atom* child = item->GetData();
        sample_entry->AddChild(child->Clone());
    }
    return sample_entry;
}

/*----------------------------------------------------------------------
|   AP4_GenericVideoSampleDescription::GetCodecString
+---------------------------------------------------------------------*/
AP4_Result
AP4_GenericVideoSampleDescription::GetCodecString(AP4_String& codec)
{
    // VPx override
    AP4_VpccAtom* vpcc = AP4_DYNAMIC_CAST(AP4_VpccAtom, m_Details.GetChild(AP4_ATOM_TYPE_VPCC));
    if (vpcc) {
        return vpcc->GetCodecString(GetFormat(), codec);
    }

    return AP4_SampleDescription::GetCodecString(codec);
}

/*----------------------------------------------------------------------
|   AP4_GenericVideoSampleDescription
+---------------------------------------------------------------------*/
AP4_Atom*
AP4_GenericVideoSampleDescription::ToAtom() const
{
    AP4_VisualSampleEntry* sample_entry = new AP4_VisualSampleEntry(m_Format,
                                                                    m_Width,
                                                                    m_Height,
                                                                    m_Depth,
                                                                    m_CompressorName.GetChars());
    AP4_AtomParent& details = const_cast<AP4_AtomParent&>(m_Details);
    for (AP4_List<AP4_Atom>::Item* item = details.GetChildren().FirstItem();
                                   item;
                                   item = item->GetNext()) {
        AP4_Atom* child = item->GetData();
        sample_entry->AddChild(child->Clone());
    }
    return sample_entry;
}

/*----------------------------------------------------------------------
|   AP4_AvcSampleDescription::AP4_AvcSampleDescription
+---------------------------------------------------------------------*/
AP4_AvcSampleDescription::AP4_AvcSampleDescription(AP4_UI32     format,
                                                   AP4_UI16     width,
                                                   AP4_UI16     height,
                                                   AP4_UI16     depth,
                                                   const char*  compressor_name,
                                                   AP4_UI08     profile,
                                                   AP4_UI08     level,
                                                   AP4_UI08     profile_compatibility,
                                                   AP4_UI08     length_size,
                                                   AP4_UI08     chroma_format,
                                                   AP4_UI08     bit_depth_luma_minus8,
                                                   AP4_UI08     bit_depth_chroma_minus8,
                                                   const AP4_Array<AP4_DataBuffer>& sequence_parameters,
                                                   const AP4_Array<AP4_DataBuffer>& picture_parameters) :
    AP4_SampleDescription(TYPE_AVC, format, NULL),
    AP4_VideoSampleDescription(width, height, depth, compressor_name)
{
    m_AvccAtom = new AP4_AvccAtom(profile, 
                                  level, 
                                  profile_compatibility,
                                  length_size,
                                  chroma_format,
                                  bit_depth_luma_minus8,
                                  bit_depth_chroma_minus8,
                                  sequence_parameters,
                                  picture_parameters);
    m_Details.AddChild(m_AvccAtom);
}

/*----------------------------------------------------------------------
|   AP4_AvcSampleDescription::AP4_AvcSampleDescription
+---------------------------------------------------------------------*/
AP4_AvcSampleDescription::AP4_AvcSampleDescription(AP4_UI32            format,
                                                   AP4_UI16            width,
                                                   AP4_UI16            height,
                                                   AP4_UI16            depth,
                                                   const char*         compressor_name,
                                                   const AP4_AvccAtom* avcc) :
    AP4_SampleDescription(TYPE_AVC, format, NULL),
    AP4_VideoSampleDescription(width, height, depth, compressor_name)
{
    if (avcc) {
        m_AvccAtom = new AP4_AvccAtom(*avcc);
    } else {
        // should never happen
        m_AvccAtom = new AP4_AvccAtom();
    }
    m_Details.AddChild(m_AvccAtom);
}

/*----------------------------------------------------------------------
|   AP4_AvcSampleDescription::AP4_AvcSampleDescription
+---------------------------------------------------------------------*/
AP4_AvcSampleDescription::AP4_AvcSampleDescription(AP4_UI32        format,
                                                   AP4_UI16        width,
                                                   AP4_UI16        height,
                                                   AP4_UI16        depth,
                                                   const char*     compressor_name,
                                                   AP4_AtomParent* details) :
    AP4_SampleDescription(TYPE_AVC, format, details),
    AP4_VideoSampleDescription(width, height, depth, compressor_name),
    m_AvccAtom(NULL)
{
    AP4_AvccAtom* avcc = AP4_DYNAMIC_CAST(AP4_AvccAtom, m_Details.GetChild(AP4_ATOM_TYPE_AVCC));
    if (avcc) {
        m_AvccAtom = avcc;
    } else {
        // shoud never happen
        m_AvccAtom = new AP4_AvccAtom();
        m_Details.AddChild(m_AvccAtom);
    }
}

/*----------------------------------------------------------------------
|   AP4_AvcSampleDescription::GetCodecString
+---------------------------------------------------------------------*/
AP4_Result
AP4_AvcSampleDescription::GetCodecString(AP4_String& codec) {
    char coding[5];
    AP4_FormatFourChars(coding, GetFormat());
    char workspace[64];
    AP4_FormatString(workspace,
                     sizeof(workspace),
                     "%s.%02X%02X%02X",
                     coding,
                     GetProfile(),
                     GetProfileCompatibility(),
                     GetLevel());
    
    // Dolby Vision override
    AP4_DvccAtom* dvcc = AP4_DYNAMIC_CAST(AP4_DvccAtom, m_Details.GetChild(AP4_ATOM_TYPE_DVCC));
    if (dvcc) {
        return dvcc->GetCodecString(workspace, GetFormat(), codec);
    }

    codec = workspace;

    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_AvcSampleDescription::ToAtom
+---------------------------------------------------------------------*/
AP4_Atom*
AP4_AvcSampleDescription::ToAtom() const
{
    return new AP4_AvcSampleEntry(m_Format,
                                  m_Width,
                                  m_Height,
                                  m_Depth,
                                  m_CompressorName.GetChars(),
                                  &m_Details);
}

/*----------------------------------------------------------------------
|   AP4_AvcDoviSampleDescription::AP4_AvcDoviSampleDescription
+---------------------------------------------------------------------*/
AP4_AvcDoviSampleDescription::AP4_AvcDoviSampleDescription(AP4_UI32                         format,
                                                           AP4_UI16                         width,
                                                           AP4_UI16                         height,
                                                           AP4_UI16                         depth,
                                                           const char*                      compressor_name,
                                                           AP4_UI08                         profile,
                                                           AP4_UI08                         level,
                                                           AP4_UI08                         profile_compatibility,
                                                           AP4_UI08                         length_size,
                                                           const AP4_Array<AP4_DataBuffer>& sequence_parameters,
                                                           const AP4_Array<AP4_DataBuffer>& picture_parameters,
                                                           AP4_UI08                         chroma_format,
                                                           AP4_UI08                         bit_depth_luma_minus8,
                                                           AP4_UI08                         bit_depth_chroma_minus8,
                                                           AP4_UI08                         dv_version_major,
                                                           AP4_UI08                         dv_version_minor,
                                                           AP4_UI08                         dv_profile,
                                                           AP4_UI08                         dv_level,
                                                           bool                             rpu_present_flag,
                                                           bool                             el_present_flag,
                                                           bool                             bl_present_flag,
                                                           AP4_UI08                         dv_bl_signal_compatibility_id) :
    AP4_AvcSampleDescription(format,
                             width,
                             height,
                             depth,
                             compressor_name,
                             profile,
                             level,
                             profile_compatibility,
                             length_size,
                             chroma_format,
                             bit_depth_luma_minus8,
                             bit_depth_chroma_minus8,
                             sequence_parameters,
                             picture_parameters)
{
    m_DvccAtom = new AP4_DvccAtom(dv_version_major,
                                  dv_version_minor,
                                  dv_profile,
                                  dv_level,
                                  rpu_present_flag,
                                  el_present_flag,
                                  bl_present_flag,
                                  dv_bl_signal_compatibility_id);
    m_Details.AddChild(m_DvccAtom);
}

/*----------------------------------------------------------------------
|   AP4_HevcSampleDescription::AP4_HevcSampleDescription
+---------------------------------------------------------------------*/
AP4_HevcSampleDescription::AP4_HevcSampleDescription(AP4_UI32            format,
                                                     AP4_UI16            width,
                                                     AP4_UI16            height,
                                                     AP4_UI16            depth,
                                                     const char*         compressor_name,
                                                     const AP4_HvccAtom* hvcc) :
    AP4_SampleDescription(TYPE_HEVC, format, NULL),
    AP4_VideoSampleDescription(width, height, depth, compressor_name)
{
    if (hvcc) {
        m_HvccAtom = new AP4_HvccAtom(*hvcc);
    } else {
        // should never happen
        m_HvccAtom = new AP4_HvccAtom();
    }
    m_Details.AddChild(m_HvccAtom);
}

/*----------------------------------------------------------------------
|   AP4_HevcSampleDescription::AP4_HevcSampleDescription
+---------------------------------------------------------------------*/
AP4_HevcSampleDescription::AP4_HevcSampleDescription(AP4_UI32        format,
                                                     AP4_UI16        width,
                                                     AP4_UI16        height,
                                                     AP4_UI16        depth,
                                                     const char*     compressor_name,
                                                     AP4_AtomParent* details) :
    AP4_SampleDescription(TYPE_HEVC, format, details),
    AP4_VideoSampleDescription(width, height, depth, compressor_name),
    m_HvccAtom(NULL)
{
    AP4_HvccAtom* hvcc = AP4_DYNAMIC_CAST(AP4_HvccAtom, m_Details.GetChild(AP4_ATOM_TYPE_HVCC));
    if (hvcc) {
        m_HvccAtom = hvcc;
    } else {
        // shoud never happen
        m_HvccAtom = new AP4_HvccAtom();
        m_Details.AddChild(m_HvccAtom);
    }
}

/*----------------------------------------------------------------------
|   AP4_HevcSampleDescription::AP4_HevcSampleDescription
+---------------------------------------------------------------------*/
AP4_HevcSampleDescription::AP4_HevcSampleDescription(AP4_UI32                         format,
                                                     AP4_UI16                         width,
                                                     AP4_UI16                         height,
                                                     AP4_UI16                         depth,
                                                     const char*                      compressor_name,
                                                     AP4_UI08                         general_profile_space,
                                                     AP4_UI08                         general_tier_flag,
                                                     AP4_UI08                         general_profile,
                                                     AP4_UI32                         general_profile_compatibility_flags,
                                                     AP4_UI64                         general_constraint_indicator_flags,
                                                     AP4_UI08                         general_level,
                                                     AP4_UI32                         min_spatial_segmentation,
                                                     AP4_UI08                         parallelism_type,
                                                     AP4_UI08                         chroma_format,
                                                     AP4_UI08                         luma_bit_depth,
                                                     AP4_UI08                         chroma_bit_depth,
                                                     AP4_UI16                         average_frame_rate,
                                                     AP4_UI08                         constant_frame_rate,
                                                     AP4_UI08                         num_temporal_layers,
                                                     AP4_UI08                         temporal_id_nested,
                                                     AP4_UI08                         nalu_length_size,
                                                     const AP4_Array<AP4_DataBuffer>& video_parameters,
                                                     AP4_UI08                         video_parameters_completeness,
                                                     const AP4_Array<AP4_DataBuffer>& sequence_parameters,
                                                     AP4_UI08                         sequence_parameters_completeness,
                                                     const AP4_Array<AP4_DataBuffer>& picture_parameters,
                                                     AP4_UI08                         picture_parameters_completeness) :
    AP4_SampleDescription(TYPE_HEVC, format, NULL),
    AP4_VideoSampleDescription(width, height, depth, compressor_name)
{
    m_HvccAtom = new AP4_HvccAtom(general_profile_space,
                                  general_tier_flag,
                                  general_profile,
                                  general_profile_compatibility_flags,
                                  general_constraint_indicator_flags,
                                  general_level,
                                  min_spatial_segmentation,
                                  parallelism_type,
                                  chroma_format,
                                  luma_bit_depth,
                                  chroma_bit_depth,
                                  average_frame_rate,
                                  constant_frame_rate,
                                  num_temporal_layers,
                                  temporal_id_nested,
                                  nalu_length_size,
                                  video_parameters,
                                  video_parameters_completeness,
                                  sequence_parameters,
                                  sequence_parameters_completeness,
                                  picture_parameters,
                                  picture_parameters_completeness);
    m_Details.AddChild(m_HvccAtom);
}

/*----------------------------------------------------------------------
|   ReverseBits
+---------------------------------------------------------------------*/
static AP4_UI32
ReverseBits(AP4_UI32 bits)
{
    unsigned int count = sizeof(bits) * 8;
    AP4_UI32 reverse_bits = 0;
     
    while (bits) {
       reverse_bits = (reverse_bits << 1) | (bits & 1);
       bits >>= 1;
       count--;
    }
    return (count < 32) ? (reverse_bits << count) : 0;
}

/*----------------------------------------------------------------------
|   AP4_HevcSampleDescription:GetCodecString
+---------------------------------------------------------------------*/
AP4_Result
AP4_HevcSampleDescription::GetCodecString(AP4_String& codec) {
    char coding[5];
    AP4_FormatFourChars(coding, GetFormat());
    char profile_space[2] = {0,0};
    if (GetGeneralProfileSpace() > 0 && GetGeneralProfileSpace() <= 3) {
        profile_space[0] = 'A'+GetGeneralProfileSpace()-1;
    }
    AP4_UI64 constraints = GetGeneralConstraintIndicatorFlags();
    while (constraints && ((constraints & 0xFF) == 0)) {
        constraints >>= 8;
    }
    char workspace[64];
    AP4_FormatString(workspace,
                     sizeof(workspace),
                     "%s.%s%d.%X.%c%d.%llx",
                     coding,
                     profile_space,
                     GetGeneralProfile(),
                     ReverseBits(GetGeneralProfileCompatibilityFlags()),
                     GetGeneralTierFlag()?'H':'L',
                     GetGeneralLevel(),
                     constraints);

    // Dolby Vision override
    AP4_DvccAtom* dvcc = AP4_DYNAMIC_CAST(AP4_DvccAtom, m_Details.GetChild(AP4_ATOM_TYPE_DVCC));
    if (dvcc) {
        return dvcc->GetCodecString(workspace, GetFormat(), codec);
    }

    codec = workspace;
    
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_HevcSampleDescription::ToAtom
+---------------------------------------------------------------------*/
AP4_Atom*
AP4_HevcSampleDescription::ToAtom() const
{
    return new AP4_HevcSampleEntry(m_Format,
                                   m_Width,
                                   m_Height,
                                   m_Depth,
                                   m_CompressorName.GetChars(),
                                   &m_Details);
}

/*----------------------------------------------------------------------
|   AP4_HevcDoviSampleDescription::AP4_HevcDoviSampleDescription
+---------------------------------------------------------------------*/
AP4_HevcDoviSampleDescription::AP4_HevcDoviSampleDescription(AP4_UI32                         format,
                                                             AP4_UI16                         width,
                                                             AP4_UI16                         height,
                                                             AP4_UI16                         depth,
                                                             const char*                      compressor_name,
                                                             AP4_UI08                         general_profile_space,
                                                             AP4_UI08                         general_tier_flag,
                                                             AP4_UI08                         general_profile,
                                                             AP4_UI32                         general_profile_compatibility_flags,
                                                             AP4_UI64                         general_constraint_indicator_flags,
                                                             AP4_UI08                         general_level,
                                                             AP4_UI32                         min_spatial_segmentation,
                                                             AP4_UI08                         parallelism_type,
                                                             AP4_UI08                         chroma_format,
                                                             AP4_UI08                         luma_bit_depth,
                                                             AP4_UI08                         chroma_bit_depth,
                                                             AP4_UI16                         average_frame_rate,
                                                             AP4_UI08                         constant_frame_rate,
                                                             AP4_UI08                         num_temporal_layers,
                                                             AP4_UI08                         temporal_id_nested,
                                                             AP4_UI08                         nalu_length_size,
                                                             const AP4_Array<AP4_DataBuffer>& video_parameters,
                                                             AP4_UI08                         video_parameters_completeness,
                                                             const AP4_Array<AP4_DataBuffer>& sequence_parameters,
                                                             AP4_UI08                         sequence_parameters_completeness,
                                                             const AP4_Array<AP4_DataBuffer>& picture_parameters,
                                                             AP4_UI08                         picture_parameters_completeness,
                                                             AP4_UI08                         dv_version_major,
                                                             AP4_UI08                         dv_version_minor,
                                                             AP4_UI08                         dv_profile,
                                                             AP4_UI08                         dv_level,
                                                             bool                             rpu_present_flag,
                                                             bool                             el_present_flag,
                                                             bool                             bl_present_flag,
                                                             AP4_UI08                         dv_bl_signal_compatibility_id) :
    AP4_HevcSampleDescription(format,
                              width,
                              height,
                              depth,
                              compressor_name,
                              general_profile_space,
                              general_tier_flag,
                              general_profile,
                              general_profile_compatibility_flags,
                              general_constraint_indicator_flags,
                              general_level,
                              min_spatial_segmentation,
                              parallelism_type,
                              chroma_format,
                              luma_bit_depth,
                              chroma_bit_depth,
                              average_frame_rate,
                              constant_frame_rate,
                              num_temporal_layers,
                              temporal_id_nested,
                              nalu_length_size,
                              video_parameters,
                              video_parameters_completeness,
                              sequence_parameters,
                              sequence_parameters_completeness,
                              picture_parameters,
                              picture_parameters_completeness)
{
    m_DvccAtom = new AP4_DvccAtom(dv_version_major,
                                  dv_version_minor,
                                  dv_profile,
                                  dv_level,
                                  rpu_present_flag,
                                  el_present_flag,
                                  bl_present_flag,
                                  dv_bl_signal_compatibility_id);
    m_Details.AddChild(m_DvccAtom);
}

/*----------------------------------------------------------------------
|   AP4_Av1SampleDescription::AP4_Av1SampleDescription
+---------------------------------------------------------------------*/
AP4_Av1SampleDescription::AP4_Av1SampleDescription(AP4_UI32        format,
                                                   AP4_UI16        width,
                                                   AP4_UI16        height,
                                                   AP4_UI16        depth,
                                                   const char*     compressor_name,
                                                   AP4_UI08        version,
                                                   AP4_UI08        seq_profile,
                                                   AP4_UI08        seq_level_idx_0,
                                                   AP4_UI08        seq_tier_0,
                                                   AP4_UI08        high_bitdepth,
                                                   AP4_UI08        twelve_bit,
                                                   AP4_UI08        monochrome,
                                                   AP4_UI08        chroma_subsampling_x,
                                                   AP4_UI08        chroma_subsampling_y,
                                                   AP4_UI08        chroma_sample_position,
                                                   AP4_UI08        initial_presentation_delay_present,
                                                   AP4_UI08        initial_presentation_delay_minus_one,
                                                   const AP4_UI08* config_obus,
                                                   AP4_Size        config_obus_size) :
    AP4_SampleDescription(TYPE_AV1, format, NULL),
    AP4_VideoSampleDescription(width, height, depth, compressor_name)
{
    m_Av1cAtom = new AP4_Av1cAtom(version,
                                  seq_profile,
                                  seq_level_idx_0,
                                  seq_tier_0,
                                  high_bitdepth,
                                  twelve_bit,
                                  monochrome,
                                  chroma_subsampling_x,
                                  chroma_subsampling_y,
                                  chroma_sample_position,
                                  initial_presentation_delay_present,
                                  initial_presentation_delay_minus_one,
                                  config_obus,
                                  config_obus_size);
    m_Details.AddChild(m_Av1cAtom);
}

/*----------------------------------------------------------------------
|   AP4_Av1SampleDescription::AP4_Av1SampleDescription
+---------------------------------------------------------------------*/
AP4_Av1SampleDescription::AP4_Av1SampleDescription(AP4_UI32            format,
                                                   AP4_UI16            width,
                                                   AP4_UI16            height,
                                                   AP4_UI16            depth,
                                                   const char*         compressor_name,
                                                   const AP4_Av1cAtom* av1c) :
    AP4_SampleDescription(TYPE_AV1, format, NULL),
    AP4_VideoSampleDescription(width, height, depth, compressor_name)
{
    if (av1c) {
        m_Av1cAtom = new AP4_Av1cAtom(*av1c);
    } else {
        // should never happen
        m_Av1cAtom = new AP4_Av1cAtom();
    }
    m_Details.AddChild(m_Av1cAtom);
}

/*----------------------------------------------------------------------
|   AP4_Av1SampleDescription::AP4_Av1SampleDescription
+---------------------------------------------------------------------*/
AP4_Av1SampleDescription::AP4_Av1SampleDescription(AP4_UI32        format,
                                                   AP4_UI16        width,
                                                   AP4_UI16        height,
                                                   AP4_UI16        depth,
                                                   const char*     compressor_name,
                                                   AP4_AtomParent* details) :
    AP4_SampleDescription(TYPE_AV1, format, details),
    AP4_VideoSampleDescription(width, height, depth, compressor_name),
    m_Av1cAtom(NULL)
{
    AP4_Av1cAtom* av1c = AP4_DYNAMIC_CAST(AP4_Av1cAtom, m_Details.GetChild(AP4_ATOM_TYPE_AV1C));
    if (av1c) {
        m_Av1cAtom = av1c;
    } else {
        // shoud never happen
        m_Av1cAtom = new AP4_Av1cAtom();
        m_Details.AddChild(m_Av1cAtom);
    }
}

/*----------------------------------------------------------------------
|   AP4_Av1cSampleDescription::GetCodecString
+---------------------------------------------------------------------*/
AP4_Result
AP4_Av1SampleDescription::GetCodecString(AP4_String& codec) {
    AP4_UI08 bit_depth = 10;
    AP4_UI08 color_primaries = 1;
    AP4_UI08 transfer_characteristics = 1;
    AP4_UI08 matrix_coefficients = 1;
    AP4_UI08 video_full_range_flag = 0;
    char coding[5];
    AP4_FormatFourChars(coding, GetFormat());
    char workspace[64];
    AP4_FormatString(workspace,
                     sizeof(workspace),
                     "%s.%d.%02d%c.%02d.%d.%d%d%d.%02d.%02d.%02d.%d",
                     coding,
                     this->GetSeqProfile(),
                     this->GetSeqLevelIdx0() >> 4,
                     this->GetSeqTier0() == 0 ? 'M' : 'H',
                     bit_depth,
                     this->GetMonochrome(),
                     this->GetChromaSubsamplingX(),
                     this->GetChromaSubsamplingY(),
                     (this->GetChromaSubsamplingX() == 1 && this->GetChromaSubsamplingY() == 1) ?
                     this->GetChromaSamplePosition() : 0,
                     color_primaries,
                     transfer_characteristics,
                     matrix_coefficients,
                     video_full_range_flag);
    codec = workspace;
    
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_Av1SampleDescription::ToAtom
+---------------------------------------------------------------------*/
AP4_Atom*
AP4_Av1SampleDescription::ToAtom() const
{
    return new AP4_Av1SampleEntry(m_Format,
                                  m_Width,
                                  m_Height,
                                  m_Depth,
                                  m_CompressorName.GetChars(),
                                  &m_Details);
}

/*----------------------------------------------------------------------
|   AP4_MpegSampleDescription::AP4_MpegSampleDescription
+---------------------------------------------------------------------*/
AP4_MpegSampleDescription::AP4_MpegSampleDescription(AP4_UI32      format,
                                                     AP4_EsdsAtom* esds) :
    AP4_SampleDescription(TYPE_MPEG, format, NULL),
    m_StreamType(0),
    m_ObjectTypeId(0),
    m_BufferSize(0),
    m_MaxBitrate(0),
    m_AvgBitrate(0)
{
    if (esds) {
        // get the es descriptor
        const AP4_EsDescriptor* es_desc = esds->GetEsDescriptor();
        if (es_desc == NULL) return;

        // get the decoder config descriptor
        const AP4_DecoderConfigDescriptor* dc_desc =
            es_desc->GetDecoderConfigDescriptor();
        if (dc_desc) {
            m_StreamType   = dc_desc->GetStreamType();
            m_ObjectTypeId = dc_desc->GetObjectTypeIndication();
            m_BufferSize   = dc_desc->GetBufferSize();
            m_MaxBitrate   = dc_desc->GetMaxBitrate();
            m_AvgBitrate   = dc_desc->GetAvgBitrate();
            const AP4_DecoderSpecificInfoDescriptor* dsi_desc =
                dc_desc->GetDecoderSpecificInfoDescriptor();
            if (dsi_desc != NULL) {
                m_DecoderInfo.SetData(dsi_desc->GetDecoderSpecificInfo().GetData(),
                                      dsi_desc->GetDecoderSpecificInfo().GetDataSize());
            }
        }
    }
}

/*----------------------------------------------------------------------
|   AP4_MpegSampleDescription::AP4_MpegSampleDescription
+---------------------------------------------------------------------*/
AP4_MpegSampleDescription::AP4_MpegSampleDescription(
    AP4_UI32              format,
    StreamType            stream_type,
    OTI                   oti,
    const AP4_DataBuffer* decoder_info,
    AP4_UI32              buffer_size,
    AP4_UI32              max_bitrate,
    AP4_UI32              avg_bitrate) :
    AP4_SampleDescription(TYPE_MPEG, format, NULL),
    m_StreamType(stream_type),
    m_ObjectTypeId(oti),
    m_BufferSize(buffer_size),
    m_MaxBitrate(max_bitrate),
    m_AvgBitrate(avg_bitrate)
{
    if (decoder_info != NULL) {
        m_DecoderInfo.SetData(decoder_info->GetData(), decoder_info->GetDataSize());
    }
}

/*----------------------------------------------------------------------
|   AP4_MpegSampleDescription::CreateEsDescriptor
+---------------------------------------------------------------------*/
AP4_EsDescriptor* 
AP4_MpegSampleDescription::CreateEsDescriptor() const
{
    AP4_EsDescriptor* desc = new AP4_EsDescriptor(0);
    AP4_DecoderSpecificInfoDescriptor* dsi_desc;
    if (m_DecoderInfo.GetDataSize() != 0) {
        dsi_desc = new AP4_DecoderSpecificInfoDescriptor(m_DecoderInfo);
    } else {
        dsi_desc = NULL;
    }
    AP4_DecoderConfigDescriptor* decoder_config = 
        new AP4_DecoderConfigDescriptor(m_StreamType,
        m_ObjectTypeId,
        m_BufferSize,
        m_MaxBitrate,
        m_AvgBitrate,
        dsi_desc);
    desc->AddSubDescriptor(decoder_config);
    
    // add a fixed SL Config
    desc->AddSubDescriptor(new AP4_SLConfigDescriptor());
    
    return desc;
}

/*----------------------------------------------------------------------
|   AP4_MpegSystemSampleDescription::AP4_MpegSystemSampleDescription
+---------------------------------------------------------------------*/
AP4_MpegSystemSampleDescription::AP4_MpegSystemSampleDescription(AP4_EsdsAtom* esds) :
    AP4_MpegSampleDescription(AP4_ATOM_TYPE_MP4S, esds)
{
}

/*----------------------------------------------------------------------
|   AP4_MpegSystemSampleDescription::AP4_MpegSystemSampleDescription
+---------------------------------------------------------------------*/
AP4_MpegSystemSampleDescription::AP4_MpegSystemSampleDescription(
    StreamType            stream_type,
    OTI                   oti,
    const AP4_DataBuffer* decoder_info,
    AP4_UI32              buffer_size,
    AP4_UI32              max_bitrate,
    AP4_UI32              avg_bitrate) :
    AP4_MpegSampleDescription(AP4_ATOM_TYPE_MP4S,
                              stream_type,
                              oti,
                              decoder_info,
                              buffer_size,
                              max_bitrate,
                              avg_bitrate)
{
}

/*----------------------------------------------------------------------
|   AP4_MpegSystemSampleDescription::ToAtom
+---------------------------------------------------------------------*/
AP4_Atom*
AP4_MpegSystemSampleDescription::ToAtom() const
{
    return new AP4_Mp4sSampleEntry(CreateEsDescriptor());
}

/*----------------------------------------------------------------------
|   AP4_MpegAudioSampleDescription::AP4_MpegAudioSampleDescription
+---------------------------------------------------------------------*/
AP4_MpegAudioSampleDescription::AP4_MpegAudioSampleDescription(
    AP4_UI32  sample_rate,
    AP4_UI16  sample_size,
    AP4_UI16  channel_count,
    AP4_EsdsAtom* esds) :
    AP4_MpegSampleDescription(AP4_ATOM_TYPE_MP4A, esds),
    AP4_AudioSampleDescription(sample_rate, sample_size, channel_count)
{
}

/*----------------------------------------------------------------------
|   AP4_MpegAudioSampleDescription::AP4_MpegAudioSampleDescription
+---------------------------------------------------------------------*/
AP4_MpegAudioSampleDescription::AP4_MpegAudioSampleDescription(
    OTI                   oti,
    AP4_UI32              sample_rate,
    AP4_UI16              sample_size,
    AP4_UI16              channel_count,
    const AP4_DataBuffer* decoder_info,
    AP4_UI32              buffer_size,
    AP4_UI32              max_bitrate,
    AP4_UI32              avg_bitrate) :
    AP4_MpegSampleDescription(AP4_ATOM_TYPE_MP4A,
                              AP4_STREAM_TYPE_AUDIO,
                              oti, 
                              decoder_info, buffer_size, 
                              max_bitrate, avg_bitrate),
    AP4_AudioSampleDescription(sample_rate, sample_size, channel_count)
{
}

/*----------------------------------------------------------------------
|   AP4_MpegAudioSampleDescription::ToAtom
+---------------------------------------------------------------------*/
AP4_Atom*
AP4_MpegAudioSampleDescription::ToAtom() const
{
    return new AP4_Mp4aSampleEntry(m_SampleRate<<16,
                                   m_SampleSize,
                                   m_ChannelCount,
                                   CreateEsDescriptor());
}

/*----------------------------------------------------------------------
|   AP4_MpegAudioSampleDescription::GetCodecString
+---------------------------------------------------------------------*/
AP4_Result
AP4_MpegAudioSampleDescription::GetCodecString(AP4_String& codec)
{
    char coding[5];
    AP4_FormatFourChars(coding, GetFormat());
    char workspace[64];
    workspace[0] = 0;
    if (GetFormat() == AP4_SAMPLE_FORMAT_MP4A) {
        if (GetObjectTypeId() == AP4_OTI_MPEG4_AUDIO) {
            Mpeg4AudioObjectType object_type = GetMpeg4AudioObjectType();
            if (object_type == AP4_MPEG4_AUDIO_OBJECT_TYPE_AAC_LC) {
                const AP4_DataBuffer& dsi = GetDecoderInfo();
                if (dsi.GetDataSize()) {
                    AP4_Mp4AudioDecoderConfig dec_config;
                    AP4_Result result = dec_config.Parse(dsi.GetData(), dsi.GetDataSize());
                    if (AP4_SUCCEEDED(result)) {
                        if (dec_config.m_Extension.m_PsPresent) {
                            object_type = AP4_MPEG4_AUDIO_OBJECT_TYPE_PS;
                        } else if (dec_config.m_Extension.m_SbrPresent) {
                            object_type = AP4_MPEG4_AUDIO_OBJECT_TYPE_SBR;
                        }
                    }
                }
            }
            AP4_FormatString(workspace, sizeof(workspace), "%s.%02X.%d", coding, (int)GetObjectTypeId(), object_type);
        } else {
            AP4_FormatString(workspace, sizeof(workspace), "%s.%02X", coding, (int)GetObjectTypeId());
        }
    }
    
    codec = workspace;
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_MpegAudioSampleDescription::GetMpeg4AudioObjectType
+---------------------------------------------------------------------*/
AP4_MpegAudioSampleDescription::Mpeg4AudioObjectType
AP4_MpegAudioSampleDescription::GetMpeg4AudioObjectType() const
{
    if (m_ObjectTypeId == AP4_OTI_MPEG4_AUDIO &&
        m_DecoderInfo.GetDataSize() >= 1) {
        AP4_UI08 type = m_DecoderInfo.GetData()[0]>>3;
        if (type == 31) {
            if (m_DecoderInfo.GetDataSize() < 2) return 0;
            type = 32+(((m_DecoderInfo.GetData()[0]&0x07)<<3) |
                       ((m_DecoderInfo.GetData()[1]&0xE0)>>5));
        }
        return type;
    } else {
        return 0;
    }
}

/*----------------------------------------------------------------------
 |   AP4_Ac3SampleDescription::AP4_Ac3SampleDescription
 +---------------------------------------------------------------------*/
AP4_Ac3SampleDescription::AP4_Ac3SampleDescription(AP4_UI32            sample_rate,
                                                   AP4_UI16            sample_size,
                                                   AP4_UI16            channel_count,
                                                   const AP4_Dac3Atom* dac3):
    AP4_SampleDescription(TYPE_AC3, AP4_SAMPLE_FORMAT_AC_3, NULL),
    AP4_AudioSampleDescription(sample_rate, sample_size, channel_count)
{
    if (dac3) {
        m_Dac3Atom = new AP4_Dac3Atom(*dac3);
    } else {
        // TODO: add default construtor, m_Dac3Atom = new AP4_Dac3Atom() / should never happen
        m_Dac3Atom = NULL;
    }
    m_Details.AddChild(m_Dac3Atom);
}

/*----------------------------------------------------------------------
 |   AP4_Ac3SampleDescription::AP4_Ac3SampleDescription
 +---------------------------------------------------------------------*/
AP4_Ac3SampleDescription::AP4_Ac3SampleDescription(AP4_UI32        sample_rate,
                                                   AP4_UI16        sample_size,
                                                   AP4_UI16        channel_count,
                                                   AP4_AtomParent* details) :
    AP4_SampleDescription(TYPE_AC3, AP4_SAMPLE_FORMAT_AC_3, details),
    AP4_AudioSampleDescription(sample_rate, sample_size, channel_count),
m_Dac3Atom(NULL)
{
    AP4_Dac3Atom* ac3 = AP4_DYNAMIC_CAST(AP4_Dac3Atom, m_Details.GetChild(AP4_SAMPLE_FORMAT_AC_3));
    if (ac3) {
        m_Dac3Atom = ac3;
    } else {
        // TODO: add default construtor, m_Dac3Atom = new AP4_Dac3Atom() / should never happen
        m_Dac3Atom = NULL;
        m_Details.AddChild(m_Dac3Atom);
    }
}

/*----------------------------------------------------------------------
 |   AP4_Ac3SampleDescription::AP4_Ac3SampleDescription
 +---------------------------------------------------------------------*/

AP4_Ac3SampleDescription::AP4_Ac3SampleDescription(AP4_UI32                        sample_rate,
                                                   AP4_UI16                        sample_size,
                                                   AP4_UI16                        channel_count,
                                                   AP4_UI32                        size,
                                                   const AP4_Dac3Atom::StreamInfo* ac3_stream_info):
    AP4_SampleDescription(TYPE_AC3, AP4_SAMPLE_FORMAT_AC_3, NULL),
    AP4_AudioSampleDescription(sample_rate, sample_size, channel_count)
{
    m_Dac3Atom = new AP4_Dac3Atom(ac3_stream_info);
    m_Details.AddChild(m_Dac3Atom);
}

/*----------------------------------------------------------------------
 |   AP4_Ac3SampleDescription::ToAtom
 +---------------------------------------------------------------------*/

AP4_Atom*
AP4_Ac3SampleDescription::ToAtom() const
{
    return new AP4_Ac3SampleEntry(m_Format,
                                  m_SampleRate<<16,
                                  m_SampleSize,
                                  m_ChannelCount,
                                  &m_Details);
}

/*----------------------------------------------------------------------
|   AP4_Eac3SampleDescription::AP4_Eac3SampleDescription
+---------------------------------------------------------------------*/
AP4_Eac3SampleDescription::AP4_Eac3SampleDescription():
    AP4_SampleDescription(TYPE_EAC3, AP4_SAMPLE_FORMAT_EC_3, NULL),
    AP4_AudioSampleDescription(48000, 16, 2)
{
    m_Details.AddChild(new AP4_Dec3Atom());
}

/*----------------------------------------------------------------------
|   AP4_Eac3SampleDescription::AP4_Eac3SampleDescription
+---------------------------------------------------------------------*/
AP4_Eac3SampleDescription::AP4_Eac3SampleDescription(AP4_UI32            sample_rate, 
                                                     AP4_UI16            sample_size,
                                                     AP4_UI16            channel_count,
                                                     const AP4_Dec3Atom* dec3):
    AP4_SampleDescription(TYPE_EAC3, AP4_SAMPLE_FORMAT_EC_3, NULL),
    AP4_AudioSampleDescription(sample_rate, sample_size, channel_count)
{
    if (dec3) {
        m_Dec3Atom = new AP4_Dec3Atom(*dec3);
    } else {
        // shoud never happen
        m_Dec3Atom = new AP4_Dec3Atom();
    }
    m_Details.AddChild(m_Dec3Atom);
}

/*----------------------------------------------------------------------
|   AP4_Eac3SampleDescription::AP4_Eac3SampleDescription
+---------------------------------------------------------------------*/
AP4_Eac3SampleDescription::AP4_Eac3SampleDescription(AP4_UI32        sample_rate, 
                                                     AP4_UI16        sample_size,
                                                     AP4_UI16        channel_count,
                                                     AP4_AtomParent* details) :
    AP4_SampleDescription(TYPE_EAC3, AP4_SAMPLE_FORMAT_EC_3, details),
    AP4_AudioSampleDescription(sample_rate, sample_size, channel_count),
    m_Dec3Atom(NULL)
{
    AP4_Dec3Atom* eac3 = AP4_DYNAMIC_CAST(AP4_Dec3Atom, m_Details.GetChild(AP4_SAMPLE_FORMAT_EC_3));
    if (eac3) {
        m_Dec3Atom = eac3;
    } else {
        // shoud never happen
        m_Dec3Atom = new AP4_Dec3Atom();
        m_Details.AddChild(m_Dec3Atom);
    }
}

/*----------------------------------------------------------------------
|   AP4_Eac3SampleDescription::AP4_Eac3SampleDescription
+---------------------------------------------------------------------*/

AP4_Eac3SampleDescription::AP4_Eac3SampleDescription(AP4_UI32                       sample_rate, 
                                                     AP4_UI16                       sample_size,
                                                     AP4_UI16                       channel_count,
                                                     AP4_UI32                       size,
                                                     const AP4_Dec3Atom::SubStream* subStream,
                                                     const AP4_UI32                 complexity_index_type_a):
    AP4_SampleDescription(TYPE_EAC3, AP4_SAMPLE_FORMAT_EC_3, NULL),
    AP4_AudioSampleDescription(sample_rate, sample_size, channel_count)
{
    m_Dec3Atom = new AP4_Dec3Atom(size, subStream, complexity_index_type_a);
    m_Details.AddChild(m_Dec3Atom);
}

/*----------------------------------------------------------------------
|   AP4_Eac3SampleDescription::ToAtom
+---------------------------------------------------------------------*/

AP4_Atom*
AP4_Eac3SampleDescription::ToAtom() const
{
    return new AP4_Eac3SampleEntry(m_Format,
                                   m_SampleRate<<16,
                                   m_SampleSize,
                                   m_ChannelCount,
                                   &m_Details);
}

/*----------------------------------------------------------------------
|   AP4_Ac4SampleDescription::AP4_Ac4SampleDescription
+---------------------------------------------------------------------*/
AP4_Ac4SampleDescription::AP4_Ac4SampleDescription(AP4_UI32            sample_rate, 
                                                   AP4_UI16            sample_size,
                                                   AP4_UI16            channel_count,
                                                   const AP4_Dac4Atom* dac4):
    AP4_SampleDescription(TYPE_AC4, AP4_SAMPLE_FORMAT_AC_4, NULL),
    AP4_AudioSampleDescription(sample_rate, sample_size, channel_count)
{
    if (dac4) {
        m_Dac4Atom = dac4->CloneConst();
;
    } else {
        // TODO: add default construtor, m_Dac4Atom = new AP4_Dac4Atom() / should never happen
        m_Dac4Atom = NULL;
    }
    m_Details.AddChild(m_Dac4Atom);
}

/*----------------------------------------------------------------------
|   AP4_Ac4SampleDescription::AP4_Ac4SampleDescription
+---------------------------------------------------------------------*/
AP4_Ac4SampleDescription::AP4_Ac4SampleDescription(AP4_UI32        sample_rate, 
                                                   AP4_UI16        sample_size,
                                                   AP4_UI16        channel_count,
                                                   AP4_AtomParent* details) :
    AP4_SampleDescription(TYPE_AC4, AP4_SAMPLE_FORMAT_AC_4, details),
    AP4_AudioSampleDescription(sample_rate, sample_size, channel_count),
    m_Dac4Atom(NULL)
{
    AP4_Dac4Atom* ac4 = AP4_DYNAMIC_CAST(AP4_Dac4Atom, m_Details.GetChild(AP4_ATOM_TYPE_AC_4));
    if (ac4) {
        m_Dac4Atom = ac4;
    } else {
        // TODO: add default construtor, m_Dac4Atom = new AP4_Dac4Atom() / should never happen
        m_Dac4Atom = NULL;
        m_Details.AddChild(m_Dac4Atom);
    }
}

/*----------------------------------------------------------------------
|   AP4_Ac4SampleDescription::AP4_Ac4SampleDescription
+---------------------------------------------------------------------*/
AP4_Ac4SampleDescription::AP4_Ac4SampleDescription(AP4_UI32                    sample_rate, 
                                                   AP4_UI16                    sample_size,
                                                   AP4_UI16                    channel_count,
                                                   AP4_UI32                    size,
                                                   const AP4_Dac4Atom::Ac4Dsi* ac4Dsi):
    AP4_SampleDescription(TYPE_AC4, AP4_SAMPLE_FORMAT_AC_4, NULL),
    AP4_AudioSampleDescription(sample_rate, sample_size, channel_count)
{
    m_Dac4Atom = new AP4_Dac4Atom(size, ac4Dsi);
    m_Details.AddChild(m_Dac4Atom);
}

/*----------------------------------------------------------------------
|   AP4_Ac4SampleDescription::ToAtom
+---------------------------------------------------------------------*/

AP4_Atom*
AP4_Ac4SampleDescription::ToAtom() const
{
    return new AP4_Ac4SampleEntry(m_Format,
                                  m_SampleRate<<16,
                                  m_SampleSize,
                                  m_ChannelCount,
                                  &m_Details);
}

/*----------------------------------------------------------------------
|   AP4_MpegVideoSampleDescription::AP4_MpegVideoSampleDescription
+---------------------------------------------------------------------*/
AP4_MpegVideoSampleDescription::AP4_MpegVideoSampleDescription(
    AP4_UI16      width,
    AP4_UI16      height,
    AP4_UI16      depth,
    const char*   compressor_name,
    AP4_EsdsAtom* esds) :
    
    AP4_MpegSampleDescription(AP4_ATOM_TYPE_MP4V, esds),
    AP4_VideoSampleDescription(width, height, depth, compressor_name)
{
}

/*----------------------------------------------------------------------
|   AP4_MpegVideoSampleDescription::AP4_MpegVideoSampleDescription
+---------------------------------------------------------------------*/
AP4_MpegVideoSampleDescription::AP4_MpegVideoSampleDescription(
    OTI                   oti,
    AP4_UI16              width,
    AP4_UI16              height,
    AP4_UI16              depth,
    const char*           compressor_name,
    const AP4_DataBuffer* decoder_info,
    AP4_UI32              buffer_size,
    AP4_UI32              max_bitrate,
    AP4_UI32              avg_bitrate) :
    AP4_MpegSampleDescription(AP4_ATOM_TYPE_MP4V, 
                              AP4_STREAM_TYPE_VISUAL,
                              oti,
                              decoder_info,
                              buffer_size,
                              max_bitrate,
                              avg_bitrate),
    AP4_VideoSampleDescription(width, height, depth, compressor_name)
{
}

/*----------------------------------------------------------------------
|   AP4_MpegVideoSampleDescription::ToAtom
+---------------------------------------------------------------------*/
AP4_Atom*
AP4_MpegVideoSampleDescription::ToAtom() const
{
    return new AP4_Mp4vSampleEntry(m_Width,
                                   m_Height,
                                   m_Depth,
                                   m_CompressorName.GetChars(),
                                   CreateEsDescriptor());
}

/*----------------------------------------------------------------------
|   AP4_MpegSampleDescription::GetStreamTypeString
+---------------------------------------------------------------------*/
const char* 
AP4_MpegSampleDescription::GetStreamTypeString(StreamType type)
{
    switch (type) {
        case AP4_STREAM_TYPE_FORBIDDEN: return "INVALID"; 
        case AP4_STREAM_TYPE_OD:        return "Object Descriptor";
        case AP4_STREAM_TYPE_CR:        return "CR";
        case AP4_STREAM_TYPE_BIFS:      return "BIFS";
        case AP4_STREAM_TYPE_VISUAL:    return "Visual";
        case AP4_STREAM_TYPE_AUDIO:     return "Audio";
        case AP4_STREAM_TYPE_MPEG7:     return "MPEG-7";
        case AP4_STREAM_TYPE_IPMP:      return "IPMP";
        case AP4_STREAM_TYPE_OCI:       return "OCI";
        case AP4_STREAM_TYPE_MPEGJ:     return "MPEG-J";
        default:                        return "UNKNOWN";
    }
}

/*----------------------------------------------------------------------
|   AP4_MpegSampleDescription::GetObjectTypeString
+---------------------------------------------------------------------*/
const char* 
AP4_MpegSampleDescription::GetObjectTypeString(OTI oti)
{
    switch (oti) {
        case AP4_OTI_MPEG4_SYSTEM:         return "MPEG-4 System";
        case AP4_OTI_MPEG4_SYSTEM_COR:     return "MPEG-4 System COR";
        case AP4_OTI_MPEG4_VISUAL:         return "MPEG-4 Video";
        case AP4_OTI_MPEG4_AUDIO:          return "MPEG-4 Audio";
        case AP4_OTI_MPEG2_VISUAL_SIMPLE:  return "MPEG-2 Video Simple Profile";
        case AP4_OTI_MPEG2_VISUAL_MAIN:    return "MPEG-2 Video Main Profile";
        case AP4_OTI_MPEG2_VISUAL_SNR:     return "MPEG-2 Video SNR";
        case AP4_OTI_MPEG2_VISUAL_SPATIAL: return "MPEG-2 Video Spatial";
        case AP4_OTI_MPEG2_VISUAL_HIGH:    return "MPEG-2 Video High";
        case AP4_OTI_MPEG2_VISUAL_422:     return "MPEG-2 Video 4:2:2";
        case AP4_OTI_MPEG2_AAC_AUDIO_MAIN: return "MPEG-2 Audio AAC Main Profile";
        case AP4_OTI_MPEG2_AAC_AUDIO_LC:   return "MPEG-2 Audio AAC Low Complexity";
        case AP4_OTI_MPEG2_AAC_AUDIO_SSRP: return "MPEG-2 Audio AAC SSRP";
        case AP4_OTI_MPEG2_PART3_AUDIO:    return "MPEG-2 Audio Part-3";
        case AP4_OTI_MPEG1_VISUAL:         return "MPEG-1 Video";
        case AP4_OTI_MPEG1_AUDIO:          return "MPEG-1 Audio";
        case AP4_OTI_JPEG:                 return "JPEG";
        case AP4_OTI_JPEG2000:             return "JPEG-2000";
        case AP4_OTI_EVRC_VOICE:           return "EVRC Voice";
        case AP4_OTI_SMV_VOICE:            return "SMV Voice";
        case AP4_OTI_3GPP2_CMF:            return "3GPP2 CMF";
        case AP4_OTI_SMPTE_VC1:            return "SMPTE VC1 Video";
        case AP4_OTI_DIRAC_VIDEO:          return "Dirac Video";
        case AP4_OTI_AC3_AUDIO:            return "AC3 Audio";
        case AP4_OTI_EAC3_AUDIO:           return "E-AC3 Audio";
        case AP4_OTI_DRA_AUDIO:            return "DRA Audio";
        case AP4_OTI_G719_AUDIO:           return "G.719 Audio";
        case AP4_OTI_DTS_AUDIO:            return "DTS Audio";
        case AP4_OTI_DTS_HIRES_AUDIO:      return "DTS High Resolution Audio";
        case AP4_OTI_DTS_MASTER_AUDIO:     return "DTS Master Audio";
        case AP4_OTI_DTS_EXPRESS_AUDIO:    return "DTS Express/LBR Audio";
        case AP4_OTI_OPUS_AUDIO:           return "Opus Audio";
        case AP4_OTI_VP9_VIDEO:            return "VP9 Video";
        case AP4_OTI_VORBIS_AUDIO:         return "Vorbis Audio";
        case AP4_OTI_13K_VOICE:            return "13K Voice";
        default:                           return "UNKNOWN";
    }
}

/*----------------------------------------------------------------------
|   AP4_MpegAudioSampleDescription::GetMpeg4AudioObjectTypeString
+---------------------------------------------------------------------*/
const char* 
AP4_MpegAudioSampleDescription::GetMpeg4AudioObjectTypeString(Mpeg4AudioObjectType type)
{
    switch (type) {
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_AAC_MAIN:                return "AAC Main Profile";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_AAC_LC:                  return "AAC Low Complexity";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_AAC_SSR:                 return "AAC Scalable Sample Rate";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_AAC_LTP:                 return "AAC Long Term Predictor";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_SBR:                     return "Spectral Band Replication";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_AAC_SCALABLE:            return "AAC Scalable";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_TWINVQ:                  return "Twin VQ";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_CELP:                    return "CELP";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_HVXC:                    return "HVXC";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_TTSI:                    return "TTSI";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_MAIN_SYNTHETIC:          return "Main Synthetic";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_WAVETABLE_SYNTHESIS:     return "Wavetable Synthesis";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_GENERAL_MIDI:            return "General MIDI";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_ALGORITHMIC_SYNTHESIS:   return "Algorithmic Synthesis";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_ER_AAC_LC:               return "Error Resilient AAC Low Complexity";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_ER_AAC_LTP:              return "Error Resilient AAC Long Term Prediction";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_ER_AAC_SCALABLE:         return "Error Resilient AAC Scalable";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_ER_TWINVQ:               return "Error Resilient Twin VQ";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_ER_BSAC:                 return "Error Resilient Bit Sliced Arithmetic Coding";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_ER_AAC_LD:               return "Error Resilient AAC Low Delay";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_ER_CELP:                 return "Error Resilient CELP";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_ER_HVXC:                 return "Error Resilient HVXC";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_ER_HILN:                 return "Error Resilient HILN";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_ER_PARAMETRIC:           return "Error Resilient Parametric";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_SSC:                     return "SSC";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_PS:                      return "Parametric Stereo";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_MPEG_SURROUND:           return "MPEG Surround";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_LAYER_1:                 return "MPEG Layer 1";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_LAYER_2:                 return "MPEG Layer 2";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_LAYER_3:                 return "MPEG Layer 3";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_DST:                     return "Direct Stream Transfer";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_ALS:                     return "ALS Lossless Coding";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_SLS:                     return "SLS Scalable Lossless Coding";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_SLS_NON_CORE:            return "SLS Scalable Lossless Coding (Non Core)";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_ER_AAC_ELD:              return "Error Resilient AAC ELD";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_SMR_SIMPLE:              return "SMR Simple";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_SMR_MAIN:                return "SMR Main";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_USAC:                    return "USAC";
        case AP4_MPEG4_AUDIO_OBJECT_TYPE_SAOC:                    return "SAOC";
        default:                                                  return "UNKNOWN";
    }
}

/*----------------------------------------------------------------------
|   AP4_SubtitleSampleDescription::AP4_SubtitleSampleDescription
+---------------------------------------------------------------------*/
AP4_SubtitleSampleDescription::AP4_SubtitleSampleDescription(AP4_UI32    format,
                                                             const char* namespce,
                                                             const char* schema_location,
                                                             const char* image_mime_type) :
    AP4_SampleDescription(AP4_SampleDescription::TYPE_SUBTITLES, format, NULL),
    m_Namespace(namespce),
    m_SchemaLocation(schema_location),
    m_ImageMimeType(image_mime_type)
{
}

/*----------------------------------------------------------------------
|   AP4_SubtitleSampleDescription::Clone
+---------------------------------------------------------------------*/
AP4_SampleDescription*
AP4_SubtitleSampleDescription::Clone(AP4_Result* result)
{
    if (result) *result = AP4_SUCCESS;
    return new AP4_SubtitleSampleDescription(m_Format,
                                             m_Namespace.GetChars(),
                                             m_SchemaLocation.GetChars(),
                                             m_ImageMimeType.GetChars());
}

/*----------------------------------------------------------------------
|   AP4_SubtitleSampleDescription::ToAtom
+---------------------------------------------------------------------*/
AP4_Atom*
AP4_SubtitleSampleDescription::ToAtom() const
{
    return new AP4_SubtitleSampleEntry(m_Format,
                                       m_Namespace.GetChars(),
                                       m_SchemaLocation.GetChars(),
                                       m_ImageMimeType.GetChars());
}
    

