Add HEVC Dolby Vision support
- Add relevant FOURCCs for Dolby Vision. - Parse DOVIDecoderConfigurationRecord (dvcC, dvvC) to generate Dolby Vision codec string. - Propagate Dolby Vision configs (dvcC, dvvC, hvcE) from Demuxer to Muxer. - Add a Dolby Vision end to end test. Support for backward compatibility signaling in DASH and HLS will be added in a later CL. Issue #341 Change-Id: If1385df5f48e04b59cb7661130bea48e26b453bf
This commit is contained in:
parent
00fde07bf7
commit
8029004c6b
|
@ -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 testDolbyVisionWithEncryption(self):
|
||||||
|
streams = [
|
||||||
|
self._GetStream('video', test_file='426x240-dvh1.mp4')
|
||||||
|
]
|
||||||
|
flags = self._GetFlags(encryption=True, output_dash=True, output_hls=True)
|
||||||
|
|
||||||
|
self.assertPackageSuccess(streams, flags)
|
||||||
|
self._CheckTestResults('dvh1-with-encryption')
|
||||||
|
|
||||||
def testVp8Mp4WithEncryption(self):
|
def testVp8Mp4WithEncryption(self):
|
||||||
streams = [
|
streams = [
|
||||||
self._GetStream('video',
|
self._GetStream('video',
|
||||||
|
|
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=375371,AVERAGE-BANDWIDTH=368196,CODECS="dvh1.05.01",RESOLUTION=426x240
|
||||||
|
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="PT10.052000045776367S">
|
||||||
|
<Period id="0">
|
||||||
|
<AdaptationSet id="0" contentType="video" width="426" height="240" frameRate="1000/42" 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="375371" codecs="dvh1.05.01" mimeType="video/mp4" sar="1:1">
|
||||||
|
<BaseURL>426x240-dvh1-video.mp4</BaseURL>
|
||||||
|
<SegmentBase indexRange="1353-1420" timescale="1000">
|
||||||
|
<Initialization range="0-1352"/>
|
||||||
|
</SegmentBase>
|
||||||
|
</Representation>
|
||||||
|
</AdaptationSet>
|
||||||
|
</Period>
|
||||||
|
</MPD>
|
|
@ -0,0 +1,17 @@
|
||||||
|
#EXTM3U
|
||||||
|
#EXT-X-VERSION:6
|
||||||
|
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
|
||||||
|
#EXT-X-TARGETDURATION:6
|
||||||
|
#EXT-X-PLAYLIST-TYPE:VOD
|
||||||
|
#EXT-X-MAP:URI="426x240-dvh1-video.mp4",BYTERANGE="1353@0"
|
||||||
|
#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",KEYFORMAT="identity"
|
||||||
|
#EXTINF:5.005,
|
||||||
|
#EXT-X-BYTERANGE:234841@1421
|
||||||
|
426x240-dvh1-video.mp4
|
||||||
|
#EXTINF:5.005,
|
||||||
|
#EXT-X-BYTERANGE:218284
|
||||||
|
426x240-dvh1-video.mp4
|
||||||
|
#EXTINF:0.042,
|
||||||
|
#EXT-X-BYTERANGE:9513
|
||||||
|
426x240-dvh1-video.mp4
|
||||||
|
#EXT-X-ENDLIST
|
|
@ -53,6 +53,10 @@ enum FourCC : uint32_t {
|
||||||
FOURCC_dtsl = 0x6474736c,
|
FOURCC_dtsl = 0x6474736c,
|
||||||
FOURCC_dtsm = 0x6474732d, // "dts-"
|
FOURCC_dtsm = 0x6474732d, // "dts-"
|
||||||
FOURCC_dtsp = 0x6474732b, // "dts+"
|
FOURCC_dtsp = 0x6474732b, // "dts+"
|
||||||
|
FOURCC_dvcC = 0x64766343,
|
||||||
|
FOURCC_dvh1 = 0x64766831,
|
||||||
|
FOURCC_dvhe = 0x64766865,
|
||||||
|
FOURCC_dvvC = 0x64767643,
|
||||||
FOURCC_ec_3 = 0x65632d33, // "ec-3"
|
FOURCC_ec_3 = 0x65632d33, // "ec-3"
|
||||||
FOURCC_ec3d = 0x65633364,
|
FOURCC_ec3d = 0x65633364,
|
||||||
FOURCC_edts = 0x65647473,
|
FOURCC_edts = 0x65647473,
|
||||||
|
@ -69,6 +73,7 @@ enum FourCC : uint32_t {
|
||||||
FOURCC_hint = 0x68696e74,
|
FOURCC_hint = 0x68696e74,
|
||||||
FOURCC_hvc1 = 0x68766331,
|
FOURCC_hvc1 = 0x68766331,
|
||||||
FOURCC_hvcC = 0x68766343,
|
FOURCC_hvcC = 0x68766343,
|
||||||
|
FOURCC_hvcE = 0x68766345,
|
||||||
FOURCC_iden = 0x6964656e,
|
FOURCC_iden = 0x6964656e,
|
||||||
FOURCC_iso6 = 0x69736f36,
|
FOURCC_iso6 = 0x69736f36,
|
||||||
FOURCC_iso8 = 0x69736f38,
|
FOURCC_iso8 = 0x69736f38,
|
||||||
|
|
|
@ -32,6 +32,7 @@ enum Codec {
|
||||||
kCodecAV1 = kCodecVideo,
|
kCodecAV1 = kCodecVideo,
|
||||||
kCodecH264,
|
kCodecH264,
|
||||||
kCodecH265,
|
kCodecH265,
|
||||||
|
kCodecH265DolbyVision,
|
||||||
kCodecVP8,
|
kCodecVP8,
|
||||||
kCodecVP9,
|
kCodecVP9,
|
||||||
kCodecVideoMaxPlusOne,
|
kCodecVideoMaxPlusOne,
|
||||||
|
|
|
@ -24,6 +24,8 @@ std::string VideoCodecToString(Codec codec) {
|
||||||
return "H264";
|
return "H264";
|
||||||
case kCodecH265:
|
case kCodecH265:
|
||||||
return "H265";
|
return "H265";
|
||||||
|
case kCodecH265DolbyVision:
|
||||||
|
return "H265 Dolby Vision";
|
||||||
case kCodecVP8:
|
case kCodecVP8:
|
||||||
return "VP8";
|
return "VP8";
|
||||||
case kCodecVP9:
|
case kCodecVP9:
|
||||||
|
|
|
@ -53,6 +53,7 @@ class VideoStreamInfo : public StreamInfo {
|
||||||
std::unique_ptr<StreamInfo> Clone() const override;
|
std::unique_ptr<StreamInfo> Clone() const override;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
|
const std::vector<uint8_t>& extra_config() const { return extra_config_; }
|
||||||
H26xStreamFormat h26x_stream_format() const { return h26x_stream_format_; }
|
H26xStreamFormat h26x_stream_format() const { return h26x_stream_format_; }
|
||||||
uint16_t width() const { return width_; }
|
uint16_t width() const { return width_; }
|
||||||
uint16_t height() const { return height_; }
|
uint16_t height() const { return height_; }
|
||||||
|
@ -67,6 +68,9 @@ class VideoStreamInfo : public StreamInfo {
|
||||||
uint32_t playback_rate() const { return playback_rate_; }
|
uint32_t playback_rate() const { return playback_rate_; }
|
||||||
const std::vector<uint8_t>& eme_init_data() const { return eme_init_data_; }
|
const std::vector<uint8_t>& eme_init_data() const { return eme_init_data_; }
|
||||||
|
|
||||||
|
void set_extra_config(const std::vector<uint8_t>& extra_config) {
|
||||||
|
extra_config_ = extra_config;
|
||||||
|
}
|
||||||
void set_width(uint32_t width) { width_ = width; }
|
void set_width(uint32_t width) { width_ = width; }
|
||||||
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; }
|
||||||
|
@ -83,6 +87,9 @@ class VideoStreamInfo : public StreamInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Extra codec configuration in a stream of mp4 boxes. It is only applicable
|
||||||
|
// to mp4 container only. It is needed by some codecs, e.g. Dolby Vision.
|
||||||
|
std::vector<uint8_t> extra_config_;
|
||||||
H26xStreamFormat h26x_stream_format_;
|
H26xStreamFormat h26x_stream_format_;
|
||||||
uint16_t width_;
|
uint16_t width_;
|
||||||
uint16_t height_;
|
uint16_t height_;
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
'avc_decoder_configuration_record.h',
|
'avc_decoder_configuration_record.h',
|
||||||
'decoder_configuration_record.cc',
|
'decoder_configuration_record.cc',
|
||||||
'decoder_configuration_record.h',
|
'decoder_configuration_record.h',
|
||||||
|
'dovi_decoder_configuration_record.cc',
|
||||||
|
'dovi_decoder_configuration_record.h',
|
||||||
'ec3_audio_util.cc',
|
'ec3_audio_util.cc',
|
||||||
'ec3_audio_util.h',
|
'ec3_audio_util.h',
|
||||||
'es_descriptor.cc',
|
'es_descriptor.cc',
|
||||||
|
@ -73,6 +75,7 @@
|
||||||
'av1_codec_configuration_record_unittest.cc',
|
'av1_codec_configuration_record_unittest.cc',
|
||||||
'av1_parser_unittest.cc',
|
'av1_parser_unittest.cc',
|
||||||
'avc_decoder_configuration_record_unittest.cc',
|
'avc_decoder_configuration_record_unittest.cc',
|
||||||
|
'dovi_decoder_configuration_record_unittest.cc',
|
||||||
'ec3_audio_util_unittest.cc',
|
'ec3_audio_util_unittest.cc',
|
||||||
'es_descriptor_unittest.cc',
|
'es_descriptor_unittest.cc',
|
||||||
'h264_byte_to_unit_stream_converter_unittest.cc',
|
'h264_byte_to_unit_stream_converter_unittest.cc',
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
// 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/codecs/dovi_decoder_configuration_record.h"
|
||||||
|
|
||||||
|
#include "packager/base/strings/stringprintf.h"
|
||||||
|
#include "packager/media/base/bit_reader.h"
|
||||||
|
#include "packager/media/base/rcheck.h"
|
||||||
|
|
||||||
|
namespace shaka {
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
bool DOVIDecoderConfigurationRecord::Parse(const std::vector<uint8_t>& data) {
|
||||||
|
BitReader reader(data.data(), data.size());
|
||||||
|
|
||||||
|
// Dolby Vision Streams Within the ISO Base Media File Format Version 2.0:
|
||||||
|
// https://www.dolby.com/us/en/technologies/dolby-vision/dolby-vision-bitstreams-within-the-iso-base-media-file-format-v2.0.pdf
|
||||||
|
uint8_t major_version = 0;
|
||||||
|
uint8_t minor_version = 0;
|
||||||
|
RCHECK(reader.ReadBits(8, &major_version) && major_version == 1 &&
|
||||||
|
reader.ReadBits(8, &minor_version) && minor_version == 0 &&
|
||||||
|
reader.ReadBits(7, &profile_) && reader.ReadBits(6, &level_));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DOVIDecoderConfigurationRecord::GetCodecString(
|
||||||
|
FourCC codec_fourcc) const {
|
||||||
|
// Dolby Vision Streams within the HTTP Live Streaming format Version 2.0:
|
||||||
|
// https://www.dolby.com/us/en/technologies/dolby-vision/dolby-vision-streams-within-the-http-live-streaming-format-v2.0.pdf
|
||||||
|
return base::StringPrintf(
|
||||||
|
"%s.%02d.%02d", FourCCToString(codec_fourcc).c_str(), profile_, level_);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
} // namespace shaka
|
|
@ -0,0 +1,51 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
#ifndef PACKAGER_MEDIA_CODECS_DOVI_DECODER_CONFIGURATION_RECORD_H_
|
||||||
|
#define PACKAGER_MEDIA_CODECS_DOVI_DECODER_CONFIGURATION_RECORD_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "packager/media/base/fourccs.h"
|
||||||
|
|
||||||
|
namespace shaka {
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
/// Class for parsing Dolby Vision decoder configuration record.
|
||||||
|
// Implemented according to Dolby Vision Streams Within the ISO Base Media File
|
||||||
|
// Format Version 2.0:
|
||||||
|
// https://www.dolby.com/us/en/technologies/dolby-vision/dolby-vision-bitstreams-within-the-iso-base-media-file-format-v2.0.pdf
|
||||||
|
// and Dolby Vision Streams within the HTTP Live Streaming format Version 2.0:
|
||||||
|
// https://www.dolby.com/us/en/technologies/dolby-vision/dolby-vision-streams-within-the-http-live-streaming-format-v2.0.pdf
|
||||||
|
class DOVIDecoderConfigurationRecord {
|
||||||
|
public:
|
||||||
|
DOVIDecoderConfigurationRecord() = default;
|
||||||
|
~DOVIDecoderConfigurationRecord() = default;
|
||||||
|
|
||||||
|
/// Parses input to extract decoder configuration record.
|
||||||
|
/// @return false if there are parsing errors.
|
||||||
|
bool Parse(const std::vector<uint8_t>& data);
|
||||||
|
|
||||||
|
/// @return The codec string in the format defined by RFC6381. It is used in
|
||||||
|
/// DASH and HLS manifests.
|
||||||
|
std::string GetCodecString(FourCC codec_fourcc) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DOVIDecoderConfigurationRecord(const DOVIDecoderConfigurationRecord&) =
|
||||||
|
delete;
|
||||||
|
DOVIDecoderConfigurationRecord& operator=(
|
||||||
|
const DOVIDecoderConfigurationRecord&) = delete;
|
||||||
|
|
||||||
|
uint8_t profile_ = 0;
|
||||||
|
uint8_t level_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
} // namespace shaka
|
||||||
|
|
||||||
|
#endif // PACKAGER_MEDIA_CODECS_DOVI_DECODER_CONFIGURATION_RECORD_H_
|
|
@ -0,0 +1,48 @@
|
||||||
|
// 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/codecs/dovi_decoder_configuration_record.h"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "packager/base/macros.h"
|
||||||
|
|
||||||
|
namespace shaka {
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
TEST(DOVIDecoderConfigurationRecordTest, Success) {
|
||||||
|
const std::vector<uint8_t> dovi_config_data = {
|
||||||
|
0x01, // Major Version
|
||||||
|
0x00, // Minor Version
|
||||||
|
0x05 << 1, // Profile
|
||||||
|
0x08 << 3, // Level
|
||||||
|
// Other data we do not care.
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
|
||||||
|
DOVIDecoderConfigurationRecord dovi_config;
|
||||||
|
ASSERT_TRUE(dovi_config.Parse(dovi_config_data));
|
||||||
|
|
||||||
|
EXPECT_EQ("dvh1.05.08", dovi_config.GetCodecString(FOURCC_dvh1));
|
||||||
|
EXPECT_EQ("dvhe.05.08", dovi_config.GetCodecString(FOURCC_dvhe));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DOVIDecoderConfigurationRecordTest, FailOnIncorectVersion) {
|
||||||
|
const std::vector<uint8_t> dovi_config_data = {0x02, 0x00, 0x0A, 0x04};
|
||||||
|
|
||||||
|
DOVIDecoderConfigurationRecord dovi_config;
|
||||||
|
ASSERT_FALSE(dovi_config.Parse(dovi_config_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DOVIDecoderConfigurationRecordTest, FailOnInsufficientData) {
|
||||||
|
const std::vector<uint8_t> dovi_config_data = {0x01, 0x00, 0x0A};
|
||||||
|
|
||||||
|
DOVIDecoderConfigurationRecord dovi_config;
|
||||||
|
ASSERT_FALSE(dovi_config.Parse(dovi_config_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
} // namespace shaka
|
|
@ -142,6 +142,7 @@ Status SubsampleGenerator::Initialize(FourCC protection_scheme,
|
||||||
header_parser_.reset(new H264VideoSliceHeaderParser);
|
header_parser_.reset(new H264VideoSliceHeaderParser);
|
||||||
break;
|
break;
|
||||||
case kCodecH265:
|
case kCodecH265:
|
||||||
|
case kCodecH265DolbyVision:
|
||||||
header_parser_.reset(new H265VideoSliceHeaderParser);
|
header_parser_.reset(new H265VideoSliceHeaderParser);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -220,6 +221,7 @@ Status SubsampleGenerator::GenerateSubsamples(
|
||||||
case kCodecH264:
|
case kCodecH264:
|
||||||
FALLTHROUGH_INTENDED;
|
FALLTHROUGH_INTENDED;
|
||||||
case kCodecH265:
|
case kCodecH265:
|
||||||
|
case kCodecH265DolbyVision:
|
||||||
return GenerateSubsamplesFromH26xFrame(frame, frame_size, subsamples);
|
return GenerateSubsamplesFromH26xFrame(frame, frame_size, subsamples);
|
||||||
case kCodecVP9:
|
case kCodecVP9:
|
||||||
if (vp9_subsample_encryption_)
|
if (vp9_subsample_encryption_)
|
||||||
|
@ -300,7 +302,8 @@ Status SubsampleGenerator::GenerateSubsamplesFromH26xFrame(
|
||||||
SubsampleOrganizer subsample_organizer(align_protected_data_, subsamples);
|
SubsampleOrganizer subsample_organizer(align_protected_data_, subsamples);
|
||||||
|
|
||||||
const Nalu::CodecType nalu_type =
|
const Nalu::CodecType nalu_type =
|
||||||
(codec_ == kCodecH265) ? Nalu::kH265 : Nalu::kH264;
|
(codec_ == kCodecH265 || codec_ == kCodecH265DolbyVision) ? Nalu::kH265
|
||||||
|
: Nalu::kH264;
|
||||||
NaluReader reader(nalu_type, nalu_length_size_, frame, frame_size);
|
NaluReader reader(nalu_type, nalu_length_size_, frame, frame_size);
|
||||||
|
|
||||||
Nalu nalu;
|
Nalu nalu;
|
||||||
|
|
|
@ -36,6 +36,7 @@ const uint16_t kVideoDepth = 0x0018;
|
||||||
const uint32_t kCompressorNameSize = 32u;
|
const uint32_t kCompressorNameSize = 32u;
|
||||||
const char kAv1CompressorName[] = "\012AOM Coding";
|
const char kAv1CompressorName[] = "\012AOM Coding";
|
||||||
const char kAvcCompressorName[] = "\012AVC Coding";
|
const char kAvcCompressorName[] = "\012AVC Coding";
|
||||||
|
const char kDolbyVisionCompressorName[] = "\013DOVI Coding";
|
||||||
const char kHevcCompressorName[] = "\013HEVC Coding";
|
const char kHevcCompressorName[] = "\013HEVC Coding";
|
||||||
const char kVpcCompressorName[] = "\012VPC Coding";
|
const char kVpcCompressorName[] = "\012VPC Coding";
|
||||||
|
|
||||||
|
@ -1488,6 +1489,11 @@ bool VideoSampleEntry::ReadWriteInternal(BoxBuffer* buffer) {
|
||||||
compressor_name.assign(std::begin(kAvcCompressorName),
|
compressor_name.assign(std::begin(kAvcCompressorName),
|
||||||
std::end(kAvcCompressorName));
|
std::end(kAvcCompressorName));
|
||||||
break;
|
break;
|
||||||
|
case FOURCC_dvh1:
|
||||||
|
case FOURCC_dvhe:
|
||||||
|
compressor_name.assign(std::begin(kDolbyVisionCompressorName),
|
||||||
|
std::end(kDolbyVisionCompressorName));
|
||||||
|
break;
|
||||||
case FOURCC_hev1:
|
case FOURCC_hev1:
|
||||||
case FOURCC_hvc1:
|
case FOURCC_hvc1:
|
||||||
compressor_name.assign(std::begin(kHevcCompressorName),
|
compressor_name.assign(std::begin(kHevcCompressorName),
|
||||||
|
@ -1544,6 +1550,27 @@ bool VideoSampleEntry::ReadWriteInternal(BoxBuffer* buffer) {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
RCHECK(buffer->ReadWriteChild(&codec_configuration));
|
RCHECK(buffer->ReadWriteChild(&codec_configuration));
|
||||||
|
|
||||||
|
if (buffer->Reading()) {
|
||||||
|
extra_codec_configs.clear();
|
||||||
|
// Handle Dolby Vision boxes.
|
||||||
|
const bool is_hevc =
|
||||||
|
actual_format == FOURCC_dvhe || actual_format == FOURCC_dvh1 ||
|
||||||
|
actual_format == FOURCC_hev1 || actual_format == FOURCC_hvc1;
|
||||||
|
if (is_hevc) {
|
||||||
|
for (FourCC fourcc : {FOURCC_dvcC, FOURCC_dvvC, FOURCC_hvcE}) {
|
||||||
|
CodecConfiguration dv_box;
|
||||||
|
dv_box.box_type = fourcc;
|
||||||
|
RCHECK(buffer->TryReadWriteChild(&dv_box));
|
||||||
|
if (!dv_box.data.empty())
|
||||||
|
extra_codec_configs.push_back(std::move(dv_box));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (CodecConfiguration& extra_codec_config : extra_codec_configs)
|
||||||
|
RCHECK(buffer->ReadWriteChild(&extra_codec_config));
|
||||||
|
}
|
||||||
|
|
||||||
RCHECK(buffer->TryReadWriteChild(&pixel_aspect));
|
RCHECK(buffer->TryReadWriteChild(&pixel_aspect));
|
||||||
|
|
||||||
// Somehow Edge does not support having sinf box before codec_configuration,
|
// Somehow Edge does not support having sinf box before codec_configuration,
|
||||||
|
@ -1562,12 +1589,15 @@ size_t VideoSampleEntry::ComputeSizeInternal() {
|
||||||
return 0;
|
return 0;
|
||||||
codec_configuration.box_type = GetCodecConfigurationBoxType(actual_format);
|
codec_configuration.box_type = GetCodecConfigurationBoxType(actual_format);
|
||||||
DCHECK_NE(codec_configuration.box_type, FOURCC_NULL);
|
DCHECK_NE(codec_configuration.box_type, FOURCC_NULL);
|
||||||
return HeaderSize() + sizeof(data_reference_index) + sizeof(width) +
|
size_t size = HeaderSize() + sizeof(data_reference_index) + sizeof(width) +
|
||||||
sizeof(height) + sizeof(kVideoResolution) * 2 +
|
sizeof(height) + sizeof(kVideoResolution) * 2 +
|
||||||
sizeof(kVideoFrameCount) + sizeof(kVideoDepth) +
|
sizeof(kVideoFrameCount) + sizeof(kVideoDepth) +
|
||||||
pixel_aspect.ComputeSize() + sinf.ComputeSize() +
|
pixel_aspect.ComputeSize() + sinf.ComputeSize() +
|
||||||
codec_configuration.ComputeSize() + kCompressorNameSize + 6 + 4 + 16 +
|
codec_configuration.ComputeSize() + kCompressorNameSize + 6 +
|
||||||
2; // 6 + 4 bytes reserved, 16 + 2 bytes predefined.
|
4 + 16 + 2; // 6 + 4 bytes reserved, 16 + 2 bytes predefined.
|
||||||
|
for (CodecConfiguration& codec_config : extra_codec_configs)
|
||||||
|
size += codec_config.ComputeSize();
|
||||||
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
FourCC VideoSampleEntry::GetCodecConfigurationBoxType(FourCC format) const {
|
FourCC VideoSampleEntry::GetCodecConfigurationBoxType(FourCC format) const {
|
||||||
|
@ -1577,6 +1607,8 @@ FourCC VideoSampleEntry::GetCodecConfigurationBoxType(FourCC format) const {
|
||||||
case FOURCC_avc1:
|
case FOURCC_avc1:
|
||||||
case FOURCC_avc3:
|
case FOURCC_avc3:
|
||||||
return FOURCC_avcC;
|
return FOURCC_avcC;
|
||||||
|
case FOURCC_dvh1:
|
||||||
|
case FOURCC_dvhe:
|
||||||
case FOURCC_hev1:
|
case FOURCC_hev1:
|
||||||
case FOURCC_hvc1:
|
case FOURCC_hvc1:
|
||||||
return FOURCC_hvcC;
|
return FOURCC_hvcC;
|
||||||
|
@ -1589,6 +1621,33 @@ FourCC VideoSampleEntry::GetCodecConfigurationBoxType(FourCC format) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> VideoSampleEntry::ExtraCodecConfigsAsVector() const {
|
||||||
|
BufferWriter buffer;
|
||||||
|
for (CodecConfiguration codec_config : extra_codec_configs)
|
||||||
|
codec_config.Write(&buffer);
|
||||||
|
return std::vector<uint8_t>(buffer.Buffer(), buffer.Buffer() + buffer.Size());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VideoSampleEntry::ParseExtraCodecConfigsVector(
|
||||||
|
const std::vector<uint8_t>& data) {
|
||||||
|
extra_codec_configs.clear();
|
||||||
|
size_t pos = 0;
|
||||||
|
while (pos < data.size()) {
|
||||||
|
bool err = false;
|
||||||
|
std::unique_ptr<BoxReader> box_reader(
|
||||||
|
BoxReader::ReadBox(data.data() + pos, data.size() - pos, &err));
|
||||||
|
RCHECK(!err && box_reader);
|
||||||
|
|
||||||
|
CodecConfiguration codec_config;
|
||||||
|
codec_config.box_type = box_reader->type();
|
||||||
|
RCHECK(codec_config.Parse(box_reader.get()));
|
||||||
|
extra_codec_configs.push_back(std::move(codec_config));
|
||||||
|
|
||||||
|
pos += box_reader->pos();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
ElementaryStreamDescriptor::ElementaryStreamDescriptor() = default;
|
ElementaryStreamDescriptor::ElementaryStreamDescriptor() = default;
|
||||||
ElementaryStreamDescriptor::~ElementaryStreamDescriptor() = default;
|
ElementaryStreamDescriptor::~ElementaryStreamDescriptor() = default;
|
||||||
|
|
||||||
|
|
|
@ -284,6 +284,11 @@ struct VideoSampleEntry : Box {
|
||||||
// Returns the box type of codec configuration box from video format.
|
// Returns the box type of codec configuration box from video format.
|
||||||
FourCC GetCodecConfigurationBoxType(FourCC format) const;
|
FourCC GetCodecConfigurationBoxType(FourCC format) const;
|
||||||
|
|
||||||
|
// Convert |extra_codec_configs| to vector.
|
||||||
|
std::vector<uint8_t> ExtraCodecConfigsAsVector() const;
|
||||||
|
// Parse |extra_codec_configs| from vector.
|
||||||
|
bool ParseExtraCodecConfigsVector(const std::vector<uint8_t>& data);
|
||||||
|
|
||||||
FourCC format = FOURCC_NULL;
|
FourCC format = FOURCC_NULL;
|
||||||
// data_reference_index is 1-based and "dref" box is mandatory so it is
|
// data_reference_index is 1-based and "dref" box is mandatory so it is
|
||||||
// always present.
|
// always present.
|
||||||
|
@ -294,6 +299,9 @@ struct VideoSampleEntry : Box {
|
||||||
PixelAspectRatio pixel_aspect;
|
PixelAspectRatio pixel_aspect;
|
||||||
ProtectionSchemeInfo sinf;
|
ProtectionSchemeInfo sinf;
|
||||||
CodecConfiguration codec_configuration;
|
CodecConfiguration codec_configuration;
|
||||||
|
// Some codecs, e.g. Dolby Vision, have extra codec configuration boxes that
|
||||||
|
// need to be propagated to muxers.
|
||||||
|
std::vector<CodecConfiguration> extra_codec_configs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ElementaryStreamDescriptor : FullBox {
|
struct ElementaryStreamDescriptor : FullBox {
|
||||||
|
|
|
@ -355,19 +355,30 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
||||||
kCodecConfigurationData + arraysize(kCodecConfigurationData));
|
kCodecConfigurationData + arraysize(kCodecConfigurationData));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fill(VideoSampleEntry* encv) {
|
void Fill(VideoSampleEntry* entry) {
|
||||||
encv->format = FOURCC_encv;
|
entry->format = FOURCC_encv;
|
||||||
encv->data_reference_index = 1;
|
entry->data_reference_index = 1;
|
||||||
encv->width = 800;
|
entry->width = 800;
|
||||||
encv->height = 600;
|
entry->height = 600;
|
||||||
Fill(&encv->pixel_aspect);
|
Fill(&entry->pixel_aspect);
|
||||||
Fill(&encv->sinf);
|
Fill(&entry->sinf);
|
||||||
Fill(&encv->codec_configuration);
|
Fill(&entry->codec_configuration);
|
||||||
|
|
||||||
|
const uint8_t kExtraCodecConfigData[] = {0x01, 0x02, 0x03, 0x04};
|
||||||
|
CodecConfiguration extra_codec_config;
|
||||||
|
extra_codec_config.data.assign(std::begin(kExtraCodecConfigData),
|
||||||
|
std::end(kExtraCodecConfigData));
|
||||||
|
for (FourCC fourcc : {FOURCC_dvcC, FOURCC_dvvC, FOURCC_hvcE}) {
|
||||||
|
extra_codec_config.box_type = fourcc;
|
||||||
|
entry->extra_codec_configs.push_back(extra_codec_config);
|
||||||
|
// Increment it so the boxes have different data.
|
||||||
|
extra_codec_config.data[0]++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Modify(VideoSampleEntry* encv) {
|
void Modify(VideoSampleEntry* entry) {
|
||||||
encv->height += 600;
|
entry->height += 600;
|
||||||
Modify(&encv->codec_configuration);
|
Modify(&entry->codec_configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fill(ElementaryStreamDescriptor* esds) {
|
void Fill(ElementaryStreamDescriptor* esds) {
|
||||||
|
@ -1261,6 +1272,24 @@ TEST_F(BoxDefinitionsTest, FlacSampleEntry) {
|
||||||
ASSERT_EQ(entry, entry_readback);
|
ASSERT_EQ(entry, entry_readback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(BoxDefinitionsTest, SampleEntryExtraCodecConfigs) {
|
||||||
|
VideoSampleEntry entry;
|
||||||
|
Fill(&entry);
|
||||||
|
|
||||||
|
const uint8_t kExpectedVector[] = {
|
||||||
|
0, 0, 0, 12, 'd', 'v', 'c', 'C', 1, 2, 3, 4,
|
||||||
|
0, 0, 0, 12, 'd', 'v', 'v', 'C', 2, 2, 3, 4,
|
||||||
|
0, 0, 0, 12, 'h', 'v', 'c', 'E', 3, 2, 3, 4,
|
||||||
|
};
|
||||||
|
const std::vector<uint8_t> expected_vector(std::begin(kExpectedVector),
|
||||||
|
std::end(kExpectedVector));
|
||||||
|
EXPECT_EQ(expected_vector, entry.ExtraCodecConfigsAsVector());
|
||||||
|
|
||||||
|
VideoSampleEntry new_entry;
|
||||||
|
ASSERT_TRUE(new_entry.ParseExtraCodecConfigsVector(expected_vector));
|
||||||
|
EXPECT_EQ(entry.extra_codec_configs, new_entry.extra_codec_configs);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(BoxDefinitionsTest, CompactSampleSize_FieldSize16) {
|
TEST_F(BoxDefinitionsTest, CompactSampleSize_FieldSize16) {
|
||||||
CompactSampleSize stz2;
|
CompactSampleSize stz2;
|
||||||
stz2.field_size = 16;
|
stz2.field_size = 16;
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "packager/media/codecs/ac3_audio_util.h"
|
#include "packager/media/codecs/ac3_audio_util.h"
|
||||||
#include "packager/media/codecs/av1_codec_configuration_record.h"
|
#include "packager/media/codecs/av1_codec_configuration_record.h"
|
||||||
#include "packager/media/codecs/avc_decoder_configuration_record.h"
|
#include "packager/media/codecs/avc_decoder_configuration_record.h"
|
||||||
|
#include "packager/media/codecs/dovi_decoder_configuration_record.h"
|
||||||
#include "packager/media/codecs/ec3_audio_util.h"
|
#include "packager/media/codecs/ec3_audio_util.h"
|
||||||
#include "packager/media/codecs/es_descriptor.h"
|
#include "packager/media/codecs/es_descriptor.h"
|
||||||
#include "packager/media/codecs/hevc_decoder_configuration_record.h"
|
#include "packager/media/codecs/hevc_decoder_configuration_record.h"
|
||||||
|
@ -46,13 +47,13 @@ uint64_t Rescale(uint64_t time_in_old_scale,
|
||||||
H26xStreamFormat GetH26xStreamFormat(FourCC fourcc) {
|
H26xStreamFormat GetH26xStreamFormat(FourCC fourcc) {
|
||||||
switch (fourcc) {
|
switch (fourcc) {
|
||||||
case FOURCC_avc1:
|
case FOURCC_avc1:
|
||||||
return H26xStreamFormat::kNalUnitStreamWithoutParameterSetNalus;
|
case FOURCC_dvh1:
|
||||||
case FOURCC_avc3:
|
|
||||||
return H26xStreamFormat::kNalUnitStreamWithParameterSetNalus;
|
|
||||||
case FOURCC_hev1:
|
|
||||||
return H26xStreamFormat::kNalUnitStreamWithParameterSetNalus;
|
|
||||||
case FOURCC_hvc1:
|
case FOURCC_hvc1:
|
||||||
return H26xStreamFormat::kNalUnitStreamWithoutParameterSetNalus;
|
return H26xStreamFormat::kNalUnitStreamWithoutParameterSetNalus;
|
||||||
|
case FOURCC_avc3:
|
||||||
|
case FOURCC_dvhe:
|
||||||
|
case FOURCC_hev1:
|
||||||
|
return H26xStreamFormat::kNalUnitStreamWithParameterSetNalus;
|
||||||
default:
|
default:
|
||||||
return H26xStreamFormat::kUnSpecified;
|
return H26xStreamFormat::kUnSpecified;
|
||||||
}
|
}
|
||||||
|
@ -65,6 +66,9 @@ Codec FourCCToCodec(FourCC fourcc) {
|
||||||
case FOURCC_avc1:
|
case FOURCC_avc1:
|
||||||
case FOURCC_avc3:
|
case FOURCC_avc3:
|
||||||
return kCodecH264;
|
return kCodecH264;
|
||||||
|
case FOURCC_dvh1:
|
||||||
|
case FOURCC_dvhe:
|
||||||
|
return kCodecH265DolbyVision;
|
||||||
case FOURCC_hev1:
|
case FOURCC_hev1:
|
||||||
case FOURCC_hvc1:
|
case FOURCC_hvc1:
|
||||||
return kCodecH265;
|
return kCodecH265;
|
||||||
|
@ -115,6 +119,36 @@ Codec ObjectTypeToCodec(ObjectType object_type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> GetDOVIDecoderConfig(
|
||||||
|
const std::vector<CodecConfiguration>& configs) {
|
||||||
|
for (const CodecConfiguration& config : configs) {
|
||||||
|
if (config.box_type == FOURCC_dvcC || config.box_type == FOURCC_dvvC) {
|
||||||
|
return config.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::vector<uint8_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UpdateCodecStringForDolbyVision(
|
||||||
|
FourCC actual_format,
|
||||||
|
const std::vector<CodecConfiguration>& configs,
|
||||||
|
std::string* codec_string) {
|
||||||
|
DOVIDecoderConfigurationRecord dovi_config;
|
||||||
|
if (!dovi_config.Parse(GetDOVIDecoderConfig(configs))) {
|
||||||
|
LOG(ERROR) << "Failed to parse Dolby Vision decoder "
|
||||||
|
"configuration record.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (actual_format == FOURCC_dvh1 || actual_format == FOURCC_dvhe) {
|
||||||
|
// Non-Backward compatibility mode. Replace the code string with
|
||||||
|
// Dolby Vision only.
|
||||||
|
*codec_string = dovi_config.GetCodecString(actual_format);
|
||||||
|
} else {
|
||||||
|
// TODO(kqyang): Support backward compatible signaling.
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const uint64_t kNanosecondsPerSecond = 1000000000ull;
|
const uint64_t kNanosecondsPerSecond = 1000000000ull;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -579,6 +613,8 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FOURCC_dvh1:
|
||||||
|
case FOURCC_dvhe:
|
||||||
case FOURCC_hev1:
|
case FOURCC_hev1:
|
||||||
case FOURCC_hvc1: {
|
case FOURCC_hvc1: {
|
||||||
HEVCDecoderConfigurationRecord hevc_config;
|
HEVCDecoderConfigurationRecord hevc_config;
|
||||||
|
@ -588,6 +624,13 @@ 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();
|
||||||
|
|
||||||
|
if (!entry.extra_codec_configs.empty()) {
|
||||||
|
if (!UpdateCodecStringForDolbyVision(
|
||||||
|
actual_format, entry.extra_codec_configs, &codec_string)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FOURCC_vp08:
|
case FOURCC_vp08:
|
||||||
|
@ -631,6 +674,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
||||||
coded_width, coded_height, pixel_width, pixel_height,
|
coded_width, coded_height, pixel_width, pixel_height,
|
||||||
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());
|
||||||
|
|
||||||
// Set pssh raw data if it has.
|
// Set pssh raw data if it has.
|
||||||
if (moov_->pssh.size() > 0) {
|
if (moov_->pssh.size() > 0) {
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "packager/base/strings/string_number_conversions.h"
|
||||||
#include "packager/base/time/clock.h"
|
#include "packager/base/time/clock.h"
|
||||||
#include "packager/base/time/time.h"
|
#include "packager/base/time/time.h"
|
||||||
#include "packager/file/file.h"
|
#include "packager/file/file.h"
|
||||||
|
@ -56,6 +57,11 @@ FourCC CodecToFourCC(Codec codec, H26xStreamFormat h26x_stream_format) {
|
||||||
H26xStreamFormat::kNalUnitStreamWithParameterSetNalus
|
H26xStreamFormat::kNalUnitStreamWithParameterSetNalus
|
||||||
? FOURCC_hev1
|
? FOURCC_hev1
|
||||||
: FOURCC_hvc1;
|
: FOURCC_hvc1;
|
||||||
|
case kCodecH265DolbyVision:
|
||||||
|
return h26x_stream_format ==
|
||||||
|
H26xStreamFormat::kNalUnitStreamWithParameterSetNalus
|
||||||
|
? FOURCC_dvhe
|
||||||
|
: FOURCC_dvh1;
|
||||||
case kCodecVP8:
|
case kCodecVP8:
|
||||||
return FOURCC_vp08;
|
return FOURCC_vp08;
|
||||||
case kCodecVP9:
|
case kCodecVP9:
|
||||||
|
@ -410,6 +416,12 @@ bool MP4Muxer::GenerateVideoTrak(const VideoStreamInfo* video_info,
|
||||||
video.width = video_info->width();
|
video.width = video_info->width();
|
||||||
video.height = video_info->height();
|
video.height = video_info->height();
|
||||||
video.codec_configuration.data = video_info->codec_config();
|
video.codec_configuration.data = video_info->codec_config();
|
||||||
|
if (!video.ParseExtraCodecConfigsVector(video_info->extra_config())) {
|
||||||
|
LOG(ERROR) << "Malformed extra codec configs: "
|
||||||
|
<< base::HexEncode(video_info->extra_config().data(),
|
||||||
|
video_info->extra_config().size());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (pixel_width != 1 || pixel_height != 1) {
|
if (pixel_width != 1 || pixel_height != 1) {
|
||||||
video.pixel_aspect.h_spacing = pixel_width;
|
video.pixel_aspect.h_spacing = pixel_width;
|
||||||
video.pixel_aspect.v_spacing = pixel_height;
|
video.pixel_aspect.v_spacing = pixel_height;
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue