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:
parent
86b10b6316
commit
0709db4bbc
|
@ -849,6 +849,33 @@ class PackagerFunctionalTest(PackagerAppTest):
|
|||
self._GetFlags(output_dash=True))
|
||||
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):
|
||||
self.assertPackageSuccess(
|
||||
self._GetStreams(['audio', '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=69160,AVERAGE-BANDWIDTH=69160,CODECS="av01.0.00M.08.0.110",RESOLUTION=320x240
|
||||
stream_0.m3u8
|
|
@ -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>
|
|
@ -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.
|
@ -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
|
|
@ -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>
|
|
@ -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.
|
@ -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
|
|
@ -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>
|
|
@ -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
|
|
@ -22,6 +22,8 @@ enum FourCC : uint32_t {
|
|||
FOURCC_ac_3 = 0x61632d33, // "ac-3"
|
||||
FOURCC_ac3d = 0x61633364,
|
||||
FOURCC_apad = 0x61706164,
|
||||
FOURCC_av01 = 0x61763031,
|
||||
FOURCC_av1C = 0x61763143,
|
||||
FOURCC_avc1 = 0x61766331,
|
||||
FOURCC_avc3 = 0x61766333,
|
||||
FOURCC_avcC = 0x61766343,
|
||||
|
@ -139,7 +141,6 @@ enum FourCC : uint32_t {
|
|||
FOURCC_vmhd = 0x766d6864,
|
||||
FOURCC_vp08 = 0x76703038,
|
||||
FOURCC_vp09 = 0x76703039,
|
||||
FOURCC_vp10 = 0x76703130,
|
||||
FOURCC_vpcC = 0x76706343,
|
||||
FOURCC_vsid = 0x76736964,
|
||||
FOURCC_vttC = 0x76747443,
|
||||
|
|
|
@ -29,11 +29,11 @@ enum Codec {
|
|||
kUnknownCodec = 0,
|
||||
|
||||
kCodecVideo = 100,
|
||||
kCodecH264 = kCodecVideo,
|
||||
kCodecAV1 = kCodecVideo,
|
||||
kCodecH264,
|
||||
kCodecH265,
|
||||
kCodecVP8,
|
||||
kCodecVP9,
|
||||
kCodecVP10,
|
||||
kCodecVideoMaxPlusOne,
|
||||
|
||||
kCodecAudio = 200,
|
||||
|
|
|
@ -18,6 +18,8 @@ namespace media {
|
|||
namespace {
|
||||
std::string VideoCodecToString(Codec codec) {
|
||||
switch (codec) {
|
||||
case kCodecAV1:
|
||||
return "AV1";
|
||||
case kCodecH264:
|
||||
return "H264";
|
||||
case kCodecH265:
|
||||
|
@ -26,8 +28,6 @@ std::string VideoCodecToString(Codec codec) {
|
|||
return "VP8";
|
||||
case kCodecVP9:
|
||||
return "VP9";
|
||||
case kCodecVP10:
|
||||
return "VP10";
|
||||
default:
|
||||
NOTIMPLEMENTED() << "Unknown Video Codec: " << codec;
|
||||
return "UnknownCodec";
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -17,6 +17,8 @@
|
|||
'aac_audio_specific_config.h',
|
||||
'ac3_audio_util.cc',
|
||||
'ac3_audio_util.h',
|
||||
'av1_codec_configuration_record.cc',
|
||||
'av1_codec_configuration_record.h',
|
||||
'avc_decoder_configuration_record.cc',
|
||||
'avc_decoder_configuration_record.h',
|
||||
'decoder_configuration_record.cc',
|
||||
|
@ -66,6 +68,7 @@
|
|||
'sources': [
|
||||
'aac_audio_specific_config_unittest.cc',
|
||||
'ac3_audio_util_unittest.cc',
|
||||
'av1_codec_configuration_record_unittest.cc',
|
||||
'avc_decoder_configuration_record_unittest.cc',
|
||||
'ec3_audio_util_unittest.cc',
|
||||
'es_descriptor_unittest.cc',
|
||||
|
|
|
@ -30,8 +30,6 @@ std::string VPCodecAsString(Codec codec) {
|
|||
return "vp08";
|
||||
case kCodecVP9:
|
||||
return "vp09";
|
||||
case kCodecVP10:
|
||||
return "vp10";
|
||||
default:
|
||||
LOG(WARNING) << "Unknown VP codec: " << codec;
|
||||
return std::string();
|
||||
|
|
|
@ -34,6 +34,7 @@ const uint16_t kVideoFrameCount = 1;
|
|||
const uint16_t kVideoDepth = 0x0018;
|
||||
|
||||
const uint32_t kCompressorNameSize = 32u;
|
||||
const char kAv1CompressorName[] = "\012AOM Coding";
|
||||
const char kAvcCompressorName[] = "\012AVC Coding";
|
||||
const char kHevcCompressorName[] = "\013HEVC Coding";
|
||||
const char kVpcCompressorName[] = "\012VPC Coding";
|
||||
|
@ -1478,24 +1479,24 @@ bool VideoSampleEntry::ReadWriteInternal(BoxBuffer* buffer) {
|
|||
|
||||
const FourCC actual_format = GetActualFormat();
|
||||
switch (actual_format) {
|
||||
case FOURCC_av01:
|
||||
compressor_name.assign(std::begin(kAv1CompressorName),
|
||||
std::end(kAv1CompressorName));
|
||||
break;
|
||||
case FOURCC_avc1:
|
||||
case FOURCC_avc3:
|
||||
compressor_name.assign(
|
||||
kAvcCompressorName,
|
||||
kAvcCompressorName + arraysize(kAvcCompressorName));
|
||||
compressor_name.assign(std::begin(kAvcCompressorName),
|
||||
std::end(kAvcCompressorName));
|
||||
break;
|
||||
case FOURCC_hev1:
|
||||
case FOURCC_hvc1:
|
||||
compressor_name.assign(
|
||||
kHevcCompressorName,
|
||||
kHevcCompressorName + arraysize(kHevcCompressorName));
|
||||
compressor_name.assign(std::begin(kHevcCompressorName),
|
||||
std::end(kHevcCompressorName));
|
||||
break;
|
||||
case FOURCC_vp08:
|
||||
case FOURCC_vp09:
|
||||
case FOURCC_vp10:
|
||||
compressor_name.assign(
|
||||
kVpcCompressorName,
|
||||
kVpcCompressorName + arraysize(kVpcCompressorName));
|
||||
compressor_name.assign(std::begin(kVpcCompressorName),
|
||||
std::end(kVpcCompressorName));
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << FourCCToString(actual_format) << " is not supported.";
|
||||
|
@ -1571,6 +1572,8 @@ size_t VideoSampleEntry::ComputeSizeInternal() {
|
|||
|
||||
FourCC VideoSampleEntry::GetCodecConfigurationBoxType(FourCC format) const {
|
||||
switch (format) {
|
||||
case FOURCC_av01:
|
||||
return FOURCC_av1C;
|
||||
case FOURCC_avc1:
|
||||
case FOURCC_avc3:
|
||||
return FOURCC_avcC;
|
||||
|
@ -1579,7 +1582,6 @@ FourCC VideoSampleEntry::GetCodecConfigurationBoxType(FourCC format) const {
|
|||
return FOURCC_hvcC;
|
||||
case FOURCC_vp08:
|
||||
case FOURCC_vp09:
|
||||
case FOURCC_vp10:
|
||||
return FOURCC_vpcC;
|
||||
default:
|
||||
LOG(ERROR) << FourCCToString(format) << " is not supported.";
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "packager/media/base/rcheck.h"
|
||||
#include "packager/media/base/video_stream_info.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/ec3_audio_util.h"
|
||||
#include "packager/media/codecs/es_descriptor.h"
|
||||
|
@ -59,6 +60,8 @@ H26xStreamFormat GetH26xStreamFormat(FourCC fourcc) {
|
|||
|
||||
Codec FourCCToCodec(FourCC fourcc) {
|
||||
switch (fourcc) {
|
||||
case FOURCC_av01:
|
||||
return kCodecAV1;
|
||||
case FOURCC_avc1:
|
||||
case FOURCC_avc3:
|
||||
return kCodecH264;
|
||||
|
@ -69,8 +72,6 @@ Codec FourCCToCodec(FourCC fourcc) {
|
|||
return kCodecVP8;
|
||||
case FOURCC_vp09:
|
||||
return kCodecVP9;
|
||||
case FOURCC_vp10:
|
||||
return kCodecVP10;
|
||||
case FOURCC_Opus:
|
||||
return kCodecOpus;
|
||||
case FOURCC_dtsc:
|
||||
|
@ -518,6 +519,15 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
const FourCC actual_format = entry.GetActualFormat();
|
||||
const Codec video_codec = FourCCToCodec(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_avc3: {
|
||||
AVCDecoderConfigurationRecord avc_config;
|
||||
|
@ -567,8 +577,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
break;
|
||||
}
|
||||
case FOURCC_vp08:
|
||||
case FOURCC_vp09:
|
||||
case FOURCC_vp10: {
|
||||
case FOURCC_vp09: {
|
||||
VPCodecConfigurationRecord vp_config;
|
||||
if (!vp_config.ParseMP4(entry.codec_configuration.data)) {
|
||||
LOG(ERROR) << "Failed to parse vpcc.";
|
||||
|
|
|
@ -44,6 +44,8 @@ void SetStartAndEndFromOffsetAndSize(size_t offset,
|
|||
|
||||
FourCC CodecToFourCC(Codec codec, H26xStreamFormat h26x_stream_format) {
|
||||
switch (codec) {
|
||||
case kCodecAV1:
|
||||
return FOURCC_av01;
|
||||
case kCodecH264:
|
||||
return h26x_stream_format ==
|
||||
H26xStreamFormat::kNalUnitStreamWithParameterSetNalus
|
||||
|
@ -58,8 +60,6 @@ FourCC CodecToFourCC(Codec codec, H26xStreamFormat h26x_stream_format) {
|
|||
return FOURCC_vp08;
|
||||
case kCodecVP9:
|
||||
return FOURCC_vp09;
|
||||
case kCodecVP10:
|
||||
return FOURCC_vp10;
|
||||
case kCodecAAC:
|
||||
return FOURCC_mp4a;
|
||||
case kCodecAC3:
|
||||
|
|
|
@ -284,10 +284,17 @@ void Segmenter::UpdateProgress(uint64_t progress) {
|
|||
|
||||
Status Segmenter::InitializeVideoTrack(const VideoStreamInfo& info,
|
||||
VideoTrack* track) {
|
||||
if (info.codec() == kCodecVP8) {
|
||||
track->set_codec_id(mkvmuxer::Tracks::kVp8CodecId);
|
||||
if (info.codec() == kCodecAV1) {
|
||||
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) {
|
||||
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
|
||||
// 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");
|
||||
}
|
||||
} 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,
|
||||
"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());
|
||||
|
|
|
@ -433,6 +433,10 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
|
|||
streams.push_back(audio_stream_info_);
|
||||
if (video_stream_info_) {
|
||||
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;
|
||||
switch (video_stream_info_->codec()) {
|
||||
case kCodecVP8:
|
||||
|
@ -442,8 +446,8 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
|
|||
vpx_parser.reset(new VP9Parser);
|
||||
break;
|
||||
default:
|
||||
NOTIMPLEMENTED() << "Unsupported codec "
|
||||
<< video_stream_info_->codec();
|
||||
NOTIMPLEMENTED()
|
||||
<< "Unsupported codec " << video_stream_info_->codec();
|
||||
return false;
|
||||
}
|
||||
std::vector<VPxFrameInfo> vpx_frames;
|
||||
|
@ -463,6 +467,8 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
|
|||
std::vector<uint8_t> config_serialized;
|
||||
vp_config_.WriteMP4(&config_serialized);
|
||||
video_stream_info_->set_codec_config(config_serialized);
|
||||
}
|
||||
|
||||
streams.push_back(video_stream_info_);
|
||||
init_cb_.Run(streams);
|
||||
initialized_ = true;
|
||||
|
|
|
@ -223,10 +223,14 @@ bool WebMTracksParser::OnListEnd(int id) {
|
|||
}
|
||||
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_);
|
||||
|
||||
DCHECK(!video_stream_info_);
|
||||
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_)
|
||||
return false;
|
||||
} else {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "packager/media/formats/webm/webm_video_client.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/formats/webm/webm_constants.h"
|
||||
|
||||
|
@ -57,24 +58,34 @@ void WebMVideoClient::Reset() {
|
|||
std::shared_ptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
|
||||
int64_t track_num,
|
||||
const std::string& codec_id,
|
||||
const std::vector<uint8_t>& codec_private,
|
||||
bool is_encrypted) {
|
||||
std::string codec_string;
|
||||
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;
|
||||
// codec_string for VP8 is parsed later.
|
||||
} else if (codec_id == "V_VP9") {
|
||||
video_codec = kCodecVP9;
|
||||
// The codec private data is in WebM format, but needs to be converted to
|
||||
// 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;
|
||||
// codec_string for VP9 is parsed later.
|
||||
} else {
|
||||
LOG(ERROR) << "Unsupported video codec_id " << codec_id;
|
||||
return std::shared_ptr<VideoStreamInfo>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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.
|
||||
if (crop_bottom_ == -1)
|
||||
|
@ -102,10 +113,10 @@ std::shared_ptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
|
|||
display_height_ = height_after_crop;
|
||||
} else if (display_unit_ == 3) {
|
||||
if (display_width_ <= 0 || display_height_ <= 0)
|
||||
return std::shared_ptr<VideoStreamInfo>();
|
||||
return nullptr;
|
||||
} else {
|
||||
LOG(ERROR) << "Unsupported display unit type " << display_unit_;
|
||||
return std::shared_ptr<VideoStreamInfo>();
|
||||
return nullptr;
|
||||
}
|
||||
// Calculate sample aspect ratio.
|
||||
int64_t sar_x = display_width_ * height_after_crop;
|
||||
|
@ -114,10 +125,14 @@ std::shared_ptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
|
|||
sar_x /= 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>(
|
||||
track_num, kWebMTimeScale, 0, video_codec, H26xStreamFormat::kUnSpecified,
|
||||
std::string(), nullptr, 0, width_after_crop, height_after_crop, sar_x,
|
||||
sar_y, 0, 0, std::string(), is_encrypted);
|
||||
codec_string, codec_private.data(), codec_private.size(),
|
||||
width_after_crop, height_after_crop, sar_x, sar_y, 0, 0, std::string(),
|
||||
is_encrypted);
|
||||
}
|
||||
|
||||
VPCodecConfigurationRecord WebMVideoClient::GetVpCodecConfig(
|
||||
|
|
|
@ -27,14 +27,15 @@ class WebMVideoClient : public WebMParserClient {
|
|||
void Reset();
|
||||
|
||||
/// Create a VideoStreamInfo with the data in |track_num|, |codec_id|,
|
||||
/// |is_encrypted| and the fields parsed from the last video track element
|
||||
/// this object was used to parse.
|
||||
/// |codec_private|, |is_encrypted| and the fields parsed from the last video
|
||||
/// track element this object was used to parse.
|
||||
/// @return A VideoStreamInfo if successful.
|
||||
/// @return An empty pointer if there was unexpected values in the
|
||||
/// provided parameters or video track element fields.
|
||||
std::shared_ptr<VideoStreamInfo> GetVideoStreamInfo(
|
||||
int64_t track_num,
|
||||
const std::string& codec_id,
|
||||
const std::vector<uint8_t>& codec_private,
|
||||
bool is_encrypted);
|
||||
|
||||
/// Extracts VPCodecConfigurationRecord parsed from codec private data and
|
||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue