diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py index 695ed5b38a..79ee48f5af 100755 --- a/packager/app/test/packager_test.py +++ b/packager/app/test/packager_test.py @@ -486,7 +486,8 @@ class PackagerAppTest(unittest.TestCase): allow_codec_switching=False, dash_force_segment_list=False, force_cl_index=None, - start_segment_number=None): + start_segment_number=None, + use_dovi_supplemental_codecs=None): flags = ['--single_threaded'] if not strip_parameter_set_nalus: @@ -544,6 +545,9 @@ class PackagerAppTest(unittest.TestCase): if not dash_if_iop: flags.append('--generate_dash_if_iop_compliant_mpd=false') + if use_dovi_supplemental_codecs: + flags.append('--use_dovi_supplemental_codecs') + if output_media_info: flags.append('--output_media_info') if output_dash: @@ -1456,6 +1460,42 @@ class PackagerFunctionalTest(PackagerAppTest): self.assertPackageSuccess(streams, flags) self._CheckTestResults('dolby-vision-profile-8-with-encryption') + # TODO(cosmin): shared_library build does not support + # use_dovi_supplemental_codecs + @unittest.skipIf( + test_env.BUILD_TYPE == 'shared', + 'libpackager shared_library does not support ' + '--use_dovi_supplemental_codecs flag.' + ) + def testDolbyVisionProfile8UsingSupplementalCodecs(self): + streams = [ + self._GetStream('video', test_file='sparks_dovi_8.mp4') + ] + flags = self._GetFlags(output_dash=True, + output_hls=True, + use_dovi_supplemental_codecs=True) + + self.assertPackageSuccess(streams, flags) + self._CheckTestResults('dolby-vision-profile-8-supplemental-codecs') + + # TODO(cosmin): shared_library build does not support + # use_dovi_supplemental_codecs + @unittest.skipIf( + test_env.BUILD_TYPE == 'shared', + 'libpackager shared_library does not support ' + '--use_dovi_supplemental_codecs flag.' + ) + def testDolbyVisionProfile10UsingSupplementalCodecs(self): + streams = [ + self._GetStream('video', test_file='sparks_dovi_10.mp4') + ] + flags = self._GetFlags(output_dash=True, + output_hls=True, + use_dovi_supplemental_codecs=True) + + self.assertPackageSuccess(streams, flags) + self._CheckTestResults('dolby-vision-profile-10-supplemental-codecs') + def testVp8Mp4WithEncryption(self): streams = [ self._GetStream('video', diff --git a/packager/app/test/testdata/dolby-vision-profile-10-supplemental-codecs/output.m3u8 b/packager/app/test/testdata/dolby-vision-profile-10-supplemental-codecs/output.m3u8 new file mode 100644 index 0000000000..d9bf5154cc --- /dev/null +++ b/packager/app/test/testdata/dolby-vision-profile-10-supplemental-codecs/output.m3u8 @@ -0,0 +1,7 @@ +#EXTM3U +## Generated with https://github.com/shaka-project/shaka-packager version -- + +#EXT-X-INDEPENDENT-SEGMENTS + +#EXT-X-STREAM-INF:BANDWIDTH=550702,AVERAGE-BANDWIDTH=577484,CODECS="av01.0.04M.10.0.111.09.16.09.0",SUPPLEMENTAL-CODECS="dav1.10.01/db1p",RESOLUTION=640x360,FRAME-RATE=59.940,VIDEO-RANGE=PQ,CLOSED-CAPTIONS=NONE +stream_0.m3u8 diff --git a/packager/app/test/testdata/dolby-vision-profile-10-supplemental-codecs/output.mpd b/packager/app/test/testdata/dolby-vision-profile-10-supplemental-codecs/output.mpd new file mode 100644 index 0000000000..2a43ee4e41 --- /dev/null +++ b/packager/app/test/testdata/dolby-vision-profile-10-supplemental-codecs/output.mpd @@ -0,0 +1,17 @@ + + + + + + + + + + sparks_dovi_10-video.mp4 + + + + + + + diff --git a/packager/app/test/testdata/dolby-vision-profile-10-supplemental-codecs/sparks_dovi_10-video.mp4 b/packager/app/test/testdata/dolby-vision-profile-10-supplemental-codecs/sparks_dovi_10-video.mp4 new file mode 100644 index 0000000000..b4cd29e4ab Binary files /dev/null and b/packager/app/test/testdata/dolby-vision-profile-10-supplemental-codecs/sparks_dovi_10-video.mp4 differ diff --git a/packager/app/test/testdata/dolby-vision-profile-10-supplemental-codecs/stream_0.m3u8 b/packager/app/test/testdata/dolby-vision-profile-10-supplemental-codecs/stream_0.m3u8 new file mode 100644 index 0000000000..06ba5a56fc --- /dev/null +++ b/packager/app/test/testdata/dolby-vision-profile-10-supplemental-codecs/stream_0.m3u8 @@ -0,0 +1,13 @@ +#EXTM3U +#EXT-X-VERSION:6 +## Generated with https://github.com/shaka-project/shaka-packager version -- +#EXT-X-TARGETDURATION:6 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-MAP:URI="sparks_dovi_10-video.mp4",BYTERANGE="871@0" +#EXTINF:5.355, +#EXT-X-BYTERANGE:368650@927 +sparks_dovi_10-video.mp4 +#EXTINF:0.667, +#EXT-X-BYTERANGE:66100 +sparks_dovi_10-video.mp4 +#EXT-X-ENDLIST diff --git a/packager/app/test/testdata/dolby-vision-profile-5-with-encryption/output.mpd b/packager/app/test/testdata/dolby-vision-profile-5-with-encryption/output.mpd index 300d51637a..da522e3591 100644 --- a/packager/app/test/testdata/dolby-vision-profile-5-with-encryption/output.mpd +++ b/packager/app/test/testdata/dolby-vision-profile-5-with-encryption/output.mpd @@ -3,6 +3,8 @@ + + diff --git a/packager/app/test/testdata/dolby-vision-profile-8-supplemental-codecs/output.m3u8 b/packager/app/test/testdata/dolby-vision-profile-8-supplemental-codecs/output.m3u8 new file mode 100644 index 0000000000..c18631c4c6 --- /dev/null +++ b/packager/app/test/testdata/dolby-vision-profile-8-supplemental-codecs/output.m3u8 @@ -0,0 +1,7 @@ +#EXTM3U +## Generated with https://github.com/shaka-project/shaka-packager version -- + +#EXT-X-INDEPENDENT-SEGMENTS + +#EXT-X-STREAM-INF:BANDWIDTH=807837,AVERAGE-BANDWIDTH=748074,CODECS="hvc1.2.4.L90.90",SUPPLEMENTAL-CODECS="dvh1.08.01/db2g",RESOLUTION=640x360,FRAME-RATE=59.940,VIDEO-RANGE=PQ,CLOSED-CAPTIONS=NONE +stream_0.m3u8 diff --git a/packager/app/test/testdata/dolby-vision-profile-8-supplemental-codecs/output.mpd b/packager/app/test/testdata/dolby-vision-profile-8-supplemental-codecs/output.mpd new file mode 100644 index 0000000000..5d06e41c78 --- /dev/null +++ b/packager/app/test/testdata/dolby-vision-profile-8-supplemental-codecs/output.mpd @@ -0,0 +1,17 @@ + + + + + + + + + + sparks_dovi_8-video.mp4 + + + + + + + diff --git a/packager/app/test/testdata/dolby-vision-profile-8-supplemental-codecs/sparks_dovi_8-video.mp4 b/packager/app/test/testdata/dolby-vision-profile-8-supplemental-codecs/sparks_dovi_8-video.mp4 new file mode 100644 index 0000000000..ce05290a78 Binary files /dev/null and b/packager/app/test/testdata/dolby-vision-profile-8-supplemental-codecs/sparks_dovi_8-video.mp4 differ diff --git a/packager/app/test/testdata/dolby-vision-profile-8-supplemental-codecs/stream_0.m3u8 b/packager/app/test/testdata/dolby-vision-profile-8-supplemental-codecs/stream_0.m3u8 new file mode 100644 index 0000000000..6b90ac6a7e --- /dev/null +++ b/packager/app/test/testdata/dolby-vision-profile-8-supplemental-codecs/stream_0.m3u8 @@ -0,0 +1,19 @@ +#EXTM3U +#EXT-X-VERSION:6 +## Generated with https://github.com/shaka-project/shaka-packager version -- +#EXT-X-TARGETDURATION:3 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-MAP:URI="sparks_dovi_8-video.mp4",BYTERANGE="992@0" +#EXTINF:2.002, +#EXT-X-BYTERANGE:172013@1072 +sparks_dovi_8-video.mp4 +#EXTINF:2.002, +#EXT-X-BYTERANGE:186781 +sparks_dovi_8-video.mp4 +#EXTINF:2.002, +#EXT-X-BYTERANGE:202161 +sparks_dovi_8-video.mp4 +#EXTINF:0.017, +#EXT-X-BYTERANGE:2221 +sparks_dovi_8-video.mp4 +#EXT-X-ENDLIST diff --git a/packager/app/test/testdata/dolby-vision-profile-8-with-encryption/output.mpd b/packager/app/test/testdata/dolby-vision-profile-8-with-encryption/output.mpd index 0acbe5ca43..7f5da8fad9 100644 --- a/packager/app/test/testdata/dolby-vision-profile-8-with-encryption/output.mpd +++ b/packager/app/test/testdata/dolby-vision-profile-8-with-encryption/output.mpd @@ -3,6 +3,8 @@ + + @@ -16,6 +18,8 @@ + + diff --git a/packager/app/test/testdata/encryption-and-output-media-info-and-mpd-from-media-info/bear-640x360-video.mp4.media_info b/packager/app/test/testdata/encryption-and-output-media-info-and-mpd-from-media-info/bear-640x360-video.mp4.media_info index f13a0e9fa8..e9054215f7 100644 --- a/packager/app/test/testdata/encryption-and-output-media-info-and-mpd-from-media-info/bear-640x360-video.mp4.media_info +++ b/packager/app/test/testdata/encryption-and-output-media-info-and-mpd-from-media-info/bear-640x360-video.mp4.media_info @@ -8,6 +8,8 @@ video_info { decoder_config: "\001d\000\036\377\341\000\031gd\000\036\254\331@\240/\371p\021\000\000\003\003\351\000\000\352`\017\026-\226\001\000\006h\353\343\313\"\300" pixel_width: 1 pixel_height: 1 + supplemental_codec: "" + compatible_brand: 0 } init_range { begin: 0 diff --git a/packager/app/test/testdata/encryption-and-output-media-info/bear-640x360-video.mp4.media_info b/packager/app/test/testdata/encryption-and-output-media-info/bear-640x360-video.mp4.media_info index f13a0e9fa8..e9054215f7 100644 --- a/packager/app/test/testdata/encryption-and-output-media-info/bear-640x360-video.mp4.media_info +++ b/packager/app/test/testdata/encryption-and-output-media-info/bear-640x360-video.mp4.media_info @@ -8,6 +8,8 @@ video_info { decoder_config: "\001d\000\036\377\341\000\031gd\000\036\254\331@\240/\371p\021\000\000\003\003\351\000\000\352`\017\026-\226\001\000\006h\353\343\313\"\300" pixel_width: 1 pixel_height: 1 + supplemental_codec: "" + compatible_brand: 0 } init_range { begin: 0 diff --git a/packager/app/test/testdata/hdr10-with-encryption/output.mpd b/packager/app/test/testdata/hdr10-with-encryption/output.mpd index af2733fe84..3eaf56c203 100644 --- a/packager/app/test/testdata/hdr10-with-encryption/output.mpd +++ b/packager/app/test/testdata/hdr10-with-encryption/output.mpd @@ -3,6 +3,8 @@ + + diff --git a/packager/hls/base/master_playlist.cc b/packager/hls/base/master_playlist.cc index 0b5c22f935..8cc5eb714d 100644 --- a/packager/hls/base/master_playlist.cc +++ b/packager/hls/base/master_playlist.cc @@ -228,6 +228,15 @@ void BuildStreamInfTag(const MediaPlaylist& playlist, variant.text_codecs.end()); tag.AddQuotedString("CODECS", absl::StrJoin(all_codecs, ",")); + if (playlist.supplemental_codec() != "" && + playlist.compatible_brand() != media::FOURCC_NULL) { + std::vector supplemental_codecs; + supplemental_codecs.push_back(playlist.supplemental_codec()); + supplemental_codecs.push_back(FourCCToString(playlist.compatible_brand())); + tag.AddQuotedString("SUPPLEMENTAL-CODECS", + absl::StrJoin(supplemental_codecs, "/")); + } + uint32_t width; uint32_t height; if (playlist.GetDisplayResolution(&width, &height)) { diff --git a/packager/hls/base/media_playlist.cc b/packager/hls/base/media_playlist.cc index ca5ac9f09c..b480c0e2ce 100644 --- a/packager/hls/base/media_playlist.cc +++ b/packager/hls/base/media_playlist.cc @@ -393,6 +393,13 @@ bool MediaPlaylist::SetMediaInfo(const MediaInfo& media_info) { if (media_info.has_video_info()) { stream_type_ = MediaPlaylistStreamType::kVideo; codec_ = AdjustVideoCodec(media_info.video_info().codec()); + if (media_info.video_info().has_supplemental_codec() && + media_info.video_info().has_compatible_brand()) { + supplemental_codec_ = + AdjustVideoCodec(media_info.video_info().supplemental_codec()); + compatible_brand_ = static_cast( + media_info.video_info().compatible_brand()); + } } else if (media_info.has_audio_info()) { stream_type_ = MediaPlaylistStreamType::kAudio; codec_ = media_info.audio_info().codec(); @@ -576,10 +583,23 @@ std::string MediaPlaylist::GetVideoRange() const { // https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-02#section-4.4.4.2 switch (media_info_.video_info().transfer_characteristics()) { case 1: + case 6: + case 13: + case 14: + // Dolby Vision profile 8.4 may have a transfer_characteristics 14, the + // actual value refers to preferred_transfer_characteristic value in SEI + // message, using compatible brand as a workaround + if (!supplemental_codec_.empty() && + compatible_brand_ == media::FOURCC_db4g) + return "HLG"; + else + return "SDR"; + case 15: return "SDR"; case 16: - case 18: return "PQ"; + case 18: + return "HLG"; default: // Leave it empty if we do not have the transfer characteristics // information. diff --git a/packager/hls/base/media_playlist.h b/packager/hls/base/media_playlist.h index b2a97c1069..75127ccf7c 100644 --- a/packager/hls/base/media_playlist.h +++ b/packager/hls/base/media_playlist.h @@ -17,6 +17,7 @@ #include #include #include +#include "packager/media/base/fourccs.h" namespace shaka { @@ -80,6 +81,8 @@ class MediaPlaylist { const std::string& group_id() const { return group_id_; } MediaPlaylistStreamType stream_type() const { return stream_type_; } const std::string& codec() const { return codec_; } + const std::string& supplemental_codec() const { return supplemental_codec_; } + const media::FourCC& compatible_brand() const { return compatible_brand_; } /// For testing only. void SetStreamTypeForTesting(MediaPlaylistStreamType stream_type); @@ -265,6 +268,8 @@ class MediaPlaylist { // Whether to use byte range for SegmentInfoEntry. bool use_byte_range_ = false; std::string codec_; + std::string supplemental_codec_; + media::FourCC compatible_brand_; std::string language_; std::vector characteristics_; bool forced_subtitle_ = false; diff --git a/packager/hls/base/media_playlist_unittest.cc b/packager/hls/base/media_playlist_unittest.cc index d92190d0ca..7b17b412e2 100644 --- a/packager/hls/base/media_playlist_unittest.cc +++ b/packager/hls/base/media_playlist_unittest.cc @@ -1227,7 +1227,7 @@ INSTANTIATE_TEST_CASE_P(VideoRanges, Values(VideoRangeTestData{"hvc1.2.4.L63.90", 0, ""}, VideoRangeTestData{"hvc1.2.4.L63.90", 1, "SDR"}, VideoRangeTestData{"hvc1.2.4.L63.90", 16, "PQ"}, - VideoRangeTestData{"hvc1.2.4.L63.90", 18, "PQ"}, + VideoRangeTestData{"hvc1.2.4.L63.90", 18, "HLG"}, VideoRangeTestData{"dvh1.05.08", 0, "PQ"})); } // namespace hls diff --git a/packager/media/base/fourccs.h b/packager/media/base/fourccs.h index 4c85c02c10..335ca3723f 100644 --- a/packager/media/base/fourccs.h +++ b/packager/media/base/fourccs.h @@ -47,6 +47,11 @@ enum FourCC : uint32_t { FOURCC_dac3 = 0x64616333, FOURCC_dac4 = 0x64616334, FOURCC_dash = 0x64617368, + FOURCC_dav1 = 0x64617631, + FOURCC_db1p = 0x64623170, + FOURCC_db2g = 0x64623267, + FOURCC_db4g = 0x64623467, + FOURCC_db4h = 0x64623468, FOURCC_dby1 = 0x64627931, FOURCC_ddts = 0x64647473, FOURCC_dec3 = 0x64656333, diff --git a/packager/media/base/media_handler_test_base.cc b/packager/media/base/media_handler_test_base.cc index 8a5a7d1e03..a9c78f9100 100644 --- a/packager/media/base/media_handler_test_base.cc +++ b/packager/media/base/media_handler_test_base.cc @@ -31,7 +31,10 @@ const uint32_t kWidth = 10u; const uint32_t kHeight = 20u; const uint32_t kPixelWidth = 2u; const uint32_t kPixelHeight = 3u; +const uint8_t kColorPrimaries = 0; +const uint8_t kMatrixCoefficients = 0; const uint8_t kTransferCharacteristics = 0; + const int16_t kTrickPlayFactor = 0; const uint8_t kNaluLengthSize = 1u; const bool kEncrypted = true; @@ -207,8 +210,9 @@ std::unique_ptr MediaHandlerTestBase::GetVideoStreamInfo( return std::unique_ptr(new VideoStreamInfo( kTrackId, time_scale, kDuration, codec, H26xStreamFormat::kUnSpecified, kCodecString, kCodecConfig, sizeof(kCodecConfig), width, height, - kPixelWidth, kPixelHeight, kTransferCharacteristics, kTrickPlayFactor, - kNaluLengthSize, kLanguage, !kEncrypted)); + kPixelWidth, kPixelHeight, kColorPrimaries, kMatrixCoefficients, + kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage, + !kEncrypted)); } std::unique_ptr MediaHandlerTestBase::GetAudioStreamInfo( diff --git a/packager/media/base/video_stream_info.cc b/packager/media/base/video_stream_info.cc index 1512ce83c2..b557482bf2 100644 --- a/packager/media/base/video_stream_info.cc +++ b/packager/media/base/video_stream_info.cc @@ -50,6 +50,8 @@ VideoStreamInfo::VideoStreamInfo(int track_id, uint32_t height, uint32_t pixel_width, uint32_t pixel_height, + uint8_t color_primaries, + uint8_t matrix_coefficients, uint8_t transfer_characteristics, uint32_t trick_play_factor, uint8_t nalu_length_size, @@ -71,6 +73,8 @@ VideoStreamInfo::VideoStreamInfo(int track_id, pixel_width_(pixel_width), pixel_height_(pixel_height), transfer_characteristics_(transfer_characteristics), + color_primaries_(color_primaries), + matrix_coefficients_(matrix_coefficients), trick_play_factor_(trick_play_factor), nalu_length_size_(nalu_length_size) {} diff --git a/packager/media/base/video_stream_info.h b/packager/media/base/video_stream_info.h index 5e6f24345d..8f5e705ee4 100644 --- a/packager/media/base/video_stream_info.h +++ b/packager/media/base/video_stream_info.h @@ -39,6 +39,8 @@ class VideoStreamInfo : public StreamInfo { uint32_t height, uint32_t pixel_width, uint32_t pixel_height, + uint8_t color_primaries, + uint8_t matrix_coefficients, uint8_t transfer_characteristics, uint32_t trick_play_factor, uint8_t nalu_length_size, @@ -54,6 +56,8 @@ class VideoStreamInfo : public StreamInfo { std::unique_ptr Clone() const override; /// @} + const std::string supplemental_codec() const { return supplemental_codec_; } + FourCC compatible_brand() const { return compatible_brand_; } const std::vector& extra_config() const { return extra_config_; } H26xStreamFormat h26x_stream_format() const { return h26x_stream_format_; } uint32_t width() const { return width_; } @@ -65,12 +69,22 @@ class VideoStreamInfo : public StreamInfo { /// @return 0 if unknown. uint32_t pixel_height() const { return pixel_height_; } uint8_t transfer_characteristics() const { return transfer_characteristics_; } + uint8_t color_primaries() const { return color_primaries_; } + uint8_t matrix_coefficients() const { return matrix_coefficients_; } uint8_t nalu_length_size() const { return nalu_length_size_; } uint32_t trick_play_factor() const { return trick_play_factor_; } uint32_t playback_rate() const { return playback_rate_; } const std::vector& eme_init_data() const { return eme_init_data_; } const std::vector& colr_data() const { return colr_data_; } + void set_supplemental_codec(const std::string supplemental_codec) { + supplemental_codec_ = supplemental_codec; + } + + void set_compatible_brand(const FourCC compatible_brand) { + compatible_brand_ = compatible_brand; + } + void set_extra_config(const std::vector& extra_config) { extra_config_ = extra_config; } @@ -81,6 +95,12 @@ class VideoStreamInfo : public StreamInfo { void set_transfer_characteristics(uint8_t transfer_characteristics) { transfer_characteristics_ = transfer_characteristics; } + void set_color_primaries(uint8_t color_primaries) { + color_primaries_ = color_primaries; + } + void set_matrix_coefficients(uint8_t matrix_coefficients) { + matrix_coefficients_ = matrix_coefficients; + } void set_trick_play_factor(uint32_t trick_play_factor) { trick_play_factor_ = trick_play_factor; } @@ -98,6 +118,8 @@ class VideoStreamInfo : public StreamInfo { private: // Extra codec configuration in a stream of mp4 boxes. It is only applicable // to mp4 container only. It is needed by some codecs, e.g. Dolby Vision. + std::string supplemental_codec_ = ""; + FourCC compatible_brand_ = FOURCC_NULL; std::vector extra_config_; H26xStreamFormat h26x_stream_format_; uint32_t width_; @@ -108,6 +130,8 @@ class VideoStreamInfo : public StreamInfo { uint32_t pixel_width_; uint32_t pixel_height_; uint8_t transfer_characteristics_ = 0; + uint8_t color_primaries_ = 0; + uint8_t matrix_coefficients_ = 0; uint32_t trick_play_factor_ = 0; // Non-zero for trick-play streams. // Playback rate is the attribute for trick play stream, which signals the diff --git a/packager/media/codecs/decoder_configuration_record.h b/packager/media/codecs/decoder_configuration_record.h index b826484e71..45221cf528 100644 --- a/packager/media/codecs/decoder_configuration_record.h +++ b/packager/media/codecs/decoder_configuration_record.h @@ -48,6 +48,12 @@ class DecoderConfigurationRecord { /// @return Transfer characteristics of the config. uint8_t transfer_characteristics() const { return transfer_characteristics_; } + /// @return Colour Primaries of the config. + uint8_t color_primaries() const { return color_primaries_; } + + /// @return Matrix Coeffs of the config. + uint8_t matrix_coefficients() const { return matrix_coefficients_; } + protected: DecoderConfigurationRecord(); @@ -71,6 +77,15 @@ class DecoderConfigurationRecord { transfer_characteristics_ = transfer_characteristics; } + /// Sets the colour primaries. + void set_color_primaries(uint8_t color_primaries) { + color_primaries_ = color_primaries; + } + /// Sets the matrix coeffs. + void set_matrix_coefficients(uint8_t matrix_coefficients) { + matrix_coefficients_ = matrix_coefficients; + } + private: // Performs the actual parsing of the data. virtual bool ParseInternal() = 0; @@ -86,6 +101,9 @@ class DecoderConfigurationRecord { // The parameter is extracted from SPS. uint8_t transfer_characteristics_ = 0; + uint8_t color_primaries_ = 0; + uint8_t matrix_coefficients_ = 0; + DISALLOW_COPY_AND_ASSIGN(DecoderConfigurationRecord); }; diff --git a/packager/media/codecs/dovi_decoder_configuration_record.cc b/packager/media/codecs/dovi_decoder_configuration_record.cc index 4814c70a91..44d5dbfc8b 100644 --- a/packager/media/codecs/dovi_decoder_configuration_record.cc +++ b/packager/media/codecs/dovi_decoder_configuration_record.cc @@ -23,7 +23,9 @@ bool DOVIDecoderConfigurationRecord::Parse(const std::vector& data) { uint8_t minor_version = 0; RCHECK(reader.ReadBits(8, &major_version) && major_version == 1 && reader.ReadBits(8, &minor_version) && minor_version == 0 && - reader.ReadBits(7, &profile_) && reader.ReadBits(6, &level_)); + reader.ReadBits(7, &profile_) && reader.ReadBits(6, &level_) && + reader.SkipBits(3) && + reader.ReadBits(4, &bl_signal_compatibility_id_)); return true; } @@ -35,5 +37,24 @@ std::string DOVIDecoderConfigurationRecord::GetCodecString( profile_, level_); } +FourCC DOVIDecoderConfigurationRecord::GetDoViCompatibleBrand( + const uint8_t transfer_characteristics) const { + // Dolby Vision Streams within the ISO Base Media File Format Version 2.4: + switch (bl_signal_compatibility_id_) { + case 1: + return FOURCC_db1p; + case 2: + return FOURCC_db2g; + case 4: + if (transfer_characteristics == 14) { + return FOURCC_db4g; + } + // transfer_characteristics == 18 + return FOURCC_db4h; + default: + return FOURCC_NULL; + } +} + } // namespace media } // namespace shaka diff --git a/packager/media/codecs/dovi_decoder_configuration_record.h b/packager/media/codecs/dovi_decoder_configuration_record.h index 64e9445f28..da61e8aded 100644 --- a/packager/media/codecs/dovi_decoder_configuration_record.h +++ b/packager/media/codecs/dovi_decoder_configuration_record.h @@ -35,6 +35,10 @@ class DOVIDecoderConfigurationRecord { /// DASH and HLS manifests. std::string GetCodecString(FourCC codec_fourcc) const; + /// @return The compatiable brand in the format defined by + /// https://mp4ra.org/#/brands. + FourCC GetDoViCompatibleBrand(const uint8_t transfer_characteristics) const; + private: DOVIDecoderConfigurationRecord(const DOVIDecoderConfigurationRecord&) = delete; @@ -42,6 +46,7 @@ class DOVIDecoderConfigurationRecord { const DOVIDecoderConfigurationRecord&) = delete; uint8_t profile_ = 0; + uint8_t bl_signal_compatibility_id_ = 0; uint8_t level_ = 0; }; diff --git a/packager/media/codecs/h264_parser.cc b/packager/media/codecs/h264_parser.cc index 8b86133b1d..c25dcbb0c9 100644 --- a/packager/media/codecs/h264_parser.cc +++ b/packager/media/codecs/h264_parser.cc @@ -519,9 +519,9 @@ H264Parser::Result H264Parser::ParseVUIParameters(H26xBitReader* br, READ_BOOL_OR_RETURN(&data); // video_full_range_flag READ_BOOL_OR_RETURN(&data); // colour_description_present_flag if (data) { - READ_BITS_OR_RETURN(8, &data); // colour primaries + READ_BITS_OR_RETURN(8, &sps->color_primaries); // colour primaries READ_BITS_OR_RETURN(8, &sps->transfer_characteristics); - READ_BITS_OR_RETURN(8, &data); // matrix coeffs + READ_BITS_OR_RETURN(8, &sps->matrix_coefficients); // matrix coeffs } } diff --git a/packager/media/codecs/h264_parser.h b/packager/media/codecs/h264_parser.h index 423fe5a40d..2752e31a38 100644 --- a/packager/media/codecs/h264_parser.h +++ b/packager/media/codecs/h264_parser.h @@ -81,6 +81,8 @@ struct H264Sps { int sar_width; // Set to 0 when not specified. int sar_height; // Set to 0 when not specified. int transfer_characteristics; + int color_primaries; + int matrix_coefficients; bool timing_info_present_flag; long num_units_in_tick; diff --git a/packager/media/codecs/h265_parser.cc b/packager/media/codecs/h265_parser.cc index bd7f2a411c..7c3cab7bf5 100644 --- a/packager/media/codecs/h265_parser.cc +++ b/packager/media/codecs/h265_parser.cc @@ -680,9 +680,11 @@ H265Parser::Result H265Parser::ParseVuiParameters(int max_num_sub_layers_minus1, bool colour_description_present_flag; TRUE_OR_RETURN(br->ReadBool(&colour_description_present_flag)); if (colour_description_present_flag) { - TRUE_OR_RETURN(br->SkipBits(8)); // colour_primaries + TRUE_OR_RETURN( + br->ReadBits(8, &vui->color_primaries)); // color_primaries TRUE_OR_RETURN(br->ReadBits(8, &vui->transfer_characteristics)); - TRUE_OR_RETURN(br->SkipBits(8)); // matrix_coeffs + TRUE_OR_RETURN( + br->ReadBits(8, &vui->matrix_coefficients)); // matrix_coeffs } } diff --git a/packager/media/codecs/h265_parser.h b/packager/media/codecs/h265_parser.h index a36f82e8ab..febb2e0731 100644 --- a/packager/media/codecs/h265_parser.h +++ b/packager/media/codecs/h265_parser.h @@ -52,6 +52,8 @@ struct H265VuiParameters { int sar_width = 0; int sar_height = 0; int transfer_characteristics = 0; + int color_primaries = 0; + int matrix_coefficients = 0; bool vui_timing_info_present_flag = false; long vui_num_units_in_tick = 0; diff --git a/packager/media/codecs/hevc_decoder_configuration_record.cc b/packager/media/codecs/hevc_decoder_configuration_record.cc index 9fef0fe06e..f171fa56fc 100644 --- a/packager/media/codecs/hevc_decoder_configuration_record.cc +++ b/packager/media/codecs/hevc_decoder_configuration_record.cc @@ -120,6 +120,10 @@ bool HEVCDecoderConfigurationRecord::ParseInternal() { RCHECK(parser.ParseSps(nalu, &sps_id) == H265Parser::kOk); set_transfer_characteristics( parser.GetSps(sps_id)->vui_parameters.transfer_characteristics); + set_color_primaries( + parser.GetSps(sps_id)->vui_parameters.color_primaries); + set_matrix_coefficients( + parser.GetSps(sps_id)->vui_parameters.matrix_coefficients); } } } diff --git a/packager/media/crypto/subsample_generator_unittest.cc b/packager/media/crypto/subsample_generator_unittest.cc index c384f45adf..e12dc72116 100644 --- a/packager/media/crypto/subsample_generator_unittest.cc +++ b/packager/media/crypto/subsample_generator_unittest.cc @@ -63,6 +63,8 @@ VideoStreamInfo GetVideoStreamInfo(Codec codec) { const uint16_t kHeight = 20u; const uint32_t kPixelWidth = 2u; const uint32_t kPixelHeight = 3u; + const uint8_t kColorPrimaries = 0; + const uint8_t kMatrixCoefficients = 0; const uint8_t kTransferCharacteristics = 0; const int16_t kTrickPlayFactor = 0; const uint8_t kNaluLengthSize = 1u; @@ -85,8 +87,9 @@ VideoStreamInfo GetVideoStreamInfo(Codec codec) { return VideoStreamInfo( kTrackId, kTimeScale, kDuration, codec, H26xStreamFormat::kUnSpecified, kCodecString, codec_config, codec_config_size, kWidth, kHeight, - kPixelWidth, kPixelHeight, kTransferCharacteristics, kTrickPlayFactor, - kNaluLengthSize, kLanguage, !kEncrypted); + kPixelWidth, kPixelHeight, kColorPrimaries, kMatrixCoefficients, + kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage, + !kEncrypted); } AudioStreamInfo GetAudioStreamInfo(Codec codec) { diff --git a/packager/media/event/mpd_notify_muxer_listener_unittest.cc b/packager/media/event/mpd_notify_muxer_listener_unittest.cc index 841a581849..d2d13aea5d 100644 --- a/packager/media/event/mpd_notify_muxer_listener_unittest.cc +++ b/packager/media/event/mpd_notify_muxer_listener_unittest.cc @@ -301,6 +301,8 @@ TEST_F(MpdNotifyMuxerListenerTest, VodOnSampleDurationReady) { " time_scale: 10\n" " pixel_width: 1\n" " pixel_height: 1\n" + " supplemental_codec: ''\n" + " compatible_brand: 0\n" "}\n" "init_range {\n" " begin: 0\n" @@ -349,6 +351,8 @@ TEST_F(MpdNotifyMuxerListenerTest, VodOnSampleDurationReadySegmentList) { " time_scale: 10\n" " pixel_width: 1\n" " pixel_height: 1\n" + " supplemental_codec: ''\n" + " compatible_brand: 0\n" "}\n" "init_range {\n" " begin: 0\n" @@ -616,6 +620,8 @@ TEST_F(MpdNotifyMuxerListenerTest, LowLatencyDash) { " time_scale: 10\n" " pixel_width: 1\n" " pixel_height: 1\n" + " supplemental_codec: ''\n" + " compatible_brand: 0\n" "}\n" "media_duration_seconds: 20.0\n" "index: 0\n" @@ -682,6 +688,8 @@ TEST_P(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) { " time_scale: 10\n" " pixel_width: 1\n" " pixel_height: 1\n" + " supplemental_codec: ''\n" + " compatible_brand: 0\n" "}\n" "media_duration_seconds: 20.0\n" "index: 0\n" @@ -767,6 +775,8 @@ TEST_P(MpdNotifyMuxerListenerTest, LiveWithKeyRotation) { " time_scale: 10\n" " pixel_width: 1\n" " pixel_height: 1\n" + " supplemental_codec: ''\n" + " compatible_brand: 0\n" "}\n" "media_duration_seconds: 20.0\n" "index: 0\n" diff --git a/packager/media/event/muxer_listener_internal.cc b/packager/media/event/muxer_listener_internal.cc index 03f3dc6af1..0bc150fd0c 100644 --- a/packager/media/event/muxer_listener_internal.cc +++ b/packager/media/event/muxer_listener_internal.cc @@ -73,6 +73,8 @@ void AddVideoInfo(const VideoStreamInfo* video_stream_info, DCHECK(video_stream_info); DCHECK(media_info); MediaInfo_VideoInfo* video_info = media_info->mutable_video_info(); + video_info->set_supplemental_codec(video_stream_info->supplemental_codec()); + video_info->set_compatible_brand(video_stream_info->compatible_brand()); video_info->set_codec(video_stream_info->codec_string()); video_info->set_width(video_stream_info->width()); video_info->set_height(video_stream_info->height()); @@ -96,6 +98,13 @@ void AddVideoInfo(const VideoStreamInfo* video_stream_info, video_info->set_transfer_characteristics( video_stream_info->transfer_characteristics()); } + if (video_stream_info->color_primaries() > 0) { + video_info->set_color_primaries(video_stream_info->color_primaries()); + } + if (video_stream_info->matrix_coefficients() > 0) { + video_info->set_matrix_coefficients( + video_stream_info->matrix_coefficients()); + } } void AddAudioInfo(const AudioStreamInfo* audio_stream_info, diff --git a/packager/media/event/muxer_listener_test_helper.cc b/packager/media/event/muxer_listener_test_helper.cc index 9225f71e88..f802976344 100644 --- a/packager/media/event/muxer_listener_test_helper.cc +++ b/packager/media/event/muxer_listener_test_helper.cc @@ -23,6 +23,8 @@ std::shared_ptr CreateVideoStreamInfo( H26xStreamFormat::kUnSpecified, param.codec_string, param.codec_config.data(), param.codec_config.size(), param.width, param.height, param.pixel_width, param.pixel_height, + 0, // color_primaries + 0, // matrix_coefficients 0, // transfer_characteristics 0, // trick_play_factor param.nalu_length_size, param.language, param.is_encrypted); diff --git a/packager/media/event/muxer_listener_test_helper.h b/packager/media/event/muxer_listener_test_helper.h index 59ca8fbe1f..6eff3ed966 100644 --- a/packager/media/event/muxer_listener_test_helper.h +++ b/packager/media/event/muxer_listener_test_helper.h @@ -26,6 +26,8 @@ const char kExpectedDefaultPsshBox[] = "expected_pssh_box"; const char kExpectedDefaultMediaInfo[] = "video_info {\n" " codec: 'avc1.010101'\n" + " supplemental_codec: ''\n" + " compatible_brand: 0\n" " width: 720\n" " height: 480\n" " time_scale: 10\n" @@ -54,6 +56,8 @@ const char kExpectedDefaultMediaInfoSubsegmentRange[] = " time_scale: 10\n" " pixel_width: 1\n" " pixel_height: 1\n" + " supplemental_codec: ''\n" + " compatible_brand: 0\n" "}\n" "init_range {\n" " begin: 0\n" diff --git a/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc b/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc index aa81cf1990..157c36a43e 100644 --- a/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc +++ b/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc @@ -138,6 +138,8 @@ TEST_F(VodMediaInfoDumpMuxerListenerTest, UnencryptedStream_Normal) { " time_scale: 10\n" " pixel_width: 1\n" " pixel_height: 1\n" + " supplemental_codec: ''\n" + " compatible_brand: 0\n" "}\n" "init_range {\n" " begin: 0\n" @@ -170,6 +172,8 @@ TEST_F(VodMediaInfoDumpMuxerListenerTest, EncryptedStream_Normal) { " time_scale: 10\n" " pixel_width: 1\n" " pixel_height: 1\n" + " supplemental_codec: ''\n" + " compatible_brand: 0\n" "}\n" "init_range {\n" " begin: 0\n" @@ -218,6 +222,8 @@ TEST_F(VodMediaInfoDumpMuxerListenerTest, CheckPixelWidthAndHeightSet) { " time_scale: 10\n" " pixel_width: 8\n" " pixel_height: 9\n" + " supplemental_codec: ''\n" + " compatible_brand: 0\n" "}\n" "init_range {\n" " begin: 0\n" @@ -260,6 +266,8 @@ TEST_F(VodMediaInfoDumpMuxerListenerTest, CheckBandwidth) { " time_scale: 10\n" " pixel_width: 1\n" " pixel_height: 1\n" + " supplemental_codec: ''\n" + " compatible_brand: 0\n" "}\n" "init_range {\n" " begin: 0\n" @@ -297,6 +305,8 @@ TEST_F(VodMediaInfoDumpMuxerListenerTest, UnencryptedStream_Normal_SegmentList) " time_scale: 10\n" " pixel_width: 1\n" " pixel_height: 1\n" + " supplemental_codec: ''\n" + " compatible_brand: 0\n" "}\n" "init_range {\n" " begin: 0\n" diff --git a/packager/media/formats/mp2t/es_parser_h264.cc b/packager/media/formats/mp2t/es_parser_h264.cc index e450c81202..eb4eec2d1e 100644 --- a/packager/media/formats/mp2t/es_parser_h264.cc +++ b/packager/media/formats/mp2t/es_parser_h264.cc @@ -174,7 +174,8 @@ bool EsParserH264::UpdateVideoDecoderConfig(int pps_id) { codec_fourcc, decoder_config_record[1], decoder_config_record[2], decoder_config_record[3]), decoder_config_record.data(), decoder_config_record.size(), coded_width, - coded_height, pixel_width, pixel_height, sps->transfer_characteristics, 0, + coded_height, pixel_width, pixel_height, sps->color_primaries, + sps->matrix_coefficients, sps->transfer_characteristics, 0, nalu_length_size, std::string(), false); DVLOG(1) << "Profile IDC: " << sps->profile_idc; DVLOG(1) << "Level IDC: " << sps->level_idc; diff --git a/packager/media/formats/mp2t/es_parser_h265.cc b/packager/media/formats/mp2t/es_parser_h265.cc index 319ce47bcf..b89dd3f3f4 100644 --- a/packager/media/formats/mp2t/es_parser_h265.cc +++ b/packager/media/formats/mp2t/es_parser_h265.cc @@ -177,8 +177,10 @@ bool EsParserH265::UpdateVideoDecoderConfig(int pps_id) { pid(), kMpeg2Timescale, kInfiniteDuration, kCodecH265, stream_format, decoder_config.GetCodecString(codec_fourcc), decoder_config_record.data(), decoder_config_record.size(), coded_width, coded_height, pixel_width, - pixel_height, sps->vui_parameters.transfer_characteristics, 0, - nalu_length_size, std::string(), false); + pixel_height, sps->vui_parameters.color_primaries, + sps->vui_parameters.matrix_coefficients, + sps->vui_parameters.transfer_characteristics, 0, nalu_length_size, + std::string(), false); // Video config notification. new_stream_info_cb_(last_video_decoder_config_); diff --git a/packager/media/formats/mp2t/pes_packet_generator_unittest.cc b/packager/media/formats/mp2t/pes_packet_generator_unittest.cc index c47e8aa53f..c3cb0021e0 100644 --- a/packager/media/formats/mp2t/pes_packet_generator_unittest.cc +++ b/packager/media/formats/mp2t/pes_packet_generator_unittest.cc @@ -84,6 +84,8 @@ const uint32_t kWidth = 1280; const uint32_t kHeight = 720; const uint32_t kPixelWidth = 1; const uint32_t kPixelHeight = 1; +const uint8_t kColorPrimaries = 0; +const uint8_t kMatrixCoefficients = 0; const uint8_t kTransferCharacteristics = 0; const uint16_t kTrickPlayFactor = 1; const uint8_t kNaluLengthSize = 1; @@ -125,8 +127,8 @@ std::shared_ptr CreateVideoStreamInfo(Codec codec) { kTrackId, kTimeScale, kDuration, codec, H26xStreamFormat::kAnnexbByteStream, kCodecString, kVideoExtraData, std::size(kVideoExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight, - kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage, - kIsEncrypted)); + kColorPrimaries, kMatrixCoefficients, kTransferCharacteristics, + kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted)); return stream_info; } @@ -358,8 +360,8 @@ TEST_F(PesPacketGeneratorTest, TimeStampScaling) { kTrackId, kTestTimescale, kDuration, kH264Codec, H26xStreamFormat::kAnnexbByteStream, kCodecString, kVideoExtraData, std::size(kVideoExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight, - kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage, - kIsEncrypted)); + kColorPrimaries, kMatrixCoefficients, kTransferCharacteristics, + kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted)); EXPECT_TRUE(generator_.Initialize(*stream_info)); EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets()); diff --git a/packager/media/formats/mp2t/ts_segmenter_unittest.cc b/packager/media/formats/mp2t/ts_segmenter_unittest.cc index 7751e1a561..3f34856e22 100644 --- a/packager/media/formats/mp2t/ts_segmenter_unittest.cc +++ b/packager/media/formats/mp2t/ts_segmenter_unittest.cc @@ -45,6 +45,8 @@ const uint32_t kWidth = 1280; const uint32_t kHeight = 720; const uint32_t kPixelWidth = 1; const uint32_t kPixelHeight = 1; +const uint8_t kColorPrimaries = 0; +const uint8_t kMatrixCoefficients = 0; const uint8_t kTransferCharacteristics = 0; const uint16_t kTrickPlayFactor = 1; const uint8_t kNaluLengthSize = 1; @@ -115,8 +117,8 @@ TEST_F(TsSegmenterTest, Initialize) { kTrackId, kTimeScale, kDuration, kH264Codec, H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData, std::size(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight, - kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage, - kIsEncrypted)); + kColorPrimaries, kMatrixCoefficients, kTransferCharacteristics, + kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted)); MuxerOptions options; options.segment_template = "file$Number$.ts"; TsSegmenter segmenter(options, nullptr); @@ -135,8 +137,8 @@ TEST_F(TsSegmenterTest, AddSample) { kTrackId, kTimeScale, kDuration, kH264Codec, H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData, std::size(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight, - kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage, - kIsEncrypted)); + kColorPrimaries, kMatrixCoefficients, kTransferCharacteristics, + kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted)); MuxerOptions options; options.segment_template = "file$Number$.ts"; TsSegmenter segmenter(options, nullptr); @@ -187,8 +189,8 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) { kTrackId, kInputTimescale, kDuration, kH264Codec, H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData, std::size(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight, - kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage, - kIsEncrypted)); + kColorPrimaries, kMatrixCoefficients, kTransferCharacteristics, + kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted)); MuxerOptions options; options.segment_template = "memory://file$Number$.ts"; @@ -269,8 +271,8 @@ TEST_F(TsSegmenterTest, InitializeThenFinalize) { kTrackId, kTimeScale, kDuration, kH264Codec, H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData, std::size(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight, - kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage, - kIsEncrypted)); + kColorPrimaries, kMatrixCoefficients, kTransferCharacteristics, + kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted)); MuxerOptions options; options.segment_template = "file$Number$.ts"; TsSegmenter segmenter(options, nullptr); @@ -297,8 +299,8 @@ TEST_F(TsSegmenterTest, FinalizeSegment) { kTrackId, kTimeScale, kDuration, kH264Codec, H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData, std::size(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight, - kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage, - kIsEncrypted)); + kColorPrimaries, kMatrixCoefficients, kTransferCharacteristics, + kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted)); MuxerOptions options; options.segment_template = "file$Number$.ts"; TsSegmenter segmenter(options, nullptr); @@ -324,8 +326,8 @@ TEST_F(TsSegmenterTest, EncryptedSample) { kTrackId, kTimeScale, kDuration, kH264Codec, H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData, std::size(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight, - kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage, - kIsEncrypted)); + kColorPrimaries, kMatrixCoefficients, kTransferCharacteristics, + kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted)); MuxerOptions options; options.segment_template = "memory://file$Number$.ts"; diff --git a/packager/media/formats/mp4/box_definitions.cc b/packager/media/formats/mp4/box_definitions.cc index 81d175889a..1f3cd260b4 100644 --- a/packager/media/formats/mp4/box_definitions.cc +++ b/packager/media/formats/mp4/box_definitions.cc @@ -1633,6 +1633,16 @@ bool VideoSampleEntry::ReadWriteInternal(BoxBuffer* buffer) { extra_codec_configs.push_back(std::move(dv_box)); } } + const bool is_av1 = actual_format == FOURCC_av01; + if (is_av1) { + for (FourCC fourcc : {FOURCC_dvvC}) { + CodecConfiguration dv_box; + dv_box.box_type = fourcc; + RCHECK(buffer->TryReadWriteChild(&dv_box)); + if (!dv_box.data.empty()) + extra_codec_configs.push_back(std::move(dv_box)); + } + } } else { for (CodecConfiguration& extra_codec_config : extra_codec_configs) RCHECK(buffer->ReadWriteChild(&extra_codec_config)); diff --git a/packager/media/formats/mp4/mp4_media_parser.cc b/packager/media/formats/mp4/mp4_media_parser.cc index b3be126385..7aac01590f 100644 --- a/packager/media/formats/mp4/mp4_media_parser.cc +++ b/packager/media/formats/mp4/mp4_media_parser.cc @@ -37,6 +37,13 @@ #include #include +ABSL_FLAG(bool, + use_dovi_supplemental_codecs, + false, + "Set to true to signal DolbyVision using the modern supplemental " + "codecs approach instead of the legacy " + "duplicate representations approach"); + namespace shaka { namespace media { namespace mp4 { @@ -156,6 +163,7 @@ bool UpdateCodecStringForDolbyVision( switch (actual_format) { case FOURCC_dvh1: case FOURCC_dvhe: + case FOURCC_dav1: // Non-Backward compatibility mode. Replace the code string with // Dolby Vision only. *codec_string = dovi_config.GetCodecString(actual_format); @@ -169,6 +177,9 @@ bool UpdateCodecStringForDolbyVision( // See above. *codec_string += ";" + dovi_config.GetCodecString(FOURCC_dvh1); break; + case FOURCC_av01: + *codec_string += ";" + dovi_config.GetCodecString(FOURCC_dav1); + break; default: LOG(ERROR) << "Unsupported format with extra codec " << FourCCToString(actual_format); @@ -177,6 +188,48 @@ bool UpdateCodecStringForDolbyVision( return true; } +bool UpdateDolbyVisionInfo(FourCC actual_format, + const std::vector& configs, + uint8_t transfer_characteristics, + std::string* codec_string, + std::string* dovi_supplemental_codec_string, + FourCC* dovi_compatible_brand) { + DOVIDecoderConfigurationRecord dovi_config; + if (!dovi_config.Parse(GetDOVIDecoderConfig(configs))) { + LOG(ERROR) << "Failed to parse Dolby Vision decoder " + "configuration record."; + return false; + } + switch (actual_format) { + case FOURCC_dvh1: + case FOURCC_dvhe: + case FOURCC_dav1: + // Non-Backward compatibility mode. Replace the code string with + // Dolby Vision only. + *codec_string = dovi_config.GetCodecString(actual_format); + break; + case FOURCC_hev1: + // Backward compatibility mode. Use supplemental codec indicating Dolby + // Dolby Vision content. + *dovi_supplemental_codec_string = dovi_config.GetCodecString(FOURCC_dvhe); + break; + case FOURCC_hvc1: + // See above. + *dovi_supplemental_codec_string = dovi_config.GetCodecString(FOURCC_dvh1); + break; + case FOURCC_av01: + *dovi_supplemental_codec_string = dovi_config.GetCodecString(FOURCC_dav1); + break; + default: + LOG(ERROR) << "Unsupported format with extra codec " + << FourCCToString(actual_format); + return false; + } + *dovi_compatible_brand = + dovi_config.GetDoViCompatibleBrand(transfer_characteristics); + return true; +} + const uint64_t kNanosecondsPerSecond = 1000000000ull; } // namespace @@ -391,6 +444,9 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { std::vector> streams; + bool use_dovi_supplemental = + absl::GetFlag(FLAGS_use_dovi_supplemental_codecs); + for (std::vector::const_iterator track = moov_->tracks.begin(); track != moov_->tracks.end(); ++track) { const int32_t timescale = track->media.header.timescale; @@ -604,8 +660,12 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { &pixel_height); } std::string codec_string; + std::string dovi_supplemental_codec_string(""); + FourCC dovi_compatible_brand = FOURCC_NULL; uint8_t nalu_length_size = 0; uint8_t transfer_characteristics = 0; + uint8_t color_primaries = 0; + uint8_t matrix_coefficients = 0; const FourCC actual_format = entry.GetActualFormat(); const Codec video_codec = FourCCToCodec(actual_format); @@ -618,13 +678,34 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { } // Generate the full codec string if the colr atom is present. if (entry.colr.color_parameter_type != FOURCC_NULL) { + transfer_characteristics = entry.colr.transfer_characteristics; + color_primaries = entry.colr.color_primaries; + matrix_coefficients = entry.colr.matrix_coefficients; codec_string = av1_config.GetCodecString( - entry.colr.color_primaries, entry.colr.transfer_characteristics, - entry.colr.matrix_coefficients, + color_primaries, transfer_characteristics, matrix_coefficients, entry.colr.video_full_range_flag); } else { codec_string = av1_config.GetCodecString(); } + + if (!entry.extra_codec_configs.empty()) { + // |extra_codec_configs| is present only for Dolby Vision. + if (use_dovi_supplemental) { + if (!UpdateDolbyVisionInfo( + actual_format, entry.extra_codec_configs, + transfer_characteristics, &codec_string, + &dovi_supplemental_codec_string, + &dovi_compatible_brand)) { + return false; + } + } else { + if (!UpdateCodecStringForDolbyVision(actual_format, + entry.extra_codec_configs, + &codec_string)) { + return false; + } + } + } break; } case FOURCC_avc1: @@ -637,6 +718,8 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { codec_string = avc_config.GetCodecString(actual_format); nalu_length_size = avc_config.nalu_length_size(); transfer_characteristics = avc_config.transfer_characteristics(); + color_primaries = avc_config.color_primaries(); + matrix_coefficients = avc_config.matrix_coefficients(); // Use configurations from |avc_config| if it is valid. if (avc_config.coded_width() != 0) { @@ -685,12 +768,25 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { codec_string = hevc_config.GetCodecString(actual_format); nalu_length_size = hevc_config.nalu_length_size(); transfer_characteristics = hevc_config.transfer_characteristics(); + color_primaries = hevc_config.color_primaries(); + matrix_coefficients = hevc_config.matrix_coefficients(); if (!entry.extra_codec_configs.empty()) { // |extra_codec_configs| is present only for Dolby Vision. - if (!UpdateCodecStringForDolbyVision( - actual_format, entry.extra_codec_configs, &codec_string)) { - return false; + if (use_dovi_supplemental) { + if (!UpdateDolbyVisionInfo( + actual_format, entry.extra_codec_configs, + transfer_characteristics, &codec_string, + &dovi_supplemental_codec_string, + &dovi_compatible_brand)) { + return false; + } + } else { + if (!UpdateCodecStringForDolbyVision(actual_format, + entry.extra_codec_configs, + &codec_string)) { + return false; + } } } break; @@ -733,10 +829,16 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { track->header.track_id, timescale, duration, video_codec, GetH26xStreamFormat(actual_format), codec_string, codec_configuration_data.data(), codec_configuration_data.size(), - coded_width, coded_height, pixel_width, pixel_height, - transfer_characteristics, + coded_width, coded_height, pixel_width, pixel_height, color_primaries, + matrix_coefficients, transfer_characteristics, 0, // trick_play_factor nalu_length_size, track->media.header.language.code, is_encrypted)); + + if (use_dovi_supplemental) { + video_stream_info->set_supplemental_codec( + dovi_supplemental_codec_string); + video_stream_info->set_compatible_brand(dovi_compatible_brand); + } video_stream_info->set_extra_config(entry.ExtraCodecConfigsAsVector()); video_stream_info->set_colr_data((entry.colr.raw_box).data(), (entry.colr.raw_box).size()); diff --git a/packager/media/formats/mp4/mp4_media_parser.h b/packager/media/formats/mp4/mp4_media_parser.h index ffa5687a67..4463aae95c 100644 --- a/packager/media/formats/mp4/mp4_media_parser.h +++ b/packager/media/formats/mp4/mp4_media_parser.h @@ -12,11 +12,16 @@ #include #include +#include +#include + #include #include #include #include +ABSL_DECLARE_FLAG(bool, use_dovi_supplemental_codecs); + namespace shaka { namespace media { namespace mp4 { diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index 20907423b9..b681dc448c 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -237,8 +237,22 @@ Status MP4Muxer::DelayInitializeMuxer() { ftyp->compatible_brands.push_back(codec_fourcc); // https://professional.dolby.com/siteassets/content-creation/dolby-vision-for-content-creators/dolby_vision_bitstreams_within_the_iso_base_media_file_format_dec2017.pdf - if (streams()[0].get()->codec_string().find("dvh") != std::string::npos) + std::string codec_string = + static_cast(streams()[0].get()) + ->codec_string(); + std::string supplemental_codec_string = + static_cast(streams()[0].get()) + ->supplemental_codec(); + if (codec_string.find("dvh") != std::string::npos || + supplemental_codec_string.find("dvh") != std::string::npos || + codec_string.find("dav1") != std::string::npos || + supplemental_codec_string.find("dav1") != std::string::npos) ftyp->compatible_brands.push_back(FOURCC_dby1); + FourCC extra_brand = + static_cast(streams()[0].get()) + ->compatible_brand(); + if (extra_brand != FOURCC_NULL) + ftyp->compatible_brands.push_back(extra_brand); } // CMAF allows only one track/stream per file. diff --git a/packager/media/formats/webm/segmenter_test_base.cc b/packager/media/formats/webm/segmenter_test_base.cc index 9e2ef8d511..c965e26915 100644 --- a/packager/media/formats/webm/segmenter_test_base.cc +++ b/packager/media/formats/webm/segmenter_test_base.cc @@ -32,6 +32,8 @@ const uint16_t kWidth = 100; const uint16_t kHeight = 100; const uint16_t kPixelWidth = 100; const uint16_t kPixelHeight = 100; +const uint8_t kColorPrimaries = 0; +const uint8_t kMatrixCoefficients = 0; const uint8_t kTransferCharacteristics = 0; const int16_t kTrickPlayFactor = 1; const uint8_t kNaluLengthSize = 0; @@ -88,8 +90,9 @@ VideoStreamInfo* SegmentTestBase::CreateVideoStreamInfo( return new VideoStreamInfo( kTrackId, time_scale, kDurationInSeconds * time_scale, kCodec, H26xStreamFormat::kUnSpecified, kCodecString, NULL, 0, kWidth, kHeight, - kPixelWidth, kPixelHeight, kTransferCharacteristics, kTrickPlayFactor, - kNaluLengthSize, kLanguage, false); + kPixelWidth, kPixelHeight, kColorPrimaries, kMatrixCoefficients, + kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage, + false); } std::string SegmentTestBase::OutputFileName() const { diff --git a/packager/media/formats/webm/webm_cluster_parser_unittest.cc b/packager/media/formats/webm/webm_cluster_parser_unittest.cc index a287932dfd..9a3f353d9d 100644 --- a/packager/media/formats/webm/webm_cluster_parser_unittest.cc +++ b/packager/media/formats/webm/webm_cluster_parser_unittest.cc @@ -94,6 +94,8 @@ const uint16_t kWidth = 320u; const uint16_t kHeight = 180u; const uint32_t kPixelWidth = 1u; const uint32_t kPixelHeight = 1u; +const uint8_t kColorPrimaries = 0; +const uint8_t kMatrixCoefficients = 0; const uint8_t kTransferCharacteristics = 0; const int16_t kTrickPlayFactor = 0; const uint8_t kNaluLengthSize = 0u; @@ -354,6 +356,8 @@ class WebMClusterParserTest : public testing::Test { kHeight, kPixelWidth, kPixelHeight, + kColorPrimaries, + kMatrixCoefficients, kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, diff --git a/packager/media/formats/webm/webm_video_client.cc b/packager/media/formats/webm/webm_video_client.cc index fb8dc13142..be83cee922 100644 --- a/packager/media/formats/webm/webm_video_client.cc +++ b/packager/media/formats/webm/webm_video_client.cc @@ -125,8 +125,8 @@ std::shared_ptr WebMVideoClient::GetVideoStreamInfo( return std::make_shared( track_num, kWebMTimeScale, 0, video_codec, H26xStreamFormat::kUnSpecified, codec_string, codec_private.data(), codec_private.size(), - width_after_crop, height_after_crop, pixel_width, pixel_height, 0, 0, - 0 /* transfer_characteristics */, std::string(), is_encrypted); + width_after_crop, height_after_crop, pixel_width, pixel_height, 0, 0, 0, + 0, 0, std::string(), is_encrypted); } VPCodecConfigurationRecord WebMVideoClient::GetVpCodecConfig( diff --git a/packager/media/formats/wvm/wvm_media_parser.cc b/packager/media/formats/wvm/wvm_media_parser.cc index c66e921b09..41d98cb129 100644 --- a/packager/media/formats/wvm/wvm_media_parser.cc +++ b/packager/media/formats/wvm/wvm_media_parser.cc @@ -744,9 +744,10 @@ bool WvmMediaParser::ParseIndexEntry() { stream_id_count_, time_scale, track_duration, kCodecH264, byte_to_unit_stream_converter_.stream_format(), std::string(), video_codec_config.data(), video_codec_config.size(), video_width, - video_height, pixel_width, pixel_height, - 0 /* transfer_characteristics */, trick_play_factor, nalu_length_size, - std::string(), decryption_key_source_ ? false : true)); + video_height, pixel_width, pixel_height, 0 /* color_primaries */, + 0 /*matrix_coefficients */, 0 /* transfer_characteristics */, + trick_play_factor, nalu_length_size, std::string(), + decryption_key_source_ ? false : true)); program_demux_stream_map_[absl::StrFormat( "%u:%u", index_program_id_, video_pes_stream_id ? video_pes_stream_id : kDefaultVideoStreamId)] = diff --git a/packager/media/test/data/sparks_dovi_10.mp4 b/packager/media/test/data/sparks_dovi_10.mp4 new file mode 100644 index 0000000000..4585efdaf3 Binary files /dev/null and b/packager/media/test/data/sparks_dovi_10.mp4 differ diff --git a/packager/mpd/base/adaptation_set.cc b/packager/mpd/base/adaptation_set.cc index 3ba5f2a814..e0874cb70d 100644 --- a/packager/mpd/base/adaptation_set.cc +++ b/packager/mpd/base/adaptation_set.cc @@ -382,6 +382,22 @@ std::optional AdaptationSet::GetXml() { } } + // https://dashif.org/docs/DASH-IF-IOP-v4.3.pdf - 4.2.5.1 + if (IsVideo() && matrix_coefficients_ > 0 && + !adaptation_set.AddSupplementalProperty( + "urn:mpeg:mpegB:cicp:MatrixCoefficients", + std::to_string(matrix_coefficients_))) { + return std::nullopt; + } + + // https://dashif.org/docs/DASH-IF-IOP-v4.3.pdf - 4.2.5.1 + if (IsVideo() && color_primaries_ > 0 && + !adaptation_set.AddSupplementalProperty( + "urn:mpeg:mpegB:cicp:ColourPrimaries", + std::to_string(color_primaries_))) { + return std::nullopt; + } + // https://dashif.org/docs/DASH-IF-IOP-v4.3.pdf - 4.2.5.1 if (IsVideo() && transfer_characteristics_ > 0 && !adaptation_set.AddSupplementalProperty( diff --git a/packager/mpd/base/adaptation_set.h b/packager/mpd/base/adaptation_set.h index fb02273eb9..c080d40e45 100644 --- a/packager/mpd/base/adaptation_set.h +++ b/packager/mpd/base/adaptation_set.h @@ -203,6 +203,24 @@ class AdaptationSet { /// @param codec is the new codec to be set. void set_codec(const std::string& codec) { codec_ = codec; }; + /// @return matrix_coefficients. + uint32_t matrix_coefficients() const { return matrix_coefficients_; } + + /// Set AdaptationSet's video matrix coefficients. + /// @param matrix_coefficients is the video matrix coefficients. + void set_matrix_coefficients(const uint32_t& matrix_coefficients) { + matrix_coefficients_ = matrix_coefficients; + }; + + /// @return color_primaries. + uint32_t color_primaries() const { return color_primaries_; } + + /// Set AdaptationSet's video colour primaries. + /// @param color_primaries is the video colour primaries. + void set_color_primaries(const uint32_t& color_primaries) { + color_primaries_ = color_primaries; + }; + /// @return transfer_characteristics. uint32_t transfer_characteristics() const { return transfer_characteristics_; @@ -378,6 +396,11 @@ class AdaptationSet { // stream. std::vector trick_play_references_; + // Matrix Coefficients. + uint32_t matrix_coefficients_ = 0; + // Colour Primaries. + uint32_t color_primaries_ = 0; + // Transfer characteristics. uint32_t transfer_characteristics_ = 0; diff --git a/packager/mpd/base/media_info.proto b/packager/mpd/base/media_info.proto index 67777ea1ce..61afe6a18d 100644 --- a/packager/mpd/base/media_info.proto +++ b/packager/mpd/base/media_info.proto @@ -49,6 +49,12 @@ message MediaInfo { // Transfer characteristics. Useful to determine the VIDEO-RANGE for HLS, // i.e. whether it is SDR or HDR. optional uint32 transfer_characteristics = 10; + optional uint32 color_primaries = 11; + optional uint32 matrix_coefficients = 12; + + // Fro Dolby Vision back compatiable content. + optional string supplemental_codec = 13; + optional uint32 compatible_brand = 14; } message AudioInfo { diff --git a/packager/mpd/base/mpd_builder.cc b/packager/mpd/base/mpd_builder.cc index 2533fc01e3..48fcc01239 100644 --- a/packager/mpd/base/mpd_builder.cc +++ b/packager/mpd/base/mpd_builder.cc @@ -53,12 +53,12 @@ bool AddMpdNameSpaceInfo(XmlNode* mpd) { "urn:marlin:mas:1-0:services:schemas:mpd"; static const char kXmlNamespaceXlink[] = "http://www.w3.org/1999/xlink"; static const char kMsprNamespace[] = "urn:microsoft:playready"; + static const char kScte214Namespace[] = "urn:scte:dash:scte214-extensions"; const std::map uris = { - {"cenc", kCencNamespace}, - {"mas", kMarlinNamespace}, - {"xlink", kXmlNamespaceXlink}, - {"mspr", kMsprNamespace}, + {"cenc", kCencNamespace}, {"mas", kMarlinNamespace}, + {"xlink", kXmlNamespaceXlink}, {"mspr", kMsprNamespace}, + {"scte214", kScte214Namespace}, }; for (const std::string& namespace_name : namespaces) { diff --git a/packager/mpd/base/mpd_utils.cc b/packager/mpd/base/mpd_utils.cc index 856aab40d3..32df6a5f1e 100644 --- a/packager/mpd/base/mpd_utils.cc +++ b/packager/mpd/base/mpd_utils.cc @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -128,6 +129,29 @@ std::string GetCodecs(const MediaInfo& media_info) { return ""; } +std::string GetSupplementalCodecs(const MediaInfo& media_info) { + CHECK(OnlyOneTrue(media_info.has_video_info(), media_info.has_audio_info(), + media_info.has_text_info())); + + if (media_info.has_video_info() && + media_info.video_info().has_supplemental_codec()) { + return media_info.video_info().supplemental_codec(); + } + return ""; +} + +std::string GetSupplementalProfiles(const MediaInfo& media_info) { + CHECK(OnlyOneTrue(media_info.has_video_info(), media_info.has_audio_info(), + media_info.has_text_info())); + + if (media_info.has_video_info() && + media_info.video_info().has_compatible_brand()) { + return FourCCToString( + static_cast(media_info.video_info().compatible_brand())); + } + return ""; +} + std::string GetBaseCodec(const MediaInfo& media_info) { std::string codec; if (media_info.has_video_info()) { diff --git a/packager/mpd/base/mpd_utils.h b/packager/mpd/base/mpd_utils.h index 846988228e..4e944b0d27 100644 --- a/packager/mpd/base/mpd_utils.h +++ b/packager/mpd/base/mpd_utils.h @@ -27,6 +27,7 @@ const char kEncryptedMp4Scheme[] = "urn:mpeg:dash:mp4protection:2011"; const char kPsshElementName[] = "cenc:pssh"; const char kMsproElementName[] = "mspr:pro"; const uint32_t kTransferFunctionPQ = 16; +const uint32_t kTransferFunctionHLG = 18; bool HasVODOnlyFields(const MediaInfo& media_info); @@ -45,6 +46,10 @@ std::string GetLanguage(const MediaInfo& media_info); // comma. std::string GetCodecs(const MediaInfo& media_info); +std::string GetSupplementalCodecs(const MediaInfo& media_info); + +std::string GetSupplementalProfiles(const MediaInfo& media_info); + // Returns a codec string without variants. For example, "mp4a" instead of // "mp4a.40.2". May return a format for text streams. std::string GetBaseCodec(const MediaInfo& media_info); diff --git a/packager/mpd/base/period.cc b/packager/mpd/base/period.cc index 89efceae14..0809bcb3f8 100644 --- a/packager/mpd/base/period.cc +++ b/packager/mpd/base/period.cc @@ -272,6 +272,11 @@ bool Period::SetNewAdaptationSetAttributes( new_adaptation_set->set_transfer_characteristics( media_info.video_info().transfer_characteristics()); } + + new_adaptation_set->set_matrix_coefficients( + media_info.video_info().matrix_coefficients()); + new_adaptation_set->set_color_primaries( + media_info.video_info().color_primaries()); } else if (media_info.has_audio_info()) { if (codec == "mp4a" || codec == "ac-3" || codec == "ec-3" || codec == "ac-4") { diff --git a/packager/mpd/base/representation.cc b/packager/mpd/base/representation.cc index bfd55c38d4..e27ae26aaf 100644 --- a/packager/mpd/base/representation.cc +++ b/packager/mpd/base/representation.cc @@ -151,6 +151,8 @@ bool Representation::Init() { return false; codecs_ = GetCodecs(media_info_); + supplemental_codecs_ = GetSupplementalCodecs(media_info_); + supplemental_profiles_ = GetSupplementalProfiles(media_info_); return true; } @@ -270,6 +272,16 @@ std::optional Representation::GetXml() { return std::nullopt; } + if (!supplemental_codecs_.empty() && !supplemental_profiles_.empty()) { + if (!representation.SetStringAttribute("scte214:supplementalCodecs", + supplemental_codecs_) || + !representation.SetStringAttribute("scte214:supplementalProfiles", + supplemental_profiles_)) { + LOG(ERROR) << "Failed to add supplemental codecs/profiles to " + "Representation XML."; + } + } + const bool has_video_info = media_info_.has_video_info(); const bool has_audio_info = media_info_.has_audio_info(); diff --git a/packager/mpd/base/representation.h b/packager/mpd/base/representation.h index 3335b6b11d..ba3d2ba536 100644 --- a/packager/mpd/base/representation.h +++ b/packager/mpd/base/representation.h @@ -249,6 +249,8 @@ class Representation { const uint32_t id_; std::string mime_type_; std::string codecs_; + std::string supplemental_codecs_; + std::string supplemental_profiles_; BandwidthEstimator bandwidth_estimator_; const MpdOptions& mpd_options_;