From 0c2ee4c844e2fcda73342c95c586c0da52ab5951 Mon Sep 17 00:00:00 2001 From: Kyle Alexander Date: Fri, 11 Nov 2016 15:17:17 -0800 Subject: [PATCH] Add support for 4K and 8K content. Adds UHD1 and UHD2 track types to support 4K and 8K content. Bug: Closes #163 Change-Id: I8fd893725cae88e9944244a48607cbaab591b401 --- packager/app/packager_main.cc | 2 + packager/app/widevine_encryption_flags.cc | 30 ++++++++++++- packager/app/widevine_encryption_flags.h | 2 + packager/media/base/key_source.cc | 8 ++++ packager/media/base/key_source.h | 8 ++-- packager/media/base/muxer.cc | 6 +++ packager/media/base/muxer.h | 21 ++++++++- packager/media/base/muxer_util.cc | 14 ++++-- packager/media/base/muxer_util.h | 8 +++- packager/media/base/widevine_key_source.cc | 6 +++ .../base/widevine_key_source_unittest.cc | 34 ++++++++------ packager/media/formats/mp2t/ts_muxer.cc | 3 +- packager/media/formats/mp2t/ts_segmenter.cc | 5 ++- packager/media/formats/mp2t/ts_segmenter.h | 2 + .../formats/mp2t/ts_segmenter_unittest.cc | 21 ++++++--- packager/media/formats/mp4/mp4_muxer.cc | 5 ++- packager/media/formats/mp4/segmenter.cc | 5 ++- packager/media/formats/mp4/segmenter.h | 14 +++++- packager/media/formats/webm/segmenter.cc | 13 ++++-- packager/media/formats/webm/segmenter.h | 17 +++++-- .../media/formats/webm/segmenter_test_base.h | 1 + packager/media/formats/webm/webm_muxer.cc | 2 +- packager/media/test/packager_test.cc | 45 ++++++++++++++++++- 23 files changed, 225 insertions(+), 47 deletions(-) diff --git a/packager/app/packager_main.cc b/packager/app/packager_main.cc index 6d0fe4c4b4..28343514fd 100644 --- a/packager/app/packager_main.cc +++ b/packager/app/packager_main.cc @@ -334,6 +334,8 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors, if (key_source) { muxer->SetKeySource(key_source, FLAGS_max_sd_pixels, + FLAGS_max_hd_pixels, + FLAGS_max_uhd1_pixels, FLAGS_clear_lead, FLAGS_crypto_period_duration, GetProtectionScheme(FLAGS_protection_scheme)); diff --git a/packager/app/widevine_encryption_flags.cc b/packager/app/widevine_encryption_flags.cc index 09ef9f0955..d8c444d79a 100644 --- a/packager/app/widevine_encryption_flags.cc +++ b/packager/app/widevine_encryption_flags.cc @@ -37,8 +37,18 @@ DEFINE_string(policy, "rights."); DEFINE_int32(max_sd_pixels, 768 * 576, - "If the video track has more pixels per frame than max_sd_pixels, " - "it is considered as HD, SD otherwise. Default: 768 * 576."); + "The video track is considered SD if its max pixels per frame is " + "no higher than max_sd_pixels. Default: 442368 (768 x 576)."); +DEFINE_int32(max_hd_pixels, + 1920 * 1080, + "The video track is considered HD if its max pixels per frame is " + "higher than max_sd_pixels, but no higher than max_hd_pixels. " + "Default: 2073600 (1920 x 1080)."); +DEFINE_int32(max_uhd1_pixels, + 4096 * 2160, + "The video track is considered UHD1 if its max pixels per frame " + "is higher than max_hd_pixels, but no higher than max_uhd1_pixels." + " Otherwise it is UHD2. Default: 8847360 (4096 x 2160)."); DEFINE_string(signer, "", "The name of the signer."); DEFINE_string(aes_signing_key, "", @@ -119,6 +129,22 @@ bool ValidateWidevineCryptoFlags() { PrintError("--max_sd_pixels must be positive."); success = false; } + if (FLAGS_max_hd_pixels <= 0) { + PrintError("--max_hd_pixels must be positive."); + success = false; + } + if (FLAGS_max_uhd1_pixels <= 0) { + PrintError("--max_uhd1_pixels must be positive."); + success = false; + } + if (FLAGS_max_hd_pixels <= FLAGS_max_sd_pixels) { + PrintError("--max_hd_pixels must be greater than --max_sd_pixels."); + success = false; + } + if (FLAGS_max_uhd1_pixels <= FLAGS_max_hd_pixels) { + PrintError("--max_uhd1_pixels must be greater than --max_hd_pixels."); + success = false; + } const bool aes = !FLAGS_signer.empty() && FLAGS_rsa_signing_key_path.empty(); const char aes_label[] = diff --git a/packager/app/widevine_encryption_flags.h b/packager/app/widevine_encryption_flags.h index acb78a61b8..8218fabd6c 100644 --- a/packager/app/widevine_encryption_flags.h +++ b/packager/app/widevine_encryption_flags.h @@ -18,6 +18,8 @@ DECLARE_string(key_server_url); DECLARE_string(content_id); DECLARE_string(policy); DECLARE_int32(max_sd_pixels); +DECLARE_int32(max_hd_pixels); +DECLARE_int32(max_uhd1_pixels); DECLARE_string(signer); DECLARE_string(aes_signing_key); DECLARE_string(aes_signing_iv); diff --git a/packager/media/base/key_source.cc b/packager/media/base/key_source.cc index 474103bf57..240c92538b 100644 --- a/packager/media/base/key_source.cc +++ b/packager/media/base/key_source.cc @@ -22,6 +22,10 @@ KeySource::TrackType KeySource::GetTrackTypeFromString( return TRACK_TYPE_SD; if (track_type_string == "HD") return TRACK_TYPE_HD; + if (track_type_string == "UHD1") + return TRACK_TYPE_UHD1; + if (track_type_string == "UHD2") + return TRACK_TYPE_UHD2; if (track_type_string == "AUDIO") return TRACK_TYPE_AUDIO; if (track_type_string == "UNSPECIFIED") @@ -36,6 +40,10 @@ std::string KeySource::TrackTypeToString(TrackType track_type) { return "SD"; case TRACK_TYPE_HD: return "HD"; + case TRACK_TYPE_UHD1: + return "UHD1"; + case TRACK_TYPE_UHD2: + return "UHD2"; case TRACK_TYPE_AUDIO: return "AUDIO"; default: diff --git a/packager/media/base/key_source.h b/packager/media/base/key_source.h index 788c83ff78..4498ebfdb3 100644 --- a/packager/media/base/key_source.h +++ b/packager/media/base/key_source.h @@ -33,9 +33,11 @@ class KeySource { TRACK_TYPE_UNKNOWN = 0, TRACK_TYPE_SD = 1, TRACK_TYPE_HD = 2, - TRACK_TYPE_AUDIO = 3, - TRACK_TYPE_UNSPECIFIED = 4, - NUM_VALID_TRACK_TYPES = 4 + TRACK_TYPE_UHD1 = 3, + TRACK_TYPE_UHD2 = 4, + TRACK_TYPE_AUDIO = 5, + TRACK_TYPE_UNSPECIFIED = 6, + NUM_VALID_TRACK_TYPES = 6 }; KeySource(); diff --git a/packager/media/base/muxer.cc b/packager/media/base/muxer.cc index e863be886a..4ab37e957a 100644 --- a/packager/media/base/muxer.cc +++ b/packager/media/base/muxer.cc @@ -20,6 +20,8 @@ Muxer::Muxer(const MuxerOptions& options) initialized_(false), encryption_key_source_(NULL), max_sd_pixels_(0), + max_hd_pixels_(0), + max_uhd1_pixels_(0), clear_lead_in_seconds_(0), crypto_period_duration_in_seconds_(0), protection_scheme_(FOURCC_NULL), @@ -30,12 +32,16 @@ Muxer::~Muxer() {} void Muxer::SetKeySource(KeySource* encryption_key_source, uint32_t max_sd_pixels, + uint32_t max_hd_pixels, + uint32_t max_uhd1_pixels, double clear_lead_in_seconds, double crypto_period_duration_in_seconds, FourCC protection_scheme) { DCHECK(encryption_key_source); encryption_key_source_ = encryption_key_source; max_sd_pixels_ = max_sd_pixels; + max_hd_pixels_ = max_hd_pixels; + max_uhd1_pixels_ = max_uhd1_pixels; clear_lead_in_seconds_ = clear_lead_in_seconds; crypto_period_duration_in_seconds_ = crypto_period_duration_in_seconds; protection_scheme_ = protection_scheme; diff --git a/packager/media/base/muxer.h b/packager/media/base/muxer.h index 751b497539..0412b4a215 100644 --- a/packager/media/base/muxer.h +++ b/packager/media/base/muxer.h @@ -35,12 +35,23 @@ class Muxer { explicit Muxer(const MuxerOptions& options); virtual ~Muxer(); + // TODO(kqyang): refactor max_sd_pixels through crypto_period_duration into + // an encapsulated EncryptionParams structure. + /// Set encryption key source. /// @param encryption_key_source points to the encryption key source. The /// caller retains ownership, and should not be NULL. /// @param max_sd_pixels specifies the threshold to determine whether a video - /// track should be considered as SD or HD. If the track has more - /// pixels per frame than max_sd_pixels, it is HD, SD otherwise. + /// track should be considered as SD. If the max pixels per frame is + /// no higher than max_sd_pixels, it is SD. + /// @param max_hd_pixels specifies the threshold to determine whether a video + /// track should be considered as HD. If the max pixels per frame is + /// higher than max_sd_pixels, but no higher than max_hd_pixels, + /// it is HD. + /// @param max_uhd1_pixels specifies the threshold to determine whether a video + /// track should be considered as UHD1. If the max pixels per frame is + /// higher than max_hd_pixels, but no higher than max_uhd1_pixels, + /// it is UHD1. Otherwise it is UHD2. /// @param clear_lead_in_seconds specifies clear lead duration in seconds. /// @param crypto_period_duration_in_seconds specifies crypto period duration /// in seconds. A positive value means key rotation is enabled, the @@ -49,6 +60,8 @@ class Muxer { /// 'cbc1', 'cbcs'. void SetKeySource(KeySource* encryption_key_source, uint32_t max_sd_pixels, + uint32_t max_hd_pixels, + uint32_t max_uhd1_pixels, double clear_lead_in_seconds, double crypto_period_duration_in_seconds, FourCC protection_scheme); @@ -89,6 +102,8 @@ class Muxer { return encryption_key_source_; } uint32_t max_sd_pixels() const { return max_sd_pixels_; } + uint32_t max_hd_pixels() const { return max_hd_pixels_; } + uint32_t max_uhd1_pixels() const { return max_uhd1_pixels_; } double clear_lead_in_seconds() const { return clear_lead_in_seconds_; } double crypto_period_duration_in_seconds() const { return crypto_period_duration_in_seconds_; @@ -120,6 +135,8 @@ class Muxer { std::vector streams_; KeySource* encryption_key_source_; uint32_t max_sd_pixels_; + uint32_t max_hd_pixels_; + uint32_t max_uhd1_pixels_; double clear_lead_in_seconds_; double crypto_period_duration_in_seconds_; FourCC protection_scheme_; diff --git a/packager/media/base/muxer_util.cc b/packager/media/base/muxer_util.cc index 6ce887b59e..765d535677 100644 --- a/packager/media/base/muxer_util.cc +++ b/packager/media/base/muxer_util.cc @@ -155,7 +155,9 @@ std::string GetSegmentName(const std::string& segment_template, } KeySource::TrackType GetTrackTypeForEncryption(const StreamInfo& stream_info, - uint32_t max_sd_pixels) { + uint32_t max_sd_pixels, + uint32_t max_hd_pixels, + uint32_t max_uhd1_pixels) { if (stream_info.stream_type() == kStreamAudio) return KeySource::TRACK_TYPE_AUDIO; @@ -166,8 +168,14 @@ KeySource::TrackType GetTrackTypeForEncryption(const StreamInfo& stream_info, const VideoStreamInfo& video_stream_info = static_cast(stream_info); uint32_t pixels = video_stream_info.width() * video_stream_info.height(); - return (pixels > max_sd_pixels) ? KeySource::TRACK_TYPE_HD - : KeySource::TRACK_TYPE_SD; + if (pixels > max_uhd1_pixels) { + return KeySource::TRACK_TYPE_UHD2; + } else if (pixels > max_hd_pixels) { + return KeySource::TRACK_TYPE_UHD1; + } else if (pixels > max_sd_pixels) { + return KeySource::TRACK_TYPE_HD; + } + return KeySource::TRACK_TYPE_SD; } } // namespace media diff --git a/packager/media/base/muxer_util.h b/packager/media/base/muxer_util.h index a9b59cd598..a2f7cd4bdb 100644 --- a/packager/media/base/muxer_util.h +++ b/packager/media/base/muxer_util.h @@ -42,10 +42,14 @@ std::string GetSegmentName(const std::string& segment_template, /// Determine the track type for encryption from input. /// @param stream_info is the info of the stream. /// @param max_sd_pixels is the maximum number of pixels to be considered SD. -/// Anything above is HD. +/// @param max_hd_pixels is the maximum number of pixels to be considered HD. +/// @param max_uhd1_pixels is the maximum number of pixels to be considered UHD1. +/// Anything above is UHD2. /// @return track type for encryption. KeySource::TrackType GetTrackTypeForEncryption(const StreamInfo& stream_info, - uint32_t max_sd_pixels); + uint32_t max_sd_pixels, + uint32_t max_hd_pixels, + uint32_t max_uhd1_pixels); } // namespace media } // namespace shaka diff --git a/packager/media/base/widevine_key_source.cc b/packager/media/base/widevine_key_source.cc index e0574e4e57..c15ece9b1d 100644 --- a/packager/media/base/widevine_key_source.cc +++ b/packager/media/base/widevine_key_source.cc @@ -401,6 +401,12 @@ void WidevineKeySource::FillRequest(bool enable_key_rotation, base::DictionaryValue* track_hd = new base::DictionaryValue(); track_hd->SetString("type", "HD"); tracks->Append(track_hd); + base::DictionaryValue* track_uhd1 = new base::DictionaryValue(); + track_uhd1->SetString("type", "UHD1"); + tracks->Append(track_uhd1); + base::DictionaryValue* track_uhd2 = new base::DictionaryValue(); + track_uhd2->SetString("type", "UHD2"); + tracks->Append(track_uhd2); base::DictionaryValue* track_audio = new base::DictionaryValue(); track_audio->SetString("type", "AUDIO"); tracks->Append(track_audio); diff --git a/packager/media/base/widevine_key_source_unittest.cc b/packager/media/base/widevine_key_source_unittest.cc index 75f15198b9..b81175a386 100644 --- a/packager/media/base/widevine_key_source_unittest.cc +++ b/packager/media/base/widevine_key_source_unittest.cc @@ -45,13 +45,16 @@ const char kLicenseStatusUnknownError[] = "UNKNOWN_ERROR"; const char kExpectedRequestMessageFormat[] = "{\"content_id\":\"%s\",\"drm_types\":[\"WIDEVINE\"],\"policy\":\"%s\"," - "\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"AUDIO\"}]}"; + "\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"UHD1\"}," + "{\"type\":\"UHD2\"},{\"type\":\"AUDIO\"}]}"; const char kExpectedRequestMessageWithAssetIdFormat[] = "{\"asset_id\":%u,\"drm_types\":[\"WIDEVINE\"]," - "\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"AUDIO\"}]}"; + "\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"UHD1\"}," + "{\"type\":\"UHD2\"},{\"type\":\"AUDIO\"}]}"; const char kExpectedRequestMessageWithPsshFormat[] = "{\"drm_types\":[\"WIDEVINE\"],\"pssh_data\":\"%s\"," - "\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"AUDIO\"}]}"; + "\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"UHD1\"}," + "{\"type\":\"UHD2\"},{\"type\":\"AUDIO\"}]}"; const char kExpectedSignedMessageFormat[] = "{\"request\":\"%s\",\"signature\":\"%s\",\"signer\":\"%s\"}"; const char kTrackFormat[] = @@ -99,9 +102,9 @@ std::string GetMockPsshData(const std::string& track_type) { } std::string GenerateMockLicenseResponse() { - const std::string kTrackTypes[] = {"SD", "HD", "AUDIO"}; + const std::string kTrackTypes[] = {"SD", "HD", "UHD1", "UHD2", "AUDIO"}; std::string tracks; - for (size_t i = 0; i < 3; ++i) { + for (size_t i = 0; i < 5; ++i) { if (!tracks.empty()) tracks += ","; tracks += base::StringPrintf( @@ -115,9 +118,9 @@ std::string GenerateMockLicenseResponse() { } std::string GenerateMockClassicLicenseResponse() { - const std::string kTrackTypes[] = {"SD", "HD", "AUDIO"}; + const std::string kTrackTypes[] = {"SD", "HD", "UHD1", "UHD2", "AUDIO"}; std::string tracks; - for (size_t i = 0; i < 3; ++i) { + for (size_t i = 0; i < 5; ++i) { if (!tracks.empty()) tracks += ","; tracks += base::StringPrintf( @@ -180,7 +183,7 @@ class WidevineKeySourceTest : public ::testing::Test, void VerifyKeys(bool classic) { EncryptionKey encryption_key; - const std::string kTrackTypes[] = {"SD", "HD", "AUDIO"}; + const std::string kTrackTypes[] = {"SD", "HD", "UHD1", "UHD2", "AUDIO"}; for (size_t i = 0; i < arraysize(kTrackTypes); ++i) { ASSERT_OK(widevine_key_source_->GetKey( KeySource::GetTrackTypeFromString(kTrackTypes[i]), @@ -228,6 +231,10 @@ TEST_P(WidevineKeySourceTest, GetTrackTypeFromString) { KeySource::GetTrackTypeFromString("SD")); EXPECT_EQ(KeySource::TRACK_TYPE_HD, KeySource::GetTrackTypeFromString("HD")); + EXPECT_EQ(KeySource::TRACK_TYPE_UHD1, + KeySource::GetTrackTypeFromString("UHD1")); + EXPECT_EQ(KeySource::TRACK_TYPE_UHD2, + KeySource::GetTrackTypeFromString("UHD2")); EXPECT_EQ(KeySource::TRACK_TYPE_AUDIO, KeySource::GetTrackTypeFromString("AUDIO")); EXPECT_EQ(KeySource::TRACK_TYPE_UNKNOWN, @@ -413,7 +420,8 @@ namespace { const char kCryptoPeriodRequestMessageFormat[] = "{\"content_id\":\"%s\",\"crypto_period_count\":%u,\"drm_types\":[" "\"WIDEVINE\"],\"first_crypto_period_index\":%u,\"policy\":\"%s\"," - "\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"AUDIO\"}]}"; + "\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"UHD1\"}," + "{\"type\":\"UHD2\"},{\"type\":\"AUDIO\"}]}"; const char kCryptoPeriodTrackFormat[] = "{\"type\":\"%s\",\"key_id\":\"%s\",\"key\":" @@ -427,12 +435,12 @@ std::string GetMockKey(const std::string& track_type, uint32_t index) { std::string GenerateMockKeyRotationLicenseResponse( uint32_t initial_crypto_period_index, uint32_t crypto_period_count) { - const std::string kTrackTypes[] = {"SD", "HD", "AUDIO"}; + const std::string kTrackTypes[] = {"SD", "HD", "UHD1", "UHD2", "AUDIO"}; std::string tracks; for (uint32_t index = initial_crypto_period_index; index < initial_crypto_period_index + crypto_period_count; ++index) { - for (size_t i = 0; i < 3; ++i) { + for (size_t i = 0; i < 5; ++i) { if (!tracks.empty()) tracks += ","; tracks += base::StringPrintf( @@ -495,8 +503,8 @@ TEST_P(WidevineKeySourceTest, KeyRotationTest) { EncryptionKey encryption_key; for (size_t i = 0; i < arraysize(kCryptoPeriodIndexes); ++i) { - const std::string kTrackTypes[] = {"SD", "HD", "AUDIO"}; - for (size_t j = 0; j < 3; ++j) { + const std::string kTrackTypes[] = {"SD", "HD", "UHD1", "UHD2", "AUDIO"}; + for (size_t j = 0; j < 5; ++j) { ASSERT_OK(widevine_key_source_->GetCryptoPeriodKey( kCryptoPeriodIndexes[i], KeySource::GetTrackTypeFromString(kTrackTypes[j]), diff --git a/packager/media/formats/mp2t/ts_muxer.cc b/packager/media/formats/mp2t/ts_muxer.cc index 1951d76ca1..0b822fade7 100644 --- a/packager/media/formats/mp2t/ts_muxer.cc +++ b/packager/media/formats/mp2t/ts_muxer.cc @@ -24,7 +24,8 @@ Status TsMuxer::Initialize() { segmenter_.reset(new TsSegmenter(options(), muxer_listener())); Status status = segmenter_->Initialize(*streams()[0]->info(), encryption_key_source(), - max_sd_pixels(), clear_lead_in_seconds()); + max_sd_pixels(), max_hd_pixels(), + max_uhd1_pixels(), clear_lead_in_seconds()); FireOnMediaStartEvent(); return status; } diff --git a/packager/media/formats/mp2t/ts_segmenter.cc b/packager/media/formats/mp2t/ts_segmenter.cc index b2002a079f..99a1afb01a 100644 --- a/packager/media/formats/mp2t/ts_segmenter.cc +++ b/packager/media/formats/mp2t/ts_segmenter.cc @@ -34,6 +34,8 @@ TsSegmenter::~TsSegmenter() {} Status TsSegmenter::Initialize(const StreamInfo& stream_info, KeySource* encryption_key_source, uint32_t max_sd_pixels, + uint32_t max_hd_pixels, + uint32_t max_uhd1_pixels, double clear_lead_in_seconds) { if (muxer_options_.segment_template.empty()) return Status(error::MUXER_FAILURE, "Segment template not specified."); @@ -47,7 +49,8 @@ Status TsSegmenter::Initialize(const StreamInfo& stream_info, if (encryption_key_source) { std::unique_ptr encryption_key(new EncryptionKey()); const KeySource::TrackType type = - GetTrackTypeForEncryption(stream_info, max_sd_pixels); + GetTrackTypeForEncryption(stream_info, max_sd_pixels, + max_hd_pixels, max_uhd1_pixels); Status status = encryption_key_source->GetKey(type, encryption_key.get()); if (encryption_key->iv.empty()) { diff --git a/packager/media/formats/mp2t/ts_segmenter.h b/packager/media/formats/mp2t/ts_segmenter.h index 55055c633c..b61da4be1c 100644 --- a/packager/media/formats/mp2t/ts_segmenter.h +++ b/packager/media/formats/mp2t/ts_segmenter.h @@ -43,6 +43,8 @@ class TsSegmenter { Status Initialize(const StreamInfo& stream_info, KeySource* encryption_key_source, uint32_t max_sd_pixels, + uint32_t max_hd_pixels, + uint32_t max_uhd1_pixels, double clear_lead_in_seconds); /// Finalize the segmenter. diff --git a/packager/media/formats/mp2t/ts_segmenter_unittest.cc b/packager/media/formats/mp2t/ts_segmenter_unittest.cc index e24a8a42f1..d2f1e240de 100644 --- a/packager/media/formats/mp2t/ts_segmenter_unittest.cc +++ b/packager/media/formats/mp2t/ts_segmenter_unittest.cc @@ -124,7 +124,7 @@ TEST_F(TsSegmenterTest, Initialize) { segmenter.InjectPesPacketGeneratorForTesting( std::move(mock_pes_packet_generator_)); - EXPECT_OK(segmenter.Initialize(*stream_info, nullptr, 0, 0)); + EXPECT_OK(segmenter.Initialize(*stream_info, nullptr, 0, 0, 0, 0)); } TEST_F(TsSegmenterTest, AddSample) { @@ -171,7 +171,7 @@ TEST_F(TsSegmenterTest, AddSample) { segmenter.InjectPesPacketGeneratorForTesting( std::move(mock_pes_packet_generator_)); - EXPECT_OK(segmenter.Initialize(*stream_info, nullptr, 0, 0)); + EXPECT_OK(segmenter.Initialize(*stream_info, nullptr, 0, 0, 0, 0)); EXPECT_OK(segmenter.AddSample(sample)); } @@ -275,7 +275,7 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) { segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_)); segmenter.InjectPesPacketGeneratorForTesting( std::move(mock_pes_packet_generator_)); - EXPECT_OK(segmenter.Initialize(*stream_info, nullptr, 0, 0)); + EXPECT_OK(segmenter.Initialize(*stream_info, nullptr, 0, 0, 0, 0)); EXPECT_OK(segmenter.AddSample(sample1)); EXPECT_OK(segmenter.AddSample(sample2)); } @@ -302,7 +302,7 @@ TEST_F(TsSegmenterTest, InitializeThenFinalize) { segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_)); segmenter.InjectPesPacketGeneratorForTesting( std::move(mock_pes_packet_generator_)); - EXPECT_OK(segmenter.Initialize(*stream_info, nullptr, 0, 0)); + EXPECT_OK(segmenter.Initialize(*stream_info, nullptr, 0, 0, 0, 0)); EXPECT_OK(segmenter.Finalize()); } @@ -333,7 +333,7 @@ TEST_F(TsSegmenterTest, Finalize) { segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_)); segmenter.InjectPesPacketGeneratorForTesting( std::move(mock_pes_packet_generator_)); - EXPECT_OK(segmenter.Initialize(*stream_info, nullptr, 0, 0)); + EXPECT_OK(segmenter.Initialize(*stream_info, nullptr, 0, 0, 0, 0)); segmenter.SetTsWriterFileOpenedForTesting(true); EXPECT_OK(segmenter.Finalize()); } @@ -439,7 +439,7 @@ TEST_F(TsSegmenterTest, SegmentOnlyBeforeKeyFrame) { segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_)); segmenter.InjectPesPacketGeneratorForTesting( std::move(mock_pes_packet_generator_)); - EXPECT_OK(segmenter.Initialize(*stream_info, nullptr, 0, 0)); + EXPECT_OK(segmenter.Initialize(*stream_info, nullptr, 0, 0, 0, 0)); EXPECT_OK(segmenter.AddSample(key_frame_sample1)); EXPECT_OK(segmenter.AddSample(non_key_frame_sample)); EXPECT_OK(segmenter.AddSample(key_frame_sample2)); @@ -475,11 +475,14 @@ TEST_F(TsSegmenterTest, WithEncryptionNoClearLead) { .WillOnce(Return(Status::OK)); const uint32_t k480pPixels = 640 * 480; + const uint32_t k1080pPixels = 1920 * 1080; + const uint32_t k2160pPixels = 4096 * 2160; // Set this to 0 so that Finalize will call // PesPacketGenerator::SetEncryptionKey(). // Even tho no samples have been added. const double kClearLeadSeconds = 0; EXPECT_OK(segmenter.Initialize(*stream_info, &mock_key_source, k480pPixels, + k1080pPixels, k2160pPixels, kClearLeadSeconds)); } @@ -513,11 +516,14 @@ TEST_F(TsSegmenterTest, WithEncryptionNoClearLeadNoMuxerListener) { .WillOnce(Return(Status::OK)); const uint32_t k480pPixels = 640 * 480; + const uint32_t k1080pPixels = 1920 * 1080; + const uint32_t k2160pPixels = 4096 * 2160; // Set this to 0 so that Finalize will call // PesPacketGenerator::SetEncryptionKey(). // Even tho no samples have been added. const double kClearLeadSeconds = 0; EXPECT_OK(segmenter.Initialize(*stream_info, &mock_key_source, k480pPixels, + k1080pPixels, k2160pPixels, kClearLeadSeconds)); } @@ -530,6 +536,8 @@ TEST_F(TsSegmenterTest, WithEncryptionWithClearLead) { MuxerOptions options; options.segment_duration = 1.0; + const uint32_t k1080pPixels = 1920 * 1080; + const uint32_t k2160pPixels = 4096 * 2160; const double kClearLeadSeconds = 1.0; options.segment_template = "file$Number$.ts"; @@ -610,6 +618,7 @@ TEST_F(TsSegmenterTest, WithEncryptionWithClearLead) { EXPECT_CALL(mock_listener, OnEncryptionInfoReady(_, _, _, _, _)); EXPECT_OK(segmenter.Initialize(*stream_info, &mock_key_source, 0, + k1080pPixels, k2160pPixels, kClearLeadSeconds)); EXPECT_OK(segmenter.AddSample(sample1)); diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index f19d4ea335..a4753176c4 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -144,8 +144,9 @@ Status MP4Muxer::Initialize() { const Status segmenter_initialized = segmenter_->Initialize( streams(), muxer_listener(), progress_listener(), encryption_key_source(), - max_sd_pixels(), clear_lead_in_seconds(), - crypto_period_duration_in_seconds(), protection_scheme()); + max_sd_pixels(), max_hd_pixels(), max_uhd1_pixels(), + clear_lead_in_seconds(), crypto_period_duration_in_seconds(), + protection_scheme()); if (!segmenter_initialized.ok()) return segmenter_initialized; diff --git a/packager/media/formats/mp4/segmenter.cc b/packager/media/formats/mp4/segmenter.cc index 1487ad3849..0bf527f6d8 100644 --- a/packager/media/formats/mp4/segmenter.cc +++ b/packager/media/formats/mp4/segmenter.cc @@ -167,6 +167,8 @@ Status Segmenter::Initialize(const std::vector& streams, ProgressListener* progress_listener, KeySource* encryption_key_source, uint32_t max_sd_pixels, + uint32_t max_hd_pixels, + uint32_t max_uhd1_pixels, double clear_lead_in_seconds, double crypto_period_duration_in_seconds, FourCC protection_scheme) { @@ -196,7 +198,8 @@ Status Segmenter::Initialize(const std::vector& streams, } KeySource::TrackType track_type = - GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels); + GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels, + max_hd_pixels, max_uhd1_pixels); SampleDescription& description = moov_->tracks[i].media.information.sample_table.description; ProtectionPattern pattern = diff --git a/packager/media/formats/mp4/segmenter.h b/packager/media/formats/mp4/segmenter.h index 450c6aaca5..e3d57ba9d8 100644 --- a/packager/media/formats/mp4/segmenter.h +++ b/packager/media/formats/mp4/segmenter.h @@ -55,8 +55,16 @@ class Segmenter { /// the encryption keys. It can be NULL to indicate that no encryption /// is required. /// @param max_sd_pixels specifies the threshold to determine whether a video - /// track should be considered as SD or HD. If the track has more - /// pixels per frame than max_sd_pixels, it is HD, SD otherwise. + /// track should be considered as SD. If the max pixels per frame is + /// no higher than max_sd_pixels, it is SD. + /// @param max_hd_pixels specifies the threshold to determine whether a video + /// track should be considered as HD. If the max pixels per frame is + /// higher than max_sd_pixels, but no higher than max_hd_pixels, + /// it is HD. + /// @param max_uhd1_pixels specifies the threshold to determine whether a video + /// track should be considered as UHD1. If the max pixels per frame is + /// higher than max_hd_pixels, but no higher than max_uhd1_pixels, + /// it is UHD1. Otherwise it is UHD2. /// @param clear_time specifies clear lead duration in seconds. /// @param crypto_period_duration specifies crypto period duration in seconds. /// @param protection_scheme specifies the protection scheme: 'cenc', 'cens', @@ -67,6 +75,8 @@ class Segmenter { ProgressListener* progress_listener, KeySource* encryption_key_source, uint32_t max_sd_pixels, + uint32_t max_hd_pixels, + uint32_t max_uhd1_pixels, double clear_lead_in_seconds, double crypto_period_duration_in_seconds, FourCC protection_scheme); diff --git a/packager/media/formats/webm/segmenter.cc b/packager/media/formats/webm/segmenter.cc index 4b75be2978..b6310b37ff 100644 --- a/packager/media/formats/webm/segmenter.cc +++ b/packager/media/formats/webm/segmenter.cc @@ -54,6 +54,8 @@ Status Segmenter::Initialize(std::unique_ptr writer, MuxerListener* muxer_listener, KeySource* encryption_key_source, uint32_t max_sd_pixels, + uint32_t max_hd_pixels, + uint32_t max_uhd1_pixels, double clear_lead_in_seconds) { muxer_listener_ = muxer_listener; info_ = info; @@ -82,7 +84,9 @@ Status Segmenter::Initialize(std::unique_ptr writer, Status status; if (encryption_key_source) { status = InitializeEncryptor(encryption_key_source, - max_sd_pixels); + max_sd_pixels, + max_hd_pixels, + max_uhd1_pixels); if (!status.ok()) return status; } @@ -366,10 +370,13 @@ Status Segmenter::CreateAudioTrack(AudioStreamInfo* info) { } Status Segmenter::InitializeEncryptor(KeySource* key_source, - uint32_t max_sd_pixels) { + uint32_t max_sd_pixels, + uint32_t max_hd_pixels, + uint32_t max_uhd1_pixels) { encryptor_.reset(new Encryptor()); const KeySource::TrackType track_type = - GetTrackTypeForEncryption(*info_, max_sd_pixels); + GetTrackTypeForEncryption(*info_, max_sd_pixels, max_hd_pixels, + max_uhd1_pixels); if (track_type == KeySource::TrackType::TRACK_TYPE_UNKNOWN) return Status::OK; return encryptor_->Initialize(muxer_listener_, track_type, info_->codec(), diff --git a/packager/media/formats/webm/segmenter.h b/packager/media/formats/webm/segmenter.h index e9f6b95678..aeea449eb5 100644 --- a/packager/media/formats/webm/segmenter.h +++ b/packager/media/formats/webm/segmenter.h @@ -46,8 +46,16 @@ class Segmenter { /// the encryption keys. It can be NULL to indicate that no encryption /// is required. /// @param max_sd_pixels specifies the threshold to determine whether a video - /// track should be considered as SD or HD. If the track has more - /// pixels per frame than max_sd_pixels, it is HD, SD otherwise. + /// track should be considered as SD. If the max pixels per frame is + /// no higher than max_sd_pixels, it is SD. + /// @param max_hd_pixels specifies the threshold to determine whether a video + /// track should be considered as HD. If the max pixels per frame is + /// higher than max_sd_pixels, but no higher than max_hd_pixels, + /// it is HD. + /// @param max_uhd1_pixels specifies the threshold to determine whether a video + /// track should be considered as UHD1. If the max pixels per frame is + /// higher than max_hd_pixels, but no higher than max_uhd1_pixels, + /// it is UHD1. Otherwise it is UHD2. /// @param clear_time specifies clear lead duration in seconds. /// @return OK on success, an error status otherwise. Status Initialize(std::unique_ptr writer, @@ -56,6 +64,8 @@ class Segmenter { MuxerListener* muxer_listener, KeySource* encryption_key_source, uint32_t max_sd_pixels, + uint32_t max_hd_pixels, + uint32_t max_uhd1_pixels, double clear_lead_in_seconds); /// Finalize the segmenter. @@ -114,7 +124,8 @@ class Segmenter { private: Status CreateVideoTrack(VideoStreamInfo* info); Status CreateAudioTrack(AudioStreamInfo* info); - Status InitializeEncryptor(KeySource* key_source, uint32_t max_sd_pixels); + Status InitializeEncryptor(KeySource* key_source, uint32_t max_sd_pixels, + uint32_t max_hd_pixels, uint32_t max_uhd1_pixels); // Writes the previous frame to the file. Status WriteFrame(bool write_duration); diff --git a/packager/media/formats/webm/segmenter_test_base.h b/packager/media/formats/webm/segmenter_test_base.h index 4f3168092b..89cc2d7a12 100644 --- a/packager/media/formats/webm/segmenter_test_base.h +++ b/packager/media/formats/webm/segmenter_test_base.h @@ -56,6 +56,7 @@ class SegmentTestBase : public ::testing::Test { ASSERT_OK(segmenter->Initialize( std::move(writer), info, NULL /* progress_listener */, NULL /* muxer_listener */, key_source, 0 /* max_sd_pixels */, + 0 /* max_hd_pixels */, 0 /* max_uhd1_pixels */, 1 /* clear_lead_in_seconds */)); *result = std::move(segmenter); } diff --git a/packager/media/formats/webm/webm_muxer.cc b/packager/media/formats/webm/webm_muxer.cc index 3f3d55e552..9ba07cef84 100644 --- a/packager/media/formats/webm/webm_muxer.cc +++ b/packager/media/formats/webm/webm_muxer.cc @@ -52,7 +52,7 @@ Status WebMMuxer::Initialize() { Status initialized = segmenter_->Initialize( std::move(writer), streams()[0]->info().get(), progress_listener(), muxer_listener(), encryption_key_source(), max_sd_pixels(), - clear_lead_in_seconds()); + max_hd_pixels(), max_uhd1_pixels(), clear_lead_in_seconds()); if (!initialized.ok()) return initialized; diff --git a/packager/media/test/packager_test.cc b/packager/media/test/packager_test.cc index 512a0abda7..81bad2b5b3 100644 --- a/packager/media/test/packager_test.cc +++ b/packager/media/test/packager_test.cc @@ -15,6 +15,7 @@ #include "packager/media/base/fourccs.h" #include "packager/media/base/media_stream.h" #include "packager/media/base/muxer.h" +#include "packager/media/base/muxer_util.h" #include "packager/media/base/stream_info.h" #include "packager/media/base/test/status_test_util.h" #include "packager/media/formats/mp4/mp4_muxer.h" @@ -57,6 +58,11 @@ const char kKeyHex[] = "6fc96fe628a265b13aeddec0bc421f4d"; const double kClearLeadInSeconds = 1.5; const double kCryptoDurationInSeconds = 0; // Key rotation is disabled. +// Track resolution constants. +const uint32_t kMaxSDPixels = 640 * 480; +const uint32_t kMaxHDPixels = 1920 * 1080; +const uint32_t kMaxUHD1Pixels = 4096 * 2160; + MediaStream* FindFirstStreamOfType( const std::vector>& streams, StreamType stream_type) { @@ -178,7 +184,8 @@ void PackagerTestBasic::Remux(const std::string& input, if (enable_encryption) { muxer_video->SetKeySource(encryption_key_source.get(), - KeySource::TRACK_TYPE_SD, kClearLeadInSeconds, + kMaxSDPixels, kMaxHDPixels, + kMaxUHD1Pixels, kClearLeadInSeconds, kCryptoDurationInSeconds, FOURCC_cenc); } } @@ -198,7 +205,8 @@ void PackagerTestBasic::Remux(const std::string& input, if (enable_encryption) { muxer_audio->SetKeySource(encryption_key_source.get(), - KeySource::TRACK_TYPE_SD, kClearLeadInSeconds, + kMaxSDPixels, kMaxHDPixels, + kMaxUHD1Pixels, kClearLeadInSeconds, kCryptoDurationInSeconds, FOURCC_cenc); } } @@ -314,6 +322,39 @@ TEST_P(PackagerTestBasic, MP4MuxerLanguageWithSubtag) { ASSERT_EQ("por", stream->info()->language()); } +TEST_P(PackagerTestBasic, GetTrackTypeForEncryption) { + Demuxer demuxer(GetFullPath(GetParam())); + ASSERT_OK(demuxer.Initialize()); + + MediaStream* video_stream = FindFirstVideoStream(demuxer.streams()); + MediaStream* audio_stream = FindFirstAudioStream(demuxer.streams()); + + // Typical resolution constraints should set the resolution in the SD range + KeySource::TrackType track_type = GetTrackTypeForEncryption( + *video_stream->info(), kMaxSDPixels, kMaxHDPixels, kMaxUHD1Pixels); + ASSERT_EQ(FixedKeySource::GetTrackTypeFromString("SD"), track_type); + + // Setting the max SD value to 1 should set the resolution in the HD range + track_type = GetTrackTypeForEncryption( + *video_stream->info(), 1, kMaxHDPixels, kMaxUHD1Pixels); + ASSERT_EQ(FixedKeySource::GetTrackTypeFromString("HD"), track_type); + + // Setting the max HD value to 2 should set the resolution in the UHD1 range + track_type = GetTrackTypeForEncryption( + *video_stream->info(), 1, 2, kMaxUHD1Pixels); + ASSERT_EQ(FixedKeySource::GetTrackTypeFromString("UHD1"), track_type); + + // Setting the max UHD1 value to 3 should set the resolution in the UHD2 range + track_type = GetTrackTypeForEncryption( + *video_stream->info(), 1, 2, 3); + ASSERT_EQ(FixedKeySource::GetTrackTypeFromString("UHD2"), track_type); + + // Audio stream should always set the track_type to AUDIO + track_type = GetTrackTypeForEncryption( + *audio_stream->info(), kMaxSDPixels, kMaxHDPixels, kMaxUHD1Pixels); + ASSERT_EQ(FixedKeySource::GetTrackTypeFromString("AUDIO"), track_type); +} + class PackagerTest : public PackagerTestBasic { public: void SetUp() override {