7#include <packager/media/codecs/vp_codec_configuration_record.h>
15#include <absl/log/log.h>
16#include <absl/strings/str_format.h>
17#include <absl/strings/str_replace.h>
19#include <packager/media/base/bit_reader.h>
20#include <packager/media/base/buffer_reader.h>
21#include <packager/media/base/buffer_writer.h>
22#include <packager/media/base/rcheck.h>
23#include <packager/media/base/stream_info.h>
28enum VP9CodecFeatures {
32 kFeatureChromaSubsampling = 4,
35std::string VPCodecAsString(Codec codec) {
42 LOG(WARNING) <<
"Unknown VP codec: " << codec;
48void MergeField(
const std::string& name,
49 const std::optional<T>& source_value,
50 std::optional<T>* dest_value) {
52 if (source_value && *source_value != **dest_value) {
53 LOG(WARNING) <<
"VPx " << name <<
" is inconsistent, "
54 <<
static_cast<int>(**dest_value) <<
" vs "
55 <<
static_cast<int>(*source_value);
59 *dest_value = source_value;
82struct VP9LevelCharacteristics {
83 uint64_t max_luma_sample_rate;
84 uint32_t max_luma_picture_size;
85 double max_avg_bitrate;
87 double min_compression_ratio;
88 uint8_t max_num_column_tiles;
89 uint32_t min_altref_distance;
90 uint8_t max_ref_frame_buffers;
93struct VP9LevelDefinition {
95 VP9LevelCharacteristics characteristics;
98VP9Level LevelFromCharacteristics(uint64_t luma_sample_rate,
99 uint32_t luma_picture_size) {
101 const VP9LevelDefinition vp9_level_definitions[] = {
102 {LEVEL_1, {829440, 36864, 200, 400, 2, 1, 4, 8}},
103 {LEVEL_1_1, {2764800, 73728, 800, 1000, 2, 1, 4, 8}},
104 {LEVEL_2, {4608000, 122880, 1800, 1500, 2, 1, 4, 8}},
105 {LEVEL_2_1, {9216000, 245760, 3600, 2800, 2, 2, 4, 8}},
106 {LEVEL_3, {20736000, 552960, 7200, 6000, 2, 4, 4, 8}},
107 {LEVEL_3_1, {36864000, 983040, 12000, 10000, 2, 4, 4, 8}},
108 {LEVEL_4, {83558400, 2228224, 18000, 16000, 4, 4, 4, 8}},
109 {LEVEL_4_1, {160432128, 2228224, 30000, 18000, 4, 4, 5, 6}},
110 {LEVEL_5, {311951360, 8912896, 60000, 36000, 6, 8, 6, 4}},
111 {LEVEL_5_1, {588251136, 8912896, 120000, 46000, 8, 8, 10, 4}},
112 {LEVEL_5_2, {1176502272, 8912896, 180000, 90000, 8, 8, 10, 4}},
113 {LEVEL_6, {1176502272, 35651584, 180000, 90000, 8, 16, 10, 4}},
114 {LEVEL_6_1, {2353004544u, 35651584, 240000, 180000, 8, 16, 10, 4}},
115 {LEVEL_6_2, {4706009088u, 35651584, 480000, 360000, 8, 16, 10, 4}},
118 for (
const VP9LevelDefinition& def : vp9_level_definitions) {
124 if (luma_sample_rate <= def.characteristics.max_luma_sample_rate &&
125 luma_picture_size <= def.characteristics.max_luma_picture_size) {
130 LOG(WARNING) <<
"Cannot determine VP9 level for luma_sample_rate ("
131 << luma_sample_rate <<
") or luma_picture_size ("
132 << luma_picture_size <<
"). Returning LEVEL_1.";
138VPCodecConfigurationRecord::VPCodecConfigurationRecord() {}
140VPCodecConfigurationRecord::VPCodecConfigurationRecord(
144 uint8_t chroma_subsampling,
145 bool video_full_range_flag,
146 uint8_t color_primaries,
147 uint8_t transfer_characteristics,
148 uint8_t matrix_coefficients,
149 const std::vector<uint8_t>& codec_initialization_data)
152 bit_depth_(bit_depth),
153 chroma_subsampling_(chroma_subsampling),
154 video_full_range_flag_(video_full_range_flag),
155 color_primaries_(color_primaries),
156 transfer_characteristics_(transfer_characteristics),
157 matrix_coefficients_(matrix_coefficients),
158 codec_initialization_data_(codec_initialization_data) {}
160VPCodecConfigurationRecord::~VPCodecConfigurationRecord() {};
163bool VPCodecConfigurationRecord::ParseMP4(
const std::vector<uint8_t>& data) {
164 BitReader reader(data.data(), data.size());
173 chroma_subsampling_ = value;
175 RCHECK(reader.
ReadBits(1, &bool_value));
176 video_full_range_flag_ = bool_value;
178 color_primaries_ = value;
180 transfer_characteristics_ = value;
182 matrix_coefficients_ = value;
184 uint16_t codec_initialization_data_size = 0;
185 RCHECK(reader.
ReadBits(16, &codec_initialization_data_size));
186 RCHECK(reader.
bits_available() >= codec_initialization_data_size * 8u);
187 const size_t header_size = data.size() - reader.
bits_available() / 8;
188 codec_initialization_data_.assign(
189 data.begin() + header_size,
190 data.begin() + header_size + codec_initialization_data_size);
195bool VPCodecConfigurationRecord::ParseWebM(
const std::vector<uint8_t>& data) {
201 RCHECK(reader.
Read1(&
id));
202 RCHECK(reader.
Read1(&size));
206 case kFeatureProfile:
208 RCHECK(reader.
Read1(&value));
213 RCHECK(reader.
Read1(&value));
216 case kFeatureBitDepth:
218 RCHECK(reader.
Read1(&value));
221 case kFeatureChromaSubsampling:
223 RCHECK(reader.
Read1(&value));
224 chroma_subsampling_ = value;
227 LOG(WARNING) <<
"Skipping unknown VP9 codec feature " << id;
236void VPCodecConfigurationRecord::SetVP9Level(uint16_t width,
238 double sample_duration_seconds) {
241 const uint32_t luma_picture_size = width * height;
247 const double kUnknownSampleDuration = 0.0;
249 uint64_t luma_sample_rate = 0;
250 if (sample_duration_seconds != kUnknownSampleDuration)
251 luma_sample_rate = luma_picture_size / sample_duration_seconds;
253 level_ = LevelFromCharacteristics(luma_sample_rate, luma_picture_size);
256void VPCodecConfigurationRecord::WriteMP4(std::vector<uint8_t>* data)
const {
260 uint8_t bit_depth_chroma = (bit_depth() << 4) | (chroma_subsampling() << 1) |
261 (video_full_range_flag() ? 1 : 0);
264 writer.
AppendInt(transfer_characteristics());
266 uint16_t codec_initialization_data_size =
267 static_cast<uint16_t
>(codec_initialization_data_.size());
268 writer.
AppendInt(codec_initialization_data_size);
269 writer.AppendVector(codec_initialization_data_);
270 writer.SwapBuffer(data);
273void VPCodecConfigurationRecord::WriteWebM(std::vector<uint8_t>* data)
const {
277 writer.
AppendInt(
static_cast<uint8_t
>(kFeatureProfile));
278 writer.
AppendInt(
static_cast<uint8_t
>(1));
283 writer.
AppendInt(
static_cast<uint8_t
>(kFeatureLevel));
284 writer.
AppendInt(
static_cast<uint8_t
>(1));
289 writer.
AppendInt(
static_cast<uint8_t
>(kFeatureBitDepth));
290 writer.
AppendInt(
static_cast<uint8_t
>(1));
294 if (chroma_subsampling_) {
296 writer.
AppendInt(
static_cast<uint8_t
>(kFeatureChromaSubsampling));
297 writer.
AppendInt(
static_cast<uint8_t
>(1));
301 writer.SwapBuffer(data);
304std::string VPCodecConfigurationRecord::GetCodecString(Codec codec)
const {
305 const std::string fields[] = {
306 absl::StrFormat(
"%d", profile()),
307 absl::StrFormat(
"%d", level()),
308 absl::StrFormat(
"%d", bit_depth()),
309 absl::StrFormat(
"%d", chroma_subsampling()),
310 absl::StrFormat(
"%d", color_primaries()),
311 absl::StrFormat(
"%d", transfer_characteristics()),
312 absl::StrFormat(
"%d", matrix_coefficients()),
313 (video_full_range_flag_ && *video_full_range_flag_) ?
"01" :
"00",
316 std::string codec_string = VPCodecAsString(codec);
317 for (
const std::string& field : fields) {
320 absl::StrAppendFormat(&codec_string,
".%2s", field.c_str());
322 absl::StrReplaceAll({{
" ",
"0"}}, &codec_string);
326void VPCodecConfigurationRecord::MergeFrom(
328 MergeField(
"profile", other.profile_, &profile_);
329 MergeField(
"level", other.level_, &level_);
330 MergeField(
"bit depth", other.bit_depth_, &bit_depth_);
331 MergeField(
"chroma subsampling", other.chroma_subsampling_,
332 &chroma_subsampling_);
333 MergeField(
"video full range flag", other.video_full_range_flag_,
334 &video_full_range_flag_);
335 MergeField(
"color primaries", other.color_primaries_, &color_primaries_);
336 MergeField(
"transfer characteristics", other.transfer_characteristics_,
337 &transfer_characteristics_);
338 MergeField(
"matrix coefficients", other.matrix_coefficients_,
339 &matrix_coefficients_);
341 if (codec_initialization_data_.empty() ||
342 !other.codec_initialization_data_.empty()) {
343 if (!codec_initialization_data_.empty() &&
344 codec_initialization_data_ != other.codec_initialization_data_) {
345 LOG(WARNING) <<
"VPx codec initialization data is inconsistent";
347 codec_initialization_data_ = other.codec_initialization_data_;
350 MergeField(
"chroma location", other.chroma_location_, &chroma_location_);
351 UpdateChromaSubsamplingIfNeeded();
354void VPCodecConfigurationRecord::SetChromaSubsampling(uint8_t subsampling_x,
355 uint8_t subsampling_y) {
356 VLOG(3) <<
"Set Chroma subsampling " <<
static_cast<int>(subsampling_x) <<
" "
357 <<
static_cast<int>(subsampling_y);
358 if (subsampling_x == 0 && subsampling_y == 0) {
359 chroma_subsampling_ = CHROMA_444;
360 }
else if (subsampling_x == 0 && subsampling_y == 1) {
361 chroma_subsampling_ = CHROMA_440;
362 }
else if (subsampling_x == 1 && subsampling_y == 0) {
363 chroma_subsampling_ = CHROMA_422;
364 }
else if (subsampling_x == 1 && subsampling_y == 1) {
367 chroma_subsampling_ = CHROMA_420_COLLOCATED_WITH_LUMA;
369 LOG(WARNING) <<
"Unexpected chroma subsampling values: "
370 <<
static_cast<int>(subsampling_x) <<
" "
371 <<
static_cast<int>(subsampling_y);
373 UpdateChromaSubsamplingIfNeeded();
376void VPCodecConfigurationRecord::SetChromaSubsampling(
377 ChromaSubsampling chroma_subsampling) {
378 chroma_subsampling_ = chroma_subsampling;
379 UpdateChromaSubsamplingIfNeeded();
382void VPCodecConfigurationRecord::SetChromaLocation(uint8_t chroma_siting_x,
383 uint8_t chroma_siting_y) {
384 VLOG(3) <<
"Set Chroma Location " <<
static_cast<int>(chroma_siting_x) <<
" "
385 <<
static_cast<int>(chroma_siting_y);
386 if (chroma_siting_x == kLeftCollocated && chroma_siting_y == kTopCollocated) {
387 chroma_location_ = AVCHROMA_LOC_TOPLEFT;
388 }
else if (chroma_siting_x == kLeftCollocated && chroma_siting_y == kHalf) {
389 chroma_location_ = AVCHROMA_LOC_LEFT;
390 }
else if (chroma_siting_x == kHalf && chroma_siting_y == kTopCollocated) {
391 chroma_location_ = AVCHROMA_LOC_TOP;
392 }
else if (chroma_siting_x == kHalf && chroma_siting_y == kHalf) {
393 chroma_location_ = AVCHROMA_LOC_CENTER;
395 LOG(WARNING) <<
"Unexpected chroma siting values: "
396 <<
static_cast<int>(chroma_siting_x) <<
" "
397 <<
static_cast<int>(chroma_siting_y);
399 UpdateChromaSubsamplingIfNeeded();
402void VPCodecConfigurationRecord::UpdateChromaSubsamplingIfNeeded() {
404 if (chroma_location_ && chroma_subsampling_ &&
405 (*chroma_subsampling_ == CHROMA_420_VERTICAL ||
406 *chroma_subsampling_ == CHROMA_420_COLLOCATED_WITH_LUMA)) {
407 if (*chroma_location_ == AVCHROMA_LOC_TOPLEFT)
408 chroma_subsampling_ = CHROMA_420_COLLOCATED_WITH_LUMA;
409 else if (*chroma_location_ == AVCHROMA_LOC_LEFT)
410 chroma_subsampling_ = CHROMA_420_VERTICAL;
411 VLOG(3) <<
"Chroma subsampling " <<
static_cast<int>(*chroma_subsampling_);
All the methods that are virtual are virtual for mocking.