├── src ├── util │ ├── rounding.rs │ ├── chroma │ │ ├── mod.rs │ │ └── location.rs │ ├── format │ │ └── mod.rs │ ├── mathematics │ │ ├── mod.rs │ │ ├── rounding.rs │ │ └── rescale.rs │ ├── frame │ │ ├── flag.rs │ │ └── mod.rs │ ├── log │ │ ├── flag.rs │ │ ├── mod.rs │ │ ├── level.rs │ │ └── callback.rs │ ├── color │ │ ├── mod.rs │ │ ├── range.rs │ │ ├── space.rs │ │ ├── primaries.rs │ │ └── transfer_characteristic.rs │ ├── dictionary │ │ ├── mod.rs │ │ ├── iter.rs │ │ ├── mutable.rs │ │ ├── immutable.rs │ │ └── owned.rs │ ├── time.rs │ ├── range.rs │ ├── interrupt.rs │ ├── mod.rs │ ├── media.rs │ ├── picture.rs │ ├── option │ │ ├── mod.rs │ │ └── traits.rs │ └── channel_layout.rs ├── format │ ├── chapter │ │ ├── mod.rs │ │ ├── chapter.rs │ │ └── chapter_mut.rs │ ├── stream │ │ ├── mod.rs │ │ ├── disposition.rs │ │ ├── stream_mut.rs │ │ └── stream.rs │ ├── network.rs │ ├── context │ │ ├── destructor.rs │ │ └── mod.rs │ └── format │ │ ├── flag.rs │ │ ├── mod.rs │ │ ├── input.rs │ │ ├── iter.rs │ │ └── output.rs ├── filter │ ├── context │ │ ├── mod.rs │ │ ├── source.rs │ │ ├── sink.rs │ │ └── context.rs │ ├── flag.rs │ ├── pad.rs │ ├── mod.rs │ └── filter.rs ├── codec │ ├── subtitle │ │ ├── flag.rs │ │ └── rect.rs │ ├── packet │ │ ├── traits.rs │ │ ├── flag.rs │ │ ├── mod.rs │ │ └── borrow.rs │ ├── decoder │ │ ├── conceal.rs │ │ ├── slice.rs │ │ ├── check.rs │ │ ├── mod.rs │ │ ├── subtitle.rs │ │ ├── opened.rs │ │ ├── audio.rs │ │ ├── decoder.rs │ │ └── video.rs │ ├── encoder │ │ ├── prediction.rs │ │ ├── decision.rs │ │ ├── mod.rs │ │ ├── motion_estimation.rs │ │ ├── comparison.rs │ │ └── subtitle.rs │ ├── field_order.rs │ ├── debug.rs │ ├── flag.rs │ ├── compliance.rs │ ├── capabilities.rs │ ├── discard.rs │ ├── mod.rs │ ├── threading.rs │ ├── audio_service.rs │ ├── traits.rs │ ├── video.rs │ ├── codec.rs │ ├── audio.rs │ └── context.rs ├── software │ ├── resampling │ │ ├── flag.rs │ │ ├── engine.rs │ │ ├── delay.rs │ │ ├── mod.rs │ │ ├── filter.rs │ │ ├── extensions.rs │ │ └── dither.rs │ ├── scaling │ │ ├── support.rs │ │ ├── mod.rs │ │ ├── color_space.rs │ │ ├── flag.rs │ │ ├── filter.rs │ │ ├── extensions.rs │ │ └── vector.rs │ └── mod.rs ├── device │ ├── input.rs │ ├── output.rs │ ├── mod.rs │ └── extensions.rs └── lib.rs ├── .gitignore ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature.md │ └── bug.md └── workflows │ ├── release.yml │ └── build.yml ├── LICENSE ├── README.md ├── BUILDING.md ├── examples ├── chapters.rs ├── remux.rs ├── dump-frames.rs ├── codec-info.rs └── metadata.rs └── CHANGELOG.md /src/util/rounding.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/util/chroma/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod location; 2 | pub use self::location::Location; 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Rust files 2 | target 3 | Cargo.lock 4 | 5 | # Vim temporary files 6 | *.swp 7 | *.swo 8 | *.swn 9 | -------------------------------------------------------------------------------- /src/util/format/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod sample; 2 | pub use self::sample::Sample; 3 | 4 | pub mod pixel; 5 | pub use self::pixel::Pixel; 6 | -------------------------------------------------------------------------------- /src/format/chapter/mod.rs: -------------------------------------------------------------------------------- 1 | mod chapter; 2 | pub use self::chapter::Chapter; 3 | 4 | mod chapter_mut; 5 | pub use self::chapter_mut::ChapterMut; 6 | -------------------------------------------------------------------------------- /src/util/mathematics/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod rounding; 2 | pub use self::rounding::Rounding; 3 | 4 | pub mod rescale; 5 | pub use self::rescale::Rescale; 6 | -------------------------------------------------------------------------------- /src/filter/context/mod.rs: -------------------------------------------------------------------------------- 1 | mod context; 2 | pub use self::context::Context; 3 | 4 | mod source; 5 | pub use self::source::Source; 6 | 7 | mod sink; 8 | pub use self::sink::Sink; 9 | -------------------------------------------------------------------------------- /src/util/frame/flag.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | pub struct Flags: c_int { 6 | const CORRUPT = AV_FRAME_FLAG_CORRUPT; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/codec/subtitle/flag.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | pub struct Flags: c_int { 6 | const FORCED = AV_SUBTITLE_FLAG_FORCED; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/software/resampling/flag.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | pub struct Flags: c_int { 6 | const FORCE = SWR_FLAG_RESAMPLE; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/codec/packet/traits.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | 3 | pub trait Ref { 4 | fn as_ptr(&self) -> *const AVPacket; 5 | } 6 | 7 | pub trait Mut { 8 | fn as_mut_ptr(&mut self) -> *mut AVPacket; 9 | } 10 | -------------------------------------------------------------------------------- /src/format/stream/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod disposition; 2 | pub use self::disposition::Disposition; 3 | 4 | mod stream; 5 | pub use self::stream::Stream; 6 | 7 | mod stream_mut; 8 | pub use self::stream_mut::StreamMut; 9 | -------------------------------------------------------------------------------- /src/codec/packet/flag.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | pub struct Flags: c_int { 6 | const KEY = AV_PKT_FLAG_KEY; 7 | const CORRUPT = AV_PKT_FLAG_CORRUPT; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/format/network.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | 3 | pub fn init() { 4 | unsafe { 5 | avformat_network_init(); 6 | } 7 | } 8 | 9 | pub fn deinit() { 10 | unsafe { 11 | avformat_network_deinit(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/util/log/flag.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | pub struct Flags: c_int { 6 | const SKIP_REPEATED = AV_LOG_SKIP_REPEATED; 7 | const PRINT_LEVEL = AV_LOG_PRINT_LEVEL; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/codec/decoder/conceal.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | pub struct Conceal: c_int { 6 | const GUESS_MVS = FF_EC_GUESS_MVS; 7 | const DEBLOCK = FF_EC_DEBLOCK; 8 | const FAVOR_INTER = FF_EC_FAVOR_INTER; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/codec/decoder/slice.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | pub struct Flags: c_int { 6 | const CODED_ORDER = SLICE_FLAG_CODED_ORDER; 7 | const ALLOW_FIELD = SLICE_FLAG_ALLOW_FIELD; 8 | const ALLOW_PLANE = SLICE_FLAG_ALLOW_PLANE; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/util/color/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod range; 2 | pub use self::range::Range; 3 | 4 | pub mod space; 5 | pub use self::space::Space; 6 | 7 | pub mod primaries; 8 | pub use self::primaries::Primaries; 9 | 10 | pub mod transfer_characteristic; 11 | pub use self::transfer_characteristic::TransferCharacteristic; 12 | -------------------------------------------------------------------------------- /src/codec/packet/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod traits; 2 | pub use self::traits::{Mut, Ref}; 3 | 4 | pub mod packet; 5 | pub use self::packet::Packet; 6 | 7 | pub mod borrow; 8 | pub use self::borrow::Borrow; 9 | 10 | pub mod side_data; 11 | pub use self::side_data::SideData; 12 | 13 | pub mod flag; 14 | pub use self::flag::Flags; 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | 3 | contact_links: 4 | - name: Questions about usage 5 | url: https://github.com/flavioroth/rust-ffmpeg/discussions 6 | about: If you have a question about usage, please use discussions instead of opening a (non-)issue. Note that I (solo maintainer short on time) unfortunately might not be able to respond, though I try to be helpful when time permits. 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New feature request 3 | about: If you have a feature request, your best bet is probably a PR. However, for anything nontrivial, please open an issue to discuss it first. 4 | --- 5 | 6 | *Please discuss your new feature before implementing it if it's nontrivial. Adding a small method is probably trivial, anything larger than that, maybe not. Note that API stability is paramount.* 7 | -------------------------------------------------------------------------------- /src/software/scaling/support.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use util::format; 3 | 4 | pub fn input(format: format::Pixel) -> bool { 5 | unsafe { sws_isSupportedInput(format.into()) != 0 } 6 | } 7 | 8 | pub fn output(format: format::Pixel) -> bool { 9 | unsafe { sws_isSupportedOutput(format.into()) != 0 } 10 | } 11 | 12 | pub fn endianness_conversion(format: format::Pixel) -> bool { 13 | unsafe { sws_isSupportedEndiannessConversion(format.into()) != 0 } 14 | } 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Everyone is permitted to copy and distribute verbatim or modified 5 | copies of this license document, and changing it is allowed as long 6 | as the name is changed. 7 | 8 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 9 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 10 | 11 | 0. You just DO WHAT THE FUCK YOU WANT TO. 12 | -------------------------------------------------------------------------------- /src/util/dictionary/mod.rs: -------------------------------------------------------------------------------- 1 | mod immutable; 2 | pub use self::immutable::Ref; 3 | 4 | mod mutable; 5 | pub use self::mutable::Ref as Mut; 6 | 7 | mod owned; 8 | pub use self::owned::Owned; 9 | 10 | mod iter; 11 | pub use self::iter::Iter; 12 | 13 | #[macro_export] 14 | macro_rules! dict { 15 | ( $($key:expr => $value:expr),* $(,)*) => ({ 16 | let mut dict = ::ffmpeg_rs::Dictionary::new(); 17 | 18 | $( 19 | dict.set($key, $value); 20 | )* 21 | 22 | dict 23 | } 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /src/codec/decoder/check.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | pub struct Check: c_int { 6 | const CRC = AV_EF_CRCCHECK; 7 | const BISTREAM = AV_EF_BITSTREAM; 8 | const BUFFER = AV_EF_BUFFER; 9 | const EXPLODE = AV_EF_EXPLODE; 10 | 11 | const IGNORE_ERROR = AV_EF_IGNORE_ERR; 12 | const CAREFUL = AV_EF_CAREFUL; 13 | const COMPLIANT = AV_EF_COMPLIANT; 14 | const AGGRESSIVE = AV_EF_AGGRESSIVE; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/filter/flag.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | pub struct Flags: c_int { 6 | const DYNAMIC_INPUTS = AVFILTER_FLAG_DYNAMIC_INPUTS; 7 | const DYNAMIC_OUTPUTS = AVFILTER_FLAG_DYNAMIC_OUTPUTS; 8 | const SLICE_THREADS = AVFILTER_FLAG_SLICE_THREADS; 9 | const SUPPORT_TIMELINE_GENERIC = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC; 10 | const SUPPORT_TIMELINE_INTERNAL = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL; 11 | const SUPPORT_TIMELINE = AVFILTER_FLAG_SUPPORT_TIMELINE; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/util/time.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use Error; 3 | 4 | #[inline(always)] 5 | pub fn current() -> i64 { 6 | unsafe { av_gettime() } 7 | } 8 | 9 | #[inline(always)] 10 | pub fn relative() -> i64 { 11 | unsafe { av_gettime_relative() } 12 | } 13 | 14 | #[inline(always)] 15 | pub fn is_monotonic() -> bool { 16 | unsafe { av_gettime_relative_is_monotonic() != 0 } 17 | } 18 | 19 | #[inline(always)] 20 | pub fn sleep(usec: u32) -> Result<(), Error> { 21 | unsafe { 22 | match av_usleep(usec) { 23 | 0 => Ok(()), 24 | e => Err(Error::from(e)), 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: create release 2 | on: workflow_dispatch 3 | jobs: 4 | release: 5 | name: Create release 6 | runs-on: ubuntu-latest 7 | container: jrottenberg/ffmpeg:5.0-ubuntu 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Install dependencies 11 | run: | 12 | apt update 13 | apt install -y --no-install-recommends clang curl pkg-config 14 | - uses: actions-rs/toolchain@v1 15 | with: 16 | toolchain: stable 17 | override: true 18 | - uses: katyo/publish-crates@v1 19 | with: 20 | registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }} -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New issue for reproducible bug 3 | about: If you found a reproducible bug, submit it along with as much info as possible. 4 | --- 5 | 6 | *Please include as much info as possible to save me (solo maintainer helping for free) some time. A [minimal, complete, and reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) is a must. Link to a gist if you don't feel like posting all the code inline. At the same time, please leave out unnecessary code so I don't need to wade through a hundred lines to get to the problematic part. Tell me your OS, FFmpeg version, etc. if there's even a slim chance of relevancy.* 7 | -------------------------------------------------------------------------------- /src/util/range.rs: -------------------------------------------------------------------------------- 1 | use std::ops; 2 | 3 | pub trait Range { 4 | fn start(&self) -> Option<&T> { 5 | None 6 | } 7 | 8 | fn end(&self) -> Option<&T> { 9 | None 10 | } 11 | } 12 | 13 | impl Range for ops::Range { 14 | fn start(&self) -> Option<&T> { 15 | Some(&self.start) 16 | } 17 | 18 | fn end(&self) -> Option<&T> { 19 | Some(&self.end) 20 | } 21 | } 22 | 23 | impl Range for ops::RangeTo { 24 | fn end(&self) -> Option<&T> { 25 | Some(&self.end) 26 | } 27 | } 28 | 29 | impl Range for ops::RangeFrom { 30 | fn start(&self) -> Option<&T> { 31 | Some(&self.start) 32 | } 33 | } 34 | 35 | impl Range for ops::RangeFull {} 36 | -------------------------------------------------------------------------------- /src/util/log/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod level; 2 | pub use self::level::Level; 3 | 4 | pub mod flag; 5 | pub use self::flag::Flags; 6 | 7 | use ffi::*; 8 | use std::convert::TryInto; 9 | 10 | #[cfg(all( 11 | target_arch = "x86_64", 12 | any(target_family = "windows", target_family = "unix") 13 | ))] 14 | pub mod callback; 15 | 16 | pub fn set_level(value: Level) { 17 | unsafe { av_log_set_level(value.into()) } 18 | } 19 | 20 | pub fn get_level() -> Result { 21 | unsafe { av_log_get_level().try_into() } 22 | } 23 | 24 | pub fn set_flags(value: Flags) { 25 | unsafe { av_log_set_flags(value.bits()) } 26 | } 27 | 28 | pub fn get_flags() -> Flags { 29 | unsafe { Flags::from_bits_truncate(av_log_get_flags()) } 30 | } 31 | -------------------------------------------------------------------------------- /src/software/resampling/engine.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use sys::SwrEngine::*; 3 | 4 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] 5 | pub enum Engine { 6 | Software, 7 | SoundExchange, 8 | } 9 | 10 | impl From for Engine { 11 | fn from(value: SwrEngine) -> Engine { 12 | match value { 13 | SWR_ENGINE_SWR => Engine::Software, 14 | SWR_ENGINE_SOXR => Engine::SoundExchange, 15 | SWR_ENGINE_NB => Engine::Software, 16 | } 17 | } 18 | } 19 | 20 | impl From for SwrEngine { 21 | fn from(value: Engine) -> SwrEngine { 22 | match value { 23 | Engine::Software => SWR_ENGINE_SWR, 24 | Engine::SoundExchange => SWR_ENGINE_SOXR, 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/software/resampling/delay.rs: -------------------------------------------------------------------------------- 1 | use super::Context; 2 | use ffi::*; 3 | 4 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] 5 | pub struct Delay { 6 | pub seconds: i64, 7 | pub milliseconds: i64, 8 | pub input: i64, 9 | pub output: i64, 10 | } 11 | 12 | impl Delay { 13 | pub fn from(context: &Context) -> Self { 14 | unsafe { 15 | Delay { 16 | seconds: swr_get_delay(context.as_ptr() as *mut _, 1), 17 | milliseconds: swr_get_delay(context.as_ptr() as *mut _, 1000), 18 | input: swr_get_delay(context.as_ptr() as *mut _, i64::from(context.input().rate)), 19 | output: swr_get_delay(context.as_ptr() as *mut _, i64::from(context.output().rate)), 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/util/interrupt.rs: -------------------------------------------------------------------------------- 1 | use std::panic; 2 | use std::process; 3 | 4 | use ffi::*; 5 | use libc::{c_int, c_void}; 6 | 7 | pub struct Interrupt { 8 | pub interrupt: AVIOInterruptCB, 9 | } 10 | 11 | extern "C" fn callback(opaque: *mut c_void) -> c_int 12 | where 13 | F: FnMut() -> bool, 14 | { 15 | match panic::catch_unwind(|| (unsafe { &mut *(opaque as *mut F) })()) { 16 | Ok(ret) => ret as c_int, 17 | Err(_) => process::abort(), 18 | } 19 | } 20 | 21 | pub fn new(opaque: Box) -> Interrupt 22 | where 23 | F: FnMut() -> bool, 24 | { 25 | let interrupt_cb = AVIOInterruptCB { 26 | callback: Some(callback::), 27 | opaque: Box::into_raw(opaque) as *mut c_void, 28 | }; 29 | Interrupt { 30 | interrupt: interrupt_cb, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/format/context/destructor.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | 3 | #[derive(Copy, Clone, Debug)] 4 | pub enum Mode { 5 | Input, 6 | Output, 7 | } 8 | 9 | pub struct Destructor { 10 | ptr: *mut AVFormatContext, 11 | mode: Mode, 12 | } 13 | 14 | impl Destructor { 15 | pub unsafe fn new(ptr: *mut AVFormatContext, mode: Mode) -> Self { 16 | Destructor { ptr, mode } 17 | } 18 | } 19 | 20 | impl Drop for Destructor { 21 | fn drop(&mut self) { 22 | unsafe { 23 | match self.mode { 24 | Mode::Input => avformat_close_input(&mut self.ptr), 25 | 26 | Mode::Output => { 27 | avio_close((*self.ptr).pb); 28 | avformat_free_context(self.ptr); 29 | } 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/software/scaling/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod flag; 2 | pub use self::flag::Flags; 3 | 4 | pub mod color_space; 5 | pub use self::color_space::ColorSpace; 6 | 7 | pub mod support; 8 | 9 | pub mod vector; 10 | pub use self::vector::Vector; 11 | 12 | pub mod filter; 13 | pub use self::filter::Filter; 14 | 15 | pub mod context; 16 | pub use self::context::Context; 17 | 18 | mod extensions; 19 | 20 | use std::ffi::CStr; 21 | use std::str::from_utf8_unchecked; 22 | 23 | use ffi::*; 24 | 25 | pub fn version() -> u32 { 26 | unsafe { swscale_version() } 27 | } 28 | 29 | pub fn configuration() -> &'static str { 30 | unsafe { from_utf8_unchecked(CStr::from_ptr(swscale_configuration()).to_bytes()) } 31 | } 32 | 33 | pub fn license() -> &'static str { 34 | unsafe { from_utf8_unchecked(CStr::from_ptr(swscale_license()).to_bytes()) } 35 | } 36 | -------------------------------------------------------------------------------- /src/codec/encoder/prediction.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Prediction { 6 | Left, 7 | Plane, 8 | Median, 9 | } 10 | 11 | impl From for Prediction { 12 | fn from(value: c_int) -> Prediction { 13 | match value { 14 | FF_PRED_LEFT => Prediction::Left, 15 | FF_PRED_PLANE => Prediction::Plane, 16 | FF_PRED_MEDIAN => Prediction::Median, 17 | 18 | _ => Prediction::Left, 19 | } 20 | } 21 | } 22 | 23 | impl From for c_int { 24 | fn from(value: Prediction) -> c_int { 25 | match value { 26 | Prediction::Left => FF_PRED_LEFT, 27 | Prediction::Plane => FF_PRED_PLANE, 28 | Prediction::Median => FF_PRED_MEDIAN, 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/software/resampling/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod flag; 2 | pub use self::flag::Flags; 3 | 4 | pub mod dither; 5 | pub use self::dither::Dither; 6 | 7 | pub mod engine; 8 | pub use self::engine::Engine; 9 | 10 | pub mod filter; 11 | pub use self::filter::Filter; 12 | 13 | pub mod delay; 14 | pub use self::delay::Delay; 15 | 16 | pub mod context; 17 | pub use self::context::Context; 18 | 19 | mod extensions; 20 | 21 | use std::ffi::CStr; 22 | use std::str::from_utf8_unchecked; 23 | 24 | use ffi::*; 25 | 26 | pub fn version() -> u32 { 27 | unsafe { swresample_version() } 28 | } 29 | 30 | pub fn configuration() -> &'static str { 31 | unsafe { from_utf8_unchecked(CStr::from_ptr(swresample_configuration()).to_bytes()) } 32 | } 33 | 34 | pub fn license() -> &'static str { 35 | unsafe { from_utf8_unchecked(CStr::from_ptr(swresample_license()).to_bytes()) } 36 | } 37 | -------------------------------------------------------------------------------- /src/codec/encoder/decision.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Decision { 6 | Simple, 7 | Bits, 8 | RateDistortion, 9 | } 10 | 11 | impl From for Decision { 12 | fn from(value: c_int) -> Decision { 13 | match value { 14 | FF_MB_DECISION_SIMPLE => Decision::Simple, 15 | FF_MB_DECISION_BITS => Decision::Bits, 16 | FF_MB_DECISION_RD => Decision::RateDistortion, 17 | 18 | _ => Decision::Simple, 19 | } 20 | } 21 | } 22 | 23 | impl From for c_int { 24 | fn from(value: Decision) -> c_int { 25 | match value { 26 | Decision::Simple => FF_MB_DECISION_SIMPLE, 27 | Decision::Bits => FF_MB_DECISION_BITS, 28 | Decision::RateDistortion => FF_MB_DECISION_RD, 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | pub mod dictionary; 3 | pub mod channel_layout; 4 | pub mod chroma; 5 | pub mod color; 6 | pub mod error; 7 | pub mod format; 8 | pub mod frame; 9 | pub mod interrupt; 10 | pub mod log; 11 | pub mod mathematics; 12 | pub mod media; 13 | pub mod option; 14 | pub mod picture; 15 | pub mod range; 16 | pub mod rational; 17 | pub mod time; 18 | 19 | use std::ffi::CStr; 20 | use std::str::from_utf8_unchecked; 21 | 22 | use ffi::*; 23 | 24 | #[inline(always)] 25 | pub fn version() -> u32 { 26 | unsafe { avutil_version() } 27 | } 28 | 29 | #[inline(always)] 30 | pub fn configuration() -> &'static str { 31 | unsafe { from_utf8_unchecked(CStr::from_ptr(avutil_configuration()).to_bytes()) } 32 | } 33 | 34 | #[inline(always)] 35 | pub fn license() -> &'static str { 36 | unsafe { from_utf8_unchecked(CStr::from_ptr(avutil_license()).to_bytes()) } 37 | } 38 | -------------------------------------------------------------------------------- /src/software/resampling/filter.rs: -------------------------------------------------------------------------------- 1 | use ffi::SwrFilterType::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] 5 | pub enum Filter { 6 | Cubic, 7 | BlackmanNuttall, 8 | Kaiser, 9 | } 10 | 11 | impl From for Filter { 12 | fn from(value: SwrFilterType) -> Filter { 13 | match value { 14 | SWR_FILTER_TYPE_CUBIC => Filter::Cubic, 15 | SWR_FILTER_TYPE_BLACKMAN_NUTTALL => Filter::BlackmanNuttall, 16 | SWR_FILTER_TYPE_KAISER => Filter::Kaiser, 17 | } 18 | } 19 | } 20 | 21 | impl From for SwrFilterType { 22 | fn from(value: Filter) -> SwrFilterType { 23 | match value { 24 | Filter::Cubic => SWR_FILTER_TYPE_CUBIC, 25 | Filter::BlackmanNuttall => SWR_FILTER_TYPE_BLACKMAN_NUTTALL, 26 | Filter::Kaiser => SWR_FILTER_TYPE_KAISER, 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/format/context/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod destructor; 2 | pub use self::destructor::Destructor; 3 | 4 | pub mod input; 5 | pub use self::input::Input; 6 | 7 | pub mod output; 8 | pub use self::output::Output; 9 | 10 | #[doc(hidden)] 11 | pub mod common; 12 | 13 | pub enum Context { 14 | Input(Input), 15 | Output(Output), 16 | } 17 | 18 | unsafe impl Send for Context {} 19 | 20 | impl Context { 21 | pub fn is_input(&self) -> bool { 22 | matches!(*self, Context::Input(..)) 23 | } 24 | 25 | pub fn input(self) -> Input { 26 | if let Context::Input(context) = self { 27 | return context; 28 | } 29 | 30 | unreachable!(); 31 | } 32 | 33 | pub fn is_output(&self) -> bool { 34 | matches!(*self, Context::Output(..)) 35 | } 36 | 37 | pub fn output(self) -> Output { 38 | if let Context::Output(context) = self { 39 | return context; 40 | } 41 | 42 | unreachable!(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/format/stream/disposition.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | pub struct Disposition: c_int { 6 | const DEFAULT = AV_DISPOSITION_DEFAULT; 7 | const DUB = AV_DISPOSITION_DUB; 8 | const ORIGINAL = AV_DISPOSITION_ORIGINAL; 9 | const COMMENT = AV_DISPOSITION_COMMENT; 10 | const LYRICS = AV_DISPOSITION_LYRICS; 11 | const KARAOKE = AV_DISPOSITION_KARAOKE; 12 | const FORCED = AV_DISPOSITION_FORCED; 13 | const HEARING_IMPAIRED = AV_DISPOSITION_HEARING_IMPAIRED; 14 | const VISUAL_IMPAIRED = AV_DISPOSITION_VISUAL_IMPAIRED; 15 | const CLEAN_EFFECTS = AV_DISPOSITION_CLEAN_EFFECTS; 16 | const ATTACHED_PIC = AV_DISPOSITION_ATTACHED_PIC; 17 | const CAPTIONS = AV_DISPOSITION_CAPTIONS; 18 | const DESCRIPTIONS = AV_DISPOSITION_DESCRIPTIONS; 19 | const METADATA = AV_DISPOSITION_METADATA; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/software/resampling/extensions.rs: -------------------------------------------------------------------------------- 1 | use super::Context; 2 | use util::format; 3 | use {decoder, frame, ChannelLayout, Error}; 4 | 5 | impl frame::Audio { 6 | #[inline] 7 | pub fn resampler( 8 | &self, 9 | format: format::Sample, 10 | channel_layout: ChannelLayout, 11 | rate: u32, 12 | ) -> Result { 13 | Context::get( 14 | self.format(), 15 | self.channel_layout(), 16 | unsafe { (*self.as_ptr()).sample_rate as u32 }, 17 | format, 18 | channel_layout, 19 | rate, 20 | ) 21 | } 22 | } 23 | 24 | impl decoder::Audio { 25 | #[inline] 26 | pub fn resampler( 27 | &self, 28 | format: format::Sample, 29 | channel_layout: ChannelLayout, 30 | rate: u32, 31 | ) -> Result { 32 | Context::get( 33 | self.format(), 34 | self.channel_layout(), 35 | self.rate(), 36 | format, 37 | channel_layout, 38 | rate, 39 | ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/codec/field_order.rs: -------------------------------------------------------------------------------- 1 | use ffi::AVFieldOrder::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum FieldOrder { 6 | Unknown, 7 | Progressive, 8 | TT, 9 | BB, 10 | TB, 11 | BT, 12 | } 13 | 14 | impl From for FieldOrder { 15 | fn from(value: AVFieldOrder) -> Self { 16 | match value { 17 | AV_FIELD_UNKNOWN => FieldOrder::Unknown, 18 | AV_FIELD_PROGRESSIVE => FieldOrder::Progressive, 19 | AV_FIELD_TT => FieldOrder::TT, 20 | AV_FIELD_BB => FieldOrder::BB, 21 | AV_FIELD_TB => FieldOrder::TB, 22 | AV_FIELD_BT => FieldOrder::BT, 23 | } 24 | } 25 | } 26 | 27 | impl From for AVFieldOrder { 28 | fn from(value: FieldOrder) -> AVFieldOrder { 29 | match value { 30 | FieldOrder::Unknown => AV_FIELD_UNKNOWN, 31 | FieldOrder::Progressive => AV_FIELD_PROGRESSIVE, 32 | FieldOrder::TT => AV_FIELD_TT, 33 | FieldOrder::BB => AV_FIELD_BB, 34 | FieldOrder::TB => AV_FIELD_TB, 35 | FieldOrder::BT => AV_FIELD_BT, 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/codec/packet/borrow.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ptr; 3 | 4 | use super::Ref; 5 | use ffi::*; 6 | use libc::c_int; 7 | 8 | pub struct Borrow<'a> { 9 | packet: AVPacket, 10 | data: &'a [u8], 11 | } 12 | 13 | impl<'a> Borrow<'a> { 14 | pub fn new(data: &[u8]) -> Borrow { 15 | unsafe { 16 | let mut packet: AVPacket = mem::zeroed(); 17 | 18 | packet.data = data.as_ptr() as *mut _; 19 | packet.size = data.len() as c_int; 20 | 21 | Borrow { packet, data } 22 | } 23 | } 24 | 25 | #[inline] 26 | pub fn size(&self) -> usize { 27 | self.packet.size as usize 28 | } 29 | 30 | #[inline] 31 | pub fn data(&self) -> Option<&[u8]> { 32 | Some(self.data) 33 | } 34 | } 35 | 36 | impl<'a> Ref for Borrow<'a> { 37 | fn as_ptr(&self) -> *const AVPacket { 38 | &self.packet 39 | } 40 | } 41 | 42 | impl<'a> Drop for Borrow<'a> { 43 | fn drop(&mut self) { 44 | unsafe { 45 | self.packet.data = ptr::null_mut(); 46 | self.packet.size = 0; 47 | 48 | av_packet_unref(&mut self.packet); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/codec/debug.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | pub struct Debug: c_int { 6 | const PICT_INFO = FF_DEBUG_PICT_INFO; 7 | const RC = FF_DEBUG_RC; 8 | const BITSTREAM = FF_DEBUG_BITSTREAM; 9 | const MB_TYPE = FF_DEBUG_MB_TYPE; 10 | const QP = FF_DEBUG_QP; 11 | #[cfg(not(feature = "ffmpeg_4_0"))] 12 | const MV = FF_DEBUG_MV; 13 | const DCT_COEFF = FF_DEBUG_DCT_COEFF; 14 | const SKIP = FF_DEBUG_SKIP; 15 | const STARTCODE = FF_DEBUG_STARTCODE; 16 | #[cfg(not(feature = "ffmpeg_4_0"))] 17 | const PTS = FF_DEBUG_PTS; 18 | const ER = FF_DEBUG_ER; 19 | const MMCO = FF_DEBUG_MMCO; 20 | const BUGS = FF_DEBUG_BUGS; 21 | #[cfg(not(feature = "ffmpeg_4_0"))] 22 | const VIS_QP = FF_DEBUG_VIS_QP; 23 | #[cfg(not(feature = "ffmpeg_4_0"))] 24 | const VIS_MB_TYPE = FF_DEBUG_VIS_MB_TYPE; 25 | const BUFFERS = FF_DEBUG_BUFFERS; 26 | const THREADS = FF_DEBUG_THREADS; 27 | const NOMC = FF_DEBUG_NOMC; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/codec/flag.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_uint; 3 | 4 | bitflags! { 5 | pub struct Flags: c_uint { 6 | const UNALIGNED = AV_CODEC_FLAG_UNALIGNED; 7 | const QSCALE = AV_CODEC_FLAG_QSCALE; 8 | const _4MV = AV_CODEC_FLAG_4MV; 9 | const OUTPUT_CORRUPT = AV_CODEC_FLAG_OUTPUT_CORRUPT; 10 | const QPEL = AV_CODEC_FLAG_QPEL; 11 | const PASS1 = AV_CODEC_FLAG_PASS1; 12 | const PASS2 = AV_CODEC_FLAG_PASS2; 13 | const GRAY = AV_CODEC_FLAG_GRAY; 14 | const PSNR = AV_CODEC_FLAG_PSNR; 15 | const TRUNCATED = AV_CODEC_FLAG_TRUNCATED; 16 | const INTERLACED_DCT = AV_CODEC_FLAG_INTERLACED_DCT; 17 | const LOW_DELAY = AV_CODEC_FLAG_LOW_DELAY; 18 | const GLOBAL_HEADER = AV_CODEC_FLAG_GLOBAL_HEADER; 19 | const BITEXACT = AV_CODEC_FLAG_BITEXACT; 20 | const AC_PRED = AV_CODEC_FLAG_AC_PRED; 21 | const LOOP_FILTER = AV_CODEC_FLAG_LOOP_FILTER; 22 | const INTERLACED_ME = AV_CODEC_FLAG_INTERLACED_ME; 23 | const CLOSED_GOP = AV_CODEC_FLAG_CLOSED_GOP; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/software/scaling/color_space.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum ColorSpace { 6 | Default, 7 | 8 | ITU709, 9 | FCC, 10 | ITU601, 11 | ITU624, 12 | SMPTE170M, 13 | SMPTE240M, 14 | } 15 | 16 | impl From for ColorSpace { 17 | fn from(value: c_int) -> ColorSpace { 18 | match value { 19 | SWS_CS_ITU709 => ColorSpace::ITU709, 20 | SWS_CS_FCC => ColorSpace::FCC, 21 | SWS_CS_DEFAULT => ColorSpace::Default, 22 | SWS_CS_SMPTE240M => ColorSpace::SMPTE240M, 23 | 24 | _ => ColorSpace::Default, 25 | } 26 | } 27 | } 28 | 29 | impl From for c_int { 30 | fn from(value: ColorSpace) -> c_int { 31 | match value { 32 | ColorSpace::Default => SWS_CS_DEFAULT, 33 | ColorSpace::ITU709 => SWS_CS_ITU709, 34 | ColorSpace::FCC => SWS_CS_FCC, 35 | ColorSpace::ITU601 => SWS_CS_ITU601, 36 | ColorSpace::ITU624 => SWS_CS_ITU624, 37 | ColorSpace::SMPTE170M => SWS_CS_SMPTE170M, 38 | ColorSpace::SMPTE240M => SWS_CS_SMPTE240M, 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/codec/compliance.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Compliance { 6 | VeryStrict, 7 | Strict, 8 | Normal, 9 | Unofficial, 10 | Experimental, 11 | } 12 | 13 | impl From for Compliance { 14 | fn from(value: c_int) -> Self { 15 | match value { 16 | FF_COMPLIANCE_VERY_STRICT => Compliance::VeryStrict, 17 | FF_COMPLIANCE_STRICT => Compliance::Strict, 18 | FF_COMPLIANCE_NORMAL => Compliance::Normal, 19 | FF_COMPLIANCE_UNOFFICIAL => Compliance::Unofficial, 20 | FF_COMPLIANCE_EXPERIMENTAL => Compliance::Experimental, 21 | 22 | _ => Compliance::Normal, 23 | } 24 | } 25 | } 26 | 27 | impl From for c_int { 28 | fn from(value: Compliance) -> c_int { 29 | match value { 30 | Compliance::VeryStrict => FF_COMPLIANCE_VERY_STRICT, 31 | Compliance::Strict => FF_COMPLIANCE_STRICT, 32 | Compliance::Normal => FF_COMPLIANCE_NORMAL, 33 | Compliance::Unofficial => FF_COMPLIANCE_UNOFFICIAL, 34 | Compliance::Experimental => FF_COMPLIANCE_EXPERIMENTAL, 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/codec/capabilities.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_uint; 3 | 4 | bitflags! { 5 | pub struct Capabilities: c_uint { 6 | const DRAW_HORIZ_BAND = AV_CODEC_CAP_DRAW_HORIZ_BAND; 7 | const DR1 = AV_CODEC_CAP_DR1; 8 | const TRUNCATED = AV_CODEC_CAP_TRUNCATED; 9 | const DELAY = AV_CODEC_CAP_DELAY; 10 | const SMALL_LAST_FRAME = AV_CODEC_CAP_SMALL_LAST_FRAME; 11 | #[cfg(not(feature = "ffmpeg_4_0"))] 12 | const HWACCEL_VDPAU = AV_CODEC_CAP_HWACCEL_VDPAU; 13 | const SUBFRAMES = AV_CODEC_CAP_SUBFRAMES; 14 | const EXPERIMENTAL = AV_CODEC_CAP_EXPERIMENTAL; 15 | const CHANNEL_CONF = AV_CODEC_CAP_CHANNEL_CONF; 16 | const FRAME_THREADS = AV_CODEC_CAP_FRAME_THREADS; 17 | const SLICE_THREADS = AV_CODEC_CAP_SLICE_THREADS; 18 | const PARAM_CHANGE = AV_CODEC_CAP_PARAM_CHANGE; 19 | const AUTO_THREADS = AV_CODEC_CAP_AUTO_THREADS; 20 | const VARIABLE_FRAME_SIZE = AV_CODEC_CAP_VARIABLE_FRAME_SIZE; 21 | const INTRA_ONLY = AV_CODEC_CAP_INTRA_ONLY; 22 | const LOSSLESS = AV_CODEC_CAP_LOSSLESS; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/util/mathematics/rounding.rs: -------------------------------------------------------------------------------- 1 | use ffi::AVRounding::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Rounding { 6 | Zero, 7 | Infinity, 8 | Down, 9 | Up, 10 | NearInfinity, 11 | PassMinMax, 12 | } 13 | 14 | impl From for Rounding { 15 | #[inline(always)] 16 | fn from(value: AVRounding) -> Self { 17 | match value { 18 | AV_ROUND_ZERO => Rounding::Zero, 19 | AV_ROUND_INF => Rounding::Infinity, 20 | AV_ROUND_DOWN => Rounding::Down, 21 | AV_ROUND_UP => Rounding::Up, 22 | AV_ROUND_NEAR_INF => Rounding::NearInfinity, 23 | AV_ROUND_PASS_MINMAX => Rounding::PassMinMax, 24 | } 25 | } 26 | } 27 | 28 | impl From for AVRounding { 29 | #[inline(always)] 30 | fn from(value: Rounding) -> AVRounding { 31 | match value { 32 | Rounding::Zero => AV_ROUND_ZERO, 33 | Rounding::Infinity => AV_ROUND_INF, 34 | Rounding::Down => AV_ROUND_DOWN, 35 | Rounding::Up => AV_ROUND_UP, 36 | Rounding::NearInfinity => AV_ROUND_NEAR_INF, 37 | Rounding::PassMinMax => AV_ROUND_PASS_MINMAX, 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/filter/context/source.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use super::Context; 4 | use ffi::*; 5 | use {Error, Frame}; 6 | 7 | pub struct Source<'a> { 8 | ctx: &'a mut Context<'a>, 9 | } 10 | 11 | impl<'a> Source<'a> { 12 | pub unsafe fn wrap<'b>(ctx: &'b mut Context<'b>) -> Source<'b> { 13 | Source { ctx } 14 | } 15 | } 16 | 17 | impl<'a> Source<'a> { 18 | pub fn failed_requests(&self) -> usize { 19 | unsafe { av_buffersrc_get_nb_failed_requests(self.ctx.as_ptr() as *mut _) as usize } 20 | } 21 | 22 | pub fn add(&mut self, frame: &Frame) -> Result<(), Error> { 23 | unsafe { 24 | match av_buffersrc_add_frame(self.ctx.as_mut_ptr(), frame.as_ptr() as *mut _) { 25 | 0 => Ok(()), 26 | e => Err(Error::from(e)), 27 | } 28 | } 29 | } 30 | 31 | pub fn flush(&mut self) -> Result<(), Error> { 32 | unsafe { self.add(&Frame::wrap(ptr::null_mut())) } 33 | } 34 | 35 | pub fn close(&mut self, pts: i64) -> Result<(), Error> { 36 | unsafe { 37 | match av_buffersrc_close(self.ctx.as_mut_ptr(), pts, 0) { 38 | 0 => Ok(()), 39 | e => Err(Error::from(e)), 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/filter/pad.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::marker::PhantomData; 3 | use std::str::from_utf8_unchecked; 4 | 5 | use ffi::*; 6 | use media; 7 | 8 | pub struct Pad<'a> { 9 | ptr: *const AVFilterPad, 10 | idx: isize, 11 | 12 | _marker: PhantomData<&'a ()>, 13 | } 14 | 15 | impl<'a> Pad<'a> { 16 | pub unsafe fn wrap(ptr: *const AVFilterPad, idx: isize) -> Self { 17 | Pad { 18 | ptr, 19 | idx, 20 | _marker: PhantomData, 21 | } 22 | } 23 | 24 | pub unsafe fn as_ptr(&self) -> *const AVFilterPad { 25 | self.ptr 26 | } 27 | 28 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFilterPad { 29 | self.ptr as *mut _ 30 | } 31 | } 32 | 33 | impl<'a> Pad<'a> { 34 | pub fn name(&self) -> Option<&str> { 35 | unsafe { 36 | let ptr = avfilter_pad_get_name(self.ptr, self.idx as i32); 37 | 38 | if ptr.is_null() { 39 | None 40 | } else { 41 | Some(from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) 42 | } 43 | } 44 | } 45 | 46 | pub fn medium(&self) -> media::Type { 47 | unsafe { media::Type::from(avfilter_pad_get_type(self.ptr, self.idx as i32)) } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/util/media.rs: -------------------------------------------------------------------------------- 1 | use ffi::AVMediaType::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Type { 6 | Unknown, 7 | Video, 8 | Audio, 9 | Data, 10 | Subtitle, 11 | Attachment, 12 | } 13 | 14 | impl From for Type { 15 | #[inline(always)] 16 | fn from(value: AVMediaType) -> Self { 17 | match value { 18 | AVMEDIA_TYPE_UNKNOWN => Type::Unknown, 19 | AVMEDIA_TYPE_VIDEO => Type::Video, 20 | AVMEDIA_TYPE_AUDIO => Type::Audio, 21 | AVMEDIA_TYPE_DATA => Type::Data, 22 | AVMEDIA_TYPE_SUBTITLE => Type::Subtitle, 23 | AVMEDIA_TYPE_ATTACHMENT => Type::Attachment, 24 | AVMEDIA_TYPE_NB => Type::Unknown, 25 | } 26 | } 27 | } 28 | 29 | impl From for AVMediaType { 30 | #[inline(always)] 31 | fn from(value: Type) -> AVMediaType { 32 | match value { 33 | Type::Unknown => AVMEDIA_TYPE_UNKNOWN, 34 | Type::Video => AVMEDIA_TYPE_VIDEO, 35 | Type::Audio => AVMEDIA_TYPE_AUDIO, 36 | Type::Data => AVMEDIA_TYPE_DATA, 37 | Type::Subtitle => AVMEDIA_TYPE_SUBTITLE, 38 | Type::Attachment => AVMEDIA_TYPE_ATTACHMENT, 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/filter/context/sink.rs: -------------------------------------------------------------------------------- 1 | use super::Context; 2 | use ffi::*; 3 | use libc::c_int; 4 | use {Error, Frame}; 5 | 6 | pub struct Sink<'a> { 7 | ctx: &'a mut Context<'a>, 8 | } 9 | 10 | impl<'a> Sink<'a> { 11 | pub unsafe fn wrap<'b>(ctx: &'b mut Context<'b>) -> Sink<'b> { 12 | Sink { ctx } 13 | } 14 | } 15 | 16 | impl<'a> Sink<'a> { 17 | pub fn frame(&mut self, frame: &mut Frame) -> Result<(), Error> { 18 | unsafe { 19 | match av_buffersink_get_frame(self.ctx.as_mut_ptr(), frame.as_mut_ptr()) { 20 | n if n >= 0 => Ok(()), 21 | e => Err(Error::from(e)), 22 | } 23 | } 24 | } 25 | 26 | pub fn samples(&mut self, frame: &mut Frame, samples: usize) -> Result<(), Error> { 27 | unsafe { 28 | match av_buffersink_get_samples( 29 | self.ctx.as_mut_ptr(), 30 | frame.as_mut_ptr(), 31 | samples as c_int, 32 | ) { 33 | n if n >= 0 => Ok(()), 34 | e => Err(Error::from(e)), 35 | } 36 | } 37 | } 38 | 39 | pub fn set_frame_size(&mut self, value: u32) { 40 | unsafe { 41 | av_buffersink_set_frame_size(self.ctx.as_mut_ptr(), value); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/codec/decoder/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod decoder; 2 | pub use self::decoder::Decoder; 3 | 4 | pub mod video; 5 | pub use self::video::Video; 6 | 7 | pub mod audio; 8 | pub use self::audio::Audio; 9 | 10 | pub mod subtitle; 11 | pub use self::subtitle::Subtitle; 12 | 13 | pub mod slice; 14 | 15 | pub mod conceal; 16 | pub use self::conceal::Conceal; 17 | 18 | pub mod check; 19 | pub use self::check::Check; 20 | 21 | pub mod opened; 22 | pub use self::opened::Opened; 23 | 24 | use std::ffi::CString; 25 | 26 | use codec::Context; 27 | use codec::Id; 28 | use ffi::*; 29 | use Codec; 30 | 31 | pub fn new() -> Decoder { 32 | Context::new().decoder() 33 | } 34 | 35 | pub fn find(id: Id) -> Option { 36 | unsafe { 37 | let ptr = avcodec_find_decoder(id.into()) as *mut AVCodec; 38 | 39 | if ptr.is_null() { 40 | None 41 | } else { 42 | Some(Codec::wrap(ptr)) 43 | } 44 | } 45 | } 46 | 47 | pub fn find_by_name(name: &str) -> Option { 48 | unsafe { 49 | let name = CString::new(name).unwrap(); 50 | let ptr = avcodec_find_decoder_by_name(name.as_ptr()) as *mut AVCodec; 51 | 52 | if ptr.is_null() { 53 | None 54 | } else { 55 | Some(Codec::wrap(ptr)) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/software/scaling/flag.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | pub struct Flags: c_int { 6 | const FAST_BILINEAR = SWS_FAST_BILINEAR; 7 | const BILINEAR = SWS_BILINEAR; 8 | const BICUBIC = SWS_BICUBIC; 9 | const X = SWS_X; 10 | const POINT = SWS_POINT; 11 | const AREA = SWS_AREA; 12 | const BICUBLIN = SWS_BICUBLIN; 13 | const GAUSS = SWS_GAUSS; 14 | const SINC = SWS_SINC; 15 | const LANCZOS = SWS_LANCZOS; 16 | const SPLINE = SWS_SPLINE; 17 | const SRC_V_CHR_DROP_MASK = SWS_SRC_V_CHR_DROP_MASK; 18 | const SRC_V_CHR_DROP_SHIFT = SWS_SRC_V_CHR_DROP_SHIFT; 19 | const PARAM_DEFAULT = SWS_PARAM_DEFAULT; 20 | const PRINT_INFO = SWS_PRINT_INFO; 21 | const FULL_CHR_H_INT = SWS_FULL_CHR_H_INT; 22 | const FULL_CHR_H_INP = SWS_FULL_CHR_H_INP; 23 | const DIRECT_BGR = SWS_DIRECT_BGR; 24 | const ACCURATE_RND = SWS_ACCURATE_RND; 25 | const BITEXACT = SWS_BITEXACT; 26 | const ERROR_DIFFUSION = SWS_ERROR_DIFFUSION; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/codec/discard.rs: -------------------------------------------------------------------------------- 1 | use ffi::AVDiscard::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Discard { 6 | None, 7 | Default, 8 | NonReference, 9 | Bidirectional, 10 | NonIntra, 11 | NonKey, 12 | All, 13 | } 14 | 15 | impl From for Discard { 16 | fn from(value: AVDiscard) -> Self { 17 | match value { 18 | AVDISCARD_NONE => Discard::None, 19 | AVDISCARD_DEFAULT => Discard::Default, 20 | AVDISCARD_NONREF => Discard::NonReference, 21 | AVDISCARD_BIDIR => Discard::Bidirectional, 22 | AVDISCARD_NONINTRA => Discard::NonIntra, 23 | AVDISCARD_NONKEY => Discard::NonKey, 24 | AVDISCARD_ALL => Discard::All, 25 | } 26 | } 27 | } 28 | 29 | impl From for AVDiscard { 30 | fn from(value: Discard) -> AVDiscard { 31 | match value { 32 | Discard::None => AVDISCARD_NONE, 33 | Discard::Default => AVDISCARD_DEFAULT, 34 | Discard::NonReference => AVDISCARD_NONREF, 35 | Discard::Bidirectional => AVDISCARD_BIDIR, 36 | Discard::NonIntra => AVDISCARD_NONINTRA, 37 | Discard::NonKey => AVDISCARD_NONKEY, 38 | Discard::All => AVDISCARD_ALL, 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/util/dictionary/iter.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{CStr, CString}; 2 | use std::marker::PhantomData; 3 | use std::ptr; 4 | use std::str::from_utf8_unchecked; 5 | 6 | use ffi::*; 7 | 8 | pub struct Iter<'a> { 9 | ptr: *const AVDictionary, 10 | cur: *mut AVDictionaryEntry, 11 | 12 | _marker: PhantomData<&'a ()>, 13 | } 14 | 15 | impl<'a> Iter<'a> { 16 | pub fn new(dictionary: *const AVDictionary) -> Self { 17 | Iter { 18 | ptr: dictionary, 19 | cur: ptr::null_mut(), 20 | 21 | _marker: PhantomData, 22 | } 23 | } 24 | } 25 | 26 | impl<'a> Iterator for Iter<'a> { 27 | type Item = (&'a str, &'a str); 28 | 29 | fn next(&mut self) -> Option<::Item> { 30 | unsafe { 31 | let empty = CString::new("").unwrap(); 32 | let entry = av_dict_get(self.ptr, empty.as_ptr(), self.cur, AV_DICT_IGNORE_SUFFIX); 33 | 34 | if !entry.is_null() { 35 | let key = from_utf8_unchecked(CStr::from_ptr((*entry).key).to_bytes()); 36 | let val = from_utf8_unchecked(CStr::from_ptr((*entry).value).to_bytes()); 37 | 38 | self.cur = entry; 39 | 40 | Some((key, val)) 41 | } else { 42 | None 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/util/color/range.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::str::from_utf8_unchecked; 3 | 4 | use ffi::AVColorRange::*; 5 | use ffi::*; 6 | 7 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 8 | pub enum Range { 9 | Unspecified, 10 | MPEG, 11 | JPEG, 12 | } 13 | 14 | impl Range { 15 | pub fn name(&self) -> Option<&'static str> { 16 | if *self == Range::Unspecified { 17 | return None; 18 | } 19 | unsafe { 20 | let ptr = av_color_range_name((*self).into()); 21 | ptr.as_ref() 22 | .map(|ptr| from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) 23 | } 24 | } 25 | } 26 | 27 | impl From for Range { 28 | fn from(value: AVColorRange) -> Self { 29 | match value { 30 | AVCOL_RANGE_UNSPECIFIED => Range::Unspecified, 31 | AVCOL_RANGE_MPEG => Range::MPEG, 32 | AVCOL_RANGE_JPEG => Range::JPEG, 33 | AVCOL_RANGE_NB => Range::Unspecified, 34 | } 35 | } 36 | } 37 | 38 | impl From for AVColorRange { 39 | fn from(value: Range) -> AVColorRange { 40 | match value { 41 | Range::Unspecified => AVCOL_RANGE_UNSPECIFIED, 42 | Range::MPEG => AVCOL_RANGE_MPEG, 43 | Range::JPEG => AVCOL_RANGE_JPEG, 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/util/picture.rs: -------------------------------------------------------------------------------- 1 | use ffi::AVPictureType::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Type { 6 | None, 7 | I, 8 | P, 9 | B, 10 | S, 11 | SI, 12 | SP, 13 | BI, 14 | } 15 | 16 | impl From for Type { 17 | #[inline(always)] 18 | fn from(value: AVPictureType) -> Type { 19 | match value { 20 | AV_PICTURE_TYPE_NONE => Type::None, 21 | AV_PICTURE_TYPE_I => Type::I, 22 | AV_PICTURE_TYPE_P => Type::P, 23 | AV_PICTURE_TYPE_B => Type::B, 24 | AV_PICTURE_TYPE_S => Type::S, 25 | AV_PICTURE_TYPE_SI => Type::SI, 26 | AV_PICTURE_TYPE_SP => Type::SP, 27 | AV_PICTURE_TYPE_BI => Type::BI, 28 | } 29 | } 30 | } 31 | 32 | impl From for AVPictureType { 33 | #[inline(always)] 34 | fn from(value: Type) -> AVPictureType { 35 | match value { 36 | Type::None => AV_PICTURE_TYPE_NONE, 37 | Type::I => AV_PICTURE_TYPE_I, 38 | Type::P => AV_PICTURE_TYPE_P, 39 | Type::B => AV_PICTURE_TYPE_B, 40 | Type::S => AV_PICTURE_TYPE_S, 41 | Type::SI => AV_PICTURE_TYPE_SI, 42 | Type::SP => AV_PICTURE_TYPE_SP, 43 | Type::BI => AV_PICTURE_TYPE_BI, 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/device/input.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use ffi::*; 4 | use format; 5 | use Format; 6 | 7 | pub struct AudioIter(*mut AVInputFormat); 8 | 9 | impl Iterator for AudioIter { 10 | type Item = Format; 11 | 12 | fn next(&mut self) -> Option<::Item> { 13 | unsafe { 14 | let ptr = av_input_audio_device_next(self.0) as *mut AVInputFormat; 15 | 16 | if ptr.is_null() && !self.0.is_null() { 17 | None 18 | } else { 19 | self.0 = ptr; 20 | 21 | Some(Format::Input(format::Input::wrap(ptr))) 22 | } 23 | } 24 | } 25 | } 26 | 27 | pub fn audio() -> AudioIter { 28 | AudioIter(ptr::null_mut()) 29 | } 30 | 31 | pub struct VideoIter(*mut AVInputFormat); 32 | 33 | impl Iterator for VideoIter { 34 | type Item = Format; 35 | 36 | fn next(&mut self) -> Option<::Item> { 37 | unsafe { 38 | let ptr = av_input_video_device_next(self.0) as *mut AVInputFormat; 39 | 40 | if ptr.is_null() && !self.0.is_null() { 41 | None 42 | } else { 43 | self.0 = ptr; 44 | 45 | Some(Format::Input(format::Input::wrap(ptr))) 46 | } 47 | } 48 | } 49 | } 50 | 51 | pub fn video() -> VideoIter { 52 | VideoIter(ptr::null_mut()) 53 | } 54 | -------------------------------------------------------------------------------- /src/format/format/flag.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | pub struct Flags: c_int { 6 | const NO_FILE = AVFMT_NOFILE; 7 | const NEED_NUMBER = AVFMT_NEEDNUMBER; 8 | const SHOW_IDS = AVFMT_SHOW_IDS; 9 | #[cfg(not(feature = "ffmpeg_4_0"))] 10 | const RAW_PICTURE = AVFMT_RAWPICTURE; 11 | const GLOBAL_HEADER = AVFMT_GLOBALHEADER; 12 | const NO_TIMESTAMPS = AVFMT_NOTIMESTAMPS; 13 | const GENERIC_INDEX = AVFMT_GENERIC_INDEX; 14 | const TS_DISCONT = AVFMT_TS_DISCONT; 15 | const VARIABLE_FPS = AVFMT_VARIABLE_FPS; 16 | const NO_DIMENSIONS = AVFMT_NODIMENSIONS; 17 | const NO_STREAMS = AVFMT_NOSTREAMS; 18 | const NO_BINSEARCH = AVFMT_NOBINSEARCH; 19 | const NO_GENSEARCH = AVFMT_NOGENSEARCH; 20 | const NO_BYTE_SEEK = AVFMT_NO_BYTE_SEEK; 21 | const ALLOW_FLUSH = AVFMT_ALLOW_FLUSH; 22 | const TS_NONSTRICT = AVFMT_TS_NONSTRICT; 23 | const TS_NEGATIVE = AVFMT_TS_NEGATIVE; 24 | const SEEK_TO_PTS = AVFMT_SEEK_TO_PTS; 25 | } 26 | } 27 | 28 | bitflags! { 29 | pub struct SeekFlags: c_int { 30 | const BACKWARD = AVSEEK_FLAG_BACKWARD; 31 | const BYTE = AVSEEK_FLAG_BYTE; 32 | const ANY = AVSEEK_FLAG_ANY; 33 | const FRAME = AVSEEK_FLAG_FRAME; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/format/format/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod flag; 2 | pub use self::flag::Flags; 3 | pub use self::flag::SeekFlags; 4 | 5 | mod input; 6 | pub use self::input::Input; 7 | 8 | mod output; 9 | pub use self::output::Output; 10 | 11 | #[cfg(not(feature = "ffmpeg_5_0"))] 12 | mod iter; 13 | #[cfg(not(feature = "ffmpeg_5_0"))] 14 | pub use self::iter::Iter; 15 | 16 | pub enum Format { 17 | Input(Input), 18 | Output(Output), 19 | } 20 | 21 | impl Format { 22 | pub fn name(&self) -> &str { 23 | match *self { 24 | Format::Input(ref f) => f.name(), 25 | Format::Output(ref f) => f.name(), 26 | } 27 | } 28 | 29 | pub fn description(&self) -> &str { 30 | match *self { 31 | Format::Input(ref f) => f.description(), 32 | Format::Output(ref f) => f.description(), 33 | } 34 | } 35 | 36 | pub fn extensions(&self) -> Vec<&str> { 37 | match *self { 38 | Format::Input(ref f) => f.extensions(), 39 | Format::Output(ref f) => f.extensions(), 40 | } 41 | } 42 | 43 | pub fn mime_types(&self) -> Vec<&str> { 44 | match *self { 45 | Format::Input(ref f) => f.mime_types(), 46 | Format::Output(ref f) => f.mime_types(), 47 | } 48 | } 49 | } 50 | 51 | #[cfg(not(feature = "ffmpeg_5_0"))] 52 | pub fn list() -> Iter { 53 | Iter::new() 54 | } 55 | -------------------------------------------------------------------------------- /src/codec/decoder/subtitle.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | use ffi::*; 4 | use libc::c_int; 5 | 6 | use super::Opened; 7 | use codec::Context; 8 | use {packet, Error}; 9 | 10 | pub struct Subtitle(pub Opened); 11 | 12 | impl Subtitle { 13 | pub fn decode( 14 | &mut self, 15 | packet: &P, 16 | out: &mut ::Subtitle, 17 | ) -> Result { 18 | unsafe { 19 | let mut got: c_int = 0; 20 | 21 | match avcodec_decode_subtitle2( 22 | self.as_mut_ptr(), 23 | out.as_mut_ptr(), 24 | &mut got, 25 | packet.as_ptr() as *mut _, 26 | ) { 27 | e if e < 0 => Err(Error::from(e)), 28 | _ => Ok(got != 0), 29 | } 30 | } 31 | } 32 | } 33 | 34 | impl Deref for Subtitle { 35 | type Target = Opened; 36 | 37 | fn deref(&self) -> &::Target { 38 | &self.0 39 | } 40 | } 41 | 42 | impl DerefMut for Subtitle { 43 | fn deref_mut(&mut self) -> &mut ::Target { 44 | &mut self.0 45 | } 46 | } 47 | 48 | impl AsRef for Subtitle { 49 | fn as_ref(&self) -> &Context { 50 | self 51 | } 52 | } 53 | 54 | impl AsMut for Subtitle { 55 | fn as_mut(&mut self) -> &mut Context { 56 | &mut self.0 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/util/mathematics/rescale.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use {Rational, Rounding}; 3 | 4 | pub const TIME_BASE: Rational = Rational(AV_TIME_BASE_Q.num, AV_TIME_BASE_Q.den); 5 | 6 | pub trait Rescale { 7 | fn rescale(&self, source: S, destination: D) -> i64 8 | where 9 | S: Into, 10 | D: Into; 11 | 12 | fn rescale_with(&self, source: S, destination: D, rounding: Rounding) -> i64 13 | where 14 | S: Into, 15 | D: Into; 16 | } 17 | 18 | impl + Clone> Rescale for T { 19 | fn rescale(&self, source: S, destination: D) -> i64 20 | where 21 | S: Into, 22 | D: Into, 23 | { 24 | unsafe { 25 | av_rescale_q( 26 | self.clone().into(), 27 | source.into().into(), 28 | destination.into().into(), 29 | ) 30 | } 31 | } 32 | 33 | fn rescale_with(&self, source: S, destination: D, rounding: Rounding) -> i64 34 | where 35 | S: Into, 36 | D: Into, 37 | { 38 | unsafe { 39 | av_rescale_q_rnd( 40 | self.clone().into(), 41 | source.into().into(), 42 | destination.into().into(), 43 | rounding.into(), 44 | ) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/codec/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod flag; 2 | pub use self::flag::Flags; 3 | 4 | pub mod id; 5 | pub use self::id::Id; 6 | 7 | pub mod packet; 8 | 9 | pub mod subtitle; 10 | 11 | #[cfg(not(feature = "ffmpeg_5_0"))] 12 | pub mod picture; 13 | 14 | pub mod discard; 15 | 16 | pub mod context; 17 | pub use self::context::Context; 18 | 19 | pub mod capabilities; 20 | pub use self::capabilities::Capabilities; 21 | 22 | pub mod codec; 23 | 24 | pub mod parameters; 25 | pub use self::parameters::Parameters; 26 | 27 | pub mod video; 28 | pub use self::video::Video; 29 | 30 | pub mod audio; 31 | pub use self::audio::Audio; 32 | 33 | pub mod audio_service; 34 | pub mod field_order; 35 | 36 | pub mod compliance; 37 | pub use self::compliance::Compliance; 38 | 39 | pub mod debug; 40 | pub use self::debug::Debug; 41 | 42 | pub mod profile; 43 | pub use self::profile::Profile; 44 | 45 | pub mod threading; 46 | 47 | pub mod decoder; 48 | pub mod encoder; 49 | pub mod traits; 50 | 51 | use std::ffi::CStr; 52 | use std::str::from_utf8_unchecked; 53 | 54 | use ffi::*; 55 | 56 | pub fn version() -> u32 { 57 | unsafe { avcodec_version() } 58 | } 59 | 60 | pub fn configuration() -> &'static str { 61 | unsafe { from_utf8_unchecked(CStr::from_ptr(avcodec_configuration()).to_bytes()) } 62 | } 63 | 64 | pub fn license() -> &'static str { 65 | unsafe { from_utf8_unchecked(CStr::from_ptr(avcodec_license()).to_bytes()) } 66 | } 67 | -------------------------------------------------------------------------------- /src/device/output.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use ffi::*; 4 | use format; 5 | use Format; 6 | 7 | pub struct AudioIter(*mut AVOutputFormat); 8 | 9 | impl Iterator for AudioIter { 10 | type Item = Format; 11 | 12 | fn next(&mut self) -> Option<::Item> { 13 | unsafe { 14 | let ptr = av_output_audio_device_next(self.0) as *mut AVOutputFormat; 15 | 16 | if ptr.is_null() && !self.0.is_null() { 17 | None 18 | } else { 19 | self.0 = ptr as *mut AVOutputFormat; 20 | 21 | Some(Format::Output(format::Output::wrap(ptr))) 22 | } 23 | } 24 | } 25 | } 26 | 27 | pub fn audio() -> AudioIter { 28 | AudioIter(ptr::null_mut()) 29 | } 30 | 31 | pub struct VideoIter(*mut AVOutputFormat); 32 | 33 | impl Iterator for VideoIter { 34 | type Item = Format; 35 | 36 | fn next(&mut self) -> Option<::Item> { 37 | unsafe { 38 | let ptr = av_output_video_device_next(self.0) as *mut AVOutputFormat; 39 | 40 | if ptr.is_null() && !self.0.is_null() { 41 | None 42 | } else { 43 | self.0 = ptr as *mut AVOutputFormat; 44 | 45 | Some(Format::Output(format::Output::wrap(ptr))) 46 | } 47 | } 48 | } 49 | } 50 | 51 | pub fn video() -> VideoIter { 52 | VideoIter(ptr::null_mut()) 53 | } 54 | -------------------------------------------------------------------------------- /src/software/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "software-scaling")] 2 | pub mod scaling; 3 | 4 | #[cfg(feature = "software-scaling")] 5 | #[inline] 6 | pub fn scaler( 7 | format: ::format::Pixel, 8 | flags: scaling::Flags, 9 | (in_width, in_height): (u32, u32), 10 | (out_width, out_height): (u32, u32), 11 | ) -> Result { 12 | scaling::Context::get( 13 | format, in_width, in_height, format, out_width, out_height, flags, 14 | ) 15 | } 16 | 17 | #[cfg(feature = "software-scaling")] 18 | #[inline] 19 | pub fn converter( 20 | (width, height): (u32, u32), 21 | input: ::format::Pixel, 22 | output: ::format::Pixel, 23 | ) -> Result { 24 | scaling::Context::get( 25 | input, 26 | width, 27 | height, 28 | output, 29 | width, 30 | height, 31 | scaling::flag::Flags::FAST_BILINEAR, 32 | ) 33 | } 34 | 35 | #[cfg(feature = "software-resampling")] 36 | pub mod resampling; 37 | 38 | #[cfg(feature = "software-resampling")] 39 | #[inline] 40 | pub fn resampler( 41 | (in_format, in_layout, in_rate): (::format::Sample, ::ChannelLayout, u32), 42 | (out_format, out_layout, out_rate): (::format::Sample, ::ChannelLayout, u32), 43 | ) -> Result { 44 | resampling::Context::get( 45 | in_format, in_layout, in_rate, out_format, out_layout, out_rate, 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /src/util/chroma/location.rs: -------------------------------------------------------------------------------- 1 | use ffi::AVChromaLocation::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Location { 6 | Unspecified, 7 | Left, 8 | Center, 9 | TopLeft, 10 | Top, 11 | BottomLeft, 12 | Bottom, 13 | } 14 | 15 | impl From for Location { 16 | fn from(value: AVChromaLocation) -> Self { 17 | match value { 18 | AVCHROMA_LOC_UNSPECIFIED => Location::Unspecified, 19 | AVCHROMA_LOC_LEFT => Location::Left, 20 | AVCHROMA_LOC_CENTER => Location::Center, 21 | AVCHROMA_LOC_TOPLEFT => Location::TopLeft, 22 | AVCHROMA_LOC_TOP => Location::Top, 23 | AVCHROMA_LOC_BOTTOMLEFT => Location::BottomLeft, 24 | AVCHROMA_LOC_BOTTOM => Location::Bottom, 25 | AVCHROMA_LOC_NB => Location::Unspecified, 26 | } 27 | } 28 | } 29 | 30 | impl From for AVChromaLocation { 31 | fn from(value: Location) -> AVChromaLocation { 32 | match value { 33 | Location::Unspecified => AVCHROMA_LOC_UNSPECIFIED, 34 | Location::Left => AVCHROMA_LOC_LEFT, 35 | Location::Center => AVCHROMA_LOC_CENTER, 36 | Location::TopLeft => AVCHROMA_LOC_TOPLEFT, 37 | Location::Top => AVCHROMA_LOC_TOP, 38 | Location::BottomLeft => AVCHROMA_LOC_BOTTOMLEFT, 39 | Location::Bottom => AVCHROMA_LOC_BOTTOM, 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/codec/encoder/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod encoder; 2 | pub use self::encoder::Encoder; 3 | 4 | pub mod video; 5 | pub use self::video::Encoder as Video; 6 | 7 | pub mod audio; 8 | pub use self::audio::Encoder as Audio; 9 | 10 | pub mod subtitle; 11 | pub use self::subtitle::Encoder as Subtitle; 12 | 13 | pub mod motion_estimation; 14 | pub use self::motion_estimation::MotionEstimation; 15 | 16 | #[cfg(not(feature = "ffmpeg_5_0"))] 17 | pub mod prediction; 18 | #[cfg(not(feature = "ffmpeg_5_0"))] 19 | pub use self::prediction::Prediction; 20 | 21 | pub mod comparison; 22 | pub use self::comparison::Comparison; 23 | 24 | pub mod decision; 25 | pub use self::decision::Decision; 26 | 27 | use std::ffi::CString; 28 | 29 | use codec::Context; 30 | use codec::Id; 31 | use ffi::*; 32 | use Codec; 33 | 34 | pub fn new() -> Encoder { 35 | Context::new().encoder() 36 | } 37 | 38 | pub fn find(id: Id) -> Option { 39 | unsafe { 40 | let ptr = avcodec_find_encoder(id.into()) as *mut AVCodec; 41 | 42 | if ptr.is_null() { 43 | None 44 | } else { 45 | Some(Codec::wrap(ptr)) 46 | } 47 | } 48 | } 49 | 50 | pub fn find_by_name(name: &str) -> Option { 51 | unsafe { 52 | let name = CString::new(name).unwrap(); 53 | let ptr = avcodec_find_encoder_by_name(name.as_ptr()) as *mut AVCodec; 54 | 55 | if ptr.is_null() { 56 | None 57 | } else { 58 | Some(Codec::wrap(ptr)) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/util/dictionary/mutable.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CString; 2 | use std::fmt; 3 | use std::marker::PhantomData; 4 | use std::ops::Deref; 5 | 6 | use super::immutable; 7 | use ffi::*; 8 | 9 | pub struct Ref<'a> { 10 | ptr: *mut AVDictionary, 11 | imm: immutable::Ref<'a>, 12 | 13 | _marker: PhantomData<&'a ()>, 14 | } 15 | 16 | impl<'a> Ref<'a> { 17 | pub unsafe fn wrap(ptr: *mut AVDictionary) -> Self { 18 | Ref { 19 | ptr, 20 | imm: immutable::Ref::wrap(ptr), 21 | _marker: PhantomData, 22 | } 23 | } 24 | 25 | pub unsafe fn as_mut_ptr(&self) -> *mut AVDictionary { 26 | self.ptr 27 | } 28 | } 29 | 30 | impl<'a> Ref<'a> { 31 | pub fn set(&mut self, key: &str, value: &str) { 32 | unsafe { 33 | let key = CString::new(key).unwrap(); 34 | let value = CString::new(value).unwrap(); 35 | let mut ptr = self.as_mut_ptr(); 36 | 37 | if av_dict_set(&mut ptr, key.as_ptr(), value.as_ptr(), 0) < 0 { 38 | panic!("out of memory"); 39 | } 40 | 41 | self.ptr = ptr; 42 | self.imm = immutable::Ref::wrap(ptr); 43 | } 44 | } 45 | } 46 | 47 | impl<'a> Deref for Ref<'a> { 48 | type Target = immutable::Ref<'a>; 49 | 50 | fn deref(&self) -> &Self::Target { 51 | &self.imm 52 | } 53 | } 54 | 55 | impl<'a> fmt::Debug for Ref<'a> { 56 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 57 | self.imm.fmt(fmt) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/codec/encoder/motion_estimation.rs: -------------------------------------------------------------------------------- 1 | use libc::c_int; 2 | 3 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 4 | pub enum MotionEstimation { 5 | Zero, 6 | Full, 7 | Log, 8 | Phods, 9 | Epzs, 10 | X1, 11 | Hex, 12 | Umh, 13 | Iter, 14 | Tesa, 15 | } 16 | 17 | impl From for MotionEstimation { 18 | fn from(value: c_int) -> MotionEstimation { 19 | match value { 20 | 1 => MotionEstimation::Zero, 21 | 2 => MotionEstimation::Full, 22 | 3 => MotionEstimation::Log, 23 | 4 => MotionEstimation::Phods, 24 | 5 => MotionEstimation::Epzs, 25 | 6 => MotionEstimation::X1, 26 | 7 => MotionEstimation::Hex, 27 | 8 => MotionEstimation::Umh, 28 | 9 => MotionEstimation::Iter, 29 | 10 => MotionEstimation::Tesa, 30 | 31 | _ => MotionEstimation::Zero, 32 | } 33 | } 34 | } 35 | 36 | impl From for c_int { 37 | fn from(value: MotionEstimation) -> c_int { 38 | match value { 39 | MotionEstimation::Zero => 1, 40 | MotionEstimation::Full => 2, 41 | MotionEstimation::Log => 3, 42 | MotionEstimation::Phods => 4, 43 | MotionEstimation::Epzs => 5, 44 | MotionEstimation::X1 => 6, 45 | MotionEstimation::Hex => 7, 46 | MotionEstimation::Umh => 8, 47 | MotionEstimation::Iter => 9, 48 | MotionEstimation::Tesa => 10, 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/format/chapter/chapter.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use {DictionaryRef, Rational}; 3 | 4 | use format::context::common::Context; 5 | 6 | // WARNING: index refers to the offset in the chapters array (starting from 0) 7 | // it is not necessarly equal to the id (which may start at 1) 8 | pub struct Chapter<'a> { 9 | context: &'a Context, 10 | index: usize, 11 | } 12 | 13 | impl<'a> Chapter<'a> { 14 | pub unsafe fn wrap(context: &Context, index: usize) -> Chapter { 15 | Chapter { context, index } 16 | } 17 | 18 | pub unsafe fn as_ptr(&self) -> *const AVChapter { 19 | *(*self.context.as_ptr()).chapters.add(self.index) 20 | } 21 | } 22 | 23 | impl<'a> Chapter<'a> { 24 | pub fn index(&self) -> usize { 25 | self.index 26 | } 27 | 28 | pub fn id(&self) -> i64 { 29 | #[allow(clippy::unnecessary_cast)] 30 | unsafe { 31 | (*self.as_ptr()).id as i64 32 | } 33 | } 34 | 35 | pub fn time_base(&self) -> Rational { 36 | unsafe { Rational::from((*self.as_ptr()).time_base) } 37 | } 38 | 39 | pub fn start(&self) -> i64 { 40 | unsafe { (*self.as_ptr()).start } 41 | } 42 | 43 | pub fn end(&self) -> i64 { 44 | unsafe { (*self.as_ptr()).end } 45 | } 46 | 47 | pub fn metadata(&self) -> DictionaryRef { 48 | unsafe { DictionaryRef::wrap((*self.as_ptr()).metadata) } 49 | } 50 | } 51 | 52 | impl<'a> PartialEq for Chapter<'a> { 53 | fn eq(&self, other: &Self) -> bool { 54 | unsafe { self.as_ptr() == other.as_ptr() } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/device/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod extensions; 2 | pub mod input; 3 | pub mod output; 4 | 5 | use std::ffi::CStr; 6 | use std::marker::PhantomData; 7 | use std::str::from_utf8_unchecked; 8 | 9 | use ffi::*; 10 | 11 | pub struct Info<'a> { 12 | ptr: *mut AVDeviceInfo, 13 | 14 | _marker: PhantomData<&'a ()>, 15 | } 16 | 17 | impl<'a> Info<'a> { 18 | pub unsafe fn wrap(ptr: *mut AVDeviceInfo) -> Self { 19 | Info { 20 | ptr, 21 | _marker: PhantomData, 22 | } 23 | } 24 | 25 | pub unsafe fn as_ptr(&self) -> *const AVDeviceInfo { 26 | self.ptr as *const _ 27 | } 28 | 29 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVDeviceInfo { 30 | self.ptr 31 | } 32 | } 33 | 34 | impl<'a> Info<'a> { 35 | pub fn name(&self) -> &str { 36 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).device_name).to_bytes()) } 37 | } 38 | 39 | pub fn description(&self) -> &str { 40 | unsafe { 41 | from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).device_description).to_bytes()) 42 | } 43 | } 44 | } 45 | 46 | pub fn register_all() { 47 | unsafe { 48 | avdevice_register_all(); 49 | } 50 | } 51 | 52 | pub fn version() -> u32 { 53 | unsafe { avdevice_version() } 54 | } 55 | 56 | pub fn configuration() -> &'static str { 57 | unsafe { from_utf8_unchecked(CStr::from_ptr(avdevice_configuration()).to_bytes()) } 58 | } 59 | 60 | pub fn license() -> &'static str { 61 | unsafe { from_utf8_unchecked(CStr::from_ptr(avdevice_license()).to_bytes()) } 62 | } 63 | -------------------------------------------------------------------------------- /src/codec/threading.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub struct Config { 6 | pub kind: Type, 7 | pub count: usize, 8 | pub safe: bool, 9 | } 10 | 11 | impl Config { 12 | pub fn kind(value: Type) -> Self { 13 | Config { 14 | kind: value, 15 | ..Default::default() 16 | } 17 | } 18 | 19 | pub fn count(value: usize) -> Self { 20 | Config { 21 | count: value, 22 | ..Default::default() 23 | } 24 | } 25 | 26 | pub fn safe(value: bool) -> Self { 27 | Config { 28 | safe: value, 29 | ..Default::default() 30 | } 31 | } 32 | } 33 | 34 | impl Default for Config { 35 | fn default() -> Self { 36 | Config { 37 | kind: Type::None, 38 | count: 0, 39 | safe: false, 40 | } 41 | } 42 | } 43 | 44 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 45 | pub enum Type { 46 | None, 47 | Frame, 48 | Slice, 49 | } 50 | 51 | impl From for Type { 52 | fn from(value: c_int) -> Type { 53 | match value { 54 | FF_THREAD_FRAME => Type::Frame, 55 | FF_THREAD_SLICE => Type::Slice, 56 | 57 | _ => Type::None, 58 | } 59 | } 60 | } 61 | 62 | impl From for c_int { 63 | fn from(value: Type) -> c_int { 64 | match value { 65 | Type::None => 0, 66 | Type::Frame => FF_THREAD_FRAME, 67 | Type::Slice => FF_THREAD_SLICE, 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/format/format/input.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::str::from_utf8_unchecked; 3 | 4 | use ffi::*; 5 | 6 | pub struct Input { 7 | ptr: *mut AVInputFormat, 8 | } 9 | 10 | impl Input { 11 | pub unsafe fn wrap(ptr: *mut AVInputFormat) -> Self { 12 | Input { ptr } 13 | } 14 | 15 | pub unsafe fn as_ptr(&self) -> *const AVInputFormat { 16 | self.ptr as *const _ 17 | } 18 | 19 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVInputFormat { 20 | self.ptr 21 | } 22 | } 23 | 24 | impl Input { 25 | pub fn name(&self) -> &str { 26 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).name).to_bytes()) } 27 | } 28 | 29 | pub fn description(&self) -> &str { 30 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).long_name).to_bytes()) } 31 | } 32 | 33 | pub fn extensions(&self) -> Vec<&str> { 34 | unsafe { 35 | let ptr = (*self.as_ptr()).extensions; 36 | 37 | if ptr.is_null() { 38 | Vec::new() 39 | } else { 40 | from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) 41 | .split(',') 42 | .collect() 43 | } 44 | } 45 | } 46 | 47 | pub fn mime_types(&self) -> Vec<&str> { 48 | unsafe { 49 | let ptr = (*self.as_ptr()).mime_type; 50 | 51 | if ptr.is_null() { 52 | Vec::new() 53 | } else { 54 | from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) 55 | .split(',') 56 | .collect() 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/util/dictionary/immutable.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{CStr, CString}; 2 | use std::fmt; 3 | use std::marker::PhantomData; 4 | use std::ptr; 5 | use std::str::from_utf8_unchecked; 6 | 7 | use super::{Iter, Owned}; 8 | use ffi::*; 9 | 10 | pub struct Ref<'a> { 11 | ptr: *const AVDictionary, 12 | 13 | _marker: PhantomData<&'a ()>, 14 | } 15 | 16 | impl<'a> Ref<'a> { 17 | pub unsafe fn wrap(ptr: *const AVDictionary) -> Self { 18 | Ref { 19 | ptr, 20 | _marker: PhantomData, 21 | } 22 | } 23 | 24 | pub unsafe fn as_ptr(&self) -> *const AVDictionary { 25 | self.ptr 26 | } 27 | } 28 | 29 | impl<'a> Ref<'a> { 30 | pub fn get(&'a self, key: &str) -> Option<&'a str> { 31 | unsafe { 32 | let key = CString::new(key).unwrap(); 33 | let entry = av_dict_get(self.as_ptr(), key.as_ptr(), ptr::null_mut(), 0); 34 | 35 | if entry.is_null() { 36 | None 37 | } else { 38 | Some(from_utf8_unchecked( 39 | CStr::from_ptr((*entry).value).to_bytes(), 40 | )) 41 | } 42 | } 43 | } 44 | 45 | pub fn iter(&self) -> Iter { 46 | unsafe { Iter::new(self.as_ptr()) } 47 | } 48 | 49 | pub fn to_owned<'b>(&self) -> Owned<'b> { 50 | self.iter().collect() 51 | } 52 | } 53 | 54 | impl<'a> IntoIterator for &'a Ref<'a> { 55 | type Item = (&'a str, &'a str); 56 | type IntoIter = Iter<'a>; 57 | 58 | fn into_iter(self) -> Self::IntoIter { 59 | self.iter() 60 | } 61 | } 62 | 63 | impl<'a> fmt::Debug for Ref<'a> { 64 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 65 | fmt.debug_map().entries(self.iter()).finish() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/format/format/iter.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use super::{Format, Input, Output}; 4 | use ffi::*; 5 | 6 | pub struct Iter { 7 | input: *mut AVInputFormat, 8 | output: *mut AVOutputFormat, 9 | step: Step, 10 | } 11 | 12 | enum Step { 13 | Input, 14 | Output, 15 | Done, 16 | } 17 | 18 | impl Iter { 19 | pub fn new() -> Self { 20 | Iter { 21 | input: ptr::null_mut(), 22 | output: ptr::null_mut(), 23 | step: Step::Input, 24 | } 25 | } 26 | } 27 | 28 | impl Default for Iter { 29 | fn default() -> Self { 30 | Self::new() 31 | } 32 | } 33 | 34 | impl Iterator for Iter { 35 | type Item = Format; 36 | 37 | fn next(&mut self) -> Option<::Item> { 38 | unsafe { 39 | match self.step { 40 | Step::Input => { 41 | let ptr = av_iformat_next(self.input); 42 | 43 | if ptr.is_null() && !self.input.is_null() { 44 | self.step = Step::Output; 45 | 46 | self.next() 47 | } else { 48 | self.input = ptr; 49 | 50 | Some(Format::Input(Input::wrap(ptr))) 51 | } 52 | } 53 | 54 | Step::Output => { 55 | let ptr = av_oformat_next(self.output); 56 | 57 | if ptr.is_null() && !self.output.is_null() { 58 | self.step = Step::Done; 59 | 60 | self.next() 61 | } else { 62 | self.output = ptr; 63 | 64 | Some(Format::Output(Output::wrap(ptr))) 65 | } 66 | } 67 | 68 | Step::Done => None, 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/filter/context/context.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use super::{Sink, Source}; 4 | use ffi::*; 5 | use libc::c_void; 6 | use {format, option, ChannelLayout}; 7 | 8 | pub struct Context<'a> { 9 | ptr: *mut AVFilterContext, 10 | 11 | _marker: PhantomData<&'a ()>, 12 | } 13 | 14 | impl<'a> Context<'a> { 15 | pub unsafe fn wrap(ptr: *mut AVFilterContext) -> Self { 16 | Context { 17 | ptr, 18 | _marker: PhantomData, 19 | } 20 | } 21 | 22 | pub unsafe fn as_ptr(&self) -> *const AVFilterContext { 23 | self.ptr as *const _ 24 | } 25 | 26 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFilterContext { 27 | self.ptr 28 | } 29 | } 30 | 31 | impl<'a> Context<'a> { 32 | pub fn source(&'a mut self) -> Source<'a> { 33 | unsafe { Source::wrap(self) } 34 | } 35 | 36 | pub fn sink(&'a mut self) -> Sink<'a> { 37 | unsafe { Sink::wrap(self) } 38 | } 39 | 40 | pub fn set_pixel_format(&mut self, value: format::Pixel) { 41 | let _ = option::Settable::set::(self, "pix_fmts", &value.into()); 42 | } 43 | 44 | pub fn set_sample_format(&mut self, value: format::Sample) { 45 | let _ = option::Settable::set::(self, "sample_fmts", &value.into()); 46 | } 47 | 48 | pub fn set_sample_rate(&mut self, value: u32) { 49 | let _ = option::Settable::set(self, "sample_rates", &i64::from(value)); 50 | } 51 | 52 | pub fn set_channel_layout(&mut self, value: ChannelLayout) { 53 | let _ = option::Settable::set(self, "channel_layouts", &value.bits()); 54 | } 55 | } 56 | 57 | unsafe impl<'a> option::Target for Context<'a> { 58 | fn as_ptr(&self) -> *const c_void { 59 | self.ptr as *const _ 60 | } 61 | 62 | fn as_mut_ptr(&mut self) -> *mut c_void { 63 | self.ptr as *mut _ 64 | } 65 | } 66 | 67 | impl<'a> option::Settable for Context<'a> {} 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Rust FFmpeg wrapper 2 | [![Cargo](https://img.shields.io/crates/v/ffmpeg-rs.svg)](https://crates.io/crates/ffmpeg-rs) 3 | ![stable-x86_64](https://github.com/flavioroth/ffmpeg-rs/actions/workflows/build.yml/badge.svg?branch=master) 4 | 5 | Pull requests are welcome. This is originally a fork of [rust-ffmpeg](https://github.com/zmwangx/rust-ffmpeg) crate by [zmwangx](https://github.com/zmwangx/rust-ffmpeg). 6 | 7 | -------------------- 8 | 9 | Currently supported FFmpeg versions: 4.0 through 5.1 10 | 11 | See [BUILDING.md](BUILDING.md) for build instructions. 12 | 13 | Documentation: 14 | - [docs.rs](https://docs.rs/ffmpeg-rs/); 15 | - [FFmpeg user manual](https://ffmpeg.org/ffmpeg-all.html); 16 | - [FFmpeg Doxygen](https://ffmpeg.org/doxygen/trunk/). 17 | 18 | *Note on upgrading to v4.3.4 or later: v4.3.4 introduced automatic FFmpeg version detection, obsoleting feature flags `ffmpeg4`, `ffmpeg41`, `ffmpeg42` and `ffmpeg43`. If you manually specify any of these features, now is the time to remove them; if you use `ffmpeg43` through the `default` feature, it's still on for backward-compatibility but it has turned into a no-op, and you don't need to do anything. Deprecation plan: `ffmpeg43` will be dropped from default features come 4.4, and all these features will be removed come 5.0.* 19 | 20 | *See [CHANGELOG.md](CHANGELOG.md) for other information on version upgrades.* 21 | 22 | A word on versioning: major and minor versions of this crate track major and minor versions of FFmpeg, e.g. 4.2.x of this crate has been updated to support the 4.2.x series of FFmpeg. Patch level is reserved for changes to this crate and does not track FFmpeg patch versions. Since we can only freely bump the patch level, versioning of this crate differs from semver: minor versions may behave like semver major versions and introduce backward-incompatible changes; patch versions may behave like semver minor versions and introduce new APIs. Please peg the version you use accordingly. 23 | 24 | 25 | -------------------------------------------------------------------------------- /BUILDING.md: -------------------------------------------------------------------------------- 1 | ## Dependencies 2 | 3 | On *nix systems, `clang`, `pkg-config` and FFmpeg libraries (including development headers) are required. 4 | 5 | On macOS: 6 | 7 | brew install pkg-config ffmpeg 8 | 9 | On Debian-based systems: 10 | 11 | apt install -y clang libavcodec-dev libavformat-dev libavutil-dev pkg-config 12 | 13 | Other `libav*-dev` and `libsw*-dev` packages may be required if you enable the corresponding features, e.g., `libavdevice-dev` for the `device` feature. 14 | 15 | ## Feature selection 16 | 17 | At the moment **`codec` and `format`** are mandatory features, or `ffmpeg-sys-next` won't build. 18 | 19 | ## Building on Windows 20 | 21 | ### MSVC toolchain 22 | 23 | - Install LLVM (through official installer, Visual Studio, Chocolatey, or any other means), and add LLVM's `bin` path to `PATH`, or set `LIBCLANG_PATH` to that (see [`clang-sys` documentation](https://github.com/KyleMayes/clang-sys#environment-variables) for additional info). 24 | - Install FFmpeg (complete with headers) through any means, e.g. downloading a pre-built "full_build-shared" version from https://ffmpeg.org/download.html. Set `FFMPEG_DIR` to the directory containing `include` and `lib`. 25 | - `cargo build`. 26 | 27 | You can find an example in https://github.com/zmwangx/rust-ffmpeg/blob/master/.github/workflows/build.yml. 28 | 29 | ### GNU toolchain 30 | 31 | It works with GNU toolchain(haven't checked with MSVC), so you should: 32 | 1. Install MSYS2 33 | 2. In `.cargo/config` add 34 | 35 | ```toml 36 | [target.x86_64-pc-windows-gnu] 37 | linker = "C:\\msys64\\mingw64\\bin\\gcc.exe 38 | ``` 39 | 40 | 3. Install these packages: `pacman -S mingw-w64-x86_64-toolchain mingw-w64-x86_64-ffmpeg mingw-w64-x86_64-clang` 41 | 4. Add `C:\msys64\mingw64\bin` to your `PATH` environment variable 42 | 43 | 44 | ## Building on Raspberry Pi 45 | 46 | To build against an FFmpeg with rpi-specific patches (tell: `rpi` can be found in `ffmpeg -hwaccels`), the `rpi` feature needs to be enabled. -------------------------------------------------------------------------------- /src/format/stream/stream_mut.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ops::Deref; 3 | 4 | use super::Stream; 5 | use ffi::*; 6 | use format::context::common::Context; 7 | use {codec, Dictionary, Rational}; 8 | 9 | pub struct StreamMut<'a> { 10 | context: &'a mut Context, 11 | index: usize, 12 | 13 | immutable: Stream<'a>, 14 | } 15 | 16 | impl<'a> StreamMut<'a> { 17 | pub unsafe fn wrap(context: &mut Context, index: usize) -> StreamMut { 18 | StreamMut { 19 | context: mem::transmute_copy(&context), 20 | index, 21 | 22 | immutable: Stream::wrap(mem::transmute_copy(&context), index), 23 | } 24 | } 25 | 26 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVStream { 27 | *(*self.context.as_mut_ptr()).streams.add(self.index) 28 | } 29 | } 30 | 31 | impl<'a> StreamMut<'a> { 32 | pub fn set_time_base>(&mut self, value: R) { 33 | unsafe { 34 | (*self.as_mut_ptr()).time_base = value.into().into(); 35 | } 36 | } 37 | 38 | pub fn set_rate>(&mut self, value: R) { 39 | unsafe { 40 | (*self.as_mut_ptr()).r_frame_rate = value.into().into(); 41 | } 42 | } 43 | 44 | pub fn set_avg_frame_rate>(&mut self, value: R) { 45 | unsafe { 46 | (*self.as_mut_ptr()).avg_frame_rate = value.into().into(); 47 | } 48 | } 49 | 50 | pub fn set_parameters>(&mut self, parameters: P) { 51 | let parameters = parameters.into(); 52 | 53 | unsafe { 54 | avcodec_parameters_copy((*self.as_mut_ptr()).codecpar, parameters.as_ptr()); 55 | } 56 | } 57 | 58 | pub fn set_metadata(&mut self, metadata: Dictionary) { 59 | unsafe { 60 | let metadata = metadata.disown(); 61 | (*self.as_mut_ptr()).metadata = metadata; 62 | } 63 | } 64 | } 65 | 66 | impl<'a> Deref for StreamMut<'a> { 67 | type Target = Stream<'a>; 68 | 69 | fn deref(&self) -> &Self::Target { 70 | &self.immutable 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/codec/audio_service.rs: -------------------------------------------------------------------------------- 1 | use ffi::AVAudioServiceType::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum AudioService { 6 | Main, 7 | Effects, 8 | VisuallyImpaired, 9 | HearingImpaired, 10 | Dialogue, 11 | Commentary, 12 | Emergency, 13 | VoiceOver, 14 | Karaoke, 15 | } 16 | 17 | impl From for AudioService { 18 | fn from(value: AVAudioServiceType) -> Self { 19 | match value { 20 | AV_AUDIO_SERVICE_TYPE_MAIN => AudioService::Main, 21 | AV_AUDIO_SERVICE_TYPE_EFFECTS => AudioService::Effects, 22 | AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED => AudioService::VisuallyImpaired, 23 | AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED => AudioService::HearingImpaired, 24 | AV_AUDIO_SERVICE_TYPE_DIALOGUE => AudioService::Dialogue, 25 | AV_AUDIO_SERVICE_TYPE_COMMENTARY => AudioService::Commentary, 26 | AV_AUDIO_SERVICE_TYPE_EMERGENCY => AudioService::Emergency, 27 | AV_AUDIO_SERVICE_TYPE_VOICE_OVER => AudioService::VoiceOver, 28 | AV_AUDIO_SERVICE_TYPE_KARAOKE => AudioService::Karaoke, 29 | AV_AUDIO_SERVICE_TYPE_NB => AudioService::Main, 30 | } 31 | } 32 | } 33 | 34 | impl From for AVAudioServiceType { 35 | fn from(value: AudioService) -> AVAudioServiceType { 36 | match value { 37 | AudioService::Main => AV_AUDIO_SERVICE_TYPE_MAIN, 38 | AudioService::Effects => AV_AUDIO_SERVICE_TYPE_EFFECTS, 39 | AudioService::VisuallyImpaired => AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED, 40 | AudioService::HearingImpaired => AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED, 41 | AudioService::Dialogue => AV_AUDIO_SERVICE_TYPE_DIALOGUE, 42 | AudioService::Commentary => AV_AUDIO_SERVICE_TYPE_COMMENTARY, 43 | AudioService::Emergency => AV_AUDIO_SERVICE_TYPE_EMERGENCY, 44 | AudioService::VoiceOver => AV_AUDIO_SERVICE_TYPE_VOICE_OVER, 45 | AudioService::Karaoke => AV_AUDIO_SERVICE_TYPE_KARAOKE, 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/filter/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod flag; 2 | pub use self::flag::Flags; 3 | 4 | pub mod pad; 5 | pub use self::pad::Pad; 6 | 7 | pub mod filter; 8 | pub use self::filter::Filter; 9 | 10 | pub mod context; 11 | pub use self::context::{Context, Sink, Source}; 12 | 13 | pub mod graph; 14 | pub use self::graph::Graph; 15 | 16 | use std::ffi::{CStr, CString}; 17 | use std::str::from_utf8_unchecked; 18 | 19 | use ffi::*; 20 | #[cfg(not(feature = "ffmpeg_5_0"))] 21 | use Error; 22 | 23 | #[cfg(not(feature = "ffmpeg_5_0"))] 24 | pub fn register_all() { 25 | unsafe { 26 | avfilter_register_all(); 27 | } 28 | } 29 | 30 | #[cfg(not(feature = "ffmpeg_5_0"))] 31 | pub fn register(filter: &Filter) -> Result<(), Error> { 32 | unsafe { 33 | match avfilter_register(filter.as_ptr() as *mut _) { 34 | 0 => Ok(()), 35 | _ => Err(Error::InvalidData), 36 | } 37 | } 38 | } 39 | 40 | pub fn version() -> u32 { 41 | unsafe { avfilter_version() } 42 | } 43 | 44 | pub fn configuration() -> &'static str { 45 | unsafe { from_utf8_unchecked(CStr::from_ptr(avfilter_configuration()).to_bytes()) } 46 | } 47 | 48 | pub fn license() -> &'static str { 49 | unsafe { from_utf8_unchecked(CStr::from_ptr(avfilter_license()).to_bytes()) } 50 | } 51 | 52 | pub fn find(name: &str) -> Option { 53 | unsafe { 54 | let name = CString::new(name).unwrap(); 55 | let ptr = avfilter_get_by_name(name.as_ptr()); 56 | 57 | if ptr.is_null() { 58 | None 59 | } else { 60 | Some(Filter::wrap(ptr as *mut _)) 61 | } 62 | } 63 | } 64 | 65 | #[cfg(test)] 66 | mod tests { 67 | use super::*; 68 | 69 | #[test] 70 | fn test_paditer() { 71 | #[cfg(not(feature = "ffmpeg_5_0"))] 72 | register_all(); 73 | assert_eq!( 74 | find("overlay") 75 | .unwrap() 76 | .inputs() 77 | .unwrap() 78 | .map(|input| input.name().unwrap().to_string()) 79 | .collect::>(), 80 | vec!("main", "overlay") 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/device/extensions.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::ptr; 3 | 4 | use device; 5 | use ffi::*; 6 | use format::context::common::Context; 7 | use libc::c_int; 8 | use Error; 9 | 10 | impl Context { 11 | pub fn devices(&self) -> Result { 12 | unsafe { DeviceIter::wrap(self.as_ptr()) } 13 | } 14 | } 15 | 16 | pub struct DeviceIter<'a> { 17 | ptr: *mut AVDeviceInfoList, 18 | cur: c_int, 19 | 20 | _marker: PhantomData<&'a ()>, 21 | } 22 | 23 | impl<'a> DeviceIter<'a> { 24 | pub unsafe fn wrap(ctx: *const AVFormatContext) -> Result { 25 | let mut ptr: *mut AVDeviceInfoList = ptr::null_mut(); 26 | 27 | match avdevice_list_devices(ctx as *mut _, &mut ptr) { 28 | n if n < 0 => Err(Error::from(n)), 29 | 30 | _ => Ok(DeviceIter { 31 | ptr, 32 | cur: 0, 33 | _marker: PhantomData, 34 | }), 35 | } 36 | } 37 | } 38 | 39 | impl<'a> DeviceIter<'a> { 40 | pub fn default(&self) -> usize { 41 | unsafe { (*self.ptr).default_device as usize } 42 | } 43 | } 44 | 45 | impl<'a> Drop for DeviceIter<'a> { 46 | fn drop(&mut self) { 47 | unsafe { 48 | avdevice_free_list_devices(&mut self.ptr); 49 | } 50 | } 51 | } 52 | 53 | impl<'a> Iterator for DeviceIter<'a> { 54 | type Item = device::Info<'a>; 55 | 56 | fn next(&mut self) -> Option<::Item> { 57 | unsafe { 58 | if self.cur >= (*self.ptr).nb_devices { 59 | None 60 | } else { 61 | self.cur += 1; 62 | Some(device::Info::wrap( 63 | *(*self.ptr).devices.offset((self.cur - 1) as isize), 64 | )) 65 | } 66 | } 67 | } 68 | 69 | fn size_hint(&self) -> (usize, Option) { 70 | unsafe { 71 | let length = (*self.ptr).nb_devices as usize; 72 | 73 | (length - self.cur as usize, Some(length - self.cur as usize)) 74 | } 75 | } 76 | } 77 | 78 | impl<'a> ExactSizeIterator for DeviceIter<'a> {} 79 | -------------------------------------------------------------------------------- /src/util/log/level.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | 3 | use ffi::*; 4 | use libc::c_int; 5 | 6 | use log_crate::LevelFilter; 7 | 8 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 9 | pub enum Level { 10 | Quiet, 11 | Panic, 12 | Fatal, 13 | Error, 14 | Warning, 15 | Info, 16 | Verbose, 17 | Debug, 18 | Trace, 19 | } 20 | 21 | pub struct LevelError; 22 | 23 | impl TryFrom for Level { 24 | type Error = &'static str; 25 | 26 | fn try_from(value: c_int) -> Result { 27 | match value { 28 | AV_LOG_QUIET => Ok(Level::Quiet), 29 | AV_LOG_PANIC => Ok(Level::Panic), 30 | AV_LOG_FATAL => Ok(Level::Fatal), 31 | AV_LOG_ERROR => Ok(Level::Error), 32 | AV_LOG_WARNING => Ok(Level::Warning), 33 | AV_LOG_INFO => Ok(Level::Info), 34 | AV_LOG_VERBOSE => Ok(Level::Verbose), 35 | AV_LOG_DEBUG => Ok(Level::Debug), 36 | AV_LOG_TRACE => Ok(Level::Trace), 37 | _ => Err("illegal log level"), 38 | } 39 | } 40 | } 41 | 42 | impl From for c_int { 43 | fn from(value: Level) -> c_int { 44 | match value { 45 | Level::Quiet => AV_LOG_QUIET, 46 | Level::Panic => AV_LOG_PANIC, 47 | Level::Fatal => AV_LOG_FATAL, 48 | Level::Error => AV_LOG_ERROR, 49 | Level::Warning => AV_LOG_WARNING, 50 | Level::Info => AV_LOG_INFO, 51 | Level::Verbose => AV_LOG_VERBOSE, 52 | Level::Debug => AV_LOG_DEBUG, 53 | Level::Trace => AV_LOG_TRACE, 54 | } 55 | } 56 | } 57 | 58 | impl From for LevelFilter { 59 | fn from(level: Level) -> LevelFilter { 60 | match level { 61 | Level::Quiet => LevelFilter::Off, 62 | Level::Trace => LevelFilter::Trace, 63 | Level::Debug | Level::Verbose => LevelFilter::Debug, 64 | Level::Info => LevelFilter::Info, 65 | Level::Warning => LevelFilter::Warn, 66 | Level::Error | Level::Fatal | Level::Panic => LevelFilter::Error, 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/codec/encoder/comparison.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Comparison { 6 | SAD, 7 | SSE, 8 | SATD, 9 | DCT, 10 | PSNR, 11 | BIT, 12 | RD, 13 | ZERO, 14 | VSAD, 15 | VSSE, 16 | NSSE, 17 | W53, 18 | W97, 19 | DCTMAX, 20 | DCT264, 21 | CHROMA, 22 | } 23 | 24 | impl From for Comparison { 25 | fn from(value: c_int) -> Comparison { 26 | match value { 27 | FF_CMP_SAD => Comparison::SAD, 28 | FF_CMP_SSE => Comparison::SSE, 29 | FF_CMP_SATD => Comparison::SATD, 30 | FF_CMP_DCT => Comparison::DCT, 31 | FF_CMP_PSNR => Comparison::PSNR, 32 | FF_CMP_BIT => Comparison::BIT, 33 | FF_CMP_RD => Comparison::RD, 34 | FF_CMP_ZERO => Comparison::ZERO, 35 | FF_CMP_VSAD => Comparison::VSAD, 36 | FF_CMP_VSSE => Comparison::VSSE, 37 | FF_CMP_NSSE => Comparison::NSSE, 38 | FF_CMP_W53 => Comparison::W53, 39 | FF_CMP_W97 => Comparison::W97, 40 | FF_CMP_DCTMAX => Comparison::DCTMAX, 41 | FF_CMP_DCT264 => Comparison::DCT264, 42 | FF_CMP_CHROMA => Comparison::CHROMA, 43 | 44 | _ => Comparison::ZERO, 45 | } 46 | } 47 | } 48 | 49 | impl From for c_int { 50 | fn from(value: Comparison) -> c_int { 51 | match value { 52 | Comparison::SAD => FF_CMP_SAD, 53 | Comparison::SSE => FF_CMP_SSE, 54 | Comparison::SATD => FF_CMP_SATD, 55 | Comparison::DCT => FF_CMP_DCT, 56 | Comparison::PSNR => FF_CMP_PSNR, 57 | Comparison::BIT => FF_CMP_BIT, 58 | Comparison::RD => FF_CMP_RD, 59 | Comparison::ZERO => FF_CMP_ZERO, 60 | Comparison::VSAD => FF_CMP_VSAD, 61 | Comparison::VSSE => FF_CMP_VSSE, 62 | Comparison::NSSE => FF_CMP_NSSE, 63 | Comparison::W53 => FF_CMP_W53, 64 | Comparison::W97 => FF_CMP_W97, 65 | Comparison::DCTMAX => FF_CMP_DCTMAX, 66 | Comparison::DCT264 => FF_CMP_DCT264, 67 | Comparison::CHROMA => FF_CMP_CHROMA, 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /examples/chapters.rs: -------------------------------------------------------------------------------- 1 | extern crate ffmpeg_rs; 2 | use std::env; 3 | 4 | fn main() { 5 | ffmpeg_rs::init().unwrap(); 6 | 7 | match ffmpeg_rs::format::input(&env::args().nth(1).expect("missing input file name")) { 8 | Ok(ictx) => { 9 | println!("Nb chapters: {}", ictx.nb_chapters()); 10 | 11 | for chapter in ictx.chapters() { 12 | println!("chapter id {}:", chapter.id()); 13 | println!("\ttime_base: {}", chapter.time_base()); 14 | println!("\tstart: {}", chapter.start()); 15 | println!("\tend: {}", chapter.end()); 16 | 17 | for (k, v) in chapter.metadata().iter() { 18 | println!("\t{k}: {v}"); 19 | } 20 | } 21 | 22 | let mut octx = ffmpeg_rs::format::output(&"test.mkv").expect("Couldn't open test file"); 23 | 24 | for chapter in ictx.chapters() { 25 | let title = match chapter.metadata().get("title") { 26 | Some(title) => String::from(title), 27 | None => String::new(), 28 | }; 29 | 30 | match octx.add_chapter( 31 | chapter.id(), 32 | chapter.time_base(), 33 | chapter.start(), 34 | chapter.end(), 35 | &title, 36 | ) { 37 | Ok(chapter) => println!("Added chapter with id {} to output", chapter.id()), 38 | Err(error) => { 39 | println!("Error adding chapter with id: {} - {}", chapter.id(), error) 40 | } 41 | } 42 | } 43 | 44 | println!("\nOuput: nb chapters: {}", octx.nb_chapters()); 45 | for chapter in octx.chapters() { 46 | println!("chapter id {}:", chapter.id()); 47 | println!("\ttime_base: {}", chapter.time_base()); 48 | println!("\tstart: {}", chapter.start()); 49 | println!("\tend: {}", chapter.end()); 50 | for (k, v) in chapter.metadata().iter() { 51 | println!("\t{k}: {v}"); 52 | } 53 | } 54 | } 55 | 56 | Err(error) => println!("error: {error}"), 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /examples/remux.rs: -------------------------------------------------------------------------------- 1 | extern crate ffmpeg_rs; 2 | use std::env; 3 | 4 | use ffmpeg_rs::{codec, encoder, format, log, media, Rational}; 5 | 6 | fn main() { 7 | let input_file = env::args().nth(1).expect("missing input file"); 8 | let output_file = env::args().nth(2).expect("missing output file"); 9 | 10 | ffmpeg_rs::init().unwrap(); 11 | log::set_level(log::Level::Warning); 12 | 13 | let mut ictx = format::input(&input_file).unwrap(); 14 | let mut octx = format::output(&output_file).unwrap(); 15 | 16 | let mut stream_mapping = vec![0; ictx.nb_streams() as _]; 17 | let mut ist_time_bases = vec![Rational(0, 1); ictx.nb_streams() as _]; 18 | let mut ost_index = 0; 19 | for (ist_index, ist) in ictx.streams().enumerate() { 20 | let ist_medium = ist.parameters().codec_type(); 21 | if ist_medium != media::Type::Audio 22 | && ist_medium != media::Type::Video 23 | && ist_medium != media::Type::Subtitle 24 | { 25 | stream_mapping[ist_index] = -1; 26 | continue; 27 | } 28 | stream_mapping[ist_index] = ost_index; 29 | ist_time_bases[ist_index] = ist.time_base(); 30 | ost_index += 1; 31 | let mut ost = octx.add_stream(encoder::find(codec::Id::None)).unwrap(); 32 | ost.set_parameters(ist.parameters()); 33 | // We need to set codec_tag to 0 lest we run into incompatible codec tag 34 | // issues when muxing into a different container format. Unfortunately 35 | // there's no high level API to do this (yet). 36 | unsafe { 37 | (*ost.parameters().as_mut_ptr()).codec_tag = 0; 38 | } 39 | } 40 | 41 | octx.set_metadata(ictx.metadata().to_owned()); 42 | octx.write_header().unwrap(); 43 | 44 | for (stream, mut packet) in ictx.packets() { 45 | let ist_index = stream.index(); 46 | let ost_index = stream_mapping[ist_index]; 47 | if ost_index < 0 { 48 | continue; 49 | } 50 | let ost = octx.stream(ost_index as _).unwrap(); 51 | packet.rescale_ts(ist_time_bases[ist_index], ost.time_base()); 52 | packet.set_position(-1); 53 | packet.set_stream(ost_index as _); 54 | packet.write_interleaved(&mut octx).unwrap(); 55 | } 56 | 57 | octx.write_trailer().unwrap(); 58 | } 59 | -------------------------------------------------------------------------------- /src/format/format/output.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use std::ffi::{CStr, CString}; 4 | use std::ptr; 5 | use std::str::from_utf8_unchecked; 6 | 7 | use super::Flags; 8 | use ffi::*; 9 | use {codec, media}; 10 | 11 | pub struct Output { 12 | ptr: *mut AVOutputFormat, 13 | } 14 | 15 | impl Output { 16 | pub unsafe fn wrap(ptr: *mut AVOutputFormat) -> Self { 17 | Output { ptr } 18 | } 19 | 20 | pub unsafe fn as_ptr(&self) -> *const AVOutputFormat { 21 | self.ptr as *const _ 22 | } 23 | 24 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVOutputFormat { 25 | self.ptr 26 | } 27 | } 28 | 29 | impl Output { 30 | pub fn name(&self) -> &str { 31 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).name).to_bytes()) } 32 | } 33 | 34 | pub fn description(&self) -> &str { 35 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).long_name).to_bytes()) } 36 | } 37 | 38 | pub fn extensions(&self) -> Vec<&str> { 39 | unsafe { 40 | let ptr = (*self.as_ptr()).extensions; 41 | 42 | if ptr.is_null() { 43 | Vec::new() 44 | } else { 45 | from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) 46 | .split(',') 47 | .collect() 48 | } 49 | } 50 | } 51 | 52 | pub fn mime_types(&self) -> Vec<&str> { 53 | unsafe { 54 | let ptr = (*self.as_ptr()).mime_type; 55 | 56 | if ptr.is_null() { 57 | Vec::new() 58 | } else { 59 | from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) 60 | .split(',') 61 | .collect() 62 | } 63 | } 64 | } 65 | 66 | pub fn codec>(&self, path: &P, kind: media::Type) -> codec::Id { 67 | // XXX: use to_cstring when stable 68 | let path = CString::new(path.as_ref().as_os_str().to_str().unwrap()).unwrap(); 69 | 70 | unsafe { 71 | codec::Id::from(av_guess_codec( 72 | self.as_ptr() as *mut _, 73 | ptr::null(), 74 | path.as_ptr(), 75 | ptr::null(), 76 | kind.into(), 77 | )) 78 | } 79 | } 80 | 81 | pub fn flags(&self) -> Flags { 82 | unsafe { Flags::from_bits_truncate((*self.as_ptr()).flags) } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/software/scaling/filter.rs: -------------------------------------------------------------------------------- 1 | use super::Vector; 2 | use ffi::*; 3 | 4 | pub struct Filter { 5 | ptr: *mut SwsFilter, 6 | } 7 | 8 | impl Filter { 9 | pub unsafe fn as_ptr(&self) -> *const SwsFilter { 10 | self.ptr as *const _ 11 | } 12 | 13 | pub unsafe fn as_mut_ptr(&mut self) -> *mut SwsFilter { 14 | self.ptr 15 | } 16 | } 17 | 18 | impl Filter { 19 | pub fn get( 20 | luma_g_blur: f32, 21 | chroma_g_blur: f32, 22 | luma_sharpen: f32, 23 | chroma_sharpen: f32, 24 | chroma_h_shift: f32, 25 | chroma_v_shift: f32, 26 | ) -> Self { 27 | unsafe { 28 | Filter { 29 | ptr: sws_getDefaultFilter( 30 | luma_g_blur, 31 | chroma_g_blur, 32 | luma_sharpen, 33 | chroma_sharpen, 34 | chroma_h_shift, 35 | chroma_v_shift, 36 | 0, 37 | ), 38 | } 39 | } 40 | } 41 | 42 | pub fn new() -> Self { 43 | Self::get(0.0, 0.0, 0.0, 0.0, 0.0, 0.0) 44 | } 45 | 46 | pub fn luma_horizontal(&self) -> Vector { 47 | unsafe { Vector::wrap((*self.as_ptr()).lumH) } 48 | } 49 | 50 | pub fn luma_horizontal_mut(&mut self) -> Vector { 51 | unsafe { Vector::wrap((*self.as_mut_ptr()).lumH) } 52 | } 53 | 54 | pub fn luma_vertical(&self) -> Vector { 55 | unsafe { Vector::wrap((*self.as_ptr()).lumV) } 56 | } 57 | 58 | pub fn luma_vertical_mut(&mut self) -> Vector { 59 | unsafe { Vector::wrap((*self.as_mut_ptr()).lumV) } 60 | } 61 | 62 | pub fn chroma_horizontal(&self) -> Vector { 63 | unsafe { Vector::wrap((*self.as_ptr()).lumV) } 64 | } 65 | 66 | pub fn chroma_horizontal_mut(&mut self) -> Vector { 67 | unsafe { Vector::wrap((*self.as_mut_ptr()).lumV) } 68 | } 69 | 70 | pub fn chroma_vertical(&self) -> Vector { 71 | unsafe { Vector::wrap((*self.as_ptr()).lumV) } 72 | } 73 | 74 | pub fn chroma_vertical_mut(&mut self) -> Vector { 75 | unsafe { Vector::wrap((*self.as_mut_ptr()).lumV) } 76 | } 77 | } 78 | 79 | impl Default for Filter { 80 | fn default() -> Self { 81 | Self::new() 82 | } 83 | } 84 | 85 | impl Drop for Filter { 86 | fn drop(&mut self) { 87 | unsafe { 88 | sws_freeFilter(self.as_mut_ptr()); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/codec/traits.rs: -------------------------------------------------------------------------------- 1 | use super::{decoder, encoder}; 2 | use codec::{Audio, Id, Video}; 3 | use Codec; 4 | 5 | pub trait Decoder { 6 | fn decoder(self) -> Option; 7 | } 8 | 9 | impl<'a> Decoder for &'a str { 10 | fn decoder(self) -> Option { 11 | decoder::find_by_name(self) 12 | } 13 | } 14 | 15 | impl Decoder for Id { 16 | fn decoder(self) -> Option { 17 | decoder::find(self) 18 | } 19 | } 20 | 21 | impl Decoder for Codec { 22 | fn decoder(self) -> Option { 23 | if self.is_decoder() { 24 | Some(self) 25 | } else { 26 | None 27 | } 28 | } 29 | } 30 | 31 | impl Decoder for Option { 32 | fn decoder(self) -> Option { 33 | self.and_then(|c| c.decoder()) 34 | } 35 | } 36 | 37 | impl Decoder for Audio { 38 | fn decoder(self) -> Option { 39 | if self.is_decoder() { 40 | Some(*self) 41 | } else { 42 | None 43 | } 44 | } 45 | } 46 | 47 | impl Decoder for Video { 48 | fn decoder(self) -> Option { 49 | if self.is_decoder() { 50 | Some(*self) 51 | } else { 52 | None 53 | } 54 | } 55 | } 56 | 57 | pub trait Encoder { 58 | fn encoder(self) -> Option; 59 | } 60 | 61 | impl<'a> Encoder for &'a str { 62 | fn encoder(self) -> Option { 63 | encoder::find_by_name(self) 64 | } 65 | } 66 | 67 | impl Encoder for Id { 68 | fn encoder(self) -> Option { 69 | encoder::find(self) 70 | } 71 | } 72 | 73 | impl Encoder for Codec { 74 | fn encoder(self) -> Option { 75 | if self.is_encoder() { 76 | Some(self) 77 | } else { 78 | None 79 | } 80 | } 81 | } 82 | 83 | impl Encoder for Option { 84 | fn encoder(self) -> Option { 85 | self.and_then(|c| c.encoder()) 86 | } 87 | } 88 | 89 | impl Encoder for Audio { 90 | fn encoder(self) -> Option { 91 | if self.is_encoder() { 92 | Some(*self) 93 | } else { 94 | None 95 | } 96 | } 97 | } 98 | 99 | impl Encoder for Video { 100 | fn encoder(self) -> Option { 101 | if self.is_encoder() { 102 | Some(*self) 103 | } else { 104 | None 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/software/resampling/dither.rs: -------------------------------------------------------------------------------- 1 | use ffi::SwrDitherType::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] 5 | pub enum Dither { 6 | None, 7 | Rectangular, 8 | Triangular, 9 | TriangularHighPass, 10 | 11 | NoiseShapingLipshitz, 12 | NoiseShapingFWeighted, 13 | NoiseShapingModifiedEWeighted, 14 | NoiseShapingImprovedEWeighted, 15 | NoiseShapingShibata, 16 | NoiseShapingLowShibata, 17 | NoiseShapingHighShibata, 18 | } 19 | 20 | impl From for Dither { 21 | fn from(value: SwrDitherType) -> Dither { 22 | match value { 23 | SWR_DITHER_NONE => Dither::None, 24 | SWR_DITHER_RECTANGULAR => Dither::Rectangular, 25 | SWR_DITHER_TRIANGULAR => Dither::Triangular, 26 | SWR_DITHER_TRIANGULAR_HIGHPASS => Dither::TriangularHighPass, 27 | 28 | SWR_DITHER_NS => Dither::None, 29 | SWR_DITHER_NS_LIPSHITZ => Dither::NoiseShapingLipshitz, 30 | SWR_DITHER_NS_F_WEIGHTED => Dither::NoiseShapingFWeighted, 31 | SWR_DITHER_NS_MODIFIED_E_WEIGHTED => Dither::NoiseShapingModifiedEWeighted, 32 | SWR_DITHER_NS_IMPROVED_E_WEIGHTED => Dither::NoiseShapingImprovedEWeighted, 33 | SWR_DITHER_NS_SHIBATA => Dither::NoiseShapingShibata, 34 | SWR_DITHER_NS_LOW_SHIBATA => Dither::NoiseShapingLowShibata, 35 | SWR_DITHER_NS_HIGH_SHIBATA => Dither::NoiseShapingHighShibata, 36 | SWR_DITHER_NB => Dither::None, 37 | } 38 | } 39 | } 40 | 41 | impl From for SwrDitherType { 42 | fn from(value: Dither) -> SwrDitherType { 43 | match value { 44 | Dither::None => SWR_DITHER_NONE, 45 | Dither::Rectangular => SWR_DITHER_RECTANGULAR, 46 | Dither::Triangular => SWR_DITHER_TRIANGULAR, 47 | Dither::TriangularHighPass => SWR_DITHER_TRIANGULAR_HIGHPASS, 48 | 49 | Dither::NoiseShapingLipshitz => SWR_DITHER_NS_LIPSHITZ, 50 | Dither::NoiseShapingFWeighted => SWR_DITHER_NS_F_WEIGHTED, 51 | Dither::NoiseShapingModifiedEWeighted => SWR_DITHER_NS_MODIFIED_E_WEIGHTED, 52 | Dither::NoiseShapingImprovedEWeighted => SWR_DITHER_NS_IMPROVED_E_WEIGHTED, 53 | Dither::NoiseShapingShibata => SWR_DITHER_NS_SHIBATA, 54 | Dither::NoiseShapingLowShibata => SWR_DITHER_NS_LOW_SHIBATA, 55 | Dither::NoiseShapingHighShibata => SWR_DITHER_NS_HIGH_SHIBATA, 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/format/chapter/chapter_mut.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ops::Deref; 3 | 4 | use super::Chapter; 5 | use ffi::*; 6 | use format::context::common::Context; 7 | use {Dictionary, DictionaryMut, Rational}; 8 | 9 | // WARNING: index refers to the offset in the chapters array (starting from 0) 10 | // it is not necessarly equal to the id (which may start at 1) 11 | pub struct ChapterMut<'a> { 12 | context: &'a mut Context, 13 | index: usize, 14 | 15 | immutable: Chapter<'a>, 16 | } 17 | 18 | impl<'a> ChapterMut<'a> { 19 | pub unsafe fn wrap(context: &mut Context, index: usize) -> ChapterMut { 20 | ChapterMut { 21 | context: mem::transmute_copy(&context), 22 | index, 23 | 24 | immutable: Chapter::wrap(mem::transmute_copy(&context), index), 25 | } 26 | } 27 | 28 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVChapter { 29 | *(*self.context.as_mut_ptr()).chapters.add(self.index) 30 | } 31 | } 32 | 33 | impl<'a> ChapterMut<'a> { 34 | pub fn set_id(&mut self, value: i64) { 35 | unsafe { 36 | (*self.as_mut_ptr()).id = value as _; 37 | } 38 | } 39 | 40 | pub fn set_time_base>(&mut self, value: R) { 41 | unsafe { 42 | (*self.as_mut_ptr()).time_base = value.into().into(); 43 | } 44 | } 45 | 46 | pub fn set_start(&mut self, value: i64) { 47 | unsafe { 48 | (*self.as_mut_ptr()).start = value; 49 | } 50 | } 51 | 52 | pub fn set_end(&mut self, value: i64) { 53 | unsafe { 54 | (*self.as_mut_ptr()).end = value; 55 | } 56 | } 57 | 58 | pub fn set_metadata, V: AsRef>(&mut self, key: K, value: V) { 59 | // dictionary.set() allocates the AVDictionary the first time a key/value is inserted 60 | // so we want to update the metadata dictionary afterwards 61 | unsafe { 62 | let mut dictionary = Dictionary::own(self.metadata().as_mut_ptr()); 63 | dictionary.set(key.as_ref(), value.as_ref()); 64 | (*self.as_mut_ptr()).metadata = dictionary.disown(); 65 | } 66 | } 67 | 68 | pub fn metadata(&mut self) -> DictionaryMut { 69 | unsafe { DictionaryMut::wrap((*self.as_mut_ptr()).metadata) } 70 | } 71 | } 72 | 73 | impl<'a> Deref for ChapterMut<'a> { 74 | type Target = Chapter<'a>; 75 | 76 | fn deref(&self) -> &Self::Target { 77 | &self.immutable 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /examples/dump-frames.rs: -------------------------------------------------------------------------------- 1 | extern crate ffmpeg_rs; 2 | use ffmpeg_rs::format::{input, Pixel}; 3 | use ffmpeg_rs::media::Type; 4 | use ffmpeg_rs::software::scaling::{context::Context, flag::Flags}; 5 | use ffmpeg_rs::util::frame::video::Video; 6 | use std::env; 7 | use std::fs::File; 8 | use std::io::prelude::*; 9 | 10 | fn main() -> Result<(), ffmpeg_rs::Error> { 11 | ffmpeg_rs::init().unwrap(); 12 | 13 | if let Ok(mut ictx) = input(&env::args().nth(1).expect("Cannot open file.")) { 14 | let input = ictx 15 | .streams() 16 | .best(Type::Video) 17 | .ok_or(ffmpeg_rs::Error::StreamNotFound)?; 18 | let video_stream_index = input.index(); 19 | 20 | let context_decoder = 21 | ffmpeg_rs::codec::context::Context::from_parameters(input.parameters())?; 22 | let mut decoder = context_decoder.decoder().video()?; 23 | 24 | let mut scaler = Context::get( 25 | decoder.format(), 26 | decoder.width(), 27 | decoder.height(), 28 | Pixel::RGB24, 29 | decoder.width(), 30 | decoder.height(), 31 | Flags::BILINEAR, 32 | )?; 33 | 34 | let mut frame_index = 0; 35 | 36 | let mut receive_and_process_decoded_frames = 37 | |decoder: &mut ffmpeg_rs::decoder::Video| -> Result<(), ffmpeg_rs::Error> { 38 | let mut decoded = Video::empty(); 39 | while decoder.receive_frame(&mut decoded).is_ok() { 40 | let mut rgb_frame = Video::empty(); 41 | scaler.run(&decoded, &mut rgb_frame)?; 42 | save_file(&rgb_frame, frame_index).unwrap(); 43 | frame_index += 1; 44 | } 45 | Ok(()) 46 | }; 47 | 48 | for (stream, packet) in ictx.packets() { 49 | if stream.index() == video_stream_index { 50 | decoder.send_packet(&packet)?; 51 | receive_and_process_decoded_frames(&mut decoder)?; 52 | } 53 | } 54 | decoder.send_eof()?; 55 | receive_and_process_decoded_frames(&mut decoder)?; 56 | } 57 | 58 | Ok(()) 59 | } 60 | 61 | fn save_file(frame: &Video, index: usize) -> std::result::Result<(), std::io::Error> { 62 | let mut file = File::create(format!("frame{index}.ppm"))?; 63 | file.write_all(format!("P6\n{} {}\n255\n", frame.width(), frame.height()).as_bytes())?; 64 | file.write_all(frame.data(0))?; 65 | Ok(()) 66 | } 67 | -------------------------------------------------------------------------------- /src/software/scaling/extensions.rs: -------------------------------------------------------------------------------- 1 | use super::{Context, Flags}; 2 | use util::format; 3 | #[cfg(not(feature = "ffmpeg_5_0"))] 4 | use Picture; 5 | use {decoder, frame, Error}; 6 | 7 | #[cfg(not(feature = "ffmpeg_5_0"))] 8 | impl<'a> Picture<'a> { 9 | #[inline] 10 | pub fn scaler(&self, width: u32, height: u32, flags: Flags) -> Result { 11 | Context::get( 12 | self.format(), 13 | self.width(), 14 | self.height(), 15 | self.format(), 16 | width, 17 | height, 18 | flags, 19 | ) 20 | } 21 | 22 | #[inline] 23 | pub fn converter(&self, format: format::Pixel) -> Result { 24 | Context::get( 25 | self.format(), 26 | self.width(), 27 | self.height(), 28 | format, 29 | self.width(), 30 | self.height(), 31 | Flags::FAST_BILINEAR, 32 | ) 33 | } 34 | } 35 | 36 | impl frame::Video { 37 | #[inline] 38 | pub fn scaler(&self, width: u32, height: u32, flags: Flags) -> Result { 39 | Context::get( 40 | self.format(), 41 | self.width(), 42 | self.height(), 43 | self.format(), 44 | width, 45 | height, 46 | flags, 47 | ) 48 | } 49 | 50 | #[inline] 51 | pub fn converter(&self, format: format::Pixel) -> Result { 52 | Context::get( 53 | self.format(), 54 | self.width(), 55 | self.height(), 56 | format, 57 | self.width(), 58 | self.height(), 59 | Flags::FAST_BILINEAR, 60 | ) 61 | } 62 | } 63 | 64 | impl decoder::Video { 65 | #[inline] 66 | pub fn scaler(&self, width: u32, height: u32, flags: Flags) -> Result { 67 | Context::get( 68 | self.format(), 69 | self.width(), 70 | self.height(), 71 | self.format(), 72 | width, 73 | height, 74 | flags, 75 | ) 76 | } 77 | 78 | #[inline] 79 | pub fn converter(&self, format: format::Pixel) -> Result { 80 | Context::get( 81 | self.format(), 82 | self.width(), 83 | self.height(), 84 | format, 85 | self.width(), 86 | self.height(), 87 | Flags::FAST_BILINEAR, 88 | ) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/codec/video.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use super::codec::Codec; 4 | use ffi::*; 5 | use {format, Rational}; 6 | 7 | #[derive(PartialEq, Eq, Copy, Clone)] 8 | pub struct Video { 9 | codec: Codec, 10 | } 11 | 12 | impl Video { 13 | pub unsafe fn new(codec: Codec) -> Video { 14 | Video { codec } 15 | } 16 | } 17 | 18 | impl Video { 19 | pub fn rates(&self) -> Option { 20 | unsafe { 21 | if (*self.codec.as_ptr()).supported_framerates.is_null() { 22 | None 23 | } else { 24 | Some(RateIter::new((*self.codec.as_ptr()).supported_framerates)) 25 | } 26 | } 27 | } 28 | 29 | pub fn formats(&self) -> Option { 30 | unsafe { 31 | if (*self.codec.as_ptr()).pix_fmts.is_null() { 32 | None 33 | } else { 34 | Some(FormatIter::new((*self.codec.as_ptr()).pix_fmts)) 35 | } 36 | } 37 | } 38 | } 39 | 40 | impl Deref for Video { 41 | type Target = Codec; 42 | 43 | fn deref(&self) -> &Self::Target { 44 | &self.codec 45 | } 46 | } 47 | 48 | pub struct RateIter { 49 | ptr: *const AVRational, 50 | } 51 | 52 | impl RateIter { 53 | pub fn new(ptr: *const AVRational) -> Self { 54 | RateIter { ptr } 55 | } 56 | } 57 | 58 | impl Iterator for RateIter { 59 | type Item = Rational; 60 | 61 | fn next(&mut self) -> Option<::Item> { 62 | unsafe { 63 | if (*self.ptr).num == 0 && (*self.ptr).den == 0 { 64 | return None; 65 | } 66 | 67 | let rate = (*self.ptr).into(); 68 | self.ptr = self.ptr.offset(1); 69 | 70 | Some(rate) 71 | } 72 | } 73 | } 74 | 75 | pub struct FormatIter { 76 | ptr: *const AVPixelFormat, 77 | } 78 | 79 | impl FormatIter { 80 | pub fn new(ptr: *const AVPixelFormat) -> Self { 81 | FormatIter { ptr } 82 | } 83 | } 84 | 85 | impl Iterator for FormatIter { 86 | type Item = format::Pixel; 87 | 88 | fn next(&mut self) -> Option<::Item> { 89 | unsafe { 90 | if *self.ptr == AVPixelFormat::AV_PIX_FMT_NONE { 91 | return None; 92 | } 93 | 94 | let format = (*self.ptr).into(); 95 | self.ptr = self.ptr.offset(1); 96 | 97 | Some(format) 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/filter/filter.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::marker::PhantomData; 3 | use std::str::from_utf8_unchecked; 4 | 5 | use super::{Flags, Pad}; 6 | use ffi::*; 7 | 8 | pub struct Filter { 9 | ptr: *mut AVFilter, 10 | } 11 | 12 | impl Filter { 13 | pub unsafe fn wrap(ptr: *mut AVFilter) -> Self { 14 | Filter { ptr } 15 | } 16 | 17 | pub unsafe fn as_ptr(&self) -> *const AVFilter { 18 | self.ptr as *const _ 19 | } 20 | 21 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFilter { 22 | self.ptr 23 | } 24 | } 25 | 26 | impl Filter { 27 | pub fn name(&self) -> &str { 28 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).name).to_bytes()) } 29 | } 30 | 31 | pub fn description(&self) -> Option<&str> { 32 | unsafe { 33 | let ptr = (*self.as_ptr()).description; 34 | 35 | if ptr.is_null() { 36 | None 37 | } else { 38 | Some(from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) 39 | } 40 | } 41 | } 42 | 43 | pub fn inputs(&self) -> Option { 44 | unsafe { 45 | let ptr = (*self.as_ptr()).inputs; 46 | 47 | if ptr.is_null() { 48 | None 49 | } else { 50 | Some(PadIter::new((*self.as_ptr()).inputs)) 51 | } 52 | } 53 | } 54 | 55 | pub fn outputs(&self) -> Option { 56 | unsafe { 57 | let ptr = (*self.as_ptr()).outputs; 58 | 59 | if ptr.is_null() { 60 | None 61 | } else { 62 | Some(PadIter::new((*self.as_ptr()).outputs)) 63 | } 64 | } 65 | } 66 | 67 | pub fn flags(&self) -> Flags { 68 | unsafe { Flags::from_bits_truncate((*self.as_ptr()).flags) } 69 | } 70 | } 71 | 72 | pub struct PadIter<'a> { 73 | ptr: *const AVFilterPad, 74 | cur: isize, 75 | 76 | _marker: PhantomData<&'a ()>, 77 | } 78 | 79 | impl<'a> PadIter<'a> { 80 | pub fn new(ptr: *const AVFilterPad) -> Self { 81 | PadIter { 82 | ptr, 83 | cur: 0, 84 | _marker: PhantomData, 85 | } 86 | } 87 | } 88 | 89 | impl<'a> Iterator for PadIter<'a> { 90 | type Item = Pad<'a>; 91 | 92 | fn next(&mut self) -> Option { 93 | unsafe { 94 | if self.cur >= avfilter_pad_count(self.ptr) as isize { 95 | return None; 96 | } 97 | 98 | let pad = Pad::wrap(self.ptr, self.cur); 99 | self.cur += 1; 100 | 101 | Some(pad) 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 5.0.0 2 | ----- 3 | 4 | - Introduce conditional compilation flags to preserve functions that are 5 | removed from ffmpeg 5.0 and onwards. 6 | - Fix examples so they are using the ffmpeg-sanctionned way of doing 7 | things. More specifically, AVStream.codec has been removed, and the 8 | correct way of getting the codec from a stream is to use 9 | Context::from_parameters(stream.parameters()) and then that context's 10 | encoder / decoder. 11 | 12 | 4.4.0 13 | ----- 14 | 15 | - crate: `ffmpeg43` feature flag (noop since 4.3.4) has been dropped from default features. 16 | 17 | - codec: deprecate APIs based on deprecated (since FFmpeg 3.1) `avcodec_decode_video2()` / `avcodec_decode_audio4()` / `avcodec_encode_video2()` /`avcodec_encode_audio2()` -- `decoder::Video::decode()`, `decode::Audio::decode()`, `encoder::Video::encode()` and `encoder::Audio::decode()`. Users should migrate to `send_packet()` / `send_eof()`, `receive_frame()`, `send_frame()` / `send_eof()`, and `receive_packet()` APIs instead, which are based on the modern send/receive APIs. See [documentation in `libavcodec/avcodec.h`](https://github.com/FFmpeg/FFmpeg/blob/n4.3.1/libavcodec/avcodec.h#L84-L196) for details. (#28) 18 | 19 | - codec: fix signature of `Packet::write_interleaved`; previously `Result`, now `Result<(), Error>`. (#25) 20 | 21 | 4.3.8 22 | ----- 23 | - software::resampling: add Context::get_with for specifying additional options. (#41) 24 | 25 | 4.3.7 26 | ----- 27 | 28 | - codec: fix codec description potential null ptr issue. (#36) 29 | 30 | 4.3.6 31 | ----- 32 | 33 | - util: fix Windows compatibility due to unavailable errnos. (#30) 34 | 35 | 4.3.5 36 | ----- 37 | 38 | - util: add `util::log` module to expose FFmpeg's logging facilities. 39 | 40 | - filter: add method `Source::close()` to expose `av_buffersrc_close`. (#23) 41 | 42 | - codec: add new encoding/decoding APIs `send_frame()` / `send_eof()`, `receive_packet()` to `encoder::{Audio, Video}` and `send_packet()` / `send_eof()`, `receive_frame()` to `decoder::{Audio, Video}` based on modern send/receive APIs (instead of `avcodec_decode_video2()` / `avcodec_decode_audio4()` / `avcodec_encode_video2()` /`avcodec_encode_audio2()` which have been deprecated since FFmpeg 3.1). Users should consider switching to the new APIs. See [documentation in `libavcodec/avcodec.h`](https://github.com/FFmpeg/FFmpeg/blob/n4.3.1/libavcodec/avcodec.h#L84-L196) for details. (#28) 43 | 44 | - util: introduce new `Error` variant `Error::Other { errno }` for wrapped POSIX error codes (see the `AVERROR` macro in `libavutil/error.h`), and reexport common POSIX error codes under `util::error`. (#24) 45 | 46 | 4.3.4 47 | ----- 48 | 49 | - crate: FFmpeg version detection is now automatic, obseleting feature flags `ffmpeg4`, `ffmpeg41`, `ffmpeg42` and `ffmpeg43`. The flags are kept as noop for now, will be removed in 5.0. 50 | -------------------------------------------------------------------------------- /src/util/color/space.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::str::from_utf8_unchecked; 3 | 4 | use ffi::AVColorSpace::*; 5 | use ffi::*; 6 | 7 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 8 | pub enum Space { 9 | RGB, 10 | BT709, 11 | Unspecified, 12 | Reserved, 13 | FCC, 14 | BT470BG, 15 | SMPTE170M, 16 | SMPTE240M, 17 | YCGCO, 18 | BT2020NCL, 19 | BT2020CL, 20 | SMPTE2085, 21 | 22 | ChromaDerivedNCL, 23 | ChromaDerivedCL, 24 | ICTCP, 25 | } 26 | 27 | impl Space { 28 | pub const YCOCG: Space = Space::YCGCO; 29 | 30 | pub fn name(&self) -> Option<&'static str> { 31 | if *self == Space::Unspecified { 32 | return None; 33 | } 34 | unsafe { 35 | let ptr = av_color_space_name((*self).into()); 36 | ptr.as_ref() 37 | .map(|ptr| from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) 38 | } 39 | } 40 | } 41 | 42 | impl From for Space { 43 | fn from(value: AVColorSpace) -> Self { 44 | match value { 45 | AVCOL_SPC_RGB => Space::RGB, 46 | AVCOL_SPC_BT709 => Space::BT709, 47 | AVCOL_SPC_UNSPECIFIED => Space::Unspecified, 48 | AVCOL_SPC_RESERVED => Space::Reserved, 49 | AVCOL_SPC_FCC => Space::FCC, 50 | AVCOL_SPC_BT470BG => Space::BT470BG, 51 | AVCOL_SPC_SMPTE170M => Space::SMPTE170M, 52 | AVCOL_SPC_SMPTE240M => Space::SMPTE240M, 53 | AVCOL_SPC_YCGCO => Space::YCGCO, 54 | AVCOL_SPC_BT2020_NCL => Space::BT2020NCL, 55 | AVCOL_SPC_BT2020_CL => Space::BT2020CL, 56 | AVCOL_SPC_SMPTE2085 => Space::SMPTE2085, 57 | AVCOL_SPC_NB => Space::Unspecified, 58 | 59 | AVCOL_SPC_CHROMA_DERIVED_NCL => Space::ChromaDerivedNCL, 60 | AVCOL_SPC_CHROMA_DERIVED_CL => Space::ChromaDerivedCL, 61 | AVCOL_SPC_ICTCP => Space::ICTCP, 62 | } 63 | } 64 | } 65 | 66 | impl From for AVColorSpace { 67 | fn from(value: Space) -> AVColorSpace { 68 | match value { 69 | Space::RGB => AVCOL_SPC_RGB, 70 | Space::BT709 => AVCOL_SPC_BT709, 71 | Space::Unspecified => AVCOL_SPC_UNSPECIFIED, 72 | Space::Reserved => AVCOL_SPC_RESERVED, 73 | Space::FCC => AVCOL_SPC_FCC, 74 | Space::BT470BG => AVCOL_SPC_BT470BG, 75 | Space::SMPTE170M => AVCOL_SPC_SMPTE170M, 76 | Space::SMPTE240M => AVCOL_SPC_SMPTE240M, 77 | Space::YCGCO => AVCOL_SPC_YCGCO, 78 | Space::BT2020NCL => AVCOL_SPC_BT2020_NCL, 79 | Space::BT2020CL => AVCOL_SPC_BT2020_CL, 80 | Space::SMPTE2085 => AVCOL_SPC_SMPTE2085, 81 | 82 | Space::ChromaDerivedNCL => AVCOL_SPC_CHROMA_DERIVED_NCL, 83 | Space::ChromaDerivedCL => AVCOL_SPC_CHROMA_DERIVED_CL, 84 | Space::ICTCP => AVCOL_SPC_ICTCP, 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/util/option/mod.rs: -------------------------------------------------------------------------------- 1 | mod traits; 2 | pub use self::traits::{Gettable, Iterable, Settable, Target}; 3 | 4 | use ffi::AVOptionType::*; 5 | use ffi::*; 6 | 7 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] 8 | pub enum Type { 9 | Flags, 10 | Int, 11 | Int64, 12 | Double, 13 | Float, 14 | String, 15 | Rational, 16 | Binary, 17 | Dictionary, 18 | Constant, 19 | 20 | ImageSize, 21 | PixelFormat, 22 | SampleFormat, 23 | VideoRate, 24 | Duration, 25 | Color, 26 | ChannelLayout, 27 | c_ulong, 28 | bool, 29 | 30 | #[cfg(feature = "ffmpeg_5_1")] 31 | ChLayout, 32 | } 33 | 34 | impl From for Type { 35 | fn from(value: AVOptionType) -> Self { 36 | match value { 37 | AV_OPT_TYPE_FLAGS => Type::Flags, 38 | AV_OPT_TYPE_INT => Type::Int, 39 | AV_OPT_TYPE_INT64 => Type::Int64, 40 | AV_OPT_TYPE_DOUBLE => Type::Double, 41 | AV_OPT_TYPE_FLOAT => Type::Float, 42 | AV_OPT_TYPE_STRING => Type::String, 43 | AV_OPT_TYPE_RATIONAL => Type::Rational, 44 | AV_OPT_TYPE_BINARY => Type::Binary, 45 | AV_OPT_TYPE_DICT => Type::Dictionary, 46 | AV_OPT_TYPE_CONST => Type::Constant, 47 | AV_OPT_TYPE_UINT64 => Type::c_ulong, 48 | AV_OPT_TYPE_BOOL => Type::bool, 49 | 50 | AV_OPT_TYPE_IMAGE_SIZE => Type::ImageSize, 51 | AV_OPT_TYPE_PIXEL_FMT => Type::PixelFormat, 52 | AV_OPT_TYPE_SAMPLE_FMT => Type::SampleFormat, 53 | AV_OPT_TYPE_VIDEO_RATE => Type::VideoRate, 54 | AV_OPT_TYPE_DURATION => Type::Duration, 55 | AV_OPT_TYPE_COLOR => Type::Color, 56 | AV_OPT_TYPE_CHANNEL_LAYOUT => Type::ChannelLayout, 57 | 58 | #[cfg(feature = "ffmpeg_5_1")] 59 | AV_OPT_TYPE_CHLAYOUT => Type::ChLayout, 60 | } 61 | } 62 | } 63 | 64 | impl From for AVOptionType { 65 | fn from(value: Type) -> AVOptionType { 66 | match value { 67 | Type::Flags => AV_OPT_TYPE_FLAGS, 68 | Type::Int => AV_OPT_TYPE_INT, 69 | Type::Int64 => AV_OPT_TYPE_INT64, 70 | Type::Double => AV_OPT_TYPE_DOUBLE, 71 | Type::Float => AV_OPT_TYPE_FLOAT, 72 | Type::String => AV_OPT_TYPE_STRING, 73 | Type::Rational => AV_OPT_TYPE_RATIONAL, 74 | Type::Binary => AV_OPT_TYPE_BINARY, 75 | Type::Dictionary => AV_OPT_TYPE_DICT, 76 | Type::Constant => AV_OPT_TYPE_CONST, 77 | Type::c_ulong => AV_OPT_TYPE_UINT64, 78 | Type::bool => AV_OPT_TYPE_BOOL, 79 | 80 | Type::ImageSize => AV_OPT_TYPE_IMAGE_SIZE, 81 | Type::PixelFormat => AV_OPT_TYPE_PIXEL_FMT, 82 | Type::SampleFormat => AV_OPT_TYPE_SAMPLE_FMT, 83 | Type::VideoRate => AV_OPT_TYPE_VIDEO_RATE, 84 | Type::Duration => AV_OPT_TYPE_DURATION, 85 | Type::Color => AV_OPT_TYPE_COLOR, 86 | Type::ChannelLayout => AV_OPT_TYPE_CHANNEL_LAYOUT, 87 | 88 | #[cfg(feature = "ffmpeg_5_1")] 89 | Type::ChLayout => AV_OPT_TYPE_CHLAYOUT, 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/util/log/callback.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryInto; 2 | use std::ffi::CStr; 3 | use std::io::Error; 4 | 5 | use libc::c_char; 6 | use libc::c_int; 7 | use log_crate::LevelFilter; 8 | 9 | use util::log::Level; 10 | 11 | // This is ugly, but va_list is not stabilized 12 | 13 | #[cfg(all(target_arch = "x86_64", target_family = "windows"))] 14 | pub type Args = sys::va_list; 15 | 16 | #[cfg(all(target_arch = "x86_64", target_family = "unix"))] 17 | pub type Args = *mut sys::__va_list_tag; 18 | 19 | pub struct LogContext { 20 | context: usize, 21 | level: Level, 22 | fmt: *const c_char, 23 | args: Args, 24 | } 25 | 26 | impl LogContext { 27 | /// Formats the message 28 | #[inline] 29 | pub fn to_message(&self) -> Result { 30 | unsafe { vsprintf::vsprintf(self.fmt, self.args) } 31 | } 32 | 33 | /// The format string 34 | #[inline] 35 | pub fn format(&self) -> &CStr { 36 | unsafe { CStr::from_ptr(self.fmt) } 37 | } 38 | 39 | /// The log level 40 | #[inline] 41 | pub fn level(&self) -> Level { 42 | self.level 43 | } 44 | 45 | /// The log context. Mostly the address of whatever component called the log function. 46 | #[inline] 47 | pub fn context(&self) -> usize { 48 | self.context 49 | } 50 | 51 | /// The log varargs. 52 | /// 53 | /// **Platform dependant**, use with caution. 54 | #[inline] 55 | pub unsafe fn args(&self) -> Args { 56 | self.args 57 | } 58 | } 59 | 60 | pub trait Callback { 61 | fn call(context: &LogContext); 62 | } 63 | 64 | unsafe extern "C" fn wrapped_callback( 65 | context: *mut libc::c_void, 66 | level: c_int, 67 | fmt: *const c_char, 68 | args: Args, 69 | ) { 70 | let context = LogContext { 71 | context: context as usize, 72 | level: level.try_into().unwrap_or(Level::Info), 73 | fmt, 74 | args, 75 | }; 76 | T::call(&context); 77 | } 78 | 79 | /// Sets the log callback 80 | pub fn set_callback() { 81 | unsafe { 82 | sys::av_log_set_callback(Some(wrapped_callback::)); 83 | } 84 | } 85 | 86 | /// Resets the log callback 87 | pub fn reset_callback() { 88 | unsafe { 89 | sys::av_log_set_callback(None); 90 | } 91 | } 92 | 93 | /// Sets the logging callback 94 | /// 95 | /// Logs using the log crate to the target 'ffmpeg' 96 | pub fn set_logging_callback() { 97 | set_callback::(); 98 | } 99 | 100 | /// Logs using the log crate to the target 'ffmpeg' 101 | pub struct LoggingCallback; 102 | 103 | impl Callback for LoggingCallback { 104 | fn call(context: &LogContext) { 105 | if let Some(log_level) = LevelFilter::from(context.level()).to_level() { 106 | // Don't format when level is disabled 107 | if log::log_enabled!(log_level) { 108 | match context.to_message() { 109 | Ok(message) => log::log!(target: "ffmpeg", log_level, "{}", message.trim()), 110 | Err(e) => { 111 | log::warn!(target: "ffmpeg", "failed to format ffmpeg log message: {:?}", e) 112 | } 113 | } 114 | } 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/util/color/primaries.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::str::from_utf8_unchecked; 3 | 4 | use ffi::AVColorPrimaries::*; 5 | use ffi::*; 6 | 7 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 8 | pub enum Primaries { 9 | Reserved0, 10 | BT709, 11 | Unspecified, 12 | Reserved, 13 | BT470M, 14 | 15 | BT470BG, 16 | SMPTE170M, 17 | SMPTE240M, 18 | Film, 19 | BT2020, 20 | 21 | SMPTE428, 22 | SMPTE431, 23 | SMPTE432, 24 | #[cfg(not(feature = "ffmpeg_4_3"))] 25 | JEDEC_P22, 26 | #[cfg(feature = "ffmpeg_4_3")] 27 | EBU3213, 28 | } 29 | 30 | impl Primaries { 31 | #[cfg(feature = "ffmpeg_4_3")] 32 | pub const JEDEC_P22: Primaries = Primaries::EBU3213; 33 | 34 | pub fn name(&self) -> Option<&'static str> { 35 | if *self == Primaries::Unspecified { 36 | return None; 37 | } 38 | unsafe { 39 | let ptr = av_color_primaries_name((*self).into()); 40 | ptr.as_ref() 41 | .map(|ptr| from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) 42 | } 43 | } 44 | } 45 | 46 | impl From for Primaries { 47 | fn from(value: AVColorPrimaries) -> Primaries { 48 | match value { 49 | AVCOL_PRI_RESERVED0 => Primaries::Reserved0, 50 | AVCOL_PRI_BT709 => Primaries::BT709, 51 | AVCOL_PRI_UNSPECIFIED => Primaries::Unspecified, 52 | AVCOL_PRI_RESERVED => Primaries::Reserved, 53 | AVCOL_PRI_BT470M => Primaries::BT470M, 54 | 55 | AVCOL_PRI_BT470BG => Primaries::BT470BG, 56 | AVCOL_PRI_SMPTE170M => Primaries::SMPTE170M, 57 | AVCOL_PRI_SMPTE240M => Primaries::SMPTE240M, 58 | AVCOL_PRI_FILM => Primaries::Film, 59 | AVCOL_PRI_BT2020 => Primaries::BT2020, 60 | AVCOL_PRI_NB => Primaries::Reserved0, 61 | 62 | AVCOL_PRI_SMPTE428 => Primaries::SMPTE428, 63 | AVCOL_PRI_SMPTE431 => Primaries::SMPTE431, 64 | AVCOL_PRI_SMPTE432 => Primaries::SMPTE432, 65 | #[cfg(not(feature = "ffmpeg_4_3"))] 66 | AVCOL_PRI_JEDEC_P22 => Primaries::JEDEC_P22, 67 | #[cfg(feature = "ffmpeg_4_3")] 68 | AVCOL_PRI_EBU3213 => Primaries::EBU3213, 69 | } 70 | } 71 | } 72 | 73 | impl From for AVColorPrimaries { 74 | fn from(value: Primaries) -> AVColorPrimaries { 75 | match value { 76 | Primaries::Reserved0 => AVCOL_PRI_RESERVED0, 77 | Primaries::BT709 => AVCOL_PRI_BT709, 78 | Primaries::Unspecified => AVCOL_PRI_UNSPECIFIED, 79 | Primaries::Reserved => AVCOL_PRI_RESERVED, 80 | Primaries::BT470M => AVCOL_PRI_BT470M, 81 | 82 | Primaries::BT470BG => AVCOL_PRI_BT470BG, 83 | Primaries::SMPTE170M => AVCOL_PRI_SMPTE170M, 84 | Primaries::SMPTE240M => AVCOL_PRI_SMPTE240M, 85 | Primaries::Film => AVCOL_PRI_FILM, 86 | Primaries::BT2020 => AVCOL_PRI_BT2020, 87 | 88 | Primaries::SMPTE428 => AVCOL_PRI_SMPTE428, 89 | Primaries::SMPTE431 => AVCOL_PRI_SMPTE431, 90 | Primaries::SMPTE432 => AVCOL_PRI_SMPTE432, 91 | #[cfg(not(feature = "ffmpeg_4_3"))] 92 | Primaries::JEDEC_P22 => AVCOL_PRI_JEDEC_P22, 93 | #[cfg(feature = "ffmpeg_4_3")] 94 | Primaries::EBU3213 => AVCOL_PRI_EBU3213, 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/codec/encoder/subtitle.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | use std::ptr; 3 | 4 | use ffi::*; 5 | use libc::c_int; 6 | 7 | use super::Encoder as Super; 8 | use codec::{traits, Context}; 9 | use {Dictionary, Error}; 10 | 11 | pub struct Subtitle(pub Super); 12 | 13 | impl Subtitle { 14 | pub fn open(mut self) -> Result { 15 | unsafe { 16 | match avcodec_open2(self.as_mut_ptr(), ptr::null(), ptr::null_mut()) { 17 | 0 => Ok(Encoder(self)), 18 | e => Err(Error::from(e)), 19 | } 20 | } 21 | } 22 | 23 | pub fn open_as(mut self, codec: E) -> Result { 24 | unsafe { 25 | if let Some(codec) = codec.encoder() { 26 | match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) { 27 | 0 => Ok(Encoder(self)), 28 | e => Err(Error::from(e)), 29 | } 30 | } else { 31 | Err(Error::EncoderNotFound) 32 | } 33 | } 34 | } 35 | 36 | pub fn open_as_with( 37 | mut self, 38 | codec: E, 39 | options: Dictionary, 40 | ) -> Result { 41 | unsafe { 42 | if let Some(codec) = codec.encoder() { 43 | let mut opts = options.disown(); 44 | let res = avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), &mut opts); 45 | 46 | Dictionary::own(opts); 47 | 48 | match res { 49 | 0 => Ok(Encoder(self)), 50 | e => Err(Error::from(e)), 51 | } 52 | } else { 53 | Err(Error::EncoderNotFound) 54 | } 55 | } 56 | } 57 | } 58 | 59 | impl Deref for Subtitle { 60 | type Target = Super; 61 | 62 | fn deref(&self) -> &::Target { 63 | &self.0 64 | } 65 | } 66 | 67 | impl DerefMut for Subtitle { 68 | fn deref_mut(&mut self) -> &mut ::Target { 69 | &mut self.0 70 | } 71 | } 72 | 73 | impl AsRef for Subtitle { 74 | fn as_ref(&self) -> &Context { 75 | self 76 | } 77 | } 78 | 79 | impl AsMut for Subtitle { 80 | fn as_mut(&mut self) -> &mut Context { 81 | &mut self.0 82 | } 83 | } 84 | 85 | pub struct Encoder(pub Subtitle); 86 | 87 | impl Encoder { 88 | pub fn encode(&mut self, subtitle: &::Subtitle, out: &mut [u8]) -> Result { 89 | unsafe { 90 | match avcodec_encode_subtitle( 91 | self.0.as_mut_ptr(), 92 | out.as_mut_ptr(), 93 | out.len() as c_int, 94 | subtitle.as_ptr(), 95 | ) { 96 | e if e < 0 => Err(Error::from(e)), 97 | _ => Ok(true), 98 | } 99 | } 100 | } 101 | } 102 | 103 | impl Deref for Encoder { 104 | type Target = Subtitle; 105 | 106 | fn deref(&self) -> &::Target { 107 | &self.0 108 | } 109 | } 110 | 111 | impl DerefMut for Encoder { 112 | fn deref_mut(&mut self) -> &mut ::Target { 113 | &mut self.0 114 | } 115 | } 116 | 117 | impl AsRef for Encoder { 118 | fn as_ref(&self) -> &Context { 119 | self 120 | } 121 | } 122 | 123 | impl AsMut for Encoder { 124 | fn as_mut(&mut self) -> &mut Context { 125 | &mut self.0 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/util/dictionary/owned.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::iter::FromIterator; 3 | use std::ops::{Deref, DerefMut}; 4 | use std::ptr; 5 | 6 | use super::mutable; 7 | use ffi::*; 8 | 9 | pub struct Owned<'a> { 10 | inner: mutable::Ref<'a>, 11 | } 12 | 13 | impl<'a> Default for Owned<'a> { 14 | fn default() -> Self { 15 | Self::new() 16 | } 17 | } 18 | 19 | impl<'a> Owned<'a> { 20 | pub unsafe fn own(ptr: *mut AVDictionary) -> Self { 21 | Owned { 22 | inner: mutable::Ref::wrap(ptr), 23 | } 24 | } 25 | 26 | pub unsafe fn disown(mut self) -> *mut AVDictionary { 27 | let result = self.inner.as_mut_ptr(); 28 | self.inner = mutable::Ref::wrap(ptr::null_mut()); 29 | 30 | result 31 | } 32 | } 33 | 34 | impl<'a> Owned<'a> { 35 | pub fn new() -> Self { 36 | unsafe { 37 | Owned { 38 | inner: mutable::Ref::wrap(ptr::null_mut()), 39 | } 40 | } 41 | } 42 | } 43 | 44 | impl<'a, 'b> FromIterator<(&'b str, &'b str)> for Owned<'a> { 45 | fn from_iter>(iterator: T) -> Self { 46 | let mut result = Owned::new(); 47 | 48 | for (key, value) in iterator { 49 | result.set(key, value); 50 | } 51 | 52 | result 53 | } 54 | } 55 | 56 | impl<'a, 'b> FromIterator<&'b (&'b str, &'b str)> for Owned<'a> { 57 | fn from_iter>(iterator: T) -> Self { 58 | let mut result = Owned::new(); 59 | 60 | for &(key, value) in iterator { 61 | result.set(key, value); 62 | } 63 | 64 | result 65 | } 66 | } 67 | 68 | impl<'a> FromIterator<(String, String)> for Owned<'a> { 69 | fn from_iter>(iterator: T) -> Self { 70 | let mut result = Owned::new(); 71 | 72 | for (key, value) in iterator { 73 | result.set(&key, &value); 74 | } 75 | 76 | result 77 | } 78 | } 79 | 80 | impl<'a, 'b> FromIterator<&'b (String, String)> for Owned<'a> { 81 | fn from_iter>(iterator: T) -> Self { 82 | let mut result = Owned::new(); 83 | 84 | for (key, value) in iterator { 85 | result.set(key, value); 86 | } 87 | 88 | result 89 | } 90 | } 91 | 92 | impl<'a> Deref for Owned<'a> { 93 | type Target = mutable::Ref<'a>; 94 | 95 | fn deref(&self) -> &Self::Target { 96 | &self.inner 97 | } 98 | } 99 | 100 | impl<'a> DerefMut for Owned<'a> { 101 | fn deref_mut(&mut self) -> &mut Self::Target { 102 | &mut self.inner 103 | } 104 | } 105 | 106 | impl<'a> Clone for Owned<'a> { 107 | fn clone(&self) -> Self { 108 | let mut dictionary = Owned::new(); 109 | dictionary.clone_from(self); 110 | 111 | dictionary 112 | } 113 | 114 | fn clone_from(&mut self, source: &Self) { 115 | unsafe { 116 | let mut ptr = self.as_mut_ptr(); 117 | av_dict_copy(&mut ptr, source.as_ptr(), 0); 118 | self.inner = mutable::Ref::wrap(ptr); 119 | } 120 | } 121 | } 122 | 123 | impl<'a> Drop for Owned<'a> { 124 | fn drop(&mut self) { 125 | unsafe { 126 | av_dict_free(&mut self.inner.as_mut_ptr()); 127 | } 128 | } 129 | } 130 | 131 | impl<'a> fmt::Debug for Owned<'a> { 132 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 133 | self.inner.fmt(fmt) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/codec/decoder/opened.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | use std::ptr; 3 | 4 | use super::{Audio, Decoder, Subtitle, Video}; 5 | use codec::{Context, Profile}; 6 | use ffi::*; 7 | use {media, packet, Error, Frame, Rational}; 8 | 9 | pub struct Opened(pub Decoder); 10 | 11 | impl Opened { 12 | pub fn video(self) -> Result { 13 | if self.medium() == media::Type::Video { 14 | Ok(Video(self)) 15 | } else { 16 | Err(Error::InvalidData) 17 | } 18 | } 19 | 20 | pub fn audio(self) -> Result { 21 | if self.medium() == media::Type::Audio { 22 | Ok(Audio(self)) 23 | } else { 24 | Err(Error::InvalidData) 25 | } 26 | } 27 | 28 | pub fn subtitle(self) -> Result { 29 | if self.medium() == media::Type::Subtitle { 30 | Ok(Subtitle(self)) 31 | } else { 32 | Err(Error::InvalidData) 33 | } 34 | } 35 | 36 | pub fn send_packet(&mut self, packet: &P) -> Result<(), Error> { 37 | unsafe { 38 | match avcodec_send_packet(self.as_mut_ptr(), packet.as_ptr()) { 39 | e if e < 0 => Err(Error::from(e)), 40 | _ => Ok(()), 41 | } 42 | } 43 | } 44 | 45 | /// Sends a NULL packet to the decoder to signal end of stream and enter 46 | /// draining mode. 47 | pub fn send_eof(&mut self) -> Result<(), Error> { 48 | unsafe { 49 | match avcodec_send_packet(self.as_mut_ptr(), ptr::null()) { 50 | e if e < 0 => Err(Error::from(e)), 51 | _ => Ok(()), 52 | } 53 | } 54 | } 55 | 56 | pub fn receive_frame(&mut self, frame: &mut Frame) -> Result<(), Error> { 57 | unsafe { 58 | match avcodec_receive_frame(self.as_mut_ptr(), frame.as_mut_ptr()) { 59 | e if e < 0 => Err(Error::from(e)), 60 | _ => Ok(()), 61 | } 62 | } 63 | } 64 | 65 | pub fn gop_size(&self) -> usize { 66 | unsafe { (*self.as_ptr()).gop_size as usize } 67 | } 68 | 69 | pub fn bit_rate(&self) -> usize { 70 | unsafe { (*self.as_ptr()).bit_rate as usize } 71 | } 72 | 73 | pub fn delay(&self) -> usize { 74 | unsafe { (*self.as_ptr()).delay as usize } 75 | } 76 | 77 | pub fn profile(&self) -> Profile { 78 | unsafe { Profile::from((self.id(), (*self.as_ptr()).profile)) } 79 | } 80 | 81 | pub fn frame_rate(&self) -> Option { 82 | unsafe { 83 | let value = (*self.as_ptr()).framerate; 84 | 85 | if value == (AVRational { num: 0, den: 1 }) { 86 | None 87 | } else { 88 | Some(Rational::from(value)) 89 | } 90 | } 91 | } 92 | 93 | pub fn flush(&mut self) { 94 | unsafe { 95 | avcodec_flush_buffers(self.as_mut_ptr()); 96 | } 97 | } 98 | } 99 | 100 | impl Drop for Opened { 101 | fn drop(&mut self) { 102 | unsafe { 103 | avcodec_close(self.as_mut_ptr()); 104 | } 105 | } 106 | } 107 | 108 | impl Deref for Opened { 109 | type Target = Decoder; 110 | 111 | fn deref(&self) -> &::Target { 112 | &self.0 113 | } 114 | } 115 | 116 | impl DerefMut for Opened { 117 | fn deref_mut(&mut self) -> &mut ::Target { 118 | &mut self.0 119 | } 120 | } 121 | 122 | impl AsRef for Opened { 123 | fn as_ref(&self) -> &Context { 124 | self 125 | } 126 | } 127 | 128 | impl AsMut for Opened { 129 | fn as_mut(&mut self) -> &mut Context { 130 | &mut self.0 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/codec/codec.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::str::from_utf8_unchecked; 3 | 4 | use super::{Audio, Capabilities, Id, Profile, Video}; 5 | use ffi::*; 6 | use {media, Error}; 7 | 8 | #[derive(PartialEq, Eq, Copy, Clone)] 9 | pub struct Codec { 10 | ptr: *mut AVCodec, 11 | } 12 | 13 | unsafe impl Send for Codec {} 14 | unsafe impl Sync for Codec {} 15 | 16 | impl Codec { 17 | pub unsafe fn wrap(ptr: *mut AVCodec) -> Self { 18 | Codec { ptr } 19 | } 20 | 21 | pub unsafe fn as_ptr(&self) -> *const AVCodec { 22 | self.ptr as *const _ 23 | } 24 | 25 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVCodec { 26 | self.ptr 27 | } 28 | } 29 | 30 | impl Codec { 31 | pub fn is_encoder(&self) -> bool { 32 | unsafe { av_codec_is_encoder(self.as_ptr()) != 0 } 33 | } 34 | 35 | pub fn is_decoder(&self) -> bool { 36 | unsafe { av_codec_is_decoder(self.as_ptr()) != 0 } 37 | } 38 | 39 | pub fn name(&self) -> &str { 40 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).name).to_bytes()) } 41 | } 42 | 43 | pub fn description(&self) -> &str { 44 | unsafe { 45 | let long_name = (*self.as_ptr()).long_name; 46 | if long_name.is_null() { 47 | "" 48 | } else { 49 | from_utf8_unchecked(CStr::from_ptr(long_name).to_bytes()) 50 | } 51 | } 52 | } 53 | 54 | pub fn medium(&self) -> media::Type { 55 | unsafe { media::Type::from((*self.as_ptr()).type_) } 56 | } 57 | 58 | pub fn id(&self) -> Id { 59 | unsafe { Id::from((*self.as_ptr()).id) } 60 | } 61 | 62 | pub fn is_video(&self) -> bool { 63 | self.medium() == media::Type::Video 64 | } 65 | 66 | pub fn video(self) -> Result { 67 | unsafe { 68 | if self.medium() == media::Type::Video { 69 | Ok(Video::new(self)) 70 | } else { 71 | Err(Error::InvalidData) 72 | } 73 | } 74 | } 75 | 76 | pub fn is_audio(&self) -> bool { 77 | self.medium() == media::Type::Audio 78 | } 79 | 80 | pub fn audio(self) -> Result { 81 | unsafe { 82 | if self.medium() == media::Type::Audio { 83 | Ok(Audio::new(self)) 84 | } else { 85 | Err(Error::InvalidData) 86 | } 87 | } 88 | } 89 | 90 | pub fn max_lowres(&self) -> i32 { 91 | unsafe { (*self.as_ptr()).max_lowres.into() } 92 | } 93 | 94 | pub fn capabilities(&self) -> Capabilities { 95 | unsafe { Capabilities::from_bits_truncate((*self.as_ptr()).capabilities as u32) } 96 | } 97 | 98 | pub fn profiles(&self) -> Option { 99 | unsafe { 100 | if (*self.as_ptr()).profiles.is_null() { 101 | None 102 | } else { 103 | Some(ProfileIter::new(self.id(), (*self.as_ptr()).profiles)) 104 | } 105 | } 106 | } 107 | } 108 | 109 | pub struct ProfileIter { 110 | id: Id, 111 | ptr: *const AVProfile, 112 | } 113 | 114 | impl ProfileIter { 115 | pub fn new(id: Id, ptr: *const AVProfile) -> Self { 116 | ProfileIter { id, ptr } 117 | } 118 | } 119 | 120 | impl Iterator for ProfileIter { 121 | type Item = Profile; 122 | 123 | fn next(&mut self) -> Option<::Item> { 124 | unsafe { 125 | if (*self.ptr).profile == FF_PROFILE_UNKNOWN { 126 | return None; 127 | } 128 | 129 | let profile = Profile::from((self.id, (*self.ptr).profile)); 130 | self.ptr = self.ptr.offset(1); 131 | 132 | Some(profile) 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/util/channel_layout.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_ulonglong; 3 | 4 | bitflags! { 5 | pub struct ChannelLayout: c_ulonglong { 6 | const FRONT_LEFT = AV_CH_FRONT_LEFT; 7 | const FRONT_RIGHT = AV_CH_FRONT_RIGHT; 8 | const FRONT_CENTER = AV_CH_FRONT_CENTER; 9 | const LOW_FREQUENCY = AV_CH_LOW_FREQUENCY; 10 | const BACK_LEFT = AV_CH_BACK_LEFT; 11 | const BACK_RIGHT = AV_CH_BACK_RIGHT; 12 | const FRONT_LEFT_OF_CENTER = AV_CH_FRONT_LEFT_OF_CENTER; 13 | const FRONT_RIGHT_OF_CENTER = AV_CH_FRONT_RIGHT_OF_CENTER; 14 | const BACK_CENTER = AV_CH_BACK_CENTER; 15 | const SIDE_LEFT = AV_CH_SIDE_LEFT; 16 | const SIDE_RIGHT = AV_CH_SIDE_RIGHT; 17 | const TOP_CENTER = AV_CH_TOP_CENTER; 18 | const TOP_FRONT_LEFT = AV_CH_TOP_FRONT_LEFT; 19 | const TOP_FRONT_CENTER = AV_CH_TOP_FRONT_CENTER; 20 | const TOP_FRONT_RIGHT = AV_CH_TOP_FRONT_RIGHT; 21 | const TOP_BACK_LEFT = AV_CH_TOP_BACK_LEFT; 22 | const TOP_BACK_CENTER = AV_CH_TOP_BACK_CENTER; 23 | const TOP_BACK_RIGHT = AV_CH_TOP_BACK_RIGHT; 24 | const STEREO_LEFT = AV_CH_STEREO_LEFT; 25 | const STEREO_RIGHT = AV_CH_STEREO_RIGHT; 26 | const WIDE_LEFT = AV_CH_WIDE_LEFT; 27 | const WIDE_RIGHT = AV_CH_WIDE_RIGHT; 28 | const SURROUND_DIRECT_LEFT = AV_CH_SURROUND_DIRECT_LEFT; 29 | const SURROUND_DIRECT_RIGHT = AV_CH_SURROUND_DIRECT_RIGHT; 30 | const LOW_FREQUENCY_2 = AV_CH_LOW_FREQUENCY_2; 31 | const NATIVE = AV_CH_LAYOUT_NATIVE; 32 | 33 | const MONO = AV_CH_LAYOUT_MONO; 34 | const STEREO = AV_CH_LAYOUT_STEREO; 35 | const _2POINT1 = AV_CH_LAYOUT_2POINT1; 36 | const _2_1 = AV_CH_LAYOUT_2_1; 37 | const SURROUND = AV_CH_LAYOUT_SURROUND; 38 | const _3POINT1 = AV_CH_LAYOUT_3POINT1; 39 | const _4POINT0 = AV_CH_LAYOUT_4POINT0; 40 | const _4POINT1 = AV_CH_LAYOUT_4POINT1; 41 | const _2_2 = AV_CH_LAYOUT_2_2; 42 | const QUAD = AV_CH_LAYOUT_QUAD; 43 | const _5POINT0 = AV_CH_LAYOUT_5POINT0; 44 | const _5POINT1 = AV_CH_LAYOUT_5POINT1; 45 | const _5POINT0_BACK = AV_CH_LAYOUT_5POINT0_BACK; 46 | const _5POINT1_BACK = AV_CH_LAYOUT_5POINT1_BACK; 47 | const _6POINT0 = AV_CH_LAYOUT_6POINT0; 48 | const _6POINT0_FRONT = AV_CH_LAYOUT_6POINT0_FRONT; 49 | const HEXAGONAL = AV_CH_LAYOUT_HEXAGONAL; 50 | const _6POINT1 = AV_CH_LAYOUT_6POINT1; 51 | const _6POINT1_BACK = AV_CH_LAYOUT_6POINT1_BACK; 52 | const _6POINT1_FRONT = AV_CH_LAYOUT_6POINT1_FRONT; 53 | const _7POINT0 = AV_CH_LAYOUT_7POINT0; 54 | const _7POINT0_FRONT = AV_CH_LAYOUT_7POINT0_FRONT; 55 | const _7POINT1 = AV_CH_LAYOUT_7POINT1; 56 | const _7POINT1_WIDE = AV_CH_LAYOUT_7POINT1_WIDE; 57 | const _7POINT1_WIDE_BACK = AV_CH_LAYOUT_7POINT1_WIDE_BACK; 58 | const OCTAGONAL = AV_CH_LAYOUT_OCTAGONAL; 59 | const HEXADECAGONAL = AV_CH_LAYOUT_HEXADECAGONAL; 60 | const STEREO_DOWNMIX = AV_CH_LAYOUT_STEREO_DOWNMIX; 61 | } 62 | } 63 | 64 | impl ChannelLayout { 65 | #[inline] 66 | pub fn channels(&self) -> i32 { 67 | unsafe { av_get_channel_layout_nb_channels(self.bits()) } 68 | } 69 | 70 | pub fn default(number: i32) -> ChannelLayout { 71 | unsafe { 72 | ChannelLayout::from_bits_truncate(av_get_default_channel_layout(number) as c_ulonglong) 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/codec/audio.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use super::codec::Codec; 4 | use ffi::*; 5 | use {format, ChannelLayout}; 6 | 7 | #[derive(PartialEq, Eq, Copy, Clone)] 8 | pub struct Audio { 9 | codec: Codec, 10 | } 11 | 12 | impl Audio { 13 | pub unsafe fn new(codec: Codec) -> Audio { 14 | Audio { codec } 15 | } 16 | } 17 | 18 | impl Audio { 19 | pub fn rates(&self) -> Option { 20 | unsafe { 21 | if (*self.as_ptr()).supported_samplerates.is_null() { 22 | None 23 | } else { 24 | Some(RateIter::new((*self.codec.as_ptr()).supported_samplerates)) 25 | } 26 | } 27 | } 28 | 29 | pub fn formats(&self) -> Option { 30 | unsafe { 31 | if (*self.codec.as_ptr()).sample_fmts.is_null() { 32 | None 33 | } else { 34 | Some(FormatIter::new((*self.codec.as_ptr()).sample_fmts)) 35 | } 36 | } 37 | } 38 | 39 | pub fn channel_layouts(&self) -> Option { 40 | unsafe { 41 | if (*self.codec.as_ptr()).channel_layouts.is_null() { 42 | None 43 | } else { 44 | Some(ChannelLayoutIter::new( 45 | (*self.codec.as_ptr()).channel_layouts, 46 | )) 47 | } 48 | } 49 | } 50 | } 51 | 52 | impl Deref for Audio { 53 | type Target = Codec; 54 | 55 | fn deref(&self) -> &Self::Target { 56 | &self.codec 57 | } 58 | } 59 | 60 | pub struct RateIter { 61 | ptr: *const i32, 62 | } 63 | 64 | impl RateIter { 65 | pub fn new(ptr: *const i32) -> Self { 66 | RateIter { ptr } 67 | } 68 | } 69 | 70 | impl Iterator for RateIter { 71 | type Item = i32; 72 | 73 | fn next(&mut self) -> Option<::Item> { 74 | unsafe { 75 | if *self.ptr == 0 { 76 | return None; 77 | } 78 | 79 | let rate = *self.ptr; 80 | self.ptr = self.ptr.offset(1); 81 | 82 | Some(rate) 83 | } 84 | } 85 | } 86 | 87 | pub struct FormatIter { 88 | ptr: *const AVSampleFormat, 89 | } 90 | 91 | impl FormatIter { 92 | pub fn new(ptr: *const AVSampleFormat) -> Self { 93 | FormatIter { ptr } 94 | } 95 | } 96 | 97 | impl Iterator for FormatIter { 98 | type Item = format::Sample; 99 | 100 | fn next(&mut self) -> Option<::Item> { 101 | unsafe { 102 | if *self.ptr == AVSampleFormat::AV_SAMPLE_FMT_NONE { 103 | return None; 104 | } 105 | 106 | let format = (*self.ptr).into(); 107 | self.ptr = self.ptr.offset(1); 108 | 109 | Some(format) 110 | } 111 | } 112 | } 113 | 114 | pub struct ChannelLayoutIter { 115 | ptr: *const u64, 116 | } 117 | 118 | impl ChannelLayoutIter { 119 | pub fn new(ptr: *const u64) -> Self { 120 | ChannelLayoutIter { ptr } 121 | } 122 | 123 | pub fn best(self, max: i32) -> ChannelLayout { 124 | self.fold(ChannelLayout::MONO, |acc, cur| { 125 | if cur.channels() > acc.channels() && cur.channels() <= max { 126 | cur 127 | } else { 128 | acc 129 | } 130 | }) 131 | } 132 | } 133 | 134 | impl Iterator for ChannelLayoutIter { 135 | type Item = ChannelLayout; 136 | 137 | fn next(&mut self) -> Option<::Item> { 138 | unsafe { 139 | if *self.ptr == 0 { 140 | return None; 141 | } 142 | 143 | let layout = ChannelLayout::from_bits_truncate(*self.ptr); 144 | self.ptr = self.ptr.offset(1); 145 | 146 | Some(layout) 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | #![allow(clippy::missing_safety_doc)] 3 | #![allow(clippy::module_inception)] 4 | #![allow(clippy::too_many_arguments)] 5 | 6 | #[macro_use] 7 | extern crate bitflags; 8 | pub extern crate ffmpeg_sys_next as sys; 9 | #[cfg(feature = "image")] 10 | extern crate image; 11 | extern crate libc; 12 | extern crate log as log_crate; 13 | 14 | pub use sys as ffi; 15 | 16 | #[macro_use] 17 | pub mod util; 18 | pub use util::channel_layout::{self, ChannelLayout}; 19 | pub use util::chroma; 20 | pub use util::color; 21 | pub use util::dictionary; 22 | pub use util::dictionary::Mut as DictionaryMut; 23 | pub use util::dictionary::Owned as Dictionary; 24 | pub use util::dictionary::Ref as DictionaryRef; 25 | pub use util::error::{self, Error}; 26 | pub use util::frame::{self, Frame}; 27 | pub use util::log; 28 | pub use util::mathematics::{self, rescale, Rescale, Rounding}; 29 | pub use util::media; 30 | pub use util::option; 31 | pub use util::picture; 32 | pub use util::rational::{self, Rational}; 33 | pub use util::time; 34 | 35 | #[cfg(feature = "format")] 36 | pub mod format; 37 | #[cfg(feature = "format")] 38 | pub use format::chapter::{Chapter, ChapterMut}; 39 | #[cfg(feature = "format")] 40 | pub use format::format::Format; 41 | #[cfg(feature = "format")] 42 | pub use format::stream::{Stream, StreamMut}; 43 | 44 | #[cfg(feature = "codec")] 45 | pub mod codec; 46 | #[cfg(feature = "codec")] 47 | pub use codec::audio_service::AudioService; 48 | #[cfg(feature = "codec")] 49 | pub use codec::codec::Codec; 50 | #[cfg(feature = "codec")] 51 | pub use codec::discard::Discard; 52 | #[cfg(feature = "codec")] 53 | pub use codec::field_order::FieldOrder; 54 | #[cfg(feature = "codec")] 55 | pub use codec::packet::{self, Packet}; 56 | #[cfg(all(feature = "codec", not(feature = "ffmpeg_5_0")))] 57 | pub use codec::picture::Picture; 58 | #[cfg(feature = "codec")] 59 | pub use codec::subtitle::{self, Subtitle}; 60 | #[cfg(feature = "codec")] 61 | pub use codec::threading; 62 | #[cfg(feature = "codec")] 63 | pub use codec::{decoder, encoder}; 64 | 65 | #[cfg(feature = "device")] 66 | pub mod device; 67 | 68 | #[cfg(feature = "filter")] 69 | pub mod filter; 70 | #[cfg(feature = "filter")] 71 | pub use filter::Filter; 72 | 73 | pub mod software; 74 | 75 | fn init_error() { 76 | util::error::register_all(); 77 | } 78 | 79 | #[cfg(all(feature = "format", not(feature = "ffmpeg_5_0")))] 80 | fn init_format() { 81 | format::register_all(); 82 | } 83 | 84 | #[cfg(not(feature = "format"))] 85 | fn init_format() {} 86 | 87 | #[cfg(feature = "device")] 88 | fn init_device() { 89 | device::register_all(); 90 | } 91 | 92 | #[cfg(not(feature = "device"))] 93 | fn init_device() {} 94 | 95 | #[cfg(all(feature = "filter", not(feature = "ffmpeg_5_0")))] 96 | fn init_filter() { 97 | filter::register_all(); 98 | } 99 | 100 | #[cfg(not(feature = "filter"))] 101 | fn init_filter() {} 102 | 103 | #[cfg(all( 104 | target_arch = "x86_64", 105 | any(target_family = "windows", target_family = "unix") 106 | ))] 107 | fn init_log() { 108 | util::log::callback::set_logging_callback(); 109 | } 110 | 111 | #[cfg(not(all( 112 | target_arch = "x86_64", 113 | any(target_family = "windows", target_family = "unix") 114 | )))] 115 | fn init_log() {} 116 | 117 | #[cfg_attr( 118 | any(feature = "ffmpeg4", feature = "ffmpeg41", feature = "ffmpeg42"), 119 | deprecated( 120 | note = "features ffmpeg4/ffmpeg41/ffmpeg42/ffmpeg43 are now auto-detected \ 121 | and will be removed in a future version" 122 | ) 123 | )] 124 | pub fn init() -> Result<(), Error> { 125 | init_error(); 126 | #[cfg(not(feature = "ffmpeg_5_0"))] 127 | init_format(); 128 | init_device(); 129 | #[cfg(not(feature = "ffmpeg_5_0"))] 130 | init_filter(); 131 | init_log(); 132 | 133 | Ok(()) 134 | } 135 | -------------------------------------------------------------------------------- /src/format/stream/stream.rs: -------------------------------------------------------------------------------- 1 | use super::Disposition; 2 | use codec::{self, packet}; 3 | use ffi::*; 4 | use format::context::common::Context; 5 | use libc::c_int; 6 | use {DictionaryRef, Discard, Rational}; 7 | 8 | #[derive(Debug)] 9 | pub struct Stream<'a> { 10 | context: &'a Context, 11 | index: usize, 12 | } 13 | 14 | impl<'a> Stream<'a> { 15 | pub unsafe fn wrap(context: &Context, index: usize) -> Stream { 16 | Stream { context, index } 17 | } 18 | 19 | pub unsafe fn as_ptr(&self) -> *const AVStream { 20 | *(*self.context.as_ptr()).streams.add(self.index) 21 | } 22 | } 23 | 24 | impl<'a> Stream<'a> { 25 | pub fn id(&self) -> i32 { 26 | unsafe { (*self.as_ptr()).id } 27 | } 28 | 29 | #[cfg(not(feature = "ffmpeg_5_0"))] 30 | pub fn codec(&self) -> codec::Context { 31 | unsafe { codec::Context::wrap((*self.as_ptr()).codec, Some(self.context.destructor())) } 32 | } 33 | 34 | pub fn parameters(&self) -> codec::Parameters { 35 | unsafe { 36 | codec::Parameters::wrap((*self.as_ptr()).codecpar, Some(self.context.destructor())) 37 | } 38 | } 39 | 40 | pub fn index(&self) -> usize { 41 | unsafe { (*self.as_ptr()).index as usize } 42 | } 43 | 44 | pub fn time_base(&self) -> Rational { 45 | unsafe { Rational::from((*self.as_ptr()).time_base) } 46 | } 47 | 48 | pub fn start_time(&self) -> i64 { 49 | unsafe { (*self.as_ptr()).start_time } 50 | } 51 | 52 | pub fn duration(&self) -> i64 { 53 | unsafe { (*self.as_ptr()).duration } 54 | } 55 | 56 | pub fn frames(&self) -> i64 { 57 | unsafe { (*self.as_ptr()).nb_frames } 58 | } 59 | 60 | pub fn disposition(&self) -> Disposition { 61 | unsafe { Disposition::from_bits_truncate((*self.as_ptr()).disposition) } 62 | } 63 | 64 | pub fn discard(&self) -> Discard { 65 | unsafe { Discard::from((*self.as_ptr()).discard) } 66 | } 67 | 68 | pub fn side_data(&self) -> SideDataIter { 69 | SideDataIter::new(self) 70 | } 71 | 72 | pub fn rate(&self) -> Rational { 73 | unsafe { Rational::from((*self.as_ptr()).r_frame_rate) } 74 | } 75 | 76 | pub fn avg_frame_rate(&self) -> Rational { 77 | unsafe { Rational::from((*self.as_ptr()).avg_frame_rate) } 78 | } 79 | 80 | pub fn metadata(&self) -> DictionaryRef { 81 | unsafe { DictionaryRef::wrap((*self.as_ptr()).metadata) } 82 | } 83 | } 84 | 85 | impl<'a> PartialEq for Stream<'a> { 86 | fn eq(&self, other: &Self) -> bool { 87 | unsafe { self.as_ptr() == other.as_ptr() } 88 | } 89 | } 90 | 91 | impl<'a> Eq for Stream<'a> {} 92 | 93 | pub struct SideDataIter<'a> { 94 | stream: &'a Stream<'a>, 95 | current: c_int, 96 | } 97 | 98 | impl<'a> SideDataIter<'a> { 99 | pub fn new<'sd, 's: 'sd>(stream: &'s Stream) -> SideDataIter<'sd> { 100 | SideDataIter { stream, current: 0 } 101 | } 102 | } 103 | 104 | impl<'a> Iterator for SideDataIter<'a> { 105 | type Item = packet::SideData<'a>; 106 | 107 | fn next(&mut self) -> Option<::Item> { 108 | unsafe { 109 | if self.current >= (*self.stream.as_ptr()).nb_side_data { 110 | return None; 111 | } 112 | 113 | self.current += 1; 114 | 115 | Some(packet::SideData::wrap( 116 | (*self.stream.as_ptr()) 117 | .side_data 118 | .offset((self.current - 1) as isize), 119 | )) 120 | } 121 | } 122 | 123 | fn size_hint(&self) -> (usize, Option) { 124 | unsafe { 125 | let length = (*self.stream.as_ptr()).nb_side_data as usize; 126 | 127 | ( 128 | length - self.current as usize, 129 | Some(length - self.current as usize), 130 | ) 131 | } 132 | } 133 | } 134 | 135 | impl<'a> ExactSizeIterator for SideDataIter<'a> {} 136 | -------------------------------------------------------------------------------- /src/codec/decoder/audio.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | #[cfg(not(feature = "ffmpeg_5_0"))] 4 | use ffi::*; 5 | #[cfg(not(feature = "ffmpeg_5_0"))] 6 | use libc::c_int; 7 | 8 | use super::Opened; 9 | use codec::Context; 10 | #[cfg(not(feature = "ffmpeg_5_0"))] 11 | use frame; 12 | use util::format; 13 | #[cfg(not(feature = "ffmpeg_5_0"))] 14 | use {packet, Error}; 15 | use {AudioService, ChannelLayout}; 16 | 17 | pub struct Audio(pub Opened); 18 | 19 | impl Audio { 20 | #[deprecated( 21 | since = "4.4.0", 22 | note = "Underlying API avcodec_decode_audio4 has been deprecated since FFmpeg 3.1; \ 23 | consider switching to send_packet() and receive_frame()" 24 | )] 25 | #[cfg(not(feature = "ffmpeg_5_0"))] 26 | pub fn decode( 27 | &mut self, 28 | packet: &P, 29 | out: &mut frame::Audio, 30 | ) -> Result { 31 | unsafe { 32 | let mut got: c_int = 0; 33 | 34 | match avcodec_decode_audio4( 35 | self.as_mut_ptr(), 36 | out.as_mut_ptr(), 37 | &mut got, 38 | packet.as_ptr(), 39 | ) { 40 | e if e < 0 => Err(Error::from(e)), 41 | _ => Ok(got != 0), 42 | } 43 | } 44 | } 45 | 46 | pub fn rate(&self) -> u32 { 47 | unsafe { (*self.as_ptr()).sample_rate as u32 } 48 | } 49 | 50 | pub fn channels(&self) -> u16 { 51 | unsafe { (*self.as_ptr()).channels as u16 } 52 | } 53 | 54 | pub fn format(&self) -> format::Sample { 55 | unsafe { format::Sample::from((*self.as_ptr()).sample_fmt) } 56 | } 57 | 58 | pub fn request_format(&mut self, value: format::Sample) { 59 | unsafe { 60 | (*self.as_mut_ptr()).request_sample_fmt = value.into(); 61 | } 62 | } 63 | 64 | pub fn frames(&self) -> usize { 65 | unsafe { (*self.as_ptr()).frame_number as usize } 66 | } 67 | 68 | pub fn align(&self) -> usize { 69 | unsafe { (*self.as_ptr()).block_align as usize } 70 | } 71 | 72 | pub fn channel_layout(&self) -> ChannelLayout { 73 | unsafe { ChannelLayout::from_bits_truncate((*self.as_ptr()).channel_layout) } 74 | } 75 | 76 | pub fn set_channel_layout(&mut self, value: ChannelLayout) { 77 | unsafe { 78 | (*self.as_mut_ptr()).channel_layout = value.bits(); 79 | } 80 | } 81 | 82 | pub fn request_channel_layout(&mut self, value: ChannelLayout) { 83 | unsafe { 84 | (*self.as_mut_ptr()).request_channel_layout = value.bits(); 85 | } 86 | } 87 | 88 | pub fn audio_service(&mut self) -> AudioService { 89 | unsafe { AudioService::from((*self.as_mut_ptr()).audio_service_type) } 90 | } 91 | 92 | pub fn max_bit_rate(&self) -> usize { 93 | unsafe { (*self.as_ptr()).rc_max_rate as usize } 94 | } 95 | 96 | pub fn frame_size(&self) -> u32 { 97 | unsafe { (*self.as_ptr()).frame_size as u32 } 98 | } 99 | 100 | #[cfg(not(feature = "ffmpeg_5_0"))] 101 | pub fn frame_start(&self) -> Option { 102 | unsafe { 103 | // Removed in ffmpeg >= 5.0 in favor of using encoder 104 | // private options. 105 | match (*self.as_ptr()).timecode_frame_start { 106 | -1 => None, 107 | n => Some(n as usize), 108 | } 109 | } 110 | } 111 | } 112 | 113 | impl Deref for Audio { 114 | type Target = Opened; 115 | 116 | fn deref(&self) -> &::Target { 117 | &self.0 118 | } 119 | } 120 | 121 | impl DerefMut for Audio { 122 | fn deref_mut(&mut self) -> &mut ::Target { 123 | &mut self.0 124 | } 125 | } 126 | 127 | impl AsRef for Audio { 128 | fn as_ref(&self) -> &Context { 129 | self 130 | } 131 | } 132 | 133 | impl AsMut for Audio { 134 | fn as_mut(&mut self) -> &mut Context { 135 | &mut self.0 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/software/scaling/vector.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::slice; 3 | 4 | use ffi::*; 5 | use libc::{c_double, c_int}; 6 | 7 | pub struct Vector<'a> { 8 | ptr: *mut SwsVector, 9 | 10 | _own: bool, 11 | _marker: PhantomData<&'a ()>, 12 | } 13 | 14 | impl<'a> Vector<'a> { 15 | pub unsafe fn wrap(ptr: *mut SwsVector) -> Self { 16 | Vector { 17 | ptr, 18 | _own: false, 19 | _marker: PhantomData, 20 | } 21 | } 22 | 23 | pub unsafe fn as_ptr(&self) -> *const SwsVector { 24 | self.ptr as *const _ 25 | } 26 | 27 | pub unsafe fn as_mut_ptr(&mut self) -> *mut SwsVector { 28 | self.ptr 29 | } 30 | } 31 | 32 | impl<'a> Vector<'a> { 33 | pub fn new(length: usize) -> Self { 34 | unsafe { 35 | Vector { 36 | ptr: sws_allocVec(length as c_int), 37 | _own: true, 38 | _marker: PhantomData, 39 | } 40 | } 41 | } 42 | 43 | pub fn gaussian(variance: f64, quality: f64) -> Self { 44 | unsafe { 45 | Vector { 46 | ptr: sws_getGaussianVec(variance as c_double, quality as c_double), 47 | _own: true, 48 | _marker: PhantomData, 49 | } 50 | } 51 | } 52 | 53 | #[cfg(not(feature = "ffmpeg_5_0"))] 54 | pub fn value(value: f64, length: usize) -> Self { 55 | unsafe { 56 | Vector { 57 | ptr: sws_getConstVec(value as c_double, length as c_int), 58 | _own: true, 59 | _marker: PhantomData, 60 | } 61 | } 62 | } 63 | 64 | #[cfg(not(feature = "ffmpeg_5_0"))] 65 | pub fn identity() -> Self { 66 | unsafe { 67 | Vector { 68 | ptr: sws_getIdentityVec(), 69 | _own: true, 70 | _marker: PhantomData, 71 | } 72 | } 73 | } 74 | 75 | pub fn scale(&mut self, scalar: f64) { 76 | unsafe { 77 | sws_scaleVec(self.as_mut_ptr(), scalar as c_double); 78 | } 79 | } 80 | 81 | pub fn normalize(&mut self, height: f64) { 82 | unsafe { 83 | sws_normalizeVec(self.as_mut_ptr(), height as c_double); 84 | } 85 | } 86 | 87 | #[cfg(not(feature = "ffmpeg_5_0"))] 88 | pub fn conv(&mut self, other: &Vector) { 89 | unsafe { 90 | sws_convVec(self.as_mut_ptr(), other.as_ptr() as *mut _); 91 | } 92 | } 93 | 94 | #[cfg(not(feature = "ffmpeg_5_0"))] 95 | pub fn add(&mut self, other: &Vector) { 96 | unsafe { 97 | sws_addVec(self.as_mut_ptr(), other.as_ptr() as *mut _); 98 | } 99 | } 100 | 101 | #[cfg(not(feature = "ffmpeg_5_0"))] 102 | pub fn sub(&mut self, other: &Vector) { 103 | unsafe { 104 | sws_subVec(self.as_mut_ptr(), other.as_ptr() as *mut _); 105 | } 106 | } 107 | 108 | #[cfg(not(feature = "ffmpeg_5_0"))] 109 | pub fn shift(&mut self, value: usize) { 110 | unsafe { 111 | sws_shiftVec(self.as_mut_ptr(), value as c_int); 112 | } 113 | } 114 | 115 | pub fn coefficients(&self) -> &[f64] { 116 | unsafe { slice::from_raw_parts((*self.as_ptr()).coeff, (*self.as_ptr()).length as usize) } 117 | } 118 | 119 | pub fn coefficients_mut(&self) -> &[f64] { 120 | unsafe { 121 | slice::from_raw_parts_mut((*self.as_ptr()).coeff, (*self.as_ptr()).length as usize) 122 | } 123 | } 124 | } 125 | 126 | #[cfg(not(feature = "ffmpeg_5_0"))] 127 | impl<'a> Clone for Vector<'a> { 128 | fn clone(&self) -> Self { 129 | unsafe { 130 | Vector { 131 | ptr: sws_cloneVec(self.as_ptr() as *mut _), 132 | _own: true, 133 | _marker: PhantomData, 134 | } 135 | } 136 | } 137 | } 138 | 139 | impl<'a> Drop for Vector<'a> { 140 | fn drop(&mut self) { 141 | unsafe { 142 | if self._own { 143 | sws_freeVec(self.as_mut_ptr()); 144 | } 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/util/color/transfer_characteristic.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::str::from_utf8_unchecked; 3 | 4 | use ffi::AVColorTransferCharacteristic::*; 5 | use ffi::*; 6 | 7 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 8 | pub enum TransferCharacteristic { 9 | Reserved0, 10 | BT709, 11 | Unspecified, 12 | Reserved, 13 | GAMMA22, 14 | GAMMA28, 15 | SMPTE170M, 16 | SMPTE240M, 17 | Linear, 18 | Log, 19 | LogSqrt, 20 | IEC61966_2_4, 21 | BT1361_ECG, 22 | IEC61966_2_1, 23 | BT2020_10, 24 | BT2020_12, 25 | SMPTE2084, 26 | SMPTE428, 27 | ARIB_STD_B67, 28 | } 29 | 30 | impl TransferCharacteristic { 31 | pub fn name(&self) -> Option<&'static str> { 32 | if *self == TransferCharacteristic::Unspecified { 33 | return None; 34 | } 35 | unsafe { 36 | let ptr = av_color_transfer_name((*self).into()); 37 | ptr.as_ref() 38 | .map(|ptr| from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) 39 | } 40 | } 41 | } 42 | 43 | impl From for TransferCharacteristic { 44 | fn from(value: AVColorTransferCharacteristic) -> TransferCharacteristic { 45 | match value { 46 | AVCOL_TRC_RESERVED0 => TransferCharacteristic::Reserved0, 47 | AVCOL_TRC_BT709 => TransferCharacteristic::BT709, 48 | AVCOL_TRC_UNSPECIFIED => TransferCharacteristic::Unspecified, 49 | AVCOL_TRC_RESERVED => TransferCharacteristic::Reserved, 50 | AVCOL_TRC_GAMMA22 => TransferCharacteristic::GAMMA22, 51 | AVCOL_TRC_GAMMA28 => TransferCharacteristic::GAMMA28, 52 | AVCOL_TRC_SMPTE170M => TransferCharacteristic::SMPTE170M, 53 | AVCOL_TRC_SMPTE240M => TransferCharacteristic::SMPTE240M, 54 | AVCOL_TRC_LINEAR => TransferCharacteristic::Linear, 55 | AVCOL_TRC_LOG => TransferCharacteristic::Log, 56 | AVCOL_TRC_LOG_SQRT => TransferCharacteristic::LogSqrt, 57 | AVCOL_TRC_IEC61966_2_4 => TransferCharacteristic::IEC61966_2_4, 58 | AVCOL_TRC_BT1361_ECG => TransferCharacteristic::BT1361_ECG, 59 | AVCOL_TRC_IEC61966_2_1 => TransferCharacteristic::IEC61966_2_1, 60 | AVCOL_TRC_BT2020_10 => TransferCharacteristic::BT2020_10, 61 | AVCOL_TRC_BT2020_12 => TransferCharacteristic::BT2020_12, 62 | AVCOL_TRC_NB => TransferCharacteristic::Reserved0, 63 | AVCOL_TRC_SMPTE2084 => TransferCharacteristic::SMPTE2084, 64 | AVCOL_TRC_SMPTE428 => TransferCharacteristic::SMPTE428, 65 | AVCOL_TRC_ARIB_STD_B67 => TransferCharacteristic::ARIB_STD_B67, 66 | } 67 | } 68 | } 69 | 70 | impl From for AVColorTransferCharacteristic { 71 | fn from(value: TransferCharacteristic) -> AVColorTransferCharacteristic { 72 | match value { 73 | TransferCharacteristic::Reserved0 => AVCOL_TRC_RESERVED0, 74 | TransferCharacteristic::BT709 => AVCOL_TRC_BT709, 75 | TransferCharacteristic::Unspecified => AVCOL_TRC_UNSPECIFIED, 76 | TransferCharacteristic::Reserved => AVCOL_TRC_RESERVED, 77 | TransferCharacteristic::GAMMA22 => AVCOL_TRC_GAMMA22, 78 | TransferCharacteristic::GAMMA28 => AVCOL_TRC_GAMMA28, 79 | TransferCharacteristic::SMPTE170M => AVCOL_TRC_SMPTE170M, 80 | TransferCharacteristic::SMPTE240M => AVCOL_TRC_SMPTE240M, 81 | TransferCharacteristic::Linear => AVCOL_TRC_LINEAR, 82 | TransferCharacteristic::Log => AVCOL_TRC_LOG, 83 | TransferCharacteristic::LogSqrt => AVCOL_TRC_LOG_SQRT, 84 | TransferCharacteristic::IEC61966_2_4 => AVCOL_TRC_IEC61966_2_4, 85 | TransferCharacteristic::BT1361_ECG => AVCOL_TRC_BT1361_ECG, 86 | TransferCharacteristic::IEC61966_2_1 => AVCOL_TRC_IEC61966_2_1, 87 | TransferCharacteristic::BT2020_10 => AVCOL_TRC_BT2020_10, 88 | TransferCharacteristic::BT2020_12 => AVCOL_TRC_BT2020_12, 89 | TransferCharacteristic::SMPTE2084 => AVCOL_TRC_SMPTE2084, 90 | TransferCharacteristic::SMPTE428 => AVCOL_TRC_SMPTE428, 91 | TransferCharacteristic::ARIB_STD_B67 => AVCOL_TRC_ARIB_STD_B67, 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/codec/subtitle/rect.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::marker::PhantomData; 3 | use std::str::from_utf8_unchecked; 4 | 5 | use super::{Flags, Type}; 6 | use ffi::*; 7 | #[cfg(not(feature = "ffmpeg_5_0"))] 8 | use {format, Picture}; 9 | 10 | pub enum Rect<'a> { 11 | None(*const AVSubtitleRect), 12 | Bitmap(Bitmap<'a>), 13 | Text(Text<'a>), 14 | Ass(Ass<'a>), 15 | } 16 | 17 | impl<'a> Rect<'a> { 18 | pub unsafe fn wrap(ptr: *const AVSubtitleRect) -> Self { 19 | match Type::from((*ptr).type_) { 20 | Type::None => Rect::None(ptr), 21 | Type::Bitmap => Rect::Bitmap(Bitmap::wrap(ptr)), 22 | Type::Text => Rect::Text(Text::wrap(ptr)), 23 | Type::Ass => Rect::Ass(Ass::wrap(ptr)), 24 | } 25 | } 26 | 27 | pub unsafe fn as_ptr(&self) -> *const AVSubtitleRect { 28 | match *self { 29 | Rect::None(ptr) => ptr, 30 | Rect::Bitmap(ref b) => b.as_ptr(), 31 | Rect::Text(ref t) => t.as_ptr(), 32 | Rect::Ass(ref a) => a.as_ptr(), 33 | } 34 | } 35 | } 36 | 37 | impl<'a> Rect<'a> { 38 | pub fn flags(&self) -> Flags { 39 | unsafe { 40 | Flags::from_bits_truncate(match *self { 41 | Rect::None(ptr) => (*ptr).flags, 42 | Rect::Bitmap(ref b) => (*b.as_ptr()).flags, 43 | Rect::Text(ref t) => (*t.as_ptr()).flags, 44 | Rect::Ass(ref a) => (*a.as_ptr()).flags, 45 | }) 46 | } 47 | } 48 | } 49 | 50 | pub struct Bitmap<'a> { 51 | ptr: *const AVSubtitleRect, 52 | 53 | _marker: PhantomData<&'a ()>, 54 | } 55 | 56 | impl<'a> Bitmap<'a> { 57 | pub unsafe fn wrap(ptr: *const AVSubtitleRect) -> Self { 58 | Bitmap { 59 | ptr, 60 | _marker: PhantomData, 61 | } 62 | } 63 | 64 | pub unsafe fn as_ptr(&self) -> *const AVSubtitleRect { 65 | self.ptr 66 | } 67 | } 68 | 69 | impl<'a> Bitmap<'a> { 70 | pub fn x(&self) -> usize { 71 | unsafe { (*self.as_ptr()).x as usize } 72 | } 73 | 74 | pub fn y(&self) -> usize { 75 | unsafe { (*self.as_ptr()).y as usize } 76 | } 77 | 78 | pub fn width(&self) -> u32 { 79 | unsafe { (*self.as_ptr()).w as u32 } 80 | } 81 | 82 | pub fn height(&self) -> u32 { 83 | unsafe { (*self.as_ptr()).h as u32 } 84 | } 85 | 86 | pub fn colors(&self) -> usize { 87 | unsafe { (*self.as_ptr()).nb_colors as usize } 88 | } 89 | 90 | // XXX: must split Picture and PictureMut 91 | #[cfg(not(feature = "ffmpeg_5_0"))] 92 | pub fn picture(&self, format: format::Pixel) -> Picture<'a> { 93 | unsafe { 94 | Picture::wrap( 95 | &(*self.as_ptr()).pict as *const _ as *mut _, 96 | format, 97 | (*self.as_ptr()).w as u32, 98 | (*self.as_ptr()).h as u32, 99 | ) 100 | } 101 | } 102 | } 103 | 104 | pub struct Text<'a> { 105 | ptr: *const AVSubtitleRect, 106 | 107 | _marker: PhantomData<&'a ()>, 108 | } 109 | 110 | impl<'a> Text<'a> { 111 | pub unsafe fn wrap(ptr: *const AVSubtitleRect) -> Self { 112 | Text { 113 | ptr, 114 | _marker: PhantomData, 115 | } 116 | } 117 | 118 | pub unsafe fn as_ptr(&self) -> *const AVSubtitleRect { 119 | self.ptr 120 | } 121 | } 122 | 123 | impl<'a> Text<'a> { 124 | pub fn get(&self) -> &str { 125 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).text).to_bytes()) } 126 | } 127 | } 128 | 129 | pub struct Ass<'a> { 130 | ptr: *const AVSubtitleRect, 131 | 132 | _marker: PhantomData<&'a ()>, 133 | } 134 | 135 | impl<'a> Ass<'a> { 136 | pub unsafe fn wrap(ptr: *const AVSubtitleRect) -> Self { 137 | Ass { 138 | ptr, 139 | _marker: PhantomData, 140 | } 141 | } 142 | 143 | pub unsafe fn as_ptr(&self) -> *const AVSubtitleRect { 144 | self.ptr 145 | } 146 | } 147 | 148 | impl<'a> Ass<'a> { 149 | pub fn get(&self) -> &str { 150 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).ass).to_bytes()) } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/codec/decoder/decoder.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | use std::ptr; 3 | 4 | use super::{Audio, Check, Conceal, Opened, Subtitle, Video}; 5 | use codec::{traits, Context}; 6 | use ffi::*; 7 | use {Dictionary, Discard, Error, Rational}; 8 | 9 | pub struct Decoder(pub Context); 10 | 11 | impl Decoder { 12 | pub fn open(mut self) -> Result { 13 | unsafe { 14 | match avcodec_open2(self.as_mut_ptr(), ptr::null(), ptr::null_mut()) { 15 | 0 => Ok(Opened(self)), 16 | e => Err(Error::from(e)), 17 | } 18 | } 19 | } 20 | 21 | pub fn open_as(mut self, codec: D) -> Result { 22 | unsafe { 23 | if let Some(codec) = codec.decoder() { 24 | match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) { 25 | 0 => Ok(Opened(self)), 26 | e => Err(Error::from(e)), 27 | } 28 | } else { 29 | Err(Error::DecoderNotFound) 30 | } 31 | } 32 | } 33 | 34 | pub fn open_as_with( 35 | mut self, 36 | codec: D, 37 | options: Dictionary, 38 | ) -> Result { 39 | unsafe { 40 | if let Some(codec) = codec.decoder() { 41 | let mut opts = options.disown(); 42 | let res = avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), &mut opts); 43 | 44 | Dictionary::own(opts); 45 | 46 | match res { 47 | 0 => Ok(Opened(self)), 48 | e => Err(Error::from(e)), 49 | } 50 | } else { 51 | Err(Error::DecoderNotFound) 52 | } 53 | } 54 | } 55 | 56 | pub fn video(self) -> Result { 57 | if let Some(codec) = super::find(self.id()) { 58 | self.open_as(codec).and_then(|o| o.video()) 59 | } else { 60 | Err(Error::DecoderNotFound) 61 | } 62 | } 63 | 64 | pub fn audio(self) -> Result { 65 | if let Some(codec) = super::find(self.id()) { 66 | self.open_as(codec).and_then(|o| o.audio()) 67 | } else { 68 | Err(Error::DecoderNotFound) 69 | } 70 | } 71 | 72 | pub fn subtitle(self) -> Result { 73 | if let Some(codec) = super::find(self.id()) { 74 | self.open_as(codec).and_then(|o| o.subtitle()) 75 | } else { 76 | Err(Error::DecoderNotFound) 77 | } 78 | } 79 | 80 | pub fn conceal(&mut self, value: Conceal) { 81 | unsafe { 82 | (*self.as_mut_ptr()).error_concealment = value.bits(); 83 | } 84 | } 85 | 86 | pub fn check(&mut self, value: Check) { 87 | unsafe { 88 | (*self.as_mut_ptr()).err_recognition = value.bits(); 89 | } 90 | } 91 | 92 | pub fn skip_loop_filter(&mut self, value: Discard) { 93 | unsafe { 94 | (*self.as_mut_ptr()).skip_loop_filter = value.into(); 95 | } 96 | } 97 | 98 | pub fn skip_idct(&mut self, value: Discard) { 99 | unsafe { 100 | (*self.as_mut_ptr()).skip_idct = value.into(); 101 | } 102 | } 103 | 104 | pub fn skip_frame(&mut self, value: Discard) { 105 | unsafe { 106 | (*self.as_mut_ptr()).skip_frame = value.into(); 107 | } 108 | } 109 | 110 | pub fn time_base(&self) -> Rational { 111 | unsafe { Rational::from((*self.as_ptr()).time_base) } 112 | } 113 | } 114 | 115 | impl Deref for Decoder { 116 | type Target = Context; 117 | 118 | fn deref(&self) -> &::Target { 119 | &self.0 120 | } 121 | } 122 | 123 | impl DerefMut for Decoder { 124 | fn deref_mut(&mut self) -> &mut ::Target { 125 | &mut self.0 126 | } 127 | } 128 | 129 | impl AsRef for Decoder { 130 | fn as_ref(&self) -> &Context { 131 | self 132 | } 133 | } 134 | 135 | impl AsMut for Decoder { 136 | fn as_mut(&mut self) -> &mut Context { 137 | &mut self.0 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build-stable-x86_64 2 | on: 3 | push: 4 | pull_request: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | jobs: 8 | build-test-lint-linux: 9 | name: Linux - FFmpeg ${{ matrix.ffmpeg_version }} - build, test and lint 10 | runs-on: ubuntu-latest 11 | container: jrottenberg/ffmpeg:${{ matrix.ffmpeg_version }}-ubuntu 12 | strategy: 13 | matrix: 14 | ffmpeg_version: ["4.0", "4.1", "4.2", "4.3", "4.4", "5.0", "5.1"] 15 | fail-fast: false 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Install dependencies 19 | run: | 20 | apt update 21 | apt install -y --no-install-recommends clang curl pkg-config 22 | - name: Set up Rust 23 | uses: actions-rs/toolchain@v1 24 | with: 25 | toolchain: stable 26 | override: true 27 | components: rustfmt, clippy 28 | - name: Build 29 | run: | 30 | cargo build --examples 31 | - name: Test 32 | run: | 33 | cargo test --examples 34 | - name: Lint 35 | run: | 36 | cargo clippy --examples -- -D warnings 37 | - name: Check format 38 | run: | 39 | cargo fmt -- --check 40 | 41 | build-test-lint-macos: 42 | name: macOS - FFmpeg latest - build, test and lint 43 | runs-on: macos-latest 44 | steps: 45 | - uses: actions/checkout@v2 46 | - name: Install dependencies 47 | run: | 48 | brew install ffmpeg@5 pkg-config 49 | - name: Set up Rust 50 | uses: actions-rs/toolchain@v1 51 | with: 52 | toolchain: stable 53 | override: true 54 | components: rustfmt, clippy 55 | - name: Build 56 | env: 57 | PKG_CONFIG_PATH: /usr/local/opt/ffmpeg@5/lib/pkgconfig 58 | run: | 59 | cargo build --examples 60 | - name: Test 61 | env: 62 | PKG_CONFIG_PATH: /usr/local/opt/ffmpeg@5/lib/pkgconfig 63 | run: | 64 | cargo test --examples 65 | - name: Lint 66 | env: 67 | PKG_CONFIG_PATH: /usr/local/opt/ffmpeg@5/lib/pkgconfig 68 | run: | 69 | cargo clippy --examples -- -D warnings 70 | - name: Check format 71 | env: 72 | PKG_CONFIG_PATH: /usr/local/opt/ffmpeg@5/lib/pkgconfig 73 | run: | 74 | cargo fmt -- --check 75 | 76 | # Windows builds are disabled until they can get fixed 77 | # build-test-lint-windows: 78 | # name: Windows - FFmpeg ${{ matrix.ffmpeg_version }} - build, test and lint 79 | # runs-on: windows-latest 80 | # strategy: 81 | # matrix: 82 | # include: 83 | # - ffmpeg_version: latest 84 | # ffmpeg_download_url: https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full-shared.7z 85 | # fail-fast: false 86 | # env: 87 | # FFMPEG_DOWNLOAD_URL: ${{ matrix.ffmpeg_download_url }} 88 | # steps: 89 | # - uses: actions/checkout@v2 90 | # - name: Install dependencies 91 | # run: | 92 | # $VCINSTALLDIR = $(& "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath) 93 | # Add-Content $env:GITHUB_ENV "LIBCLANG_PATH=${VCINSTALLDIR}\VC\Tools\LLVM\x64\bin`n" 94 | # Invoke-WebRequest "${env:FFMPEG_DOWNLOAD_URL}" -OutFile ffmpeg-release-full-shared.7z 95 | # 7z x ffmpeg-release-full-shared.7z 96 | # mkdir ffmpeg 97 | # mv ffmpeg-*/* ffmpeg/ 98 | # Add-Content $env:GITHUB_ENV "FFMPEG_DIR=${pwd}\ffmpeg`n" 99 | # Add-Content $env:GITHUB_PATH "${pwd}\ffmpeg\bin`n" 100 | # - name: Set up Rust 101 | # uses: actions-rs/toolchain@v1 102 | # with: 103 | # toolchain: stable 104 | # override: true 105 | # components: rustfmt, clippy 106 | # - name: Build 107 | # run: | 108 | # cargo build --examples 109 | # - name: Test 110 | # run: | 111 | # cargo test --examples 112 | # - name: Lint 113 | # run: | 114 | # cargo clippy --examples -- -D warnings 115 | # - name: Check format 116 | # run: | 117 | # cargo fmt -- --check 118 | -------------------------------------------------------------------------------- /examples/codec-info.rs: -------------------------------------------------------------------------------- 1 | extern crate ffmpeg_rs; 2 | use std::env; 3 | 4 | fn main() { 5 | ffmpeg_rs::init().unwrap(); 6 | 7 | for arg in env::args().skip(1) { 8 | if let Some(codec) = ffmpeg_rs::decoder::find_by_name(&arg) { 9 | println!("type: decoder"); 10 | println!("\t id: {:?}", codec.id()); 11 | println!("\t name: {}", codec.name()); 12 | println!("\t description: {}", codec.description()); 13 | println!("\t medium: {:?}", codec.medium()); 14 | println!("\t capabilities: {:?}", codec.capabilities()); 15 | 16 | if let Some(profiles) = codec.profiles() { 17 | println!("\t profiles: {:?}", profiles.collect::>()); 18 | } else { 19 | println!("\t profiles: none"); 20 | } 21 | 22 | if let Ok(video) = codec.video() { 23 | if let Some(rates) = video.rates() { 24 | println!("\t rates: {:?}", rates.collect::>()); 25 | } else { 26 | println!("\t rates: any"); 27 | } 28 | 29 | if let Some(formats) = video.formats() { 30 | println!("\t formats: {:?}", formats.collect::>()); 31 | } else { 32 | println!("\t formats: any"); 33 | } 34 | } 35 | 36 | if let Ok(audio) = codec.audio() { 37 | if let Some(rates) = audio.rates() { 38 | println!("\t rates: {:?}", rates.collect::>()); 39 | } else { 40 | println!("\t rates: any"); 41 | } 42 | 43 | if let Some(formats) = audio.formats() { 44 | println!("\t formats: {:?}", formats.collect::>()); 45 | } else { 46 | println!("\t formats: any"); 47 | } 48 | 49 | if let Some(layouts) = audio.channel_layouts() { 50 | println!("\t channel_layouts: {:?}", layouts.collect::>()); 51 | } else { 52 | println!("\t channel_layouts: any"); 53 | } 54 | } 55 | 56 | println!("\t max_lowres: {:?}", codec.max_lowres()); 57 | } 58 | 59 | if let Some(codec) = ffmpeg_rs::encoder::find_by_name(&arg) { 60 | println!(); 61 | println!("type: encoder"); 62 | println!("\t id: {:?}", codec.id()); 63 | println!("\t name: {}", codec.name()); 64 | println!("\t description: {}", codec.description()); 65 | println!("\t medium: {:?}", codec.medium()); 66 | println!("\t capabilities: {:?}", codec.capabilities()); 67 | 68 | if let Some(profiles) = codec.profiles() { 69 | println!("\t profiles: {:?}", profiles.collect::>()); 70 | } 71 | 72 | if let Ok(video) = codec.video() { 73 | if let Some(rates) = video.rates() { 74 | println!("\t rates: {:?}", rates.collect::>()); 75 | } else { 76 | println!("\t rates: any"); 77 | } 78 | 79 | if let Some(formats) = video.formats() { 80 | println!("\t formats: {:?}", formats.collect::>()); 81 | } else { 82 | println!("\t formats: any"); 83 | } 84 | } 85 | 86 | if let Ok(audio) = codec.audio() { 87 | if let Some(rates) = audio.rates() { 88 | println!("\t rates: {:?}", rates.collect::>()); 89 | } else { 90 | println!("\t rates: any"); 91 | } 92 | 93 | if let Some(formats) = audio.formats() { 94 | println!("\t formats: {:?}", formats.collect::>()); 95 | } else { 96 | println!("\t formats: any"); 97 | } 98 | 99 | if let Some(layouts) = audio.channel_layouts() { 100 | println!("\t channel_layouts: {:?}", layouts.collect::>()); 101 | } else { 102 | println!("\t channel_layouts: any"); 103 | } 104 | } 105 | 106 | println!("\t max_lowres: {:?}", codec.max_lowres()); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/codec/decoder/video.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | #[cfg(not(feature = "ffmpeg_5_0"))] 4 | use ffi::*; 5 | use libc::c_int; 6 | 7 | use super::{slice, Opened}; 8 | use codec::Context; 9 | use color; 10 | #[cfg(not(feature = "ffmpeg_5_0"))] 11 | use frame; 12 | use util::chroma; 13 | use util::format; 14 | #[cfg(not(feature = "ffmpeg_5_0"))] 15 | use {packet, Error}; 16 | use {FieldOrder, Rational}; 17 | 18 | pub struct Video(pub Opened); 19 | 20 | impl Video { 21 | #[deprecated( 22 | since = "4.4.0", 23 | note = "Underlying API avcodec_decode_video2 has been deprecated since FFmpeg 3.1; \ 24 | consider switching to send_packet() and receive_frame()" 25 | )] 26 | #[cfg(not(feature = "ffmpeg_5_0"))] 27 | pub fn decode( 28 | &mut self, 29 | packet: &P, 30 | out: &mut frame::Video, 31 | ) -> Result { 32 | unsafe { 33 | let mut got: c_int = 0; 34 | 35 | match avcodec_decode_video2( 36 | self.as_mut_ptr(), 37 | out.as_mut_ptr(), 38 | &mut got, 39 | packet.as_ptr(), 40 | ) { 41 | e if e < 0 => Err(Error::from(e)), 42 | _ => Ok(got != 0), 43 | } 44 | } 45 | } 46 | 47 | pub fn width(&self) -> u32 { 48 | unsafe { (*self.as_ptr()).width as u32 } 49 | } 50 | 51 | pub fn height(&self) -> u32 { 52 | unsafe { (*self.as_ptr()).height as u32 } 53 | } 54 | 55 | pub fn format(&self) -> format::Pixel { 56 | unsafe { format::Pixel::from((*self.as_ptr()).pix_fmt) } 57 | } 58 | 59 | pub fn has_b_frames(&self) -> bool { 60 | unsafe { (*self.as_ptr()).has_b_frames != 0 } 61 | } 62 | 63 | pub fn aspect_ratio(&self) -> Rational { 64 | unsafe { Rational::from((*self.as_ptr()).sample_aspect_ratio) } 65 | } 66 | 67 | pub fn color_space(&self) -> color::Space { 68 | unsafe { color::Space::from((*self.as_ptr()).colorspace) } 69 | } 70 | 71 | pub fn color_range(&self) -> color::Range { 72 | unsafe { color::Range::from((*self.as_ptr()).color_range) } 73 | } 74 | 75 | pub fn color_primaries(&self) -> color::Primaries { 76 | unsafe { color::Primaries::from((*self.as_ptr()).color_primaries) } 77 | } 78 | 79 | pub fn color_transfer_characteristic(&self) -> color::TransferCharacteristic { 80 | unsafe { color::TransferCharacteristic::from((*self.as_ptr()).color_trc) } 81 | } 82 | 83 | pub fn chroma_location(&self) -> chroma::Location { 84 | unsafe { chroma::Location::from((*self.as_ptr()).chroma_sample_location) } 85 | } 86 | 87 | pub fn set_slice_count(&mut self, value: usize) { 88 | unsafe { 89 | (*self.as_mut_ptr()).slice_count = value as c_int; 90 | } 91 | } 92 | 93 | pub fn set_slice_flags(&mut self, value: slice::Flags) { 94 | unsafe { 95 | (*self.as_mut_ptr()).slice_flags = value.bits(); 96 | } 97 | } 98 | 99 | pub fn skip_top(&mut self, value: usize) { 100 | unsafe { 101 | (*self.as_mut_ptr()).skip_top = value as c_int; 102 | } 103 | } 104 | 105 | pub fn skip_bottom(&mut self, value: usize) { 106 | unsafe { 107 | (*self.as_mut_ptr()).skip_bottom = value as c_int; 108 | } 109 | } 110 | 111 | pub fn references(&self) -> usize { 112 | unsafe { (*self.as_ptr()).refs as usize } 113 | } 114 | 115 | pub fn set_field_order(&mut self, value: FieldOrder) { 116 | unsafe { 117 | (*self.as_mut_ptr()).field_order = value.into(); 118 | } 119 | } 120 | 121 | // intra_matrix 122 | // inter_matrix 123 | 124 | pub fn intra_dc_precision(&self) -> u8 { 125 | unsafe { (*self.as_ptr()).intra_dc_precision as u8 } 126 | } 127 | 128 | pub fn max_bit_rate(&self) -> usize { 129 | unsafe { (*self.as_ptr()).rc_max_rate as usize } 130 | } 131 | } 132 | 133 | impl Deref for Video { 134 | type Target = Opened; 135 | 136 | fn deref(&self) -> &::Target { 137 | &self.0 138 | } 139 | } 140 | 141 | impl DerefMut for Video { 142 | fn deref_mut(&mut self) -> &mut ::Target { 143 | &mut self.0 144 | } 145 | } 146 | 147 | impl AsRef for Video { 148 | fn as_ref(&self) -> &Context { 149 | self 150 | } 151 | } 152 | 153 | impl AsMut for Video { 154 | fn as_mut(&mut self) -> &mut Context { 155 | &mut self.0 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/util/option/traits.rs: -------------------------------------------------------------------------------- 1 | //! NOTE: this will be much better once specialization comes 2 | 3 | use std::ffi::CString; 4 | use std::mem; 5 | 6 | use ffi::*; 7 | use libc::{c_int, c_void}; 8 | use util::format; 9 | use {ChannelLayout, Error, Rational}; 10 | 11 | macro_rules! check { 12 | ($expr:expr) => { 13 | match $expr { 14 | 0 => Ok(()), 15 | e => Err(Error::from(e)), 16 | } 17 | }; 18 | } 19 | 20 | pub unsafe trait Target { 21 | fn as_ptr(&self) -> *const c_void; 22 | fn as_mut_ptr(&mut self) -> *mut c_void; 23 | } 24 | 25 | pub trait Settable: Target { 26 | fn set(&mut self, name: &str, value: &T) -> Result<(), Error> { 27 | unsafe { 28 | let name = CString::new(name).unwrap(); 29 | 30 | check!(av_opt_set_bin( 31 | self.as_mut_ptr(), 32 | name.as_ptr(), 33 | value as *const _ as *const _, 34 | mem::size_of::() as c_int, 35 | AV_OPT_SEARCH_CHILDREN 36 | )) 37 | } 38 | } 39 | 40 | fn set_str(&mut self, name: &str, value: &str) -> Result<(), Error> { 41 | unsafe { 42 | let name = CString::new(name).unwrap(); 43 | let value = CString::new(value).unwrap(); 44 | 45 | check!(av_opt_set( 46 | self.as_mut_ptr(), 47 | name.as_ptr(), 48 | value.as_ptr(), 49 | AV_OPT_SEARCH_CHILDREN 50 | )) 51 | } 52 | } 53 | 54 | fn set_int(&mut self, name: &str, value: i64) -> Result<(), Error> { 55 | unsafe { 56 | let name = CString::new(name).unwrap(); 57 | 58 | check!(av_opt_set_int( 59 | self.as_mut_ptr(), 60 | name.as_ptr(), 61 | value, 62 | AV_OPT_SEARCH_CHILDREN 63 | )) 64 | } 65 | } 66 | 67 | fn set_double(&mut self, name: &str, value: f64) -> Result<(), Error> { 68 | unsafe { 69 | let name = CString::new(name).unwrap(); 70 | 71 | check!(av_opt_set_double( 72 | self.as_mut_ptr(), 73 | name.as_ptr(), 74 | value, 75 | AV_OPT_SEARCH_CHILDREN 76 | )) 77 | } 78 | } 79 | 80 | fn set_rational>(&mut self, name: &str, value: T) -> Result<(), Error> { 81 | unsafe { 82 | let name = CString::new(name).unwrap(); 83 | 84 | check!(av_opt_set_q( 85 | self.as_mut_ptr(), 86 | name.as_ptr(), 87 | value.into().into(), 88 | AV_OPT_SEARCH_CHILDREN 89 | )) 90 | } 91 | } 92 | 93 | fn set_image_size(&mut self, name: &str, w: u32, h: u32) -> Result<(), Error> { 94 | unsafe { 95 | let name = CString::new(name).unwrap(); 96 | 97 | check!(av_opt_set_image_size( 98 | self.as_mut_ptr(), 99 | name.as_ptr(), 100 | w as c_int, 101 | h as c_int, 102 | AV_OPT_SEARCH_CHILDREN 103 | )) 104 | } 105 | } 106 | 107 | fn set_pixel_format(&mut self, name: &str, format: format::Pixel) -> Result<(), Error> { 108 | unsafe { 109 | let name = CString::new(name).unwrap(); 110 | 111 | check!(av_opt_set_pixel_fmt( 112 | self.as_mut_ptr(), 113 | name.as_ptr(), 114 | format.into(), 115 | AV_OPT_SEARCH_CHILDREN 116 | )) 117 | } 118 | } 119 | 120 | fn set_sample_format(&mut self, name: &str, format: format::Sample) -> Result<(), Error> { 121 | unsafe { 122 | let name = CString::new(name).unwrap(); 123 | 124 | check!(av_opt_set_sample_fmt( 125 | self.as_mut_ptr(), 126 | name.as_ptr(), 127 | format.into(), 128 | AV_OPT_SEARCH_CHILDREN 129 | )) 130 | } 131 | } 132 | 133 | fn set_channel_layout(&mut self, name: &str, layout: ChannelLayout) -> Result<(), Error> { 134 | unsafe { 135 | let name = CString::new(name).unwrap(); 136 | 137 | check!(av_opt_set_channel_layout( 138 | self.as_mut_ptr(), 139 | name.as_ptr(), 140 | layout.bits() as i64, 141 | AV_OPT_SEARCH_CHILDREN 142 | )) 143 | } 144 | } 145 | } 146 | 147 | pub trait Gettable: Target {} 148 | 149 | pub trait Iterable: Target {} 150 | -------------------------------------------------------------------------------- /examples/metadata.rs: -------------------------------------------------------------------------------- 1 | extern crate ffmpeg_rs; 2 | use std::env; 3 | 4 | fn main() -> Result<(), ffmpeg_rs::Error> { 5 | ffmpeg_rs::init().unwrap(); 6 | 7 | match ffmpeg_rs::format::input(&env::args().nth(1).expect("missing file")) { 8 | Ok(context) => { 9 | for (k, v) in context.metadata().iter() { 10 | println!("{k}: {v}"); 11 | } 12 | 13 | if let Some(stream) = context.streams().best(ffmpeg_rs::media::Type::Video) { 14 | println!("Best video stream index: {}", stream.index()); 15 | } 16 | 17 | if let Some(stream) = context.streams().best(ffmpeg_rs::media::Type::Audio) { 18 | println!("Best audio stream index: {}", stream.index()); 19 | } 20 | 21 | if let Some(stream) = context.streams().best(ffmpeg_rs::media::Type::Subtitle) { 22 | println!("Best subtitle stream index: {}", stream.index()); 23 | } 24 | 25 | println!( 26 | "duration (seconds): {:.2}", 27 | context.duration() as f64 / f64::from(ffmpeg_rs::ffi::AV_TIME_BASE) 28 | ); 29 | 30 | for stream in context.streams() { 31 | println!("stream index {}:", stream.index()); 32 | println!("\ttime_base: {}", stream.time_base()); 33 | println!("\tstart_time: {}", stream.start_time()); 34 | println!("\tduration (stream timebase): {}", stream.duration()); 35 | println!( 36 | "\tduration (seconds): {:.2}", 37 | stream.duration() as f64 * f64::from(stream.time_base()) 38 | ); 39 | println!("\tframes: {}", stream.frames()); 40 | println!("\tdisposition: {:?}", stream.disposition()); 41 | println!("\tdiscard: {:?}", stream.discard()); 42 | println!("\trate: {}", stream.rate()); 43 | 44 | let codec = 45 | ffmpeg_rs::codec::context::Context::from_parameters(stream.parameters())?; 46 | println!("\tmedium: {:?}", codec.medium()); 47 | println!("\tid: {:?}", codec.id()); 48 | 49 | if codec.medium() == ffmpeg_rs::media::Type::Video { 50 | if let Ok(video) = codec.decoder().video() { 51 | println!("\tbit_rate: {}", video.bit_rate()); 52 | println!("\tmax_rate: {}", video.max_bit_rate()); 53 | println!("\tdelay: {}", video.delay()); 54 | println!("\tvideo.width: {}", video.width()); 55 | println!("\tvideo.height: {}", video.height()); 56 | println!("\tvideo.format: {:?}", video.format()); 57 | println!("\tvideo.has_b_frames: {}", video.has_b_frames()); 58 | println!("\tvideo.aspect_ratio: {}", video.aspect_ratio()); 59 | println!("\tvideo.color_space: {:?}", video.color_space()); 60 | println!("\tvideo.color_range: {:?}", video.color_range()); 61 | println!("\tvideo.color_primaries: {:?}", video.color_primaries()); 62 | println!( 63 | "\tvideo.color_transfer_characteristic: {:?}", 64 | video.color_transfer_characteristic() 65 | ); 66 | println!("\tvideo.chroma_location: {:?}", video.chroma_location()); 67 | println!("\tvideo.references: {}", video.references()); 68 | println!("\tvideo.intra_dc_precision: {}", video.intra_dc_precision()); 69 | } 70 | } else if codec.medium() == ffmpeg_rs::media::Type::Audio { 71 | if let Ok(audio) = codec.decoder().audio() { 72 | println!("\tbit_rate: {}", audio.bit_rate()); 73 | println!("\tmax_rate: {}", audio.max_bit_rate()); 74 | println!("\tdelay: {}", audio.delay()); 75 | println!("\taudio.rate: {}", audio.rate()); 76 | println!("\taudio.channels: {}", audio.channels()); 77 | println!("\taudio.format: {:?}", audio.format()); 78 | println!("\taudio.frames: {}", audio.frames()); 79 | println!("\taudio.align: {}", audio.align()); 80 | println!("\taudio.channel_layout: {:?}", audio.channel_layout()); 81 | } 82 | } 83 | } 84 | } 85 | 86 | Err(error) => println!("error: {error}"), 87 | } 88 | Ok(()) 89 | } 90 | -------------------------------------------------------------------------------- /src/util/frame/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod side_data; 2 | pub use self::side_data::SideData; 3 | 4 | pub mod video; 5 | pub use self::video::Video; 6 | 7 | pub mod audio; 8 | pub use self::audio::Audio; 9 | 10 | pub mod flag; 11 | pub use self::flag::Flags; 12 | 13 | use ffi::*; 14 | use {Dictionary, DictionaryRef}; 15 | 16 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] 17 | pub struct Packet { 18 | pub duration: i64, 19 | pub position: i64, 20 | pub size: usize, 21 | 22 | #[cfg(not(feature = "ffmpeg_5_0"))] 23 | pub pts: i64, 24 | pub dts: i64, 25 | } 26 | 27 | #[derive(PartialEq, Eq)] 28 | pub struct Frame { 29 | ptr: *mut AVFrame, 30 | 31 | _own: bool, 32 | } 33 | 34 | unsafe impl Send for Frame {} 35 | unsafe impl Sync for Frame {} 36 | 37 | impl Frame { 38 | #[inline(always)] 39 | pub unsafe fn wrap(ptr: *mut AVFrame) -> Self { 40 | Frame { ptr, _own: false } 41 | } 42 | 43 | #[inline(always)] 44 | pub unsafe fn empty() -> Self { 45 | Frame { 46 | ptr: av_frame_alloc(), 47 | _own: true, 48 | } 49 | } 50 | 51 | #[inline(always)] 52 | pub unsafe fn as_ptr(&self) -> *const AVFrame { 53 | self.ptr as *const _ 54 | } 55 | 56 | #[inline(always)] 57 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFrame { 58 | self.ptr 59 | } 60 | 61 | #[inline(always)] 62 | pub unsafe fn is_empty(&self) -> bool { 63 | (*self.as_ptr()).data[0].is_null() 64 | } 65 | } 66 | 67 | impl Frame { 68 | #[inline] 69 | pub fn is_key(&self) -> bool { 70 | unsafe { (*self.as_ptr()).key_frame == 1 } 71 | } 72 | 73 | #[inline] 74 | pub fn is_corrupt(&self) -> bool { 75 | self.flags().contains(Flags::CORRUPT) 76 | } 77 | 78 | #[inline] 79 | pub fn packet(&self) -> Packet { 80 | unsafe { 81 | Packet { 82 | duration: (*self.as_ptr()).pkt_duration, 83 | position: (*self.as_ptr()).pkt_pos, 84 | size: (*self.as_ptr()).pkt_size as usize, 85 | 86 | #[cfg(not(feature = "ffmpeg_5_0"))] 87 | pts: (*self.as_ptr()).pkt_pts, 88 | dts: (*self.as_ptr()).pkt_dts, 89 | } 90 | } 91 | } 92 | 93 | #[inline] 94 | pub fn pts(&self) -> Option { 95 | unsafe { 96 | match (*self.as_ptr()).pts { 97 | AV_NOPTS_VALUE => None, 98 | pts => Some(pts), 99 | } 100 | } 101 | } 102 | 103 | #[inline] 104 | pub fn set_pts(&mut self, value: Option) { 105 | unsafe { 106 | (*self.as_mut_ptr()).pts = value.unwrap_or(AV_NOPTS_VALUE); 107 | } 108 | } 109 | 110 | #[inline] 111 | pub fn timestamp(&self) -> Option { 112 | unsafe { 113 | match (*self.as_ptr()).best_effort_timestamp { 114 | AV_NOPTS_VALUE => None, 115 | t => Some(t), 116 | } 117 | } 118 | } 119 | 120 | #[inline] 121 | pub fn quality(&self) -> usize { 122 | unsafe { (*self.as_ptr()).quality as usize } 123 | } 124 | 125 | #[inline] 126 | pub fn flags(&self) -> Flags { 127 | unsafe { Flags::from_bits_truncate((*self.as_ptr()).flags) } 128 | } 129 | 130 | #[inline] 131 | pub fn metadata(&self) -> DictionaryRef { 132 | unsafe { DictionaryRef::wrap((*self.as_ptr()).metadata) } 133 | } 134 | 135 | #[inline] 136 | pub fn set_metadata(&mut self, value: Dictionary) { 137 | unsafe { (*self.as_mut_ptr()).metadata = value.disown() } 138 | } 139 | 140 | #[inline] 141 | pub fn side_data(&self, kind: side_data::Type) -> Option { 142 | unsafe { 143 | let ptr = av_frame_get_side_data(self.as_ptr(), kind.into()); 144 | 145 | if ptr.is_null() { 146 | None 147 | } else { 148 | Some(SideData::wrap(ptr)) 149 | } 150 | } 151 | } 152 | 153 | #[inline] 154 | pub fn new_side_data(&mut self, kind: side_data::Type, size: usize) -> Option { 155 | unsafe { 156 | let ptr = av_frame_new_side_data(self.as_mut_ptr(), kind.into(), size as _); 157 | 158 | if ptr.is_null() { 159 | None 160 | } else { 161 | Some(SideData::wrap(ptr)) 162 | } 163 | } 164 | } 165 | 166 | #[inline] 167 | pub fn remove_side_data(&mut self, kind: side_data::Type) { 168 | unsafe { 169 | av_frame_remove_side_data(self.as_mut_ptr(), kind.into()); 170 | } 171 | } 172 | } 173 | 174 | impl Drop for Frame { 175 | #[inline] 176 | fn drop(&mut self) { 177 | unsafe { 178 | av_frame_free(&mut self.as_mut_ptr()); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/codec/context.rs: -------------------------------------------------------------------------------- 1 | use std::any::Any; 2 | use std::ptr; 3 | use std::rc::Rc; 4 | 5 | use super::decoder::Decoder; 6 | use super::encoder::Encoder; 7 | use super::{threading, Compliance, Debug, Flags, Id, Parameters}; 8 | use ffi::*; 9 | use libc::c_int; 10 | use media; 11 | use {Codec, Error}; 12 | 13 | pub struct Context { 14 | ptr: *mut AVCodecContext, 15 | owner: Option>, 16 | } 17 | 18 | unsafe impl Send for Context {} 19 | 20 | impl Context { 21 | pub unsafe fn wrap(ptr: *mut AVCodecContext, owner: Option>) -> Self { 22 | Context { ptr, owner } 23 | } 24 | 25 | pub unsafe fn as_ptr(&self) -> *const AVCodecContext { 26 | self.ptr as *const _ 27 | } 28 | 29 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVCodecContext { 30 | self.ptr 31 | } 32 | } 33 | 34 | impl Context { 35 | pub fn new() -> Self { 36 | unsafe { 37 | Context { 38 | ptr: avcodec_alloc_context3(ptr::null()), 39 | owner: None, 40 | } 41 | } 42 | } 43 | 44 | pub fn from_parameters>(parameters: P) -> Result { 45 | let parameters = parameters.into(); 46 | let mut context = Self::new(); 47 | 48 | unsafe { 49 | match avcodec_parameters_to_context(context.as_mut_ptr(), parameters.as_ptr()) { 50 | e if e < 0 => Err(Error::from(e)), 51 | _ => Ok(context), 52 | } 53 | } 54 | } 55 | 56 | pub fn new_with_codec(codec: &Codec) -> Self { 57 | unsafe { 58 | Context { 59 | ptr: avcodec_alloc_context3(codec.as_ptr()), 60 | owner: None, 61 | } 62 | } 63 | } 64 | 65 | pub fn decoder(self) -> Decoder { 66 | Decoder(self) 67 | } 68 | 69 | pub fn encoder(self) -> Encoder { 70 | Encoder(self) 71 | } 72 | 73 | pub fn codec(&self) -> Option { 74 | unsafe { 75 | if (*self.as_ptr()).codec.is_null() { 76 | None 77 | } else { 78 | Some(Codec::wrap((*self.as_ptr()).codec as *mut _)) 79 | } 80 | } 81 | } 82 | 83 | pub fn medium(&self) -> media::Type { 84 | unsafe { media::Type::from((*self.as_ptr()).codec_type) } 85 | } 86 | 87 | pub fn set_flags(&mut self, value: Flags) { 88 | unsafe { 89 | (*self.as_mut_ptr()).flags = value.bits() as c_int; 90 | } 91 | } 92 | 93 | pub fn id(&self) -> Id { 94 | unsafe { Id::from((*self.as_ptr()).codec_id) } 95 | } 96 | 97 | pub fn compliance(&mut self, value: Compliance) { 98 | unsafe { 99 | (*self.as_mut_ptr()).strict_std_compliance = value.into(); 100 | } 101 | } 102 | 103 | pub fn debug(&mut self, value: Debug) { 104 | unsafe { 105 | (*self.as_mut_ptr()).debug = value.bits(); 106 | } 107 | } 108 | 109 | pub fn set_threading(&mut self, config: threading::Config) { 110 | unsafe { 111 | (*self.as_mut_ptr()).thread_type = config.kind.into(); 112 | (*self.as_mut_ptr()).thread_count = config.count as c_int; 113 | (*self.as_mut_ptr()).thread_safe_callbacks = i32::from(config.safe); 114 | } 115 | } 116 | 117 | pub fn threading(&self) -> threading::Config { 118 | unsafe { 119 | threading::Config { 120 | kind: threading::Type::from((*self.as_ptr()).active_thread_type), 121 | count: (*self.as_ptr()).thread_count as usize, 122 | safe: (*self.as_ptr()).thread_safe_callbacks != 0, 123 | } 124 | } 125 | } 126 | 127 | pub fn set_parameters>(&mut self, parameters: P) -> Result<(), Error> { 128 | let parameters = parameters.into(); 129 | 130 | unsafe { 131 | match avcodec_parameters_to_context(self.as_mut_ptr(), parameters.as_ptr()) { 132 | e if e < 0 => Err(Error::from(e)), 133 | _ => Ok(()), 134 | } 135 | } 136 | } 137 | } 138 | 139 | impl Default for Context { 140 | fn default() -> Self { 141 | Self::new() 142 | } 143 | } 144 | 145 | impl Drop for Context { 146 | fn drop(&mut self) { 147 | unsafe { 148 | if self.owner.is_none() { 149 | avcodec_free_context(&mut self.as_mut_ptr()); 150 | } 151 | } 152 | } 153 | } 154 | 155 | #[cfg(not(feature = "ffmpeg_5_0"))] 156 | impl Clone for Context { 157 | fn clone(&self) -> Self { 158 | let mut ctx = Context::new(); 159 | ctx.clone_from(self); 160 | 161 | ctx 162 | } 163 | 164 | fn clone_from(&mut self, source: &Self) { 165 | unsafe { 166 | // Removed in ffmpeg >= 5.0. 167 | avcodec_copy_context(self.as_mut_ptr(), source.as_ptr()); 168 | } 169 | } 170 | } 171 | --------------------------------------------------------------------------------