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:
KongQun Yang 2019-09-20 18:02:13 -07:00
parent 00fde07bf7
commit 8029004c6b
20 changed files with 382 additions and 23 deletions

View File

@ -1203,6 +1203,15 @@ class PackagerFunctionalTest(PackagerAppTest):
self.assertPackageSuccess(streams, flags) self.assertPackageSuccess(streams, flags)
self._CheckTestResults('hevc-with-encryption', verify_decryption=True) self._CheckTestResults('hevc-with-encryption', verify_decryption=True)
def 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',

View File

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

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="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>

View File

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

View File

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

View File

@ -32,6 +32,7 @@ enum Codec {
kCodecAV1 = kCodecVideo, kCodecAV1 = kCodecVideo,
kCodecH264, kCodecH264,
kCodecH265, kCodecH265,
kCodecH265DolbyVision,
kCodecVP8, kCodecVP8,
kCodecVP9, kCodecVP9,
kCodecVideoMaxPlusOne, kCodecVideoMaxPlusOne,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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