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 17 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
3 changes: 3 additions & 0 deletions include/packager/packager.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ struct PackagingParams {

/// Only use to package init segment separately.
bool init_segment_only = false;

/// Specify weather or not to enable null packet stuffing for TS segments.
bool enable_null_ts_packet_stuffing = false;
};

/// Defines a single input/output stream.
Expand Down
3 changes: 2 additions & 1 deletion packager/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ target_link_libraries(packager_test
gtest_main)

# Disabled by default, otherwise build live packager unit tests if BUILD_LIVE_TEST is on.
if(BUILD_LIVE_TEST)
if(BUILD_LIVE_TEST AND NOT MSVC)
add_executable(live_packager_test
live_packager_test.cc
)
Expand All @@ -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: 4 additions & 1 deletion packager/app/muxer_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ MuxerFactory::MuxerFactory(const PackagingParams& packaging_params)
temp_dir_(packaging_params.temp_dir),
transport_stream_timestamp_offset_ms_(
packaging_params.transport_stream_timestamp_offset_ms),
init_segment_only_(packaging_params.init_segment_only) {}
init_segment_only_(packaging_params.init_segment_only),
enable_null_ts_packet_stuffing_(
packaging_params.enable_null_ts_packet_stuffing) {}

std::shared_ptr<Muxer> MuxerFactory::CreateMuxer(
MediaContainerName output_format,
Expand All @@ -37,6 +39,7 @@ std::shared_ptr<Muxer> MuxerFactory::CreateMuxer(
options.output_file_name = stream.output;
options.segment_template = stream.segment_template;
options.bandwidth = stream.bandwidth;
options.enable_null_ts_packet_stuffing = enable_null_ts_packet_stuffing_;

std::shared_ptr<Muxer> muxer;

Expand Down
3 changes: 3 additions & 0 deletions packager/app/muxer_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ class MuxerFactory {

// enable init segment packaging separately
bool init_segment_only_;

// enable null TS packet stuffing
bool enable_null_ts_packet_stuffing_;
};

} // namespace media
Expand Down
6 changes: 4 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 Expand Up @@ -329,6 +330,7 @@ Status LivePackager::Package(const Segment& init_segment,
packaging_params.mp4_output_params.include_pssh_in_stream = false;
packaging_params.transport_stream_timestamp_offset_ms =
config_.m2ts_offset_ms;
packaging_params.enable_null_ts_packet_stuffing = true;

EncryptionParams& encryption_params = packaging_params.encryption_params;
// As a side effect of InitializeEncryption, encryption_params will be
Expand Down
84 changes: 79 additions & 5 deletions packager/live_packager_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@
#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/program_map_table_writer.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 +340,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 +390,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 +564,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 +600,75 @@ 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);
ASSERT_EQ(skipped_bytes, 0);

// 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));
ASSERT_NE(nullptr, ts_packet);

if (ts_packet->payload_unit_start_indicator() &&
(ts_packet->pid() == media::mp2t::TsSection::kPidPat ||
ts_packet->pid() == media::mp2t::ProgramMapTableWriter::kPmtPid)) {
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 +722,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 +734,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
3 changes: 3 additions & 0 deletions packager/media/base/muxer_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ struct MuxerOptions {
/// User-specified bit rate for the media stream. If zero, the muxer will
/// attempt to estimate.
uint32_t bandwidth = 0;

/// Specify weather or not to enable null packet stuffing for TS segments.
bool enable_null_ts_packet_stuffing = false;
};

} // namespace media
Expand Down
10 changes: 8 additions & 2 deletions packager/media/formats/mp2t/continuity_counter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ namespace shaka {
namespace media {
namespace mp2t {

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

ContinuityCounter::~ContinuityCounter() = default;

int ContinuityCounter::GetNext() {
int ret = counter_;
Expand All @@ -20,6 +22,10 @@ int ContinuityCounter::GetNext() {
return ret;
}

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

} // namespace mp2t
} // namespace media
} // namespace shaka
5 changes: 4 additions & 1 deletion packager/media/formats/mp2t/continuity_counter.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@ namespace mp2t {

class ContinuityCounter {
public:
ContinuityCounter();
ContinuityCounter(unsigned int segment_number = 0);
lfordyce marked this conversation as resolved.
Show resolved Hide resolved
~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();

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

private:
int counter_ = 0;
DISALLOW_COPY_AND_ASSIGN(ContinuityCounter);
Expand Down
Loading