| 1 | use nihav_core::frame::*; |
| 2 | use nihav_core::demuxers::*; |
| 3 | |
| 4 | const SAMPLE_RATES: [u32; 15] = [ |
| 5 | 6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, |
| 6 | 32000, 44100, 48000, 64000, 88200, 96000, 192000 |
| 7 | ]; |
| 8 | const WV_FLAG_MONO: u32 = 1 << 2; |
| 9 | //const WV_FLAG_HYBRID: u32 = 1 << 3; |
| 10 | //const WV_FLAG_JSTEREO: u32 = 1 << 4; |
| 11 | //const WV_FLAG_CH_DECORR: u32 = 1 << 5; |
| 12 | //const WV_FLAG_HYB_NOISE_SHAPING: u32 = 1 << 6; |
| 13 | const WV_FLAG_FLOATS: u32 = 1 << 7; |
| 14 | //const WV_FLAG_EXT_INTEGERS: u32 = 1 << 8; |
| 15 | //const WV_FLAG_HYB_BITRATE: u32 = 1 << 9; |
| 16 | //const WV_FLAG_HYB_BALANCED_NOISE: u32 = 1 << 10; |
| 17 | const WV_FLAG_START_BLOCK: u32 = 1 << 11; |
| 18 | const WV_FLAG_END_BLOCK: u32 = 1 << 12; |
| 19 | //const WV_FLAG_HAS_CRC: u32 = 1 << 28; |
| 20 | const WV_FLAG_FALSE_STEREO: u32 = 1 << 30; |
| 21 | //const WV_FLAG_DSD_AUDIO: u32 = 1 << 31; |
| 22 | |
| 23 | const WV_STREAM_FLAGS: u32 = 0x8000008B; |
| 24 | |
| 25 | #[derive(Clone,Copy,Default)] |
| 26 | struct WVHeader { |
| 27 | size: usize, |
| 28 | ver: u16, |
| 29 | tot_samples: u64, |
| 30 | block_index: u64, |
| 31 | block_samples: u32, |
| 32 | flags: u32, |
| 33 | crc: u32, |
| 34 | } |
| 35 | |
| 36 | const WV_HEADER_SIZE: usize = 32; |
| 37 | |
| 38 | impl WVHeader { |
| 39 | fn parse(src: &[u8]) -> DemuxerResult<Self> { |
| 40 | let mut mr = MemoryReader::new_read(src); |
| 41 | let mut br = ByteReader::new(&mut mr); |
| 42 | let tag = br.read_tag()?; |
| 43 | validate!(&tag == b"wvpk"); |
| 44 | let mut hdr = Self::default(); |
| 45 | hdr.size = br.read_u32le()? as usize; |
| 46 | validate!(hdr.size >= 24); |
| 47 | hdr.ver = br.read_u16le()?; |
| 48 | validate!(hdr.ver >= 0x402 || hdr.ver <= 0x410); |
| 49 | let top_idx = br.read_byte()?; |
| 50 | let top_samps = br.read_byte()?; |
| 51 | hdr.tot_samples = u64::from(br.read_u32le()?) | (u64::from(top_samps) << 32); |
| 52 | hdr.block_index = u64::from(br.read_u32le()?) | (u64::from(top_idx) << 32); |
| 53 | hdr.block_samples = br.read_u32le()?; |
| 54 | hdr.flags = br.read_u32le()?; |
| 55 | hdr.crc = br.read_u32le()?; |
| 56 | Ok(hdr) |
| 57 | } |
| 58 | fn stream_eq(&self, rval: &Self) -> bool { |
| 59 | self.ver == rval.ver && |
| 60 | (self.flags & WV_STREAM_FLAGS) == (rval.flags & WV_STREAM_FLAGS) |
| 61 | } |
| 62 | fn block_eq(&self, rval: &Self) -> bool { |
| 63 | self.stream_eq(rval) && self.tot_samples == rval.tot_samples && |
| 64 | self.block_index == rval.block_index && |
| 65 | self.block_samples == rval.block_samples |
| 66 | } |
| 67 | fn is_start_block(&self) -> bool { |
| 68 | (self.flags & WV_FLAG_START_BLOCK) != 0 |
| 69 | } |
| 70 | fn is_end_block(&self) -> bool { |
| 71 | (self.flags & WV_FLAG_END_BLOCK) != 0 |
| 72 | } |
| 73 | fn get_num_channels(&self) -> u8 { |
| 74 | if (self.flags & WV_FLAG_MONO) != 0 && (self.flags & WV_FLAG_FALSE_STEREO) == 0 { 1 } else { 2 } |
| 75 | } |
| 76 | fn get_sample_rate(&self) -> u32 { |
| 77 | let idx = ((self.flags >> 23) & 0xF) as usize; |
| 78 | if idx != 15 { |
| 79 | SAMPLE_RATES[idx] |
| 80 | } else { |
| 81 | 0 |
| 82 | } |
| 83 | } |
| 84 | fn get_size(&self) -> usize { |
| 85 | self.size - (WV_HEADER_SIZE - 8) |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | #[derive(Clone,Copy,Default)] |
| 90 | struct FrameSeekInfo { |
| 91 | off: u64, |
| 92 | samplepos: u64, |
| 93 | } |
| 94 | |
| 95 | struct WavPackDemuxer<'a> { |
| 96 | src: &'a mut ByteReader<'a>, |
| 97 | samplepos: u64, |
| 98 | nsamples: u64, |
| 99 | first_blocks: Option<(WVHeader, Vec<u8>)>, |
| 100 | srate: u32, |
| 101 | known_frames: Vec<FrameSeekInfo>, |
| 102 | } |
| 103 | |
| 104 | impl<'a> WavPackDemuxer<'a> { |
| 105 | fn new(io: &'a mut ByteReader<'a>) -> Self { |
| 106 | Self { |
| 107 | src: io, |
| 108 | samplepos: 0, |
| 109 | nsamples: 0, |
| 110 | first_blocks: None, |
| 111 | srate: 0, |
| 112 | known_frames: Vec::new(), |
| 113 | } |
| 114 | } |
| 115 | fn read_blocks(&mut self) -> DemuxerResult<(WVHeader, Vec<u8>)> { |
| 116 | let mut hdrbuf = [0u8; WV_HEADER_SIZE]; |
| 117 | let mut buf: Vec<u8> = Vec::new(); |
| 118 | let mut first = true; |
| 119 | let mut refhdr = WVHeader::default(); |
| 120 | loop { |
| 121 | self.src.read_buf(&mut hdrbuf)?; |
| 122 | let hdr = WVHeader::parse(&hdrbuf)?; |
| 123 | if first { |
| 124 | validate!(hdr.is_start_block()); |
| 125 | refhdr = hdr; |
| 126 | first = false; |
| 127 | } else { |
| 128 | validate!(refhdr.block_eq(&hdr)); |
| 129 | } |
| 130 | buf.extend_from_slice(&hdrbuf); |
| 131 | let pos = buf.len(); |
| 132 | buf.resize(pos + hdr.get_size(), 0); |
| 133 | self.src.read_buf(&mut buf[pos..])?; |
| 134 | |
| 135 | if hdr.is_end_block() { |
| 136 | break; |
| 137 | } |
| 138 | } |
| 139 | Ok((refhdr, buf)) |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | impl<'a> DemuxCore<'a> for WavPackDemuxer<'a> { |
| 144 | fn open(&mut self, strmgr: &mut StreamManager, seek_index: &mut SeekIndex) -> DemuxerResult<()> { |
| 145 | |
| 146 | let (hdr, buf) = self.read_blocks()?; |
| 147 | |
| 148 | let srate = hdr.get_sample_rate(); |
| 149 | validate!(srate != 0); |
| 150 | let channels = if !hdr.is_end_block() { |
| 151 | let mut ch_count = 0; |
| 152 | let mut off = 0; |
| 153 | loop { |
| 154 | let hdr = WVHeader::parse(&buf[off..]).unwrap(); |
| 155 | off += WV_HEADER_SIZE + hdr.get_size(); |
| 156 | ch_count += hdr.get_num_channels(); |
| 157 | if hdr.is_end_block() { |
| 158 | break; |
| 159 | } |
| 160 | } |
| 161 | ch_count |
| 162 | } else { |
| 163 | hdr.get_num_channels() |
| 164 | }; |
| 165 | |
| 166 | self.nsamples = hdr.tot_samples; |
| 167 | |
| 168 | let mut fmt = SND_S16P_FORMAT; |
| 169 | if (hdr.flags & WV_FLAG_FLOATS) != 0 { |
| 170 | fmt.float = true; |
| 171 | } else { |
| 172 | fmt.bits = (((hdr.flags & 3) + 1) * 8) as u8; |
| 173 | } |
| 174 | |
| 175 | let ahdr = NAAudioInfo::new(srate, channels, SND_S16P_FORMAT, 1); |
| 176 | let ainfo = NACodecInfo::new("wavpack", NACodecTypeInfo::Audio(ahdr), Some(buf.clone())); |
| 177 | strmgr.add_stream(NAStream::new(StreamType::Audio, 0, ainfo, 1, srate, hdr.tot_samples)).unwrap(); |
| 178 | seek_index.mode = SeekIndexMode::Automatic; |
| 179 | self.srate = srate; |
| 180 | self.known_frames = Vec::with_capacity(((self.nsamples + u64::from(srate) - 1) / u64::from(srate)) as usize); |
| 181 | self.known_frames.push(FrameSeekInfo { off: 0, samplepos: hdr.block_index }); |
| 182 | |
| 183 | self.first_blocks = Some((hdr, buf)); |
| 184 | Ok(()) |
| 185 | } |
| 186 | fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> { |
| 187 | if self.first_blocks.is_some() { |
| 188 | let mut fb = None; |
| 189 | std::mem::swap(&mut fb, &mut self.first_blocks); |
| 190 | let (refhdr, buf) = fb.unwrap(); |
| 191 | let stream = strmgr.get_stream(0).unwrap(); |
| 192 | let (tb_num, tb_den) = stream.get_timebase(); |
| 193 | let ts = NATimeInfo::new(Some(self.samplepos), None, None, tb_num, tb_den); |
| 194 | let pkt = NAPacket::new(stream, ts, true, buf); |
| 195 | |
| 196 | self.samplepos += u64::from(refhdr.block_samples); |
| 197 | |
| 198 | return Ok(pkt); |
| 199 | } |
| 200 | if self.samplepos == self.nsamples { |
| 201 | return Err(DemuxerError::EOF); |
| 202 | } |
| 203 | let cur_off = self.src.tell(); |
| 204 | let cur_spos = self.samplepos; |
| 205 | let (refhdr, buf) = self.read_blocks()?; |
| 206 | |
| 207 | let stream = strmgr.get_stream(0).unwrap(); |
| 208 | let (tb_num, tb_den) = stream.get_timebase(); |
| 209 | let ts = NATimeInfo::new(Some(self.samplepos), None, None, tb_num, tb_den); |
| 210 | let pkt = NAPacket::new(stream, ts, true, buf); |
| 211 | |
| 212 | self.samplepos += u64::from(refhdr.block_samples); |
| 213 | if self.known_frames.last().unwrap_or(&FrameSeekInfo::default()).samplepos < cur_spos { |
| 214 | self.known_frames.push(FrameSeekInfo{off: cur_off, samplepos: cur_spos }); |
| 215 | } |
| 216 | |
| 217 | Ok(pkt) |
| 218 | } |
| 219 | fn seek(&mut self, time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> { |
| 220 | self.first_blocks = None; |
| 221 | if let NATimePoint::Milliseconds(ms) = time { |
| 222 | let samppos = ms * u64::from(self.srate) / 1000; |
| 223 | if self.known_frames.last().unwrap_or(&FrameSeekInfo::default()).samplepos >= samppos { |
| 224 | for point in self.known_frames.iter().rev() { |
| 225 | if point.samplepos <= samppos { |
| 226 | self.src.seek(SeekFrom::Start(point.off))?; |
| 227 | self.samplepos = point.samplepos; |
| 228 | return Ok(()); |
| 229 | } |
| 230 | } |
| 231 | } else { |
| 232 | let mut hdrbuf = [0u8; WV_HEADER_SIZE]; |
| 233 | let lastoff = self.known_frames.last().unwrap_or(&FrameSeekInfo::default()).off; |
| 234 | self.src.seek(SeekFrom::Start(lastoff))?; |
| 235 | loop { |
| 236 | self.src.peek_buf(&mut hdrbuf)?; |
| 237 | let hdr = WVHeader::parse(&hdrbuf)?; |
| 238 | if hdr.is_start_block() { |
| 239 | self.known_frames.push(FrameSeekInfo{off: self.src.tell(), samplepos: hdr.block_index }); |
| 240 | if hdr.block_index <= samppos && hdr.block_index + u64::from(hdr.block_samples) > samppos { |
| 241 | self.samplepos = hdr.block_index; |
| 242 | return Ok(()); |
| 243 | } |
| 244 | if hdr.block_index > samppos { |
| 245 | break; |
| 246 | } |
| 247 | } |
| 248 | self.src.read_skip(WV_HEADER_SIZE + hdr.get_size())? |
| 249 | } |
| 250 | } |
| 251 | Err(DemuxerError::SeekError) |
| 252 | } else { |
| 253 | Err(DemuxerError::NotPossible) |
| 254 | } |
| 255 | } |
| 256 | fn get_duration(&self) -> u64 { 0 } |
| 257 | } |
| 258 | |
| 259 | impl<'a> NAOptionHandler for WavPackDemuxer<'a> { |
| 260 | fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } |
| 261 | fn set_options(&mut self, _options: &[NAOption]) { } |
| 262 | fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } |
| 263 | } |
| 264 | |
| 265 | |
| 266 | pub struct WavPackDemuxerCreator { } |
| 267 | |
| 268 | impl DemuxerCreator for WavPackDemuxerCreator { |
| 269 | fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> { |
| 270 | Box::new(WavPackDemuxer::new(br)) |
| 271 | } |
| 272 | fn get_name(&self) -> &'static str { "wavpack" } |
| 273 | } |
| 274 | |
| 275 | #[cfg(test)] |
| 276 | mod test { |
| 277 | use super::*; |
| 278 | use std::fs::File; |
| 279 | |
| 280 | #[test] |
| 281 | fn test_wavpack_demux() { |
| 282 | // sample from the official WavPack test samples set |
| 283 | let mut file = File::open("assets/LLaudio/wv/false_stereo.wv").unwrap(); |
| 284 | let mut fr = FileReader::new_read(&mut file); |
| 285 | let mut br = ByteReader::new(&mut fr); |
| 286 | let mut dmx = WavPackDemuxer::new(&mut br); |
| 287 | let mut sm = StreamManager::new(); |
| 288 | let mut si = SeekIndex::new(); |
| 289 | dmx.open(&mut sm, &mut si).unwrap(); |
| 290 | loop { |
| 291 | let pktres = dmx.get_frame(&mut sm); |
| 292 | if let Err(e) = pktres { |
| 293 | if (e as i32) == (DemuxerError::EOF as i32) { break; } |
| 294 | panic!("error"); |
| 295 | } |
| 296 | let pkt = pktres.unwrap(); |
| 297 | println!("Got {}", pkt); |
| 298 | } |
| 299 | } |
| 300 | } |