From 9ae452e22d12359a2a08c2454e534a2e55fe770c Mon Sep 17 00:00:00 2001 From: Paul Lietar Date: Fri, 3 Jul 2015 02:23:49 +0200 Subject: [PATCH] Keep audio files cached in ram. --- Cargo.lock | 2 +- src/audio_file.rs | 78 +++++++++++++++++++++++++++++++---------------- src/player.rs | 2 +- src/session.rs | 7 +++++ src/stream.rs | 25 +++++++++------ 5 files changed, 77 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff325e08..56283849 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -203,7 +203,7 @@ dependencies = [ [[package]] name = "vorbis" version = "0.0.11" -source = "git+https://github.com/plietar/vorbis-rs#b28d0d14f623b0204b29cba435b3cf9be249290a" +source = "git+https://github.com/plietar/vorbis-rs#78058c3341832969030f49cbc4b8bc9deb376776" dependencies = [ "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "ogg-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/audio_file.rs b/src/audio_file.rs index 8e738fa3..5b736e58 100644 --- a/src/audio_file.rs +++ b/src/audio_file.rs @@ -1,6 +1,6 @@ use byteorder::{ByteOrder, BigEndian}; use std::cmp::min; -use std::collections::BitSet; +use std::collections::{BitSet, HashMap}; use std::io::{self, SeekFrom}; use std::slice::bytes::copy_memory; use std::sync::{Arc, Condvar, Mutex}; @@ -35,31 +35,7 @@ struct AudioFileData { } impl <'s> AudioFile <'s> { - pub fn new(session: &Session, file_id: FileId) -> AudioFile { - let mut it = session.stream(file_id, 0, 1).into_iter() - .filter_map(|event| { - match event { - StreamEvent::Header(id, ref data) if id == 0x3 => { - Some(BigEndian::read_u32(data) as usize * 4) - } - _ => None - } - }); - - let size = it.next().unwrap(); - - let bufsize = size + (CHUNK_SIZE - size % CHUNK_SIZE); - - let shared = Arc::new(AudioFileShared { - file_id: file_id, - size: size, - data: Mutex::new(AudioFileData { - buffer: vec![0u8; bufsize], - bitmap: BitSet::with_capacity(bufsize / CHUNK_SIZE as usize) - }), - cond: Condvar::new(), - }); - + fn new(session: &Session, shared: Arc) -> AudioFile { let shared_ = shared.clone(); let (seek_tx, seek_rx) = mpsc::channel(); @@ -164,4 +140,54 @@ impl <'s> io::Seek for AudioFile <'s> { } } +impl AudioFileShared { + fn new(session: &Session, file_id: FileId) -> Arc { + let size = session.stream(file_id, 0, 1).into_iter() + .filter_map(|event| { + match event { + StreamEvent::Header(id, ref data) if id == 0x3 => { + Some(BigEndian::read_u32(data) as usize * 4) + } + _ => None + } + }).next().unwrap(); + + let bufsize = size + (CHUNK_SIZE - size % CHUNK_SIZE); + + Arc::new(AudioFileShared { + file_id: file_id, + size: size, + data: Mutex::new(AudioFileData { + buffer: vec![0u8; bufsize], + bitmap: BitSet::with_capacity(bufsize / CHUNK_SIZE as usize) + }), + cond: Condvar::new(), + }) + } +} + +pub struct AudioFileManager { + cache: HashMap> +} + +impl AudioFileManager { + pub fn new() -> AudioFileManager { + AudioFileManager { + cache: HashMap::new() + } + } + + pub fn request<'a> (&mut self, session: &'a Session, file_id: FileId) -> AudioFile<'a> { + let shared = self.cache + .get(&file_id) + .cloned() + .unwrap_or_else(|| { + println!("Cache miss"); + let shared = AudioFileShared::new(session, file_id.clone()); + self.cache.insert(file_id, shared.clone()); + shared + }); + AudioFile::new(session, shared) + } +} diff --git a/src/player.rs b/src/player.rs index 6a79b31e..2cba53ea 100644 --- a/src/player.rs +++ b/src/player.rs @@ -107,7 +107,7 @@ impl <'s> PlayerInternal<'s> { vorbis::Decoder::new( Subfile::new( AudioDecrypt::new(key, - AudioFile::new(self.session, file_id)), 0xa7)).unwrap()); + self.session.audio_file(file_id)), 0xa7)).unwrap()); decoder.as_mut().unwrap().time_seek(position as f64 / 1000f64).unwrap(); let mut h = self.state.0.lock().unwrap(); diff --git a/src/session.rs b/src/session.rs index 8a5ae63b..015702c3 100644 --- a/src/session.rs +++ b/src/session.rs @@ -13,6 +13,7 @@ use mercury::{MercuryManager, MercuryRequest, MercuryResponse}; use metadata::{MetadataManager, MetadataRef, MetadataTrait}; use stream::{StreamManager, StreamEvent}; use audio_key::{AudioKeyManager, AudioKey}; +use audio_file::{AudioFileManager, AudioFile}; use connection::PacketHandler; use util; @@ -30,6 +31,7 @@ pub struct Session { metadata: Mutex, stream: Mutex, audio_key: Mutex, + audio_file: Mutex, rx_connection: Mutex, tx_connection: Mutex, } @@ -118,6 +120,7 @@ impl Session { metadata: Mutex::new(MetadataManager::new()), stream: Mutex::new(StreamManager::new()), audio_key: Mutex::new(AudioKeyManager::new()), + audio_file: Mutex::new(AudioFileManager::new()), } } @@ -171,6 +174,10 @@ impl Session { self.audio_key.lock().unwrap().request(self, track, file) } + pub fn audio_file(&self, file: FileId) -> AudioFile { + self.audio_file.lock().unwrap().request(self, file) + } + pub fn stream(&self, file: FileId, offset: u32, size: u32) -> mpsc::Receiver { self.stream.lock().unwrap().request(self, file, offset, size) } diff --git a/src/stream.rs b/src/stream.rs index c2d4b115..3414bb2d 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -87,16 +87,20 @@ impl PacketHandler for StreamManager { ChannelMode::Header => { let mut length = 0; - while packet.position() < data.len() as u64 { + while packet.position() < data.len() as u64 && !close { length = packet.read_u16::().unwrap(); if length > 0 { let header_id = packet.read_u8().unwrap(); - channel.callback.send(StreamEvent::Header( - header_id, - data.clone() - .offset(packet.position() as usize) - .limit(length as usize - 1) - )).unwrap(); + channel.callback + .send(StreamEvent::Header( + header_id, + data.clone() + .offset(packet.position() as usize) + .limit(length as usize - 1) + )) + .unwrap_or_else(|_| { + close = true; + }); packet.seek(SeekFrom::Current(length as i64 - 1)).unwrap(); } @@ -109,8 +113,11 @@ impl PacketHandler for StreamManager { ChannelMode::Data => { if packet.position() < data.len() as u64 { - channel.callback.send(StreamEvent::Data( - data.clone().offset(packet.position() as usize))).unwrap(); + channel.callback + .send(StreamEvent::Data(data.clone().offset(packet.position() as usize))) + .unwrap_or_else(|_| { + close = true; + }); } else { close = true; }