Skip to content
This repository has been archived by the owner on Feb 2, 2019. It is now read-only.

Commit

Permalink
Support for timestamps, stream duration, seeking, and networking
Browse files Browse the repository at this point in the history
  • Loading branch information
eeeeeta committed Jan 28, 2017
1 parent 2ede365 commit 153f450
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 23 deletions.
47 changes: 47 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -8,6 +8,7 @@ ffmpeg-sys = "3.2"
error-chain = "0.7"
libc = "0.2.19"
sample = "0.6.2"
chrono = "0.2"

[dev-dependencies]
sqa-engine = { git = "https://github.com/eeeeeta/sqa-engine" }
28 changes: 18 additions & 10 deletions examples/player.rs
Expand Up @@ -2,18 +2,20 @@ extern crate sqa_engine;
extern crate sqa_ffmpeg;

use sqa_engine::{EngineContext, jack, Sender};
use sqa_ffmpeg::{MediaFile, init};
use sqa_ffmpeg::{MediaFile, init, Duration};
use std::io::{self, BufRead, Read};
use std::thread;

fn main() {
let mctx = init();
let mut mctx = init().unwrap();
mctx.network_init();
println!("Provide a FFmpeg URL:");

let stdin = io::stdin();
let mut stdin = stdin.lock();
let mut buffer = String::new();
stdin.read_line(&mut buffer).unwrap();
let file = MediaFile::new(&mctx, &buffer.trim()).unwrap();
let mut file = MediaFile::new(&mut mctx, &buffer.trim()).unwrap();
let mut ec = EngineContext::new(None).unwrap();
let mut chans = vec![];
let mut ctls = vec![];
Expand All @@ -30,17 +32,23 @@ fn main() {
ec.conn.connect_ports(&ch, &port).unwrap();
}
}
println!("Chans: {} Sample rate: {}", file.channels(), file.sample_rate());
println!("Chans: {} Sample rate: {} Duration: {}", file.channels(), file.sample_rate(), file.duration());
let thr = ::std::thread::spawn(move || {
for x in file {
if let Ok(mut x) = x {
for (i, ch) in chans.iter_mut().enumerate() {
x.set_chan(i);
for smpl in &mut x {
ch.1.buf.push(smpl.f32() * 0.5);
loop {
for x in &mut file {
if let Ok(mut x) = x {
for (i, ch) in chans.iter_mut().enumerate() {
x.set_chan(i);
for smpl in &mut x {
ch.1.buf.push(smpl.f32() * 0.5);
}
}
if x.pts() > Duration::seconds(15) {
break;
}
}
}
file.seek(Duration::seconds(1)).unwrap();
}
});
let time = Sender::<()>::precise_time_ns();
Expand Down
3 changes: 3 additions & 0 deletions src/errors.rs
Expand Up @@ -33,6 +33,9 @@ error_chain! {
UnsupportedFormat {
description("The file's sample format is currently unsupported.")
}
OnceOnly {
description("You may only call that function once.")
}
BsfNotFound { }
FfmpegBug { }
BufferTooSmall { }
Expand Down
2 changes: 1 addition & 1 deletion src/ffi.rs
Expand Up @@ -5,7 +5,7 @@ macro_rules! call {
($name:ident($($arg:expr),*)) => {{
#[allow(unused_unsafe)]
let ret = unsafe {
$name($($arg),+)
$name($($arg),*)
};
if ret < 0 {
use ErrorKind::*;
Expand Down
11 changes: 10 additions & 1 deletion src/frame.rs
Expand Up @@ -2,13 +2,16 @@
use ffmpeg_sys::*;
use errors::{MediaResult, ErrorKind};
use super::{SampleFormat, Sample};
use chrono::Duration;
use libc;
#[derive(Debug)]
pub struct Frame {
ptr: *mut AVFrame,
cur_chan: usize,
cur_idx: usize,
cap: usize,
chans: usize,
pts: libc::c_double,
format: SampleFormat
}
impl Drop for Frame {
Expand All @@ -19,7 +22,7 @@ impl Drop for Frame {
}
}
impl Frame {
pub unsafe fn from_ptr(ptr: *mut AVFrame) -> MediaResult<Self> {
pub unsafe fn from_ptr(ptr: *mut AVFrame, time: AVRational) -> MediaResult<Self> {
let format = (*ptr).format;
let format = if let Some(x) = SampleFormat::from_ffi(format) { x }
else {
Expand All @@ -30,11 +33,14 @@ impl Frame {
if !format.is_planar() {
cap *= chans;
}
let pts = (*ptr).pts as libc::c_double;
let time = av_q2d(time);
Ok(Frame {
ptr: ptr,
cur_chan: 0,
cur_idx: 0,
format: format,
pts: pts * time,
cap: cap as usize,
chans: chans as usize
})
Expand All @@ -58,6 +64,9 @@ impl Frame {
true
}
}
pub fn pts(&self) -> Duration {
Duration::nanoseconds((1_000_000_000f64 * self.pts) as _)
}
}
impl<'a> Iterator for &'a mut Frame {
type Item = Sample;
Expand Down
45 changes: 34 additions & 11 deletions src/lib.rs
Expand Up @@ -4,6 +4,7 @@ extern crate ffmpeg_sys;
extern crate error_chain;
extern crate libc;
extern crate sample;
extern crate chrono;

pub mod errors;
pub mod frame;
Expand All @@ -12,6 +13,7 @@ mod ffi;

pub use errors::{MediaResult, Error, ErrorKind};
pub use frame::Frame;
pub use chrono::Duration;
use ffmpeg_sys::*;
use std::ptr;
use ffi::str_to_cstr;
Expand Down Expand Up @@ -72,24 +74,32 @@ impl SampleFormat {
static mut INIT_ONCE: bool = false;
/// FFmpeg context (for thread safety).
pub struct MediaContext {
net: bool,
_ptr: *mut () // for !Send and !Sync
}
impl MediaContext {
pub fn network_init(&mut self) -> MediaResult<()> {
if self.net {
bail!(ErrorKind::OnceOnly);
}
call!(avformat_network_init());
self.net = true;
Ok(())
}
}
/// Initialise FFmpeg.
///
/// # Panics
///
/// Cannot be called more than once. Panics if you do so.
pub fn init() -> MediaContext {
pub fn init() -> MediaResult<MediaContext> {
unsafe {
if INIT_ONCE {
panic!("sqa-ffmpeg: init() called twice!");
bail!(ErrorKind::OnceOnly);
}
av_register_all();
INIT_ONCE = true;
av_register_all()
}
MediaContext {
Ok(MediaContext {
net: false,
_ptr: ptr::null_mut()
}
})
}
/// A media file, from which you can obtain many `AVFrame`s.
pub struct MediaFile {
Expand All @@ -100,7 +110,7 @@ unsafe impl Send for MediaFile { }
impl MediaFile {
/// Open a file from the given `url`, which is a [FFmpeg URL]
/// (https://ffmpeg.org/ffmpeg-protocols.html).
pub fn new(_ctx: &MediaContext, url: &str) -> MediaResult<MediaFile> {
pub fn new(_ctx: &mut MediaContext, url: &str) -> MediaResult<MediaFile> {
let url = str_to_cstr(url)?;
let mut ctx: *mut AVFormatContext = ptr::null_mut();
call!(avformat_open_input(&mut ctx, url.as_ptr(), ptr::null_mut(), ptr::null_mut()));
Expand Down Expand Up @@ -142,18 +152,31 @@ impl MediaFile {
let ptr = unsafe {
av_frame_alloc()
};
let base = unsafe { (*self.audio_ctx).time_base };
if ptr.is_null() {
bail!(ErrorKind::AllocationFailed);
}
call!(avcodec_receive_frame(self.audio_ctx, ptr));
Ok(unsafe { Frame::from_ptr(ptr)? })
Ok(unsafe { Frame::from_ptr(ptr, base)? })
}
pub fn channels(&self) -> usize {
(unsafe { (*self.audio_ctx).channels }) as usize
}
pub fn sample_rate(&self) -> usize {
(unsafe { (*self.audio_ctx).sample_rate }) as usize
}
pub fn duration(&self) -> Duration {
let dur = unsafe { (*self.format_ctx).duration };
Duration::microseconds(dur)
}
pub fn seek(&mut self, to: Duration) -> MediaResult<()> {
let to = to.num_microseconds().unwrap();
call!(av_seek_frame(self.format_ctx, -1, to, 0));
unsafe {
avcodec_flush_buffers(self.audio_ctx);
}
Ok(())
}
}
impl Iterator for MediaFile {
type Item = MediaResult<Frame>;
Expand Down

0 comments on commit 153f450

Please sign in to comment.