From 8eba386306c856b328b909d7c6724f4a41bc80f5 Mon Sep 17 00:00:00 2001 From: CastagnaIT Date: Mon, 28 Apr 2025 18:01:11 +0200 Subject: [PATCH] wip --- src/Session.cpp | 2 + src/demuxers/ADTSReader.cpp | 57 +++++++++++- src/demuxers/ADTSReader.h | 2 + src/parser/CodecParser.cpp | 169 ++++++++++++++++++++++++++++++++++++ src/parser/CodecParser.h | 8 ++ src/utils/Utils.h | 12 +-- 6 files changed, 241 insertions(+), 9 deletions(-) diff --git a/src/Session.cpp b/src/Session.cpp index 92bbdfa6c..d850758f1 100644 --- a/src/Session.cpp +++ b/src/Session.cpp @@ -577,6 +577,8 @@ void SESSION::CSession::UpdateStream(CStream& stream) stream.m_info.SetCodecName(CODEC::NAME_DTS); else if (CODEC::Contains(codecs, CODEC::FOURCC_AC_3, codecStr)) stream.m_info.SetCodecName(CODEC::NAME_AC3); + else if (CODEC::Contains(codecs, CODEC::FOURCC_AC_4, codecStr)) + stream.m_info.SetCodecName(CODEC::NAME_AC4); else if (CODEC::Contains(codecs, CODEC::FOURCC_EC_3, codecStr)) { stream.m_info.SetCodecName(CODEC::NAME_EAC3); diff --git a/src/demuxers/ADTSReader.cpp b/src/demuxers/ADTSReader.cpp index 9d8f87b84..0c603060c 100644 --- a/src/demuxers/ADTSReader.cpp +++ b/src/demuxers/ADTSReader.cpp @@ -137,11 +137,12 @@ ADTSFrame::ADTSFrameInfo ADTSFrame::GetFrameInfo(AP4_ByteStream* stream) case AdtsType::AC3: ParseAc3Header(stream, frameInfo); break; + case AdtsType::AC4: + ParseAc4Header(stream, frameInfo); + break; case AdtsType::EAC3: ParseEc3Header(stream, frameInfo); break; - case AdtsType::AC4: - break; default: break; } @@ -157,10 +158,10 @@ bool ADTSFrame::parse(AP4_ByteStream* stream) return ParseAac(stream); case AdtsType::AC3: return ParseAc3(stream); + case AdtsType::AC4: + return ParseAc4(stream); case AdtsType::EAC3: return ParseEc3(stream); - case AdtsType::AC4: - return false; default: return false; } @@ -255,6 +256,50 @@ bool ADTSFrame::ParseAc3Header(AP4_ByteStream* stream, ADTSFrameInfo& frameInfo) return true; } +bool ADTSFrame::ParseAc4(AP4_ByteStream* stream) +{ + if (!ParseAc4Header(stream, m_frameInfo)) + return false; + + m_summedFrameCount += m_frameInfo.m_frameCount; + + // rewind stream to beginning of syncframe + AP4_Position currentPos; + stream->Tell(currentPos); + stream->Seek(currentPos - (AP4_AC4_HEADER_SIZE)); + + m_dataBuffer.SetDataSize(m_frameInfo.m_frameSize); + if (!AP4_SUCCEEDED(stream->Read(m_dataBuffer.UseData(), m_dataBuffer.GetDataSize()))) + return false; + + AdjustStreamForPadding(stream); + return true; +} + +bool ADTSFrame::ParseAc4Header(AP4_ByteStream* stream, ADTSFrameInfo& frameInfo) +{ + AP4_DataBuffer buffer; + buffer.SetDataSize(AP4_AC3_HEADER_SIZE); + + if (!AP4_SUCCEEDED(stream->Read(buffer.UseData(), AP4_AC3_HEADER_SIZE))) + return false; + + CAdaptiveAc3Parser parser; + AP4_Size sz = buffer.GetDataSize(); + parser.Feed(buffer.GetData(), &sz); + + AP4_Ac3Frame frame; + AP4_Result result = parser.FindFrameHeader(frame); + if (!AP4_SUCCEEDED(result)) + return false; + + frameInfo.m_frameSize = frame.m_Info.m_FrameSize; + frameInfo.m_frameCount = 256u * frame.m_Info.m_ChannelCount; + frameInfo.m_sampleRate = frame.m_Info.m_SampleRate; + frameInfo.m_channels = frame.m_Info.m_ChannelCount; + return true; +} + bool ADTSFrame::ParseEc3(AP4_ByteStream* stream) { if (!ParseEc3Header(stream, m_frameInfo)) @@ -379,6 +424,10 @@ bool ADTSReader::GetInformation(kodi::addon::InputstreamInfo& info) { codecName = CODEC::NAME_AC3; } + else if (frameInfo.m_codecType == AdtsType::AC4) + { + codecName = CODEC::NAME_AC4; + } else if (frameInfo.m_codecType == AdtsType::EAC3) { codecName = CODEC::NAME_EAC3; diff --git a/src/demuxers/ADTSReader.h b/src/demuxers/ADTSReader.h index 022f549c1..43f5c5c17 100644 --- a/src/demuxers/ADTSReader.h +++ b/src/demuxers/ADTSReader.h @@ -80,6 +80,8 @@ class ATTR_DLL_LOCAL ADTSFrame bool ParseAacHeader(AP4_ByteStream* stream, ADTSFrameInfo& frameInfo); bool ParseAc3(AP4_ByteStream* stream); bool ParseAc3Header(AP4_ByteStream* stream, ADTSFrameInfo& frameInfo); + bool ParseAc4(AP4_ByteStream* stream); + bool ParseAc4Header(AP4_ByteStream* stream, ADTSFrameInfo& frameInfo); bool ParseEc3(AP4_ByteStream* stream); bool ParseEc3Header(AP4_ByteStream* stream, ADTSFrameInfo& frameInfo); void reset(); diff --git a/src/parser/CodecParser.cpp b/src/parser/CodecParser.cpp index bc7d1c549..43cd99d3e 100644 --- a/src/parser/CodecParser.cpp +++ b/src/parser/CodecParser.cpp @@ -157,6 +157,175 @@ AP4_Result CAdaptiveAc3Parser::FindFrameHeader(AP4_Ac3Frame& frame) return AP4_SUCCESS; } +AP4_Result CAdaptiveAc4Parser::FindFrameHeader(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_Result CAdaptiveEac3Parser::FindFrameHeader(AP4_Eac3Frame& frame) { bool dependent_stream_exist = false; diff --git a/src/parser/CodecParser.h b/src/parser/CodecParser.h index 3e9168344..d69e8db3e 100644 --- a/src/parser/CodecParser.h +++ b/src/parser/CodecParser.h @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -44,6 +45,13 @@ class ATTR_DLL_LOCAL CAdaptiveAc3Parser : public AP4_Ac3Parser AP4_Result FindFrameHeader(AP4_Ac3Frame& frame); }; +class ATTR_DLL_LOCAL CAdaptiveAc4Parser : public AP4_Ac4Parser +{ +public: + CAdaptiveAc4Parser() {} + AP4_Result FindFrameHeader(AP4_Ac4Frame& frame); +}; + class ATTR_DLL_LOCAL CAdaptiveEac3Parser : public AP4_Eac3Parser { public: diff --git a/src/utils/Utils.h b/src/utils/Utils.h index cb66f2c00..37be0a03c 100644 --- a/src/utils/Utils.h +++ b/src/utils/Utils.h @@ -93,11 +93,12 @@ constexpr std::array VIDEO_NAME_LIST = {NAME_MPEG1, NAME_MPEG2, NAME_MPEG4, NAME constexpr const char* NAME_AAC = "aac"; constexpr const char* NAME_DTS = "dca"; constexpr const char* NAME_AC3 = "ac3"; +constexpr const char* NAME_AC4 = "ac4"; constexpr const char* NAME_EAC3 = "eac3"; // Enhanced AC-3 constexpr const char* NAME_OPUS = "opus"; constexpr const char* NAME_VORBIS = "vorbis"; -constexpr std::array AUDIO_NAME_LIST = {NAME_AAC, NAME_DTS, NAME_AC3, +constexpr std::array AUDIO_NAME_LIST = {NAME_AAC, NAME_DTS, NAME_AC3, NAME_AC4, NAME_EAC3, NAME_OPUS, NAME_VORBIS}; // Subtitles definitions @@ -132,6 +133,7 @@ constexpr const char* FOURCC_MP4A = "mp4a"; constexpr const char* FOURCC_AAC_ = "aac"; // Generic prefix for all aac* fourcc, e.g. aac, aacl... constexpr const char* FOURCC_AACL = "aacl"; constexpr const char* FOURCC_AC_3 = "ac-3"; +constexpr const char* FOURCC_AC_4 = "ac-4"; constexpr const char* FOURCC_EC_3 = "ec-3"; // Enhanced AC-3 constexpr const char* FOURCC_OPUS = "opus"; constexpr const char* FOURCC_VORB = "vorb"; // Vorbis @@ -143,10 +145,10 @@ constexpr const char* FOURCC_VORB3 = "vor3"; // Vorbis 3 constexpr const char* FOURCC_VORB3P = "vo3+"; // Vorbis 3+ constexpr const char* FOURCC_DTS_ = "dts"; // Generic prefix for all dts* fourcc, e.g. dtsx -constexpr std::array AUDIO_FOURCC_LIST = {FOURCC_MP4A, FOURCC_AAC_, FOURCC_AACL, - FOURCC_AC_3, FOURCC_EC_3, FOURCC_OPUS, FOURCC_VORB, FOURCC_VORB1, - FOURCC_VORB1P, FOURCC_VORB2, FOURCC_VORB2P, FOURCC_VORB3, - FOURCC_VORB3P, FOURCC_DTS_}; +constexpr std::array AUDIO_FOURCC_LIST = {FOURCC_MP4A, FOURCC_AAC_, FOURCC_AACL, FOURCC_AC_3, + FOURCC_AC_4, FOURCC_EC_3, FOURCC_OPUS, FOURCC_VORB, + FOURCC_VORB1, FOURCC_VORB1P, FOURCC_VORB2, FOURCC_VORB2P, + FOURCC_VORB3, FOURCC_VORB3P, FOURCC_DTS_}; // Fourcc subtitles definitions