2015-09-01 11:20:37 +00:00
|
|
|
use bit_set::BitSet;
|
2015-06-23 14:38:29 +00:00
|
|
|
use byteorder::{ByteOrder, BigEndian};
|
2016-03-16 04:07:04 +00:00
|
|
|
use eventual;
|
2015-06-23 14:38:29 +00:00
|
|
|
use std::cmp::min;
|
2015-06-23 17:34:48 +00:00
|
|
|
use std::sync::{Arc, Condvar, Mutex};
|
|
|
|
use std::sync::mpsc::{self, TryRecvError};
|
2015-07-02 17:24:25 +00:00
|
|
|
use std::thread;
|
2015-07-07 21:40:31 +00:00
|
|
|
use std::fs;
|
|
|
|
use std::io::{self, Read, Write, Seek, SeekFrom};
|
2016-01-12 23:29:31 +00:00
|
|
|
use tempfile::NamedTempFile;
|
2015-06-23 14:38:29 +00:00
|
|
|
|
2016-03-16 04:07:04 +00:00
|
|
|
use util::{FileId, IgnoreExt};
|
2015-07-02 17:24:25 +00:00
|
|
|
use session::Session;
|
2015-09-01 11:20:37 +00:00
|
|
|
use stream::StreamEvent;
|
2015-06-23 14:38:29 +00:00
|
|
|
|
2016-01-02 15:19:39 +00:00
|
|
|
const CHUNK_SIZE: usize = 0x20000;
|
2015-06-23 14:38:29 +00:00
|
|
|
|
2016-03-16 04:07:04 +00:00
|
|
|
pub struct AudioFile {
|
2016-01-12 23:29:31 +00:00
|
|
|
read_file: fs::File,
|
2015-07-07 21:40:31 +00:00
|
|
|
|
|
|
|
position: u64,
|
2015-06-23 17:34:48 +00:00
|
|
|
seek: mpsc::Sender<u64>,
|
2015-07-07 21:40:31 +00:00
|
|
|
|
2015-06-24 00:41:39 +00:00
|
|
|
shared: Arc<AudioFileShared>,
|
|
|
|
}
|
2015-06-23 14:38:29 +00:00
|
|
|
|
2015-06-24 00:41:39 +00:00
|
|
|
struct AudioFileShared {
|
2015-07-02 17:24:25 +00:00
|
|
|
file_id: FileId,
|
2015-07-07 21:40:31 +00:00
|
|
|
chunk_count: usize,
|
|
|
|
cond: Condvar,
|
|
|
|
bitmap: Mutex<BitSet>,
|
2015-06-23 14:38:29 +00:00
|
|
|
}
|
|
|
|
|
2016-03-16 04:07:04 +00:00
|
|
|
impl AudioFile {
|
|
|
|
pub fn new(session: &Session, file_id: FileId)
|
|
|
|
-> (AudioFile, eventual::Future<NamedTempFile, ()>) {
|
|
|
|
|
2016-01-02 15:19:39 +00:00
|
|
|
let size = session.stream(file_id, 0, 1)
|
2016-03-13 20:03:40 +00:00
|
|
|
.iter()
|
2016-01-02 15:19:39 +00:00
|
|
|
.filter_map(|event| {
|
|
|
|
match event {
|
|
|
|
StreamEvent::Header(id, ref data) if id == 0x3 => {
|
|
|
|
Some(BigEndian::read_u32(data) as usize * 4)
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.next()
|
|
|
|
.unwrap();
|
2015-07-07 21:40:31 +00:00
|
|
|
|
2015-12-30 11:42:47 +00:00
|
|
|
let chunk_count = (size + CHUNK_SIZE - 1) / CHUNK_SIZE;
|
2015-07-07 21:40:31 +00:00
|
|
|
|
|
|
|
let shared = Arc::new(AudioFileShared {
|
|
|
|
file_id: file_id,
|
|
|
|
chunk_count: chunk_count,
|
|
|
|
cond: Condvar::new(),
|
|
|
|
bitmap: Mutex::new(BitSet::with_capacity(chunk_count)),
|
|
|
|
});
|
|
|
|
|
2016-01-12 23:29:31 +00:00
|
|
|
let write_file = NamedTempFile::new().unwrap();
|
|
|
|
write_file.set_len(size as u64).unwrap();
|
|
|
|
let read_file = write_file.reopen().unwrap();
|
2015-06-23 14:38:29 +00:00
|
|
|
|
2015-07-02 17:24:25 +00:00
|
|
|
let (seek_tx, seek_rx) = mpsc::channel();
|
2016-03-16 04:07:04 +00:00
|
|
|
let (complete_tx, complete_rx) = eventual::Future::pair();
|
2015-07-02 17:24:25 +00:00
|
|
|
|
2016-01-02 15:48:44 +00:00
|
|
|
{
|
|
|
|
let shared = shared.clone();
|
|
|
|
let session = session.clone();
|
2016-03-16 04:07:04 +00:00
|
|
|
thread::spawn(move || AudioFile::fetch(&session, shared, write_file, seek_rx, complete_tx));
|
2016-01-02 15:48:44 +00:00
|
|
|
}
|
2015-09-01 11:20:37 +00:00
|
|
|
|
2016-03-16 04:07:04 +00:00
|
|
|
(AudioFile {
|
2015-07-07 21:40:31 +00:00
|
|
|
read_file: read_file,
|
|
|
|
|
2015-06-24 00:41:39 +00:00
|
|
|
position: 0,
|
2015-07-02 17:24:25 +00:00
|
|
|
seek: seek_tx,
|
2015-06-23 14:38:29 +00:00
|
|
|
|
2016-01-02 15:19:39 +00:00
|
|
|
shared: shared,
|
2016-03-16 04:07:04 +00:00
|
|
|
}, complete_rx)
|
2015-07-07 21:40:31 +00:00
|
|
|
}
|
|
|
|
|
2016-01-02 15:19:39 +00:00
|
|
|
fn fetch(session: &Session,
|
|
|
|
shared: Arc<AudioFileShared>,
|
2016-01-12 23:29:31 +00:00
|
|
|
mut write_file: NamedTempFile,
|
2016-03-16 04:07:04 +00:00
|
|
|
seek_rx: mpsc::Receiver<u64>,
|
|
|
|
complete_tx: eventual::Complete<NamedTempFile, ()>) {
|
2015-07-07 21:40:31 +00:00
|
|
|
let mut index = 0;
|
|
|
|
|
|
|
|
loop {
|
|
|
|
match seek_rx.try_recv() {
|
|
|
|
Ok(position) => {
|
|
|
|
index = position as usize / CHUNK_SIZE;
|
|
|
|
}
|
|
|
|
Err(TryRecvError::Disconnected) => break,
|
|
|
|
Err(TryRecvError::Empty) => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
let bitmap = shared.bitmap.lock().unwrap();
|
|
|
|
if bitmap.len() >= shared.chunk_count {
|
|
|
|
drop(bitmap);
|
2016-03-16 04:07:04 +00:00
|
|
|
write_file.seek(SeekFrom::Start(0)).unwrap();
|
|
|
|
complete_tx.complete(write_file);
|
2015-07-07 21:40:31 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
while bitmap.contains(&index) {
|
|
|
|
index = (index + 1) % shared.chunk_count;
|
|
|
|
}
|
|
|
|
drop(bitmap);
|
|
|
|
|
2016-03-16 04:07:04 +00:00
|
|
|
AudioFile::fetch_chunk(session, &shared, &mut write_file, index);
|
2015-07-07 21:40:31 +00:00
|
|
|
}
|
2015-06-23 17:34:48 +00:00
|
|
|
}
|
2015-06-24 00:41:39 +00:00
|
|
|
|
2016-01-02 15:19:39 +00:00
|
|
|
fn fetch_chunk(session: &Session,
|
|
|
|
shared: &Arc<AudioFileShared>,
|
2016-01-12 23:29:31 +00:00
|
|
|
write_file: &mut NamedTempFile,
|
2016-01-02 15:19:39 +00:00
|
|
|
index: usize) {
|
2015-07-07 21:40:31 +00:00
|
|
|
|
2015-07-02 17:24:25 +00:00
|
|
|
let rx = session.stream(shared.file_id,
|
2016-01-02 15:19:39 +00:00
|
|
|
(index * CHUNK_SIZE / 4) as u32,
|
|
|
|
(CHUNK_SIZE / 4) as u32);
|
2015-06-23 14:38:29 +00:00
|
|
|
|
2016-04-24 11:15:53 +00:00
|
|
|
debug!("Fetch chunk {} / {}", index + 1, shared.chunk_count);
|
2015-07-07 21:40:31 +00:00
|
|
|
|
|
|
|
write_file.seek(SeekFrom::Start((index * CHUNK_SIZE) as u64)).unwrap();
|
|
|
|
|
|
|
|
let mut size = 0usize;
|
2015-06-23 17:34:48 +00:00
|
|
|
for event in rx.iter() {
|
|
|
|
match event {
|
2015-07-07 21:40:31 +00:00
|
|
|
StreamEvent::Header(..) => (),
|
2015-06-23 17:34:48 +00:00
|
|
|
StreamEvent::Data(data) => {
|
2015-07-07 21:40:31 +00:00
|
|
|
write_file.write_all(&data).unwrap();
|
2015-06-23 17:34:48 +00:00
|
|
|
|
2015-07-07 21:40:31 +00:00
|
|
|
size += data.len();
|
|
|
|
if size >= CHUNK_SIZE {
|
2016-01-02 15:19:39 +00:00
|
|
|
break;
|
2015-06-23 14:38:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-06-23 17:34:48 +00:00
|
|
|
}
|
2015-06-24 00:41:39 +00:00
|
|
|
|
2015-07-07 21:40:31 +00:00
|
|
|
let mut bitmap = shared.bitmap.lock().unwrap();
|
|
|
|
bitmap.insert(index as usize);
|
|
|
|
|
|
|
|
shared.cond.notify_all();
|
2015-06-23 17:34:48 +00:00
|
|
|
}
|
2015-06-23 14:38:29 +00:00
|
|
|
}
|
|
|
|
|
2016-03-16 04:07:04 +00:00
|
|
|
impl Read for AudioFile {
|
2015-06-23 14:38:29 +00:00
|
|
|
fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
|
2015-07-07 21:40:31 +00:00
|
|
|
let index = self.position as usize / CHUNK_SIZE;
|
|
|
|
let offset = self.position as usize % CHUNK_SIZE;
|
2016-01-02 15:19:39 +00:00
|
|
|
let len = min(output.len(), CHUNK_SIZE - offset);
|
2015-06-23 14:38:29 +00:00
|
|
|
|
2015-07-07 21:40:31 +00:00
|
|
|
let mut bitmap = self.shared.bitmap.lock().unwrap();
|
|
|
|
while !bitmap.contains(&index) {
|
|
|
|
bitmap = self.shared.cond.wait(bitmap).unwrap();
|
2015-06-23 14:38:29 +00:00
|
|
|
}
|
2015-07-07 21:40:31 +00:00
|
|
|
drop(bitmap);
|
|
|
|
|
2015-09-01 11:20:37 +00:00
|
|
|
let read_len = try!(self.read_file.read(&mut output[..len]));
|
2015-06-23 14:38:29 +00:00
|
|
|
|
2015-09-01 11:20:37 +00:00
|
|
|
self.position += read_len as u64;
|
2015-06-23 14:38:29 +00:00
|
|
|
|
2015-09-01 11:20:37 +00:00
|
|
|
Ok(read_len)
|
2015-06-23 14:38:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-16 04:07:04 +00:00
|
|
|
impl Seek for AudioFile {
|
2015-07-07 21:40:31 +00:00
|
|
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
|
|
|
self.position = try!(self.read_file.seek(pos));
|
|
|
|
|
2016-01-02 15:19:39 +00:00
|
|
|
// Notify the fetch thread to get the correct block
|
|
|
|
// This can fail if fetch thread has completed, in which case the
|
|
|
|
// block is ready. Just ignore the error.
|
2015-07-07 21:40:31 +00:00
|
|
|
self.seek.send(self.position).ignore();
|
2015-06-23 17:34:48 +00:00
|
|
|
Ok(self.position as u64)
|
2015-06-23 14:38:29 +00:00
|
|
|
}
|
|
|
|
}
|