18#include <catch2/catch_test_macros.hpp>
22#include <libavutil/avutil.h>
32 std::vector<uint8_t> data;
40 data.push_back(streamId);
43 data.push_back((pesLength >> 8) & 0xFF);
44 data.push_back(pesLength & 0xFF);
79 for (
int i = 0; i < 20; i++) {
90 if (withLeadingZero) {
102 for (
int i = 0; i < 20; i++) {
103 data.push_back(0x00);
113 if (withLeadingZero) {
114 data.push_back(0x00);
118 data.push_back(0x00);
119 data.push_back(0x00);
120 data.push_back(0x01);
121 data.push_back(0x46);
122 data.push_back(0x10);
125 for (
int i = 0; i < 20; i++) {
126 data.push_back(0x00);
137 for (
int i = 0; i < 20; i++) {
138 data.push_back(0xFF);
145 SECTION(
"Construct with valid data") {
152 SECTION(
"Construct with empty data") {
161 SECTION(
"Valid PES header") {
168 SECTION(
"Invalid PES header - wrong start code") {
169 std::vector<uint8_t> data = {0x00, 0x00, 0x02, 0xE0, 0x00, 0x00};
175 SECTION(
"Invalid PES header - one byte too short") {
176 std::vector<uint8_t> data = {0x00, 0x00, 0x01, 0xE0, 0x00, 0x10, 0xAA, 0xBB};
182 SECTION(
"Stream type without PES extension") {
183 std::vector<uint8_t> data;
186 data.push_back(0x00);
187 data.push_back(0x00);
188 data.push_back(0x01);
191 data.push_back(0xBE);
194 data.push_back(0x00);
195 data.push_back(0x01);
198 data.push_back(0xAA);
206TEST_CASE(
"cPesVideo - Stream type detection",
"[pes]") {
207 SECTION(
"Video stream detection") {
209 for (uint8_t
id = 0xE0;
id <= 0xEF;
id++) {
217 SECTION(
"Audio stream detection") {
219 for (uint8_t
id = 0xC0;
id <= 0xCF;
id++) {
227 SECTION(
"Neither audio nor video") {
237 SECTION(
"Get PTS from packet with PTS") {
241 int64_t pts = pes.
GetPts();
243 REQUIRE(pts == 9000);
246 SECTION(
"Get PTS from packet without PTS") {
250 int64_t pts = pes.
GetPts();
257 SECTION(
"Get payload from MPEG2 packet") {
265 REQUIRE(payload !=
nullptr);
266 REQUIRE(payload[0] == 0x00);
267 REQUIRE(payload[1] == 0x00);
268 REQUIRE(payload[2] == 0x01);
270 REQUIRE(payloadSize == 24);
273 SECTION(
"Payload size consistency") {
281 int headerSize = payload - data.data();
282 REQUIRE(headerSize + payloadSize ==
static_cast<int>(data.size()));
287 SECTION(
"Get packet length for unbounded MPEG2 (length field = 0)") {
294 SECTION(
"Get packet length for unbounded H.264 (length field = 0)") {
301 SECTION(
"Get packet length with specified length field") {
304 uint16_t pesPayloadLength = 20;
308 for (
int i = data.size() - 6; i < pesPayloadLength; i++) {
309 data.push_back(0x00);
317 SECTION(
"Get packet length for packet with PTS and specified length") {
321 uint16_t pesPayloadLength = 44;
325 int currentSize = data.size();
326 int targetTotalSize = 6 + pesPayloadLength;
327 for (
int i = currentSize; i < targetTotalSize; i++) {
328 data.push_back(0x00);
336 SECTION(
"Get packet length when input buffer is larger than PES packet") {
338 uint16_t pesPayloadLength = 20;
342 for (
int i = data.size() - 6; i < pesPayloadLength; i++) {
343 data.push_back(0xAA);
347 for (
int i = 0; i < 50; i++) {
348 data.push_back(0xFF);
357 SECTION(
"Unbounded packet with buffer larger than actual data") {
362 for (
int i = 0; i < 30; i++) {
363 data.push_back(0xAA);
367 int actualSize = data.size();
370 for (
int i = 0; i < 50; i++) {
371 data.push_back(0xFF);
380 SECTION(
"Get packet length for audio packet with specified length") {
382 uint16_t pesPayloadLength = 30;
386 for (
int i = data.size() - 6; i < pesPayloadLength; i++) {
387 data.push_back(0xFF);
396TEST_CASE(
"cPesAudio - Audio stream handling",
"[pes]") {
397 SECTION(
"Audio stream validation") {
404 SECTION(
"Private stream validation (0xBD)") {
413 SECTION(
"Parse very short packet") {
414 std::vector<uint8_t> data = {0x00, 0x00, 0x01, 0xE0};
421 SECTION(
"Parse packet with no payload") {
434TEST_CASE(
"cReassemblyBufferVideo - MPEG2 codec detection",
"[reassembly][video]") {
435 SECTION(
"Detect MPEG2 video codec") {
439 std::vector<uint8_t> fragment = {0x00, 0x00, 0x01, 0xB3, 0x00, 0x00, 0x00, 0x00};
442 REQUIRE(buffer.
GetCodec() == AV_CODEC_ID_MPEG2VIDEO);
446TEST_CASE(
"cReassemblyBufferVideo - H.264 codec detection",
"[reassembly][video]") {
447 SECTION(
"Detect H.264 without leading zero") {
451 std::vector<uint8_t> fragment = {0x00, 0x00, 0x01, 0x09, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64};
454 REQUIRE(buffer.
GetCodec() == AV_CODEC_ID_H264);
457 SECTION(
"Detect H.264 with leading zero") {
461 std::vector<uint8_t> fragment = {0x00, 0x00, 0x00, 0x01, 0x09, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64};
464 REQUIRE(buffer.
GetCodec() == AV_CODEC_ID_H264);
468TEST_CASE(
"cReassemblyBufferVideo - HEVC codec detection",
"[reassembly][video]") {
469 SECTION(
"Detect HEVC without leading zero") {
473 std::vector<uint8_t> fragment = {0x00, 0x00, 0x01, 0x46, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40};
476 REQUIRE(buffer.
GetCodec() == AV_CODEC_ID_HEVC);
479 SECTION(
"Detect HEVC with leading zero") {
483 std::vector<uint8_t> fragment = {0x00, 0x00, 0x00, 0x01, 0x46, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40};
486 REQUIRE(buffer.
GetCodec() == AV_CODEC_ID_HEVC);
490TEST_CASE(
"cReassemblyBufferVideo - Unknown codec",
"[reassembly][video]") {
491 SECTION(
"No start code present") {
494 std::vector<uint8_t> fragment = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
496 REQUIRE(buffer.
ParseCodecHeader(fragment.data(), fragment.size()) ==
false);
497 REQUIRE(buffer.
GetCodec() == AV_CODEC_ID_NONE);
500 SECTION(
"Start code present but unknown codec type") {
503 std::vector<uint8_t> fragment = {0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00};
505 REQUIRE(buffer.
ParseCodecHeader(fragment.data(), fragment.size()) ==
false);
506 REQUIRE(buffer.
GetCodec() == AV_CODEC_ID_NONE);
510TEST_CASE(
"cReassemblyBufferVideo - HasLeadingZero detection",
"[reassembly][video]") {
511 SECTION(
"Detect leading zero with H.264") {
515 std::vector<uint8_t> fragment = {0x00, 0x00, 0x00, 0x01, 0x09, 0x10, 0x00, 0x00};
517 REQUIRE(buffer.
HasLeadingZero(fragment.data(), fragment.size()) ==
true);
520 SECTION(
"No leading zero - normal start code") {
524 std::vector<uint8_t> fragment = {0x00, 0x00, 0x01, 0x09, 0x10};
526 REQUIRE(buffer.
HasLeadingZero(fragment.data(), fragment.size()) ==
false);
529 SECTION(
"Leading zero with HEVC") {
533 std::vector<uint8_t> fragment = {0x00, 0x00, 0x00, 0x01, 0x46, 0x10};
535 REQUIRE(buffer.
HasLeadingZero(fragment.data(), fragment.size()) ==
true);
538 SECTION(
"Data too short") {
542 std::vector<uint8_t> fragment = {0x00, 0x00, 0x00, 0x01};
544 REQUIRE(buffer.
HasLeadingZero(fragment.data(), fragment.size()) ==
false);
547 SECTION(
"First byte not zero") {
551 std::vector<uint8_t> fragment = {0xFF, 0x00, 0x00, 0x01, 0x09};
553 REQUIRE(buffer.
HasLeadingZero(fragment.data(), fragment.size()) ==
false);
557TEST_CASE(
"cReassemblyBufferVideo - Push and drain",
"[reassembly][video]") {
558 SECTION(
"Push video data and create AVPacket") {
562 std::vector<uint8_t> fragment = {0x00, 0x00, 0x01, 0xB3, 0xAA, 0xBB, 0xCC, 0xDD};
565 buffer.
Push(fragment.data(), fragment.size(), 9000);
570 REQUIRE(pkt !=
nullptr);
571 REQUIRE(pkt->pts == 9000);
572 REQUIRE(pkt->size >= 8);
574 av_packet_free(&pkt);
577 SECTION(
"Push H.264") {
581 std::vector<uint8_t> fragment = {0x00, 0x00, 0x00, 0x01, 0x09, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64};
584 buffer.
Push(fragment.data(), fragment.size(), 12000);
588 REQUIRE(pkt !=
nullptr);
589 REQUIRE(pkt->pts == 12000);
591 REQUIRE(pkt->size == 12);
593 REQUIRE(pkt->data[0] == 0x00);
594 REQUIRE(pkt->data[1] == 0x00);
595 REQUIRE(pkt->data[2] == 0x00);
596 REQUIRE(pkt->data[3] == 0x01);
597 REQUIRE(pkt->data[4] == 0x09);
599 av_packet_free(&pkt);
607TEST_CASE(
"cReassemblyBufferAudio - MP2 codec detection",
"[reassembly][audio]") {
608 SECTION(
"Detect MP2 audio codec") {
613 std::vector<uint8_t> fragment = { 0xFF, 0xF3, 0x44, 0xC0 };
619TEST_CASE(
"cReassemblyBufferAudio - AC3 codec detection",
"[reassembly][audio]") {
620 SECTION(
"Detect AC3 audio codec") {
624 std::vector<uint8_t> fragment = { 0x0B, 0x77, 0x00, 0x00, 0x00, 0x00 };
629 SECTION(
"Detect E-AC3 audio codec") {
633 std::vector<uint8_t> fragment = { 0x0B, 0x77, 0x00, 0x00, 0x00, 0x51 };
639TEST_CASE(
"cReassemblyBufferAudio - AAC LATM codec detection",
"[reassembly][audio]") {
640 SECTION(
"Detect AAC LATM codec") {
644 std::vector<uint8_t> fragment = { 0x56, 0xE0, 0x00 };
650TEST_CASE(
"cReassemblyBufferAudio - ADTS codec detection",
"[reassembly][audio]") {
651 SECTION(
"Detect ADTS codec") {
655 std::vector<uint8_t> fragment = { 0xFF, 0xF1, 0x50, 0x80, 0x00, 0x1F, 0xFC };
661TEST_CASE(
"cReassemblyBufferAudio - Private stream handling",
"[reassembly][audio]") {
662 SECTION(
"AC3 in private stream (not audio stream)") {
666 std::vector<uint8_t> fragment = { 0x0B, 0x77, 0x00, 0x00, 0x00, 0x00 };
672TEST_CASE(
"cReassemblyBufferAudio - Unknown codec",
"[reassembly][audio]") {
673 SECTION(
"Garbage data returns NONE") {
676 std::vector<uint8_t> fragment = { 0x00, 0x00, 0x00 };
686TEST_CASE(
"cReassemblyBufferAudio - FindSyncWord at start",
"[reassembly][audio][syncword]") {
687 SECTION(
"Find MP2 sync word at position 0") {
691 std::vector<uint8_t> data = { 0xFF, 0xF3, 0x44, 0xC0 };
695 REQUIRE(result.
codecId == AV_CODEC_ID_MP2);
696 REQUIRE(result.
pos == 0);
700TEST_CASE(
"cReassemblyBufferAudio - FindSyncWord with offset",
"[reassembly][audio][syncword]") {
701 SECTION(
"Find AC3 sync word at position 10") {
704 std::vector<uint8_t> data = {
705 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
706 0x0B, 0x77, 0x00, 0x00, 0x00, 0x00
711 REQUIRE(result.
codecId == AV_CODEC_ID_AC3);
712 REQUIRE(result.
pos == 10);
715 SECTION(
"Find LATM sync word in the middle") {
718 std::vector<uint8_t> data = {
726 REQUIRE(result.
codecId == AV_CODEC_ID_AAC_LATM);
727 REQUIRE(result.
pos == 3);
731TEST_CASE(
"cReassemblyBufferAudio - FindSyncWord no match",
"[reassembly][audio][syncword]") {
732 SECTION(
"No sync word in garbage data") {
735 std::vector<uint8_t> data = {
736 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
741 REQUIRE(result.
codecId == AV_CODEC_ID_NONE);
742 REQUIRE(result.
pos == -1);
745 SECTION(
"Empty data") {
748 std::vector<uint8_t> data = {};
752 REQUIRE(result.
codecId == AV_CODEC_ID_NONE);
753 REQUIRE(result.
pos == -1);
756 SECTION(
"Data too short for any codec") {
760 std::vector<uint8_t> data = { 0xFF, 0xF1 };
764 REQUIRE(result.
codecId == AV_CODEC_ID_NONE);
765 REQUIRE(result.
pos == -1);
769TEST_CASE(
"cReassemblyBufferAudio - FindSyncWord multiple candidates",
"[reassembly][audio][syncword]") {
770 SECTION(
"Returns first valid sync word when multiple present") {
774 std::vector<uint8_t> data = {
776 0x0B, 0x77, 0x00, 0x00, 0x00, 0x00,
777 0xFF, 0xF3, 0x44, 0xC0
783 REQUIRE(result.
codecId == AV_CODEC_ID_AC3);
784 REQUIRE(result.
pos == 2);
787 SECTION(
"Find sync word when partial match exists earlier") {
791 std::vector<uint8_t> data = {
793 0x0B, 0x77, 0x00, 0x00, 0x00, 0x00
798 REQUIRE(result.
codecId == AV_CODEC_ID_AC3);
799 REQUIRE(result.
pos == 2);
803TEST_CASE(
"cReassemblyBufferAudio - TruncateBufferUntilFirstValidData at start",
"[reassembly][audio][consecutive]") {
804 SECTION(
"Two consecutive LATM frames at position 0") {
809 std::vector<uint8_t> data = {
810 0x56, 0xE0, 0x05, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE,
811 0x56, 0xE0, 0x03, 0x11, 0x22, 0x33
814 buffer.
Push(data.data(), data.size(), 0);
816 REQUIRE(buffer.
GetSize() == 14);
821TEST_CASE(
"cReassemblyBufferAudio - TruncateBufferUntilFirstValidData with offset",
"[reassembly][audio][consecutive]") {
822 SECTION(
"Two consecutive LATM frames after garbage data") {
825 std::vector<uint8_t> data = {
826 0x00, 0x01, 0x02, 0x03, 0x04,
827 0x56, 0xE0, 0x04, 0xAA, 0xBB, 0xCC, 0xDD,
828 0x56, 0xE0, 0x02, 0x11, 0x22, 0x00
831 buffer.
Push(data.data(), data.size(), 0);
834 REQUIRE(buffer.
GetSize() == 13);
837 SECTION(
"Two consecutive LATM frames after false positive sync word") {
840 std::vector<uint8_t> data = {
841 0x56, 0xE0, 0x02, 0xAA, 0xBB,
842 0x00, 0x00, 0x00, 0x00,
843 0x56, 0xE0, 0x02, 0x11, 0x22,
844 0x56, 0xE0, 0x03, 0x44, 0x55, 0x66
847 buffer.
Push(data.data(), data.size(), 0);
850 REQUIRE(buffer.
GetSize() == 11);
854TEST_CASE(
"cReassemblyBufferAudio - TruncateBufferUntilFirstValidData edge cases",
"[reassembly][audio][consecutive]") {
855 SECTION(
"Only one LATM frame present") {
858 std::vector<uint8_t> data = {
859 0x56, 0xE0, 0x05, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE
862 buffer.
Push(data.data(), data.size(), 0);
864 REQUIRE(buffer.
GetSize() == 8);
868 SECTION(
"First frame incomplete at end of buffer") {
872 std::vector<uint8_t> data = {
873 0x56, 0xE0, 0x0A, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE
876 buffer.
Push(data.data(), data.size(), 0);
878 REQUIRE(buffer.
GetSize() == 8);
882 SECTION(
"Second frame incomplete") {
885 std::vector<uint8_t> data = {
886 0x56, 0xE0, 0x02, 0xAA, 0xBB,
890 buffer.
Push(data.data(), data.size(), 0);
892 REQUIRE(buffer.
GetSize() == 7);
896 SECTION(
"Wrong codec for second frame") {
899 std::vector<uint8_t> data = {
900 0x56, 0xE0, 0x02, 0xAA, 0xBB,
901 0x0B, 0x77, 0x00, 0x00, 0x00, 0x00
904 buffer.
Push(data.data(), data.size(), 0);
907 REQUIRE(buffer.
GetSize() == 6);
910 SECTION(
"Wrong codec for second frame, and first frame contains a sync word in payload, and second sync word is not long enough") {
913 std::vector<uint8_t> data = {
914 0x56, 0xE0, 0x03, 0x56, 0xE0, 0xAA,
915 0x0B, 0x77, 0x00, 0x00, 0x00
918 buffer.
Push(data.data(), data.size(), 0);
920 REQUIRE(buffer.
GetSize() == 11);
924 SECTION(
"Only header") {
927 std::vector<uint8_t> data = {
931 buffer.
Push(data.data(), data.size(), 0);
933 REQUIRE(buffer.
GetSize() == 3);
938TEST_CASE(
"cReassemblyBufferAudio - TruncateBufferUntilFirstValidData no sync word",
"[reassembly][audio][consecutive]") {
939 SECTION(
"No sync word in data") {
942 std::vector<uint8_t> data = {
943 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
946 buffer.
Push(data.data(), data.size(), 0);
949 REQUIRE(buffer.
GetSize() == 6);
952 SECTION(
"Empty data") {
955 std::vector<uint8_t> data = {};
957 buffer.
Push(data.data(), data.size(), 0);
959 REQUIRE(buffer.
GetSize() == 0);
964TEST_CASE(
"cReassemblyBufferAudio - TruncateBufferUntilFirstValidData with maximum length",
"[reassembly][audio][consecutive]") {
965 SECTION(
"LATM frame with maximum 13-bit length field") {
971 std::vector<uint8_t> data;
972 data.push_back(0x56);
973 data.push_back(0xFF);
974 data.push_back(0xFF);
975 data.resize(8194, 0xAA);
978 data.push_back(0x56);
979 data.push_back(0xE0);
980 data.push_back(0x01);
981 data.push_back(0xBB);
982 data.push_back(0x00);
983 data.push_back(0x00);
985 buffer.
Push(data.data(), data.size(), 0);
987 REQUIRE(buffer.
GetSize() == data.size());
996TEST_CASE(
"cReassemblyBufferAudio - GetFrameSize LATM codec",
"[reassembly][audio][framesize]") {
999 SECTION(
"LATM frame with 0 byte payload") {
1001 std::vector<uint8_t> data = {0x56, 0xE0, 0x00};
1003 REQUIRE(frameSize == 3);
1006 SECTION(
"LATM frame with 256 byte payload") {
1008 std::vector<uint8_t> data = {0x56, 0xE1, 0x00};
1010 REQUIRE(frameSize == 259);
1013 SECTION(
"LATM frame with maximum 13-bit payload (8191 bytes)") {
1016 std::vector<uint8_t> data = {0x56, 0xFF, 0xFF};
1018 REQUIRE(frameSize == 8194);
1022TEST_CASE(
"cReassemblyBufferAudio - GetFrameSize AAC/ADTS codec",
"[reassembly][audio][framesize]") {
1025 SECTION(
"ADTS frame with minimum size") {
1028 std::vector<uint8_t> data = {0xFF, 0xF1, 0x50, 0x00, 0x00, 0xE0, 0x00};
1030 REQUIRE(frameSize == 7);
1033 SECTION(
"ADTS frame with 100 byte total size") {
1039 std::vector<uint8_t> data = {0xFF, 0xF1, 0x50, 0x00, 0x0C, 0x80, 0x00};
1041 REQUIRE(frameSize == 100);
1044 SECTION(
"ADTS frame with 1024 byte total size") {
1050 std::vector<uint8_t> data = {0xFF, 0xF1, 0x50, 0x00, 0x80, 0x00, 0x00};
1052 REQUIRE(frameSize == 1024);
1055 SECTION(
"ADTS frame with maximum 13-bit size (8191 bytes)") {
1060 std::vector<uint8_t> data = {0xFF, 0xF1, 0x50, 0x03, 0xFF, 0xE0, 0x00};
1062 REQUIRE(frameSize == 8191);
1066TEST_CASE(
"cReassemblyBufferAudio - GetFrameSize AC3 codec",
"[reassembly][audio][framesize]") {
1069 SECTION(
"AC3 frame - 48 kHz, smallest frame (128 bytes)") {
1072 std::vector<uint8_t> data = {0x0B, 0x77, 0x00, 0x00, 0x40, 0x00};
1074 REQUIRE(frameSize == 138);
1077 SECTION(
"AC3 frame - 44.1 kHz, smallest frame") {
1080 std::vector<uint8_t> data = {0x0B, 0x77, 0x00, 0x00, 0x00, 0x00};
1082 REQUIRE(frameSize == 128);
1085 SECTION(
"AC3 frame - 32 kHz, smallest frame") {
1088 std::vector<uint8_t> data = {0x0B, 0x77, 0x00, 0x00, 0x80, 0x00};
1090 REQUIRE(frameSize == 192);
1093 SECTION(
"AC3 frame - 48 kHz, largest frame (frmsizcod=37)") {
1096 std::vector<uint8_t> data = {0x0B, 0x77, 0x00, 0x00, 0x65, 0x00};
1098 REQUIRE(frameSize == 2788);
1101 SECTION(
"AC3 frame - 44.1 kHz, mid-range frame (frmsizcod=18)") {
1104 std::vector<uint8_t> data = {0x0B, 0x77, 0x00, 0x00, 0x12, 0x00};
1106 REQUIRE(frameSize == 640);
1110TEST_CASE(
"cReassemblyBufferAudio - GetFrameSize AC3 error conditions",
"[reassembly][audio][framesize]") {
1113 SECTION(
"AC3 invalid sample rate (fscod=11)") {
1115 std::vector<uint8_t> data = {0x0B, 0x77, 0x00, 0x00, 0xC0, 0x00};
1116 REQUIRE_THROWS_AS(buffer.
GetFrameSizeForCodec(AV_CODEC_ID_AC3, data.data()), std::invalid_argument);
1119 SECTION(
"AC3 invalid frame size code (frmsizcod=38)") {
1121 std::vector<uint8_t> data = {0x0B, 0x77, 0x00, 0x00, 0x26, 0x00};
1122 REQUIRE_THROWS_AS(buffer.
GetFrameSizeForCodec(AV_CODEC_ID_AC3, data.data()), std::invalid_argument);
1125 SECTION(
"AC3 invalid frame size code (frmsizcod=63)") {
1127 std::vector<uint8_t> data = {0x0B, 0x77, 0x00, 0x00, 0x3F, 0x00};
1128 REQUIRE_THROWS_AS(buffer.
GetFrameSizeForCodec(AV_CODEC_ID_AC3, data.data()), std::invalid_argument);
1132TEST_CASE(
"cReassemblyBufferAudio - GetFrameSize E-AC3 codec",
"[reassembly][audio][framesize]") {
1135 SECTION(
"E-AC3 frame - minimum size (1 word = 2 bytes)") {
1138 std::vector<uint8_t> data = {0x0B, 0x77, 0x00, 0x00, 0x00, 0x51};
1140 REQUIRE(frameSize == 2);
1143 SECTION(
"E-AC3 frame - 100 words (200 bytes)") {
1146 std::vector<uint8_t> data = {0x0B, 0x77, 0x00, 0x63, 0x00, 0x51};
1148 REQUIRE(frameSize == 200);
1151 SECTION(
"E-AC3 frame - 512 words (1024 bytes)") {
1154 std::vector<uint8_t> data = {0x0B, 0x77, 0x01, 0xFF, 0x00, 0x51};
1156 REQUIRE(frameSize == 1024);
1159 SECTION(
"E-AC3 frame - maximum 11-bit size (2047 words = 4094 bytes)") {
1162 std::vector<uint8_t> data = {0x0B, 0x77, 0x07, 0xFE, 0x00, 0x51};
1164 REQUIRE(frameSize == 4094);
1168TEST_CASE(
"cReassemblyBufferAudio - GetFrameSize E-AC3 error conditions",
"[reassembly][audio][framesize]") {
1171 SECTION(
"E-AC3 invalid fscod/fscod2 combination") {
1173 std::vector<uint8_t> data = {0x0B, 0x77, 0x00, 0x00, 0xF0, 0x51};
1174 REQUIRE_THROWS_AS(buffer.
GetFrameSizeForCodec(AV_CODEC_ID_EAC3, data.data()), std::invalid_argument);
1178TEST_CASE(
"cReassemblyBufferAudio - GetFrameSize MP2 codec",
"[reassembly][audio][framesize]") {
1181 SECTION(
"MP2 MPEG1 Layer 2, 128kbps, 44.1kHz, no padding") {
1184 std::vector<uint8_t> data = {0xFF, 0xFD, 0x80, 0x00};
1186 REQUIRE(frameSize == 417);
1189 SECTION(
"MP2 MPEG1 Layer 2, 128kbps, 44.1kHz, with padding") {
1192 std::vector<uint8_t> data = {0xFF, 0xFD, 0x82, 0x00};
1194 REQUIRE(frameSize == 418);
1197 SECTION(
"MP2 MPEG1 Layer 2, 192kbps, 48kHz, no padding") {
1200 std::vector<uint8_t> data = {0xFF, 0xFD, 0xA4, 0x00};
1202 REQUIRE(frameSize == 576);
1205 SECTION(
"MP2 MPEG2 Layer 2, 64kbps, 24kHz, no padding") {
1208 std::vector<uint8_t> data = {0xFF, 0xF5, 0x84, 0x00};
1210 REQUIRE(frameSize == 384);
1213 SECTION(
"MP2 MPEG1 Layer 3, 128kbps, 44.1kHz, no padding") {
1216 std::vector<uint8_t> data = {0xFF, 0xFB, 0x90, 0x00};
1218 REQUIRE(frameSize == 417);
1221 SECTION(
"MP2 MPEG1 Layer 1, 128kbps, 44.1kHz, no padding") {
1224 std::vector<uint8_t> data = {0xFF, 0xFF, 0x40, 0x00};
1226 REQUIRE(frameSize == 136);
1229 SECTION(
"MP2 MPEG1 Layer 1, 128kbps, 44.1kHz, with padding") {
1232 std::vector<uint8_t> data = {0xFF, 0xFF, 0x42, 0x00};
1234 REQUIRE(frameSize == 140);
1238TEST_CASE(
"cReassemblyBufferAudio - GetFrameSize MP2 error conditions",
"[reassembly][audio][framesize]") {
1241 SECTION(
"MP2 invalid sample rate (index 3)") {
1243 std::vector<uint8_t> data = {0xFF, 0xFD, 0x5C, 0x00};
1244 REQUIRE_THROWS_AS(buffer.
GetFrameSizeForCodec(AV_CODEC_ID_MP2, data.data()), std::invalid_argument);
1247 SECTION(
"MP2 invalid bit rate (index 0)") {
1249 std::vector<uint8_t> data = {0xFF, 0xFD, 0x04, 0x00};
1250 REQUIRE_THROWS_AS(buffer.
GetFrameSizeForCodec(AV_CODEC_ID_MP2, data.data()), std::invalid_argument);
1253 SECTION(
"MP2 invalid bit rate (index 15)") {
1255 std::vector<uint8_t> data = {0xFF, 0xFD, 0xF0, 0x00};
1256 REQUIRE_THROWS_AS(buffer.
GetFrameSizeForCodec(AV_CODEC_ID_MP2, data.data()), std::invalid_argument);
1264TEST_CASE(
"cPtsTrackingBuffer - Basic Push and GetPts",
"[ptstracking]") {
1265 SECTION(
"Push data with PTS and retrieve it") {
1268 std::vector<uint8_t> data = {0xAA, 0xBB, 0xCC, 0xDD};
1269 buffer.
Push(data.data(), data.size(), 1000);
1271 REQUIRE(buffer.
GetSize() == 4);
1272 REQUIRE(buffer.
GetPts() == 1000);
1275 SECTION(
"Push multiple data chunks with different PTS") {
1278 std::vector<uint8_t> data1 = {0xAA, 0xBB};
1279 std::vector<uint8_t> data2 = {0xCC, 0xDD};
1280 std::vector<uint8_t> data3 = {0xEE, 0xFF};
1282 buffer.
Push(data1.data(), data1.size(), 1000);
1283 buffer.
Push(data2.data(), data2.size(), 2000);
1284 buffer.
Push(data3.data(), data3.size(), 3000);
1286 REQUIRE(buffer.
GetSize() == 6);
1287 REQUIRE(buffer.
GetPts() == 1000);
1290 SECTION(
"Empty buffer returns AV_NOPTS_VALUE") {
1297TEST_CASE(
"cPtsTrackingBuffer - Erase basic functionality",
"[ptstracking][erase]") {
1298 SECTION(
"Erase from buffer with single PTS entry") {
1302 std::vector<uint8_t> data(10, 0xAA);
1303 buffer.
Push(data.data(), data.size(), 1000);
1308 REQUIRE(buffer.
GetSize() == 5);
1309 REQUIRE(buffer.
GetPts() == 1000);
1312 SECTION(
"Erase entire buffer") {
1315 std::vector<uint8_t> data(10, 0xAA);
1316 buffer.
Push(data.data(), data.size(), 1000);
1320 REQUIRE(buffer.
GetSize() == 0);
1324TEST_CASE(
"cPtsTrackingBuffer - Erase with multiple PTS entries",
"[ptstracking][erase]") {
1325 SECTION(
"Erase exactly at PTS boundary") {
1329 std::vector<uint8_t> data1(10, 0xAA);
1330 buffer.
Push(data1.data(), data1.size(), 1000);
1333 std::vector<uint8_t> data2(10, 0xBB);
1334 buffer.
Push(data2.data(), data2.size(), 2000);
1338 REQUIRE(buffer.
GetSize() == 10);
1339 REQUIRE(buffer.
GetPts() == 2000);
1342 SECTION(
"Erase removes old PTS, keeps and adjusts newer PTS") {
1346 std::vector<uint8_t> data1 = {0x00, 0x01, 0x02, 0x03, 0x04};
1347 buffer.
Push(data1.data(), data1.size(), 1000);
1350 std::vector<uint8_t> data2 = {0x05, 0x06, 0x07, 0x08, 0x09};
1351 buffer.
Push(data2.data(), data2.size(), 2000);
1353 std::vector<uint8_t> data3 = {0x0A, 0x0B, 0x0C, 0x0D, 0x0E};
1354 buffer.
Push(data3.data(), data3.size(), 3000);
1359 REQUIRE(buffer.
GetSize() == 10);
1360 REQUIRE(buffer.
GetPts() == 2000);
1363 SECTION(
"Erase preserves PTS when erasing before next PTS entry") {
1367 std::vector<uint8_t> data1(10, 0xAA);
1368 buffer.
Push(data1.data(), data1.size(), 1000);
1371 std::vector<uint8_t> data2(10, 0xBB);
1372 buffer.
Push(data2.data(), data2.size(), 2000);
1376 REQUIRE(buffer.
GetSize() == 13);
1377 REQUIRE(buffer.
GetPts() == 1000);
1381TEST_CASE(
"cPtsTrackingBuffer - Erase PTS inheritance logic",
"[ptstracking][erase]") {
1382 SECTION(
"PTS is preserved at new position 0 when erasing between PTS entries") {
1386 std::vector<uint8_t> data1(10, 0xAA);
1387 buffer.
Push(data1.data(), data1.size(), 1000);
1390 std::vector<uint8_t> data2(10, 0xBB);
1391 buffer.
Push(data2.data(), data2.size(), 2000);
1393 std::vector<uint8_t> data3(10, 0xCC);
1394 buffer.
Push(data3.data(), data3.size(), 3000);
1401 REQUIRE(buffer.
GetSize() == 18);
1402 REQUIRE(buffer.
GetPts() == 2000);
1406TEST_CASE(
"cPtsTrackingBuffer - Erase with fragmented frames",
"[ptstracking][erase]") {
1407 SECTION(
"Simulates removing partial frame while preserving PTS") {
1411 std::vector<uint8_t> frame1(50, 0xAA);
1412 buffer.
Push(frame1.data(), frame1.size(), 1000);
1415 std::vector<uint8_t> frame2(100, 0xBB);
1416 buffer.
Push(frame2.data(), frame2.size(), 2000);
1418 std::vector<uint8_t> frame3_part(50, 0xCC);
1419 buffer.
Push(frame3_part.data(), frame3_part.size(), 3000);
1424 REQUIRE(buffer.
GetSize() == 120);
1426 REQUIRE(buffer.
GetPts() == 2000);
1429 SECTION(
"Multiple erases progressively consume data") {
1433 std::vector<uint8_t> data1(10, 0xAA);
1434 buffer.
Push(data1.data(), data1.size(), 1000);
1436 std::vector<uint8_t> data2(10, 0xBB);
1437 buffer.
Push(data2.data(), data2.size(), 2000);
1439 std::vector<uint8_t> data3(10, 0xCC);
1440 buffer.
Push(data3.data(), data3.size(), 3000);
1444 REQUIRE(buffer.
GetSize() == 25);
1445 REQUIRE(buffer.
GetPts() == 1000);
1449 REQUIRE(buffer.
GetSize() == 17);
1450 REQUIRE(buffer.
GetPts() == 2000);
1454 REQUIRE(buffer.
GetSize() == 5);
1455 REQUIRE(buffer.
GetPts() == 3000);
1459TEST_CASE(
"cPtsTrackingBuffer - Erase edge cases",
"[ptstracking][erase]") {
1460 SECTION(
"Erase 0 bytes does nothing") {
1463 std::vector<uint8_t> data(10, 0xAA);
1464 buffer.
Push(data.data(), data.size(), 1000);
1468 REQUIRE(buffer.
GetSize() == 10);
1469 REQUIRE(buffer.
GetPts() == 1000);
1472 SECTION(
"Erase single byte") {
1475 std::vector<uint8_t> data = {0xAA};
1476 buffer.
Push(data.data(), data.size(), 1000);
1478 std::vector<uint8_t> data2 = {0xBB};
1479 buffer.
Push(data2.data(), data2.size(), 2000);
1483 REQUIRE(buffer.
GetSize() == 1);
1484 REQUIRE(buffer.
GetPts() == 2000);
1488TEST_CASE(
"cPtsTrackingBuffer - Complex scenarios",
"[ptstracking][erase]") {
1489 SECTION(
"Interleaved push and erase operations") {
1493 std::vector<uint8_t> data1(20, 0xAA);
1494 buffer.
Push(data1.data(), data1.size(), 1000);
1498 REQUIRE(buffer.
GetSize() == 15);
1499 REQUIRE(buffer.
GetPts() == 1000);
1502 std::vector<uint8_t> data2(10, 0xBB);
1503 buffer.
Push(data2.data(), data2.size(), 2000);
1504 REQUIRE(buffer.
GetSize() == 25);
1508 REQUIRE(buffer.
GetSize() == 7);
1509 REQUIRE(buffer.
GetPts() == 2000);
1512 SECTION(
"Three PTS entries, erase middle one") {
1516 std::vector<uint8_t> data1(10, 0xAA);
1517 buffer.
Push(data1.data(), data1.size(), 1000);
1520 std::vector<uint8_t> data2(10, 0xBB);
1521 buffer.
Push(data2.data(), data2.size(), 2000);
1523 std::vector<uint8_t> data3(10, 0xCC);
1524 buffer.
Push(data3.data(), data3.size(), 3000);
1529 REQUIRE(buffer.
GetSize() == 15);
1530 REQUIRE(buffer.
GetPts() == 2000);
1533 SECTION(
"Large buffer with many PTS entries") {
1537 for (
int i = 0; i < 10; i++) {
1538 std::vector<uint8_t> data(100,
static_cast<uint8_t
>(i));
1539 buffer.
Push(data.data(), data.size(), 1000 * (i + 1));
1542 REQUIRE(buffer.
GetSize() == 1000);
1543 REQUIRE(buffer.
GetPts() == 1000);
1548 REQUIRE(buffer.
GetSize() == 550);
1549 REQUIRE(buffer.
GetPts() == 5000);
1553TEST_CASE(
"cPtsTrackingBuffer - Reset functionality",
"[ptstracking]") {
1554 SECTION(
"Reset clears all data and PTS") {
1557 std::vector<uint8_t> data1(10, 0xAA);
1558 buffer.
Push(data1.data(), data1.size(), 1000);
1560 std::vector<uint8_t> data2(10, 0xBB);
1561 buffer.
Push(data2.data(), data2.size(), 2000);
1565 REQUIRE(buffer.
GetSize() == 0);
bool IsValid()
Check if the PES packet is valid.
int GetPayloadSize()
Get the size of the PES payload.
int GetPacketLength()
Get the total length of the PES packet.
const uint8_t * GetPayload()
Get a pointer to the PES payload data.
int64_t GetPts()
Get the Presentation Time Stamp (PTS) from the PES header.
Buffer that tracks PTS values at specific byte positions.
void Push(const uint8_t *, int, int64_t)
Push data into the PTS tracking buffer.
void Erase(size_t)
Erase data from the beginning of the buffer.
int64_t GetPts()
Get the PTS value for the current buffer position.
Audio stream reassembly buffer.
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.
int GetFrameSizeForCodec(AVCodecID, const uint8_t *)
Get the frame size for a given codec and frame header.
Video stream reassembly buffer.
bool HasLeadingZero(const uint8_t *, int)
Check if video data has a leading zero byte before the start code.
bool ParseCodecHeader(const uint8_t *, int)
Parse video codec header to detect codec type.
AVPacket * PopAvPacket() override
virtual void Push(const uint8_t *data, int size, int64_t pts)
Information about a detected audio sync word.
AVCodecID codecId
Detected codec ID.
int pos
Position of sync word in buffer.
std::vector< uint8_t > createHevcPesVideoPacket(bool withLeadingZero=false)
std::vector< uint8_t > createBasicPesHeader(uint8_t streamId, bool withPts=false, uint16_t pesLength=0)
std::vector< uint8_t > createMpeg2PesPacket()
std::vector< uint8_t > createAudioPesPacket()
std::vector< uint8_t > createH264PesPacket(bool withLeadingZero=false)
TEST_CASE("cPesVideo - Basic construction", "[pes]")