-
Notifications
You must be signed in to change notification settings - Fork 254
Implement AC4 support #1838
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: Piers
Are you sure you want to change the base?
Implement AC4 support #1838
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The AC4 header parser is using
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||
AP4_Size sz = buffer.GetDataSize(); | ||||||
parser.Feed(buffer.GetData(), &sz); | ||||||
|
||||||
AP4_Ac3Frame frame; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You’re instantiating an Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||
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; | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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]; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nitpick] Consider using a Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||
|
||||||
// copy the whole frame becasue toc size is unknown | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix typo: replace “becasue” with “because”.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||
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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix typo: replace “Calcuate” with “Calculate”.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||
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; | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -7,6 +7,7 @@ | |||||
*/ | ||||||
|
||||||
#include <bento4/Ap4Ac3Parser.h> | ||||||
#include <bento4/Ap4Ac4Parser.h> | ||||||
#include <bento4/Ap4AdtsParser.h> | ||||||
#include <bento4/Ap4Eac3Parser.h> | ||||||
#include <kodi/addon-instance/Inputstream.h> | ||||||
|
@@ -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); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nitpick] Mark this method as
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||
}; | ||||||
|
||||||
class ATTR_DLL_LOCAL CAdaptiveEac3Parser : public AP4_Eac3Parser | ||||||
{ | ||||||
public: | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Parsing AC4 headers should use
AP4_AC4_HEADER_SIZE
instead ofAP4_AC3_HEADER_SIZE
to allocate the correct buffer size.Copilot uses AI. Check for mistakes.