Add support for AV1

Implemented per AV1 Codec ISO Media File Format Binding at
  https://aomediacodec.github.io/av1-isobmff/
And AOM AV1 codec mapping in Matroska/WebM at
  https://github.com/Matroska-Org/matroska-specification/blob/av1-mappin/codec/av1.md

Note that AV1 specific boxes are not supported in this CL, i.e.
AV1 Forward Key Frame sample group entry 'av1f', AV1 Multi-Frame
sample group entry 'av1m' etc are not supported. These boxes are optional.
We will add support later if they are useful to the clients / players.

Encryption is not supported yet.

Issue #453.

Change-Id: I630432d0a9bf82d263ffaf40e57f67fc65eee902
This commit is contained in:
KongQun Yang 2018-08-24 19:04:00 -07:00
parent 86b10b6316
commit 0709db4bbc
31 changed files with 467 additions and 72 deletions

View File

@ -849,6 +849,33 @@ class PackagerFunctionalTest(PackagerAppTest):
self._GetFlags(output_dash=True)) self._GetFlags(output_dash=True))
self._CheckTestResults('vorbis-webm') self._CheckTestResults('vorbis-webm')
def testAv1Mp4(self):
self.assertPackageSuccess(
self._GetStreams(['video'],
output_format='mp4',
test_files=['bear-av1.mp4']),
self._GetFlags(output_dash=True, output_hls=True)
)
self._CheckTestResults('av1-mp4')
def testAv1Mp4ToWebM(self):
self.assertPackageSuccess(
self._GetStreams(['video'],
output_format='webm',
test_files=['bear-av1.mp4']),
self._GetFlags(output_dash=True, output_hls=True)
)
self._CheckTestResults('av1-mp4-to-webm')
def testAv1WebM(self):
self.assertPackageSuccess(
self._GetStreams(['video'],
output_format='mp4',
test_files=['bear-av1.webm']),
self._GetFlags(output_dash=True, output_hls=True)
)
self._CheckTestResults('av1-webm')
def testEncryption(self): def testEncryption(self):
self.assertPackageSuccess( self.assertPackageSuccess(
self._GetStreams(['audio', 'video']), self._GetStreams(['audio', 'video']),

Binary file not shown.

View File

@ -0,0 +1,5 @@
#EXTM3U
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-STREAM-INF:BANDWIDTH=69160,AVERAGE-BANDWIDTH=69160,CODECS="av01.0.00M.08.0.110",RESOLUTION=320x240
stream_0.m3u8

View File

@ -0,0 +1,14 @@
<?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" xmlns:xlink="http://www.w3.org/1999/xlink" 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.7360665798187256S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="320" height="240" frameRate="30000/1001" subsegmentAlignment="true" par="4:3">
<Representation id="0" bandwidth="69160" codecs="av01.0.00M.08.0.110" mimeType="video/webm" sar="1:1">
<BaseURL>bear-av1-video.webm</BaseURL>
<SegmentBase indexRange="314-332" timescale="30000">
<Initialization range="0-313"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>

View File

@ -0,0 +1,10 @@
#EXTM3U
#EXT-X-VERSION:6
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-TARGETDURATION:3
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI="bear-av1-video.webm",BYTERANGE="314@0"
#EXTINF:2.736,
#EXT-X-BYTERANGE:23653@333
bear-av1-video.webm
#EXT-X-ENDLIST

Binary file not shown.

View File

@ -0,0 +1,5 @@
#EXTM3U
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-STREAM-INF:BANDWIDTH=69777,AVERAGE-BANDWIDTH=69777,CODECS="av01.0.00M.08.0.110",RESOLUTION=320x240
stream_0.m3u8

View File

@ -0,0 +1,14 @@
<?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" xmlns:xlink="http://www.w3.org/1999/xlink" 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.7360665798187256S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="320" height="240" frameRate="30000/1001" subsegmentAlignment="true" par="4:3">
<Representation id="0" bandwidth="69777" codecs="av01.0.00M.08.0.110" mimeType="video/mp4" sar="1:1">
<BaseURL>bear-av1-video.mp4</BaseURL>
<SegmentBase indexRange="798-841" timescale="30000">
<Initialization range="0-797"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>

View File

@ -0,0 +1,10 @@
#EXTM3U
#EXT-X-VERSION:6
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-TARGETDURATION:3
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI="bear-av1-video.mp4",BYTERANGE="798@0"
#EXTINF:2.736,
#EXT-X-BYTERANGE:23864@842
bear-av1-video.mp4
#EXT-X-ENDLIST

Binary file not shown.

View File

@ -0,0 +1,5 @@
#EXTM3U
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-STREAM-INF:BANDWIDTH=70752,AVERAGE-BANDWIDTH=70752,CODECS="av01.0.00M.08.0.110",RESOLUTION=320x240
stream_0.m3u8

View File

@ -0,0 +1,14 @@
<?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" xmlns:xlink="http://www.w3.org/1999/xlink" 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.734999895095825S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="320" height="240" frameRate="1000000/33000" subsegmentAlignment="true" par="4:3">
<Representation id="0" bandwidth="70752" codecs="av01.0.00M.08.0.110" mimeType="video/mp4" sar="1:1">
<BaseURL>bear-av1-video.mp4</BaseURL>
<SegmentBase indexRange="798-841" timescale="1000000">
<Initialization range="0-797"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>

View File

@ -0,0 +1,10 @@
#EXTM3U
#EXT-X-VERSION:6
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-TARGETDURATION:3
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI="bear-av1-video.mp4",BYTERANGE="798@0"
#EXTINF:2.735,
#EXT-X-BYTERANGE:24188@842
bear-av1-video.mp4
#EXT-X-ENDLIST

View File

@ -22,6 +22,8 @@ enum FourCC : uint32_t {
FOURCC_ac_3 = 0x61632d33, // "ac-3" FOURCC_ac_3 = 0x61632d33, // "ac-3"
FOURCC_ac3d = 0x61633364, FOURCC_ac3d = 0x61633364,
FOURCC_apad = 0x61706164, FOURCC_apad = 0x61706164,
FOURCC_av01 = 0x61763031,
FOURCC_av1C = 0x61763143,
FOURCC_avc1 = 0x61766331, FOURCC_avc1 = 0x61766331,
FOURCC_avc3 = 0x61766333, FOURCC_avc3 = 0x61766333,
FOURCC_avcC = 0x61766343, FOURCC_avcC = 0x61766343,
@ -139,7 +141,6 @@ enum FourCC : uint32_t {
FOURCC_vmhd = 0x766d6864, FOURCC_vmhd = 0x766d6864,
FOURCC_vp08 = 0x76703038, FOURCC_vp08 = 0x76703038,
FOURCC_vp09 = 0x76703039, FOURCC_vp09 = 0x76703039,
FOURCC_vp10 = 0x76703130,
FOURCC_vpcC = 0x76706343, FOURCC_vpcC = 0x76706343,
FOURCC_vsid = 0x76736964, FOURCC_vsid = 0x76736964,
FOURCC_vttC = 0x76747443, FOURCC_vttC = 0x76747443,

View File

@ -29,11 +29,11 @@ enum Codec {
kUnknownCodec = 0, kUnknownCodec = 0,
kCodecVideo = 100, kCodecVideo = 100,
kCodecH264 = kCodecVideo, kCodecAV1 = kCodecVideo,
kCodecH264,
kCodecH265, kCodecH265,
kCodecVP8, kCodecVP8,
kCodecVP9, kCodecVP9,
kCodecVP10,
kCodecVideoMaxPlusOne, kCodecVideoMaxPlusOne,
kCodecAudio = 200, kCodecAudio = 200,

View File

@ -18,6 +18,8 @@ namespace media {
namespace { namespace {
std::string VideoCodecToString(Codec codec) { std::string VideoCodecToString(Codec codec) {
switch (codec) { switch (codec) {
case kCodecAV1:
return "AV1";
case kCodecH264: case kCodecH264:
return "H264"; return "H264";
case kCodecH265: case kCodecH265:
@ -26,8 +28,6 @@ std::string VideoCodecToString(Codec codec) {
return "VP8"; return "VP8";
case kCodecVP9: case kCodecVP9:
return "VP9"; return "VP9";
case kCodecVP10:
return "VP10";
default: default:
NOTIMPLEMENTED() << "Unknown Video Codec: " << codec; NOTIMPLEMENTED() << "Unknown Video Codec: " << codec;
return "UnknownCodec"; return "UnknownCodec";

View File

@ -0,0 +1,90 @@
// Copyright 2018 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/av1_codec_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 {
AV1CodecConfigurationRecord::AV1CodecConfigurationRecord() = default;
AV1CodecConfigurationRecord::~AV1CodecConfigurationRecord() = default;
// https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox-section
// aligned (8) class AV1CodecConfigurationRecord {
// unsigned int (1) marker = 1;
// unsigned int (7) version = 1;
// unsigned int (3) seq_profile;
// unsigned int (5) seq_level_idx_0;
// unsigned int (1) seq_tier_0;
// unsigned int (1) high_bitdepth;
// unsigned int (1) twelve_bit;
// unsigned int (1) monochrome;
// unsigned int (1) chroma_subsampling_x;
// unsigned int (1) chroma_subsampling_y;
// unsigned int (2) chroma_sample_position;
// unsigned int (3) reserved = 0;
//
// unsigned int (1) initial_presentation_delay_present;
// if (initial_presentation_delay_present) {
// unsigned int (4) initial_presentation_delay_minus_one;
// } else {
// unsigned int (4) reserved = 0;
// }
//
// unsigned int (8)[] configOBUs;
// }
bool AV1CodecConfigurationRecord::Parse(const uint8_t* data, size_t data_size) {
RCHECK(data_size > 0);
BitReader reader(data, data_size);
int marker;
RCHECK(reader.ReadBits(1, &marker));
RCHECK(marker == 1);
int version;
RCHECK(reader.ReadBits(7, &version));
RCHECK(version == 1);
RCHECK(reader.ReadBits(3, &profile_));
RCHECK(reader.ReadBits(5, &level_));
RCHECK(reader.ReadBits(1, &tier_));
int high_bitdepth;
int twelve_bit;
RCHECK(reader.ReadBits(1, &high_bitdepth));
RCHECK(reader.ReadBits(1, &twelve_bit));
bit_depth_ = twelve_bit ? 12 : (high_bitdepth ? 10 : 8);
RCHECK(reader.ReadBits(1, &mono_chrome_));
RCHECK(reader.ReadBits(1, &chroma_subsampling_x_));
RCHECK(reader.ReadBits(1, &chroma_subsampling_y_));
RCHECK(reader.ReadBits(2, &chroma_sample_position_));
// Skip other fields (e.g. initial_presentation_delay) which we do not need.
return true;
}
// https://aomediacodec.github.io/av1-isobmff/#codecsparam
// <sample entry 4CC>.<profile>.<level><tier>.<bitDepth>.<monochrome>.
// <chromaSubsampling>.<colorPrimaries>.<transferCharacteristics>.
// <matrixCoefficients>.<videoFullRangeFlag>
// The parameters starting from colorPrimaries are omitted as they are not
// present in AV1 Codec Configuration Record and they are optional.
std::string AV1CodecConfigurationRecord::GetCodecString() const {
return base::StringPrintf("av01.%d.%02d%c.%02d.%d.%d%d%d", profile_, level_,
tier_ ? 'H' : 'M', bit_depth_, mono_chrome_,
chroma_subsampling_x_, chroma_subsampling_y_,
chroma_sample_position_);
}
} // namespace media
} // namespace shaka

View File

@ -0,0 +1,54 @@
// Copyright 2018 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_AV1_CODEC_CONFIGURATION_RECORD_H_
#define PACKAGER_MEDIA_CODECS_AV1_CODEC_CONFIGURATION_RECORD_H_
#include <stdint.h>
#include <string>
#include <vector>
namespace shaka {
namespace media {
/// Class for parsing AV1 codec configuration record.
class AV1CodecConfigurationRecord {
public:
AV1CodecConfigurationRecord();
~AV1CodecConfigurationRecord();
/// Parses input to extract codec configuration record.
/// @return false if there are parsing errors.
bool Parse(const std::vector<uint8_t>& data) {
return Parse(data.data(), data.size());
}
/// Parses input to extract decoder configuration record.
/// @return false if there are parsing errors.
bool Parse(const uint8_t* data, size_t data_size);
/// @return The codec string.
std::string GetCodecString() const;
private:
int profile_ = 0;
int level_ = 0;
int tier_ = 0;
int bit_depth_ = 0;
int mono_chrome_ = 0;
int chroma_subsampling_x_ = 0;
int chroma_subsampling_y_ = 0;
int chroma_sample_position_ = 0;
// Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler
// generated copy constructor and assignment operator. Since the internal data
// is small, the performance impact is minimal.
};
} // namespace media
} // namespace shaka
#endif // PACKAGER_MEDIA_CODECS_AV1_CODEC_CONFIGURATION_RECORD_H_

View File

@ -0,0 +1,91 @@
// Copyright 2018 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/av1_codec_configuration_record.h"
#include <gtest/gtest.h>
namespace shaka {
namespace media {
TEST(AV1CodecConfigurationRecordTest, Success) {
const uint8_t kAV1CodecConfigurationData[] = {
0x81, // mark bit and version
0x04, // profile = 0, level = 4
0x4E, // tier = 0, bit_depth = 10, mono_chrome = 0
// chroma_subsampling_x = 1, chroma_subsampling_y = 1,
// chroma_sample_position = 2
// We do not care about other data.
0x00,
};
AV1CodecConfigurationRecord av1_config;
ASSERT_TRUE(av1_config.Parse(
std::vector<uint8_t>(std::begin(kAV1CodecConfigurationData),
std::end(kAV1CodecConfigurationData))));
EXPECT_EQ(av1_config.GetCodecString(), "av01.0.04M.10.0.112");
}
TEST(AV1CodecConfigurationRecordTest, Success2) {
const uint8_t kAV1CodecConfigurationData[] = {
0x81, // mark bit and version
0x35, // profile = 1, level = 15
0xF4, // tier = 1, bit_depth = 12, mono_chrome = 1
// chroma_subsampling_x = 0, chroma_subsampling_y = 1,
// chroma_sample_position = 0
// We do not care about other data.
0x00,
};
AV1CodecConfigurationRecord av1_config;
ASSERT_TRUE(av1_config.Parse(
std::vector<uint8_t>(std::begin(kAV1CodecConfigurationData),
std::end(kAV1CodecConfigurationData))));
EXPECT_EQ(av1_config.GetCodecString(), "av01.1.21H.12.1.010");
}
TEST(AV1CodecConfigurationRecordTest, InsufficientData) {
const uint8_t kAV1CodecConfigurationData[] = {
0x81,
0x04,
};
AV1CodecConfigurationRecord av1_config;
ASSERT_FALSE(av1_config.Parse(
std::vector<uint8_t>(std::begin(kAV1CodecConfigurationData),
std::end(kAV1CodecConfigurationData))));
}
TEST(AV1CodecConfigurationRecordTest, IncorrectMarkerBit) {
const uint8_t kAV1CodecConfigurationData[] = {
0x01,
0x04,
0x4E,
};
AV1CodecConfigurationRecord av1_config;
ASSERT_FALSE(av1_config.Parse(
std::vector<uint8_t>(std::begin(kAV1CodecConfigurationData),
std::end(kAV1CodecConfigurationData))));
}
TEST(AV1CodecConfigurationRecordTest, IncorrectVersion) {
const uint8_t kAV1CodecConfigurationData[] = {
0x82,
0x04,
0x4E,
};
AV1CodecConfigurationRecord av1_config;
ASSERT_FALSE(av1_config.Parse(
std::vector<uint8_t>(std::begin(kAV1CodecConfigurationData),
std::end(kAV1CodecConfigurationData))));
}
} // namespace media
} // namespace shaka

View File

@ -17,6 +17,8 @@
'aac_audio_specific_config.h', 'aac_audio_specific_config.h',
'ac3_audio_util.cc', 'ac3_audio_util.cc',
'ac3_audio_util.h', 'ac3_audio_util.h',
'av1_codec_configuration_record.cc',
'av1_codec_configuration_record.h',
'avc_decoder_configuration_record.cc', 'avc_decoder_configuration_record.cc',
'avc_decoder_configuration_record.h', 'avc_decoder_configuration_record.h',
'decoder_configuration_record.cc', 'decoder_configuration_record.cc',
@ -66,6 +68,7 @@
'sources': [ 'sources': [
'aac_audio_specific_config_unittest.cc', 'aac_audio_specific_config_unittest.cc',
'ac3_audio_util_unittest.cc', 'ac3_audio_util_unittest.cc',
'av1_codec_configuration_record_unittest.cc',
'avc_decoder_configuration_record_unittest.cc', 'avc_decoder_configuration_record_unittest.cc',
'ec3_audio_util_unittest.cc', 'ec3_audio_util_unittest.cc',
'es_descriptor_unittest.cc', 'es_descriptor_unittest.cc',

View File

@ -30,8 +30,6 @@ std::string VPCodecAsString(Codec codec) {
return "vp08"; return "vp08";
case kCodecVP9: case kCodecVP9:
return "vp09"; return "vp09";
case kCodecVP10:
return "vp10";
default: default:
LOG(WARNING) << "Unknown VP codec: " << codec; LOG(WARNING) << "Unknown VP codec: " << codec;
return std::string(); return std::string();

View File

@ -34,6 +34,7 @@ const uint16_t kVideoFrameCount = 1;
const uint16_t kVideoDepth = 0x0018; const uint16_t kVideoDepth = 0x0018;
const uint32_t kCompressorNameSize = 32u; const uint32_t kCompressorNameSize = 32u;
const char kAv1CompressorName[] = "\012AOM Coding";
const char kAvcCompressorName[] = "\012AVC Coding"; const char kAvcCompressorName[] = "\012AVC Coding";
const char kHevcCompressorName[] = "\013HEVC Coding"; const char kHevcCompressorName[] = "\013HEVC Coding";
const char kVpcCompressorName[] = "\012VPC Coding"; const char kVpcCompressorName[] = "\012VPC Coding";
@ -1478,24 +1479,24 @@ bool VideoSampleEntry::ReadWriteInternal(BoxBuffer* buffer) {
const FourCC actual_format = GetActualFormat(); const FourCC actual_format = GetActualFormat();
switch (actual_format) { switch (actual_format) {
case FOURCC_av01:
compressor_name.assign(std::begin(kAv1CompressorName),
std::end(kAv1CompressorName));
break;
case FOURCC_avc1: case FOURCC_avc1:
case FOURCC_avc3: case FOURCC_avc3:
compressor_name.assign( compressor_name.assign(std::begin(kAvcCompressorName),
kAvcCompressorName, std::end(kAvcCompressorName));
kAvcCompressorName + arraysize(kAvcCompressorName));
break; break;
case FOURCC_hev1: case FOURCC_hev1:
case FOURCC_hvc1: case FOURCC_hvc1:
compressor_name.assign( compressor_name.assign(std::begin(kHevcCompressorName),
kHevcCompressorName, std::end(kHevcCompressorName));
kHevcCompressorName + arraysize(kHevcCompressorName));
break; break;
case FOURCC_vp08: case FOURCC_vp08:
case FOURCC_vp09: case FOURCC_vp09:
case FOURCC_vp10: compressor_name.assign(std::begin(kVpcCompressorName),
compressor_name.assign( std::end(kVpcCompressorName));
kVpcCompressorName,
kVpcCompressorName + arraysize(kVpcCompressorName));
break; break;
default: default:
LOG(ERROR) << FourCCToString(actual_format) << " is not supported."; LOG(ERROR) << FourCCToString(actual_format) << " is not supported.";
@ -1571,6 +1572,8 @@ size_t VideoSampleEntry::ComputeSizeInternal() {
FourCC VideoSampleEntry::GetCodecConfigurationBoxType(FourCC format) const { FourCC VideoSampleEntry::GetCodecConfigurationBoxType(FourCC format) const {
switch (format) { switch (format) {
case FOURCC_av01:
return FOURCC_av1C;
case FOURCC_avc1: case FOURCC_avc1:
case FOURCC_avc3: case FOURCC_avc3:
return FOURCC_avcC; return FOURCC_avcC;
@ -1579,7 +1582,6 @@ FourCC VideoSampleEntry::GetCodecConfigurationBoxType(FourCC format) const {
return FOURCC_hvcC; return FOURCC_hvcC;
case FOURCC_vp08: case FOURCC_vp08:
case FOURCC_vp09: case FOURCC_vp09:
case FOURCC_vp10:
return FOURCC_vpcC; return FOURCC_vpcC;
default: default:
LOG(ERROR) << FourCCToString(format) << " is not supported."; LOG(ERROR) << FourCCToString(format) << " is not supported.";

View File

@ -22,6 +22,7 @@
#include "packager/media/base/rcheck.h" #include "packager/media/base/rcheck.h"
#include "packager/media/base/video_stream_info.h" #include "packager/media/base/video_stream_info.h"
#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/avc_decoder_configuration_record.h" #include "packager/media/codecs/avc_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"
@ -59,6 +60,8 @@ H26xStreamFormat GetH26xStreamFormat(FourCC fourcc) {
Codec FourCCToCodec(FourCC fourcc) { Codec FourCCToCodec(FourCC fourcc) {
switch (fourcc) { switch (fourcc) {
case FOURCC_av01:
return kCodecAV1;
case FOURCC_avc1: case FOURCC_avc1:
case FOURCC_avc3: case FOURCC_avc3:
return kCodecH264; return kCodecH264;
@ -69,8 +72,6 @@ Codec FourCCToCodec(FourCC fourcc) {
return kCodecVP8; return kCodecVP8;
case FOURCC_vp09: case FOURCC_vp09:
return kCodecVP9; return kCodecVP9;
case FOURCC_vp10:
return kCodecVP10;
case FOURCC_Opus: case FOURCC_Opus:
return kCodecOpus; return kCodecOpus;
case FOURCC_dtsc: case FOURCC_dtsc:
@ -518,6 +519,15 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
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);
switch (actual_format) { switch (actual_format) {
case FOURCC_av01: {
AV1CodecConfigurationRecord av1_config;
if (!av1_config.Parse(entry.codec_configuration.data)) {
LOG(ERROR) << "Failed to parse av1c.";
return false;
}
codec_string = av1_config.GetCodecString();
break;
}
case FOURCC_avc1: case FOURCC_avc1:
case FOURCC_avc3: { case FOURCC_avc3: {
AVCDecoderConfigurationRecord avc_config; AVCDecoderConfigurationRecord avc_config;
@ -567,8 +577,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
break; break;
} }
case FOURCC_vp08: case FOURCC_vp08:
case FOURCC_vp09: case FOURCC_vp09: {
case FOURCC_vp10: {
VPCodecConfigurationRecord vp_config; VPCodecConfigurationRecord vp_config;
if (!vp_config.ParseMP4(entry.codec_configuration.data)) { if (!vp_config.ParseMP4(entry.codec_configuration.data)) {
LOG(ERROR) << "Failed to parse vpcc."; LOG(ERROR) << "Failed to parse vpcc.";

View File

@ -44,6 +44,8 @@ void SetStartAndEndFromOffsetAndSize(size_t offset,
FourCC CodecToFourCC(Codec codec, H26xStreamFormat h26x_stream_format) { FourCC CodecToFourCC(Codec codec, H26xStreamFormat h26x_stream_format) {
switch (codec) { switch (codec) {
case kCodecAV1:
return FOURCC_av01;
case kCodecH264: case kCodecH264:
return h26x_stream_format == return h26x_stream_format ==
H26xStreamFormat::kNalUnitStreamWithParameterSetNalus H26xStreamFormat::kNalUnitStreamWithParameterSetNalus
@ -58,8 +60,6 @@ FourCC CodecToFourCC(Codec codec, H26xStreamFormat h26x_stream_format) {
return FOURCC_vp08; return FOURCC_vp08;
case kCodecVP9: case kCodecVP9:
return FOURCC_vp09; return FOURCC_vp09;
case kCodecVP10:
return FOURCC_vp10;
case kCodecAAC: case kCodecAAC:
return FOURCC_mp4a; return FOURCC_mp4a;
case kCodecAC3: case kCodecAC3:

View File

@ -284,10 +284,17 @@ void Segmenter::UpdateProgress(uint64_t progress) {
Status Segmenter::InitializeVideoTrack(const VideoStreamInfo& info, Status Segmenter::InitializeVideoTrack(const VideoStreamInfo& info,
VideoTrack* track) { VideoTrack* track) {
if (info.codec() == kCodecVP8) { if (info.codec() == kCodecAV1) {
track->set_codec_id(mkvmuxer::Tracks::kVp8CodecId); track->set_codec_id("V_AV1");
if (!track->SetCodecPrivate(info.codec_config().data(),
info.codec_config().size())) {
return Status(error::INTERNAL_ERROR,
"Private codec data required for AV1 streams");
}
} else if (info.codec() == kCodecVP8) {
track->set_codec_id("V_VP8");
} else if (info.codec() == kCodecVP9) { } else if (info.codec() == kCodecVP9) {
track->set_codec_id(mkvmuxer::Tracks::kVp9CodecId); track->set_codec_id("V_VP9");
// The |StreamInfo::codec_config| field is stored using the MP4 format; we // The |StreamInfo::codec_config| field is stored using the MP4 format; we
// need to convert it to the WebM format. // need to convert it to the WebM format.
@ -319,9 +326,9 @@ Status Segmenter::InitializeVideoTrack(const VideoStreamInfo& info,
"Private codec data required for VPx streams"); "Private codec data required for VPx streams");
} }
} else { } else {
LOG(ERROR) << "Only VP8 and VP9 video codecs are supported in WebM."; LOG(ERROR) << "Only VP8, VP9 and AV1 video codecs are supported in WebM.";
return Status(error::UNIMPLEMENTED, return Status(error::UNIMPLEMENTED,
"Only VP8 and VP9 video codecs are supported in WebM."); "Only VP8, VP9 and AV1 video codecs are supported in WebM.");
} }
track->set_uid(info.track_id()); track->set_uid(info.track_id());

View File

@ -433,6 +433,10 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
streams.push_back(audio_stream_info_); streams.push_back(audio_stream_info_);
if (video_stream_info_) { if (video_stream_info_) {
if (stream_type == kStreamVideo) { if (stream_type == kStreamVideo) {
// Setup codec string and codec config for VP8 and VP9.
// Codec config for AV1 is already retrieved from WebM CodecPrivate
// instead of extracted from the bit stream.
if (video_stream_info_->codec() != kCodecAV1) {
std::unique_ptr<VPxParser> vpx_parser; std::unique_ptr<VPxParser> vpx_parser;
switch (video_stream_info_->codec()) { switch (video_stream_info_->codec()) {
case kCodecVP8: case kCodecVP8:
@ -442,8 +446,8 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
vpx_parser.reset(new VP9Parser); vpx_parser.reset(new VP9Parser);
break; break;
default: default:
NOTIMPLEMENTED() << "Unsupported codec " NOTIMPLEMENTED()
<< video_stream_info_->codec(); << "Unsupported codec " << video_stream_info_->codec();
return false; return false;
} }
std::vector<VPxFrameInfo> vpx_frames; std::vector<VPxFrameInfo> vpx_frames;
@ -463,6 +467,8 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
std::vector<uint8_t> config_serialized; std::vector<uint8_t> config_serialized;
vp_config_.WriteMP4(&config_serialized); vp_config_.WriteMP4(&config_serialized);
video_stream_info_->set_codec_config(config_serialized); video_stream_info_->set_codec_config(config_serialized);
}
streams.push_back(video_stream_info_); streams.push_back(video_stream_info_);
init_cb_.Run(streams); init_cb_.Run(streams);
initialized_ = true; initialized_ = true;

View File

@ -223,10 +223,14 @@ bool WebMTracksParser::OnListEnd(int id) {
} }
video_default_duration_ = default_duration_; video_default_duration_ = default_duration_;
DCHECK(!video_stream_info_); // |vp_config_| is only useful for VP8 and VP9.
if (codec_id_ == "V_VP8" || codec_id_ == "V_VP9")
vp_config_ = video_client_.GetVpCodecConfig(codec_private_); vp_config_ = video_client_.GetVpCodecConfig(codec_private_);
DCHECK(!video_stream_info_);
video_stream_info_ = video_client_.GetVideoStreamInfo( video_stream_info_ = video_client_.GetVideoStreamInfo(
video_track_num_, codec_id_, !video_encryption_key_id_.empty()); video_track_num_, codec_id_, codec_private_,
!video_encryption_key_id_.empty());
if (!video_stream_info_) if (!video_stream_info_)
return false; return false;
} else { } else {

View File

@ -5,6 +5,7 @@
#include "packager/media/formats/webm/webm_video_client.h" #include "packager/media/formats/webm/webm_video_client.h"
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/media/codecs/av1_codec_configuration_record.h"
#include "packager/media/codecs/vp_codec_configuration_record.h" #include "packager/media/codecs/vp_codec_configuration_record.h"
#include "packager/media/formats/webm/webm_constants.h" #include "packager/media/formats/webm/webm_constants.h"
@ -57,24 +58,34 @@ void WebMVideoClient::Reset() {
std::shared_ptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo( std::shared_ptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
int64_t track_num, int64_t track_num,
const std::string& codec_id, const std::string& codec_id,
const std::vector<uint8_t>& codec_private,
bool is_encrypted) { bool is_encrypted) {
std::string codec_string;
Codec video_codec = kUnknownCodec; Codec video_codec = kUnknownCodec;
if (codec_id == "V_VP8") { if (codec_id == "V_AV1") {
video_codec = kCodecAV1;
// CodecPrivate is mandatory per AV in Matroska / WebM specification.
// https://github.com/Matroska-Org/matroska-specification/blob/av1-mappin/codec/av1.md#codecprivate-1
AV1CodecConfigurationRecord av1_config;
if (!av1_config.Parse(codec_private)) {
LOG(ERROR) << "Failed to parse AV1 codec_private.";
return nullptr;
}
codec_string = av1_config.GetCodecString();
} else if (codec_id == "V_VP8") {
video_codec = kCodecVP8; video_codec = kCodecVP8;
// codec_string for VP8 is parsed later.
} else if (codec_id == "V_VP9") { } else if (codec_id == "V_VP9") {
video_codec = kCodecVP9; video_codec = kCodecVP9;
// The codec private data is in WebM format, but needs to be converted to // codec_string for VP9 is parsed later.
// MP4 format. Don't do it yet, it will be handled in
// webm_cluster_parser.cc
} else if (codec_id == "V_VP10") {
video_codec = kCodecVP10;
} else { } else {
LOG(ERROR) << "Unsupported video codec_id " << codec_id; LOG(ERROR) << "Unsupported video codec_id " << codec_id;
return std::shared_ptr<VideoStreamInfo>(); return nullptr;
} }
if (pixel_width_ <= 0 || pixel_height_ <= 0) if (pixel_width_ <= 0 || pixel_height_ <= 0)
return std::shared_ptr<VideoStreamInfo>(); return nullptr;
// Set crop and display unit defaults if these elements are not present. // Set crop and display unit defaults if these elements are not present.
if (crop_bottom_ == -1) if (crop_bottom_ == -1)
@ -102,10 +113,10 @@ std::shared_ptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
display_height_ = height_after_crop; display_height_ = height_after_crop;
} else if (display_unit_ == 3) { } else if (display_unit_ == 3) {
if (display_width_ <= 0 || display_height_ <= 0) if (display_width_ <= 0 || display_height_ <= 0)
return std::shared_ptr<VideoStreamInfo>(); return nullptr;
} else { } else {
LOG(ERROR) << "Unsupported display unit type " << display_unit_; LOG(ERROR) << "Unsupported display unit type " << display_unit_;
return std::shared_ptr<VideoStreamInfo>(); return nullptr;
} }
// Calculate sample aspect ratio. // Calculate sample aspect ratio.
int64_t sar_x = display_width_ * height_after_crop; int64_t sar_x = display_width_ * height_after_crop;
@ -114,10 +125,14 @@ std::shared_ptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
sar_x /= gcd; sar_x /= gcd;
sar_y /= gcd; sar_y /= gcd;
// |codec_private| may be overriden later for some codecs, e.g. VP9 since for
// VP9, the format for MP4 and WebM are different; MP4 format is used as the
// intermediate format.
return std::make_shared<VideoStreamInfo>( return std::make_shared<VideoStreamInfo>(
track_num, kWebMTimeScale, 0, video_codec, H26xStreamFormat::kUnSpecified, track_num, kWebMTimeScale, 0, video_codec, H26xStreamFormat::kUnSpecified,
std::string(), nullptr, 0, width_after_crop, height_after_crop, sar_x, codec_string, codec_private.data(), codec_private.size(),
sar_y, 0, 0, std::string(), is_encrypted); width_after_crop, height_after_crop, sar_x, sar_y, 0, 0, std::string(),
is_encrypted);
} }
VPCodecConfigurationRecord WebMVideoClient::GetVpCodecConfig( VPCodecConfigurationRecord WebMVideoClient::GetVpCodecConfig(

View File

@ -27,14 +27,15 @@ class WebMVideoClient : public WebMParserClient {
void Reset(); void Reset();
/// Create a VideoStreamInfo with the data in |track_num|, |codec_id|, /// Create a VideoStreamInfo with the data in |track_num|, |codec_id|,
/// |is_encrypted| and the fields parsed from the last video track element /// |codec_private|, |is_encrypted| and the fields parsed from the last video
/// this object was used to parse. /// track element this object was used to parse.
/// @return A VideoStreamInfo if successful. /// @return A VideoStreamInfo if successful.
/// @return An empty pointer if there was unexpected values in the /// @return An empty pointer if there was unexpected values in the
/// provided parameters or video track element fields. /// provided parameters or video track element fields.
std::shared_ptr<VideoStreamInfo> GetVideoStreamInfo( std::shared_ptr<VideoStreamInfo> GetVideoStreamInfo(
int64_t track_num, int64_t track_num,
const std::string& codec_id, const std::string& codec_id,
const std::vector<uint8_t>& codec_private,
bool is_encrypted); bool is_encrypted);
/// Extracts VPCodecConfigurationRecord parsed from codec private data and /// Extracts VPCodecConfigurationRecord parsed from codec private data and

Binary file not shown.

Binary file not shown.