[HLS] Support HDR signaling, i.e. VIDEO-RANGE attribute

- Parse and extract transfer_characteristics from H264/H265 VUI
  parameters.
- Set VIDEO-RANGE attribute in HLS according to HLS specification:
  https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-02#section-4.4.4.2
- Also added an end to end test.

Fixes #632.

Change-Id: Iadf557d967b42ade321fb0b152e8e7b64fe9ff3e
This commit is contained in:
KongQun Yang 2019-09-22 23:24:33 -07:00
parent 8029004c6b
commit 3f909fa551
42 changed files with 464 additions and 63 deletions

View File

@ -1203,6 +1203,15 @@ class PackagerFunctionalTest(PackagerAppTest):
self.assertPackageSuccess(streams, flags) self.assertPackageSuccess(streams, flags)
self._CheckTestResults('hevc-with-encryption', verify_decryption=True) self._CheckTestResults('hevc-with-encryption', verify_decryption=True)
def testHdr10WithEncryption(self):
streams = [
self._GetStream('video', test_file='bear-640x360-hevc-hdr10.mp4')
]
flags = self._GetFlags(encryption=True, output_dash=True, output_hls=True)
self.assertPackageSuccess(streams, flags)
self._CheckTestResults('hdr10-with-encryption')
def testDolbyVisionWithEncryption(self): def testDolbyVisionWithEncryption(self):
streams = [ streams = [
self._GetStream('video', test_file='426x240-dvh1.mp4') self._GetStream('video', test_file='426x240-dvh1.mp4')

View File

@ -0,0 +1,5 @@
#EXTM3U
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-STREAM-INF:BANDWIDTH=317223,AVERAGE-BANDWIDTH=317223,CODECS="hvc1.2.4.L63.90",RESOLUTION=640x360,VIDEO-RANGE=PQ
stream_0.m3u8

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/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:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT2.802799940109253S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
<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">
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
</ContentProtection>
<Representation id="0" bandwidth="317223" codecs="hvc1.2.4.L63.90" mimeType="video/mp4" sar="1:1">
<BaseURL>bear-640x360-hevc-hdr10-video.mp4</BaseURL>
<SegmentBase indexRange="5227-5270" timescale="30000">
<Initialization range="0-5226"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>

View File

@ -0,0 +1,10 @@
#EXTM3U
#EXT-X-VERSION:6
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-TARGETDURATION:3
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI="bear-640x360-hevc-hdr10-video.mp4",BYTERANGE="5227@0"
#EXTINF:2.803,
#EXT-X-BYTERANGE:111139@5271
bear-640x360-hevc-hdr10-video.mp4
#EXT-X-ENDLIST

View File

@ -222,6 +222,9 @@ void BuildStreamInfTag(const MediaPlaylist& playlist,
uint32_t height; uint32_t height;
if (playlist.GetDisplayResolution(&width, &height)) { if (playlist.GetDisplayResolution(&width, &height)) {
tag.AddNumberPair("RESOLUTION", width, 'x', height); tag.AddNumberPair("RESOLUTION", width, 'x', height);
const std::string video_range = playlist.GetVideoRange();
if (!video_range.empty())
tag.AddString("VIDEO-RANGE", video_range);
} }
if (variant.audio_group_id) { if (variant.audio_group_id) {

View File

@ -527,6 +527,22 @@ bool MediaPlaylist::GetDisplayResolution(uint32_t* width,
return false; return false;
} }
std::string MediaPlaylist::GetVideoRange() const {
// HLS specification:
// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-02#section-4.4.4.2
switch (media_info_.video_info().transfer_characteristics()) {
case 1:
return "SDR";
case 16:
case 18:
return "PQ";
default:
// Leave it empty if we do not have the transfer characteristics
// information.
return "";
}
}
void MediaPlaylist::AddSegmentInfoEntry(const std::string& segment_file_name, void MediaPlaylist::AddSegmentInfoEntry(const std::string& segment_file_name,
int64_t start_time, int64_t start_time,
int64_t duration, int64_t duration,

View File

@ -186,6 +186,9 @@ class MediaPlaylist {
/// resolution values. /// resolution values.
virtual bool GetDisplayResolution(uint32_t* width, uint32_t* height) const; virtual bool GetDisplayResolution(uint32_t* width, uint32_t* height) const;
/// @return The video range of the stream.
virtual std::string GetVideoRange() const;
/// @return the language of the media, as an ISO language tag in its shortest /// @return the language of the media, as an ISO language tag in its shortest
/// form. May be an empty string for video. /// form. May be an empty string for video.
const std::string& language() const { return language_; } const std::string& language() const { return language_; }

View File

@ -28,6 +28,7 @@ const uint16_t kWidth = 10u;
const uint16_t kHeight = 20u; const uint16_t kHeight = 20u;
const uint32_t kPixelWidth = 2u; const uint32_t kPixelWidth = 2u;
const uint32_t kPixelHeight = 3u; const uint32_t kPixelHeight = 3u;
const uint8_t kTransferCharacteristics = 0;
const int16_t kTrickPlayFactor = 0; const int16_t kTrickPlayFactor = 0;
const uint8_t kNaluLengthSize = 1u; const uint8_t kNaluLengthSize = 1u;
const bool kEncrypted = true; const bool kEncrypted = true;
@ -199,8 +200,8 @@ std::unique_ptr<StreamInfo> MediaHandlerTestBase::GetVideoStreamInfo(
return std::unique_ptr<VideoStreamInfo>(new VideoStreamInfo( return std::unique_ptr<VideoStreamInfo>(new VideoStreamInfo(
kTrackId, time_scale, kDuration, codec, H26xStreamFormat::kUnSpecified, kTrackId, time_scale, kDuration, codec, H26xStreamFormat::kUnSpecified,
kCodecString, kCodecConfig, sizeof(kCodecConfig), width, height, kCodecString, kCodecConfig, sizeof(kCodecConfig), width, height,
kPixelWidth, kPixelHeight, kTrickPlayFactor, kNaluLengthSize, kLanguage, kPixelWidth, kPixelHeight, kTransferCharacteristics, kTrickPlayFactor,
!kEncrypted)); kNaluLengthSize, kLanguage, !kEncrypted));
} }
std::unique_ptr<StreamInfo> MediaHandlerTestBase::GetAudioStreamInfo( std::unique_ptr<StreamInfo> MediaHandlerTestBase::GetAudioStreamInfo(

View File

@ -50,6 +50,7 @@ VideoStreamInfo::VideoStreamInfo(int track_id,
uint16_t height, uint16_t height,
uint32_t pixel_width, uint32_t pixel_width,
uint32_t pixel_height, uint32_t pixel_height,
uint8_t transfer_characteristics,
uint32_t trick_play_factor, uint32_t trick_play_factor,
uint8_t nalu_length_size, uint8_t nalu_length_size,
const std::string& language, const std::string& language,
@ -69,6 +70,7 @@ VideoStreamInfo::VideoStreamInfo(int track_id,
height_(height), height_(height),
pixel_width_(pixel_width), pixel_width_(pixel_width),
pixel_height_(pixel_height), pixel_height_(pixel_height),
transfer_characteristics_(transfer_characteristics),
trick_play_factor_(trick_play_factor), trick_play_factor_(trick_play_factor),
nalu_length_size_(nalu_length_size) {} nalu_length_size_(nalu_length_size) {}

View File

@ -39,6 +39,7 @@ class VideoStreamInfo : public StreamInfo {
uint16_t height, uint16_t height,
uint32_t pixel_width, uint32_t pixel_width,
uint32_t pixel_height, uint32_t pixel_height,
uint8_t transfer_characteristics,
uint32_t trick_play_factor, uint32_t trick_play_factor,
uint8_t nalu_length_size, uint8_t nalu_length_size,
const std::string& language, const std::string& language,
@ -63,6 +64,7 @@ class VideoStreamInfo : public StreamInfo {
/// Returns the pixel height. /// Returns the pixel height.
/// @return 0 if unknown. /// @return 0 if unknown.
uint32_t pixel_height() const { return pixel_height_; } uint32_t pixel_height() const { return pixel_height_; }
uint8_t transfer_characteristics() const { return transfer_characteristics_; }
uint8_t nalu_length_size() const { return nalu_length_size_; } uint8_t nalu_length_size() const { return nalu_length_size_; }
uint32_t trick_play_factor() const { return trick_play_factor_; } uint32_t trick_play_factor() const { return trick_play_factor_; }
uint32_t playback_rate() const { return playback_rate_; } uint32_t playback_rate() const { return playback_rate_; }
@ -75,6 +77,9 @@ class VideoStreamInfo : public StreamInfo {
void set_height(uint32_t height) { height_ = height; } void set_height(uint32_t height) { height_ = height; }
void set_pixel_width(uint32_t pixel_width) { pixel_width_ = pixel_width; } void set_pixel_width(uint32_t pixel_width) { pixel_width_ = pixel_width; }
void set_pixel_height(uint32_t pixel_height) { pixel_height_ = pixel_height; } void set_pixel_height(uint32_t pixel_height) { pixel_height_ = pixel_height; }
void set_transfer_characteristics(uint8_t transfer_characteristics) {
transfer_characteristics_ = transfer_characteristics;
}
void set_trick_play_factor(uint32_t trick_play_factor) { void set_trick_play_factor(uint32_t trick_play_factor) {
trick_play_factor_ = trick_play_factor; trick_play_factor_ = trick_play_factor;
} }
@ -98,6 +103,7 @@ class VideoStreamInfo : public StreamInfo {
// 0 means unknown. // 0 means unknown.
uint32_t pixel_width_; uint32_t pixel_width_;
uint32_t pixel_height_; uint32_t pixel_height_;
uint8_t transfer_characteristics_ = 0;
uint32_t trick_play_factor_ = 0; // Non-zero for trick-play streams. uint32_t trick_play_factor_ = 0; // Non-zero for trick-play streams.
// Playback rate is the attribute for trick play stream, which signals the // Playback rate is the attribute for trick play stream, which signals the

View File

@ -64,6 +64,8 @@ bool AVCDecoderConfigurationRecord::ParseInternal() {
int sps_id = 0; int sps_id = 0;
H264Parser parser; H264Parser parser;
RCHECK(parser.ParseSps(nalu, &sps_id) == H264Parser::kOk); RCHECK(parser.ParseSps(nalu, &sps_id) == H264Parser::kOk);
set_transfer_characteristics(
parser.GetSps(sps_id)->transfer_characteristics);
RCHECK(ExtractResolutionFromSps(*parser.GetSps(sps_id), &coded_width_, RCHECK(ExtractResolutionFromSps(*parser.GetSps(sps_id), &coded_width_,
&coded_height_, &pixel_width_, &coded_height_, &pixel_width_,
&pixel_height_)); &pixel_height_));

View File

@ -12,11 +12,24 @@ namespace shaka {
namespace media { namespace media {
TEST(AVCDecoderConfigurationRecordTest, Success) { TEST(AVCDecoderConfigurationRecordTest, Success) {
// clang-format off
const uint8_t kAvcDecoderConfigurationData[] = { const uint8_t kAvcDecoderConfigurationData[] = {
0x01, 0x64, 0x00, 0x1E, 0xFF, 0xE1, 0x00, 0x1D, 0x67, 0x64, 0x00, 0x1E, 0x01, // version
0xAC, 0xD9, 0x40, 0xB4, 0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91, 0x64, // profile_indication
0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA, 0x60, 0x0F, 0x16, 0x2D, 0x00, // profile_compatibility
0x96, 0x01, 0x00, 0x06, 0x68, 0xEB, 0xE3, 0xCB, 0x22, 0xC0}; 0x1E, // avc_level
0xFF, // Least significant 3 bits is length_size_minus_one
0xE1, // Least significant 5 bits is num_sps
// sps 1
0x00, 0x1D, // size
0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4, 0x2F, 0xF9, 0x7F, 0xF0,
0x00, 0x80, 0x00, 0x91, 0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA,
0x60, 0x0F, 0x16, 0x2D, 0x96,
0x01, // num_pps
0x00, 0x06, // size
0x68, 0xEB, 0xE3, 0xCB, 0x22, 0xC0,
};
// clang-format on
AVCDecoderConfigurationRecord avc_config; AVCDecoderConfigurationRecord avc_config;
ASSERT_TRUE(avc_config.Parse(kAvcDecoderConfigurationData, ASSERT_TRUE(avc_config.Parse(kAvcDecoderConfigurationData,
@ -31,17 +44,71 @@ TEST(AVCDecoderConfigurationRecordTest, Success) {
EXPECT_EQ(360u, avc_config.coded_height()); EXPECT_EQ(360u, avc_config.coded_height());
EXPECT_EQ(8u, avc_config.pixel_width()); EXPECT_EQ(8u, avc_config.pixel_width());
EXPECT_EQ(9u, avc_config.pixel_height()); EXPECT_EQ(9u, avc_config.pixel_height());
EXPECT_EQ(0u, avc_config.transfer_characteristics());
EXPECT_EQ("avc1.64001e", avc_config.GetCodecString(FOURCC_avc1));
EXPECT_EQ("avc3.64001e", avc_config.GetCodecString(FOURCC_avc3));
}
TEST(AVCDecoderConfigurationRecordTest, SuccessWithTransferCharacteristics) {
// clang-format off
const uint8_t kAvcDecoderConfigurationData[] = {
0x01, // version
0x64, // profile_indication
0x00, // profile_compatibility
0x1E, // avc_level
0xFF, // Least significant 3 bits is length_size_minus_one
0xE1, // Least significant 5 bits is num_sps
// sps 1
0x00, 0x22, // size
0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4, 0x2F, 0xF9, 0x7F, 0xF0,
0x00, 0x80, 0x00, 0x96, 0xA1, 0x22, 0x01, 0x28, 0x00, 0x00, 0x03, 0x00,
0x08, 0x00, 0x00, 0x03, 0x01, 0x80, 0x78, 0xB1, 0x6C, 0xB0,
0x01, // num_pps
// pps 1
0x00, 0x06, // size
0x68, 0xEB, 0xE1, 0x32, 0xC8, 0xB0,
};
// clang-format on
AVCDecoderConfigurationRecord avc_config;
ASSERT_TRUE(avc_config.Parse(kAvcDecoderConfigurationData,
arraysize(kAvcDecoderConfigurationData)));
EXPECT_EQ(1u, avc_config.version());
EXPECT_EQ(0x64, avc_config.profile_indication());
EXPECT_EQ(0u, avc_config.profile_compatibility());
EXPECT_EQ(0x1E, avc_config.avc_level());
EXPECT_EQ(4u, avc_config.nalu_length_size());
EXPECT_EQ(720u, avc_config.coded_width());
EXPECT_EQ(360u, avc_config.coded_height());
EXPECT_EQ(8u, avc_config.pixel_width());
EXPECT_EQ(9u, avc_config.pixel_height());
EXPECT_EQ(16u, avc_config.transfer_characteristics());
EXPECT_EQ("avc1.64001e", avc_config.GetCodecString(FOURCC_avc1)); EXPECT_EQ("avc1.64001e", avc_config.GetCodecString(FOURCC_avc1));
EXPECT_EQ("avc3.64001e", avc_config.GetCodecString(FOURCC_avc3)); EXPECT_EQ("avc3.64001e", avc_config.GetCodecString(FOURCC_avc3));
} }
TEST(AVCDecoderConfigurationRecordTest, FailsOnInvalidNaluLengthSize) { TEST(AVCDecoderConfigurationRecordTest, FailsOnInvalidNaluLengthSize) {
// clang-format off
const uint8_t kAvcDecoderConfigurationData[] = { const uint8_t kAvcDecoderConfigurationData[] = {
0x01, 0x64, 0x00, 0x1E, 0xFE, 0xE1, 0x00, 0x1D, 0x67, 0x64, 0x00, 0x1E, 0x01, // version
0xAC, 0xD9, 0x40, 0xB4, 0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91, 0x64, // profile_indication
0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA, 0x60, 0x0F, 0x16, 0x2D, 0x00, // profile_compatibility
0x96, 0x01, 0x00, 0x06, 0x68, 0xEB, 0xE3, 0xCB, 0x22, 0xC0}; 0x1E, // avc_level
0xFE, // Least significant 3 bits is length_size_minus_one
0xE1, // Least significant 5 bits is num_sps
// sps 1
0x00, 0x1D, // size
0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4, 0x2F, 0xF9, 0x7F, 0xF0,
0x00, 0x80, 0x00, 0x91, 0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA,
0x60, 0x0F, 0x16, 0x2D, 0x96,
0x01, // num_pps
0x00, 0x06, // size
0x68, 0xEB, 0xE3, 0xCB, 0x22, 0xC0,
};
// clang-format on
AVCDecoderConfigurationRecord avc_config; AVCDecoderConfigurationRecord avc_config;
ASSERT_FALSE(avc_config.Parse(kAvcDecoderConfigurationData, ASSERT_FALSE(avc_config.Parse(kAvcDecoderConfigurationData,

View File

@ -9,9 +9,8 @@
namespace shaka { namespace shaka {
namespace media { namespace media {
DecoderConfigurationRecord::DecoderConfigurationRecord() DecoderConfigurationRecord::DecoderConfigurationRecord() = default;
: nalu_length_size_(0) {} DecoderConfigurationRecord::~DecoderConfigurationRecord() = default;
DecoderConfigurationRecord::~DecoderConfigurationRecord() {}
bool DecoderConfigurationRecord::Parse(const uint8_t* data, size_t data_size) { bool DecoderConfigurationRecord::Parse(const uint8_t* data, size_t data_size) {
data_.assign(data, data + data_size); data_.assign(data, data + data_size);

View File

@ -43,6 +43,9 @@ class DecoderConfigurationRecord {
/// lifetime of this object, even if copied. /// lifetime of this object, even if copied.
const Nalu& nalu(size_t i) const { return nalu_[i]; } const Nalu& nalu(size_t i) const { return nalu_[i]; }
/// @return Transfer characteristics of the config.
uint8_t transfer_characteristics() const { return transfer_characteristics_; }
protected: protected:
DecoderConfigurationRecord(); DecoderConfigurationRecord();
@ -61,6 +64,11 @@ class DecoderConfigurationRecord {
nalu_length_size_ = nalu_length_size; nalu_length_size_ = nalu_length_size;
} }
/// Sets the transfer characteristics.
void set_transfer_characteristics(uint8_t transfer_characteristics) {
transfer_characteristics_ = transfer_characteristics;
}
private: private:
// Performs the actual parsing of the data. // Performs the actual parsing of the data.
virtual bool ParseInternal() = 0; virtual bool ParseInternal() = 0;
@ -69,7 +77,12 @@ class DecoderConfigurationRecord {
// extracted Nalu can accessed. // extracted Nalu can accessed.
std::vector<uint8_t> data_; std::vector<uint8_t> data_;
std::vector<Nalu> nalu_; std::vector<Nalu> nalu_;
uint8_t nalu_length_size_; uint8_t nalu_length_size_ = 0;
// Indicates the opto-electronic transfer characteristics of the source
// picture, which can be used to determine whether the video is HDR or SDR.
// The parameter is extracted from SPS.
uint8_t transfer_characteristics_ = 0;
DISALLOW_COPY_AND_ASSIGN(DecoderConfigurationRecord); DISALLOW_COPY_AND_ASSIGN(DecoderConfigurationRecord);
}; };

View File

@ -527,8 +527,11 @@ H264Parser::Result H264Parser::ParseVUIParameters(H26xBitReader* br,
READ_BITS_OR_RETURN(3, &data); // video_format READ_BITS_OR_RETURN(3, &data); // video_format
READ_BOOL_OR_RETURN(&data); // video_full_range_flag READ_BOOL_OR_RETURN(&data); // video_full_range_flag
READ_BOOL_OR_RETURN(&data); // colour_description_present_flag READ_BOOL_OR_RETURN(&data); // colour_description_present_flag
if (data) if (data) {
READ_BITS_OR_RETURN(24, &data); // color description syntax elements READ_BITS_OR_RETURN(8, &data); // colour primaries
READ_BITS_OR_RETURN(8, &sps->transfer_characteristics);
READ_BITS_OR_RETURN(8, &data); // matrix coeffs
}
} }
READ_BOOL_OR_RETURN(&data); // chroma_loc_info_present_flag READ_BOOL_OR_RETURN(&data); // chroma_loc_info_present_flag

View File

@ -82,6 +82,8 @@ struct H264Sps {
bool vui_parameters_present_flag; bool vui_parameters_present_flag;
int sar_width; // Set to 0 when not specified. int sar_width; // Set to 0 when not specified.
int sar_height; // Set to 0 when not specified. int sar_height; // Set to 0 when not specified.
int transfer_characteristics;
bool bitstream_restriction_flag; bool bitstream_restriction_flag;
int max_num_reorder_frames; int max_num_reorder_frames;
int max_dec_frame_buffering; int max_dec_frame_buffering;

View File

@ -187,6 +187,47 @@ TEST(H264ParserTest, PredWeightTable) {
EXPECT_EQ(0, pred_weight_table.chroma_offset[3][1]); EXPECT_EQ(0, pred_weight_table.chroma_offset[3][1]);
} }
TEST(H264ParserTest, ParseSps) {
const uint8_t kSps[] = {0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4,
0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91,
0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA,
0x60, 0x0F, 0x16, 0x2D, 0x96};
H264Parser parser;
int sps_id = 0;
Nalu nalu;
ASSERT_TRUE(nalu.Initialize(Nalu::kH264, kSps, arraysize(kSps)));
ASSERT_EQ(H264Parser::kOk, parser.ParseSps(nalu, &sps_id));
const H264Sps* sps = parser.GetSps(sps_id);
ASSERT_TRUE(sps);
EXPECT_EQ(100, sps->profile_idc);
EXPECT_EQ(30, sps->level_idc);
EXPECT_EQ(0, sps->transfer_characteristics);
}
TEST(H264ParserTest, ParseSpsWithTransferCharacteristics) {
const uint8_t kSps[] = {
0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4, 0x2F, 0xF9, 0x7F, 0xF0,
0x00, 0x80, 0x00, 0x96, 0xA1, 0x22, 0x01, 0x28, 0x00, 0x00, 0x03, 0x00,
0x08, 0x00, 0x00, 0x03, 0x01, 0x80, 0x78, 0xB1, 0x6C, 0xB0,
};
H264Parser parser;
int sps_id = 0;
Nalu nalu;
ASSERT_TRUE(nalu.Initialize(Nalu::kH264, kSps, arraysize(kSps)));
ASSERT_EQ(H264Parser::kOk, parser.ParseSps(nalu, &sps_id));
const H264Sps* sps = parser.GetSps(sps_id);
ASSERT_TRUE(sps);
EXPECT_EQ(100, sps->profile_idc);
EXPECT_EQ(30, sps->level_idc);
EXPECT_EQ(16, sps->transfer_characteristics);
}
TEST(H264ParserTest, ExtractResolutionFromSpsData) { TEST(H264ParserTest, ExtractResolutionFromSpsData) {
const uint8_t kSps[] = {0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4, const uint8_t kSps[] = {0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4,
0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91, 0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91,

View File

@ -661,8 +661,9 @@ H265Parser::Result H265Parser::ParseVuiParameters(int max_num_sub_layers_minus1,
bool colour_description_present_flag; bool colour_description_present_flag;
TRUE_OR_RETURN(br->ReadBool(&colour_description_present_flag)); TRUE_OR_RETURN(br->ReadBool(&colour_description_present_flag));
if (colour_description_present_flag) { if (colour_description_present_flag) {
// colour_primaries, transfer_characteristics, matrix_coeffs TRUE_OR_RETURN(br->SkipBits(8)); // colour_primaries
TRUE_OR_RETURN(br->SkipBits(8 + 8 + 8)); TRUE_OR_RETURN(br->ReadBits(8, &vui->transfer_characteristics));
TRUE_OR_RETURN(br->SkipBits(8)); // matrix_coeffs
} }
} }

View File

@ -50,6 +50,7 @@ struct H265VuiParameters {
int aspect_ratio_idc = 0; int aspect_ratio_idc = 0;
int sar_width = 0; int sar_width = 0;
int sar_height = 0; int sar_height = 0;
int transfer_characteristics = 0;
bool bitstream_restriction_flag = false; bool bitstream_restriction_flag = false;
int min_spatial_segmentation_idc = 0; int min_spatial_segmentation_idc = 0;

View File

@ -15,11 +15,17 @@ namespace H265 {
namespace { namespace {
// Data taken from bear-640x360-hevc.mp4 // Data taken from bear-640x360-hevc.mp4 and bear-640x360-hevc-hdr10.mp4.
const uint8_t kSpsData[] = { const uint8_t kSpsData[] = {
0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x3f, 0xa0, 0x05, 0x02, 0x01, 0x69, 0x65, 0x95, 0xe4, 0x93, 0x00, 0x00, 0x3f, 0xa0, 0x05, 0x02, 0x01, 0x69, 0x65, 0x95, 0xe4, 0x93,
0x2b, 0xc0, 0x40, 0x40, 0x00, 0x00, 0xfa, 0x40, 0x00, 0x1d, 0x4c, 0x02}; 0x2b, 0xc0, 0x40, 0x40, 0x00, 0x00, 0xfa, 0x40, 0x00, 0x1d, 0x4c, 0x02};
const uint8_t kSpsDataWithTransferCharacteristics[] = {
0x42, 0x01, 0x01, 0x02, 0x20, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0xA0, 0x03, 0xC0, 0x80, 0x10, 0xE4,
0xD9, 0x65, 0x66, 0x92, 0x4C, 0xAF, 0x01, 0x6A, 0x12, 0x20, 0x13, 0x6C,
0x20, 0x00, 0x00, 0x7D, 0x20, 0x00, 0x0B, 0xB8, 0x0C, 0x25, 0x9A, 0x4B,
0xC0, 0x01, 0xE8, 0x48, 0x00, 0x3D, 0x09, 0x10};
const uint8_t kPpsData[] = {0x44, 0x01, 0xc1, 0x73, 0xd1, 0x89}; const uint8_t kPpsData[] = {0x44, 0x01, 0xc1, 0x73, 0xd1, 0x89};
const uint8_t kSliceData[] = { const uint8_t kSliceData[] = {
// Incomplete segment data. // Incomplete segment data.
@ -105,6 +111,32 @@ TEST(H265ParserTest, ParseSps) {
EXPECT_EQ(4, sps->log2_max_pic_order_cnt_lsb_minus4); EXPECT_EQ(4, sps->log2_max_pic_order_cnt_lsb_minus4);
EXPECT_EQ(3, sps->log2_diff_max_min_luma_transform_block_size); EXPECT_EQ(3, sps->log2_diff_max_min_luma_transform_block_size);
EXPECT_EQ(0, sps->max_transform_hierarchy_depth_intra); EXPECT_EQ(0, sps->max_transform_hierarchy_depth_intra);
EXPECT_EQ(0, sps->vui_parameters.transfer_characteristics);
}
TEST(H265ParserTest, ParseSpsWithTransferCharacteristics) {
Nalu nalu;
ASSERT_TRUE(nalu.Initialize(Nalu::kH265, kSpsDataWithTransferCharacteristics,
arraysize(kSpsDataWithTransferCharacteristics)));
ASSERT_EQ(Nalu::H265_SPS, nalu.type());
int id = 12;
H265Parser parser;
ASSERT_EQ(H265Parser::kOk, parser.ParseSps(nalu, &id));
ASSERT_EQ(0, id);
const H265Sps* sps = parser.GetSps(id);
ASSERT_TRUE(sps);
EXPECT_EQ(0, sps->video_parameter_set_id);
EXPECT_EQ(0, sps->max_sub_layers_minus1);
EXPECT_EQ(0, sps->seq_parameter_set_id);
EXPECT_EQ(1, sps->chroma_format_idc);
EXPECT_EQ(1080, sps->pic_height_in_luma_samples);
EXPECT_EQ(4, sps->log2_max_pic_order_cnt_lsb_minus4);
EXPECT_EQ(3, sps->log2_diff_max_min_luma_transform_block_size);
EXPECT_EQ(0, sps->max_transform_hierarchy_depth_intra);
EXPECT_EQ(16, sps->vui_parameters.transfer_characteristics);
} }
TEST(H265ParserTest, ParsePps) { TEST(H265ParserTest, ParsePps) {

View File

@ -10,6 +10,7 @@
#include "packager/base/strings/string_util.h" #include "packager/base/strings/string_util.h"
#include "packager/media/base/buffer_reader.h" #include "packager/media/base/buffer_reader.h"
#include "packager/media/base/rcheck.h" #include "packager/media/base/rcheck.h"
#include "packager/media/codecs/h265_parser.h"
namespace shaka { namespace shaka {
namespace media { namespace media {
@ -61,15 +62,9 @@ std::string ReverseBitsAndHexEncode(uint32_t x) {
} // namespace } // namespace
HEVCDecoderConfigurationRecord::HEVCDecoderConfigurationRecord() HEVCDecoderConfigurationRecord::HEVCDecoderConfigurationRecord() = default;
: version_(0),
general_profile_space_(0),
general_tier_flag_(false),
general_profile_idc_(0),
general_profile_compatibility_flags_(0),
general_level_idc_(0) {}
HEVCDecoderConfigurationRecord::~HEVCDecoderConfigurationRecord() {} HEVCDecoderConfigurationRecord::~HEVCDecoderConfigurationRecord() = default;
bool HEVCDecoderConfigurationRecord::ParseInternal() { bool HEVCDecoderConfigurationRecord::ParseInternal() {
BufferReader reader(data(), data_size()); BufferReader reader(data(), data_size());
@ -113,6 +108,14 @@ bool HEVCDecoderConfigurationRecord::ParseInternal() {
RCHECK(nalu.Initialize(Nalu::kH265, data() + nalu_offset, nalu_length)); RCHECK(nalu.Initialize(Nalu::kH265, data() + nalu_offset, nalu_length));
RCHECK(nalu.type() == nal_unit_type); RCHECK(nalu.type() == nal_unit_type);
AddNalu(nalu); AddNalu(nalu);
if (nalu.type() == Nalu::H265_SPS) {
H265Parser parser;
int sps_id = 0;
RCHECK(parser.ParseSps(nalu, &sps_id) == H265Parser::kOk);
set_transfer_characteristics(
parser.GetSps(sps_id)->vui_parameters.transfer_characteristics);
}
} }
} }

View File

@ -31,13 +31,13 @@ class HEVCDecoderConfigurationRecord : public DecoderConfigurationRecord {
private: private:
bool ParseInternal() override; bool ParseInternal() override;
uint8_t version_; uint8_t version_ = 0;
uint8_t general_profile_space_; uint8_t general_profile_space_ = 0;
bool general_tier_flag_; bool general_tier_flag_ = false;
uint8_t general_profile_idc_; uint8_t general_profile_idc_ = 0;
uint32_t general_profile_compatibility_flags_; uint32_t general_profile_compatibility_flags_ = 0;
std::vector<uint8_t> general_constraint_indicator_flags_; std::vector<uint8_t> general_constraint_indicator_flags_;
uint8_t general_level_idc_; uint8_t general_level_idc_ = 0;
DISALLOW_COPY_AND_ASSIGN(HEVCDecoderConfigurationRecord); DISALLOW_COPY_AND_ASSIGN(HEVCDecoderConfigurationRecord);
}; };

View File

@ -12,6 +12,7 @@ namespace shaka {
namespace media { namespace media {
TEST(HEVCDecoderConfigurationRecordTest, Success) { TEST(HEVCDecoderConfigurationRecordTest, Success) {
// clang-format off
const uint8_t kHevcDecoderConfigurationData[] = { const uint8_t kHevcDecoderConfigurationData[] = {
0x01, // Version 0x01, // Version
0x02, // profile_indication 0x02, // profile_indication
@ -33,11 +34,61 @@ TEST(HEVCDecoderConfigurationRecordTest, Success) {
// array 2 // array 2
0x21, // nal type 0x21, // nal type
0x00, 0x01, // num nalus 0x00, 0x01, // num nalus
// Nalu 1 // nalu 1
0x00, 0x0f, // nal unit length 0x00, 0x24, // nal unit length
0x42, 0x01, 0x01, 0x02, 0x20, 0x00, 0x00, 0x03, 0x00, 0x90, 0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xa0, 0x05, 0x02, 0x01, 0x69, 0x65, 0x95,
0xe4, 0x93, 0x2b, 0xc0, 0x40, 0x40, 0x00, 0x00, 0xfa, 0x40, 0x00,
0x1d, 0x4c, 0x02,
}; };
// clang-format on
HEVCDecoderConfigurationRecord hevc_config;
ASSERT_TRUE(hevc_config.Parse(kHevcDecoderConfigurationData,
arraysize(kHevcDecoderConfigurationData)));
EXPECT_EQ(4u, hevc_config.nalu_length_size());
EXPECT_EQ("hev1.2.4.L63.90", hevc_config.GetCodecString(FOURCC_hev1));
EXPECT_EQ("hvc1.2.4.L63.90", hevc_config.GetCodecString(FOURCC_hvc1));
EXPECT_EQ(2u, hevc_config.nalu_count());
EXPECT_EQ(0x16u, hevc_config.nalu(0).payload_size());
EXPECT_EQ(0x40, hevc_config.nalu(0).data()[0]);
}
TEST(HEVCDecoderConfigurationRecordTest, SuccessWithTransferCharacteristics) {
// clang-format off
const uint8_t kHevcDecoderConfigurationData[] = {
0x01, // Version
0x02, // profile_indication
0x20, 0x00, 0x00, 0x00, // general_profile_compatibility_flags
// general_constraint_indicator_flags
0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3F, // general_level_idc
0xF0, 0x00, 0xFC, 0xFD, 0xFA, 0xFA, 0x00, 0x00,
0x0F, // length_size_minus_one
0x03, // num_of_arrays
// array 1
0xA0, // nal type
0x00, 0x01, // num nalus
0x00, 0x18, // nal unit length
0x40, 0x01, 0x0C, 0x01, 0xFF, 0xFF, 0x02, 0x20, 0x00, 0x00, 0x03,
0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0x95,
0x98, 0x09,
// array 2
0xA1, // nal type
0x00, 0x01, // num nalus
0x00, 0x38, // nal unit length
0x42, 0x01, 0x01, 0x02, 0x20, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0xA0, 0x03, 0xC0, 0x80,
0x10, 0xE4, 0xD9, 0x65, 0x66, 0x92, 0x4C, 0xAF, 0x01, 0x6A, 0x12,
0x20, 0x13, 0x6C, 0x20, 0x00, 0x00, 0x7D, 0x20, 0x00, 0x0B, 0xB8,
0x0C, 0x25, 0x9A, 0x4B, 0xC0, 0x01, 0xE8, 0x48, 0x00, 0x3D, 0x09,
0x10,
// array 3
0xA2, // nal type
0x00, 0x01, // num nalus
0x00, 0x07, // nal unit length
0x44, 0x01, 0xC1, 0x72, 0xA6, 0x46, 0x24,
};
// clang-format on
HEVCDecoderConfigurationRecord hevc_config; HEVCDecoderConfigurationRecord hevc_config;
ASSERT_TRUE(hevc_config.Parse(kHevcDecoderConfigurationData, ASSERT_TRUE(hevc_config.Parse(kHevcDecoderConfigurationData,
@ -48,9 +99,11 @@ TEST(HEVCDecoderConfigurationRecordTest, Success) {
EXPECT_EQ("hev1.2.4.L63.90", hevc_config.GetCodecString(FOURCC_hev1)); EXPECT_EQ("hev1.2.4.L63.90", hevc_config.GetCodecString(FOURCC_hev1));
EXPECT_EQ("hvc1.2.4.L63.90", hevc_config.GetCodecString(FOURCC_hvc1)); EXPECT_EQ("hvc1.2.4.L63.90", hevc_config.GetCodecString(FOURCC_hvc1));
EXPECT_EQ(2u, hevc_config.nalu_count()); EXPECT_EQ(3u, hevc_config.nalu_count());
EXPECT_EQ(0x16u, hevc_config.nalu(0).payload_size()); EXPECT_EQ(0x16u, hevc_config.nalu(0).payload_size());
EXPECT_EQ(0x40, hevc_config.nalu(0).data()[0]); EXPECT_EQ(0x40, hevc_config.nalu(0).data()[0]);
EXPECT_EQ(16, hevc_config.transfer_characteristics());
} }
TEST(HEVCDecoderConfigurationRecordTest, FailOnInsufficientData) { TEST(HEVCDecoderConfigurationRecordTest, FailOnInsufficientData) {

View File

@ -63,6 +63,7 @@ VideoStreamInfo GetVideoStreamInfo(Codec codec) {
const uint16_t kHeight = 20u; const uint16_t kHeight = 20u;
const uint32_t kPixelWidth = 2u; const uint32_t kPixelWidth = 2u;
const uint32_t kPixelHeight = 3u; const uint32_t kPixelHeight = 3u;
const uint8_t kTransferCharacteristics = 0;
const int16_t kTrickPlayFactor = 0; const int16_t kTrickPlayFactor = 0;
const uint8_t kNaluLengthSize = 1u; const uint8_t kNaluLengthSize = 1u;
@ -81,10 +82,10 @@ VideoStreamInfo GetVideoStreamInfo(Codec codec) {
// We do not care about the codec configs for other codecs in this file. // We do not care about the codec configs for other codecs in this file.
break; break;
} }
return VideoStreamInfo(kTrackId, kTimeScale, kDuration, codec, return VideoStreamInfo(
H26xStreamFormat::kUnSpecified, kCodecString, kTrackId, kTimeScale, kDuration, codec, H26xStreamFormat::kUnSpecified,
codec_config, codec_config_size, kWidth, kHeight, kCodecString, codec_config, codec_config_size, kWidth, kHeight,
kPixelWidth, kPixelHeight, kTrickPlayFactor, kPixelWidth, kPixelHeight, kTransferCharacteristics, kTrickPlayFactor,
kNaluLengthSize, kLanguage, !kEncrypted); kNaluLengthSize, kLanguage, !kEncrypted);
} }

View File

@ -54,6 +54,7 @@
'type': '<(gtest_target_type)', 'type': '<(gtest_target_type)',
'sources': [ 'sources': [
'hls_notify_muxer_listener_unittest.cc', 'hls_notify_muxer_listener_unittest.cc',
'muxer_listener_internal_unittest.cc',
'mpd_notify_muxer_listener_unittest.cc', 'mpd_notify_muxer_listener_unittest.cc',
'muxer_listener_test_helper.cc', 'muxer_listener_test_helper.cc',
'muxer_listener_test_helper.h', 'muxer_listener_test_helper.h',

View File

@ -87,6 +87,10 @@ void AddVideoInfo(const VideoStreamInfo* video_stream_info,
if (video_stream_info->playback_rate() > 0) { if (video_stream_info->playback_rate() > 0) {
video_info->set_playback_rate(video_stream_info->playback_rate()); video_info->set_playback_rate(video_stream_info->playback_rate());
} }
if (video_stream_info->transfer_characteristics() > 0) {
video_info->set_transfer_characteristics(
video_stream_info->transfer_characteristics());
}
} }
void AddAudioInfo(const AudioStreamInfo* audio_stream_info, void AddAudioInfo(const AudioStreamInfo* audio_stream_info,

View File

@ -0,0 +1,77 @@
// Copyright 2019 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/media/event/muxer_listener_internal.h"
#include <gtest/gtest.h>
#include "packager/media/event/muxer_listener_test_helper.h"
#include "packager/mpd/base/media_info.pb.h"
namespace shaka {
namespace media {
namespace internal {
namespace {
const uint32_t kReferenceTimeScale = 1000;
} // namespace
class MuxerListenerInternalTest : public ::testing::Test {};
class MuxerListenerInternalVideoStreamTest : public MuxerListenerInternalTest {
protected:
std::shared_ptr<VideoStreamInfo> video_stream_info_ =
CreateVideoStreamInfo(GetDefaultVideoStreamInfoParams());
};
TEST_F(MuxerListenerInternalVideoStreamTest, Basic) {
MediaInfo media_info;
ASSERT_TRUE(GenerateMediaInfo(MuxerOptions(), *video_stream_info_,
kReferenceTimeScale,
MuxerListener::kContainerMp4, &media_info));
ASSERT_TRUE(media_info.has_video_info());
const MediaInfo_VideoInfo& video_info = media_info.video_info();
EXPECT_EQ("avc1.010101", video_info.codec());
EXPECT_EQ(720u, video_info.width());
EXPECT_EQ(480u, video_info.height());
EXPECT_EQ(10u, video_info.time_scale());
EXPECT_EQ(1u, video_info.pixel_width());
EXPECT_EQ(1u, video_info.pixel_height());
EXPECT_EQ(0u, video_info.playback_rate());
EXPECT_EQ(0u, video_info.transfer_characteristics());
}
TEST_F(MuxerListenerInternalVideoStreamTest, PixelWidthHeight) {
MediaInfo media_info;
video_stream_info_->set_pixel_width(100);
video_stream_info_->set_pixel_height(200);
ASSERT_TRUE(GenerateMediaInfo(MuxerOptions(), *video_stream_info_,
kReferenceTimeScale,
MuxerListener::kContainerMp4, &media_info));
EXPECT_EQ(100u, media_info.video_info().pixel_width());
EXPECT_EQ(200u, media_info.video_info().pixel_height());
}
TEST_F(MuxerListenerInternalVideoStreamTest, PlaybackRate) {
MediaInfo media_info;
video_stream_info_->set_playback_rate(5);
ASSERT_TRUE(GenerateMediaInfo(MuxerOptions(), *video_stream_info_,
kReferenceTimeScale,
MuxerListener::kContainerMp4, &media_info));
EXPECT_EQ(5u, media_info.video_info().playback_rate());
}
TEST_F(MuxerListenerInternalVideoStreamTest, TransferCharacteristics) {
MediaInfo media_info;
video_stream_info_->set_transfer_characteristics(18);
ASSERT_TRUE(GenerateMediaInfo(MuxerOptions(), *video_stream_info_,
kReferenceTimeScale,
MuxerListener::kContainerMp4, &media_info));
EXPECT_EQ(18u, media_info.video_info().transfer_characteristics());
}
} // namespace internal
} // namespace media
} // namespace shaka

View File

@ -15,13 +15,14 @@ namespace media {
VideoStreamInfoParameters::VideoStreamInfoParameters() {} VideoStreamInfoParameters::VideoStreamInfoParameters() {}
VideoStreamInfoParameters::~VideoStreamInfoParameters() {} VideoStreamInfoParameters::~VideoStreamInfoParameters() {}
std::shared_ptr<StreamInfo> CreateVideoStreamInfo( std::shared_ptr<VideoStreamInfo> CreateVideoStreamInfo(
const VideoStreamInfoParameters& param) { const VideoStreamInfoParameters& param) {
return std::make_shared<VideoStreamInfo>( return std::make_shared<VideoStreamInfo>(
param.track_id, param.time_scale, param.duration, param.codec, param.track_id, param.time_scale, param.duration, param.codec,
H26xStreamFormat::kUnSpecified, param.codec_string, H26xStreamFormat::kUnSpecified, param.codec_string,
param.codec_config.data(), param.codec_config.size(), param.width, param.codec_config.data(), param.codec_config.size(), param.width,
param.height, param.pixel_width, param.pixel_height, param.height, param.pixel_width, param.pixel_height,
0, // transfer_characteristics
0, // trick_play_factor 0, // trick_play_factor
param.nalu_length_size, param.language, param.is_encrypted); param.nalu_length_size, param.language, param.is_encrypted);
} }

View File

@ -80,7 +80,7 @@ struct OnMediaEndParameters {
}; };
// Creates StreamInfo instance from VideoStreamInfoParameters. // Creates StreamInfo instance from VideoStreamInfoParameters.
std::shared_ptr<StreamInfo> CreateVideoStreamInfo( std::shared_ptr<VideoStreamInfo> CreateVideoStreamInfo(
const VideoStreamInfoParameters& param); const VideoStreamInfoParameters& param);
// Returns the "default" VideoStreamInfoParameters for testing. // Returns the "default" VideoStreamInfoParameters for testing.

View File

@ -159,8 +159,8 @@ bool EsParserH264::UpdateVideoDecoderConfig(int pps_id) {
codec_fourcc, decoder_config_record[1], decoder_config_record[2], codec_fourcc, decoder_config_record[1], decoder_config_record[2],
decoder_config_record[3]), decoder_config_record[3]),
decoder_config_record.data(), decoder_config_record.size(), coded_width, decoder_config_record.data(), decoder_config_record.size(), coded_width,
coded_height, pixel_width, pixel_height, 0, nalu_length_size, coded_height, pixel_width, pixel_height, sps->transfer_characteristics, 0,
std::string(), false); nalu_length_size, std::string(), false);
DVLOG(1) << "Profile IDC: " << sps->profile_idc; DVLOG(1) << "Profile IDC: " << sps->profile_idc;
DVLOG(1) << "Level IDC: " << sps->level_idc; DVLOG(1) << "Level IDC: " << sps->level_idc;
DVLOG(1) << "log2_max_frame_num_minus4: " << sps->log2_max_frame_num_minus4; DVLOG(1) << "log2_max_frame_num_minus4: " << sps->log2_max_frame_num_minus4;

View File

@ -39,7 +39,7 @@ EsParserH265::~EsParserH265() {}
void EsParserH265::Reset() { void EsParserH265::Reset() {
DVLOG(1) << "EsParserH265::Reset"; DVLOG(1) << "EsParserH265::Reset";
h265_parser_.reset(new H265Parser()); h265_parser_.reset(new H265Parser());
last_video_decoder_config_ = std::shared_ptr<StreamInfo>(); last_video_decoder_config_ = std::shared_ptr<VideoStreamInfo>();
decoder_config_check_pending_ = false; decoder_config_check_pending_ = false;
EsParserH26x::Reset(); EsParserH26x::Reset();
} }
@ -162,7 +162,8 @@ bool EsParserH265::UpdateVideoDecoderConfig(int pps_id) {
pid(), kMpeg2Timescale, kInfiniteDuration, kCodecH265, stream_format, pid(), kMpeg2Timescale, kInfiniteDuration, kCodecH265, stream_format,
decoder_config.GetCodecString(codec_fourcc), decoder_config_record.data(), decoder_config.GetCodecString(codec_fourcc), decoder_config_record.data(),
decoder_config_record.size(), coded_width, coded_height, pixel_width, decoder_config_record.size(), coded_width, coded_height, pixel_width,
pixel_height, 0, nalu_length_size, std::string(), false); pixel_height, sps->vui_parameters.transfer_characteristics, 0,
nalu_length_size, std::string(), false);
// Video config notification. // Video config notification.
new_stream_info_cb_.Run(last_video_decoder_config_); new_stream_info_cb_.Run(last_video_decoder_config_);

View File

@ -83,6 +83,7 @@ const uint32_t kWidth = 1280;
const uint32_t kHeight = 720; const uint32_t kHeight = 720;
const uint32_t kPixelWidth = 1; const uint32_t kPixelWidth = 1;
const uint32_t kPixelHeight = 1; const uint32_t kPixelHeight = 1;
const uint8_t kTransferCharacteristics = 0;
const uint16_t kTrickPlayFactor = 1; const uint16_t kTrickPlayFactor = 1;
const uint8_t kNaluLengthSize = 1; const uint8_t kNaluLengthSize = 1;
const bool kIsEncrypted = false; const bool kIsEncrypted = false;
@ -123,7 +124,8 @@ std::shared_ptr<VideoStreamInfo> CreateVideoStreamInfo(Codec codec) {
kTrackId, kTimeScale, kDuration, codec, kTrackId, kTimeScale, kDuration, codec,
H26xStreamFormat::kAnnexbByteStream, kCodecString, kVideoExtraData, H26xStreamFormat::kAnnexbByteStream, kCodecString, kVideoExtraData,
arraysize(kVideoExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight, arraysize(kVideoExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted)); kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage,
kIsEncrypted));
return stream_info; return stream_info;
} }
@ -355,7 +357,8 @@ TEST_F(PesPacketGeneratorTest, TimeStampScaling) {
kTrackId, kTestTimescale, kDuration, kH264Codec, kTrackId, kTestTimescale, kDuration, kH264Codec,
H26xStreamFormat::kAnnexbByteStream, kCodecString, kVideoExtraData, H26xStreamFormat::kAnnexbByteStream, kCodecString, kVideoExtraData,
arraysize(kVideoExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight, arraysize(kVideoExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted)); kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage,
kIsEncrypted));
EXPECT_TRUE(generator_.Initialize(*stream_info)); EXPECT_TRUE(generator_.Initialize(*stream_info));
EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets()); EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets());

View File

@ -44,6 +44,7 @@ const uint32_t kWidth = 1280;
const uint32_t kHeight = 720; const uint32_t kHeight = 720;
const uint32_t kPixelWidth = 1; const uint32_t kPixelWidth = 1;
const uint32_t kPixelHeight = 1; const uint32_t kPixelHeight = 1;
const uint8_t kTransferCharacteristics = 0;
const uint16_t kTrickPlayFactor = 1; const uint16_t kTrickPlayFactor = 1;
const uint8_t kNaluLengthSize = 1; const uint8_t kNaluLengthSize = 1;
const bool kIsEncrypted = false; const bool kIsEncrypted = false;
@ -110,7 +111,8 @@ TEST_F(TsSegmenterTest, Initialize) {
kTrackId, kTimeScale, kDuration, kH264Codec, kTrackId, kTimeScale, kDuration, kH264Codec,
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData, H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight, arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted)); kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage,
kIsEncrypted));
MuxerOptions options; MuxerOptions options;
options.segment_template = "file$Number$.ts"; options.segment_template = "file$Number$.ts";
TsSegmenter segmenter(options, nullptr); TsSegmenter segmenter(options, nullptr);
@ -129,7 +131,8 @@ TEST_F(TsSegmenterTest, AddSample) {
kTrackId, kTimeScale, kDuration, kH264Codec, kTrackId, kTimeScale, kDuration, kH264Codec,
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData, H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight, arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted)); kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage,
kIsEncrypted));
MuxerOptions options; MuxerOptions options;
options.segment_template = "file$Number$.ts"; options.segment_template = "file$Number$.ts";
TsSegmenter segmenter(options, nullptr); TsSegmenter segmenter(options, nullptr);
@ -180,7 +183,8 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) {
kTrackId, kInputTimescale, kDuration, kH264Codec, kTrackId, kInputTimescale, kDuration, kH264Codec,
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData, H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight, arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted)); kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage,
kIsEncrypted));
MuxerOptions options; MuxerOptions options;
options.segment_template = "file$Number$.ts"; options.segment_template = "file$Number$.ts";
@ -274,7 +278,8 @@ TEST_F(TsSegmenterTest, InitializeThenFinalize) {
kTrackId, kTimeScale, kDuration, kH264Codec, kTrackId, kTimeScale, kDuration, kH264Codec,
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData, H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight, arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted)); kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage,
kIsEncrypted));
MuxerOptions options; MuxerOptions options;
options.segment_template = "file$Number$.ts"; options.segment_template = "file$Number$.ts";
TsSegmenter segmenter(options, nullptr); TsSegmenter segmenter(options, nullptr);
@ -301,7 +306,8 @@ TEST_F(TsSegmenterTest, FinalizeSegment) {
kTrackId, kTimeScale, kDuration, kH264Codec, kTrackId, kTimeScale, kDuration, kH264Codec,
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData, H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight, arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted)); kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage,
kIsEncrypted));
MuxerOptions options; MuxerOptions options;
options.segment_template = "file$Number$.ts"; options.segment_template = "file$Number$.ts";
TsSegmenter segmenter(options, nullptr); TsSegmenter segmenter(options, nullptr);
@ -328,7 +334,8 @@ TEST_F(TsSegmenterTest, EncryptedSample) {
kTrackId, kTimeScale, kDuration, kH264Codec, kTrackId, kTimeScale, kDuration, kH264Codec,
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData, H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight, arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted)); kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage,
kIsEncrypted));
MuxerOptions options; MuxerOptions options;
options.segment_template = "file$Number$.ts"; options.segment_template = "file$Number$.ts";

View File

@ -555,6 +555,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
} }
std::string codec_string; std::string codec_string;
uint8_t nalu_length_size = 0; uint8_t nalu_length_size = 0;
uint8_t transfer_characteristics = 0;
const FourCC actual_format = entry.GetActualFormat(); const FourCC actual_format = entry.GetActualFormat();
const Codec video_codec = FourCCToCodec(actual_format); const Codec video_codec = FourCCToCodec(actual_format);
@ -577,6 +578,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
} }
codec_string = avc_config.GetCodecString(actual_format); codec_string = avc_config.GetCodecString(actual_format);
nalu_length_size = avc_config.nalu_length_size(); nalu_length_size = avc_config.nalu_length_size();
transfer_characteristics = avc_config.transfer_characteristics();
// Use configurations from |avc_config| if it is valid. // Use configurations from |avc_config| if it is valid.
if (avc_config.coded_width() != 0) { if (avc_config.coded_width() != 0) {
@ -624,6 +626,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
} }
codec_string = hevc_config.GetCodecString(actual_format); codec_string = hevc_config.GetCodecString(actual_format);
nalu_length_size = hevc_config.nalu_length_size(); nalu_length_size = hevc_config.nalu_length_size();
transfer_characteristics = hevc_config.transfer_characteristics();
if (!entry.extra_codec_configs.empty()) { if (!entry.extra_codec_configs.empty()) {
if (!UpdateCodecStringForDolbyVision( if (!UpdateCodecStringForDolbyVision(
@ -672,6 +675,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
GetH26xStreamFormat(actual_format), codec_string, GetH26xStreamFormat(actual_format), codec_string,
codec_configuration_data.data(), codec_configuration_data.size(), codec_configuration_data.data(), codec_configuration_data.size(),
coded_width, coded_height, pixel_width, pixel_height, coded_width, coded_height, pixel_width, pixel_height,
transfer_characteristics,
0, // trick_play_factor 0, // trick_play_factor
nalu_length_size, track->media.header.language.code, is_encrypted)); nalu_length_size, track->media.header.language.code, is_encrypted));
video_stream_info->set_extra_config(entry.ExtraCodecConfigsAsVector()); video_stream_info->set_extra_config(entry.ExtraCodecConfigsAsVector());

View File

@ -30,6 +30,7 @@ const uint16_t kWidth = 100;
const uint16_t kHeight = 100; const uint16_t kHeight = 100;
const uint16_t kPixelWidth = 100; const uint16_t kPixelWidth = 100;
const uint16_t kPixelHeight = 100; const uint16_t kPixelHeight = 100;
const uint8_t kTransferCharacteristics = 0;
const int16_t kTrickPlayFactor = 1; const int16_t kTrickPlayFactor = 1;
const uint8_t kNaluLengthSize = 0; const uint8_t kNaluLengthSize = 0;
@ -85,8 +86,8 @@ VideoStreamInfo* SegmentTestBase::CreateVideoStreamInfo(
return new VideoStreamInfo( return new VideoStreamInfo(
kTrackId, time_scale, kDurationInSeconds * time_scale, kCodec, kTrackId, time_scale, kDurationInSeconds * time_scale, kCodec,
H26xStreamFormat::kUnSpecified, kCodecString, NULL, 0, kWidth, kHeight, H26xStreamFormat::kUnSpecified, kCodecString, NULL, 0, kWidth, kHeight,
kPixelWidth, kPixelHeight, kTrickPlayFactor, kNaluLengthSize, kLanguage, kPixelWidth, kPixelHeight, kTransferCharacteristics, kTrickPlayFactor,
false); kNaluLengthSize, kLanguage, false);
} }
std::string SegmentTestBase::OutputFileName() const { std::string SegmentTestBase::OutputFileName() const {

View File

@ -91,6 +91,7 @@ const uint16_t kWidth = 320u;
const uint16_t kHeight = 180u; const uint16_t kHeight = 180u;
const uint32_t kPixelWidth = 1u; const uint32_t kPixelWidth = 1u;
const uint32_t kPixelHeight = 1u; const uint32_t kPixelHeight = 1u;
const uint8_t kTransferCharacteristics = 0;
const int16_t kTrickPlayFactor = 0u; const int16_t kTrickPlayFactor = 0u;
const uint8_t kNaluLengthSize = 0u; const uint8_t kNaluLengthSize = 0u;
@ -350,6 +351,7 @@ class WebMClusterParserTest : public testing::Test {
kHeight, kHeight,
kPixelWidth, kPixelWidth,
kPixelHeight, kPixelHeight,
kTransferCharacteristics,
kTrickPlayFactor, kTrickPlayFactor,
kNaluLengthSize, kNaluLengthSize,
kLanguage, kLanguage,

View File

@ -124,7 +124,7 @@ std::shared_ptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
track_num, kWebMTimeScale, 0, video_codec, H26xStreamFormat::kUnSpecified, track_num, kWebMTimeScale, 0, video_codec, H26xStreamFormat::kUnSpecified,
codec_string, codec_private.data(), codec_private.size(), codec_string, codec_private.data(), codec_private.size(),
width_after_crop, height_after_crop, pixel_width, pixel_height, 0, 0, width_after_crop, height_after_crop, pixel_width, pixel_height, 0, 0,
std::string(), is_encrypted); 0 /* transfer_characteristics */, std::string(), is_encrypted);
} }
VPCodecConfigurationRecord WebMVideoClient::GetVpCodecConfig( VPCodecConfigurationRecord WebMVideoClient::GetVpCodecConfig(

View File

@ -744,9 +744,9 @@ bool WvmMediaParser::ParseIndexEntry() {
stream_id_count_, time_scale, track_duration, kCodecH264, stream_id_count_, time_scale, track_duration, kCodecH264,
byte_to_unit_stream_converter_.stream_format(), std::string(), byte_to_unit_stream_converter_.stream_format(), std::string(),
video_codec_config.data(), video_codec_config.size(), video_width, video_codec_config.data(), video_codec_config.size(), video_width,
video_height, pixel_width, pixel_height, trick_play_factor, video_height, pixel_width, pixel_height,
nalu_length_size, std::string(), 0 /* transfer_characteristics */, trick_play_factor, nalu_length_size,
decryption_key_source_ ? false : true)); std::string(), decryption_key_source_ ? false : true));
program_demux_stream_map_[base::UintToString(index_program_id_) + ":" + program_demux_stream_map_[base::UintToString(index_program_id_) + ":" +
base::UintToString( base::UintToString(
video_pes_stream_id video_pes_stream_id

View File

@ -35,6 +35,11 @@ bear-1280x720.mp4 - AVC + AAC encode, mulitplexed into an ISOBMFF container.
bear-640x360.mp4 - Same as above, but in a different resolution. bear-640x360.mp4 - Same as above, but in a different resolution.
bear-640x360-ec3.mp4 - Same content, but audio encoded with E-AC3. bear-640x360-ec3.mp4 - Same content, but audio encoded with E-AC3.
bear-640x360-hevc.mp4 - Same content, but video encoded with HEVC. bear-640x360-hevc.mp4 - Same content, but video encoded with HEVC.
bear-640x360-hevc-hdr10.mp4 - Same content, but video encoded with HEVC with HDR10 using the below command:
ffmpeg -i bear-640x360-hevc.mp4 -c:a copy -c:v libx265 -tag:v hvc1 -crf 22 \
-pix_fmt yuv420p10le \
-x265-params "colorprim=bt2020:transfer=smpte2084:colormatrix=bt2020nc" \
bear-640x360-hevc-hdr10.mp4
bear-320x180.mp4 - Same as above, but in a different resolution. bear-320x180.mp4 - Same as above, but in a different resolution.
bear-640x360-trailing-moov.mp4 - Same content, but with moov box in the end. bear-640x360-trailing-moov.mp4 - Same content, but with moov box in the end.
bear-640x360-trailing-moov-additional-mdat.mp4 - Same content, but with moov box in the end and an additional unused mdat, which should be ignored. bear-640x360-trailing-moov-additional-mdat.mp4 - Same content, but with moov box in the end and an additional unused mdat, which should be ignored.

Binary file not shown.

View File

@ -45,6 +45,10 @@ message MediaInfo {
// playback_rate: the playout capability (e.g., 4x, 8x, 16x fast foward) of // playback_rate: the playout capability (e.g., 4x, 8x, 16x fast foward) of
// the trick play stream. // the trick play stream.
optional uint32 playback_rate = 9; optional uint32 playback_rate = 9;
// Transfer characteristics. Useful to determine the VIDEO-RANGE for HLS,
// i.e. whether it is SDR or HDR.
optional uint32 transfer_characteristics = 10;
} }
message AudioInfo { message AudioInfo {