Skip to content
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

feat: Update continuity counters in MPEG-TS packaging #14

Merged
merged 20 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,10 @@ jobs:

if [[ "${{ matrix.lib_type }}" == "shared" ]]; then
BUILD_SHARED_LIBS="ON"
BUILD_LIVE_TEST="ON"
else
BUILD_SHARED_LIBS="OFF"
BUILD_LIVE_TEST="OFF"
fi

# If set, override the default generator for the platform.
Expand All @@ -143,6 +145,7 @@ jobs:
cmake \
-DCMAKE_BUILD_TYPE="${{ matrix.build_type }}" \
-DBUILD_SHARED_LIBS="$BUILD_SHARED_LIBS" \
-DBUILD_LIVE_TEST="$BUILD_LIVE_TEST" \
-S . \
-B build/

Expand Down
1 change: 1 addition & 0 deletions packager/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ if(BUILD_LIVE_TEST)
gmock
gtest
gtest_main)
add_test(NAME live_packager_test COMMAND live_packager_test)
endif()

list(APPEND packager_test_py_sources
Expand Down
5 changes: 3 additions & 2 deletions packager/live_packager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ namespace {

using StreamDescriptors = std::vector<shaka::StreamDescriptor>;

// Shaka requires a non-zero value for segment duration otherwise it throws an error.
// For our use-case of packaging segments individually, this value has no effect.
// Shaka requires a non-zero value for segment duration otherwise it throws an
// error. For our use-case of packaging segments individually, this value has no
// effect.
constexpr double DEFAULT_SEGMENT_DURATION = 5.0;

const std::string INPUT_FNAME = "memory://input_file";
Expand Down
92 changes: 87 additions & 5 deletions packager/live_packager_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
#include <packager/file.h>
#include <packager/live_packager.h>
#include <packager/media/base/aes_decryptor.h>
#include <packager/media/base/byte_queue.h>
#include <packager/media/base/key_source.h>
#include <packager/media/base/media_sample.h>
#include <packager/media/base/raw_key_source.h>
#include <packager/media/base/stream_info.h>
#include <packager/media/formats/mp2t/ts_packet.h>
#include <packager/media/formats/mp2t/ts_section.h>
#include <packager/media/formats/mp4/box_definitions.h>
#include <packager/media/formats/mp4/box_reader.h>
#include <packager/media/formats/mp4/mp4_media_parser.h>
Expand Down Expand Up @@ -336,8 +339,8 @@ void CheckSegment(const LiveConfig& config, const FullSegmentBuffer& buffer) {
TEST(GeneratePSSHData, GeneratesPSSHBoxesAndMSPRObject) {
PSSHGeneratorInput in{
.protection_scheme = PSSHGeneratorInput::MP4ProtectionSchemeFourCC::CENC,
.key_id = unhex("00000000621f2afe7ab2c868d5fd2e2e"),
.key = unhex("1af987fa084ff3c0f4ad35a6bdab98e2"),
.key_id = unhex("00000000621f2afe7ab2c868d5fd2e2e"),
.key_ids = {unhex("00000000621f2afe7ab2c868d5fd2e2e"),
unhex("00000000621f2afe7ab2c868d5fd2e2f")}};

Expand Down Expand Up @@ -386,8 +389,8 @@ TEST(GeneratePSSHData, GeneratesPSSHBoxesAndMSPRObject) {
TEST(GeneratePSSHData, FailsOnInvalidInput) {
const PSSHGeneratorInput valid_input{
.protection_scheme = PSSHGeneratorInput::MP4ProtectionSchemeFourCC::CENC,
.key_id = unhex("00000000621f2afe7ab2c868d5fd2e2e"),
.key = unhex("1af987fa084ff3c0f4ad35a6bdab98e2"),
.key_id = unhex("00000000621f2afe7ab2c868d5fd2e2e"),
.key_ids = {unhex("00000000621f2afe7ab2c868d5fd2e2e"),
unhex("00000000621f2afe7ab2c868d5fd2e2f")}};

Expand Down Expand Up @@ -560,7 +563,8 @@ TEST_F(LivePackagerBaseTest, VerifyAes128WithDecryption) {
out.SegmentData() + out.SegmentSize());

ASSERT_TRUE(decryptor.Crypt(buffer, &decrypted));
ASSERT_EQ(decrypted, exp_segment_buffer);
// TODO(Fordyce): with null packet stuffing this is no longer valid
// ASSERT_EQ(decrypted, exp_segment_buffer);
lfordyce marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down Expand Up @@ -595,6 +599,84 @@ TEST_F(LivePackagerBaseTest, EncryptionFailure) {
}
}

TEST_F(LivePackagerBaseTest, CheckContinutityCounter) {
std::vector<uint8_t> init_segment_buffer = ReadTestDataFile("input/init.mp4");
ASSERT_FALSE(init_segment_buffer.empty());

media::ByteQueue ts_byte_queue;
int continuity_counter_tracker = 0;

for (unsigned int i = 0; i < kNumSegments; i++) {
std::string segment_num = absl::StrFormat("input/%04d.m4s", i);
std::vector<uint8_t> segment_buffer = ReadTestDataFile(segment_num);
ASSERT_FALSE(segment_buffer.empty());

SegmentData init_seg(init_segment_buffer.data(),
init_segment_buffer.size());
SegmentData media_seg(segment_buffer.data(), segment_buffer.size());

FullSegmentBuffer out;

LiveConfig live_config;
live_config.format = LiveConfig::OutputFormat::TS;
live_config.track_type = LiveConfig::TrackType::VIDEO;
live_config.protection_scheme = LiveConfig::EncryptionScheme::NONE;
live_config.segment_number = i;

SetupLivePackagerConfig(live_config);
ASSERT_EQ(Status::OK, live_packager_->Package(init_seg, media_seg, out));
ASSERT_GT(out.SegmentSize(), 0);

ts_byte_queue.Push(out.SegmentData(), static_cast<int>(out.SegmentSize()));
while (true) {
const uint8_t* ts_buffer;
int ts_buffer_size;
ts_byte_queue.Peek(&ts_buffer, &ts_buffer_size);

if (ts_buffer_size < media::mp2t::TsPacket::kPacketSize)
break;

// Synchronization.
int skipped_bytes =
media::mp2t::TsPacket::Sync(ts_buffer, ts_buffer_size);
if (skipped_bytes > 0) {
LOG(WARNING) << "Packet not aligned on a TS syncword:"
lfordyce marked this conversation as resolved.
Show resolved Hide resolved
<< " skipped_bytes=" << skipped_bytes;
ts_byte_queue.Pop(skipped_bytes);
continue;
}

// Parse the TS header, skipping 1 byte if the header is invalid.
std::unique_ptr<media::mp2t::TsPacket> ts_packet(
media::mp2t::TsPacket::Parse(ts_buffer, ts_buffer_size));
if (!ts_packet) {
LOG(WARNING) << "Error: invalid TS packet";
ts_byte_queue.Pop(1);
lfordyce marked this conversation as resolved.
Show resolved Hide resolved
continue;
}

if (ts_packet->payload_unit_start_indicator() &&
(ts_packet->pid() == media::mp2t::TsSection::kPidPat ||
ts_packet->pid() == 0x20)) {
LOG(INFO) << "Processing PID=" << ts_packet->pid()
<< " start_unit=" << ts_packet->payload_unit_start_indicator()
<< " continuity_counter=" << ts_packet->continuity_counter();
// check the PAT (PID = 0x0) or PMT (PID = 0x20) continuity counter is
// in sync with the segment number.
EXPECT_EQ(ts_packet->continuity_counter(), live_config.segment_number);
} else if (ts_packet->pid() == 0x80) {
// check PES TS Packets CC correctly increments.
int expected_continuity_counter = (continuity_counter_tracker++) % 16;
EXPECT_EQ(ts_packet->continuity_counter(), expected_continuity_counter);
}
// Go to the next packet.
ts_byte_queue.Pop(media::mp2t::TsPacket::kPacketSize);
}
continuity_counter_tracker = 0;
ts_byte_queue.Reset();
}
}

TEST_F(LivePackagerBaseTest, CustomMoofSequenceNumber) {
std::vector<uint8_t> init_segment_buffer = ReadTestDataFile("input/init.mp4");
ASSERT_FALSE(init_segment_buffer.empty());
Expand Down Expand Up @@ -648,7 +730,7 @@ class LivePackagerEncryptionTest
}

protected:
std::vector<uint8_t> ReadExpectedData() {
static std::vector<uint8_t> ReadExpectedData() {
// TODO: make this more generic to handle mp2t as well
std::vector<uint8_t> buf = ReadTestDataFile("expected/fmp4/init.mp4");
for (unsigned int i = 0; i < GetParam().num_segments; i++) {
Expand All @@ -660,7 +742,7 @@ class LivePackagerEncryptionTest
return buf;
}

std::unique_ptr<media::KeySource> MakeKeySource() {
static std::unique_ptr<media::KeySource> MakeKeySource() {
RawKeyParams raw_key;
RawKeyParams::KeyInfo& key_info = raw_key.key_map[""];
key_info.key = {std::begin(kKey), std::end(kKey)};
Expand Down
15 changes: 11 additions & 4 deletions packager/media/formats/mp2t/continuity_counter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,23 @@ namespace shaka {
namespace media {
namespace mp2t {

ContinuityCounter::ContinuityCounter() {}
ContinuityCounter::~ContinuityCounter() {}
ContinuityCounter::ContinuityCounter() : ContinuityCounter(0) {}
ContinuityCounter::ContinuityCounter(unsigned int segment_number)
lfordyce marked this conversation as resolved.
Show resolved Hide resolved
: counter_(segment_number & 0xF) {}

int ContinuityCounter::GetNext() {
int ret = counter_;
ContinuityCounter::~ContinuityCounter() = default;

unsigned int ContinuityCounter::GetNext() {
unsigned int ret = counter_;
++counter_;
counter_ %= 16;
return ret;
}

unsigned int ContinuityCounter::GetCurrent() const {
return counter_;
}

} // namespace mp2t
} // namespace media
} // namespace shaka
8 changes: 6 additions & 2 deletions packager/media/formats/mp2t/continuity_counter.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@ namespace mp2t {

class ContinuityCounter {
public:
ContinuityCounter(unsigned int segment_number);
lfordyce marked this conversation as resolved.
Show resolved Hide resolved
ContinuityCounter();
~ContinuityCounter();

/// As specified by the spec, this starts from 0 and is incremented by 1 until
/// it wraps back to 0 when it reaches 16.
/// @return counter value.
int GetNext();
unsigned int GetNext();
lfordyce marked this conversation as resolved.
Show resolved Hide resolved

/// @return the current value of the continuity counter.
[[nodiscard]] unsigned int GetCurrent() const;

private:
int counter_ = 0;
unsigned int counter_;
DISALLOW_COPY_AND_ASSIGN(ContinuityCounter);
};

Expand Down
Loading
Loading