librespot_playback/decoder/
symphonia_decoder.rs1use std::{io, time::Duration};
2
3use symphonia::core::{
4 audio::SampleBuffer,
5 codecs::{Decoder, DecoderOptions},
6 errors::Error,
7 formats::{FormatOptions, SeekMode, SeekTo},
8 io::{MediaSource, MediaSourceStream, MediaSourceStreamOptions},
9 meta::{MetadataOptions, StandardTagKey, Value},
10 probe::{Hint, ProbeResult},
11};
12
13use super::{AudioDecoder, AudioPacket, AudioPacketPosition, DecoderError, DecoderResult};
14
15use crate::{NUM_CHANNELS, PAGES_PER_MS, SAMPLE_RATE, player::NormalisationData, symphonia_util};
16
17pub struct SymphoniaDecoder {
18 probe_result: ProbeResult,
19 decoder: Box<dyn Decoder>,
20 sample_buffer: Option<SampleBuffer<f64>>,
21}
22
23#[derive(Default)]
24pub(crate) struct LocalFileMetadata {
25 pub name: Option<String>,
26 pub language: Option<String>,
27 pub album: Option<String>,
28 pub artists: Option<String>,
29 pub album_artists: Option<String>,
30 pub number: Option<u32>,
31 pub disc_number: Option<u32>,
32}
33
34impl SymphoniaDecoder {
35 pub fn new<R>(input: R, hint: Hint) -> DecoderResult<Self>
36 where
37 R: MediaSource + 'static,
38 {
39 let mss_opts = MediaSourceStreamOptions {
40 buffer_len: librespot_audio::AudioFetchParams::get().minimum_download_size,
41 };
42 let mss = MediaSourceStream::new(Box::new(input), mss_opts);
43
44 let format_opts = FormatOptions {
45 enable_gapless: true,
46 ..Default::default()
47 };
48 let metadata_opts: MetadataOptions = Default::default();
49
50 let probe_result =
51 symphonia::default::get_probe().format(&hint, mss, &format_opts, &metadata_opts)?;
52
53 let format = &probe_result.format;
54
55 let track = format.default_track().ok_or_else(|| {
56 DecoderError::SymphoniaDecoder("Could not retrieve default track".into())
57 })?;
58
59 let decoder_opts: DecoderOptions = Default::default();
60
61 let decoder = symphonia::default::get_codecs().make(&track.codec_params, &decoder_opts)?;
62
63 let rate = decoder.codec_params().sample_rate.ok_or_else(|| {
64 DecoderError::SymphoniaDecoder("Could not retrieve sample rate".into())
65 })?;
66
67 if rate != SAMPLE_RATE {
72 return Err(DecoderError::SymphoniaDecoder(format!(
73 "Unsupported sample rate: {rate}"
74 )));
75 }
76
77 let channels = decoder.codec_params().channels.ok_or_else(|| {
78 DecoderError::SymphoniaDecoder("Could not retrieve channel configuration".into())
79 })?;
80 if channels.count() != NUM_CHANNELS as usize {
81 return Err(DecoderError::SymphoniaDecoder(format!(
82 "Unsupported number of channels: {channels}"
83 )));
84 }
85
86 Ok(Self {
87 probe_result,
88 decoder,
89 sample_buffer: None,
92 })
93 }
94
95 pub fn normalisation_data(&mut self) -> Option<NormalisationData> {
96 let metadata = symphonia_util::get_latest_metadata(&mut self.probe_result)?;
97 let tags = metadata.current()?.tags();
98
99 if tags.is_empty() {
100 None
101 } else {
102 let mut data = NormalisationData::default();
103
104 for tag in tags {
105 if let Value::Float(value) = tag.value {
106 match tag.std_key {
107 Some(StandardTagKey::ReplayGainAlbumGain) => data.album_gain_db = value,
108 Some(StandardTagKey::ReplayGainAlbumPeak) => data.album_peak = value,
109 Some(StandardTagKey::ReplayGainTrackGain) => data.track_gain_db = value,
110 Some(StandardTagKey::ReplayGainTrackPeak) => data.track_peak = value,
111 _ => (),
112 }
113 }
114 }
115
116 Some(data)
117 }
118 }
119
120 pub(crate) fn local_file_metadata(&mut self) -> Option<LocalFileMetadata> {
121 let metadata = symphonia_util::get_latest_metadata(&mut self.probe_result)?;
122 let tags = metadata.current()?.tags();
123 let mut metadata = LocalFileMetadata::default();
124
125 for tag in tags {
126 if let Value::String(value) = &tag.value {
127 match tag.std_key {
128 Some(StandardTagKey::TrackTitle) => metadata.name = Some(value.clone()),
131 Some(StandardTagKey::Language) => metadata.language = Some(value.clone()),
132 Some(StandardTagKey::Artist) => metadata.artists = Some(value.clone()),
133 Some(StandardTagKey::AlbumArtist) => {
134 metadata.album_artists = Some(value.clone())
135 }
136 Some(StandardTagKey::Album) => metadata.album = Some(value.clone()),
137 Some(StandardTagKey::TrackNumber) => {
138 metadata.number = match value.parse::<u32>() {
139 Ok(value) => Some(value),
140 Err(e) => {
141 warn!(
142 "Failed to parse local file's track number tag '{value}': {e}"
143 );
144 None
145 }
146 }
147 }
148 Some(StandardTagKey::DiscNumber) => {
149 metadata.disc_number = match value.parse::<u32>() {
150 Ok(value) => Some(value),
151 Err(e) => {
152 warn!(
153 "Failed to parse local file's disc number tag '{value}': {e}"
154 );
155 None
156 }
157 }
158 }
159 _ => (),
160 }
161 } else if let Value::UnsignedInt(value) = &tag.value {
162 match tag.std_key {
163 Some(StandardTagKey::TrackNumber) => metadata.number = Some(*value as u32),
164 Some(StandardTagKey::DiscNumber) => metadata.disc_number = Some(*value as u32),
165 _ => (),
166 }
167 } else if let Value::SignedInt(value) = &tag.value {
168 match tag.std_key {
169 Some(StandardTagKey::TrackNumber) => metadata.number = Some(*value as u32),
170 Some(StandardTagKey::DiscNumber) => metadata.disc_number = Some(*value as u32),
171 _ => (),
172 }
173 }
174 }
175
176 Some(metadata)
177 }
178
179 #[inline]
180 fn ts_to_ms(&self, ts: u64) -> u32 {
181 match self.decoder.codec_params().time_base {
182 Some(time_base) => {
183 let time = Duration::from(time_base.calc_time(ts));
184 time.as_millis() as u32
185 }
186 None => (ts as f64 * PAGES_PER_MS) as u32,
188 }
189 }
190}
191
192impl AudioDecoder for SymphoniaDecoder {
193 fn seek(&mut self, position_ms: u32) -> Result<u32, DecoderError> {
194 let mut target = Duration::from_millis(position_ms.into());
196 let codec_params = self.decoder.codec_params();
197 if let (Some(time_base), Some(n_frames)) = (codec_params.time_base, codec_params.n_frames) {
198 let duration = Duration::from(time_base.calc_time(n_frames));
199 if target > duration {
200 target = duration;
201 }
202 }
203
204 let seeked_to_ts = self.probe_result.format.seek(
206 SeekMode::Accurate,
207 SeekTo::Time {
208 time: target.into(),
209 track_id: None,
210 },
211 )?;
212
213 self.decoder.reset();
216
217 Ok(self.ts_to_ms(seeked_to_ts.actual_ts))
218 }
219
220 fn next_packet(&mut self) -> DecoderResult<Option<(AudioPacketPosition, AudioPacket)>> {
221 let mut skipped = false;
222
223 loop {
224 let packet = match self.probe_result.format.next_packet() {
225 Ok(packet) => packet,
226 Err(Error::IoError(err)) => {
227 if err.kind() == io::ErrorKind::UnexpectedEof {
228 return Ok(None);
229 } else {
230 return Err(DecoderError::SymphoniaDecoder(err.to_string()));
231 }
232 }
233 Err(err) => {
234 return Err(err.into());
235 }
236 };
237
238 let position_ms = self.ts_to_ms(packet.ts());
239 let packet_position = AudioPacketPosition {
240 position_ms,
241 skipped,
242 };
243
244 match self.decoder.decode(&packet) {
245 Ok(decoded) => {
246 let sample_buffer = match self.sample_buffer.as_mut() {
247 Some(buffer) => buffer,
248 None => {
249 let spec = *decoded.spec();
250 let duration = decoded.capacity() as u64;
251 self.sample_buffer.insert(SampleBuffer::new(duration, spec))
252 }
253 };
254
255 sample_buffer.copy_interleaved_ref(decoded);
256 let samples = AudioPacket::Samples(sample_buffer.samples().to_vec());
257
258 return Ok(Some((packet_position, samples)));
259 }
260 Err(Error::DecodeError(_)) => {
261 warn!("Skipping malformed audio packet at {position_ms} ms");
264 skipped = true;
265 continue;
266 }
267 Err(err) => return Err(err.into()),
268 }
269 }
270 }
271}