29#include <libavutil/avutil.h>
32static uint32_t
ReadBytes(
const uint8_t *data,
int count)
36 for (
int i = 0; i < count; i++) {
62 .MatchSyncWord = [](
const uint8_t* data) ->
bool {
63 constexpr uint32_t MPEG_AUDIO_SYNC_WORD = 0xFF'E000;
64 constexpr uint32_t MPEG_AUDIO_VERSION_FORBIDDEN_VALUE = 0x00'0800;
65 constexpr uint32_t MPEG_AUDIO_LAYER_DESCRIPTION_FORBIDDEN_VALUE = 0x00'0000;
66 constexpr uint32_t MPEG_AUDIO_BITRATE_INDEX_FORBIDDEN_VALUE = 0x00'00F0;
69 return (syncWord & 0b1111'1111'1110'0000'0000'0000) == MPEG_AUDIO_SYNC_WORD &&
70 (syncWord & 0b0000'0000'0001'1000'0000'0000) != MPEG_AUDIO_VERSION_FORBIDDEN_VALUE &&
71 (syncWord & 0b0000'0000'0000'0110'0000'0000) != MPEG_AUDIO_LAYER_DESCRIPTION_FORBIDDEN_VALUE &&
72 (syncWord & 0b0000'0000'0000'0000'1111'0000) != MPEG_AUDIO_BITRATE_INDEX_FORBIDDEN_VALUE;
74 .GetFrameSize = [](
const uint8_t* data) ->
int {
75 constexpr uint16_t BitRateTable[2][4][16] = {
78 {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0},
79 {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0},
80 {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0}},
83 {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0},
84 {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
85 {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0}
88 constexpr uint16_t SampleRateTable[4] = {44100, 48000, 32000, 0};
90 int mpeg2 = !(data[1] & 0x08) && (data[1] & 0x10);
91 int mpeg25 = !(data[1] & 0x08) && !(data[1] & 0x10);
92 int layer = 4 - ((data[1] >> 1) & 0x03);
93 int bitRateIndex = (data[2] >> 4) & 0x0F;
94 int sampleRateIndex = (data[2] >> 2) & 0x03;
95 int padding = (data[2] >> 1) & 0x01;
97 int sampleRate = SampleRateTable[sampleRateIndex];
99 throw std::invalid_argument(
"MPEG: invalid sample rate");
101 sampleRate >>= mpeg2;
102 sampleRate >>= mpeg25;
104 int bitRate = BitRateTable[mpeg2 | mpeg25][layer][bitRateIndex];
106 throw std::invalid_argument(
"MPEG: invalid bit rate");
112 frameSize = (12 * bitRate) / sampleRate;
113 frameSize = (frameSize + padding) * 4;
118 frameSize = (144 * bitRate) / sampleRate;
119 frameSize = frameSize + padding;
125 {AV_CODEC_ID_AAC_LATM, {
127 .MatchSyncWord = [](
const uint8_t* data) ->
bool {
128 constexpr uint32_t LAOS_SYNC_WORD_MASK = 0xFFE000;
129 constexpr uint32_t LAOS_SYNC_WORD = 0x2B7 << (24-11);
132 return (syncWord & LAOS_SYNC_WORD_MASK) == LAOS_SYNC_WORD;
134 .GetFrameSize = [](
const uint8_t* data) ->
int {
135 return ((data[1] & 0x1F) << 8) + data[2] + 3;
140 .MatchSyncWord = [](
const uint8_t* data) ->
bool {
141 constexpr uint32_t AC3_SYNC_WORD_MASK = 0xFFFF00;
142 constexpr uint32_t AC3_SYNC_WORD = 0x0B77 << (24-16);
145 return (syncWord & AC3_SYNC_WORD_MASK) == AC3_SYNC_WORD &&
146 data[5] <= (10 << 3);
148 .GetFrameSize = [](
const uint8_t* data) ->
int {
149 constexpr uint16_t Ac3FrameSizeTable[38][3] = {
150 {64, 69, 96}, {64, 70, 96}, {80, 87, 120}, {80, 88, 120},
151 {96, 104, 144}, {96, 105, 144}, {112, 121, 168}, {112, 122, 168},
152 {128, 139, 192}, {128, 140, 192}, {160, 174, 240}, {160, 175, 240},
153 {192, 208, 288}, {192, 209, 288}, {224, 243, 336}, {224, 244, 336},
154 {256, 278, 384}, {256, 279, 384}, {320, 348, 480}, {320, 349, 480},
155 {384, 417, 576}, {384, 418, 576}, {448, 487, 672}, {448, 488, 672},
156 {512, 557, 768}, {512, 558, 768}, {640, 696, 960}, {640, 697, 960},
157 {768, 835, 1152}, {768, 836, 1152}, {896, 975, 1344}, {896, 976, 1344},
158 {1024, 1114, 1536}, {1024, 1115, 1536}, {1152, 1253, 1728},
159 {1152, 1254, 1728}, {1280, 1393, 1920}, {1280, 1394, 1920},
162 int fscod = data[4] >> 6;
164 throw std::invalid_argument(
"AC3: invalid sample rate");
166 int frmsizcod = data[4] & 0x3F;
168 throw std::invalid_argument(
"AC3: invalid frame size");
170 return Ac3FrameSizeTable[frmsizcod][fscod] * 2;
175 .MatchSyncWord = [](
const uint8_t* data) ->
bool {
176 constexpr uint32_t AC3_SYNC_WORD = 0x0B77 << (24-16);
179 return (syncWord & 0xFFFF00) == AC3_SYNC_WORD && data[5] > (10 << 3);
181 .GetFrameSize = [](
const uint8_t* data) ->
int {
182 if ((data[4] & 0xF0) == 0xF0)
183 throw std::invalid_argument(
"AC3: invalid fscod fscod2");
185 return (((data[2] & 0x07) << 8) + data[3] + 1) * 2;
190 .MatchSyncWord = [](
const uint8_t* data) ->
bool {
191 constexpr uint32_t ADTS_SYNC_WORD = 0xFFF000;
192 constexpr uint32_t ADTS_LAYER = 0x000000;
193 constexpr uint32_t ADTS_SAMPLING_FREQUENCY_FORBIDDEN_VALUE = 15 << 6;
196 return (syncWord & 0b1111'1111'1111'0000'0000'0000) == ADTS_SYNC_WORD &&
197 (syncWord & 0b0000'0000'0000'0110'0000'0000) == ADTS_LAYER &&
198 (syncWord & 0b0000'0000'0000'0011'1100'0000) != ADTS_SAMPLING_FREQUENCY_FORBIDDEN_VALUE;
200 .GetFrameSize = [](
const uint8_t* data) ->
int {
201 return ((data[3] & 0x03) << 11) | ((data[4] & 0xFF) << 3) | ((data[5] & 0xE0) >> 5);
216 : m_data(data), m_size(size)
348 if (!PesHasLength(
m_data))
369 AVPacket *avpkt = av_packet_alloc();
372 LOGFATAL(
"pes: %s: out of memory while allocating AVPacket", __FUNCTION__);
374 if (av_new_packet(avpkt, size))
375 LOGFATAL(
"pes: %s: out of memory while allocating AVPacket payload", __FUNCTION__);
378 memset(&avpkt->data[size], 0, AV_INPUT_BUFFER_PADDING_SIZE);
417 m_codec = AV_CODEC_ID_MPEG2VIDEO;
423 if (size > &codecPayload[7] - fragment) {
424 if ( codecPayload[0] ==
H264_STREAM_TYPE && (codecPayload[1] == 0x10 || codecPayload[1] == 0xF0 || codecPayload[7] == 0x64))
426 else if (codecPayload[0] ==
HEVC_STREAM_TYPE && (codecPayload[1] == 0x10 || codecPayload[1] == 0x50 || codecPayload[7] == 0x40))
430 return m_codec != AV_CODEC_ID_NONE;
465 if (detectedCodec == AV_CODEC_ID_NONE)
468 LOGERROR(
"pes: %s: audio codec changed unexpectedly from %d to %d", __FUNCTION__, avcodec_get_name(
m_codec), avcodec_get_name(detectedCodec));
482 }
catch (
const std::invalid_argument &e) {
483 LOGWARNING(
"pes: %s: garbage in audio stream received: %s", __FUNCTION__, e.what());
507 LOGDEBUG(
"pes: %s: truncated %d of %d bytes while searching for sync word", __FUNCTION__, sizeBeforeTruncation -
m_buffer.
GetSize(), sizeBeforeTruncation);
531 if (firstFrame.
codecId == AV_CODEC_ID_NONE)
537 int secondSyncWord = firstFrame.
pos + sizeOfFirstFrame;
552 }
catch (
const std::invalid_argument &e) {
575 for (
int i = 0; i < size; i++) {
577 if (detectedCodec != AV_CODEC_ID_NONE)
598 if (size >= codecInfo.minSize && codecInfo.MatchSyncWord(syncWord)) {
603 return AV_CODEC_ID_NONE;
671 if (
m_data.empty() || amount == 0)
679 auto it =
m_pts.upper_bound(amount);
680 if (it ==
m_pts.begin())
681 LOGFATAL(
"pes: %s: %s: no PTS value found for position 0 after erasing %zu bytes", __FUNCTION__,
m_identifier, amount);
684 smallestPts = it->second;
687 std::map<size_t, int64_t> adjusted_pts;
688 for (
const auto& [pos, pts] :
m_pts) {
690 adjusted_pts[pos - amount] = pts;
692 m_pts = std::move(adjusted_pts);
694 m_pts[0] = smallestPts;
711 return m_pts.begin()->second;
bool IsValid()
Check if the PES packet is valid.
static constexpr uint32_t PES_PACKET_START_CODE_PREFIX
bool m_valid
flag indicating if the PES packet is valid
int GetPayloadSize()
Get the size of the PES payload.
int m_size
size of the PES packet
cPes(const uint8_t *, int)
Construct a PES packet parser.
int GetPacketLength()
Get the total length of the PES packet.
const uint8_t * GetPayload()
Get a pointer to the PES payload data.
const uint8_t * m_data
pointer to the raw PES packet data
virtual bool IsStreamIdValid()=0
void Init()
Initialize and validate the PES packet.
bool IsHeaderValid()
Check if the PES header is valid.
bool HasPts()
Check if the PES packet contains a Presentation Time Stamp (PTS)
int64_t GetPts()
Get the Presentation Time Stamp (PTS) from the PES header.
void Push(const uint8_t *, int, int64_t)
Push data into the PTS tracking buffer.
std::vector< uint8_t > m_data
Byte buffer.
std::map< size_t, int64_t > m_pts
Map of buffer positions to PTS values.
void Erase(size_t)
Erase data from the beginning of the buffer.
const char * m_identifier
int64_t GetPts()
Get the PTS value for the current buffer position.
AVPacket * PopAvPacket() override
Pop an audio AVPacket from the reassembly buffer.
static constexpr int MAX_HEADER_SIZE
AVCodecID TruncateBufferUntilFirstValidData()
Truncate buffer until the first valid audio frame.
AVCodecID DetectCodecFromSyncWord(const uint8_t *, int)
Detect audio codec from sync word pattern.
SyncWordInfo FindSyncWord(const uint8_t *, int)
Find the first audio sync word in data.
SyncWordInfo FindTwoConsecutiveFramesWithSameSyncWord()
Find two consecutive audio frames with the same sync word.
bool m_ptsInvalid
flag indicating if PTS is invalid for current buffer, because it was truncated
int GetFrameSizeForCodec(AVCodecID, const uint8_t *)
Get the frame size for a given codec and frame header.
static constexpr uint32_t VIDEO_FRAME_START_CODE
static constexpr int VIDEO_FRAME_START_CODE_LEN
static constexpr uint8_t H264_STREAM_TYPE
static constexpr uint8_t MPEG2_STREAM_TYPE
bool HasLeadingZero(const uint8_t *, int)
Check if video data has a leading zero byte before the start code.
static constexpr uint8_t HEVC_STREAM_TYPE
bool ParseCodecHeader(const uint8_t *, int)
Parse video codec header to detect codec type.
AVCodecID m_codec
detected codec ID
virtual AVPacket * PopAvPacket()=0
int64_t m_lastPoppedPts
PTS of the last popped AVPacket.
cPtsTrackingBuffer m_buffer
fragmentation buffer
void Reset()
Reset the reassembly buffer.
Logger class header file.
#define LOGFATAL
Logger macros.
Misc function header file.
static uint32_t ReadBytes(const uint8_t *data, int count)
static const std::map< AVCodecID, CodecInfo > AudioCodecMap
Map of audio codec information Key: AVCodecID Value: CodecInfo with sync word detection and frame siz...
PES packet parser header.
Codec information structure Contains lambdas for sync word detection and frame size calculation.
std::function< int(const uint8_t *)> GetFrameSize
std::function< bool(const uint8_t *)> MatchSyncWord
Information about a detected audio sync word.
AVCodecID codecId
Detected codec ID.
int pos
Position of sync word in buffer.