[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:
parent
8029004c6b
commit
3f909fa551
|
@ -1203,6 +1203,15 @@ class PackagerFunctionalTest(PackagerAppTest):
|
|||
self.assertPackageSuccess(streams, flags)
|
||||
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):
|
||||
streams = [
|
||||
self._GetStream('video', test_file='426x240-dvh1.mp4')
|
||||
|
|
BIN
packager/app/test/testdata/hdr10-with-encryption/bear-640x360-hevc-hdr10-video.mp4
vendored
Normal file
BIN
packager/app/test/testdata/hdr10-with-encryption/bear-640x360-hevc-hdr10-video.mp4
vendored
Normal file
Binary file not shown.
|
@ -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
|
|
@ -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>
|
|
@ -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
|
|
@ -222,6 +222,9 @@ void BuildStreamInfTag(const MediaPlaylist& playlist,
|
|||
uint32_t height;
|
||||
if (playlist.GetDisplayResolution(&width, &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) {
|
||||
|
|
|
@ -527,6 +527,22 @@ bool MediaPlaylist::GetDisplayResolution(uint32_t* width,
|
|||
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,
|
||||
int64_t start_time,
|
||||
int64_t duration,
|
||||
|
|
|
@ -186,6 +186,9 @@ class MediaPlaylist {
|
|||
/// resolution values.
|
||||
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
|
||||
/// form. May be an empty string for video.
|
||||
const std::string& language() const { return language_; }
|
||||
|
|
|
@ -28,6 +28,7 @@ const uint16_t kWidth = 10u;
|
|||
const uint16_t kHeight = 20u;
|
||||
const uint32_t kPixelWidth = 2u;
|
||||
const uint32_t kPixelHeight = 3u;
|
||||
const uint8_t kTransferCharacteristics = 0;
|
||||
const int16_t kTrickPlayFactor = 0;
|
||||
const uint8_t kNaluLengthSize = 1u;
|
||||
const bool kEncrypted = true;
|
||||
|
@ -199,8 +200,8 @@ 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, kTrickPlayFactor, kNaluLengthSize, kLanguage,
|
||||
!kEncrypted));
|
||||
kPixelWidth, kPixelHeight, kTransferCharacteristics, kTrickPlayFactor,
|
||||
kNaluLengthSize, kLanguage, !kEncrypted));
|
||||
}
|
||||
|
||||
std::unique_ptr<StreamInfo> MediaHandlerTestBase::GetAudioStreamInfo(
|
||||
|
|
|
@ -50,6 +50,7 @@ VideoStreamInfo::VideoStreamInfo(int track_id,
|
|||
uint16_t height,
|
||||
uint32_t pixel_width,
|
||||
uint32_t pixel_height,
|
||||
uint8_t transfer_characteristics,
|
||||
uint32_t trick_play_factor,
|
||||
uint8_t nalu_length_size,
|
||||
const std::string& language,
|
||||
|
@ -69,6 +70,7 @@ VideoStreamInfo::VideoStreamInfo(int track_id,
|
|||
height_(height),
|
||||
pixel_width_(pixel_width),
|
||||
pixel_height_(pixel_height),
|
||||
transfer_characteristics_(transfer_characteristics),
|
||||
trick_play_factor_(trick_play_factor),
|
||||
nalu_length_size_(nalu_length_size) {}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ class VideoStreamInfo : public StreamInfo {
|
|||
uint16_t height,
|
||||
uint32_t pixel_width,
|
||||
uint32_t pixel_height,
|
||||
uint8_t transfer_characteristics,
|
||||
uint32_t trick_play_factor,
|
||||
uint8_t nalu_length_size,
|
||||
const std::string& language,
|
||||
|
@ -63,6 +64,7 @@ class VideoStreamInfo : public StreamInfo {
|
|||
/// Returns the pixel height.
|
||||
/// @return 0 if unknown.
|
||||
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_; }
|
||||
uint32_t trick_play_factor() const { return trick_play_factor_; }
|
||||
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_pixel_width(uint32_t pixel_width) { pixel_width_ = pixel_width; }
|
||||
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) {
|
||||
trick_play_factor_ = trick_play_factor;
|
||||
}
|
||||
|
@ -98,6 +103,7 @@ class VideoStreamInfo : public StreamInfo {
|
|||
// 0 means unknown.
|
||||
uint32_t pixel_width_;
|
||||
uint32_t pixel_height_;
|
||||
uint8_t transfer_characteristics_ = 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
|
||||
|
|
|
@ -64,6 +64,8 @@ bool AVCDecoderConfigurationRecord::ParseInternal() {
|
|||
int sps_id = 0;
|
||||
H264Parser parser;
|
||||
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_,
|
||||
&coded_height_, &pixel_width_,
|
||||
&pixel_height_));
|
||||
|
|
|
@ -12,11 +12,24 @@ namespace shaka {
|
|||
namespace media {
|
||||
|
||||
TEST(AVCDecoderConfigurationRecordTest, Success) {
|
||||
// clang-format off
|
||||
const uint8_t kAvcDecoderConfigurationData[] = {
|
||||
0x01, 0x64, 0x00, 0x1E, 0xFF, 0xE1, 0x00, 0x1D, 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, 0x00, 0x06, 0x68, 0xEB, 0xE3, 0xCB, 0x22, 0xC0};
|
||||
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, 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;
|
||||
ASSERT_TRUE(avc_config.Parse(kAvcDecoderConfigurationData,
|
||||
|
@ -31,17 +44,71 @@ TEST(AVCDecoderConfigurationRecordTest, Success) {
|
|||
EXPECT_EQ(360u, avc_config.coded_height());
|
||||
EXPECT_EQ(8u, avc_config.pixel_width());
|
||||
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("avc3.64001e", avc_config.GetCodecString(FOURCC_avc3));
|
||||
}
|
||||
|
||||
TEST(AVCDecoderConfigurationRecordTest, FailsOnInvalidNaluLengthSize) {
|
||||
// clang-format off
|
||||
const uint8_t kAvcDecoderConfigurationData[] = {
|
||||
0x01, 0x64, 0x00, 0x1E, 0xFE, 0xE1, 0x00, 0x1D, 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, 0x00, 0x06, 0x68, 0xEB, 0xE3, 0xCB, 0x22, 0xC0};
|
||||
0x01, // version
|
||||
0x64, // profile_indication
|
||||
0x00, // profile_compatibility
|
||||
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;
|
||||
ASSERT_FALSE(avc_config.Parse(kAvcDecoderConfigurationData,
|
||||
|
|
|
@ -9,9 +9,8 @@
|
|||
namespace shaka {
|
||||
namespace media {
|
||||
|
||||
DecoderConfigurationRecord::DecoderConfigurationRecord()
|
||||
: nalu_length_size_(0) {}
|
||||
DecoderConfigurationRecord::~DecoderConfigurationRecord() {}
|
||||
DecoderConfigurationRecord::DecoderConfigurationRecord() = default;
|
||||
DecoderConfigurationRecord::~DecoderConfigurationRecord() = default;
|
||||
|
||||
bool DecoderConfigurationRecord::Parse(const uint8_t* data, size_t data_size) {
|
||||
data_.assign(data, data + data_size);
|
||||
|
|
|
@ -43,6 +43,9 @@ class DecoderConfigurationRecord {
|
|||
/// lifetime of this object, even if copied.
|
||||
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:
|
||||
DecoderConfigurationRecord();
|
||||
|
||||
|
@ -61,6 +64,11 @@ class DecoderConfigurationRecord {
|
|||
nalu_length_size_ = nalu_length_size;
|
||||
}
|
||||
|
||||
/// Sets the transfer characteristics.
|
||||
void set_transfer_characteristics(uint8_t transfer_characteristics) {
|
||||
transfer_characteristics_ = transfer_characteristics;
|
||||
}
|
||||
|
||||
private:
|
||||
// Performs the actual parsing of the data.
|
||||
virtual bool ParseInternal() = 0;
|
||||
|
@ -69,7 +77,12 @@ class DecoderConfigurationRecord {
|
|||
// extracted Nalu can accessed.
|
||||
std::vector<uint8_t> data_;
|
||||
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);
|
||||
};
|
||||
|
|
|
@ -527,8 +527,11 @@ H264Parser::Result H264Parser::ParseVUIParameters(H26xBitReader* br,
|
|||
READ_BITS_OR_RETURN(3, &data); // video_format
|
||||
READ_BOOL_OR_RETURN(&data); // video_full_range_flag
|
||||
READ_BOOL_OR_RETURN(&data); // colour_description_present_flag
|
||||
if (data)
|
||||
READ_BITS_OR_RETURN(24, &data); // color description syntax elements
|
||||
if (data) {
|
||||
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
|
||||
|
|
|
@ -82,6 +82,8 @@ struct H264Sps {
|
|||
bool vui_parameters_present_flag;
|
||||
int sar_width; // Set to 0 when not specified.
|
||||
int sar_height; // Set to 0 when not specified.
|
||||
int transfer_characteristics;
|
||||
|
||||
bool bitstream_restriction_flag;
|
||||
int max_num_reorder_frames;
|
||||
int max_dec_frame_buffering;
|
||||
|
|
|
@ -187,6 +187,47 @@ TEST(H264ParserTest, PredWeightTable) {
|
|||
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) {
|
||||
const uint8_t kSps[] = {0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4,
|
||||
0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91,
|
||||
|
|
|
@ -661,8 +661,9 @@ 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) {
|
||||
// colour_primaries, transfer_characteristics, matrix_coeffs
|
||||
TRUE_OR_RETURN(br->SkipBits(8 + 8 + 8));
|
||||
TRUE_OR_RETURN(br->SkipBits(8)); // colour_primaries
|
||||
TRUE_OR_RETURN(br->ReadBits(8, &vui->transfer_characteristics));
|
||||
TRUE_OR_RETURN(br->SkipBits(8)); // matrix_coeffs
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ struct H265VuiParameters {
|
|||
int aspect_ratio_idc = 0;
|
||||
int sar_width = 0;
|
||||
int sar_height = 0;
|
||||
int transfer_characteristics = 0;
|
||||
|
||||
bool bitstream_restriction_flag = false;
|
||||
int min_spatial_segmentation_idc = 0;
|
||||
|
|
|
@ -15,11 +15,17 @@ namespace H265 {
|
|||
|
||||
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[] = {
|
||||
0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x80, 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};
|
||||
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 kSliceData[] = {
|
||||
// Incomplete segment data.
|
||||
|
@ -105,6 +111,32 @@ TEST(H265ParserTest, ParseSps) {
|
|||
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(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) {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "packager/base/strings/string_util.h"
|
||||
#include "packager/media/base/buffer_reader.h"
|
||||
#include "packager/media/base/rcheck.h"
|
||||
#include "packager/media/codecs/h265_parser.h"
|
||||
|
||||
namespace shaka {
|
||||
namespace media {
|
||||
|
@ -61,15 +62,9 @@ std::string ReverseBitsAndHexEncode(uint32_t x) {
|
|||
|
||||
} // namespace
|
||||
|
||||
HEVCDecoderConfigurationRecord::HEVCDecoderConfigurationRecord()
|
||||
: 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() = default;
|
||||
|
||||
HEVCDecoderConfigurationRecord::~HEVCDecoderConfigurationRecord() {}
|
||||
HEVCDecoderConfigurationRecord::~HEVCDecoderConfigurationRecord() = default;
|
||||
|
||||
bool HEVCDecoderConfigurationRecord::ParseInternal() {
|
||||
BufferReader reader(data(), data_size());
|
||||
|
@ -113,6 +108,14 @@ bool HEVCDecoderConfigurationRecord::ParseInternal() {
|
|||
RCHECK(nalu.Initialize(Nalu::kH265, data() + nalu_offset, nalu_length));
|
||||
RCHECK(nalu.type() == nal_unit_type);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,13 +31,13 @@ class HEVCDecoderConfigurationRecord : public DecoderConfigurationRecord {
|
|||
private:
|
||||
bool ParseInternal() override;
|
||||
|
||||
uint8_t version_;
|
||||
uint8_t general_profile_space_;
|
||||
bool general_tier_flag_;
|
||||
uint8_t general_profile_idc_;
|
||||
uint32_t general_profile_compatibility_flags_;
|
||||
uint8_t version_ = 0;
|
||||
uint8_t general_profile_space_ = 0;
|
||||
bool general_tier_flag_ = false;
|
||||
uint8_t general_profile_idc_ = 0;
|
||||
uint32_t general_profile_compatibility_flags_ = 0;
|
||||
std::vector<uint8_t> general_constraint_indicator_flags_;
|
||||
uint8_t general_level_idc_;
|
||||
uint8_t general_level_idc_ = 0;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(HEVCDecoderConfigurationRecord);
|
||||
};
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace shaka {
|
|||
namespace media {
|
||||
|
||||
TEST(HEVCDecoderConfigurationRecordTest, Success) {
|
||||
// clang-format off
|
||||
const uint8_t kHevcDecoderConfigurationData[] = {
|
||||
0x01, // Version
|
||||
0x02, // profile_indication
|
||||
|
@ -33,11 +34,61 @@ TEST(HEVCDecoderConfigurationRecordTest, Success) {
|
|||
// array 2
|
||||
0x21, // nal type
|
||||
0x00, 0x01, // num nalus
|
||||
// Nalu 1
|
||||
0x00, 0x0f, // nal unit length
|
||||
0x42, 0x01, 0x01, 0x02, 0x20, 0x00, 0x00, 0x03, 0x00, 0x90,
|
||||
0x00, 0x00, 0x03, 0x00, 0x00,
|
||||
// nalu 1
|
||||
0x00, 0x24, // nal unit length
|
||||
0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x80, 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;
|
||||
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("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(0x40, hevc_config.nalu(0).data()[0]);
|
||||
|
||||
EXPECT_EQ(16, hevc_config.transfer_characteristics());
|
||||
}
|
||||
|
||||
TEST(HEVCDecoderConfigurationRecordTest, FailOnInsufficientData) {
|
||||
|
|
|
@ -63,6 +63,7 @@ VideoStreamInfo GetVideoStreamInfo(Codec codec) {
|
|||
const uint16_t kHeight = 20u;
|
||||
const uint32_t kPixelWidth = 2u;
|
||||
const uint32_t kPixelHeight = 3u;
|
||||
const uint8_t kTransferCharacteristics = 0;
|
||||
const int16_t kTrickPlayFactor = 0;
|
||||
const uint8_t kNaluLengthSize = 1u;
|
||||
|
||||
|
@ -81,11 +82,11 @@ VideoStreamInfo GetVideoStreamInfo(Codec codec) {
|
|||
// We do not care about the codec configs for other codecs in this file.
|
||||
break;
|
||||
}
|
||||
return VideoStreamInfo(kTrackId, kTimeScale, kDuration, codec,
|
||||
H26xStreamFormat::kUnSpecified, kCodecString,
|
||||
codec_config, codec_config_size, kWidth, kHeight,
|
||||
kPixelWidth, kPixelHeight, kTrickPlayFactor,
|
||||
kNaluLengthSize, kLanguage, !kEncrypted);
|
||||
return VideoStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, codec, H26xStreamFormat::kUnSpecified,
|
||||
kCodecString, codec_config, codec_config_size, kWidth, kHeight,
|
||||
kPixelWidth, kPixelHeight, kTransferCharacteristics, kTrickPlayFactor,
|
||||
kNaluLengthSize, kLanguage, !kEncrypted);
|
||||
}
|
||||
|
||||
AudioStreamInfo GetAudioStreamInfo(Codec codec) {
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
'type': '<(gtest_target_type)',
|
||||
'sources': [
|
||||
'hls_notify_muxer_listener_unittest.cc',
|
||||
'muxer_listener_internal_unittest.cc',
|
||||
'mpd_notify_muxer_listener_unittest.cc',
|
||||
'muxer_listener_test_helper.cc',
|
||||
'muxer_listener_test_helper.h',
|
||||
|
|
|
@ -87,6 +87,10 @@ void AddVideoInfo(const VideoStreamInfo* video_stream_info,
|
|||
if (video_stream_info->playback_rate() > 0) {
|
||||
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,
|
||||
|
|
|
@ -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
|
|
@ -15,13 +15,14 @@ namespace media {
|
|||
VideoStreamInfoParameters::VideoStreamInfoParameters() {}
|
||||
VideoStreamInfoParameters::~VideoStreamInfoParameters() {}
|
||||
|
||||
std::shared_ptr<StreamInfo> CreateVideoStreamInfo(
|
||||
std::shared_ptr<VideoStreamInfo> CreateVideoStreamInfo(
|
||||
const VideoStreamInfoParameters& param) {
|
||||
return std::make_shared<VideoStreamInfo>(
|
||||
param.track_id, param.time_scale, param.duration, param.codec,
|
||||
H26xStreamFormat::kUnSpecified, param.codec_string,
|
||||
param.codec_config.data(), param.codec_config.size(), param.width,
|
||||
param.height, param.pixel_width, param.pixel_height,
|
||||
0, // transfer_characteristics
|
||||
0, // trick_play_factor
|
||||
param.nalu_length_size, param.language, param.is_encrypted);
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ struct OnMediaEndParameters {
|
|||
};
|
||||
|
||||
// Creates StreamInfo instance from VideoStreamInfoParameters.
|
||||
std::shared_ptr<StreamInfo> CreateVideoStreamInfo(
|
||||
std::shared_ptr<VideoStreamInfo> CreateVideoStreamInfo(
|
||||
const VideoStreamInfoParameters& param);
|
||||
|
||||
// Returns the "default" VideoStreamInfoParameters for testing.
|
||||
|
|
|
@ -159,8 +159,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, 0, nalu_length_size,
|
||||
std::string(), false);
|
||||
coded_height, pixel_width, pixel_height, sps->transfer_characteristics, 0,
|
||||
nalu_length_size, std::string(), false);
|
||||
DVLOG(1) << "Profile IDC: " << sps->profile_idc;
|
||||
DVLOG(1) << "Level IDC: " << sps->level_idc;
|
||||
DVLOG(1) << "log2_max_frame_num_minus4: " << sps->log2_max_frame_num_minus4;
|
||||
|
|
|
@ -39,7 +39,7 @@ EsParserH265::~EsParserH265() {}
|
|||
void EsParserH265::Reset() {
|
||||
DVLOG(1) << "EsParserH265::Reset";
|
||||
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;
|
||||
EsParserH26x::Reset();
|
||||
}
|
||||
|
@ -162,7 +162,8 @@ 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, 0, nalu_length_size, std::string(), false);
|
||||
pixel_height, sps->vui_parameters.transfer_characteristics, 0,
|
||||
nalu_length_size, std::string(), false);
|
||||
|
||||
// Video config notification.
|
||||
new_stream_info_cb_.Run(last_video_decoder_config_);
|
||||
|
|
|
@ -83,6 +83,7 @@ const uint32_t kWidth = 1280;
|
|||
const uint32_t kHeight = 720;
|
||||
const uint32_t kPixelWidth = 1;
|
||||
const uint32_t kPixelHeight = 1;
|
||||
const uint8_t kTransferCharacteristics = 0;
|
||||
const uint16_t kTrickPlayFactor = 1;
|
||||
const uint8_t kNaluLengthSize = 1;
|
||||
const bool kIsEncrypted = false;
|
||||
|
@ -123,7 +124,8 @@ std::shared_ptr<VideoStreamInfo> CreateVideoStreamInfo(Codec codec) {
|
|||
kTrackId, kTimeScale, kDuration, codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kVideoExtraData,
|
||||
arraysize(kVideoExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage,
|
||||
kIsEncrypted));
|
||||
return stream_info;
|
||||
}
|
||||
|
||||
|
@ -355,7 +357,8 @@ TEST_F(PesPacketGeneratorTest, TimeStampScaling) {
|
|||
kTrackId, kTestTimescale, kDuration, kH264Codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kVideoExtraData,
|
||||
arraysize(kVideoExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage,
|
||||
kIsEncrypted));
|
||||
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
||||
|
||||
EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets());
|
||||
|
|
|
@ -44,6 +44,7 @@ const uint32_t kWidth = 1280;
|
|||
const uint32_t kHeight = 720;
|
||||
const uint32_t kPixelWidth = 1;
|
||||
const uint32_t kPixelHeight = 1;
|
||||
const uint8_t kTransferCharacteristics = 0;
|
||||
const uint16_t kTrickPlayFactor = 1;
|
||||
const uint8_t kNaluLengthSize = 1;
|
||||
const bool kIsEncrypted = false;
|
||||
|
@ -110,7 +111,8 @@ TEST_F(TsSegmenterTest, Initialize) {
|
|||
kTrackId, kTimeScale, kDuration, kH264Codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
|
||||
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage,
|
||||
kIsEncrypted));
|
||||
MuxerOptions options;
|
||||
options.segment_template = "file$Number$.ts";
|
||||
TsSegmenter segmenter(options, nullptr);
|
||||
|
@ -129,7 +131,8 @@ TEST_F(TsSegmenterTest, AddSample) {
|
|||
kTrackId, kTimeScale, kDuration, kH264Codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
|
||||
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage,
|
||||
kIsEncrypted));
|
||||
MuxerOptions options;
|
||||
options.segment_template = "file$Number$.ts";
|
||||
TsSegmenter segmenter(options, nullptr);
|
||||
|
@ -180,7 +183,8 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) {
|
|||
kTrackId, kInputTimescale, kDuration, kH264Codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
|
||||
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage,
|
||||
kIsEncrypted));
|
||||
MuxerOptions options;
|
||||
options.segment_template = "file$Number$.ts";
|
||||
|
||||
|
@ -274,7 +278,8 @@ TEST_F(TsSegmenterTest, InitializeThenFinalize) {
|
|||
kTrackId, kTimeScale, kDuration, kH264Codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
|
||||
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage,
|
||||
kIsEncrypted));
|
||||
MuxerOptions options;
|
||||
options.segment_template = "file$Number$.ts";
|
||||
TsSegmenter segmenter(options, nullptr);
|
||||
|
@ -301,7 +306,8 @@ TEST_F(TsSegmenterTest, FinalizeSegment) {
|
|||
kTrackId, kTimeScale, kDuration, kH264Codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
|
||||
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage,
|
||||
kIsEncrypted));
|
||||
MuxerOptions options;
|
||||
options.segment_template = "file$Number$.ts";
|
||||
TsSegmenter segmenter(options, nullptr);
|
||||
|
@ -328,7 +334,8 @@ TEST_F(TsSegmenterTest, EncryptedSample) {
|
|||
kTrackId, kTimeScale, kDuration, kH264Codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
|
||||
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage,
|
||||
kIsEncrypted));
|
||||
MuxerOptions options;
|
||||
|
||||
options.segment_template = "file$Number$.ts";
|
||||
|
|
|
@ -555,6 +555,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
}
|
||||
std::string codec_string;
|
||||
uint8_t nalu_length_size = 0;
|
||||
uint8_t transfer_characteristics = 0;
|
||||
|
||||
const FourCC actual_format = entry.GetActualFormat();
|
||||
const Codec video_codec = FourCCToCodec(actual_format);
|
||||
|
@ -577,6 +578,7 @@ 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();
|
||||
|
||||
// Use configurations from |avc_config| if it is valid.
|
||||
if (avc_config.coded_width() != 0) {
|
||||
|
@ -624,6 +626,7 @@ 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();
|
||||
|
||||
if (!entry.extra_codec_configs.empty()) {
|
||||
if (!UpdateCodecStringForDolbyVision(
|
||||
|
@ -672,6 +675,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
GetH26xStreamFormat(actual_format), codec_string,
|
||||
codec_configuration_data.data(), codec_configuration_data.size(),
|
||||
coded_width, coded_height, pixel_width, pixel_height,
|
||||
transfer_characteristics,
|
||||
0, // trick_play_factor
|
||||
nalu_length_size, track->media.header.language.code, is_encrypted));
|
||||
video_stream_info->set_extra_config(entry.ExtraCodecConfigsAsVector());
|
||||
|
|
|
@ -30,6 +30,7 @@ const uint16_t kWidth = 100;
|
|||
const uint16_t kHeight = 100;
|
||||
const uint16_t kPixelWidth = 100;
|
||||
const uint16_t kPixelHeight = 100;
|
||||
const uint8_t kTransferCharacteristics = 0;
|
||||
const int16_t kTrickPlayFactor = 1;
|
||||
const uint8_t kNaluLengthSize = 0;
|
||||
|
||||
|
@ -85,8 +86,8 @@ VideoStreamInfo* SegmentTestBase::CreateVideoStreamInfo(
|
|||
return new VideoStreamInfo(
|
||||
kTrackId, time_scale, kDurationInSeconds * time_scale, kCodec,
|
||||
H26xStreamFormat::kUnSpecified, kCodecString, NULL, 0, kWidth, kHeight,
|
||||
kPixelWidth, kPixelHeight, kTrickPlayFactor, kNaluLengthSize, kLanguage,
|
||||
false);
|
||||
kPixelWidth, kPixelHeight, kTransferCharacteristics, kTrickPlayFactor,
|
||||
kNaluLengthSize, kLanguage, false);
|
||||
}
|
||||
|
||||
std::string SegmentTestBase::OutputFileName() const {
|
||||
|
|
|
@ -91,6 +91,7 @@ const uint16_t kWidth = 320u;
|
|||
const uint16_t kHeight = 180u;
|
||||
const uint32_t kPixelWidth = 1u;
|
||||
const uint32_t kPixelHeight = 1u;
|
||||
const uint8_t kTransferCharacteristics = 0;
|
||||
const int16_t kTrickPlayFactor = 0u;
|
||||
const uint8_t kNaluLengthSize = 0u;
|
||||
|
||||
|
@ -350,6 +351,7 @@ class WebMClusterParserTest : public testing::Test {
|
|||
kHeight,
|
||||
kPixelWidth,
|
||||
kPixelHeight,
|
||||
kTransferCharacteristics,
|
||||
kTrickPlayFactor,
|
||||
kNaluLengthSize,
|
||||
kLanguage,
|
||||
|
|
|
@ -124,7 +124,7 @@ std::shared_ptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
|
|||
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,
|
||||
std::string(), is_encrypted);
|
||||
0 /* transfer_characteristics */, std::string(), is_encrypted);
|
||||
}
|
||||
|
||||
VPCodecConfigurationRecord WebMVideoClient::GetVpCodecConfig(
|
||||
|
|
|
@ -744,9 +744,9 @@ 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, trick_play_factor,
|
||||
nalu_length_size, std::string(),
|
||||
decryption_key_source_ ? false : true));
|
||||
video_height, pixel_width, pixel_height,
|
||||
0 /* transfer_characteristics */, trick_play_factor, nalu_length_size,
|
||||
std::string(), decryption_key_source_ ? false : true));
|
||||
program_demux_stream_map_[base::UintToString(index_program_id_) + ":" +
|
||||
base::UintToString(
|
||||
video_pes_stream_id
|
||||
|
|
|
@ -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-ec3.mp4 - Same content, but audio encoded with E-AC3.
|
||||
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-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.
|
||||
|
|
Binary file not shown.
|
@ -45,6 +45,10 @@ message MediaInfo {
|
|||
// playback_rate: the playout capability (e.g., 4x, 8x, 16x fast foward) of
|
||||
// the trick play stream.
|
||||
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 {
|
||||
|
|
Loading…
Reference in New Issue