/***************************************************************** | | AP4 - AC4 Sync Frame Parser | | Copyright 2002-2020 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 "Ap4BitStream.h" #include "Ap4Ac4Parser.h" bool AP4_Ac4Header::m_DeprecatedV0 = true; /*----------------------------------------------------------------------+ | AP4_Ac4Header::AP4_Ac4Header +----------------------------------------------------------------------*/ AP4_Ac4Header::AP4_Ac4Header(const AP4_UI08* bytes, unsigned int size) { /* Read the entire the frame */ AP4_BitReader bits(bytes, size); m_SyncWord = bits.ReadBits(16); m_HeaderSize = 2; if (m_SyncWord == AP4_AC4_SYNC_WORD_CRC){ m_CrcSize = 2; }else { m_CrcSize = 0; } m_FrameSize = bits.ReadBits(16); m_HeaderSize += 2; if (m_FrameSize == 0xFFFF){ m_FrameSize = bits.ReadBits(24); m_HeaderSize += 3; } /* Begin to parse TOC */ m_BitstreamVersion = bits.ReadBits(2); if (m_BitstreamVersion == 3) { m_BitstreamVersion = AP4_Ac4VariableBits(bits, 2); } m_SequenceCounter = bits.ReadBits(10); m_BWaitFrames = bits.ReadBit(); if (m_BWaitFrames) { m_WaitFrames = bits.ReadBits(3); if (m_WaitFrames > 0){ m_BrCode = bits.ReadBits(2);} }else { m_WaitFrames = -1; } m_FsIndex = bits.ReadBit(); m_FrameRateIndex = bits.ReadBits(4); m_BIframeGlobal = bits.ReadBit(); m_BSinglePresentation = bits.ReadBit(); if (m_BSinglePresentation == 1){ m_NPresentations = 1; m_BMorePresentations = 0; }else{ m_BMorePresentations = bits.ReadBit(); if (m_BMorePresentations == 1){ m_NPresentations = AP4_Ac4VariableBits(bits, 2) + 2; }else{ m_NPresentations = 0; } } m_PayloadBase = 0; m_BPayloadBase = bits.ReadBit(); if (m_BPayloadBase == 1){ m_PayloadBase = bits.ReadBits(5) + 1; if (m_PayloadBase == 0x20){ m_PayloadBase += AP4_Ac4VariableBits(bits, 3); } } if (m_BitstreamVersion <= 1){ if (m_DeprecatedV0){ m_DeprecatedV0 = false; printf("Warning: Bitstream version 0 is deprecated\n"); } }else{ m_BProgramId = bits.ReadBit(); if (m_BProgramId == 1){ m_ShortProgramId = bits.ReadBits(16); m_BProgramUuidPresent = bits.ReadBit(); if (m_BProgramUuidPresent == 1){ for (int cnt = 0; cnt < 16; cnt++){ m_ProgramUuid[cnt] = bits.ReadBits(8); } }else { memcpy(m_ProgramUuid,"ONDEL TEAM UUID\0", 16); } }else { m_ShortProgramId = 0; m_BProgramUuidPresent = 0; memcpy(m_ProgramUuid, "ONDEL TEAM UUID\0", 16); } unsigned int maxGroupIndex = 0; unsigned int *firstPresentationSubstreamGroupIndexes = NULL; unsigned int firstPresentationNSubstreamGroups = 0; if (m_NPresentations > 0){ m_PresentationV1 = new AP4_Dac4Atom::Ac4Dsi::PresentationV1[m_NPresentations]; AP4_SetMemory(m_PresentationV1, 0, m_NPresentations * sizeof(m_PresentationV1[0])); } else { m_PresentationV1 = NULL; } // ac4_presentation_v1_info() for (unsigned int pres_idx = 0; pres_idx < m_NPresentations; pres_idx++){ AP4_Dac4Atom::Ac4Dsi::PresentationV1& presentation = m_PresentationV1[pres_idx]; presentation.ParsePresentationV1Info(bits, m_BitstreamVersion, m_FrameRateIndex, pres_idx, maxGroupIndex, &firstPresentationSubstreamGroupIndexes, firstPresentationNSubstreamGroups); } unsigned int bObjorAjoc = 0; unsigned int channelCount = 0; unsigned int speakerGroupIndexMask = 0; AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1 *substream_groups = new AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1[maxGroupIndex + 1]; AP4_SetMemory(substream_groups, 0, (maxGroupIndex + 1) * sizeof(substream_groups[0])); for (unsigned int sg = 0 ; (sg < (maxGroupIndex + 1)) && (m_NPresentations > 0); sg ++) { AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1 &substream_group = substream_groups[sg]; AP4_Result pres_index = GetPresentationIndexBySGIndex(sg); if (pres_index == AP4_FAILURE) { break; } unsigned int localChannelCount = 0; unsigned int frame_rate_factor = (m_PresentationV1[pres_index].d.v1.dsi_frame_rate_multiply_info == 0)? 1: (m_PresentationV1[pres_index].d.v1.dsi_frame_rate_multiply_info * 2); substream_group.ParseSubstreamGroupInfo(bits, m_BitstreamVersion, GetPresentationVersionBySGIndex(sg), Ap4_Ac4SubstreamGroupPartOfDefaultPresentation(sg, firstPresentationSubstreamGroupIndexes, firstPresentationNSubstreamGroups), frame_rate_factor, m_FsIndex, localChannelCount, speakerGroupIndexMask, bObjorAjoc); if (channelCount < localChannelCount) { channelCount = localChannelCount;} } for (unsigned int pres_idx = 0; pres_idx < m_NPresentations; pres_idx++){ m_PresentationV1[pres_idx].d.v1.substream_groups = new AP4_Dac4Atom::Ac4Dsi::SubStreamGroupV1[m_PresentationV1[pres_idx].d.v1.n_substream_groups]; for (unsigned int sg = 0; sg < m_PresentationV1[pres_idx].d.v1.n_substream_groups; sg++){ // TODO: Default Copy Construct Function m_PresentationV1[pres_idx].d.v1.substream_groups[sg] = substream_groups[m_PresentationV1[pres_idx].d.v1.substream_group_indexs[sg]]; m_PresentationV1[pres_idx].d.v1.dolby_atmos_indicator |= m_PresentationV1[pres_idx].d.v1.substream_groups[sg].d.v1.dolby_atmos_indicator; } } delete [] substream_groups; if (bObjorAjoc == 0){ m_ChannelCount = AP4_Ac4ChannelCountFromSpeakerGroupIndexMask(speakerGroupIndexMask); }else { m_ChannelCount = channelCount; } } } AP4_Ac4Header::~AP4_Ac4Header() { if (m_PresentationV1) { assert(m_PresentationV1 != NULL); for (unsigned int pres_idx = 0; pres_idx < m_NPresentations; pres_idx++) { assert(m_PresentationV1[pres_idx].d.v1.substream_groups != NULL); for (int j = 0; j < m_PresentationV1[pres_idx].d.v1.n_substream_groups; j++) delete[] m_PresentationV1[pres_idx].d.v1.substream_groups[j].d.v1.substreams; delete[] m_PresentationV1[pres_idx].d.v1.substream_groups; delete[] m_PresentationV1[pres_idx].d.v1.substream_group_indexs; } delete[] m_PresentationV1; } } /*----------------------------------------------------------------------+ | AP4_Ac4Header::MatchFixed | | Check that two fixed headers are the same | +----------------------------------------------------------------------*/ bool AP4_Ac4Header::MatchFixed(AP4_Ac4Header& frame, AP4_Ac4Header& next_frame) { // Some parameter shall be const which defined in AC-4 in ISO-BMFF specs // TODO: More constraints will be added if ((frame.m_FsIndex == next_frame.m_FsIndex) && (frame.m_FrameRateIndex == next_frame.m_FrameRateIndex)){ return true; }else { return false; } } /*----------------------------------------------------------------------+ | AP4_Ac4Header::Check +----------------------------------------------------------------------*/ AP4_Result AP4_Ac4Header::Check() { // Bitstream version if (m_BitstreamVersion != 2) { return AP4_FAILURE; } if ((m_FsIndex == 0 && m_FrameRateIndex != 13) || (m_FsIndex == 1 && m_FrameRateIndex >13)){ return AP4_FAILURE; } return AP4_SUCCESS; } /*----------------------------------------------------------------------+ | AP4_Ac4Header::GetPresentationVersionBySGIndex +----------------------------------------------------------------------*/ AP4_Result AP4_Ac4Header::GetPresentationVersionBySGIndex(unsigned int substream_group_index) { for (unsigned int idx = 0; idx < m_NPresentations; idx++){ for (unsigned int sg = 0; sg < m_PresentationV1[idx].d.v1.n_substream_groups; sg++){ if (substream_group_index == m_PresentationV1[idx].d.v1.substream_group_indexs[sg]) { return m_PresentationV1[idx].presentation_version; } } } return AP4_FAILURE; } /*----------------------------------------------------------------------+ | AP4_Ac4Header::GetPresentationIndexBySGIndex +----------------------------------------------------------------------*/ AP4_Result AP4_Ac4Header::GetPresentationIndexBySGIndex(unsigned int substream_group_index) { for (unsigned int idx = 0; idx < m_NPresentations; idx++){ for (unsigned int sg = 0; sg < m_PresentationV1[idx].d.v1.n_substream_groups; sg++){ if (substream_group_index == m_PresentationV1[idx].d.v1.substream_group_indexs[sg]) { return idx; } } } return AP4_FAILURE; } /*----------------------------------------------------------------------+ | AP4_Ac4Parser::AP4_Ac4Parser +----------------------------------------------------------------------*/ AP4_Ac4Parser::AP4_Ac4Parser() : m_FrameCount(0) { } /*----------------------------------------------------------------------+ | AP4_Ac4Parser::~AP4_Ac4Parser +----------------------------------------------------------------------*/ AP4_Ac4Parser::~AP4_Ac4Parser() { } /*----------------------------------------------------------------------+ | AP4_Ac4Parser::Reset +----------------------------------------------------------------------*/ AP4_Result AP4_Ac4Parser::Reset() { m_FrameCount = 0; return AP4_SUCCESS; } /*----------------------------------------------------------------------+ | AP4_Ac4Parser::Feed +----------------------------------------------------------------------*/ AP4_Result AP4_Ac4Parser::Feed(const AP4_UI08* buffer, AP4_Size* buffer_size, AP4_Flags flags) { AP4_Size free_space; /* update flags */ m_Bits.m_Flags = flags; /* possible shortcut */ if (buffer == NULL || buffer_size == NULL || *buffer_size == 0) { return AP4_SUCCESS; } /* see how much data we can write */ free_space = m_Bits.GetBytesFree(); if (*buffer_size > free_space) *buffer_size = free_space; if (*buffer_size == 0) return AP4_SUCCESS; /* write the data */ return m_Bits.WriteBytes(buffer, *buffer_size); } /*----------------------------------------------------------------------+ | AP4_Ac4Parser::FindHeader +----------------------------------------------------------------------*/ AP4_Result AP4_Ac4Parser::FindHeader(AP4_UI08* header) { AP4_Size available = m_Bits.GetBytesAvailable(); /* look for the sync pattern */ while (available-- >= AP4_AC4_HEADER_SIZE) { m_Bits.PeekBytes(header, 2); if ((((header[0] << 8) | header[1]) == AP4_AC4_SYNC_WORD_CRC) || (((header[0] << 8) | header[1]) == AP4_AC4_SYNC_WORD)) { /* found a sync pattern, read the entire the header */ m_Bits.PeekBytes(header, AP4_AC4_HEADER_SIZE); return AP4_SUCCESS; } else { m_Bits.SkipBytes(1); } } return AP4_ERROR_NOT_ENOUGH_DATA; } /*----------------------------------------------------------------------+ | AP4_Ac4Parser::GetSyncFrameSize +----------------------------------------------------------------------*/ AP4_UI32 AP4_Ac4Parser::GetSyncFrameSize(AP4_BitReader &bits) { unsigned int sync_word = bits.ReadBits(16); unsigned int frame_size = bits.ReadBits(16); unsigned int head_size = 4; if (frame_size == 0xFFFF){ frame_size = bits.ReadBits(24); head_size += 3; } if (sync_word == AP4_AC4_SYNC_WORD_CRC) { head_size += 2; } return (head_size + frame_size); } /*----------------------------------------------------------------------+ | AP4_Ac4Parser::FindFrame +----------------------------------------------------------------------*/ AP4_Result AP4_Ac4Parser::FindFrame(AP4_Ac4Frame& frame) { unsigned int available; unsigned char raw_header[AP4_AC4_HEADER_SIZE]; AP4_Result result; /* align to the start of the next byte */ m_Bits.ByteAlign(); /* find a frame header */ result = FindHeader(raw_header); if (AP4_FAILED(result)) return result; // duplicated work, just to get the frame size AP4_BitReader tmp_bits(raw_header, AP4_AC4_HEADER_SIZE); unsigned int sync_frame_size = GetSyncFrameSize(tmp_bits); if (sync_frame_size > (AP4_BITSTREAM_BUFFER_SIZE - 1)) { return AP4_ERROR_NOT_ENOUGH_DATA; } /* * Error handling to skip the 'fake' sync word. * - the maximum sync frame size is about (AP4_BITSTREAM_BUFFER_SIZE - 1) bytes. */ if (m_Bits.GetBytesAvailable() < sync_frame_size ) { if (m_Bits.GetBytesAvailable() == (AP4_BITSTREAM_BUFFER_SIZE - 1)) { // skip the sync word, assume it's 'fake' sync word m_Bits.SkipBytes(2); } return AP4_ERROR_NOT_ENOUGH_DATA; } unsigned char *rawframe = new unsigned char[sync_frame_size]; // copy the whole frame becasue toc size is unknown m_Bits.PeekBytes(rawframe, sync_frame_size); /* parse the header */ AP4_Ac4Header ac4_header(rawframe, sync_frame_size); delete[] rawframe; // Place before goto statement to resolve Xcode compiler issue unsigned int bit_rate_mode = 0; /* check the header */ result = ac4_header.Check(); if (AP4_FAILED(result)) { m_Bits.SkipBytes(sync_frame_size); goto fail; } /* check if we have enough data to peek at the next header */ available = m_Bits.GetBytesAvailable(); // TODO: find the proper AP4_AC4_MAX_TOC_SIZE or just parse what this step need ? if (available >= ac4_header.m_FrameSize + ac4_header.m_HeaderSize + ac4_header.m_CrcSize + AP4_AC4_HEADER_SIZE + AP4_AC4_MAX_TOC_SIZE) { // enough to peek at the header of the next frame m_Bits.SkipBytes(ac4_header.m_FrameSize + ac4_header.m_HeaderSize + ac4_header.m_CrcSize); m_Bits.PeekBytes(raw_header, AP4_AC4_HEADER_SIZE); // duplicated work, just to get the frame size AP4_BitReader peak_tmp_bits(raw_header, AP4_AC4_HEADER_SIZE); unsigned int next_sync_frame_size = GetSyncFrameSize(peak_tmp_bits); unsigned char *next_rawframe = new unsigned char[next_sync_frame_size]; // copy the whole frame becasue toc size is unknown if (m_Bits.GetBytesAvailable() < (next_sync_frame_size)) { next_sync_frame_size = m_Bits.GetBytesAvailable(); } m_Bits.PeekBytes(next_rawframe, next_sync_frame_size); m_Bits.SkipBytes(-((int)(ac4_header.m_FrameSize + ac4_header.m_HeaderSize + ac4_header.m_CrcSize))); /* check the header */ AP4_Ac4Header peek_ac4_header(next_rawframe, next_sync_frame_size); delete[] next_rawframe; result = peek_ac4_header.Check(); if (AP4_FAILED(result)) { // TODO: need to reserve current sync frame ? m_Bits.SkipBytes(sync_frame_size + next_sync_frame_size); goto fail; } /* check that the fixed part of this header is the same as the */ /* fixed part of the previous header */ if (!AP4_Ac4Header::MatchFixed(ac4_header, peek_ac4_header)) { // TODO: need to reserve current sync frame ? m_Bits.SkipBytes(sync_frame_size + next_sync_frame_size); goto fail; } } else if (available < (ac4_header.m_FrameSize + ac4_header.m_HeaderSize + ac4_header.m_CrcSize) || (m_Bits.m_Flags & AP4_BITSTREAM_FLAG_EOS) == 0) { // not enough for a frame, or not at the end (in which case we'll want to peek at the next header) return AP4_ERROR_NOT_ENOUGH_DATA; } m_Bits.SkipBytes(ac4_header.m_HeaderSize); /* fill in the frame info */ frame.m_Info.m_HeaderSize = ac4_header.m_HeaderSize; frame.m_Info.m_FrameSize = ac4_header.m_FrameSize; frame.m_Info.m_CRCSize = ac4_header.m_CrcSize; frame.m_Info.m_ChannelCount = ac4_header.m_ChannelCount; frame.m_Info.m_SampleDuration = (ac4_header.m_FsIndex == 0)? 2048 : AP4_Ac4SampleDeltaTable [ac4_header.m_FrameRateIndex]; frame.m_Info.m_MediaTimeScale = (ac4_header.m_FsIndex == 0)? 44100: AP4_Ac4MediaTimeScaleTable[ac4_header.m_FrameRateIndex]; frame.m_Info.m_Iframe = ac4_header.m_BIframeGlobal; /* fill the AC4 DSI info */ frame.m_Info.m_Ac4Dsi.ac4_dsi_version = 1; frame.m_Info.m_Ac4Dsi.d.v1.bitstream_version = ac4_header.m_BitstreamVersion; frame.m_Info.m_Ac4Dsi.d.v1.fs_index = ac4_header.m_FsIndex; frame.m_Info.m_Ac4Dsi.d.v1.fs = AP4_Ac4SamplingFrequencyTable[frame.m_Info.m_Ac4Dsi.d.v1.fs_index]; frame.m_Info.m_Ac4Dsi.d.v1.frame_rate_index = ac4_header.m_FrameRateIndex; frame.m_Info.m_Ac4Dsi.d.v1.b_program_id = ac4_header.m_BProgramId; frame.m_Info.m_Ac4Dsi.d.v1.short_program_id = ac4_header.m_ShortProgramId; frame.m_Info.m_Ac4Dsi.d.v1.b_uuid = ac4_header.m_BProgramUuidPresent; AP4_CopyMemory(frame.m_Info.m_Ac4Dsi.d.v1.program_uuid, ac4_header.m_ProgramUuid, 16); // Calcuate the bit rate mode according to ETSI TS 103 190-2 V1.2.1 Annex B if (ac4_header.m_WaitFrames == 0) { bit_rate_mode = 1;} else if (ac4_header.m_WaitFrames >= 1 && ac4_header.m_WaitFrames <= 6) { bit_rate_mode = 2;} else if (ac4_header.m_WaitFrames > 6) { bit_rate_mode = 3;} frame.m_Info.m_Ac4Dsi.d.v1.ac4_bitrate_dsi.bit_rate_mode = bit_rate_mode; frame.m_Info.m_Ac4Dsi.d.v1.ac4_bitrate_dsi.bit_rate = 0; // unknown, fixed value now frame.m_Info.m_Ac4Dsi.d.v1.ac4_bitrate_dsi.bit_rate_precision = 0xffffffff; // unknown, fixed value now frame.m_Info.m_Ac4Dsi.d.v1.n_presentations = ac4_header.m_NPresentations; frame.m_Info.m_Ac4Dsi.d.v1.presentations = ac4_header.m_PresentationV1; /* set the frame source */ frame.m_Source = &m_Bits; return AP4_SUCCESS; fail: /* skip the header and return (only skip the first byte in */ /* case this was a false header that hides one just after) */ return AP4_ERROR_CORRUPTED_BITSTREAM; } /*----------------------------------------------------------------------+ | AP4_Ac4Parser::GetBytesFree +----------------------------------------------------------------------*/ AP4_Size AP4_Ac4Parser::GetBytesFree() { return (m_Bits.GetBytesFree()); } /*----------------------------------------------------------------------+ | AP4_Ac4Parser::GetBytesAvailable +----------------------------------------------------------------------*/ AP4_Size AP4_Ac4Parser::GetBytesAvailable() { return (m_Bits.GetBytesAvailable()); }