lofty/ogg/vorbis/
properties.rs1use super::find_last_page;
2use crate::error::Result;
3use crate::properties::FileProperties;
4use crate::util::math::RoundedDivision;
5
6use std::io::{Read, Seek, SeekFrom};
7use std::time::Duration;
8
9use byteorder::{LittleEndian, ReadBytesExt};
10use ogg_pager::{Packets, PageHeader};
11
12#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
14#[non_exhaustive]
15pub struct VorbisProperties {
16 pub(crate) duration: Duration,
17 pub(crate) overall_bitrate: u32,
18 pub(crate) audio_bitrate: u32,
19 pub(crate) sample_rate: u32,
20 pub(crate) channels: u8,
21 pub(crate) version: u32,
22 pub(crate) bitrate_maximum: i32,
23 pub(crate) bitrate_nominal: i32,
24 pub(crate) bitrate_minimum: i32,
25}
26
27impl From<VorbisProperties> for FileProperties {
28 fn from(input: VorbisProperties) -> Self {
29 Self {
30 duration: input.duration,
31 overall_bitrate: Some(input.overall_bitrate),
32 audio_bitrate: Some(input.audio_bitrate),
33 sample_rate: Some(input.sample_rate),
34 bit_depth: None,
35 channels: Some(input.channels),
36 channel_mask: None,
37 }
38 }
39}
40
41impl VorbisProperties {
42 pub fn duration(&self) -> Duration {
44 self.duration
45 }
46
47 pub fn overall_bitrate(&self) -> u32 {
49 self.overall_bitrate
50 }
51
52 pub fn audio_bitrate(&self) -> u32 {
54 self.audio_bitrate
55 }
56
57 pub fn sample_rate(&self) -> u32 {
59 self.sample_rate
60 }
61
62 pub fn channels(&self) -> u8 {
64 self.channels
65 }
66
67 pub fn version(&self) -> u32 {
69 self.version
70 }
71
72 pub fn bitrate_max(&self) -> i32 {
74 self.bitrate_maximum
75 }
76
77 pub fn bitrate_nominal(&self) -> i32 {
79 self.bitrate_nominal
80 }
81
82 pub fn bitrate_min(&self) -> i32 {
84 self.bitrate_minimum
85 }
86}
87
88pub(in crate::ogg) fn read_properties<R>(
89 data: &mut R,
90 first_page_header: &PageHeader,
91 packets: &Packets,
92) -> Result<VorbisProperties>
93where
94 R: Read + Seek,
95{
96 let mut properties = VorbisProperties::default();
97
98 let first_packet = packets.get(0).expect("Identification packet expected");
100
101 let first_page_content = &mut &first_packet[7..];
103
104 properties.version = first_page_content.read_u32::<LittleEndian>()?;
105
106 properties.channels = first_page_content.read_u8()?;
107 properties.sample_rate = first_page_content.read_u32::<LittleEndian>()?;
108
109 properties.bitrate_maximum = first_page_content.read_i32::<LittleEndian>()?;
110 properties.bitrate_nominal = first_page_content.read_i32::<LittleEndian>()?;
111 properties.bitrate_minimum = first_page_content.read_i32::<LittleEndian>()?;
112
113 let last_page = find_last_page(data);
114 let file_length = data.seek(SeekFrom::End(0))?;
115
116 let mut length = 1000;
119 if let Ok(last_page) = last_page {
120 let first_page_abgp = first_page_header.abgp;
121 let last_page_abgp = last_page.header().abgp;
122
123 if properties.sample_rate > 0 {
124 let total_samples = u128::from(last_page_abgp.saturating_sub(first_page_abgp));
125
126 if total_samples > 0 {
128 length =
129 (total_samples * 1000).div_round(u128::from(properties.sample_rate)) as u64;
130 properties.duration = Duration::from_millis(length);
131 } else {
132 log::warn!(
133 "Vorbis: The file contains invalid PCM values, unable to calculate length"
134 );
135 }
136 } else {
137 log::warn!("Vorbis: Sample rate = 0, unable to calculate length");
138 }
139 }
140
141 if length > 0 {
142 properties.overall_bitrate = (file_length.saturating_mul(8) / length) as u32;
143 }
144
145 if properties.bitrate_nominal > 0 {
146 properties.audio_bitrate = (properties.bitrate_nominal as u64 / 1000) as u32;
147 }
148
149 Ok(properties)
150}