feat: support Dolby Vision profile 8.x (HEVC) and 10.x (AV1) in HLS and DASH (#1396)

Support Dolby Vision profile 8.1, 8.2, 8.4, 10.1, 10.4 signaling in HLS
and DASH.

Adds new option `--use_dovi_supplemental_codecs` (off by default) to use
SUPPLEMENTAL-CODECS in HLS and `scte214:supplementalCodecs` and
`scte214:supplementalProfiles` for DASH.

To maintain compatibility with existing players the current behavior of
using two entries in the manifest remains the default. This will be
changed in a future version where `use_dovi_supplemental_codecs` will
become on by default.

Adds Dolby Vision compatible brands, 'db1p', 'db2g', 'db4g', 'db4h',
'dby1' based on https://mp4ra.org/#/brands

---------

Co-authored-by: Xingzhao Yun <xyun@dolby.com>
This commit is contained in:
Cosmin Stejerean 2024-05-10 17:42:34 -07:00 committed by GitHub
parent f6f60e5fff
commit a99cfe036f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
58 changed files with 584 additions and 50 deletions

View File

@ -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',

View File

@ -0,0 +1,7 @@
#EXTM3U
## Generated with https://github.com/shaka-project/shaka-packager version <tag>-<hash>-<test>
#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

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/shaka-project/shaka-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:scte214="urn:scte:dash:scte214-extensions" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT6.022683S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="60000/1001" subsegmentAlignment="true" par="16:9">
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:MatrixCoefficients" value="9"/>
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:ColourPrimaries" value="9"/>
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:TransferCharacteristics" value="16"/>
<Representation id="0" bandwidth="550702" codecs="av01.0.04M.10.0.111.09.16.09.0" mimeType="video/mp4" scte214:supplementalCodecs="dav1.10.01" scte214:supplementalProfiles="db1p" sar="1:1">
<BaseURL>sparks_dovi_10-video.mp4</BaseURL>
<SegmentBase indexRange="871-926" timescale="60000">
<Initialization range="0-870"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>

View File

@ -0,0 +1,13 @@
#EXTM3U
#EXT-X-VERSION:6
## Generated with https://github.com/shaka-project/shaka-packager version <tag>-<hash>-<test>
#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

View File

@ -3,6 +3,8 @@
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT6.022683S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="60000/1001" subsegmentAlignment="true" par="16:9">
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:MatrixCoefficients" value="2"/>
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:ColourPrimaries" value="2"/>
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:TransferCharacteristics" value="16"/>
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">

View File

@ -0,0 +1,7 @@
#EXTM3U
## Generated with https://github.com/shaka-project/shaka-packager version <tag>-<hash>-<test>
#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

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/shaka-project/shaka-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:scte214="urn:scte:dash:scte214-extensions" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT6.022683S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="60000/1001" subsegmentAlignment="true" par="16:9">
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:MatrixCoefficients" value="9"/>
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:ColourPrimaries" value="9"/>
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:TransferCharacteristics" value="16"/>
<Representation id="0" bandwidth="807837" codecs="hvc1.2.4.L90.90" mimeType="video/mp4" scte214:supplementalCodecs="dvh1.08.01" scte214:supplementalProfiles="db2g" sar="1:1">
<BaseURL>sparks_dovi_8-video.mp4</BaseURL>
<SegmentBase indexRange="992-1071" timescale="60000">
<Initialization range="0-991"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>

View File

@ -0,0 +1,19 @@
#EXTM3U
#EXT-X-VERSION:6
## Generated with https://github.com/shaka-project/shaka-packager version <tag>-<hash>-<test>
#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

View File

@ -3,6 +3,8 @@
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT6.022683S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="60000/1001" subsegmentAlignment="true" par="16:9">
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:MatrixCoefficients" value="9"/>
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:ColourPrimaries" value="9"/>
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:TransferCharacteristics" value="16"/>
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
@ -16,6 +18,8 @@
</Representation>
</AdaptationSet>
<AdaptationSet id="1" contentType="video" width="640" height="360" frameRate="60000/1001" subsegmentAlignment="true" par="16:9">
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:MatrixCoefficients" value="9"/>
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:ColourPrimaries" value="9"/>
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:TransferCharacteristics" value="16"/>
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">

View File

@ -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

View File

@ -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

View File

@ -3,6 +3,8 @@
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT2.8028S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:MatrixCoefficients" value="9"/>
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:ColourPrimaries" value="9"/>
<SupplementalProperty schemeIdUri="urn:mpeg:mpegB:cicp:TransferCharacteristics" value="16"/>
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">

View File

@ -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<std::string> 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)) {

View File

@ -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::FourCC>(
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.

View File

@ -17,6 +17,7 @@
#include <packager/macros/classes.h>
#include <packager/mpd/base/bandwidth_estimator.h>
#include <packager/mpd/base/media_info.pb.h>
#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<std::string> characteristics_;
bool forced_subtitle_ = false;

View File

@ -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

View File

@ -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,

View File

@ -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<StreamInfo> MediaHandlerTestBase::GetVideoStreamInfo(
return std::unique_ptr<VideoStreamInfo>(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<StreamInfo> MediaHandlerTestBase::GetAudioStreamInfo(

View File

@ -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) {}

View File

@ -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<StreamInfo> Clone() const override;
/// @}
const std::string supplemental_codec() const { return supplemental_codec_; }
FourCC compatible_brand() const { return compatible_brand_; }
const std::vector<uint8_t>& 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<uint8_t>& eme_init_data() const { return eme_init_data_; }
const std::vector<uint8_t>& 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<uint8_t>& 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<uint8_t> 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

View File

@ -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);
};

View File

@ -23,7 +23,9 @@ bool DOVIDecoderConfigurationRecord::Parse(const std::vector<uint8_t>& 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

View File

@ -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;
};

View File

@ -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
}
}

View File

@ -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;

View File

@ -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
}
}

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -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) {

View File

@ -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"

View File

@ -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,

View File

@ -23,6 +23,8 @@ std::shared_ptr<VideoStreamInfo> 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);

View File

@ -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"

View File

@ -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"

View File

@ -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;

View File

@ -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_);

View File

@ -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<VideoStreamInfo> 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());

View File

@ -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";

View File

@ -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));

View File

@ -37,6 +37,13 @@
#include <packager/media/formats/mp4/box_reader.h>
#include <packager/media/formats/mp4/track_run_iterator.h>
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<CodecConfiguration>& 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<std::shared_ptr<StreamInfo>> streams;
bool use_dovi_supplemental =
absl::GetFlag(FLAGS_use_dovi_supplemental_codecs);
for (std::vector<Track>::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,13 +768,26 @@ 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)) {
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());

View File

@ -12,11 +12,16 @@
#include <memory>
#include <vector>
#include <absl/flags/declare.h>
#include <absl/flags/flag.h>
#include <packager/macros/classes.h>
#include <packager/media/base/decryptor_source.h>
#include <packager/media/base/media_parser.h>
#include <packager/media/base/offset_byte_queue.h>
ABSL_DECLARE_FLAG(bool, use_dovi_supplemental_codecs);
namespace shaka {
namespace media {
namespace mp4 {

View File

@ -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<const VideoStreamInfo*>(streams()[0].get())
->codec_string();
std::string supplemental_codec_string =
static_cast<const VideoStreamInfo*>(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<const VideoStreamInfo*>(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.

View File

@ -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 {

View File

@ -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,

View File

@ -125,8 +125,8 @@ std::shared_ptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
return std::make_shared<VideoStreamInfo>(
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(

View File

@ -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)] =

Binary file not shown.

View File

@ -382,6 +382,22 @@ std::optional<xml::XmlNode> 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(

View File

@ -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<const AdaptationSet*> trick_play_references_;
// Matrix Coefficients.
uint32_t matrix_coefficients_ = 0;
// Colour Primaries.
uint32_t color_primaries_ = 0;
// Transfer characteristics.
uint32_t transfer_characteristics_ = 0;

View File

@ -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 {

View File

@ -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<std::string, std::string> 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) {

View File

@ -15,6 +15,7 @@
#include <libxml/tree.h>
#include <packager/macros/logging.h>
#include <packager/media/base/fourccs.h>
#include <packager/media/base/language_utils.h>
#include <packager/media/base/protection_system_specific_info.h>
#include <packager/mpd/base/adaptation_set.h>
@ -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::FourCC>(media_info.video_info().compatible_brand()));
}
return "";
}
std::string GetBaseCodec(const MediaInfo& media_info) {
std::string codec;
if (media_info.has_video_info()) {

View File

@ -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);

View File

@ -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") {

View File

@ -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<xml::XmlNode> 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();

View File

@ -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_;