├── .editorconfig ├── .gitignore ├── .travis.yml ├── COPYRIGHT ├── Cargo.toml ├── README.md ├── av_sys ├── Cargo.toml ├── build.rs ├── ffi.h └── src │ └── lib.rs ├── examples ├── decoding.rs ├── demuxing.rs └── encoding.rs ├── music-44100hz-f32-le-mono.raw ├── rgb-600x400.data └── src ├── audio ├── constants.rs ├── decoder.rs ├── encoder.rs ├── frame.rs └── mod.rs ├── codec ├── codec.rs ├── descriptor.rs ├── media_type.rs ├── mime.rs ├── mod.rs └── profile.rs ├── common ├── codec_parameters.rs ├── encoder.rs ├── mod.rs ├── packet.rs ├── stream.rs ├── timebase.rs └── ts.rs ├── errors.rs ├── format ├── demuxer.rs ├── mod.rs ├── muxer.rs └── output_format.rs ├── generic ├── decoder.rs ├── encoder.rs ├── frame.rs ├── mod.rs └── ref_mut_frame.rs ├── io.rs ├── lib.rs ├── util.rs └── video ├── decoder.rs ├── encoder.rs ├── frame.rs ├── mod.rs └── scaler.rs /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | # Matches multiple files with brace expansion notation 12 | # Set default charset 13 | [*.{rs,toml}] 14 | charset = utf-8 15 | 16 | # 4 space indentation 17 | [*.rs] 18 | indent_style = space 19 | indent_size = 4 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: false 3 | language: rust 4 | cache: cargo 5 | rust: 6 | - stable 7 | - beta 8 | - nightly 9 | 10 | env: 11 | global: 12 | - FFMPEG_VERSION="3.3" 13 | - RUST_FFMPEG_PREFIX="${TRAVIS_BUILD_DIR}/ffmpeg" 14 | - LD_LIBRARY_PATH="${RUST_FFMPEG_PREFIX}/lib:${LD_LIBRARY_PATH}" 15 | - MAKEFLAGS="-j 2" 16 | # override the default `--features unstable` used for the nightly branch (optional) 17 | - TRAVIS_CARGO_NIGHTLY_FEATURE="" 18 | - secure: W0FFp6RhCmvjwy/w+HEWSVUkczswel515xBRwDcwvLYLjU/XyOLRc3mZR+CH1JCcbPVIZTcKb5YliTQvrWbIaMP//Xxzt+dNsVU37OF3XayXmYSWiHI45A5nY+K1T5eySqYq7mM4s9/RZACShQgv64x9QQRpVGAQRbxQCZmcnyQwhVWz2WFyeGorw+ILtqlQcef9VGJl2pyPrRCO8ZtqKRB9nrWxn9U9A6YiAxMY0Ype12PUTmBF7yUj1tuW0N5Fv9T69nv9j5+3hpH651zytiwzvRoUr0jsYhBJv3EjKW2cOf7uHkeA6k3iX5zjvN9YhLPsgJdWAmuvayXt4OX4jpConQlsULXmsRJ+/DJ2uUz+LK/5UMCgTz35gq0v5D7eoIV5M0iNFqfppUc5IlwvPmcPST41ZPrFfyHDSj6Wbquc2nM3S/f7YWRFjOPyfJDKyo+HDxsep/j0paJQ/3igHq3wiyse08pA8/dqqhnduSE8o5kztxQsPEuFhHU1gTcCuLqz7uCqJJxM07pQ+MLiYQ1VdHi3YiIzUHjrMz+u9+uUHl+6IANIN21+UVVigX+D+srzXzdIRx9KR+8/9FD48l9H0b54DS0NMOQlS9vmJW/udVsVRof3/ZKZqLPxw7b3LAbtv6mScKSfY1j3UPQ20Jy3HMh2p+o3wAkweebYrRo= 19 | 20 | before_script: 21 | - pip install 'travis-cargo<0.2' --user 22 | - export PATH=$HOME/.local/bin:$PATH 23 | - wget "http://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.bz2" 24 | - tar xf "ffmpeg-${FFMPEG_VERSION}.tar.bz2" 25 | - cd "ffmpeg-${FFMPEG_VERSION}" 26 | - ./configure --prefix="${RUST_FFMPEG_PREFIX}" --enable-pic --enable-shared --enable-nonfree && make && make install 27 | - cd .. 28 | 29 | script: 30 | - travis-cargo build 31 | - travis-cargo test 32 | # - travis-cargo bench 33 | - travis-cargo --only beta doc 34 | 35 | after_success: 36 | - travis-cargo --only beta doc-upload 37 | # - travis-cargo coveralls --no-sudo 38 | 39 | addons: 40 | apt: 41 | sources: 42 | - llvm-toolchain-trusty-3.9 43 | # - trusty-media 44 | 45 | packages: 46 | - libclang1-3.9 47 | - yasm 48 | # - ffmpeg 49 | # # Required for kcov 50 | # - libcurl4-openssl-dev 51 | # - libelf-dev 52 | # - libdw-dev 53 | 54 | matrix: 55 | allow_failures: 56 | - rust: nightly 57 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | For the Rust logo contained in `rgb-600x400.data` see: https://www.rust-lang.org/en-US/legal.html 2 | For the audio sample contained in `music-44100hz-f32-le-mono.raw` (which was slighly shortened) see: https://opus-codec.org/examples/ 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["panicbit "] 3 | name = "av" 4 | version = "0.1.0" 5 | 6 | [dependencies] 7 | bitflags = "0.7.0" 8 | error-chain = "0.9.0" 9 | lazy_static = "0.2.2" 10 | smallvec = "0.3.3" 11 | 12 | [dependencies.av_sys] 13 | path = "av_sys" 14 | 15 | [workspace] 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # [Documentation](https://panicbit.github.io/rust-av/av/) 3 | 4 | # Requirements 5 | 6 | - **LLVM >=3.9** 7 | 13 | - **FFmpeg 3.x** 14 | -------------------------------------------------------------------------------- /av_sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["panicbit "] 3 | build = "build.rs" 4 | name = "av_sys" 5 | version = "0.1.0" 6 | 7 | [build-dependencies] 8 | bindgen = "0.23.*" 9 | syn = { version = "0.11", features = ["full", "parsing", "printing"] } 10 | quote = "0.3" 11 | 12 | [dependencies] 13 | -------------------------------------------------------------------------------- /av_sys/build.rs: -------------------------------------------------------------------------------- 1 | extern crate bindgen; 2 | extern crate syn; 3 | extern crate quote; 4 | 5 | use std::env; 6 | use std::path::Path; 7 | use std::fs::File; 8 | use std::io::Write; 9 | use syn::{Item, ItemKind, Visibility, ConstExpr, Expr, ExprKind}; 10 | use quote::{Tokens, ToTokens}; 11 | 12 | fn main() { 13 | let out_dir = env::var("OUT_DIR").unwrap(); 14 | let prefix = env::var("RUST_FFMPEG_PREFIX").unwrap_or("/usr".to_owned()); 15 | let out_path = Path::new(&out_dir).join("ffi.rs"); 16 | let mut out_file = File::create(out_path).unwrap(); 17 | 18 | let bindings = bindgen::builder() 19 | .header("ffi.h") 20 | .clang_arg(format!("-I{}/include", prefix)) 21 | .no_unstable_rust() 22 | .whitelisted_type("AV.*") 23 | .whitelisted_var("AV.*") 24 | .whitelisted_var("FF.*") 25 | .whitelisted_function("av.*") 26 | .whitelisted_type("SWS.*") 27 | .whitelisted_var("SWS.*") 28 | .whitelisted_function("sws.*") 29 | .whitelisted_type("RUST_AV.*") 30 | .whitelisted_var("SEEK_.*") 31 | .whitelisted_type(".*_t") 32 | .generate() 33 | .unwrap() 34 | .to_string(); 35 | 36 | let mut krate = syn::parse_crate(&bindings).unwrap(); 37 | let const_enum_index = krate.items.iter().position(|item| item.ident == "RUST_AV_CONSTANTS").expect("RUST_AV_CONSTANTS not found"); 38 | let const_enum = krate.items.remove(const_enum_index); 39 | let variants = match const_enum.node { 40 | ItemKind::Enum(variants, _) => variants, 41 | _ => panic!("RUST_AV_CONSTANTS is not an enum"), 42 | }; 43 | 44 | for variant in variants { 45 | let variant_ident: Vec<&str> = variant.ident.as_ref().split("__").collect(); 46 | let ty_prefix = match variant_ident[0] { 47 | "RUST" => "", 48 | "RUST_OS_RAW" => "::std::os::raw::", 49 | _ => panic!("Unknown type prefix"), 50 | }; 51 | let ty = syn::parse_type(&format!("{}{}", ty_prefix, variant_ident[1])).unwrap(); 52 | let ident = syn::parse_ident(variant_ident[2]).unwrap(); 53 | let expr = const_expr_into_expr(variant.discriminant.expect("Discriminant missing from RUST_AV_CONSTANTS variant")); 54 | 55 | let item = Item { 56 | ident: ident.into(), 57 | vis: Visibility::Public, 58 | attrs: vec![], 59 | node: ItemKind::Const(Box::new(ty), expr), 60 | }; 61 | 62 | krate.items.push(item); 63 | } 64 | 65 | let mut tokens = Tokens::new(); 66 | krate.to_tokens(&mut tokens); 67 | write!(out_file, "{}", tokens).unwrap(); 68 | 69 | 70 | println!("cargo:rustc-link-search=native={}/lib", prefix); 71 | println!("cargo:rustc-link-lib=dylib=avutil"); 72 | println!("cargo:rustc-link-lib=dylib=avformat"); 73 | println!("cargo:rustc-link-lib=dylib=avcodec"); 74 | println!("cargo:rustc-link-lib=dylib=avdevice"); 75 | println!("cargo:rustc-link-lib=dylib=avfilter"); 76 | println!("cargo:rustc-link-lib=dylib=swresample"); 77 | println!("cargo:rustc-link-lib=dylib=swscale"); 78 | } 79 | 80 | fn const_expr_into_expr(const_expr: ConstExpr) -> Box { 81 | let node = match const_expr { 82 | ConstExpr::Lit(lit) => ExprKind::Lit(lit), 83 | ConstExpr::Unary(op, expr) => ExprKind::Unary(op, const_expr_into_expr(*expr)), 84 | expr => panic!("Unexpected discriminant kind: {:?}", expr), 85 | }; 86 | 87 | Box::new(Expr { 88 | node: node, 89 | attrs: vec![], 90 | }) 91 | } 92 | -------------------------------------------------------------------------------- /av_sys/ffi.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | enum RUST_AV_CONSTANTS { 11 | RUST__i64__NOPTS_VALUE = AV_NOPTS_VALUE, 12 | RUST_OS_RAW__c_int__AVERROR_EAGAIN = AVERROR(EAGAIN), 13 | RUST_OS_RAW__c_int__AVERROR_EOF = AVERROR_EOF, 14 | }; 15 | -------------------------------------------------------------------------------- /av_sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(warnings)] 2 | 3 | include!(concat!(env!("OUT_DIR"), "/ffi.rs")); 4 | -------------------------------------------------------------------------------- /examples/decoding.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate error_chain; 3 | extern crate av; 4 | 5 | use std::fs::File; 6 | 7 | use av::errors::ResultExt; 8 | use av::format::Demuxer; 9 | use av::generic::{Decoder,Frame}; 10 | 11 | quick_main!(decoding); 12 | 13 | fn decoding() -> av::Result<()> { 14 | av::LibAV::init(); 15 | 16 | let file = File::open("/tmp/output_rust.mp4") 17 | .chain_err(|| "Failed to open input file")?; 18 | 19 | let mut demuxer = Demuxer::open(file)?; 20 | 21 | // Dump some info 22 | demuxer.dump_info(); 23 | println!("{:?}", demuxer); 24 | 25 | // Create decoders 26 | let mut decoders = demuxer.streams() 27 | .map(|stream| Decoder::from_stream(&stream)) 28 | .collect::>>()?; 29 | 30 | let mut num_packets = 0; 31 | let mut num_video_frames = 0; 32 | let mut num_audio_frames = 0; 33 | 34 | // Demux packets 35 | while let Some(packet) = demuxer.read_packet()? { 36 | num_packets += 1; 37 | 38 | // Find the correct decoder for the packet 39 | let decoder = &mut decoders[packet.stream_index()]; 40 | 41 | // Feed the packet to the decoder 42 | let frames = decoder.decode(packet)?; 43 | 44 | for frame in frames { 45 | handle_frame(frame?, &mut num_video_frames, &mut num_audio_frames); 46 | } 47 | } 48 | 49 | // Flush decoders 50 | for mut decoder in decoders { 51 | for frame in decoder.flush()? { 52 | handle_frame(frame?, &mut num_video_frames, &mut num_audio_frames); 53 | } 54 | } 55 | 56 | println!("Demuxed {} packets", num_packets); 57 | println!("Decoded {} video frames", num_video_frames); 58 | println!("Decoded {} audio frames", num_audio_frames); 59 | 60 | Ok(()) 61 | } 62 | 63 | fn handle_frame(frame: Frame, num_video_frames: &mut usize, num_audio_frames: &mut usize) { 64 | // Handle decoded frame 65 | match frame { 66 | Frame::Video(_) => *num_video_frames += 1, 67 | Frame::Audio(_) => *num_audio_frames += 1, 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /examples/demuxing.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate error_chain; 3 | extern crate av; 4 | 5 | use std::fs::File; 6 | 7 | use av::errors::ResultExt; 8 | use av::format::Demuxer; 9 | 10 | quick_main!(decoding); 11 | 12 | fn decoding() -> av::Result<()> { 13 | av::LibAV::init(); 14 | 15 | let file = File::open("/tmp/output_rust.mp4") 16 | .chain_err(|| "Failed to open input file")?; 17 | 18 | let mut demuxer = Demuxer::open(file)?; 19 | 20 | // Dump some info 21 | demuxer.dump_info(); 22 | println!("{:?}", demuxer); 23 | 24 | let mut num_packets = 0; 25 | 26 | while demuxer.read_packet()?.is_some() { 27 | num_packets += 1; 28 | } 29 | 30 | println!("Demuxed {} packets", num_packets); 31 | 32 | Ok(()) 33 | } 34 | -------------------------------------------------------------------------------- /examples/encoding.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate error_chain; 3 | extern crate av; 4 | 5 | use std::fs::File; 6 | use av::codec::Codec; 7 | use av::format::{ 8 | Muxer, 9 | OutputFormat, 10 | }; 11 | use av::ffi::AVPixelFormat::*; 12 | use av::ffi::AVSampleFormat::*; 13 | use av::ffi::AVCodecID::*; 14 | use av::ffi::AVRational; 15 | use av::ffi; 16 | use av::audio::constants::CHANNEL_LAYOUT_MONO; 17 | use std::cmp::min; 18 | use av::{ 19 | audio, 20 | video, 21 | }; 22 | use av::errors::ResultExt; 23 | use av::common::Ts; 24 | use av::generic::{Encoder, Frame}; 25 | 26 | const MOVIE_DURATION: i64 = 10; 27 | const MOVIE_TIMEBASE: (i32, i32) = (1, 1); 28 | const VIDEO_DATA: &'static [u8] = include_bytes!("../rgb-600x400.data"); 29 | const AUDIO_DATA: &'static [u8] = include_bytes!("../music-44100hz-f32-le-mono.raw"); 30 | 31 | quick_main!(demo); 32 | 33 | pub fn demo() -> av::Result<()> { 34 | av::LibAV::init(); 35 | 36 | let file = File::create("/tmp/output_rust.mp4") 37 | .chain_err(|| "Failed to create output file")?; 38 | 39 | let output_format = OutputFormat::from_name("mp4") 40 | .ok_or("output format not found")?; 41 | println!("{:?}", output_format); 42 | 43 | let final_ts = Ts::new(MOVIE_DURATION, MOVIE_TIMEBASE); 44 | let mut timestamps = Vec::::new(); 45 | let mut encoders = Vec::::new(); 46 | let mut frames = Vec::::new(); 47 | 48 | // Create video encoder 49 | { 50 | let width = 600; 51 | let height = 400; 52 | let framerate = 30; 53 | let align = 32; 54 | let video_codec = Codec::find_encoder_by_id(AV_CODEC_ID_H264)?; 55 | let video_encoder = video::Encoder::from_codec(video_codec)? 56 | .width(width) 57 | .height(height) 58 | .pixel_format(*video_codec.pixel_formats().first().expect("Video encoder does not support any pixel formats, wtf?")) 59 | .time_base(framerate) 60 | .open(output_format)?; 61 | 62 | frames.push(video::Frame::new(width, height, AV_PIX_FMT_RGB24, align)?.into()); 63 | timestamps.push(Ts::new(0, video_encoder.time_base())); 64 | encoders.push(video_encoder.into()); 65 | } 66 | let mut video_frame_buffer = VIDEO_DATA.to_vec(); 67 | 68 | // Create audio encoder 69 | { 70 | let sample_rate = 44100; 71 | let sample_format = AV_SAMPLE_FMT_FLTP; 72 | let channel_layout = CHANNEL_LAYOUT_MONO; 73 | let audio_codec = Codec::find_encoder_by_id(AV_CODEC_ID_AAC)?; 74 | let audio_encoder = audio::Encoder::from_codec(audio_codec)? 75 | .sample_rate(sample_rate) 76 | .sample_format(sample_format) 77 | .channel_layout(channel_layout) 78 | .open(output_format)?; 79 | let audio_frame_size = audio_encoder.frame_size().unwrap_or(10000); 80 | let audio_frame = audio::Frame::new(audio_frame_size, sample_rate, sample_format, channel_layout)?; 81 | 82 | frames.push(audio_frame.into()); 83 | timestamps.push(Ts::new(0, audio_encoder.time_base())); 84 | encoders.push(audio_encoder.into()); 85 | } 86 | let mut audio_data = AUDIO_DATA; 87 | 88 | // Create format muxer 89 | let mut muxer = Muxer::new(output_format, file)?; 90 | 91 | for encoder in &encoders { 92 | muxer.add_stream_from_encoder(&encoder)?; 93 | } 94 | 95 | let mut muxer = muxer.open()?; 96 | 97 | muxer.dump_info(); 98 | 99 | loop { 100 | let index = timestamps.iter().enumerate().min_by_key(|&(_, ts)| ts).unwrap().0; 101 | let ts = &mut timestamps[index]; 102 | let encoder = &mut encoders[index]; 103 | 104 | if *ts >= final_ts { 105 | break; 106 | } 107 | 108 | match *encoder { 109 | Encoder::Video(ref mut encoder) => { 110 | let frame = frames[index].as_mut_video_frame().unwrap(); 111 | 112 | // Render video frame 113 | frame.set_pts(ts.index()); 114 | render_demo_bar(frame, &mut video_frame_buffer, ts)?; 115 | *ts += 1; 116 | 117 | // Encode and mux video frame 118 | muxer.mux_all(encoder.encode(frame)?, index)?; 119 | }, 120 | Encoder::Audio(ref mut encoder) => { 121 | let frame = frames[index].as_mut_audio_frame().unwrap(); 122 | 123 | // Render audio frame 124 | frame.set_pts(ts.index()); 125 | render_audio(frame, &mut audio_data); 126 | *ts += frame.num_samples() as i64; 127 | 128 | // Encode and mux audio frame 129 | muxer.mux_all(encoder.encode(frame)?, index)?; 130 | }, 131 | } 132 | } 133 | 134 | // Flush encoders 135 | for (index, encoder) in encoders.into_iter().enumerate() { 136 | muxer.mux_all(encoder.flush()?, index)?; 137 | } 138 | 139 | Ok(()) 140 | } 141 | 142 | fn render_demo_bar(frame: &mut video::Frame, video_frame_buffer: &mut [u8], ts: &Ts) -> av::Result<()> { 143 | let width = frame.width(); 144 | let max_pts = 300; 145 | let pixel_per_pts = width / max_pts; 146 | let bar_pos = ts.index() as usize * pixel_per_pts; 147 | let bytes_per_pixel = 3; 148 | 149 | video_frame_buffer.copy_from_slice(VIDEO_DATA); 150 | for line in video_frame_buffer.chunks_mut(width * bytes_per_pixel) { 151 | for pixel in line.chunks_mut(bytes_per_pixel).take(bar_pos) { 152 | for component in pixel { 153 | *component = *component / 3; 154 | } 155 | } 156 | } 157 | 158 | frame.fill_channel(0, &video_frame_buffer) 159 | } 160 | 161 | fn render_audio(audio_frame: &mut audio::Frame, audio_data: &mut &[u8]) { 162 | println!("### TODO: Do proper audio rendering"); 163 | println!("### frame_size: {}", audio_frame.data_mut()[0].len()); 164 | let buf_len = min(audio_data.len(), audio_frame.data_mut()[0].len()); 165 | 166 | audio_frame.data_mut()[0][..buf_len].copy_from_slice(&audio_data[..buf_len]); 167 | *audio_data = &audio_data[buf_len..]; 168 | println!("### Remaining audio bytes: {}", audio_data.len()); 169 | } 170 | -------------------------------------------------------------------------------- /music-44100hz-f32-le-mono.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panicbit/rust-av/41ac44e4276ebfce23d7b0b0b3a76e6b2bb2de9a/music-44100hz-f32-le-mono.raw -------------------------------------------------------------------------------- /rgb-600x400.data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panicbit/rust-av/41ac44e4276ebfce23d7b0b0b3a76e6b2bb2de9a/rgb-600x400.data -------------------------------------------------------------------------------- /src/audio/constants.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | 3 | bitflags! { 4 | pub flags ChannelLayout: u64 { 5 | const CHANNEL_LAYOUT_NATIVE = AV_CH_LAYOUT_NATIVE as u64, 6 | const CHANNEL_LAYOUT_MONO = AV_CH_LAYOUT_MONO as u64, 7 | const CHANNEL_LAYOUT_STEREO = AV_CH_LAYOUT_STEREO as u64, 8 | const CHANNEL_LAYOUT_2POINT1 = AV_CH_LAYOUT_2POINT1 as u64, 9 | const CHANNEL_LAYOUT_2_1 = AV_CH_LAYOUT_2_1 as u64, 10 | const CHANNEL_LAYOUT_SURROUND = AV_CH_LAYOUT_SURROUND as u64, 11 | const CHANNEL_LAYOUT_3POINT1 = AV_CH_LAYOUT_3POINT1 as u64, 12 | const CHANNEL_LAYOUT_4POINT0 = AV_CH_LAYOUT_4POINT0 as u64, 13 | const CHANNEL_LAYOUT_4POINT1 = AV_CH_LAYOUT_4POINT1 as u64, 14 | const CHANNEL_LAYOUT_2_2 = AV_CH_LAYOUT_2_2 as u64, 15 | const CHANNEL_LAYOUT_QUAD = AV_CH_LAYOUT_QUAD as u64, 16 | const CHANNEL_LAYOUT_5POINT0 = AV_CH_LAYOUT_5POINT0 as u64, 17 | const CHANNEL_LAYOUT_5POINT1 = AV_CH_LAYOUT_5POINT1 as u64, 18 | const CHANNEL_LAYOUT_5POINT0_BACK = AV_CH_LAYOUT_5POINT0_BACK as u64, 19 | const CHANNEL_LAYOUT_5POINT1_BACK = AV_CH_LAYOUT_5POINT1_BACK as u64, 20 | const CHANNEL_LAYOUT_6POINT0 = AV_CH_LAYOUT_6POINT0 as u64, 21 | const CHANNEL_LAYOUT_6POINT0_FRONT = AV_CH_LAYOUT_6POINT0_FRONT as u64, 22 | const CHANNEL_LAYOUT_HEXAGONAL = AV_CH_LAYOUT_HEXAGONAL as u64, 23 | const CHANNEL_LAYOUT_6POINT1 = AV_CH_LAYOUT_6POINT1 as u64, 24 | const CHANNEL_LAYOUT_6POINT1_BACK = AV_CH_LAYOUT_6POINT1_BACK as u64, 25 | const CHANNEL_LAYOUT_6POINT1_FRONT = AV_CH_LAYOUT_6POINT1_FRONT as u64, 26 | const CHANNEL_LAYOUT_7POINT0 = AV_CH_LAYOUT_7POINT0 as u64, 27 | const CHANNEL_LAYOUT_7POINT0_FRONT = AV_CH_LAYOUT_7POINT0_FRONT as u64, 28 | const CHANNEL_LAYOUT_7POINT1 = AV_CH_LAYOUT_7POINT1 as u64, 29 | const CHANNEL_LAYOUT_7POINT1_WIDE = AV_CH_LAYOUT_7POINT1_WIDE as u64, 30 | const CHANNEL_LAYOUT_7POINT1_WIDE_BACK = AV_CH_LAYOUT_7POINT1_WIDE_BACK as u64, 31 | const CHANNEL_LAYOUT_OCTAGONAL = AV_CH_LAYOUT_OCTAGONAL as u64, 32 | const CHANNEL_LAYOUT_HEXADECAGONAL = AV_CH_LAYOUT_HEXADECAGONAL as u64, 33 | const CHANNEL_LAYOUT_STEREO_DOWNMIX = AV_CH_LAYOUT_STEREO_DOWNMIX as u64, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/audio/decoder.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | use ffi; 3 | use ffi::AVCodecContext; 4 | use codec::{Codec,MediaType}; 5 | use common::codec_parameters::CodecParameters; 6 | use common::stream::Stream; 7 | use common::{Packet, Timebase}; 8 | use super::Frame; 9 | use errors::*; 10 | 11 | pub struct Decoder { 12 | ptr: *mut AVCodecContext, 13 | } 14 | 15 | unsafe impl Send for Decoder{} 16 | unsafe impl Sync for Decoder{} 17 | 18 | impl Decoder { 19 | // TODO: Share code between audio/video 20 | pub fn from_codec_parameters<'fmt_ctx>(codec_parameters: CodecParameters<'fmt_ctx>) -> Result { 21 | unsafe { 22 | let codec_id = codec_parameters.codec_id(); 23 | 24 | // Try to find a suitable codec 25 | let codec = Codec::find_decoder_by_id(codec_id)?; 26 | if !codec.media_type().is_audio() { 27 | bail!(ErrorKind::MediaTypeMismatch(MediaType::Audio, codec_id)) 28 | } 29 | 30 | // Try to allocate the decoder 31 | let mut codec_context = ffi::avcodec_alloc_context3(codec.as_ptr()); 32 | if codec_context.is_null() { 33 | bail!("Could not allocate audio decoder"); 34 | } 35 | 36 | // Copy codec parameters to codec_parameters 37 | { 38 | let res = ffi::avcodec_parameters_to_context(codec_context, codec_parameters.as_ptr()); 39 | if res < 0 { 40 | ffi::avcodec_free_context(&mut codec_context); 41 | bail!(ErrorKind::CopyCodecParameters); 42 | } 43 | } 44 | 45 | // Try to open the decoder 46 | { 47 | let options = ptr::null_mut(); 48 | let res = ffi::avcodec_open2(codec_context, codec.as_ptr(), options); 49 | if res < 0 { 50 | ffi::avcodec_free_context(&mut codec_context); 51 | bail!(ErrorKind::OpenDecoder("audio")); 52 | } 53 | } 54 | 55 | Ok(Decoder { 56 | ptr: codec_context, 57 | }) 58 | } 59 | } 60 | 61 | pub fn from_stream(stream: &Stream) -> Result { 62 | Self::from_codec_parameters(stream.codec_parameters()) 63 | } 64 | 65 | pub fn codec(&self) -> Codec { 66 | unsafe { 67 | Codec::from_ptr(self.as_ref().codec) 68 | } 69 | } 70 | 71 | pub fn time_base(&self) -> Timebase { 72 | self.as_ref().time_base.into() 73 | } 74 | 75 | pub fn sample_format(&self) -> ffi::AVSampleFormat { 76 | self.as_ref().sample_fmt 77 | } 78 | 79 | pub fn decode<'decoder>(&'decoder mut self, mut packet: Packet) -> Result> { 80 | // TODO: Check that pkt->data is AV_INPUT_BUFFER_PADDING_SIZE larger than packet size 81 | 82 | unsafe { 83 | let res = ffi::avcodec_send_packet(self.as_mut_ptr(), packet.as_mut_ptr()); 84 | if res < 0 { 85 | match res { 86 | ffi::AVERROR_EAGAIN => bail!("EAGAIN in Decoder::decode. This is not supposed to happen :("), 87 | _ => bail!(format!("Failed to decode packet: 0x{:X}", res)) 88 | } 89 | } 90 | 91 | Ok(Frames::from_decoder(self)) 92 | } 93 | } 94 | 95 | pub fn flush<'decoder>(&'decoder mut self) -> Result> { 96 | // TODO: Check that pkt->data is AV_INPUT_BUFFER_PADDING_SIZE larger than packet size 97 | 98 | unsafe { 99 | let res = ffi::avcodec_send_packet(self.as_mut_ptr(), ptr::null_mut()); 100 | 101 | if res < 0 && res != ffi::AVERROR_EAGAIN { 102 | bail!(format!("Failed to flush decoder: 0x{:X}", res)) 103 | } 104 | 105 | Ok(Frames::from_decoder(self)) 106 | } 107 | } 108 | } 109 | 110 | impl Decoder { 111 | pub fn as_ref(&self) -> &AVCodecContext { unsafe { &*self.ptr } } 112 | pub fn as_mut(&mut self) -> &mut AVCodecContext { unsafe { &mut *self.ptr } } 113 | pub fn as_ptr(&self) -> *const AVCodecContext { self.ptr } 114 | pub fn as_mut_ptr(&mut self) -> *mut AVCodecContext { self.ptr } 115 | } 116 | 117 | pub struct Frames<'decoder> { 118 | decoder: &'decoder mut Decoder, 119 | } 120 | 121 | impl<'decoder> Frames<'decoder> { 122 | fn from_decoder(decoder: &'decoder mut Decoder) -> Self { 123 | Frames { 124 | decoder: decoder 125 | } 126 | } 127 | } 128 | 129 | impl<'decoder> Iterator for Frames<'decoder> { 130 | type Item = Result; 131 | 132 | fn next(&mut self) -> Option { 133 | unsafe { 134 | let mut frame = ffi::av_frame_alloc(); 135 | let res = ffi::avcodec_receive_frame(self.decoder.as_mut_ptr(), frame); 136 | 137 | if res < 0 { 138 | ffi::av_frame_free(&mut frame); 139 | 140 | match res { 141 | ffi::AVERROR_EAGAIN | ffi::AVERROR_EOF => return None, 142 | _ => return Some(Err(format!("Failed to receive frame: 0x{:X}", res).into())), 143 | } 144 | } 145 | 146 | let sample_format = self.decoder.sample_format(); 147 | let frame = Frame::from_ptr(frame, sample_format); 148 | 149 | Some(Ok(frame)) 150 | } 151 | } 152 | } 153 | 154 | impl<'decoder> Drop for Frames<'decoder> { 155 | fn drop(&mut self) { 156 | // Decode every frame possible 157 | for _ in self {} 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/audio/encoder.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | use std::os::raw::c_int; 3 | use LibAV; 4 | use codec::{ 5 | Codec, 6 | MediaType, 7 | }; 8 | use ffi::{ 9 | self, 10 | AVCodecContext, 11 | AVSampleFormat, 12 | AVRational, 13 | avcodec_alloc_context3, 14 | avcodec_free_context, 15 | av_get_channel_layout_nb_channels, 16 | }; 17 | use ffi::AVSampleFormat::AV_SAMPLE_FMT_S16; 18 | use format::OutputFormat; 19 | use audio::ChannelLayout; 20 | use audio::constants::CHANNEL_LAYOUT_STEREO; 21 | use generic::RefMutFrame; 22 | use common::{self, Packet, Timebase}; 23 | use errors::*; 24 | use util::OwnedOrRefMut; 25 | 26 | pub struct Encoder { 27 | ptr: *mut AVCodecContext, 28 | } 29 | unsafe impl Send for Encoder {} 30 | unsafe impl Sync for Encoder {} 31 | 32 | impl Encoder { 33 | pub fn from_codec(codec: Codec) -> Result { 34 | EncoderBuilder::from_codec(codec) 35 | } 36 | 37 | pub fn sample_format(&self) -> AVSampleFormat { 38 | self.as_ref().sample_fmt 39 | } 40 | 41 | /// TODO: Check for underflow 42 | pub fn sample_rate(&self) -> u32 { 43 | self.as_ref().sample_rate as u32 44 | } 45 | 46 | pub fn time_base(&self) -> Timebase { 47 | self.as_ref().time_base.into() 48 | } 49 | 50 | // Returns the frame size required by the encoder. 51 | // If the result is `None`, any frame size can be used. 52 | pub fn frame_size(&self) -> Option { 53 | match self.as_ref().frame_size as usize { 54 | 0 => None, 55 | size => Some(size), 56 | } 57 | } 58 | 59 | pub fn codec(&self) -> Codec { 60 | unsafe { 61 | Codec::from_ptr(self.as_ref().codec) 62 | } 63 | } 64 | } 65 | 66 | impl Encoder { 67 | pub fn encode<'a, F>(&mut self, frame: F) -> Result where 68 | F: Into>, 69 | { 70 | unsafe { 71 | let mut frame = frame.into().into_audio_frame() 72 | .ok_or("Cannot encode non-audio frame as audio")?; 73 | 74 | // Do scaling if needed 75 | // if !frame.is_compatible_with_encoder(self) { 76 | // self.update_scaler(frame)?; 77 | // self.init_tmp_frame()?; 78 | 79 | // let tmp_frame = self.tmp_frame.as_mut().unwrap(); 80 | // let scaler = self.scaler.as_mut().unwrap(); 81 | 82 | // scaler.scale_frame(&mut frame, tmp_frame); 83 | 84 | // // Copy frame data 85 | // tmp_frame.set_pts(frame.pts()); 86 | // frame = tmp_frame; 87 | // } 88 | 89 | // Encode the frame 90 | { 91 | let mut packet = ::std::mem::zeroed(); 92 | ffi::av_init_packet(&mut packet); 93 | 94 | let res = ffi::avcodec_send_frame(self.ptr, frame.as_mut_ptr()); 95 | if res < 0 { 96 | bail!("Could not encode frame: 0x{:X}", res) 97 | } 98 | 99 | } 100 | 101 | Ok(Packets::from_mut_encoder(self)) 102 | } 103 | } 104 | 105 | pub fn flush(self) -> Result> { 106 | unsafe { 107 | // Flush encoder 108 | let res = ffi::avcodec_send_frame(self.ptr, ptr::null_mut()); 109 | if res < 0 { 110 | bail!("Could not flush encoder: 0x{:X}", res) 111 | } 112 | 113 | Ok(Packets::from_encoder(self)) 114 | } 115 | } 116 | } 117 | 118 | impl Encoder { 119 | pub fn as_mut(&mut self) -> &mut AVCodecContext { unsafe { &mut *self.ptr } } 120 | pub fn as_ptr(&self) -> *const AVCodecContext { self.ptr } 121 | pub fn as_mut_ptr(&mut self) -> *mut AVCodecContext { self.ptr } 122 | } 123 | 124 | impl AsRef for Encoder { 125 | fn as_ref(&self) -> &AVCodecContext { 126 | unsafe { &*self.ptr } 127 | } 128 | } 129 | 130 | impl Drop for Encoder { 131 | fn drop(&mut self) { 132 | unsafe { 133 | if !self.ptr.is_null() { 134 | avcodec_free_context(&mut self.ptr); 135 | } 136 | } 137 | } 138 | } 139 | 140 | pub struct EncoderBuilder { 141 | codec: Codec, 142 | sample_format: Option, 143 | sample_rate: Option, 144 | channel_layout: Option, 145 | } 146 | 147 | impl EncoderBuilder { 148 | pub fn from_codec(codec: Codec) -> Result { 149 | common::encoder::require_is_encoder(codec)?; 150 | common::encoder::require_codec_type(MediaType::Audio, codec)?; 151 | 152 | Ok(EncoderBuilder { 153 | codec: codec, 154 | sample_format: None, 155 | sample_rate: None, 156 | channel_layout: None, 157 | }) 158 | } 159 | 160 | pub fn sample_format(&mut self, sample_format: AVSampleFormat) -> &mut Self { 161 | self.sample_format = Some(sample_format); self 162 | } 163 | 164 | /// TODO: Check for overflow 165 | pub fn sample_rate(&mut self, sample_rate: u32) -> &mut Self { 166 | self.sample_rate = Some(sample_rate); self 167 | } 168 | 169 | pub fn channel_layout(&mut self, channel_layout: ChannelLayout) -> &mut Self { 170 | self.channel_layout = Some(channel_layout); self 171 | } 172 | 173 | pub fn open(&self, format: OutputFormat) -> Result { 174 | unsafe { 175 | let sample_rate = self.sample_rate.unwrap_or(44100) as c_int; 176 | let sample_format = self.sample_format.unwrap_or(AV_SAMPLE_FMT_S16); 177 | let channel_layout = self.channel_layout.unwrap_or(CHANNEL_LAYOUT_STEREO); 178 | 179 | LibAV::init(); 180 | 181 | let mut codec_context = avcodec_alloc_context3(self.codec.as_ptr()); 182 | if codec_context.is_null() { 183 | bail!("Could not allocate an encoding context"); 184 | } 185 | 186 | // Initialize encoder fields 187 | common::encoder::init(codec_context, format); 188 | (*codec_context).sample_rate = sample_rate; 189 | (*codec_context).sample_fmt = sample_format; 190 | (*codec_context).time_base = AVRational { num: 1, den: sample_rate }; 191 | (*codec_context).channel_layout = channel_layout.bits(); 192 | (*codec_context).channels = av_get_channel_layout_nb_channels(channel_layout.bits()); 193 | 194 | common::encoder::open(codec_context, "audio")?; 195 | 196 | Ok(Encoder { 197 | ptr: codec_context, 198 | }) 199 | } 200 | } 201 | } 202 | 203 | pub struct Packets<'encoder> { 204 | encoder: OwnedOrRefMut<'encoder, Encoder>, 205 | } 206 | 207 | impl<'encoder> Packets<'encoder> { 208 | fn from_encoder(encoder: Encoder) -> Self { 209 | Packets { 210 | encoder: OwnedOrRefMut::Owned(encoder) 211 | } 212 | } 213 | 214 | fn from_mut_encoder(encoder: &'encoder mut Encoder) -> Self { 215 | Packets { 216 | encoder: OwnedOrRefMut::Borrowed(encoder) 217 | } 218 | } 219 | } 220 | 221 | impl<'encoder> Iterator for Packets<'encoder> { 222 | type Item = Result>; 223 | 224 | fn next(&mut self) -> Option { 225 | unsafe { 226 | let mut packet = ffi::av_packet_alloc(); 227 | let res = ffi::avcodec_receive_packet(self.encoder.as_mut_ptr(), packet); 228 | 229 | if res < 0 { 230 | ffi::av_packet_free(&mut packet); 231 | 232 | match res { 233 | ffi::AVERROR_EAGAIN | ffi::AVERROR_EOF => return None, 234 | _ => return Some(Err(format!("Failed to receive packet: 0x{:X}", res).into())), 235 | } 236 | } 237 | 238 | let packet = Packet::from_ptr(packet, self.encoder.time_base()); 239 | 240 | Some(Ok(packet)) 241 | } 242 | } 243 | } 244 | 245 | impl<'encoder> Drop for Packets<'encoder> { 246 | fn drop(&mut self) { 247 | // Receive every packet possible 248 | for _ in self {} 249 | } 250 | } 251 | 252 | -------------------------------------------------------------------------------- /src/audio/frame.rs: -------------------------------------------------------------------------------- 1 | use std::slice; 2 | use std::os::raw::c_int; 3 | use audio::ChannelLayout; 4 | use ffi::{ 5 | self, 6 | AVFrame, 7 | AVSampleFormat, 8 | av_frame_alloc, 9 | av_frame_get_buffer, 10 | av_frame_free, 11 | av_get_channel_layout_nb_channels, 12 | av_sample_fmt_is_planar, 13 | AV_NUM_DATA_POINTERS, 14 | }; 15 | use errors::*; 16 | 17 | pub struct Frame { 18 | ptr: *mut AVFrame, 19 | sample_format: AVSampleFormat, 20 | } 21 | 22 | // See https://github.com/panicbit/rust-av/issues/28 23 | unsafe impl Send for Frame {} 24 | unsafe impl Sync for Frame {} 25 | 26 | impl Frame { 27 | /// TODO: Check for overflows 28 | pub fn new(num_samples: usize, sample_rate: u32, sample_format: AVSampleFormat, channel_layout: ChannelLayout) -> Result { 29 | unsafe { 30 | 31 | let mut frame = av_frame_alloc(); 32 | if frame.is_null() { 33 | bail!("Could not allocate frame"); 34 | } 35 | 36 | (*frame).pts = 0; 37 | (*frame).format = sample_format as c_int; 38 | (*frame).channel_layout = channel_layout.bits(); 39 | (*frame).sample_rate = sample_rate as i32; 40 | (*frame).nb_samples = num_samples as i32; 41 | 42 | if num_samples > 0 { 43 | let align = 0; 44 | let res = av_frame_get_buffer(frame, align); 45 | if res < 0 { 46 | av_frame_free(&mut frame); 47 | bail!("Could not allocate audio frame buffer: 0x{:X}", res); 48 | } 49 | } 50 | 51 | Ok(Frame { 52 | ptr: frame, 53 | sample_format: sample_format, 54 | }) 55 | } 56 | } 57 | 58 | pub fn num_channels(&self) -> usize { 59 | unsafe { 60 | av_get_channel_layout_nb_channels(self.as_ref().channel_layout) as usize 61 | } 62 | } 63 | 64 | pub fn is_planar(&self) -> bool { 65 | unsafe { 66 | av_sample_fmt_is_planar(self.sample_format) != 0 67 | } 68 | } 69 | 70 | pub fn data_mut(&mut self) -> [&mut [u8]; AV_NUM_DATA_POINTERS as usize] { 71 | unsafe { 72 | if ffi::av_frame_make_writable(self.ptr) < 0 { 73 | panic!("av_frame_make_writable failed (OOM?)"); 74 | } 75 | 76 | // For audio only linesize[0] is set. Every channel needs to have the same size. 77 | let buf_len = self.as_ref().linesize[0] as usize; 78 | let mut channels: [&mut [u8]; AV_NUM_DATA_POINTERS as usize] = Default::default(); 79 | let mut num_channels = self.num_channels(); 80 | // interleaved formats maximally have one data channel 81 | if !self.is_planar() && num_channels > 1 { 82 | num_channels = 1; 83 | } 84 | 85 | for i in 0..num_channels { 86 | channels[i] = slice::from_raw_parts_mut(self.as_ref().data[i], buf_len); 87 | } 88 | 89 | channels 90 | } 91 | } 92 | 93 | pub fn num_samples(&self) -> usize { 94 | self.as_ref().nb_samples as usize 95 | } 96 | 97 | pub fn set_pts(&mut self, pts: i64) { 98 | self.as_mut().pts = pts; 99 | } 100 | } 101 | 102 | impl Frame { 103 | pub unsafe fn from_ptr(ptr: *mut AVFrame, sample_format: AVSampleFormat) -> Self { 104 | Frame { 105 | ptr: ptr, 106 | sample_format: sample_format, 107 | } 108 | } 109 | 110 | pub fn as_ref(&self) -> &AVFrame { 111 | unsafe { &*self.ptr } 112 | } 113 | 114 | pub fn as_mut(&mut self) -> &mut AVFrame { 115 | unsafe { &mut *self.ptr } 116 | } 117 | 118 | pub fn as_mut_ptr(&mut self) -> *mut AVFrame { 119 | self.ptr 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/audio/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | pub mod constants; 3 | pub use self::constants::ChannelLayout; 4 | 5 | mod encoder; 6 | pub use self::encoder::{ 7 | Encoder, 8 | EncoderBuilder, 9 | Packets, 10 | }; 11 | 12 | mod decoder; 13 | pub use self::decoder::{ 14 | Decoder, 15 | Frames, 16 | }; 17 | 18 | mod frame; 19 | pub use self::frame::Frame; 20 | -------------------------------------------------------------------------------- /src/codec/codec.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::slice; 3 | use std::ffi::{CString, CStr}; 4 | use LibAV; 5 | use ffi::{ 6 | AVCodec, 7 | AVCodecID, 8 | AVPixelFormat, 9 | avcodec_find_encoder_by_name, 10 | avcodec_find_encoder, 11 | avcodec_find_decoder, 12 | av_codec_is_encoder, 13 | av_codec_is_decoder, 14 | }; 15 | use codec::MediaType; 16 | use super::{ 17 | Descriptor, 18 | DescriptorIter, 19 | }; 20 | use util::AsCStr; 21 | 22 | #[derive(Copy,Clone)] 23 | pub struct Codec { 24 | ptr: *const AVCodec 25 | } 26 | use errors::*; 27 | 28 | impl Codec { 29 | pub fn find_encoder_by_name(name: &str) -> Result { 30 | unsafe { 31 | LibAV::init(); 32 | let c_name = CString::new(name) 33 | .map_err(|_| ErrorKind::EncoderNotFound(name.to_string()))?; 34 | let codec = avcodec_find_encoder_by_name(c_name.as_ptr()); 35 | if codec.is_null() { 36 | bail!(ErrorKind::EncoderNotFound(name.to_string())) 37 | } 38 | Ok(Self::from_ptr(codec)) 39 | } 40 | } 41 | 42 | pub fn find_encoder_by_id(codec_id: AVCodecID) -> Result { 43 | unsafe { 44 | LibAV::init(); 45 | let codec = avcodec_find_encoder(codec_id); 46 | if codec.is_null() { 47 | // TODO: maybe use avcodec_get_name(codec_id) 48 | bail!(ErrorKind::EncoderNotFound(format!("{:?}", codec_id))) 49 | } 50 | Ok(Self::from_ptr(codec)) 51 | } 52 | } 53 | 54 | pub fn find_decoder_by_id(codec_id: AVCodecID) -> Result { 55 | unsafe { 56 | LibAV::init(); 57 | let codec = avcodec_find_decoder(codec_id); 58 | if codec.is_null() { 59 | // TODO: maybe use avcodec_get_name(codec_id) 60 | bail!(ErrorKind::DecoderNotFound(format!("{:?}", codec_id))) 61 | } 62 | Ok(Self::from_ptr(codec)) 63 | } 64 | } 65 | 66 | pub fn is_encoder(&self) -> bool { 67 | unsafe { av_codec_is_encoder(self.ptr) != 0 } 68 | } 69 | 70 | pub fn is_decoder(&self) -> bool { 71 | unsafe { av_codec_is_decoder(self.ptr) != 0 } 72 | } 73 | 74 | pub fn id(&self) -> AVCodecID { 75 | self.as_ref().id 76 | } 77 | 78 | pub fn name(&self) -> &CStr { 79 | unsafe { self.as_ref().name.as_cstr().unwrap() } 80 | } 81 | 82 | pub fn long_name(&self) -> &CStr { 83 | unsafe { self.as_ref().name.as_cstr().unwrap() } 84 | } 85 | 86 | pub fn media_type(&self) -> MediaType { 87 | MediaType::from_raw(self.as_ref().type_) 88 | } 89 | 90 | pub fn pixel_formats(&self) -> &[AVPixelFormat] { 91 | unsafe { 92 | use ffi::AVPixelFormat::AV_PIX_FMT_NONE; 93 | 94 | let pix_fmts = (*self.ptr).pix_fmts; 95 | let mut len = 0; 96 | 97 | while *pix_fmts.offset(len) != AV_PIX_FMT_NONE { 98 | len += 1; 99 | } 100 | 101 | slice::from_raw_parts(pix_fmts, len as usize) 102 | } 103 | } 104 | 105 | pub fn descriptors() -> DescriptorIter { 106 | LibAV::init(); 107 | DescriptorIter::new() 108 | } 109 | } 110 | 111 | impl Codec { 112 | pub unsafe fn from_ptr(ptr: *const AVCodec) -> Self { 113 | Codec { ptr: ptr } 114 | } 115 | 116 | pub fn as_ref(&self) -> &AVCodec { 117 | unsafe { &*self.ptr } 118 | } 119 | 120 | pub fn as_ptr(&self) -> *const AVCodec { 121 | self.ptr 122 | } 123 | } 124 | 125 | impl fmt::Debug for Codec { 126 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 127 | f.debug_struct("Codec") 128 | .field("id", &self.id()) 129 | .field("name", &self.name()) 130 | .field("long_name", &self.long_name()) 131 | .field("is_encoder", &self.is_encoder()) 132 | .field("is_decoder", &self.is_decoder()) 133 | .field("media_type", &self.media_type()) 134 | .field("pixel_formats", &self.pixel_formats()) 135 | .finish() 136 | } 137 | } 138 | 139 | pub trait AVCodecIDExt { 140 | fn descriptor(self) -> Option; 141 | } 142 | 143 | impl AVCodecIDExt for AVCodecID { 144 | fn descriptor(self) -> Option { 145 | Descriptor::from_codec_id(self) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/codec/descriptor.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::fmt; 3 | use std::ptr; 4 | use LibAV; 5 | use ffi::{ 6 | AVCodecID, 7 | AVCodecDescriptor, 8 | avcodec_descriptor_get, 9 | avcodec_descriptor_next, 10 | }; 11 | use super::ProfileIter; 12 | use super::MimeTypeIter; 13 | use util::AsCStr; 14 | use codec::MediaType; 15 | 16 | #[derive(PartialEq)] 17 | pub struct Descriptor { 18 | ptr: *const AVCodecDescriptor 19 | } 20 | 21 | impl Descriptor { 22 | pub fn from_codec_id(codec_id: AVCodecID) -> Option { 23 | unsafe { 24 | LibAV::init(); 25 | let descriptor = avcodec_descriptor_get(codec_id);; 26 | if descriptor.is_null() { 27 | None 28 | } else { 29 | Some(Self::from_ptr(descriptor)) 30 | } 31 | } 32 | } 33 | 34 | pub fn as_ref(&self) -> &AVCodecDescriptor { 35 | unsafe { &*self.ptr } 36 | } 37 | 38 | pub fn id(&self) -> AVCodecID { 39 | self.as_ref().id 40 | } 41 | 42 | pub fn media_type(&self) -> MediaType { 43 | MediaType::from_raw(self.as_ref().type_) 44 | } 45 | 46 | pub fn name(&self) -> &CStr { 47 | unsafe { self.as_ref().name.as_cstr().unwrap() } 48 | } 49 | 50 | pub fn long_name(&self) -> &CStr { 51 | unsafe { self.as_ref().long_name.as_cstr().unwrap() } 52 | } 53 | 54 | pub fn mime_types(&self) -> MimeTypeIter { 55 | unsafe { MimeTypeIter::from_ptr(self.as_ref().mime_types) } 56 | } 57 | 58 | pub fn profiles(&self) -> ProfileIter { 59 | unsafe { ProfileIter::from_ptr(self.as_ref().profiles) } 60 | } 61 | } 62 | 63 | impl Descriptor { 64 | pub unsafe fn from_ptr(descriptor: *const AVCodecDescriptor) -> Self { 65 | Descriptor { ptr: descriptor } 66 | } 67 | 68 | } 69 | 70 | impl fmt::Debug for Descriptor { 71 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 72 | f.debug_struct("CodecDescriptorRef") 73 | .field("id", &self.id()) 74 | .field("media_type", &self.media_type()) 75 | .field("name", &self.name()) 76 | .field("long_name", &self.long_name()) 77 | .field("profiles", &self.profiles().collect::>()) 78 | .finish() 79 | } 80 | } 81 | 82 | pub struct DescriptorIter { 83 | prev: *const AVCodecDescriptor 84 | } 85 | 86 | impl DescriptorIter { 87 | pub fn new() -> Self { 88 | ::LibAV::init(); 89 | DescriptorIter { prev: ptr::null() } 90 | } 91 | } 92 | 93 | impl Iterator for DescriptorIter { 94 | type Item = Descriptor; 95 | 96 | fn next(&mut self) -> Option { 97 | unsafe { 98 | let next = avcodec_descriptor_next(self.prev); 99 | if next.is_null() { 100 | None 101 | } else { 102 | self.prev = next; 103 | Some(Descriptor::from_ptr(next)) 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/codec/media_type.rs: -------------------------------------------------------------------------------- 1 | use ffi::AVMediaType; 2 | 3 | #[derive(Copy,Clone,Debug,PartialEq,Eq)] 4 | pub enum MediaType { 5 | /// Usually treated as `Data` 6 | Unknown, 7 | Video, 8 | Audio, 9 | /// Opaque data information usually continuous 10 | Data, 11 | Subtitle, 12 | /// Opaque data information usually sparse 13 | Attachment, 14 | NB, 15 | } 16 | 17 | impl MediaType { 18 | pub fn from_raw(kind: AVMediaType) -> Self { 19 | match kind { 20 | AVMediaType::AVMEDIA_TYPE_UNKNOWN => MediaType::Unknown, 21 | AVMediaType::AVMEDIA_TYPE_VIDEO => MediaType::Video, 22 | AVMediaType::AVMEDIA_TYPE_AUDIO => MediaType::Audio, 23 | AVMediaType::AVMEDIA_TYPE_DATA => MediaType::Data, 24 | AVMediaType::AVMEDIA_TYPE_SUBTITLE => MediaType::Subtitle, 25 | AVMediaType::AVMEDIA_TYPE_ATTACHMENT => MediaType::Attachment, 26 | AVMediaType::AVMEDIA_TYPE_NB => MediaType::NB, 27 | } 28 | } 29 | 30 | pub fn as_raw(self) -> AVMediaType { 31 | match self { 32 | MediaType::Unknown => AVMediaType::AVMEDIA_TYPE_UNKNOWN, 33 | MediaType::Video => AVMediaType::AVMEDIA_TYPE_VIDEO, 34 | MediaType::Audio => AVMediaType::AVMEDIA_TYPE_AUDIO, 35 | MediaType::Data => AVMediaType::AVMEDIA_TYPE_DATA, 36 | MediaType::Subtitle => AVMediaType::AVMEDIA_TYPE_SUBTITLE, 37 | MediaType::Attachment => AVMediaType::AVMEDIA_TYPE_ATTACHMENT, 38 | MediaType::NB => AVMediaType::AVMEDIA_TYPE_NB, 39 | } 40 | } 41 | 42 | pub fn is_unknown(self) -> bool { 43 | match self { 44 | MediaType::Unknown => true, 45 | _ => false, 46 | } 47 | } 48 | 49 | pub fn is_video(self) -> bool { 50 | match self { 51 | MediaType::Video => true, 52 | _ => false, 53 | } 54 | } 55 | 56 | pub fn is_audio(self) -> bool { 57 | match self { 58 | MediaType::Audio => true, 59 | _ => false, 60 | } 61 | } 62 | 63 | pub fn is_data(self) -> bool { 64 | match self { 65 | MediaType::Data => true, 66 | _ => false, 67 | } 68 | } 69 | 70 | pub fn is_subtitle(self) -> bool { 71 | match self { 72 | MediaType::Subtitle => true, 73 | _ => false, 74 | } 75 | } 76 | 77 | pub fn is_attachment(self) -> bool { 78 | match self { 79 | MediaType::Attachment => true, 80 | _ => false, 81 | } 82 | } 83 | 84 | pub fn is_nb(self) -> bool { 85 | match self { 86 | MediaType::NB => true, 87 | _ => false, 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/codec/mime.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::os::raw::c_char; 3 | 4 | pub struct MimeTypeIter { 5 | ptr: *const *const c_char 6 | } 7 | 8 | impl MimeTypeIter { 9 | pub unsafe fn from_ptr(ptr: *const *const c_char) -> Self { 10 | MimeTypeIter { ptr: ptr } 11 | } 12 | } 13 | 14 | impl Iterator for MimeTypeIter { 15 | type Item = &'static CStr; 16 | 17 | fn next(&mut self) -> Option { 18 | unsafe { 19 | if self.ptr.is_null() || (*self.ptr).is_null() { 20 | None 21 | } else { 22 | let next = self.ptr; 23 | self.ptr = self.ptr.offset(1); 24 | Some(CStr::from_ptr(*next)) 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/codec/mod.rs: -------------------------------------------------------------------------------- 1 | mod codec; 2 | pub use self::codec::{ 3 | Codec, 4 | AVCodecIDExt, 5 | }; 6 | 7 | mod descriptor; 8 | pub use self::descriptor::{ 9 | Descriptor, 10 | DescriptorIter, 11 | }; 12 | 13 | mod profile; 14 | pub use self::profile::{ 15 | Profile, 16 | ProfileIter, 17 | }; 18 | 19 | mod mime; 20 | pub use self::mime::MimeTypeIter; 21 | 22 | mod media_type; 23 | pub use self::media_type::MediaType; 24 | -------------------------------------------------------------------------------- /src/codec/profile.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::ffi::CStr; 3 | use ffi::{ 4 | AVProfile, 5 | FF_PROFILE_UNKNOWN, 6 | }; 7 | use util::AsCStr; 8 | 9 | pub struct Profile { 10 | ptr: *const AVProfile 11 | } 12 | 13 | impl Profile { 14 | pub fn name(&self) -> &CStr { 15 | unsafe { 16 | (*self.ptr).name.as_cstr().unwrap() 17 | } 18 | } 19 | } 20 | 21 | impl fmt::Debug for Profile { 22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 23 | f.debug_struct("Profile") 24 | .field("name", &self.name()) 25 | .finish() 26 | } 27 | } 28 | 29 | pub struct ProfileIter { 30 | next: *const AVProfile 31 | } 32 | 33 | impl ProfileIter { 34 | pub unsafe fn from_ptr(ptr: *const AVProfile) -> Self { 35 | ProfileIter { next: ptr } 36 | } 37 | } 38 | 39 | impl Iterator for ProfileIter { 40 | type Item = Profile; 41 | 42 | fn next(&mut self) -> Option { 43 | unsafe { 44 | if self.next.is_null() || (*self.next).profile == FF_PROFILE_UNKNOWN { 45 | None 46 | } else { 47 | let next = self.next; 48 | self.next = next.offset(1); 49 | Some(Profile { ptr: next }) 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/common/codec_parameters.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use ffi::{self, AVCodecParameters, AVStream}; 3 | use codec::MediaType; 4 | 5 | pub struct CodecParameters<'stream> { 6 | ptr: *mut AVCodecParameters, 7 | _phantom: PhantomData<&'stream AVStream>, 8 | } 9 | 10 | impl<'stream> CodecParameters<'stream> { 11 | pub unsafe fn from_ptr(ptr: *mut AVCodecParameters) -> CodecParameters<'stream> { 12 | CodecParameters { 13 | ptr: ptr, 14 | _phantom: PhantomData, 15 | } 16 | } 17 | 18 | pub fn media_type(&self) -> MediaType { 19 | MediaType::from_raw(self.as_ref().codec_type) 20 | } 21 | 22 | pub fn codec_id(&self) -> ffi::AVCodecID { 23 | self.as_ref().codec_id 24 | } 25 | 26 | // TODO: remaining fields 27 | } 28 | 29 | impl<'stream> CodecParameters<'stream> { 30 | pub fn as_ref(&self) -> &AVCodecParameters { 31 | unsafe { &*self.ptr } 32 | } 33 | pub fn as_mut(&self) -> &mut AVCodecParameters { 34 | unsafe { &mut *self.ptr } 35 | } 36 | pub fn as_ptr(&self) -> *const AVCodecParameters { 37 | self.ptr 38 | } 39 | pub fn as_mut_ptr(&mut self) -> *mut AVCodecParameters { 40 | self.ptr 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/common/encoder.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | use ffi::{ 3 | AVCodecContext, 4 | avcodec_open2, 5 | avcodec_free_context, 6 | AVFMT_GLOBALHEADER, 7 | AV_CODEC_FLAG_GLOBAL_HEADER, 8 | }; 9 | use format::OutputFormat; 10 | use codec::{ 11 | MediaType, 12 | Codec, 13 | }; 14 | use errors::*; 15 | 16 | pub unsafe fn init(codec_context: *mut AVCodecContext, format: OutputFormat) { 17 | // Some formats require global headers 18 | if 0 != (format.as_ref().flags & AVFMT_GLOBALHEADER as i32) { 19 | (*codec_context).flags |= AV_CODEC_FLAG_GLOBAL_HEADER as i32; 20 | } 21 | } 22 | 23 | pub unsafe fn open(mut codec_context: *mut AVCodecContext, kind: &'static str) -> Result<()> { 24 | let codec = (*codec_context).codec; 25 | let options = ptr::null_mut(); 26 | let res = avcodec_open2(codec_context, codec, options); 27 | if res < 0 { 28 | avcodec_free_context(&mut codec_context); 29 | bail!(ErrorKind::OpenEncoder(kind)); 30 | } 31 | 32 | Ok(()) 33 | } 34 | 35 | pub fn require_is_encoder(codec: Codec) -> Result<()> { 36 | if !codec.is_encoder() { 37 | Err(ErrorKind::EncodingUnsupported(codec.id()).into()) 38 | } else { 39 | Ok(()) 40 | } 41 | } 42 | 43 | pub fn require_codec_type(encoder_type: MediaType, codec: Codec) -> Result<()> { 44 | if encoder_type != codec.media_type() { 45 | Err(ErrorKind::MediaTypeMismatch(encoder_type, codec.id()).into()) 46 | } else { 47 | Ok(()) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/common/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod encoder; 2 | pub mod stream; 3 | pub mod codec_parameters; 4 | mod packet; 5 | pub mod ts; 6 | mod timebase; 7 | 8 | pub use self::packet::Packet; 9 | pub use self::ts::Ts; 10 | pub use self::timebase::Timebase; 11 | -------------------------------------------------------------------------------- /src/common/packet.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::slice; 3 | use std::mem; 4 | use ffi::{self, AVPacket}; 5 | use common::Timebase; 6 | 7 | /// A reference to a packet as returned 8 | /// e.g. by Demuxer::read_packet 9 | pub struct Packet<'buf> { 10 | ptr: *mut AVPacket, 11 | time_base: Timebase, 12 | _phantom: PhantomData<&'buf AVPacket>, 13 | } 14 | 15 | // See https://github.com/panicbit/rust-av/issues/28 16 | unsafe impl<'buf> Send for Packet<'buf> {} 17 | unsafe impl<'buf> Sync for Packet<'buf> {} 18 | 19 | impl<'buf> Packet<'buf> { 20 | pub unsafe fn from_ptr(ptr: *mut AVPacket, time_base: Timebase) -> Packet<'buf> { 21 | Packet { 22 | ptr: ptr, 23 | time_base: time_base, 24 | _phantom: PhantomData, 25 | } 26 | } 27 | 28 | pub fn stream_index(&self) -> usize { 29 | self.as_raw().stream_index as usize 30 | } 31 | 32 | pub fn as_slice(&self) -> &[u8] { 33 | unsafe { 34 | let packet = self.as_raw(); 35 | slice::from_raw_parts(packet.data, packet.size as usize) 36 | } 37 | } 38 | 39 | pub fn time_base(&self) -> Timebase { 40 | self.time_base 41 | } 42 | 43 | pub fn is_rc(&self) -> bool { 44 | !self.as_raw().buf.is_null() 45 | } 46 | 47 | pub fn into_rc(mut self) -> Packet<'static> { 48 | unsafe { 49 | // Replace internal packet with a refcounted copy 50 | if !self.is_rc() { 51 | let rc_packet = Self::ref_packet(self.ptr); 52 | ffi::av_packet_unref(self.ptr); 53 | self.ptr = rc_packet; 54 | } 55 | 56 | // Transmute self to fix the lifetime 57 | mem::transmute(self) 58 | } 59 | } 60 | } 61 | 62 | // Private helpers 63 | impl<'buf> Packet<'buf> { 64 | unsafe fn ref_packet(ptr: *const AVPacket) -> *mut AVPacket { 65 | let packet = ffi::av_packet_alloc(); 66 | if packet.is_null() { 67 | panic!("av_packet_alloc: out of memory!"); 68 | } 69 | 70 | // bump ref 71 | { 72 | let res = ffi::av_packet_ref(packet, ptr); 73 | if res < 0 { 74 | panic!("av_packet_ref: 0x{:X}", res); 75 | } 76 | } 77 | 78 | packet 79 | } 80 | 81 | unsafe fn copy_packet(ptr: *const AVPacket) -> *mut AVPacket { 82 | let packet = ffi::av_packet_alloc(); 83 | if packet.is_null() { 84 | panic!("av_packet_alloc: out of memory!"); 85 | } 86 | 87 | *packet = *ptr; 88 | 89 | packet 90 | } 91 | } 92 | 93 | impl<'buf> Clone for Packet<'buf> { 94 | fn clone(&self) -> Self { 95 | unsafe { 96 | let packet = if self.is_rc() { 97 | Self::ref_packet(self.ptr) 98 | } else { 99 | Self::copy_packet(self.ptr) 100 | }; 101 | 102 | Self::from_ptr(packet, self.time_base) 103 | } 104 | } 105 | } 106 | 107 | impl<'buf> Packet<'buf> { 108 | pub fn as_raw(&self) -> &AVPacket { 109 | unsafe { &*self.ptr } 110 | } 111 | pub fn as_ptr(&self) -> *const AVPacket { 112 | self.ptr 113 | } 114 | pub fn as_mut_ptr(&mut self) -> *mut AVPacket { 115 | self.ptr 116 | } 117 | } 118 | 119 | impl<'buf> Drop for Packet<'buf> { 120 | fn drop(&mut self) { 121 | unsafe { 122 | ffi::av_packet_unref(self.ptr); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/common/stream.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use ffi::{self, AVStream, AVFormatContext}; 3 | use std::slice; 4 | use common::codec_parameters::CodecParameters; 5 | use common::Timebase; 6 | 7 | pub struct Stream<'fmt_ctx> { 8 | ptr: *mut AVStream, 9 | _phantom: PhantomData<&'fmt_ctx AVFormatContext>, 10 | } 11 | 12 | impl<'fmt_ctx> Stream<'fmt_ctx> { 13 | pub unsafe fn from_ptr(ptr: *mut AVStream) -> Stream<'fmt_ctx> { 14 | Stream { 15 | ptr: ptr, 16 | _phantom: PhantomData, 17 | } 18 | } 19 | 20 | pub fn index(&self) -> usize { 21 | self.as_ref().index as usize 22 | } 23 | 24 | // TODO: investigate what the "Format-specific stream ID" is 25 | // pub fn id(&self) -> usize { 26 | // self.as_ref().id as usize 27 | // } 28 | 29 | pub fn time_base(&self) -> Timebase { 30 | self.as_ref().time_base.into() 31 | } 32 | 33 | // TODO: start_time 34 | 35 | pub fn duration(&self) -> usize { 36 | self.as_ref().duration as usize 37 | } 38 | 39 | /// Returns the number of frames if known 40 | pub fn num_frames(&self) -> Option { 41 | let num_frames = self.as_ref().nb_frames; 42 | 43 | if num_frames > 0 { 44 | Some(num_frames as usize) 45 | } else { 46 | None 47 | } 48 | } 49 | 50 | // TODO: disposition 51 | 52 | // TODO: remaining fields 53 | 54 | pub fn codec_parameters(&self) -> CodecParameters { 55 | unsafe { 56 | CodecParameters::from_ptr(self.as_ref().codecpar) 57 | } 58 | } 59 | } 60 | 61 | impl<'fmt_ctx> Stream<'fmt_ctx> { 62 | pub fn as_ref(&self) -> &AVStream { 63 | unsafe { &*self.ptr } 64 | } 65 | pub fn as_mut(&self) -> &mut AVStream { 66 | unsafe { &mut *self.ptr } 67 | } 68 | pub fn as_ptr(&self) -> *const AVStream { 69 | self.ptr 70 | } 71 | pub fn as_mut_ptr(&mut self) -> *mut AVStream { 72 | self.ptr 73 | } 74 | } 75 | 76 | pub struct Streams<'fmt_ctx> { 77 | iter: slice::Iter<'fmt_ctx, *mut AVStream>, 78 | 79 | } 80 | 81 | impl<'fmt_ctx> Streams<'fmt_ctx> { 82 | pub unsafe fn from_slice(slice: &'fmt_ctx [*mut AVStream]) -> Streams<'fmt_ctx> { 83 | Streams { 84 | iter: slice.iter() 85 | } 86 | } 87 | } 88 | 89 | impl<'fmt_ctx> Iterator for Streams<'fmt_ctx> { 90 | type Item = Stream<'fmt_ctx>; 91 | 92 | fn next(&mut self) -> Option { 93 | unsafe { 94 | self.iter.next().map(|&ptr| Stream::from_ptr(ptr)) 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/common/timebase.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::c_int; 2 | use ffi::AVRational; 3 | 4 | #[derive(Copy,Clone,Debug)] 5 | pub struct Timebase(AVRational); 6 | 7 | impl Timebase { 8 | pub fn new(num: c_int, den: c_int) -> Self{ 9 | Timebase(AVRational { 10 | num: num, 11 | den: den, 12 | }) 13 | } 14 | 15 | pub fn num(&self) -> c_int { 16 | self.0.num 17 | } 18 | 19 | pub fn den(&self) -> c_int { 20 | self.0.den 21 | } 22 | 23 | pub fn as_f32(&self) -> f32 { 24 | self.num() as f32 / self.den() as f32 25 | } 26 | 27 | pub fn as_f64(&self) -> f64 { 28 | self.num() as f64 / self.den() as f64 29 | } 30 | } 31 | 32 | impl From for Timebase { 33 | fn from(rat: AVRational) -> Self { 34 | Timebase(rat) 35 | } 36 | } 37 | 38 | impl From<(c_int, c_int)> for Timebase { 39 | fn from((num, den): (c_int, c_int)) -> Self { 40 | Timebase::new(num, den) 41 | } 42 | } 43 | 44 | impl From for Timebase { 45 | fn from(den: c_int) -> Self { 46 | Timebase::new(1, den) 47 | } 48 | } 49 | 50 | impl Into for Timebase { 51 | fn into(self) -> AVRational { 52 | self.0 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/common/ts.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | use std::ops; 3 | use std::time::Instant; 4 | use ffi::av_compare_ts; 5 | use common::Timebase; 6 | 7 | #[derive(Copy,Clone)] 8 | pub struct Ts { 9 | index: i64, 10 | time_base: Timebase, 11 | } 12 | 13 | impl Ts { 14 | pub fn new>(index: i64, time_base: TB) -> Self { 15 | Ts { 16 | index: index, 17 | time_base: time_base.into(), 18 | } 19 | } 20 | 21 | pub fn index(&self) -> i64 { 22 | self.index 23 | } 24 | 25 | pub fn time_base(&self) -> Timebase { 26 | self.time_base 27 | } 28 | 29 | pub fn calc_index_since(&mut self, stream_start: Instant) { 30 | let duration = Instant::now().duration_since(stream_start); 31 | let seconds = duration.as_secs(); 32 | let nanos = duration.subsec_nanos() as u64; 33 | let duration = seconds * 1_000 + nanos / 1_000_000; 34 | let index = duration as f64 * self.time_base.as_f64(); 35 | self.index = index.floor() as i64; 36 | } 37 | } 38 | 39 | impl cmp::PartialEq for Ts { 40 | fn eq(&self, other: &Ts) -> bool { 41 | unsafe { 42 | av_compare_ts(self.index, self.time_base.into(), other.index, other.time_base.into()) == 0 43 | } 44 | } 45 | } 46 | 47 | impl cmp::Eq for Ts {} 48 | 49 | impl cmp::PartialOrd for Ts { 50 | fn partial_cmp(&self, other: &Ts) -> Option { 51 | Some(self.cmp(other)) 52 | } 53 | } 54 | 55 | impl cmp::Ord for Ts { 56 | fn cmp(&self, other: &Ts) -> cmp::Ordering { 57 | unsafe { 58 | use std::cmp::Ordering::*; 59 | match av_compare_ts(self.index, self.time_base.into(), other.index, other.time_base.into()) { 60 | -1 => Less, 61 | 0 => Equal, 62 | 1 => Greater, 63 | _ => unreachable!("av_compare_ts BUG") 64 | } 65 | } 66 | } 67 | } 68 | 69 | impl ops::AddAssign for Ts { 70 | fn add_assign(&mut self, rhs: i64) { 71 | self.index += rhs; 72 | } 73 | } 74 | 75 | #[cfg(test)] 76 | mod test { 77 | use super::Ts; 78 | 79 | #[test] 80 | fn index_since_instant() { 81 | use std::thread::sleep; 82 | use std::time::{Instant, Duration}; 83 | 84 | let fps = 30; 85 | let seconds = 1; 86 | let num_frames = seconds * fps; 87 | let mut ts = Ts::new(0, fps); 88 | let stream_start = Instant::now(); 89 | 90 | for expected_index in 0..num_frames { 91 | ts.calc_index_since(stream_start); 92 | let distance = ts.index() - expected_index as i64; 93 | println!("Expected index: {}, got: {}, distance: {}", expected_index, ts.index(), distance); 94 | assert!(expected_index as i64 <= ts.index()); 95 | 96 | sleep(Duration::from_millis(1000 / fps as u64)); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | use ffi::AVCodecID; 2 | use codec::MediaType; 3 | 4 | error_chain! { 5 | errors { 6 | EncoderNotFound(name: String) { 7 | description("Could not find suitable encoder") 8 | display("Could not find encoder for {}", name) 9 | } 10 | 11 | DecoderNotFound(name: String) { 12 | description("Could not find suitable decoder") 13 | display("Could not find decoder for {}", name) 14 | } 15 | 16 | OpenEncoder(kind: &'static str) { 17 | description("Could not open encoder") 18 | display("Could not open {} encoder", kind) 19 | } 20 | 21 | OpenDecoder(kind: &'static str) { 22 | description("Could not open decoder") 23 | display("Could not open {} decoder", kind) 24 | } 25 | 26 | CopyCodecParameters 27 | 28 | AllocFailed(of: &'static str) { 29 | description("Failed to allocate something (OOM?)") 30 | display("Failed to allocate {} (OOM?)", of) 31 | } 32 | 33 | EncodingUnsupported(codec_id: AVCodecID) { 34 | description("Codec does not support encoding") 35 | display("{:?} codec does not support encoding", codec_id) 36 | } 37 | 38 | MediaTypeMismatch(encoder_type: MediaType, codec_id: AVCodecID) { 39 | description("Encoder and codec media types mismatch") 40 | display("Cannot encode/decode {:?} using {:?} encoder/decoder", codec_id, encoder_type) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/format/demuxer.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | use std::fmt; 3 | use std::ffi::CStr; 4 | use LibAV; 5 | use io; 6 | use ffi; 7 | use ffi::{ 8 | AVFormatContext, 9 | AVInputFormat, 10 | AV_TIME_BASE, 11 | }; 12 | use util::AsCStr; 13 | use errors::*; 14 | use common::stream::Streams; 15 | use common::Packet; 16 | use std::slice; 17 | 18 | pub struct Demuxer { 19 | ptr: *mut AVFormatContext, 20 | // The io context is borrowed by the format context 21 | // and is kept around to be dropped at the right time. 22 | _io_context: io::IOContext, 23 | } 24 | 25 | impl Demuxer { 26 | pub fn open(reader: W) -> Result { 27 | unsafe { 28 | LibAV::init(); 29 | 30 | // Allocate IOContext and AVFormatContext 31 | let mut io_context = io::IOContext::from_reader(reader); 32 | let mut format_context = ffi::avformat_alloc_context(); 33 | 34 | if format_context.is_null() { 35 | bail!("Failed to allocate input context"); 36 | } 37 | 38 | // Lend the io context to the format context 39 | (*format_context).pb = io_context.as_mut_ptr(); 40 | 41 | // Open the demuxer 42 | { 43 | let url = ptr::null_mut(); 44 | let format = ptr::null_mut(); 45 | let options = ptr::null_mut(); 46 | let res = ffi::avformat_open_input(&mut format_context, url, format, options); 47 | 48 | if res < 0 { 49 | // No need to fre format_context here. 50 | // avformat_open_input already has freed the format context at this point. 51 | bail!("Failed to open input context"); 52 | } 53 | } 54 | 55 | // Decode some stream info 56 | { 57 | let options = ptr::null_mut(); 58 | let res = ffi::avformat_find_stream_info(format_context, options); 59 | 60 | if res < 0 { 61 | ffi::avformat_close_input(&mut format_context); 62 | bail!("Failed to find stream info"); 63 | } 64 | } 65 | 66 | Ok(Demuxer { 67 | ptr: format_context, 68 | _io_context: io_context, 69 | }) 70 | } 71 | } 72 | 73 | pub fn streams(&self) -> Streams { 74 | unsafe { 75 | let streams = slice::from_raw_parts(self.as_ref().streams, self.num_streams()); 76 | Streams::from_slice(streams) 77 | } 78 | } 79 | 80 | pub fn num_streams(&self) -> usize { 81 | unsafe { (*self.ptr).nb_streams as usize } 82 | } 83 | 84 | /// Duration in seconds (floored) 85 | /// TODO: Return a more exact/fexible representation 86 | pub fn duration(&self) -> u32 { 87 | let duration = unsafe { (*self.ptr).duration }; 88 | if duration <= 0 { 89 | return 0; 90 | } else { 91 | duration as u32 / AV_TIME_BASE 92 | } 93 | } 94 | 95 | pub fn format_name(&self) -> &CStr { 96 | unsafe { 97 | self.input_format().name.as_cstr().unwrap() 98 | } 99 | } 100 | 101 | pub fn format_long_name(&self) -> &CStr { 102 | unsafe { 103 | self.input_format().long_name.as_cstr().unwrap() 104 | } 105 | } 106 | 107 | pub fn dump_info(&self) { 108 | unsafe { 109 | let stream_index = 0; 110 | let url = ptr::null(); 111 | let is_output = 0; 112 | ffi::av_dump_format(self.as_ptr() as _, stream_index, url, is_output); 113 | } 114 | } 115 | 116 | pub fn read_packet(&mut self) -> Result> { 117 | unsafe { 118 | let mut packet = ffi::av_packet_alloc(); 119 | if packet.is_null() { 120 | bail!(ErrorKind::AllocFailed("demuxing packet")); 121 | } 122 | 123 | // Try to read a packet 124 | { 125 | let res = ffi::av_read_frame(self.ptr, packet); 126 | if res < 0 { 127 | ffi::av_packet_free(&mut packet); 128 | 129 | match res { 130 | ffi::AVERROR_EOF => return Ok(None), 131 | _ => bail!("Demuxer failed to read packet"), 132 | } 133 | } 134 | } 135 | 136 | let time_base = self.streams().nth((*packet).stream_index as usize).map(|stream| stream.time_base()) 137 | .ok_or("Demuxed packet has invalid stream index")?; 138 | 139 | Ok(Some(Packet::from_ptr(packet, time_base.into()))) 140 | } 141 | } 142 | } 143 | 144 | impl Demuxer { 145 | pub fn as_ref(&self) -> &AVFormatContext { 146 | unsafe { &*self.ptr } 147 | } 148 | pub fn as_mut(&self) -> &mut AVFormatContext { 149 | unsafe { &mut *self.ptr } 150 | } 151 | pub fn as_ptr(&self) -> *const AVFormatContext { 152 | self.ptr 153 | } 154 | pub fn as_mut_ptr(&mut self) -> *mut AVFormatContext { 155 | self.ptr 156 | } 157 | unsafe fn input_format(&self) -> &AVInputFormat { 158 | &*self.as_ref().iformat 159 | } 160 | // pub fn encoders_mut(&mut self) -> &mut [Encoder] { 161 | // &mut self.encoders 162 | // } 163 | } 164 | 165 | impl Drop for Demuxer { 166 | fn drop(&mut self) { 167 | unsafe { 168 | ffi::avformat_close_input(&mut self.ptr) 169 | } 170 | } 171 | } 172 | 173 | impl fmt::Debug for Demuxer { 174 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 175 | f.debug_struct("Demuxer") 176 | .field("num_streams", &self.num_streams()) 177 | .field("duration", &format!("{} seconds", self.duration())) 178 | .field("format_name", &self.format_name()) 179 | .field("format_long_name", &self.format_long_name()) 180 | .finish() 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/format/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | mod muxer; 3 | pub use self::muxer::{ 4 | Muxer, 5 | MuxerBuilder, 6 | }; 7 | 8 | mod demuxer; 9 | pub use self::demuxer::{ 10 | Demuxer, 11 | }; 12 | 13 | mod output_format; 14 | pub use self::output_format::OutputFormat; 15 | -------------------------------------------------------------------------------- /src/format/muxer.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | use std::mem; 3 | use std::fmt; 4 | use std::ffi::{CStr,CString}; 5 | use std::os::raw::c_uint; 6 | use LibAV; 7 | use io; 8 | use ffi; 9 | use ffi::{ 10 | AVFormatContext, 11 | AVOutputFormat, 12 | AVPacket, 13 | AVStream, 14 | AV_TIME_BASE, 15 | AVFMT_GLOBALHEADER, 16 | AV_CODEC_FLAG_GLOBAL_HEADER, 17 | AV_CODEC_CAP_DELAY, 18 | AVERROR_EAGAIN, 19 | AVERROR_EOF, 20 | }; 21 | use generic::{ 22 | Encoder, 23 | RefMutFrame, 24 | Packets, 25 | }; 26 | use format::OutputFormat; 27 | use util::AsCStr; 28 | use common::Packet; 29 | use errors::*; 30 | 31 | pub struct Muxer { 32 | ptr: *mut AVFormatContext, 33 | // The io context is borrowed by the format context 34 | // and is kept around to be dropped at the right time. 35 | _io_context: io::IOContext, 36 | // Whether muxer was closed explicitly 37 | closed: bool, 38 | } 39 | 40 | unsafe impl Send for Muxer{} 41 | unsafe impl Sync for Muxer{} 42 | 43 | impl Muxer { 44 | pub fn new(format: OutputFormat, writer: W) -> Result { 45 | MuxerBuilder::new(format, writer) 46 | } 47 | 48 | pub fn num_streams(&self) -> usize { 49 | unsafe { (*self.ptr).nb_streams as usize } 50 | } 51 | 52 | /// Duration in seconds (floored) 53 | /// TODO: Return a more exact/fexible representation 54 | pub fn duration(&self) -> u32 { 55 | let duration = unsafe { (*self.ptr).duration }; 56 | if duration <= 0 { 57 | return 0; 58 | } else { 59 | duration as u32 / AV_TIME_BASE 60 | } 61 | } 62 | 63 | pub fn format_name(&self) -> &CStr { 64 | unsafe { 65 | self.output_format().name.as_cstr().unwrap() 66 | } 67 | } 68 | 69 | pub fn format_long_name(&self) -> &CStr { 70 | unsafe { 71 | self.output_format().long_name.as_cstr().unwrap() 72 | } 73 | } 74 | 75 | pub fn dump_info(&self) { 76 | unsafe { 77 | let stream_index = 0; 78 | let url = ptr::null(); 79 | let is_output = 1; 80 | ffi::av_dump_format(self.as_ptr() as _, stream_index, url, is_output); 81 | } 82 | } 83 | 84 | pub fn mux(&mut self, mut packet: Packet, stream_index: usize) -> Result<()> { 85 | unsafe { 86 | if stream_index >= self.num_streams() { 87 | bail!("Invalid stream index {}. Only {} stream(s) exist(s).", stream_index, self.num_streams()); 88 | } 89 | 90 | let packet_time_base = packet.time_base(); 91 | let packet = &mut *packet.as_mut_ptr(); 92 | let stream = *self.as_ref().streams.offset(stream_index as isize); 93 | let stream_time_base = (*stream).time_base; 94 | ffi::av_packet_rescale_ts(packet, packet_time_base.into(), stream_time_base); 95 | packet.stream_index = stream_index as i32; 96 | 97 | // // TODO: log_packet(muxer.as_mut_ptr(), packet); 98 | 99 | let res = ffi::av_interleaved_write_frame(self.ptr, packet); 100 | if res < 0 { 101 | bail!("Failed to write packet for stream {}: 0x{:X}", stream_index, res); 102 | } 103 | 104 | Ok(()) 105 | } 106 | } 107 | 108 | pub fn mux_all<'a, P: Into>>(&mut self, packets: P, stream_index: usize) -> Result<()> { 109 | for packet in packets.into() { 110 | self.mux(packet?, stream_index)?; 111 | } 112 | Ok(()) 113 | } 114 | 115 | pub fn close(mut self) -> Result<()> { 116 | self.closed = true; 117 | self._real_close() 118 | } 119 | 120 | fn _real_close(&mut self) -> Result<()> { 121 | unsafe { 122 | // Write trailer 123 | { 124 | let res = ffi::av_write_trailer(self.as_mut_ptr()); 125 | if res < 0 { 126 | bail!("Failed to write trailer: 0x{:X}", res); 127 | } 128 | } 129 | 130 | Ok(()) 131 | } 132 | } 133 | } 134 | 135 | 136 | impl Muxer { 137 | pub fn as_ref(&self) -> &AVFormatContext { 138 | unsafe { &*self.ptr } 139 | } 140 | pub fn as_mut(&self) -> &mut AVFormatContext { 141 | unsafe { &mut *self.ptr } 142 | } 143 | pub fn as_ptr(&self) -> *const AVFormatContext { 144 | self.ptr 145 | } 146 | pub fn as_mut_ptr(&mut self) -> *mut AVFormatContext { 147 | self.ptr 148 | } 149 | unsafe fn output_format(&self) -> &AVOutputFormat { 150 | &*self.as_ref().oformat 151 | } 152 | } 153 | 154 | impl Drop for Muxer { 155 | fn drop(&mut self) { 156 | unsafe { 157 | if !self.closed { 158 | self._real_close().ok(); 159 | } 160 | ffi::avformat_free_context(self.ptr) 161 | // The associated io context will be implicitly dropped here. 162 | // It may not be dropped before the format context because 163 | // `avformat_free_context` might still write some data. 164 | } 165 | } 166 | } 167 | 168 | impl fmt::Debug for Muxer { 169 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 170 | f.debug_struct("Muxer") 171 | .field("num_streams", &self.num_streams()) 172 | .field("duration", &format!("{} seconds", self.duration())) 173 | .field("format_name", &self.format_name()) 174 | .field("format_long_name", &self.format_long_name()) 175 | .finish() 176 | } 177 | } 178 | 179 | pub struct MuxerBuilder { 180 | ptr: *mut AVFormatContext, 181 | io_context: Option, 182 | } 183 | 184 | impl MuxerBuilder { 185 | pub fn new(mut format: OutputFormat, writer: W) -> Result { 186 | unsafe { 187 | LibAV::init(); 188 | 189 | let mut muxer = ptr::null_mut(); 190 | let mut io_context = io::IOContext::from_writer(writer); 191 | 192 | // Allocate muxer 193 | { 194 | let format_name = ptr::null(); 195 | let name = ptr::null(); 196 | 197 | let res = ffi::avformat_alloc_output_context2(&mut muxer, format.as_mut_ptr(), format_name, name); 198 | 199 | if res < 0 || muxer.is_null() { 200 | bail!("Failed to allocate output context: 0x{:X}", res); 201 | } 202 | } 203 | 204 | // lend the io context to the format context 205 | (*muxer).pb = io_context.as_mut_ptr(); 206 | 207 | Ok(MuxerBuilder { 208 | ptr: muxer, 209 | io_context: Some(io_context), 210 | }) 211 | } 212 | } 213 | 214 | /// Add a new stream using the settings from an encoder. 215 | pub fn add_stream_from_encoder>(&mut self, encoder: E) -> Result<()> { 216 | unsafe { 217 | // Verify that encoder has global header flag set if required 218 | { 219 | let format_flags = (*(*self.ptr).oformat).flags as c_uint; 220 | let encoder_flags = encoder.as_ref().flags; 221 | 222 | if 0 != (format_flags & AVFMT_GLOBALHEADER) 223 | && 0 == (encoder_flags & AV_CODEC_FLAG_GLOBAL_HEADER as i32) 224 | { 225 | bail!("Format requires global headers but encoder does not use global headers") 226 | } 227 | } 228 | 229 | // Create stream context 230 | let stream = ffi::avformat_new_stream(self.ptr, encoder.as_ref().codec); 231 | if stream.is_null() { 232 | bail!("Could not allocate stream") 233 | } 234 | 235 | (*stream).id = (*self.ptr).nb_streams as i32 - 1; 236 | (*stream).time_base = encoder.as_ref().time_base; 237 | 238 | // Copy encoder parameters to stream 239 | // NOTE: Not advised for remuxing: https://ffmpeg.org/doxygen/3.2/group__lavf__encoding.html#details 240 | { 241 | let res = ffi::avcodec_parameters_from_context((*stream).codecpar, encoder.as_ref()); 242 | if res < 0 { 243 | bail!("Could not copy stream parameters ({})", res) 244 | } 245 | } 246 | 247 | Ok(()) 248 | } 249 | } 250 | 251 | pub fn open(mut self) -> Result { 252 | unsafe { 253 | // Write header 254 | { 255 | let options = ptr::null_mut(); 256 | let res = ffi::avformat_write_header(self.ptr, options); 257 | if res < 0 { 258 | ffi::avformat_free_context(self.ptr); 259 | bail!("Could not write header: 0x{:X}", res); 260 | } 261 | } 262 | 263 | Ok(Muxer { 264 | ptr: mem::replace(&mut self.ptr, ptr::null_mut()), 265 | _io_context: self.io_context.take().unwrap(), 266 | closed: false, 267 | }) 268 | } 269 | } 270 | 271 | } 272 | 273 | impl Drop for MuxerBuilder { 274 | fn drop(&mut self) { 275 | unsafe { 276 | if !self.ptr.is_null() { 277 | ffi::avformat_free_context(self.ptr); 278 | } 279 | } 280 | } 281 | } 282 | 283 | unsafe impl Send for MuxerBuilder {} 284 | unsafe impl Sync for MuxerBuilder {} 285 | -------------------------------------------------------------------------------- /src/format/output_format.rs: -------------------------------------------------------------------------------- 1 | use LibAV; 2 | use std::ffi::CString; 3 | use std::ptr; 4 | use std::fmt; 5 | use ffi::{ 6 | AVOutputFormat, 7 | av_guess_format, 8 | }; 9 | use util::AsCStr; 10 | 11 | #[derive(Copy,Clone)] 12 | pub struct OutputFormat { 13 | ptr: *mut AVOutputFormat 14 | } 15 | 16 | impl OutputFormat { 17 | /// Get format from short name like `mp4`, `avi`, `ogg` etc. 18 | pub fn from_name(name: &str) -> Option { 19 | unsafe { 20 | LibAV::init(); 21 | let name = CString::new(name).unwrap(); 22 | let format = av_guess_format(name.as_ptr(), ptr::null(), ptr::null()); 23 | if format.is_null() { 24 | None 25 | } else { 26 | Some(OutputFormat { ptr: format }) 27 | } 28 | } 29 | } 30 | 31 | /// Get format from filename extension 32 | pub fn from_filename(filename: &str) -> Option { 33 | unsafe { 34 | LibAV::init(); 35 | let filename = CString::new(filename).unwrap(); 36 | let format = av_guess_format(ptr::null(), filename.as_ptr(), ptr::null()); 37 | if format.is_null() { 38 | None 39 | } else { 40 | Some(OutputFormat { ptr: format }) 41 | } 42 | } 43 | } 44 | 45 | // TODO: implement `guess_from_mime` 46 | } 47 | 48 | impl OutputFormat { 49 | pub fn as_ptr(&self) -> *const AVOutputFormat { self.ptr } 50 | pub fn as_mut_ptr(&mut self) -> *mut AVOutputFormat { self.ptr } 51 | pub fn as_ref(&self) -> &AVOutputFormat { unsafe { &*self.ptr } } 52 | } 53 | 54 | impl fmt::Debug for OutputFormat { 55 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 56 | unsafe { 57 | f.debug_struct("OutputFormat") 58 | .field("name", &self.as_ref().name.as_cstr().unwrap()) 59 | .field("long_name", &self.as_ref().long_name.as_cstr().unwrap()) 60 | .field("mime_type", &self.as_ref().mime_type.as_cstr()) // optional 61 | .field("extensions", &self.as_ref().extensions.as_cstr().unwrap()) 62 | .finish() 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/generic/decoder.rs: -------------------------------------------------------------------------------- 1 | use codec::Codec; 2 | use generic::RefMutFrame; 3 | use ffi::{ 4 | AVCodecContext, 5 | AVPacket, 6 | }; 7 | use video; 8 | use audio; 9 | use errors::*; 10 | use common::stream::Stream; 11 | use common::{Packet, Timebase}; 12 | use codec::MediaType; 13 | use super::Frame; 14 | 15 | pub enum Decoder { 16 | Video(video::Decoder), 17 | Audio(audio::Decoder), 18 | } 19 | 20 | impl Decoder { 21 | pub fn from_stream(stream: &Stream) -> Result { 22 | Ok(match stream.codec_parameters().media_type() { 23 | MediaType::Video => video::Decoder::from_stream(stream)?.into(), 24 | MediaType::Audio => audio::Decoder::from_stream(stream)?.into(), 25 | other => bail!("Unsupported media type: {:?}", other) 26 | 27 | }) 28 | } 29 | 30 | pub fn into_video_encoder(self) -> Option { 31 | match self { 32 | Decoder::Video(decoder) => Some(decoder), 33 | _ => None 34 | } 35 | } 36 | 37 | pub fn as_video_encoder(&self) -> Option<&video::Decoder> { 38 | match *self { 39 | Decoder::Video(ref decoder) => Some(decoder), 40 | _ => None 41 | } 42 | } 43 | 44 | pub fn as_mut_video_encoder(&mut self) -> Option<&mut video::Decoder> { 45 | match *self { 46 | Decoder::Video(ref mut decoder) => Some(decoder), 47 | _ => None 48 | } 49 | } 50 | 51 | pub fn into_audio_encoder(self) -> Option { 52 | match self { 53 | Decoder::Audio(decoder) => Some(decoder), 54 | _ => None 55 | } 56 | } 57 | 58 | pub fn as_audio_encoder(&self) -> Option<&audio::Decoder> { 59 | match *self { 60 | Decoder::Audio(ref decoder) => Some(decoder), 61 | _ => None 62 | } 63 | } 64 | 65 | pub fn as_mut_audio_encoder(&mut self) -> Option<&mut audio::Decoder> { 66 | match *self { 67 | Decoder::Audio(ref mut decoder) => Some(decoder), 68 | _ => None 69 | } 70 | } 71 | 72 | pub fn codec(&self) -> Codec { 73 | match *self { 74 | Decoder::Video(ref decoder) => decoder.codec(), 75 | Decoder::Audio(ref decoder) => decoder.codec(), 76 | } 77 | } 78 | 79 | pub fn time_base(&self) -> Timebase { 80 | match *self { 81 | Decoder::Video(ref decoder) => decoder.time_base(), 82 | Decoder::Audio(ref decoder) => decoder.time_base(), 83 | } 84 | } 85 | } 86 | 87 | impl Decoder { 88 | pub fn as_mut_ptr(&mut self) -> *mut AVCodecContext { 89 | match *self { 90 | Decoder::Video(ref mut decoder) => decoder.as_mut_ptr(), 91 | Decoder::Audio(ref mut decoder) => decoder.as_mut_ptr(), 92 | } 93 | } 94 | 95 | pub fn as_ref(&self) -> &AVCodecContext { 96 | match *self { 97 | Decoder::Video(ref decoder) => decoder.as_ref(), 98 | Decoder::Audio(ref decoder) => decoder.as_ref(), 99 | } 100 | } 101 | 102 | pub fn as_mut(&mut self) -> &mut AVCodecContext { 103 | match *self { 104 | Decoder::Video(ref mut decoder) => decoder.as_mut(), 105 | Decoder::Audio(ref mut decoder) => decoder.as_mut(), 106 | } 107 | } 108 | 109 | pub fn decode<'decoder>(&'decoder mut self, packet: Packet) -> Result> { 110 | match *self { 111 | Decoder::Video(ref mut decoder) => decoder.decode(packet).map(Frames::from), 112 | Decoder::Audio(ref mut decoder) => decoder.decode(packet).map(Frames::from), 113 | } 114 | } 115 | 116 | pub fn flush<'decoder>(&'decoder mut self) -> Result> { 117 | match *self { 118 | Decoder::Video(ref mut decoder) => decoder.flush().map(Frames::from), 119 | Decoder::Audio(ref mut decoder) => decoder.flush().map(Frames::from), 120 | } 121 | } 122 | } 123 | 124 | impl From for Decoder { 125 | fn from(decoder: video::Decoder) -> Self { 126 | Decoder::Video(decoder) 127 | } 128 | } 129 | 130 | impl From for Decoder { 131 | fn from(decoder: audio::Decoder) -> Self { 132 | Decoder::Audio(decoder) 133 | } 134 | } 135 | 136 | pub enum Frames<'decoder> { 137 | Video(video::Frames<'decoder>), 138 | Audio(audio::Frames<'decoder>), 139 | } 140 | 141 | impl<'decoder> Iterator for Frames<'decoder> { 142 | type Item = Result; 143 | 144 | fn next(&mut self) -> Option { 145 | match *self { 146 | Frames::Video(ref mut frames) => frames.next().map(|res| res.map(Frame::from)), 147 | Frames::Audio(ref mut frames) => frames.next().map(|res| res.map(Frame::from)), 148 | } 149 | } 150 | } 151 | 152 | impl<'decoder> From> for Frames<'decoder> { 153 | fn from(frames: video::Frames<'decoder>) -> Self { 154 | Frames::Video(frames) 155 | } 156 | } 157 | 158 | impl<'decoder> From> for Frames<'decoder> { 159 | fn from(frames: audio::Frames<'decoder>) -> Self { 160 | Frames::Audio(frames) 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/generic/encoder.rs: -------------------------------------------------------------------------------- 1 | use codec::Codec; 2 | use generic::RefMutFrame; 3 | use ffi::{ 4 | AVCodecContext, 5 | AVPacket, 6 | }; 7 | use video; 8 | use audio; 9 | use errors::*; 10 | use common::{Packet, Timebase}; 11 | 12 | pub enum Encoder { 13 | Video(video::Encoder), 14 | Audio(audio::Encoder), 15 | } 16 | 17 | impl Encoder { 18 | pub fn into_video_encoder(self) -> Option { 19 | match self { 20 | Encoder::Video(encoder) => Some(encoder), 21 | _ => None 22 | } 23 | } 24 | 25 | pub fn as_video_encoder(&self) -> Option<&video::Encoder> { 26 | match *self { 27 | Encoder::Video(ref encoder) => Some(encoder), 28 | _ => None 29 | } 30 | } 31 | 32 | pub fn as_mut_video_encoder(&mut self) -> Option<&mut video::Encoder> { 33 | match *self { 34 | Encoder::Video(ref mut encoder) => Some(encoder), 35 | _ => None 36 | } 37 | } 38 | 39 | pub fn into_audio_encoder(self) -> Option { 40 | match self { 41 | Encoder::Audio(encoder) => Some(encoder), 42 | _ => None 43 | } 44 | } 45 | 46 | pub fn as_audio_encoder(&self) -> Option<&audio::Encoder> { 47 | match *self { 48 | Encoder::Audio(ref encoder) => Some(encoder), 49 | _ => None 50 | } 51 | } 52 | 53 | pub fn as_mut_audio_encoder(&mut self) -> Option<&mut audio::Encoder> { 54 | match *self { 55 | Encoder::Audio(ref mut encoder) => Some(encoder), 56 | _ => None 57 | } 58 | } 59 | 60 | pub fn codec(&self) -> Codec { 61 | match *self { 62 | Encoder::Video(ref encoder) => encoder.codec(), 63 | Encoder::Audio(ref encoder) => encoder.codec(), 64 | } 65 | } 66 | 67 | pub fn time_base(&self) -> Timebase { 68 | match *self { 69 | Encoder::Video(ref encoder) => encoder.time_base(), 70 | Encoder::Audio(ref encoder) => encoder.time_base(), 71 | } 72 | } 73 | 74 | pub fn encode<'a, F>(&mut self, frame: F) -> Result where 75 | F: Into>, 76 | { 77 | match *self { 78 | Encoder::Video(ref mut encoder) => encoder.encode(frame).map(Packets::from), 79 | Encoder::Audio(ref mut encoder) => encoder.encode(frame).map(Packets::from), 80 | } 81 | } 82 | 83 | pub fn flush<'a>(self) -> Result> { 84 | match self { 85 | Encoder::Video(encoder) => encoder.flush().map(Packets::from), 86 | Encoder::Audio(encoder) => encoder.flush().map(Packets::from), 87 | } 88 | } 89 | } 90 | 91 | impl Encoder { 92 | pub fn as_ptr(&self) -> *const AVCodecContext { 93 | match *self { 94 | Encoder::Video(ref encoder) => encoder.as_ptr(), 95 | Encoder::Audio(ref encoder) => encoder.as_ptr(), 96 | } 97 | } 98 | 99 | pub fn as_mut_ptr(&mut self) -> *mut AVCodecContext { 100 | match *self { 101 | Encoder::Video(ref mut encoder) => encoder.as_mut_ptr(), 102 | Encoder::Audio(ref mut encoder) => encoder.as_mut_ptr(), 103 | } 104 | } 105 | 106 | pub fn as_mut(&mut self) -> &mut AVCodecContext { 107 | match *self { 108 | Encoder::Video(ref mut encoder) => encoder.as_mut(), 109 | Encoder::Audio(ref mut encoder) => encoder.as_mut(), 110 | } 111 | } 112 | } 113 | 114 | impl AsRef for Encoder { 115 | fn as_ref(&self) -> &AVCodecContext { 116 | match *self { 117 | Encoder::Video(ref encoder) => encoder.as_ref(), 118 | Encoder::Audio(ref encoder) => encoder.as_ref(), 119 | } 120 | } 121 | } 122 | 123 | impl From for Encoder { 124 | fn from(encoder: video::Encoder) -> Self { 125 | Encoder::Video(encoder) 126 | } 127 | } 128 | 129 | impl From for Encoder { 130 | fn from(encoder: audio::Encoder) -> Self { 131 | Encoder::Audio(encoder) 132 | } 133 | } 134 | 135 | pub enum Packets<'encoder> { 136 | Video(video::Packets<'encoder>), 137 | Audio(audio::Packets<'encoder>), 138 | } 139 | 140 | impl<'encoder> Iterator for Packets<'encoder> { 141 | type Item = Result>; 142 | 143 | fn next(&mut self) -> Option { 144 | match *self { 145 | Packets::Video(ref mut packets) => packets.next(), 146 | Packets::Audio(ref mut packets) => packets.next(), 147 | } 148 | } 149 | } 150 | 151 | impl<'encoder> From> for Packets<'encoder> { 152 | fn from(packets: video::Packets<'encoder>) -> Self { 153 | Packets::Video(packets) 154 | } 155 | } 156 | 157 | impl<'decoder> From> for Packets<'decoder> { 158 | fn from(packets: audio::Packets<'decoder>) -> Self { 159 | Packets::Audio(packets) 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/generic/frame.rs: -------------------------------------------------------------------------------- 1 | use video; 2 | use audio; 3 | use errors::*; 4 | 5 | pub enum Frame { 6 | Video(video::Frame), 7 | Audio(audio::Frame), 8 | } 9 | 10 | impl Frame { 11 | pub fn into_video_frame(self) -> Option { 12 | match self { 13 | Frame::Video(frame) => Some(frame), 14 | _ => None, 15 | } 16 | } 17 | 18 | pub fn into_audio_frame(self) -> Option { 19 | match self { 20 | Frame::Audio(frame) => Some(frame), 21 | _ => None, 22 | } 23 | } 24 | 25 | pub fn as_mut_video_frame(&mut self) -> Option<&mut video::Frame> { 26 | match *self { 27 | Frame::Video(ref mut frame) => Some(frame), 28 | _ => None, 29 | } 30 | } 31 | 32 | pub fn as_mut_audio_frame(&mut self) -> Option<&mut audio::Frame> { 33 | match *self { 34 | Frame::Audio(ref mut frame) => Some(frame), 35 | _ => None, 36 | } 37 | } 38 | } 39 | 40 | impl From for Frame { 41 | fn from(frame: video::Frame) -> Self { 42 | Frame::Video(frame) 43 | } 44 | } 45 | 46 | impl From for Frame { 47 | fn from(frame: audio::Frame) -> Self { 48 | Frame::Audio(frame) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/generic/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | mod encoder; 3 | pub use self::encoder::{Encoder,Packets}; 4 | 5 | mod decoder; 6 | pub use self::decoder::{Decoder,Frames}; 7 | 8 | mod ref_mut_frame; 9 | pub use self::ref_mut_frame::RefMutFrame; 10 | 11 | mod frame; 12 | pub use self::frame::Frame; 13 | -------------------------------------------------------------------------------- /src/generic/ref_mut_frame.rs: -------------------------------------------------------------------------------- 1 | use video; 2 | use audio; 3 | 4 | pub enum RefMutFrame<'a> { 5 | Video(&'a mut video::Frame), 6 | Audio(&'a mut audio::Frame), 7 | } 8 | 9 | impl<'a> RefMutFrame<'a> { 10 | pub fn into_video_frame(self) -> Option<&'a mut video::Frame> { 11 | match self { 12 | RefMutFrame::Video(frame) => Some(frame), 13 | _ => None, 14 | } 15 | } 16 | 17 | pub fn into_audio_frame(self) -> Option<&'a mut audio::Frame> { 18 | match self { 19 | RefMutFrame::Audio(frame) => Some(frame), 20 | _ => None, 21 | } 22 | } 23 | } 24 | 25 | impl<'a> From<&'a mut video::Frame> for RefMutFrame<'a> { 26 | fn from(frame: &'a mut video::Frame) -> Self { 27 | RefMutFrame::Video(frame) 28 | } 29 | } 30 | 31 | impl<'a> From<&'a mut audio::Frame> for RefMutFrame<'a> { 32 | fn from(frame: &'a mut audio::Frame) -> Self { 33 | RefMutFrame::Audio(frame) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/io.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::net::TcpStream; 3 | use std::io::{Read,Write,Seek,SeekFrom}; 4 | use std::{mem, slice}; 5 | use std::os::raw::{self, c_void, c_int}; 6 | use ffi; 7 | use util::PtrTakeExt; 8 | 9 | pub trait AVSeek: Sized + Send + 'static { 10 | /// Seek to `pos`. Returns `Some(new_pos)` on success 11 | /// and `None` on error. 12 | fn seek(&mut self, pos: SeekFrom) -> Option; 13 | /// The size of the data. It is optional to support this. 14 | fn size(&self) -> Option { 15 | None 16 | } 17 | } 18 | 19 | /// Implementors of AVRead can be used as custom input source. 20 | pub trait AVRead: AVSeek + Sized + Send + 'static { 21 | /// Fill the buffer. 22 | /// Returns the number of bytes read. 23 | /// `None` or `Some(0)` indicates **EOF**. 24 | fn read_packet(&mut self, buf: &mut [u8]) -> Option; 25 | /// The buffer size is very important for performance. 26 | /// For protocols with fixed blocksize it should be set to this blocksize. 27 | /// For others a typical size is a cache page, e.g. 4kb. 28 | /// 29 | /// Default: 4kb. 30 | fn buffer_size() -> c_int { 4 * 1024 } 31 | } 32 | 33 | /// Implementors of AVWrite can be used as custom output source. 34 | pub trait AVWrite: AVSeek + Sized + Send + 'static { 35 | /// Write the buffer to the output. 36 | /// Returns the number of bytes written. 37 | /// `None` or `Some(0)` indicates failure. 38 | fn write_packet(&mut self, buf: &[u8]) -> Option; 39 | /// The buffer size is very important for performance. 40 | /// For protocols with fixed blocksize it should be set to this blocksize. 41 | /// For others a typical size is a cache page, e.g. 4kb. 42 | /// 43 | /// Default: 4kb. 44 | fn buffer_size() -> c_int { 4 * 1024 } 45 | } 46 | 47 | #[doc(hidden)] 48 | pub struct IOContext { 49 | ptr: *mut ffi::AVIOContext, 50 | io_dropper: IODropper, 51 | } 52 | 53 | impl IOContext { 54 | pub fn as_mut_ptr(&mut self) -> *mut ffi::AVIOContext { 55 | self.ptr 56 | } 57 | 58 | pub fn from_reader(mut input: R) -> IOContext { 59 | unsafe { 60 | let buffer_size = R::buffer_size(); 61 | let buffer = ffi::av_malloc(buffer_size as usize * mem::size_of::()) as _; 62 | let write_flag = 0; // Make buffer read-only for ffmpeg 63 | let read_packet = Some(ffi_read_packet:: as _); 64 | let write_packet = None; 65 | let seek = input.seek(SeekFrom::Current(0)).map(|_| ffi_seek:: as _); 66 | let this = Box::into_raw(Box::new(input)) as *mut c_void; 67 | let avio_ctx = ffi::avio_alloc_context( 68 | buffer, 69 | buffer_size, 70 | write_flag, 71 | this, 72 | read_packet, 73 | write_packet, 74 | seek 75 | ); 76 | 77 | assert!(!avio_ctx.is_null(), "Could not allocate AVIO context"); 78 | 79 | IOContext { 80 | ptr: avio_ctx, 81 | io_dropper: io_dropper::, 82 | } 83 | } 84 | } 85 | 86 | pub fn from_writer(mut output: W) -> IOContext { 87 | unsafe { 88 | let buffer_size = W::buffer_size(); 89 | let buffer = ffi::av_malloc(buffer_size as usize * mem::size_of::()) as _; 90 | let write_flag = 1; // Make buffer writable for ffmpeg 91 | let read_packet = None; 92 | let write_packet = Some(ffi_write_packet:: as _); 93 | let seek = output.seek(SeekFrom::Current(0)).map(|_| ffi_seek:: as _);; 94 | let this = Box::into_raw(Box::new(output)) as *mut c_void; 95 | let avio_ctx = ffi::avio_alloc_context( 96 | buffer, 97 | buffer_size, 98 | write_flag, 99 | this, 100 | read_packet, 101 | write_packet, 102 | seek 103 | ); 104 | 105 | assert!(!avio_ctx.is_null(), "Could not allocate AVIO context"); 106 | 107 | IOContext { 108 | ptr: avio_ctx, 109 | io_dropper: io_dropper::, 110 | } 111 | } 112 | } 113 | 114 | // pub unsafe fn close_with(self, mut closer: F) { 115 | // let io = (*self.ptr).opaque; 116 | // closer(); 117 | // (self.io_dropper)(io); 118 | // } 119 | } 120 | 121 | impl Drop for IOContext { 122 | fn drop(&mut self) { 123 | unsafe { 124 | if !self.ptr.is_null() { 125 | let this = &mut (*self.ptr); 126 | (self.io_dropper)(this.opaque.take()); 127 | this.read_packet.take(); 128 | this.write_packet.take(); 129 | this.seek.take(); 130 | ffi::av_free(this.buffer.take() as _); 131 | } 132 | ffi::av_free(self.ptr.take() as _); 133 | } 134 | } 135 | } 136 | 137 | type IODropper = unsafe fn(*mut c_void); 138 | 139 | unsafe fn io_dropper(io: *mut c_void) { 140 | Box::from_raw(io as *mut T); 141 | } 142 | 143 | extern fn ffi_read_packet(this: *mut c_void, buf: *mut u8, buf_size: c_int) -> c_int { 144 | let this = unsafe { &mut *(this as *mut R) }; 145 | let buf = unsafe { slice::from_raw_parts_mut(buf, buf_size as usize) }; 146 | let eof = -1; 147 | this.read_packet(buf).map(|n_read| n_read as c_int).unwrap_or(eof) 148 | } 149 | 150 | extern fn ffi_write_packet(this: *mut c_void, buf: *mut u8, buf_size: c_int) -> c_int { 151 | let this = unsafe { &mut *(this as *mut W) }; 152 | let buf = unsafe { slice::from_raw_parts(buf as *const _, buf_size as usize) }; 153 | let eof = -1; 154 | this.write_packet(buf).map(|n_written| n_written as c_int).unwrap_or(eof) 155 | } 156 | 157 | unsafe extern fn ffi_seek(this: *mut c_void, offset: i64, whence: c_int) -> i64 { 158 | let this = &mut *(this as *mut S); 159 | 160 | if whence == ffi::AVSEEK_SIZE as c_int { 161 | return this.size().and_then(u64_into_i64).unwrap_or(-1); 162 | } 163 | 164 | let pos = match whence as u32 { 165 | ffi::SEEK_SET => match i64_into_u64(offset) { 166 | Some(offset) => SeekFrom::Start(offset), 167 | None => return -1, 168 | }, 169 | ffi::SEEK_CUR => SeekFrom::Current(offset), 170 | ffi::SEEK_END => SeekFrom::End(offset), 171 | _ => return -1, 172 | }; 173 | 174 | this.seek(pos).and_then(u64_into_i64).unwrap_or(-1) 175 | } 176 | 177 | fn u64_into_i64(n: u64) -> Option { 178 | if n <= i64::max_value() as u64 { 179 | Some(n as i64) 180 | } else { 181 | None 182 | } 183 | } 184 | 185 | fn i64_into_u64(n: i64) -> Option { 186 | if n >= 0 { 187 | Some(n as u64) 188 | } else { 189 | None 190 | } 191 | } 192 | 193 | impl AVSeek for File { 194 | fn seek(&mut self, pos: SeekFrom) -> Option { 195 | Seek::seek(self, pos).ok() 196 | } 197 | fn size(&self) -> Option { 198 | self.metadata().map(|m| m.len()).ok() 199 | } 200 | } 201 | 202 | impl AVRead for File { 203 | fn read_packet(&mut self, buf: &mut [u8]) -> Option { 204 | self.read(buf).ok() 205 | } 206 | } 207 | 208 | impl AVWrite for File { 209 | fn write_packet(&mut self, buf: &[u8]) -> Option { 210 | self.write(buf).ok() 211 | } 212 | } 213 | 214 | impl AVSeek for TcpStream { 215 | fn seek(&mut self, _pos: SeekFrom) -> Option { 216 | None 217 | } 218 | fn size(&self) -> Option { 219 | None 220 | } 221 | } 222 | 223 | impl AVRead for TcpStream { 224 | fn read_packet(&mut self, buf: &mut [u8]) -> Option { 225 | self.read(buf).ok() 226 | } 227 | } 228 | 229 | impl AVWrite for TcpStream { 230 | fn write_packet(&mut self, buf: &[u8]) -> Option { 231 | self.write(buf).ok() 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // `error_chain!` can recurse deeply 2 | #![recursion_limit = "1024"] 3 | pub extern crate av_sys as ffi; 4 | #[macro_use] 5 | extern crate lazy_static; 6 | #[macro_use] 7 | extern crate bitflags; 8 | #[macro_use] 9 | extern crate error_chain; 10 | extern crate smallvec; 11 | use std::ffi::CStr; 12 | use std::sync::{Once, ONCE_INIT}; 13 | use util::AsCStr; 14 | 15 | #[macro_use] 16 | mod util; 17 | pub mod common; 18 | 19 | pub mod format; 20 | pub mod video; 21 | pub mod audio; 22 | pub mod generic; 23 | 24 | pub mod io; 25 | pub mod codec; 26 | 27 | pub mod errors; 28 | pub use self::errors::*; 29 | 30 | lazy_static! { 31 | pub static ref AV: LibAV = LibAV::init(); 32 | } 33 | 34 | pub struct LibAV(()); 35 | 36 | impl LibAV { 37 | pub fn init() -> LibAV { 38 | unsafe { 39 | static INIT: Once = ONCE_INIT; 40 | INIT.call_once(|| { 41 | // Init avformat 42 | ffi::av_register_all(); 43 | }); 44 | 45 | LibAV(()) 46 | } 47 | } 48 | 49 | pub fn set_log_level(&self, level: LogLevel) { 50 | unsafe { 51 | ffi::av_log_set_level(level as i32); 52 | } 53 | } 54 | 55 | pub fn version(&self) -> &'static CStr { 56 | unsafe { 57 | ffi::av_version_info().as_cstr().unwrap() 58 | } 59 | } 60 | 61 | pub fn build_flags(&self) -> &'static CStr { 62 | unsafe { 63 | ffi::avformat_configuration().as_cstr().unwrap() 64 | } 65 | } 66 | } 67 | 68 | #[repr(i32)] 69 | pub enum LogLevel { 70 | /// Print no output. 71 | Quiet = ffi::AV_LOG_QUIET as i32, 72 | /// Something went really wrong and we will crash now. 73 | Panic = ffi::AV_LOG_PANIC as i32, 74 | /// Something went wrong and recovery is not possible. 75 | /// For example, no header was found for a format which 76 | /// depends on headers or an illegal combination of parameters 77 | /// is used. 78 | Fatal = ffi::AV_LOG_FATAL as i32, 79 | /// Something went wrong and cannot losslessly be recovered. 80 | /// However, not all future data is affected. 81 | Error = ffi::AV_LOG_ERROR as i32, 82 | /// Something somehow does not look correct. 83 | /// This may or may not lead to problems. 84 | /// An example would be the use of '-vstrict -2'. 85 | Warning = ffi::AV_LOG_WARNING as i32, 86 | /// Standard information. 87 | Info = ffi::AV_LOG_INFO as i32, 88 | /// Detailed information. 89 | Verbose = ffi::AV_LOG_VERBOSE as i32, 90 | /// Stuff which is only useful for libav* developers. 91 | Debug = ffi::AV_LOG_DEBUG as i32, 92 | /// Extremely verbose debugging, useful for libav* development. 93 | Trace = ffi::AV_LOG_TRACE as i32, 94 | } 95 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ptr; 3 | use std::ffi::CStr; 4 | use std::os::raw::c_char; 5 | use std::ops; 6 | 7 | pub trait PtrTakeExt { 8 | fn take(&mut self) -> Self; 9 | } 10 | 11 | impl PtrTakeExt for *const T { 12 | fn take(&mut self) -> Self { 13 | mem::replace(self, ptr::null()) 14 | } 15 | } 16 | 17 | impl PtrTakeExt for *mut T { 18 | fn take(&mut self) -> Self { 19 | mem::replace(self, ptr::null_mut()) 20 | } 21 | } 22 | 23 | pub trait AsCStr: Sized { 24 | unsafe fn as_cstr<'a>(self) -> Option<&'a CStr>; 25 | } 26 | 27 | impl AsCStr for *const c_char { 28 | unsafe fn as_cstr<'a>(self) -> Option<&'a CStr> { 29 | if self.is_null() { 30 | None 31 | } else { 32 | Some(&CStr::from_ptr(self)) 33 | } 34 | } 35 | } 36 | 37 | pub enum OwnedOrRefMut<'a, T: 'a> { 38 | Owned(T), 39 | Borrowed(&'a mut T), 40 | } 41 | 42 | impl<'a, T: 'a> ops::Deref for OwnedOrRefMut<'a, T> { 43 | type Target = T; 44 | 45 | fn deref(&self) -> &T { 46 | match *self { 47 | OwnedOrRefMut::Owned(ref t) => t, 48 | OwnedOrRefMut::Borrowed(ref t) => t, 49 | } 50 | } 51 | } 52 | 53 | impl<'a, T: 'a> ops::DerefMut for OwnedOrRefMut<'a, T> { 54 | fn deref_mut(&mut self) -> &mut T { 55 | match *self { 56 | OwnedOrRefMut::Owned(ref mut t) => t, 57 | OwnedOrRefMut::Borrowed(ref mut t) => t, 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/video/decoder.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | use ffi; 3 | use ffi::AVCodecContext; 4 | use codec::{Codec,MediaType}; 5 | use common::codec_parameters::CodecParameters; 6 | use common::stream::Stream; 7 | use common::{Packet, Timebase}; 8 | use super::Frame; 9 | use errors::*; 10 | 11 | pub struct Decoder { 12 | ptr: *mut AVCodecContext, 13 | } 14 | 15 | unsafe impl Send for Decoder{} 16 | unsafe impl Sync for Decoder{} 17 | 18 | impl Decoder { 19 | // TODO: Share code between audio/video 20 | pub fn from_codec_parameters<'fmt_ctx>(codec_parameters: CodecParameters<'fmt_ctx>) -> Result { 21 | unsafe { 22 | let codec_id = codec_parameters.codec_id(); 23 | 24 | // Try to find a suitable codec 25 | let codec = Codec::find_decoder_by_id(codec_id)?; 26 | if !codec.media_type().is_video() { 27 | bail!(ErrorKind::MediaTypeMismatch(MediaType::Video, codec_id)) 28 | } 29 | 30 | // Try to allocate the decoder 31 | let mut codec_context = ffi::avcodec_alloc_context3(codec.as_ptr()); 32 | if codec_context.is_null() { 33 | bail!("Could not allocate video decoder"); 34 | } 35 | 36 | // Copy codec parameters to codec_parameters 37 | { 38 | let res = ffi::avcodec_parameters_to_context(codec_context, codec_parameters.as_ptr()); 39 | if res < 0 { 40 | ffi::avcodec_free_context(&mut codec_context); 41 | bail!(ErrorKind::CopyCodecParameters); 42 | } 43 | } 44 | 45 | // Try to open the decoder 46 | { 47 | let options = ptr::null_mut(); 48 | let res = ffi::avcodec_open2(codec_context, codec.as_ptr(), options); 49 | if res < 0 { 50 | ffi::avcodec_free_context(&mut codec_context); 51 | bail!(ErrorKind::OpenDecoder("video")); 52 | } 53 | } 54 | 55 | Ok(Decoder { 56 | ptr: codec_context, 57 | }) 58 | } 59 | } 60 | 61 | pub fn from_stream(stream: &Stream) -> Result { 62 | Self::from_codec_parameters(stream.codec_parameters()) 63 | } 64 | 65 | pub fn codec(&self) -> Codec { 66 | unsafe { 67 | Codec::from_ptr(self.as_ref().codec) 68 | } 69 | } 70 | 71 | pub fn time_base(&self) -> Timebase { 72 | self.as_ref().time_base.into() 73 | } 74 | 75 | pub fn pixel_format(&self) -> ffi::AVPixelFormat { 76 | self.as_ref().pix_fmt 77 | } 78 | 79 | pub fn decode<'decoder>(&'decoder mut self, mut packet: Packet) -> Result> { 80 | // TODO: Check that pkt->data is AV_INPUT_BUFFER_PADDING_SIZE larger than packet size 81 | 82 | unsafe { 83 | let res = ffi::avcodec_send_packet(self.as_mut_ptr(), packet.as_mut_ptr()); 84 | if res < 0 { 85 | match res { 86 | ffi::AVERROR_EAGAIN => bail!("EAGAIN in Decoder::decode. This is not supposed to happen :("), 87 | _ => bail!(format!("Failed to decode packet: 0x{:X}", res)) 88 | } 89 | } 90 | 91 | Ok(Frames::from_decoder(self)) 92 | } 93 | } 94 | 95 | pub fn flush<'decoder>(&'decoder mut self) -> Result> { 96 | // TODO: Check that pkt->data is AV_INPUT_BUFFER_PADDING_SIZE larger than packet size 97 | 98 | unsafe { 99 | let res = ffi::avcodec_send_packet(self.as_mut_ptr(), ptr::null_mut()); 100 | 101 | if res < 0 && res != ffi::AVERROR_EAGAIN { 102 | bail!(format!("Failed to flush decoder: 0x{:X}", res)) 103 | } 104 | 105 | Ok(Frames::from_decoder(self)) 106 | } 107 | } 108 | } 109 | 110 | impl Decoder { 111 | pub fn as_ref(&self) -> &AVCodecContext { unsafe { &*self.ptr } } 112 | pub fn as_mut(&mut self) -> &mut AVCodecContext { unsafe { &mut *self.ptr } } 113 | pub fn as_ptr(&self) -> *const AVCodecContext { self.ptr } 114 | pub fn as_mut_ptr(&mut self) -> *mut AVCodecContext { self.ptr } 115 | } 116 | 117 | pub struct Frames<'decoder> { 118 | decoder: &'decoder mut Decoder, 119 | } 120 | 121 | impl<'decoder> Frames<'decoder> { 122 | fn from_decoder(decoder: &'decoder mut Decoder) -> Self { 123 | Frames { 124 | decoder: decoder 125 | } 126 | } 127 | } 128 | 129 | impl<'decoder> Iterator for Frames<'decoder> { 130 | type Item = Result; 131 | 132 | fn next(&mut self) -> Option { 133 | unsafe { 134 | let mut frame = ffi::av_frame_alloc(); 135 | let res = ffi::avcodec_receive_frame(self.decoder.as_mut_ptr(), frame); 136 | 137 | if res < 0 { 138 | ffi::av_frame_free(&mut frame); 139 | 140 | match res { 141 | ffi::AVERROR_EAGAIN | ffi::AVERROR_EOF => return None, 142 | _ => return Some(Err(format!("Failed to receive frame: 0x{:X}", res).into())), 143 | } 144 | } 145 | 146 | let pixel_format = self.decoder.pixel_format(); 147 | let frame = Frame::from_ptr(frame, pixel_format); 148 | 149 | Some(Ok(frame)) 150 | } 151 | } 152 | } 153 | 154 | impl<'decoder> Drop for Frames<'decoder> { 155 | fn drop(&mut self) { 156 | // Decode every frame possible 157 | for _ in self {} 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/video/encoder.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | use std::os::raw::c_int; 3 | use LibAV; 4 | use codec::{ 5 | Codec, 6 | MediaType, 7 | }; 8 | use ffi; 9 | use ffi::{ 10 | AVCodecContext, 11 | AVPixelFormat, 12 | avcodec_alloc_context3, 13 | avcodec_free_context, 14 | }; 15 | use format::OutputFormat; 16 | use generic::RefMutFrame; 17 | use common::{self, Packet, Timebase}; 18 | use errors::*; 19 | use util::OwnedOrRefMut; 20 | use super::{Frame, Scaler}; 21 | 22 | // TODO: Add align field to encoder 23 | const ALIGN: usize = 32; 24 | 25 | pub struct Encoder { 26 | ptr: *mut AVCodecContext, 27 | scaler: Scaler, 28 | tmp_frame: Option, 29 | } 30 | 31 | unsafe impl Send for Encoder {} 32 | unsafe impl Sync for Encoder {} 33 | 34 | impl Encoder { 35 | pub fn from_codec(codec: Codec) -> Result { 36 | EncoderBuilder::from_codec(codec) 37 | } 38 | 39 | pub fn pixel_format(&self) -> ffi::AVPixelFormat { 40 | self.as_ref().pix_fmt 41 | } 42 | 43 | pub fn width(&self) -> usize { 44 | self.as_ref().width as usize 45 | } 46 | 47 | pub fn height(&self) -> usize { 48 | self.as_ref().height as usize 49 | } 50 | 51 | pub fn codec(&self) -> Codec { 52 | unsafe { 53 | Codec::from_ptr(self.as_ref().codec) 54 | } 55 | } 56 | 57 | pub fn time_base(&self) -> Timebase { 58 | self.as_ref().time_base.into() 59 | } 60 | } 61 | 62 | impl Encoder { 63 | pub fn encode<'a, F>(&mut self, frame: F) -> Result where 64 | F: Into>, 65 | { 66 | unsafe { 67 | let mut frame = frame.into().into_video_frame() 68 | .ok_or("Cannot encode non-video frame as video")?; 69 | 70 | // Do scaling if needed 71 | if !frame.is_compatible_with_encoder(self) { 72 | self.init_tmp_frame()?; 73 | 74 | let tmp_frame = self.tmp_frame.as_mut().unwrap(); 75 | let scaler = &mut self.scaler; 76 | 77 | scaler.scale_frame(&mut frame, tmp_frame)?; 78 | 79 | // Copy frame data 80 | tmp_frame.set_pts(frame.pts()); 81 | frame = tmp_frame; 82 | } 83 | 84 | // Encode the frame 85 | { 86 | let mut packet = ::std::mem::zeroed(); 87 | ffi::av_init_packet(&mut packet); 88 | 89 | let res = ffi::avcodec_send_frame(self.ptr, frame.as_mut_ptr()); 90 | if res < 0 { 91 | bail!("Could not encode frame: 0x{:X}", res) 92 | } 93 | } 94 | } 95 | 96 | Ok(Packets::from_mut_encoder(self)) 97 | } 98 | 99 | pub fn flush(self) -> Result> { 100 | unsafe { 101 | // Flush encoder 102 | let res = ffi::avcodec_send_frame(self.ptr, ptr::null_mut()); 103 | if res < 0 { 104 | bail!("Could not flush encoder: 0x{:X}", res) 105 | } 106 | 107 | Ok(Packets::from_encoder(self)) 108 | } 109 | } 110 | 111 | fn init_tmp_frame(&mut self) -> Result<()> { 112 | if self.tmp_frame.is_none() { 113 | self.tmp_frame = Some(Frame::new(self.width(), self.height(), self.pixel_format(), ALIGN)?); 114 | } 115 | Ok(()) 116 | } 117 | } 118 | 119 | impl Encoder { 120 | pub fn as_mut(&mut self) -> &mut AVCodecContext { unsafe { &mut *self.ptr } } 121 | pub fn as_ptr(&self) -> *const AVCodecContext { self.ptr } 122 | pub fn as_mut_ptr(&mut self) -> *mut AVCodecContext { self.ptr } 123 | } 124 | 125 | impl AsRef for Encoder { 126 | fn as_ref(&self) -> &AVCodecContext { 127 | unsafe { &*self.ptr } 128 | } 129 | } 130 | 131 | impl Drop for Encoder { 132 | fn drop(&mut self) { 133 | unsafe { 134 | if !self.ptr.is_null() { 135 | avcodec_free_context(&mut self.ptr); 136 | } 137 | } 138 | } 139 | } 140 | 141 | /// TODO: Check for invalid value ranges 142 | pub struct EncoderBuilder { 143 | codec: Codec, 144 | pixel_format: Option, 145 | width: Option, 146 | height: Option, 147 | time_base: Option, 148 | bitrate: Option, 149 | } 150 | 151 | impl EncoderBuilder { 152 | pub fn from_codec(codec: Codec) -> Result { 153 | common::encoder::require_is_encoder(codec)?; 154 | common::encoder::require_codec_type(MediaType::Video, codec)?; 155 | 156 | Ok(EncoderBuilder { 157 | codec: codec, 158 | pixel_format: None, 159 | width: None, 160 | height: None, 161 | time_base: None, 162 | bitrate: None, 163 | }) 164 | } 165 | 166 | /// TODO: Check for overflow 167 | pub fn width(&mut self, width: usize) -> &mut Self { 168 | self.width = Some(width as i32); self 169 | } 170 | 171 | /// TODO: Check for overflow 172 | pub fn height(&mut self, height: usize) -> &mut Self { 173 | self.height = Some(height as i32); self 174 | } 175 | 176 | pub fn pixel_format(&mut self, pixel_format: AVPixelFormat) -> &mut Self { 177 | self.pixel_format = Some(pixel_format); self 178 | } 179 | 180 | pub fn time_base>(&mut self, time_base: TB) -> &mut Self { 181 | self.time_base = Some(time_base.into()); self 182 | } 183 | 184 | pub fn open(&self, format: OutputFormat) -> Result { 185 | unsafe { 186 | let width = self.width.ok_or("Video encoder width not set")?; 187 | let height = self.height.ok_or("Video encoder height not set")?; 188 | let pixel_format = self.pixel_format.ok_or("Video encoder pixel_format not set")?; 189 | let time_base = self.time_base.unwrap_or((1, 30).into()); 190 | 191 | LibAV::init(); 192 | 193 | let mut codec_context = avcodec_alloc_context3(self.codec.as_ptr()); 194 | if codec_context.is_null() { 195 | bail!("Could not allocate an encoding context"); 196 | } 197 | 198 | // Initialize encoder fields 199 | common::encoder::init(codec_context, format); 200 | (*codec_context).codec_id = self.codec.id(); 201 | (*codec_context).width = width; 202 | (*codec_context).height = height; 203 | (*codec_context).pix_fmt = pixel_format; 204 | if let Some(bitrate) = self.bitrate { 205 | (*codec_context).bit_rate = bitrate; 206 | } 207 | // time_base: This is the fundamental unit of time (in seconds) in terms 208 | // of which frame timestamps are represented. For fixed-fps content, 209 | // time_base should be 1/framerate and timestamp increments should be 210 | // identical to 1. 211 | (*codec_context).time_base = time_base.into(); 212 | 213 | common::encoder::open(codec_context, "video")?; 214 | 215 | Ok(Encoder { 216 | ptr: codec_context, 217 | scaler: Scaler::new(), 218 | tmp_frame: None, 219 | }) 220 | } 221 | } 222 | } 223 | 224 | pub struct Packets<'encoder> { 225 | encoder: OwnedOrRefMut<'encoder, Encoder>, 226 | } 227 | 228 | impl<'encoder> Packets<'encoder> { 229 | fn from_encoder(encoder: Encoder) -> Self { 230 | Packets { 231 | encoder: OwnedOrRefMut::Owned(encoder) 232 | } 233 | } 234 | 235 | fn from_mut_encoder(encoder: &'encoder mut Encoder) -> Self { 236 | Packets { 237 | encoder: OwnedOrRefMut::Borrowed(encoder) 238 | } 239 | } 240 | } 241 | 242 | impl<'encoder> Iterator for Packets<'encoder> { 243 | type Item = Result>; 244 | 245 | fn next(&mut self) -> Option { 246 | unsafe { 247 | let mut packet = ffi::av_packet_alloc(); 248 | let res = ffi::avcodec_receive_packet(self.encoder.as_mut_ptr(), packet); 249 | 250 | if res < 0 { 251 | ffi::av_packet_free(&mut packet); 252 | 253 | match res { 254 | ffi::AVERROR_EAGAIN | ffi::AVERROR_EOF => return None, 255 | _ => return Some(Err(format!("Failed to receive packet: 0x{:X}", res).into())), 256 | } 257 | } 258 | 259 | let packet = Packet::from_ptr(packet, self.encoder.time_base()); 260 | 261 | Some(Ok(packet)) 262 | } 263 | } 264 | } 265 | 266 | impl<'encoder> Drop for Packets<'encoder> { 267 | fn drop(&mut self) { 268 | // Receive every packet possible 269 | for _ in self {} 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /src/video/frame.rs: -------------------------------------------------------------------------------- 1 | use std::slice; 2 | use std::os::raw::c_int; 3 | use smallvec::SmallVec; 4 | use ffi; 5 | use ffi::{ 6 | AVFrame, 7 | AVPixelFormat, 8 | av_frame_alloc, 9 | av_frame_free, 10 | av_frame_get_buffer, 11 | }; 12 | use super::MAX_PLANES; 13 | use video; 14 | use errors::*; 15 | 16 | pub struct Frame { 17 | ptr: *mut ffi::AVFrame, 18 | pixel_format: AVPixelFormat, 19 | } 20 | 21 | // See https://github.com/panicbit/rust-av/issues/28 22 | unsafe impl Send for Frame {} 23 | unsafe impl Sync for Frame {} 24 | 25 | impl Frame { 26 | /// # Panics 27 | /// 28 | /// Panics if `width`, `height` or `align` exceed `c_int::max_value()`. 29 | pub fn new(width: usize, height: usize, pixel_format: AVPixelFormat, align: usize) -> Result { 30 | unsafe { 31 | assert!(width <= c_int::max_value() as usize, "VideoFrame width exceeds c_int::max_value()"); 32 | assert!(height <= c_int::max_value() as usize, "VideoFrame height exceeds c_int::max_value()"); 33 | assert!(align <= c_int::max_value() as usize, "VideoFrame align exceeds c_int::max_value()"); 34 | 35 | let mut frame = av_frame_alloc(); 36 | if frame.is_null() { 37 | bail!("Could not allocate video frame"); 38 | } 39 | 40 | // Fill in required information 41 | (*frame).pts = 0; 42 | (*frame).format = pixel_format as c_int; 43 | (*frame).width = width as c_int; 44 | (*frame).height = height as c_int; 45 | 46 | // Allocate actual frame buffer. 47 | let res = av_frame_get_buffer(frame, align as c_int); 48 | if res < 0 { 49 | av_frame_free(&mut frame); 50 | bail!("Could not allocate video frame buffer: 0x{:X}", res); 51 | } 52 | 53 | Ok(Self::from_ptr(frame, pixel_format)) 54 | } 55 | } 56 | } 57 | 58 | impl Frame { 59 | pub fn pixel_format(&self) -> ffi::AVPixelFormat { 60 | self.pixel_format 61 | } 62 | 63 | pub fn width(&self) -> usize { 64 | self.as_ref().width as usize 65 | } 66 | 67 | pub fn height(&self) -> usize { 68 | self.as_ref().height as usize 69 | } 70 | 71 | pub fn linesize(&self, channel: usize) -> usize { 72 | self.as_ref().linesize[channel] as usize 73 | } 74 | 75 | pub fn linesizes(&self) -> [usize; MAX_PLANES as usize] { 76 | let mut linesizes = [0; MAX_PLANES as usize]; 77 | for channel in 0..MAX_PLANES as usize { 78 | linesizes[channel] = self.linesize(channel) as usize; 79 | } 80 | linesizes 81 | } 82 | 83 | pub fn pts(&self) -> i64 { 84 | self.as_ref().pts 85 | } 86 | 87 | pub fn set_pts(&mut self, pts: i64) { 88 | self.as_mut().pts = pts; 89 | } 90 | 91 | pub fn channel(&self, channel_index: usize) -> &[u8] { 92 | unsafe { 93 | let buf_len = self.height() * self.linesize(channel_index); 94 | slice::from_raw_parts(self.as_ref().data[channel_index], buf_len) 95 | } 96 | } 97 | 98 | pub fn is_compatible_with_encoder(&self, encoder: &video::Encoder) -> bool { 99 | self.pixel_format() == encoder.pixel_format() 100 | && self.width() == encoder.width() 101 | && self.height() == encoder.height() 102 | } 103 | 104 | pub fn channel_mut(&mut self, channel_index: usize) -> &mut [u8] { 105 | unsafe { 106 | 107 | if ffi::av_frame_make_writable(self.ptr) < 0 { 108 | panic!("av_frame_make_writable failed (OOM?)"); 109 | } 110 | 111 | let buf_len = self.height() * self.linesize(channel_index); 112 | 113 | slice::from_raw_parts_mut(self.as_mut().data[channel_index], buf_len) 114 | } 115 | } 116 | 117 | pub fn data(&self) -> SmallVec<[&[u8]; MAX_PLANES]> { 118 | unsafe { 119 | let num_planes = ffi::av_pix_fmt_count_planes(self.pixel_format()); 120 | if num_planes < 0 { 121 | panic!("num planes negative (invalid pixel_format)"); 122 | } 123 | let mut planes = SmallVec::<[&[u8]; MAX_PLANES]>::new(); 124 | 125 | for i in 0..num_planes as usize { 126 | let buf_len = self.height() * self.linesize(i); 127 | let plane = self.as_ref().data[i]; 128 | let plane = slice::from_raw_parts(plane, buf_len); 129 | planes.push(plane); 130 | } 131 | 132 | planes 133 | } 134 | } 135 | 136 | pub fn data_mut(&self) -> SmallVec<[&mut [u8]; MAX_PLANES]> { 137 | unsafe { 138 | if ffi::av_frame_make_writable(self.ptr) < 0 { 139 | panic!("av_frame_make_writable failed (OOM?)"); 140 | } 141 | 142 | let num_planes = ffi::av_pix_fmt_count_planes(self.pixel_format()); 143 | if num_planes < 0 { 144 | panic!("num planes negative (invalid pixel_format)"); 145 | } 146 | let mut planes = SmallVec::<[&mut [u8]; MAX_PLANES]>::new(); 147 | 148 | for i in 0..num_planes as usize { 149 | let buf_len = self.height() * self.linesize(i); 150 | let plane = self.as_ref().data[i]; 151 | let plane = slice::from_raw_parts_mut(plane, buf_len); 152 | planes.push(plane); 153 | } 154 | 155 | planes 156 | } 157 | } 158 | 159 | pub fn fill_channel(&mut self, channel_index: usize, source: &[u8]) -> Result<()> { 160 | unsafe { 161 | use std::cmp::min; 162 | 163 | if ffi::av_frame_make_writable(self.ptr) < 0 { 164 | panic!("av_frame_make_writable failed (OOM?)"); 165 | } 166 | 167 | let source_linesize = source.len() / self.height(); 168 | let target_linesize = self.linesize(channel_index); 169 | let linesize = min(source_linesize, target_linesize); 170 | let channel = self.channel_mut(channel_index); 171 | 172 | let source_lines = source.chunks(source_linesize); 173 | let target_lines = channel.chunks_mut(target_linesize); 174 | 175 | for (target, source) in target_lines.zip(source_lines) { 176 | target[..linesize].copy_from_slice(&source[..linesize]); 177 | } 178 | 179 | Ok(()) 180 | } 181 | } 182 | } 183 | 184 | impl Frame { 185 | pub fn as_ref(&self) -> &AVFrame { 186 | unsafe { &*self.ptr } 187 | } 188 | 189 | pub fn as_mut(&mut self) -> &mut AVFrame { 190 | unsafe { &mut *self.ptr } 191 | } 192 | 193 | pub unsafe fn from_ptr(ptr: *mut AVFrame, pixel_format: AVPixelFormat) -> Self { 194 | Frame { 195 | ptr: ptr, 196 | pixel_format: pixel_format, 197 | } 198 | } 199 | 200 | pub fn as_mut_ptr(&mut self) -> *mut AVFrame { 201 | self.ptr 202 | } 203 | 204 | pub fn into_raw(self) -> *mut AVFrame { 205 | self.ptr 206 | } 207 | } 208 | 209 | impl Drop for Frame { 210 | fn drop(&mut self) { 211 | unsafe { 212 | ffi::av_frame_free(&mut self.ptr); 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/video/mod.rs: -------------------------------------------------------------------------------- 1 | pub const MAX_PLANES: usize = 4; 2 | 3 | mod encoder; 4 | pub use self::encoder::{ 5 | Encoder, 6 | EncoderBuilder, 7 | Packets, 8 | }; 9 | 10 | mod decoder; 11 | pub use self::decoder::{ 12 | Decoder, 13 | Frames, 14 | }; 15 | 16 | mod frame; 17 | pub use self::frame::Frame; 18 | 19 | mod scaler; 20 | pub use self::scaler::Scaler; 21 | -------------------------------------------------------------------------------- /src/video/scaler.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | use smallvec::SmallVec; 3 | use ffi::{self,AVPixelFormat}; 4 | use errors::*; 5 | use super::{Frame, MAX_PLANES}; 6 | 7 | /// A context for scaling/converting video frames. 8 | /// 9 | /// Scaling is most efficiently done by reusing the scaler 10 | /// for a specific combination of source/target width, height and format. 11 | /// It's allowed to use different values for each invocation of the scaling 12 | /// functions, but it will result in reallocation of the internal scaling context, 13 | /// which might not be desireable. 14 | pub struct Scaler { 15 | context: Option, 16 | src_w: usize, 17 | src_h: usize, 18 | src_fmt: AVPixelFormat, 19 | dst_w: usize, 20 | dst_h: usize, 21 | dst_fmt: AVPixelFormat, 22 | } 23 | 24 | unsafe impl Send for Scaler{} 25 | unsafe impl Sync for Scaler{} 26 | 27 | impl Scaler { 28 | /// Create a new scaling context. 29 | pub fn new() -> Self { 30 | Scaler { 31 | context: None, 32 | src_w: 0, 33 | src_h: 0, 34 | src_fmt: AVPixelFormat::AV_PIX_FMT_RGB24, 35 | dst_w: 0, 36 | dst_h: 0, 37 | dst_fmt: AVPixelFormat::AV_PIX_FMT_RGB24, 38 | } 39 | } 40 | 41 | /// Actually initialize the context and 42 | /// reinitialize it if needed. 43 | fn init_context(&mut self, 44 | src_w: usize, src_h: usize, src_fmt: AVPixelFormat, 45 | dst_w: usize, dst_h: usize, dst_fmt: AVPixelFormat, 46 | ) -> Result<&mut SwsContext> { 47 | // (Re)allocate 48 | if self.context.is_none() 49 | || self.src_w != src_w || self.dst_w != dst_w 50 | || self.src_h != src_h || self.dst_h != dst_h 51 | || self.src_fmt != src_fmt || self.dst_fmt != dst_fmt 52 | { 53 | let flags = ffi::SWS_BICUBIC as i32; 54 | self.context = Some(SwsContext::new( 55 | src_w as i32, src_h as i32, src_fmt, 56 | dst_w as i32, dst_h as i32, dst_fmt, 57 | flags 58 | )?); 59 | 60 | self.src_h = src_h; 61 | self.src_w = src_w; 62 | self.src_fmt = src_fmt; 63 | self.dst_h = dst_h; 64 | self.dst_w = dst_w; 65 | self.dst_fmt = dst_fmt; 66 | } 67 | 68 | Ok(self.context.as_mut().unwrap()) 69 | } 70 | 71 | /// Scale `src_data` to `src_data` by using the given dimensions and formats. 72 | /// 73 | /// # Requirements 74 | /// 75 | /// - `src_fmt` and `dst_fmt` need to be valid pixel formats. 76 | /// - `src_w`, `src_h`, `dst_w` and `dst_h` need to be greater than 0. 77 | /// - The number of planes need to be greater than or equal to the 78 | /// number of planes required by the pixel formats. 79 | /// - The planes need to be big enough to contain the amount of bytes 80 | /// described by their linesize and the height (`linesize * height`). 81 | pub fn scale(&mut self, 82 | src_data: & [& [u8]], src_linesize: &[usize], src_w: usize, src_h: usize, src_fmt: AVPixelFormat, 83 | dst_data: &mut [&mut [u8]], dst_linesize: &[usize], dst_w: usize, dst_h: usize, dst_fmt: AVPixelFormat, 84 | ) -> Result<()> { 85 | unsafe { 86 | // Get appropriate scaling context 87 | let context = self.init_context( 88 | src_w, src_h, src_fmt, 89 | dst_w, dst_h, dst_fmt, 90 | )?; 91 | 92 | let src_num_planes = ffi::av_pix_fmt_count_planes(src_fmt); 93 | let dst_num_planes = ffi::av_pix_fmt_count_planes(dst_fmt); 94 | 95 | if src_num_planes <= 0 || dst_num_planes <= 0 { 96 | bail!("Invalid pixel format for scale source or target"); 97 | } 98 | 99 | let src_num_planes = src_num_planes as usize; 100 | let dst_num_planes = dst_num_planes as usize; 101 | 102 | // Check that the required planes are availabe 103 | { 104 | if src_data.len() < src_num_planes || src_linesize.len() < src_num_planes { 105 | bail!("Scale source has invalid number of planes"); 106 | } 107 | 108 | if dst_data.len() < dst_num_planes || dst_linesize.len() < dst_num_planes { 109 | bail!("Scale target has invalid number of planes"); 110 | } 111 | } 112 | 113 | // Check that the buffers are big enough for the given h/w 114 | { 115 | for (plane, linesize) in src_data.iter().zip(src_linesize) { 116 | if src_h * linesize > plane.len() { 117 | println!("Source plane data too small"); 118 | } 119 | } 120 | 121 | for (plane, linesize) in dst_data.iter().zip(dst_linesize) { 122 | if dst_h * linesize > plane.len() { 123 | println!("Target plane data too small"); 124 | } 125 | } 126 | } 127 | 128 | // Convert the slices to the proper ffmpeg types 129 | let mut src_data: SmallVec<[*const u8; MAX_PLANES]> = src_data.iter().map(| s| s.as_ptr()).collect(); 130 | let mut dst_data: SmallVec<[*const u8; MAX_PLANES]> = dst_data.iter().map(| s| s.as_ptr()).collect(); 131 | let mut src_linesize: SmallVec<[i32; MAX_PLANES]> = src_linesize.iter().map(|&s| s as i32).collect(); 132 | let mut dst_linesize: SmallVec<[i32; MAX_PLANES]> = dst_linesize.iter().map(|&s| s as i32).collect(); 133 | 134 | // Fill arrays up to MAX_PLANES 135 | while src_data.len() < 4 { src_data.push(ptr::null()) } 136 | while dst_data.len() < 4 { dst_data.push(ptr::null()) } 137 | while src_linesize.len() < 4 { src_linesize.push(0) } 138 | while dst_linesize.len() < 4 { dst_linesize.push(0) } 139 | 140 | let source_y = 0; 141 | 142 | ffi::sws_scale(context.as_mut_ptr(), 143 | src_data.as_ptr() , src_linesize.as_ptr(), source_y, src_h as i32, 144 | dst_data.as_mut_ptr(), dst_linesize.as_ptr(), 145 | ); 146 | 147 | Ok(()) 148 | } 149 | } 150 | 151 | /// Copy the `src` pixel data to the `dst` pixel data, 152 | /// scaling dimensions and converting pixel formats as required. 153 | pub fn scale_frame(&mut self, src: &Frame, dst: &mut Frame) -> Result<()> { 154 | let src_data = &src.data(); 155 | let src_linesize = &src.linesizes(); 156 | let src_h = src.height(); 157 | let src_w = src.width(); 158 | let src_fmt = src.pixel_format(); 159 | 160 | let dst_data = &mut dst.data_mut(); 161 | let dst_linesize = & dst.linesizes(); 162 | let dst_h = dst.height(); 163 | let dst_w = dst.width(); 164 | let dst_fmt = dst.pixel_format(); 165 | 166 | self.scale( 167 | src_data, src_linesize, src_w, src_h, src_fmt, 168 | dst_data, dst_linesize, dst_w, dst_h, dst_fmt, 169 | ) 170 | } 171 | } 172 | 173 | struct SwsContext(*mut ffi::SwsContext); 174 | 175 | impl SwsContext { 176 | fn new( 177 | source_width: i32, source_height: i32, source_pixel_format: AVPixelFormat, 178 | target_width: i32, target_height: i32, target_pixel_format: AVPixelFormat, 179 | flags: i32, 180 | ) -> Result { 181 | unsafe { 182 | let source_filter = ptr::null_mut(); 183 | let target_filter = ptr::null_mut(); 184 | let param = ptr::null_mut(); 185 | 186 | // Check that the pixel dimensions are valid 187 | { 188 | if source_width <= 0 || source_height <= 0 { 189 | bail!("Scale source dimension {} x {} invalid", source_width, source_height) 190 | } 191 | 192 | if target_width <= 0 || target_height <= 0 { 193 | bail!("Scale target dimension {} x {} invalid", target_width, target_height) 194 | } 195 | } 196 | 197 | let scaler = ffi::sws_getContext( 198 | source_width, source_height, source_pixel_format, 199 | target_width, target_height, target_pixel_format, 200 | flags, 201 | source_filter, 202 | target_filter, 203 | param 204 | ); 205 | 206 | if scaler.is_null() { 207 | bail!("Could not create scaler context"); 208 | } 209 | 210 | Ok(SwsContext(scaler)) 211 | } 212 | } 213 | 214 | unsafe fn free(&mut self) { 215 | if !self.0.is_null() { 216 | ffi::sws_freeContext(self.0); 217 | self.0 = ptr::null_mut(); 218 | } 219 | } 220 | 221 | fn as_mut_ptr(&mut self) -> *mut ffi::SwsContext { 222 | self.0 223 | } 224 | } 225 | 226 | impl Drop for SwsContext { 227 | fn drop(&mut self) { 228 | unsafe { 229 | self.free() 230 | } 231 | } 232 | } 233 | 234 | #[cfg(test)] 235 | mod test { 236 | use ffi::AVPixelFormat::*; 237 | use super::Scaler; 238 | 239 | #[test] 240 | fn reusable() { 241 | 242 | let source = vec![0xFF, 0x00, 0x00]; 243 | let source_data = &[&source[..], &[], &[], &[]]; 244 | let source_width = 1; 245 | let source_linesize = &[3*source_width, 0, 0, 0]; 246 | let source_height = 1; 247 | let source_format = AV_PIX_FMT_RGB24; 248 | 249 | let mut target: Vec = vec![ 250 | 1,2,3, 4,5,6, 7,8,9, 10,11,12, 251 | 1,2,3, 4,5,6, 7,8,9, 10,11,12, 252 | 1,2,3, 4,5,6, 7,8,9, 10,11,12, 253 | 1,2,3, 4,5,6, 7,8,9, 10,11,12, 254 | 99,99,99,99,99,99,99,99,99,99,99,99, // Canary 255 | 99,99,99,99,99,99,99,99,99,99,99,99, 256 | 99,99,99,99,99,99,99,99,99,99,99,99, 257 | 99,99,99,99,99,99,99,99,99,99,99,99, 258 | ]; 259 | let target_data = &mut [&mut target[..], &mut [], &mut [], &mut []]; 260 | let target_width = 4; 261 | let target_linesize = &[3*target_width, 0, 0, 0]; 262 | let target_height = 4; 263 | let target_format = AV_PIX_FMT_RGB24; 264 | 265 | let mut scaler = Scaler::new(); 266 | 267 | for _ in 0 .. 2000 { 268 | scaler.scale( 269 | source_data, source_linesize, source_width, source_height, source_format, 270 | target_data, target_linesize, target_width, target_height, target_format, 271 | ).unwrap(); 272 | } 273 | 274 | assert_eq!(target_data[0], &[ 275 | 0xFF,0x00,0x00, 0xFF,0x00,0x00, 0xFF,0x00,0x00, 0xFF,0x00,0x00, 276 | 0xFF,0x00,0x00, 0xFF,0x00,0x00, 0xFF,0x00,0x00, 0xFF,0x00,0x00, 277 | 0xFF,0x00,0x00, 0xFF,0x00,0x00, 0xFF,0x00,0x00, 0xFF,0x00,0x00, 278 | 0xFF,0x00,0x00, 0xFF,0x00,0x00, 0xFF,0x00,0x00, 0xFF,0x00,0x00, 279 | 99,99,99,99,99,99,99,99,99,99,99,99, // Canary 280 | 99,99,99,99,99,99,99,99,99,99,99,99, 281 | 99,99,99,99,99,99,99,99,99,99,99,99, 282 | 99,99,99,99,99,99,99,99,99,99,99,99, 283 | ][..]); 284 | } 285 | 286 | #[test] 287 | #[should_panic] 288 | fn not_enough_planes() { 289 | let mut scaler = Scaler::new(); 290 | 291 | scaler.scale( 292 | & [], & [], 1, 1, AV_PIX_FMT_RGB24, 293 | &mut [], &mut [], 1, 1, AV_PIX_FMT_RGB24, 294 | ).unwrap(); 295 | } 296 | 297 | #[test] 298 | #[should_panic] 299 | fn invalid_dimensions() { 300 | let mut scaler = Scaler::new(); 301 | 302 | scaler.scale( 303 | & [], & [], 0, 0, AV_PIX_FMT_RGB24, 304 | &mut [], &mut [], 0, 0, AV_PIX_FMT_RGB24, 305 | ).unwrap(); 306 | } 307 | } 308 | --------------------------------------------------------------------------------