7 #include <packager/media/codecs/vp_codec_configuration_record.h>
9 #include <absl/strings/str_format.h>
10 #include <absl/strings/str_replace.h>
12 #include <packager/macros/logging.h>
13 #include <packager/media/base/bit_reader.h>
14 #include <packager/media/base/buffer_reader.h>
15 #include <packager/media/base/buffer_writer.h>
16 #include <packager/media/base/rcheck.h>
21 enum VP9CodecFeatures {
25 kFeatureChromaSubsampling = 4,
28 std::string VPCodecAsString(Codec codec) {
35 LOG(WARNING) <<
"Unknown VP codec: " << codec;
41 void MergeField(
const std::string& name,
42 const std::optional<T>& source_value,
43 std::optional<T>* dest_value) {
45 if (source_value && *source_value != **dest_value) {
46 LOG(WARNING) <<
"VPx " << name <<
" is inconsistent, "
47 <<
static_cast<int>(**dest_value) <<
" vs "
48 <<
static_cast<int>(*source_value);
52 *dest_value = source_value;
75 struct VP9LevelCharacteristics {
76 uint64_t max_luma_sample_rate;
77 uint32_t max_luma_picture_size;
78 double max_avg_bitrate;
80 double min_compression_ratio;
81 uint8_t max_num_column_tiles;
82 uint32_t min_altref_distance;
83 uint8_t max_ref_frame_buffers;
86 struct VP9LevelDefinition {
88 VP9LevelCharacteristics characteristics;
91 VP9Level LevelFromCharacteristics(uint64_t luma_sample_rate,
92 uint32_t luma_picture_size) {
94 const VP9LevelDefinition vp9_level_definitions[] = {
95 {LEVEL_1, {829440, 36864, 200, 400, 2, 1, 4, 8}},
96 {LEVEL_1_1, {2764800, 73728, 800, 1000, 2, 1, 4, 8}},
97 {LEVEL_2, {4608000, 122880, 1800, 1500, 2, 1, 4, 8}},
98 {LEVEL_2_1, {9216000, 245760, 3600, 2800, 2, 2, 4, 8}},
99 {LEVEL_3, {20736000, 552960, 7200, 6000, 2, 4, 4, 8}},
100 {LEVEL_3_1, {36864000, 983040, 12000, 10000, 2, 4, 4, 8}},
101 {LEVEL_4, {83558400, 2228224, 18000, 16000, 4, 4, 4, 8}},
102 {LEVEL_4_1, {160432128, 2228224, 30000, 18000, 4, 4, 5, 6}},
103 {LEVEL_5, {311951360, 8912896, 60000, 36000, 6, 8, 6, 4}},
104 {LEVEL_5_1, {588251136, 8912896, 120000, 46000, 8, 8, 10, 4}},
105 {LEVEL_5_2, {1176502272, 8912896, 180000, 90000, 8, 8, 10, 4}},
106 {LEVEL_6, {1176502272, 35651584, 180000, 90000, 8, 16, 10, 4}},
107 {LEVEL_6_1, {2353004544u, 35651584, 240000, 180000, 8, 16, 10, 4}},
108 {LEVEL_6_2, {4706009088u, 35651584, 480000, 360000, 8, 16, 10, 4}},
111 for (
const VP9LevelDefinition& def : vp9_level_definitions) {
117 if (luma_sample_rate <= def.characteristics.max_luma_sample_rate &&
118 luma_picture_size <= def.characteristics.max_luma_picture_size) {
123 LOG(WARNING) <<
"Cannot determine VP9 level for luma_sample_rate ("
124 << luma_sample_rate <<
") or luma_picture_size ("
125 << luma_picture_size <<
"). Returning LEVEL_1.";
131 VPCodecConfigurationRecord::VPCodecConfigurationRecord() {}
133 VPCodecConfigurationRecord::VPCodecConfigurationRecord(
137 uint8_t chroma_subsampling,
138 bool video_full_range_flag,
139 uint8_t color_primaries,
140 uint8_t transfer_characteristics,
141 uint8_t matrix_coefficients,
142 const std::vector<uint8_t>& codec_initialization_data)
145 bit_depth_(bit_depth),
146 chroma_subsampling_(chroma_subsampling),
147 video_full_range_flag_(video_full_range_flag),
148 color_primaries_(color_primaries),
149 transfer_characteristics_(transfer_characteristics),
150 matrix_coefficients_(matrix_coefficients),
151 codec_initialization_data_(codec_initialization_data) {}
153 VPCodecConfigurationRecord::~VPCodecConfigurationRecord(){};
156 bool VPCodecConfigurationRecord::ParseMP4(
const std::vector<uint8_t>& data) {
157 BitReader reader(data.data(), data.size());
166 chroma_subsampling_ = value;
168 RCHECK(reader.
ReadBits(1, &bool_value));
169 video_full_range_flag_ = bool_value;
171 color_primaries_ = value;
173 transfer_characteristics_ = value;
175 matrix_coefficients_ = value;
177 uint16_t codec_initialization_data_size = 0;
178 RCHECK(reader.
ReadBits(16, &codec_initialization_data_size));
179 RCHECK(reader.
bits_available() >= codec_initialization_data_size * 8u);
180 const size_t header_size = data.size() - reader.
bits_available() / 8;
181 codec_initialization_data_.assign(
182 data.begin() + header_size,
183 data.begin() + header_size + codec_initialization_data_size);
188 bool VPCodecConfigurationRecord::ParseWebM(
const std::vector<uint8_t>& data) {
194 RCHECK(reader.
Read1(&
id));
195 RCHECK(reader.
Read1(&size));
199 case kFeatureProfile:
201 RCHECK(reader.
Read1(&value));
206 RCHECK(reader.
Read1(&value));
209 case kFeatureBitDepth:
211 RCHECK(reader.
Read1(&value));
214 case kFeatureChromaSubsampling:
216 RCHECK(reader.
Read1(&value));
217 chroma_subsampling_ = value;
220 LOG(WARNING) <<
"Skipping unknown VP9 codec feature " << id;
229 void VPCodecConfigurationRecord::SetVP9Level(uint16_t width,
231 double sample_duration_seconds) {
234 const uint32_t luma_picture_size = width * height;
240 const double kUnknownSampleDuration = 0.0;
242 uint64_t luma_sample_rate = 0;
243 if (sample_duration_seconds != kUnknownSampleDuration)
244 luma_sample_rate = luma_picture_size / sample_duration_seconds;
246 level_ = LevelFromCharacteristics(luma_sample_rate, luma_picture_size);
249 void VPCodecConfigurationRecord::WriteMP4(std::vector<uint8_t>* data)
const {
253 uint8_t bit_depth_chroma = (bit_depth() << 4) | (chroma_subsampling() << 1) |
254 (video_full_range_flag() ? 1 : 0);
257 writer.
AppendInt(transfer_characteristics());
259 uint16_t codec_initialization_data_size =
260 static_cast<uint16_t
>(codec_initialization_data_.size());
261 writer.
AppendInt(codec_initialization_data_size);
262 writer.AppendVector(codec_initialization_data_);
263 writer.SwapBuffer(data);
266 void VPCodecConfigurationRecord::WriteWebM(std::vector<uint8_t>* data)
const {
270 writer.
AppendInt(
static_cast<uint8_t
>(kFeatureProfile));
271 writer.
AppendInt(
static_cast<uint8_t
>(1));
276 writer.
AppendInt(
static_cast<uint8_t
>(kFeatureLevel));
277 writer.
AppendInt(
static_cast<uint8_t
>(1));
282 writer.
AppendInt(
static_cast<uint8_t
>(kFeatureBitDepth));
283 writer.
AppendInt(
static_cast<uint8_t
>(1));
287 if (chroma_subsampling_) {
289 writer.
AppendInt(
static_cast<uint8_t
>(kFeatureChromaSubsampling));
290 writer.
AppendInt(
static_cast<uint8_t
>(1));
294 writer.SwapBuffer(data);
297 std::string VPCodecConfigurationRecord::GetCodecString(Codec codec)
const {
298 const std::string fields[] = {
299 absl::StrFormat(
"%d", profile()),
300 absl::StrFormat(
"%d", level()),
301 absl::StrFormat(
"%d", bit_depth()),
302 absl::StrFormat(
"%d", chroma_subsampling()),
303 absl::StrFormat(
"%d", color_primaries()),
304 absl::StrFormat(
"%d", transfer_characteristics()),
305 absl::StrFormat(
"%d", matrix_coefficients()),
306 (video_full_range_flag_ && *video_full_range_flag_) ?
"01" :
"00",
309 std::string codec_string = VPCodecAsString(codec);
310 for (
const std::string& field : fields) {
313 absl::StrAppendFormat(&codec_string,
".%2s", field.c_str());
315 absl::StrReplaceAll({{
" ",
"0"}}, &codec_string);
319 void VPCodecConfigurationRecord::MergeFrom(
321 MergeField(
"profile", other.profile_, &profile_);
322 MergeField(
"level", other.level_, &level_);
323 MergeField(
"bit depth", other.bit_depth_, &bit_depth_);
324 MergeField(
"chroma subsampling", other.chroma_subsampling_,
325 &chroma_subsampling_);
326 MergeField(
"video full range flag", other.video_full_range_flag_,
327 &video_full_range_flag_);
328 MergeField(
"color primaries", other.color_primaries_, &color_primaries_);
329 MergeField(
"transfer characteristics", other.transfer_characteristics_,
330 &transfer_characteristics_);
331 MergeField(
"matrix coefficients", other.matrix_coefficients_,
332 &matrix_coefficients_);
334 if (codec_initialization_data_.empty() ||
335 !other.codec_initialization_data_.empty()) {
336 if (!codec_initialization_data_.empty() &&
337 codec_initialization_data_ != other.codec_initialization_data_) {
338 LOG(WARNING) <<
"VPx codec initialization data is inconsistent";
340 codec_initialization_data_ = other.codec_initialization_data_;
343 MergeField(
"chroma location", other.chroma_location_, &chroma_location_);
344 UpdateChromaSubsamplingIfNeeded();
347 void VPCodecConfigurationRecord::SetChromaSubsampling(uint8_t subsampling_x,
348 uint8_t subsampling_y) {
349 VLOG(3) <<
"Set Chroma subsampling " <<
static_cast<int>(subsampling_x) <<
" "
350 <<
static_cast<int>(subsampling_y);
351 if (subsampling_x == 0 && subsampling_y == 0) {
352 chroma_subsampling_ = CHROMA_444;
353 }
else if (subsampling_x == 0 && subsampling_y == 1) {
354 chroma_subsampling_ = CHROMA_440;
355 }
else if (subsampling_x == 1 && subsampling_y == 0) {
356 chroma_subsampling_ = CHROMA_422;
357 }
else if (subsampling_x == 1 && subsampling_y == 1) {
360 chroma_subsampling_ = CHROMA_420_COLLOCATED_WITH_LUMA;
362 LOG(WARNING) <<
"Unexpected chroma subsampling values: "
363 <<
static_cast<int>(subsampling_x) <<
" "
364 <<
static_cast<int>(subsampling_y);
366 UpdateChromaSubsamplingIfNeeded();
369 void VPCodecConfigurationRecord::SetChromaSubsampling(
370 ChromaSubsampling chroma_subsampling) {
371 chroma_subsampling_ = chroma_subsampling;
372 UpdateChromaSubsamplingIfNeeded();
375 void VPCodecConfigurationRecord::SetChromaLocation(uint8_t chroma_siting_x,
376 uint8_t chroma_siting_y) {
377 VLOG(3) <<
"Set Chroma Location " <<
static_cast<int>(chroma_siting_x) <<
" "
378 <<
static_cast<int>(chroma_siting_y);
379 if (chroma_siting_x == kLeftCollocated && chroma_siting_y == kTopCollocated) {
380 chroma_location_ = AVCHROMA_LOC_TOPLEFT;
381 }
else if (chroma_siting_x == kLeftCollocated && chroma_siting_y == kHalf) {
382 chroma_location_ = AVCHROMA_LOC_LEFT;
383 }
else if (chroma_siting_x == kHalf && chroma_siting_y == kTopCollocated) {
384 chroma_location_ = AVCHROMA_LOC_TOP;
385 }
else if (chroma_siting_x == kHalf && chroma_siting_y == kHalf) {
386 chroma_location_ = AVCHROMA_LOC_CENTER;
388 LOG(WARNING) <<
"Unexpected chroma siting values: "
389 <<
static_cast<int>(chroma_siting_x) <<
" "
390 <<
static_cast<int>(chroma_siting_y);
392 UpdateChromaSubsamplingIfNeeded();
395 void VPCodecConfigurationRecord::UpdateChromaSubsamplingIfNeeded() {
397 if (chroma_location_ && chroma_subsampling_ &&
398 (*chroma_subsampling_ == CHROMA_420_VERTICAL ||
399 *chroma_subsampling_ == CHROMA_420_COLLOCATED_WITH_LUMA)) {
400 if (*chroma_location_ == AVCHROMA_LOC_TOPLEFT)
401 chroma_subsampling_ = CHROMA_420_COLLOCATED_WITH_LUMA;
402 else if (*chroma_location_ == AVCHROMA_LOC_LEFT)
403 chroma_subsampling_ = CHROMA_420_VERTICAL;
404 VLOG(3) <<
"Chroma subsampling " <<
static_cast<int>(*chroma_subsampling_);
All the methods that are virtual are virtual for mocking.