Skip to content

Commit 2c2b389

Browse files
committed
media: Update VideoDmpReader handling of IAMF
This change modifies VideoDmpReader to parse the configuration of IAMF dmp files, allowing it to craft an accurate MIME type string. Bug: 485254424
1 parent e89a362 commit 2c2b389

File tree

2 files changed

+90
-1
lines changed

2 files changed

+90
-1
lines changed

starboard/shared/starboard/player/video_dmp_reader.cc

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616

1717
#include <algorithm>
1818
#include <functional>
19+
#include <iomanip>
1920

2021
#include "starboard/common/check_op.h"
2122

2223
namespace starboard {
2324

2425
namespace {
2526

27+
constexpr uint8_t kIamfSequenceHeaderObu = 31;
28+
2629
template <typename AccessUnit>
2730
int64_t CalculateAverageBitrate(const std::vector<AccessUnit>& access_units) {
2831
if (access_units.empty()) {
@@ -71,6 +74,29 @@ SbPlayerSampleInfo ConvertToPlayerSampleInfo(
7174
return sample_info;
7275
}
7376

77+
// Helper function to read a LEB128 value.
78+
bool ReadLeb128(const uint8_t** data, const uint8_t* end, uint32_t* value) {
79+
SB_DCHECK(data && *data);
80+
SB_DCHECK(end);
81+
SB_DCHECK(value);
82+
*value = 0;
83+
for (size_t i = 0; i < 5; ++i) {
84+
if (*data >= end) {
85+
return false;
86+
}
87+
uint8_t byte = *(*data)++;
88+
if (i == 4 && (byte & 0x7f) > 0x0f) {
89+
// A 32-bit value can't use more than 4 bits from the 5th LEB128 byte.
90+
return false;
91+
}
92+
*value |= (uint32_t)(byte & 0x7f) << (i * 7);
93+
if (!(byte & 0x80)) {
94+
return true;
95+
}
96+
}
97+
return false;
98+
}
99+
74100
} // namespace
75101

76102
using std::placeholders::_1;
@@ -157,7 +183,16 @@ std::string VideoDmpReader::audio_mime_type() const {
157183
ss << "audio/wav; codecs=\"1\";";
158184
break;
159185
case kSbMediaAudioCodecIamf:
160-
ss << "audio/mp4; codecs=\"iamf\";";
186+
if (!dmp_info_.iamf_primary_profile.has_value()) {
187+
return "";
188+
}
189+
// Only Opus IAMF substreams are currently supported.
190+
ss << "audio/mp4; codecs=\"iamf.";
191+
ss << std::setw(3) << std::setfill('0') << std::hex
192+
<< static_cast<int>(*dmp_info_.iamf_primary_profile) << "."
193+
<< std::setw(3) << std::setfill('0') << std::hex
194+
<< static_cast<int>(dmp_info_.iamf_additional_profile.value_or(0))
195+
<< ".Opus\";";
161196
break;
162197
default:
163198
SB_NOTREACHED() << "Unsupported audio codec: " << dmp_info_.audio_codec;
@@ -230,6 +265,52 @@ const AudioSampleInfo& VideoDmpReader::GetAudioSampleInfo(size_t index) {
230265
return au.audio_sample_info();
231266
}
232267

268+
// Parse IAMF Config OBUs for the primary and additional profiles,
269+
// based on IAMF specification v1.0.0-errata.
270+
// https://aomediacodec.github.io/iamf/v1.1.0.html#codecsparameter.
271+
void VideoDmpReader::ParseIamfConfigOBU() {
272+
if (dmp_info_.audio_codec != kSbMediaAudioCodecIamf) {
273+
return;
274+
}
275+
276+
if (audio_access_units_.empty()) {
277+
SB_LOG(WARNING) << "IAMF stream has no access units.";
278+
return;
279+
}
280+
281+
const auto& data = audio_access_units_[0].data();
282+
const uint8_t* ptr = data.data();
283+
const uint8_t* end = ptr + data.size();
284+
285+
uint8_t header_byte = *ptr++;
286+
uint8_t obu_type = (header_byte >> 3) & 0x1f;
287+
288+
uint32_t obu_size;
289+
if (!ReadLeb128(&ptr, end, &obu_size)) {
290+
SB_LOG(ERROR) << "Failed to parse OBU size.";
291+
return;
292+
}
293+
294+
if (static_cast<size_t>(end - ptr) < obu_size) {
295+
SB_LOG(ERROR) << "OBU size exceeds access unit size.";
296+
return;
297+
}
298+
const uint8_t* obu_end = ptr + obu_size;
299+
300+
if (obu_type == kIamfSequenceHeaderObu) {
301+
if (ptr + sizeof(uint32_t) + 2 > obu_end) {
302+
return;
303+
}
304+
// Skip ia_code (4 bytes).
305+
ptr += sizeof(uint32_t);
306+
307+
dmp_info_.iamf_primary_profile = *ptr++;
308+
dmp_info_.iamf_additional_profile = *ptr;
309+
}
310+
// The Sequence Header must be the first OBU. If it isn't found, leave the
311+
// profile values unset.
312+
}
313+
233314
void VideoDmpReader::ParseHeader(uint32_t* dmp_writer_version) {
234315
SB_DCHECK(dmp_writer_version);
235316
SB_DCHECK(!reverse_byte_order_.has_value());
@@ -305,6 +386,11 @@ void VideoDmpReader::Parse() {
305386
while (ParseOneRecord()) {
306387
}
307388

389+
if (dmp_info_.audio_codec == kSbMediaAudioCodecIamf &&
390+
!dmp_info_.iamf_primary_profile.has_value()) {
391+
ParseIamfConfigOBU();
392+
}
393+
308394
dmp_info_.audio_access_units_size = audio_access_units_.size();
309395
dmp_info_.audio_bitrate = CalculateAverageBitrate(audio_access_units_);
310396
dmp_info_.video_access_units_size = video_access_units_.size();

starboard/shared/starboard/player/video_dmp_reader.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ class VideoDmpReader {
138138
size_t audio_access_units_size = 0;
139139
int64_t audio_bitrate = 0;
140140
int audio_duration = 0;
141+
std::optional<uint8_t> iamf_primary_profile;
142+
std::optional<uint8_t> iamf_additional_profile;
141143

142144
SbMediaVideoCodec video_codec = kSbMediaVideoCodecNone;
143145
size_t video_access_units_size = 0;
@@ -159,6 +161,7 @@ class VideoDmpReader {
159161
VideoDmpReader(const VideoDmpReader&) = delete;
160162
VideoDmpReader& operator=(const VideoDmpReader&) = delete;
161163

164+
void ParseIamfConfigOBU();
162165
void ParseHeader(uint32_t* dmp_writer_version);
163166
bool ParseOneRecord();
164167
void Parse();

0 commit comments

Comments
 (0)