[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.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')
|
||||||
|
|
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;
|
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) {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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_; }
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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) {}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_));
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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() {}
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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_);
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue