in vireo/internal/demux/mp4.cpp [292:401]
void parse_audio_codec_info(lsmash_track_parameters_t& track_param) {
const SampleType type = SampleType::Audio;
lsmash_audio_summary_t* audio_summary = (lsmash_audio_summary_t*)tracks(type).summary.get();
audio.sample_rate = audio_summary->frequency;
THROW_IF(find(kSampleRate.begin(), kSampleRate.end(), audio.sample_rate) == kSampleRate.end(), Unsupported);
audio.channels = audio_summary->channels;
THROW_IF(!audio.channels, Invalid);
THROW_IF(audio.channels > 2, Unsupported);
if (lsmash_check_box_type_identical(audio_summary->sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO)) { // AAC
THROW_IF(audio_summary->sample_size != 16, Unsupported);
THROW_IF(audio_summary->aot != MP4A_AUDIO_OBJECT_TYPE_NULL &&
audio_summary->aot != MP4A_AUDIO_OBJECT_TYPE_AAC_LC, Invalid);
audio.codec = settings::Audio::Codec::AAC_LC; // assume AAC-LC - unless stated otherwise via codec specific data
} else if (lsmash_check_box_type_identical(audio_summary->sample_type, QT_CODEC_TYPE_SOWT_AUDIO)) { // PCM 16-bit Little Endian
THROW_IF(audio_summary->sample_size != 16, Invalid);
audio.codec = settings::Audio::Codec::PCM_S16LE;
} else if (lsmash_check_box_type_identical(audio_summary->sample_type, QT_CODEC_TYPE_TWOS_AUDIO)) { // PCM 16-bit Big Endian
THROW_IF(audio_summary->sample_size != 16, Invalid);
audio.codec = settings::Audio::Codec::PCM_S16BE;
} else if (lsmash_check_box_type_identical(audio_summary->sample_type, QT_CODEC_TYPE_IN24_AUDIO)) { // PCM 24-bit
THROW_IF(audio_summary->sample_size != 24, Invalid);
audio.codec = settings::Audio::Codec::PCM_S24LE; // assume Little Endian - unless stated otherwise via codec specific data
} else if (lsmash_check_box_type_identical(audio_summary->sample_type, QT_CODEC_TYPE_LPCM_AUDIO)) { // PCM (various)
THROW_IF(audio_summary->sample_size != 16, Unsupported); // can technically be different but not tested
audio.codec = settings::Audio::Codec::PCM_S16LE; // assume Little Endian - unless stated otherwise via codec specific data
}
const uint32_t audio_cs_count = lsmash_count_codec_specific_data(tracks(type).summary.get());
THROW_IF(audio_cs_count > 10, Unsafe);
for (int i = 0; i < audio_cs_count; ++i) {
lsmash_codec_specific_t* cs = lsmash_get_codec_specific_data(tracks(type).summary.get(), i + 1);
CHECK(cs);
if (cs->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_MP4SYS_DECODER_CONFIG) {
THROW_IF(audio_summary->aot != MP4A_AUDIO_OBJECT_TYPE_AAC_LC, Unsupported);
THROW_IF(audio_summary->samples_in_frame != AUDIO_FRAME_SIZE, Unsupported);
lsmash_mp4sys_decoder_parameters_t* params = (lsmash_mp4sys_decoder_parameters_t*)cs->data.structured;
THROW_IF(params->objectTypeIndication != MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3, Invalid); // AAC
uint8_t* payload;
uint32_t payload_length;
lsmash_get_mp4sys_decoder_specific_info(params, &payload, &payload_length);
common::BitReader bit_reader(common::Data32(payload, payload_length, [](uint8_t* p) { free(p); }));
// ISO/IEC 14496-3 - Section 1.6.2.1 - AudioSpecificConfig - payload in bit string format (packed bits)
const uint8_t audio_object_type = bit_reader.read_bits(5); // audioObjectType (5 bits)
THROW_IF(audio_object_type != 2, Unsupported); // 2=AAC-LC, 5=SBR
audio.codec = settings::Audio::Codec::AAC_LC;
const uint8_t sampling_frequency_index = bit_reader.read_bits(4); // samplingFrequencyIndex (4 bits)
THROW_IF(sampling_frequency_index == 0x0F, Unsupported); // if 0x0F, samplingFrequency (24 bits) - not supported!
const uint8_t channel_configuration = bit_reader.read_bits(4); // channelConfiguration (4 bits)
THROW_IF(channel_configuration != 1 && channel_configuration != 2, Unsupported); // mono/stereo
// GASpecificConfig
THROW_IF(audio_object_type != 2, Invalid); // GASpecificConfig present only for audioObjectType == 2
const bool frame_length_flag = bit_reader.read_bits(1); // frameLengthFlag (1 bit)
THROW_IF(frame_length_flag, Unsupported); // if true, alternate frame length - not supported!
const bool depends_on_core_coder = bit_reader.read_bits(1); // dependsOnCoreCoder (1 bit)
THROW_IF(depends_on_core_coder, Unsupported); // if true, coreCoderDelay (14 bits) - not supported!
const bool extension_flag = bit_reader.read_bits(1); // extensionFlag (1 bit)
THROW_IF(extension_flag, Unsupported); // if true, more data - not supported!
if (bit_reader.remaining() >= 16) {
// potentially we might read 24 bits here but we are checking for >= 16 bits per standard
// BitReader will throw an exception if we try to read beyond the available data so it's safe
THROW_IF(audio_object_type == 5, Invalid);
const uint16_t sync_extension_type = bit_reader.read_bits(11); // syncExtensionType (11 bits)
if (sync_extension_type == 0x02B7) {
const uint8_t extension_audio_object_type = bit_reader.read_bits(5); // extensionAudioObjectType (5 bits)
THROW_IF(extension_audio_object_type != 5, Unsupported);
const bool sbr_present_flag = bit_reader.read_bits(1); // sbrPresentFlag (1 bit)
if (sbr_present_flag) {
const uint8_t extension_sampling_frequency_index = bit_reader.read_bits(4); // extensionSamplingFrequencyIndex (4 bits)
THROW_IF(extension_sampling_frequency_index + 3 != sampling_frequency_index, Invalid);
audio.codec = settings::Audio::Codec::AAC_LC_SBR;
}
}
}
break; // we found the info we wanted, no need to check the rest of the codec specific data
} else if (cs->type == LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS) {
if (settings::Audio::IsPCM(audio.codec)) {
lsmash_qt_audio_format_specific_flags_t* flags = (lsmash_qt_audio_format_specific_flags_t*)cs->data.structured;
THROW_IF(flags->format_flags & QT_AUDIO_FORMAT_FLAG_NON_INTERLEAVED, Unsupported);
if (lsmash_check_box_type_identical(audio_summary->sample_type, QT_CODEC_TYPE_IN24_AUDIO)) { // PCM 24-bit
CHECK(audio.codec == settings::Audio::Codec::PCM_S24LE);
if (flags->format_flags & QT_AUDIO_FORMAT_FLAG_BIG_ENDIAN) {
audio.codec = settings::Audio::Codec::PCM_S24BE; // Big Endian
}
} else if (lsmash_check_box_type_identical(audio_summary->sample_type, QT_CODEC_TYPE_LPCM_AUDIO)) { // LPCM
CHECK(audio.codec == settings::Audio::Codec::PCM_S16LE);
THROW_IF(!(flags->format_flags & QT_AUDIO_FORMAT_FLAG_SIGNED_INTEGER), Unsupported);
THROW_IF(!(flags->format_flags & QT_AUDIO_FORMAT_FLAG_PACKED), Unsupported);
if (flags->format_flags & QT_AUDIO_FORMAT_FLAG_BIG_ENDIAN) {
audio.codec = settings::Audio::Codec::PCM_S16BE; // Big Endian
}
}
break; // we found the info we wanted, no need to check the rest of the codec specific data
}
} else {
// fail if we see an unexpected codec specific data, otherwise just skip
THROW_IF(cs->type != LSMASH_CODEC_SPECIFIC_DATA_TYPE_UNKNOWN &&
cs->type != LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_COMMON &&
cs->type != LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT &&
cs->type != LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_DECOMPRESSION_PARAMETERS, Unsupported);
}
}
}