fn read_audio_sample_entry()

in mp4parse/src/lib.rs [5642:5797]


fn read_audio_sample_entry<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleEntry> {
    let name = src.get_header().name;

    // Skip uninteresting fields.
    skip(src, 6)?;

    let data_reference_index = be_u16(src)?;

    // XXX(kinetik): This is "reserved" in BMFF, but some old QT MOV variant
    // uses it, need to work out if we have to support it.  Without checking
    // here and reading extra fields after samplerate (or bailing with an
    // error), the parser loses sync completely.
    let version = be_u16(src)?;

    // Skip uninteresting fields.
    skip(src, 6)?;

    let mut channelcount = u32::from(be_u16(src)?);
    let samplesize = be_u16(src)?;

    // Skip uninteresting fields.
    skip(src, 4)?;

    let mut samplerate = f64::from(be_u32(src)? >> 16); // 16.16 fixed point;

    match version {
        0 => (),
        1 => {
            // Quicktime sound sample description version 1.
            // Skip uninteresting fields.
            skip(src, 16)?;
        }
        2 => {
            // Quicktime sound sample description version 2.
            skip(src, 4)?;
            samplerate = f64::from_bits(be_u64(src)?);
            channelcount = be_u32(src)?;
            skip(src, 20)?;
        }
        _ => {
            return Err(Error::Unsupported(
                "unsupported non-isom audio sample entry",
            ))
        }
    }

    let (mut codec_type, mut codec_specific) = match name {
        BoxType::MP3AudioSampleEntry => (CodecType::MP3, Some(AudioCodecSpecific::MP3)),
        BoxType::LPCMAudioSampleEntry => (CodecType::LPCM, Some(AudioCodecSpecific::LPCM)),
        // Some mp4 file with AMR doesn't have AMRSpecificBox "damr" in followed while loop,
        // we use empty box by default.
        #[cfg(feature = "3gpp")]
        BoxType::AMRNBSampleEntry => (
            CodecType::AMRNB,
            Some(AudioCodecSpecific::AMRSpecificBox(Default::default())),
        ),
        #[cfg(feature = "3gpp")]
        BoxType::AMRWBSampleEntry => (
            CodecType::AMRWB,
            Some(AudioCodecSpecific::AMRSpecificBox(Default::default())),
        ),
        _ => (CodecType::Unknown, None),
    };
    let mut protection_info = TryVec::new();
    let mut iter = src.box_iter();
    while let Some(mut b) = iter.next_box()? {
        match b.head.name {
            BoxType::ESDBox => {
                if (name != BoxType::MP4AudioSampleEntry
                    && name != BoxType::ProtectedAudioSampleEntry)
                    || codec_specific.is_some()
                {
                    return Status::StsdBadAudioSampleEntry.into();
                }
                let esds = read_esds(&mut b)?;
                codec_type = esds.audio_codec;
                codec_specific = Some(AudioCodecSpecific::ES_Descriptor(esds));
            }
            BoxType::FLACSpecificBox => {
                if (name != BoxType::FLACSampleEntry && name != BoxType::ProtectedAudioSampleEntry)
                    || codec_specific.is_some()
                {
                    return Status::StsdBadAudioSampleEntry.into();
                }
                let dfla = read_dfla(&mut b)?;
                codec_type = CodecType::FLAC;
                codec_specific = Some(AudioCodecSpecific::FLACSpecificBox(dfla));
            }
            BoxType::OpusSpecificBox => {
                if (name != BoxType::OpusSampleEntry && name != BoxType::ProtectedAudioSampleEntry)
                    || codec_specific.is_some()
                {
                    return Status::StsdBadAudioSampleEntry.into();
                }
                let dops = read_dops(&mut b)?;
                codec_type = CodecType::Opus;
                codec_specific = Some(AudioCodecSpecific::OpusSpecificBox(dops));
            }
            BoxType::ALACSpecificBox => {
                if name != BoxType::ALACSpecificBox || codec_specific.is_some() {
                    return Status::StsdBadAudioSampleEntry.into();
                }
                let alac = read_alac(&mut b)?;
                codec_type = CodecType::ALAC;
                codec_specific = Some(AudioCodecSpecific::ALACSpecificBox(alac));
            }
            BoxType::QTWaveAtom => {
                let qt_esds = read_qt_wave_atom(&mut b)?;
                codec_type = qt_esds.audio_codec;
                codec_specific = Some(AudioCodecSpecific::ES_Descriptor(qt_esds));
            }
            BoxType::ProtectionSchemeInfoBox => {
                if name != BoxType::ProtectedAudioSampleEntry {
                    return Status::StsdBadAudioSampleEntry.into();
                }
                let sinf = read_sinf(&mut b)?;
                debug!("{:?} (sinf)", sinf);
                codec_type = CodecType::EncryptedAudio;
                protection_info.push(sinf)?;
            }
            #[cfg(feature = "3gpp")]
            BoxType::AMRSpecificBox => {
                if codec_type != CodecType::AMRNB && codec_type != CodecType::AMRWB {
                    return Status::StsdBadAudioSampleEntry.into();
                }
                let amr_dec_spec_struc_size = b
                    .head
                    .size
                    .checked_sub(b.head.offset)
                    .expect("offset invalid");
                let amr_dec_spec_struc = read_buf(&mut b.content, amr_dec_spec_struc_size)?;
                debug!("{:?} (AMRDecSpecStruc)", amr_dec_spec_struc);
                codec_specific = Some(AudioCodecSpecific::AMRSpecificBox(amr_dec_spec_struc));
            }
            _ => {
                debug!("Unsupported audio codec, box {:?} found", b.head.name);
                skip_box_content(&mut b)?;
            }
        }
        check_parser_state!(b.content);
    }

    Ok(
        codec_specific.map_or(SampleEntry::Unknown, |codec_specific| {
            SampleEntry::Audio(AudioSampleEntry {
                codec_type,
                data_reference_index,
                channelcount,
                samplesize,
                samplerate,
                codec_specific,
                protection_info,
            })
        }),
    )
}