From 846f4775d93868737de44edd4510af91351f930d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 15 May 2016 11:48:54 +0300 Subject: [PATCH] Implement seeking in HTTP source --- src/rshttpsrc.rs | 128 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 92 insertions(+), 36 deletions(-) diff --git a/src/rshttpsrc.rs b/src/rshttpsrc.rs index 27bca6cd..3b2add3d 100644 --- a/src/rshttpsrc.rs +++ b/src/rshttpsrc.rs @@ -1,7 +1,7 @@ use std::u64; use std::io::{Read, Seek, SeekFrom}; use url::Url; -use hyper::header::ContentLength; +use hyper::header::{ContentLength, ContentRange, ContentRangeSpec, Range, ByteRangeSpec, AcceptRanges, RangeUnit}; use hyper::client::Client; use hyper::client::response::Response; @@ -15,13 +15,16 @@ pub struct HttpSrc { url: Option, client: Client, response: Option, + seekable: bool, position: u64, size: u64, + start: u64, + stop: u64, } impl HttpSrc { fn new() -> HttpSrc { - HttpSrc { url: None, client: Client::new(), response: None, position: 0, size: u64::MAX } + HttpSrc { url: None, client: Client::new(), response: None, seekable: false, position: 0, size: u64::MAX, start: 0, stop: u64::MAX } } fn new_source() -> Box { @@ -31,10 +34,81 @@ impl HttpSrc { let instance = Box::new(HttpSrc::new_source()); return Box::into_raw(instance); } + + pub fn do_request(&mut self, start: u64, stop: u64) -> bool { + self.response = None; + self.seekable = false; + self.position = 0; + self.size = u64::MAX; + + match self.url { + None => return false, + Some(ref url) => { + let mut req = self.client.get(url.clone()); + + if start != 0 || stop != u64::MAX { + req = if stop == u64::MAX { + req.header(Range::Bytes(vec![ByteRangeSpec::AllFrom(start)])) + } else { + req.header(Range::Bytes(vec![ByteRangeSpec::FromTo(start, stop)])) + }; + } + + match req.send() { + Ok(response) => { + if response.status.is_success() { + self.size = if let Some(&ContentLength(content_length)) = response.headers.get() { + content_length + start + } else { + u64::MAX + }; + let accept_byte_ranges = if let Some(&AcceptRanges(ref ranges)) = response.headers.get() { + ranges.iter().any(|u| *u == RangeUnit::Bytes) + } else { + false + }; + + self.seekable = self.size != u64::MAX && accept_byte_ranges; + + self.start = start; + self.stop = stop; + + self.position = if let Some(&ContentRange(ContentRangeSpec::Bytes{range: Some((range_start, _)), ..})) = response.headers.get() { + range_start + } else { + start + }; + + if self.position != start { + println_err!("Failed to seek to {}: Got {}", start, self.position); + return false; + } + + self.response = Some(response); + + return true; + } else { + println_err!("Failed to fetch {}: {}", url, response.status); + return false; + } + }, + Err(err) => { + println_err!("Failed to fetch {}: {}", url, err.to_string()); + return false; + } + } + }, + } + } } impl Source for HttpSrc { fn set_uri(&mut self, uri_str: &Option) -> bool { + if self.response.is_some() { + println_err!("Can't set URI after starting"); + return false; + } + match *uri_str { None => { self.url = None; @@ -69,7 +143,7 @@ impl Source for HttpSrc { } fn is_seekable(&self) -> bool { - false + self.seekable } fn get_size(&self) -> u64 { @@ -77,37 +151,12 @@ impl Source for HttpSrc { } fn start(&mut self) -> bool { - self.response = None; - self.position = 0; - self.size = u64::MAX; - - match self.url { - None => return false, - Some(ref url) => { - match self.client.get(url.clone()).send() { - Ok(response) => { - if response.status.is_success() { - self.size = match response.headers.get::() { - Some(&ContentLength(size)) => size, - _ => u64::MAX - }; - self.response = Some(response); - return true; - } else { - println_err!("Failed to fetch {}: {}", url, response.status); - return false; - } - }, - Err(err) => { - println_err!("Failed to fetch {}: {}", url, err.to_string()); - return false; - } - } - }, - } + self.seekable = false; + return self.do_request(0, u64::MAX); } fn stop(&mut self) -> bool { + self.seekable = false; self.position = 0; self.size = u64::MAX; match self.response { @@ -119,15 +168,22 @@ impl Source for HttpSrc { return true; } + fn do_seek(&mut self, start: u64, stop: u64) -> bool { + return self.do_request(start, stop); + } + fn fill(&mut self, offset: u64, data: &mut [u8]) -> Result { + if self.position != offset || self.response.is_none() { + let stop = self.stop; + if !self.do_request(offset, stop) { + println_err!("Failed to seek to {}", offset); + return Err(GstFlowReturn::Error); + } + } + match self.response { None => return Err(GstFlowReturn::Error), Some(ref mut r) => { - if self.position != offset { - println_err!("Failed to seek to {}", offset); - return Err(GstFlowReturn::Error); - } - match r.read(data) { Ok(size) => { if size == 0 {