1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use super::find_last_page;
use crate::error::{FileDecodingError, Result};
use crate::file::FileType;
use crate::properties::FileProperties;
use std::io::{Read, Seek, SeekFrom};
use std::time::Duration;
use byteorder::{LittleEndian, ReadBytesExt};
use ogg_pager::Page;
#[derive(Debug, Copy, Clone, PartialEq, Default)]
#[non_exhaustive]
pub struct OpusProperties {
pub(crate) duration: Duration,
pub(crate) overall_bitrate: u32,
pub(crate) audio_bitrate: u32,
pub(crate) channels: u8,
pub(crate) version: u8,
pub(crate) input_sample_rate: u32,
}
impl From<OpusProperties> for FileProperties {
fn from(input: OpusProperties) -> Self {
Self {
duration: input.duration,
overall_bitrate: Some(input.overall_bitrate),
audio_bitrate: Some(input.audio_bitrate),
sample_rate: Some(input.input_sample_rate),
bit_depth: None,
channels: Some(input.channels),
}
}
}
impl OpusProperties {
pub fn duration(&self) -> Duration {
self.duration
}
pub fn overall_bitrate(&self) -> u32 {
self.overall_bitrate
}
pub fn audio_bitrate(&self) -> u32 {
self.audio_bitrate
}
pub fn channels(&self) -> u8 {
self.channels
}
pub fn version(&self) -> u8 {
self.version
}
pub fn input_sample_rate(&self) -> u32 {
self.input_sample_rate
}
}
pub(in crate::ogg) fn read_properties<R>(data: &mut R, first_page: &Page) -> Result<OpusProperties>
where
R: Read + Seek,
{
let (stream_len, file_length) = {
let current = data.stream_position()?;
let end = data.seek(SeekFrom::End(0))?;
data.seek(SeekFrom::Start(current))?;
(end - first_page.start, end)
};
let mut properties = OpusProperties::default();
let first_page_abgp = first_page.abgp;
let first_page_content = &mut &first_page.content()[8..];
properties.version = first_page_content.read_u8()?;
properties.channels = first_page_content.read_u8()?;
let pre_skip = first_page_content.read_u16::<LittleEndian>()?;
properties.input_sample_rate = first_page_content.read_u32::<LittleEndian>()?;
let _output_gain = first_page_content.read_u16::<LittleEndian>()?;
let channel_mapping_family = first_page_content.read_u8()?;
if (channel_mapping_family == 0 && properties.channels > 2)
|| (channel_mapping_family == 1 && properties.channels > 8)
{
return Err(FileDecodingError::new(
FileType::Opus,
"Invalid channel count for mapping family",
)
.into());
}
let audio_size = stream_len - data.stream_position()?;
let last_page = find_last_page(data)?;
let last_page_abgp = last_page.abgp;
if let Some(frame_count) = last_page_abgp.checked_sub(first_page_abgp + u64::from(pre_skip)) {
let length = frame_count * 1000 / 48000;
properties.duration = Duration::from_millis(length);
properties.overall_bitrate = ((file_length * 8) / length) as u32;
properties.audio_bitrate = (audio_size * 8 / length) as u32;
}
Ok(properties)
}