symphonia_format_riff/aiff/
mod.rs

1// Symphonia
2// Copyright (c) 2019-2022 The Project Symphonia Developers.
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at https://mozilla.org/MPL/2.0/.
7
8use std::io::{Seek, SeekFrom};
9
10use symphonia_core::codecs::CodecParameters;
11use symphonia_core::errors::{seek_error, unsupported_error};
12use symphonia_core::errors::{Result, SeekErrorKind};
13use symphonia_core::formats::prelude::*;
14use symphonia_core::io::*;
15use symphonia_core::meta::{Metadata, MetadataLog};
16use symphonia_core::probe::{Descriptor, Instantiate, QueryDescriptor};
17use symphonia_core::support_format;
18
19use log::debug;
20
21use crate::common::{
22    append_data_params, append_format_params, next_packet, ByteOrder, ChunksReader, PacketInfo,
23};
24mod chunks;
25use chunks::*;
26
27/// Aiff is actually a RIFF stream, with a "FORM" ASCII stream marker.
28const AIFF_STREAM_MARKER: [u8; 4] = *b"FORM";
29/// A possible RIFF form is "aiff".
30const AIFF_RIFF_FORM: [u8; 4] = *b"AIFF";
31/// A possible RIFF form is "aifc", using compressed data.
32const AIFC_RIFF_FORM: [u8; 4] = *b"AIFC";
33
34/// Audio Interchange File Format (AIFF) format reader.
35///
36/// `AiffReader` implements a demuxer for the AIFF container format.
37pub struct AiffReader {
38    reader: MediaSourceStream,
39    tracks: Vec<Track>,
40    cues: Vec<Cue>,
41    metadata: MetadataLog,
42    packet_info: PacketInfo,
43    data_start_pos: u64,
44    data_end_pos: u64,
45}
46
47impl QueryDescriptor for AiffReader {
48    fn query() -> &'static [Descriptor] {
49        &[
50            // AIFF RIFF form
51            support_format!(
52                "riff",
53                " Resource Interchange File Format",
54                &["aiff", "aif", "aifc"],
55                &["audio/aiff", "audio/x-aiff", " sound/aiff", "audio/x-pn-aiff"],
56                &[b"FORM"]
57            ),
58        ]
59    }
60
61    fn score(_context: &[u8]) -> u8 {
62        255
63    }
64}
65
66impl FormatReader for AiffReader {
67    fn try_new(mut source: MediaSourceStream, _options: &FormatOptions) -> Result<Self> {
68        // The FORM marker should be present.
69        let marker = source.read_quad_bytes()?;
70        if marker != AIFF_STREAM_MARKER {
71            return unsupported_error("aiff: missing riff stream marker");
72        }
73
74        // File is basically one RIFF chunk, with the actual meta and audio data as sub-chunks (called local chunks).
75        // Therefore, the header was the chunk ID, and the next 4 bytes is the length of the RIFF
76        // chunk.
77        let riff_len = source.read_be_u32()?;
78        let riff_form = source.read_quad_bytes()?;
79
80        let mut riff_chunks = ChunksReader::<RiffAiffChunks>::new(riff_len, ByteOrder::BigEndian);
81
82        let mut codec_params = CodecParameters::new();
83        //TODO: Chunks such as marker contain metadata, get it.
84        let metadata: MetadataLog = Default::default();
85        let mut packet_info = PacketInfo::without_blocks(0);
86
87        loop {
88            let chunk = riff_chunks.next(&mut source)?;
89
90            // The last chunk should always be a data chunk, if it is not, then the stream is
91            // unsupported.
92            // TODO: According to the spec additional chunks can be added after the sound data chunk. In fact any order can be possible.
93            if chunk.is_none() {
94                return unsupported_error("aiff: missing sound chunk");
95            }
96
97            match chunk.unwrap() {
98                RiffAiffChunks::Common(common) => {
99                    let common = match riff_form {
100                        AIFF_RIFF_FORM => common.parse_aiff(&mut source)?,
101                        AIFC_RIFF_FORM => common.parse_aifc(&mut source)?,
102                        _ => return unsupported_error("aiff: riff form is not supported"),
103                    };
104
105                    // The Format chunk contains the block_align field and possible additional information
106                    // to handle packetization and seeking.
107                    packet_info = common.packet_info()?;
108                    codec_params
109                        .with_max_frames_per_packet(packet_info.get_max_frames_per_packet())
110                        .with_frames_per_block(packet_info.frames_per_block);
111
112                    // Append Format chunk fields to codec parameters.
113                    append_format_params(
114                        &mut codec_params,
115                        &common.format_data,
116                        common.sample_rate,
117                    );
118                }
119                RiffAiffChunks::Sound(dat) => {
120                    let data = dat.parse(&mut source)?;
121
122                    // Record the bounds of the data chunk.
123                    let data_start_pos = source.pos();
124                    let data_end_pos = data_start_pos + u64::from(data.len);
125
126                    // Append Sound chunk fields to codec parameters.
127                    append_data_params(&mut codec_params, data.len as u64, &packet_info);
128
129                    // Add a new track using the collected codec parameters.
130                    return Ok(AiffReader {
131                        reader: source,
132                        tracks: vec![Track::new(0, codec_params)],
133                        cues: Vec::new(),
134                        metadata,
135                        packet_info,
136                        data_start_pos,
137                        data_end_pos,
138                    });
139                }
140            }
141        }
142    }
143
144    fn next_packet(&mut self) -> Result<Packet> {
145        next_packet(
146            &mut self.reader,
147            &self.packet_info,
148            &self.tracks,
149            self.data_start_pos,
150            self.data_end_pos,
151        )
152    }
153
154    fn metadata(&mut self) -> Metadata<'_> {
155        self.metadata.metadata()
156    }
157
158    fn cues(&self) -> &[Cue] {
159        &self.cues
160    }
161
162    fn tracks(&self) -> &[Track] {
163        &self.tracks
164    }
165
166    fn seek(&mut self, _mode: SeekMode, to: SeekTo) -> Result<SeekedTo> {
167        if self.tracks.is_empty() || self.packet_info.is_empty() {
168            return seek_error(SeekErrorKind::Unseekable);
169        }
170
171        let params = &self.tracks[0].codec_params;
172
173        let ts = match to {
174            // Frame timestamp given.
175            SeekTo::TimeStamp { ts, .. } => ts,
176            // Time value given, calculate frame timestamp from sample rate.
177            SeekTo::Time { time, .. } => {
178                // Use the sample rate to calculate the frame timestamp. If sample rate is not
179                // known, the seek cannot be completed.
180                if let Some(sample_rate) = params.sample_rate {
181                    TimeBase::new(1, sample_rate).calc_timestamp(time)
182                }
183                else {
184                    return seek_error(SeekErrorKind::Unseekable);
185                }
186            }
187        };
188
189        // If the total number of frames in the track is known, verify the desired frame timestamp
190        // does not exceed it.
191        if let Some(n_frames) = params.n_frames {
192            if ts > n_frames {
193                return seek_error(SeekErrorKind::OutOfRange);
194            }
195        }
196
197        debug!("seeking to frame_ts={}", ts);
198
199        // RIFF is not internally packetized for PCM codecs. Packetization is simulated by trying to
200        // read a constant number of samples or blocks every call to next_packet. Therefore, a packet begins
201        // wherever the data stream is currently positioned. Since timestamps on packets should be
202        // determinstic, instead of seeking to the exact timestamp requested and starting the next
203        // packet there, seek to a packet boundary. In this way, packets will have have the same
204        // timestamps regardless if the stream was seeked or not.
205        let actual_ts = self.packet_info.get_actual_ts(ts);
206
207        // Calculate the absolute byte offset of the desired audio frame.
208        let seek_pos = self.data_start_pos + (actual_ts * self.packet_info.block_size);
209
210        // If the reader supports seeking we can seek directly to the frame's offset wherever it may
211        // be.
212        if self.reader.is_seekable() {
213            self.reader.seek(SeekFrom::Start(seek_pos))?;
214        }
215        // If the reader does not support seeking, we can only emulate forward seeks by consuming
216        // bytes. If the reader has to seek backwards, return an error.
217        else {
218            let current_pos = self.reader.pos();
219            if seek_pos >= current_pos {
220                self.reader.ignore_bytes(seek_pos - current_pos)?;
221            }
222            else {
223                return seek_error(SeekErrorKind::ForwardOnly);
224            }
225        }
226
227        debug!("seeked to packet_ts={} (delta={})", actual_ts, actual_ts as i64 - ts as i64);
228
229        Ok(SeekedTo { track_id: 0, actual_ts, required_ts: ts })
230    }
231
232    fn into_inner(self: Box<Self>) -> MediaSourceStream {
233        self.reader
234    }
235}