├── .github ├── ISSUE_TEMPLATE │ ├── bug.md │ ├── config.yml │ └── feature.md └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock.MSRV ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── examples ├── chapters.rs ├── codec-info.rs ├── dump-frames.rs ├── metadata.rs ├── remux.rs ├── transcode-audio.rs └── transcode-x264.rs ├── ffmpeg-sys-the-third ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs └── src │ ├── avutil │ ├── channel_layout.rs │ ├── error.rs │ ├── mod.rs │ ├── pixfmt.rs │ ├── rational.rs │ └── util.rs │ └── lib.rs └── src ├── as_ptr.rs ├── codec ├── audio_service.rs ├── capabilities.rs ├── codec.rs ├── compliance.rs ├── config.rs ├── context.rs ├── debug.rs ├── decoder │ ├── audio.rs │ ├── check.rs │ ├── conceal.rs │ ├── decoder.rs │ ├── mod.rs │ ├── opened.rs │ ├── slice.rs │ ├── subtitle.rs │ └── video.rs ├── descriptor.rs ├── discard.rs ├── encoder │ ├── audio.rs │ ├── comparison.rs │ ├── decision.rs │ ├── encoder.rs │ ├── mod.rs │ ├── motion_estimation.rs │ ├── prediction.rs │ ├── subtitle.rs │ └── video.rs ├── field_order.rs ├── flag.rs ├── id.rs ├── mod.rs ├── packet │ ├── borrow.rs │ ├── flag.rs │ ├── mod.rs │ ├── packet.rs │ ├── side_data.rs │ └── traits.rs ├── parameters │ ├── borrowed.rs │ ├── borrowed_mut.rs │ ├── common.rs │ ├── mod.rs │ └── owned.rs ├── picture.rs ├── profile.rs ├── props.rs ├── subtitle │ ├── flag.rs │ ├── mod.rs │ ├── rect.rs │ └── rect_mut.rs ├── threading.rs └── traits.rs ├── device ├── extensions.rs ├── input.rs ├── mod.rs └── output.rs ├── filter ├── context │ ├── context.rs │ ├── mod.rs │ ├── sink.rs │ └── source.rs ├── filter.rs ├── flag.rs ├── graph.rs ├── mod.rs └── pad.rs ├── format ├── chapter │ ├── chapter.rs │ ├── chapter_mut.rs │ └── mod.rs ├── context │ ├── common.rs │ ├── destructor.rs │ ├── input.rs │ ├── mod.rs │ └── output.rs ├── format │ ├── flag.rs │ ├── input.rs │ ├── iter.rs │ ├── mod.rs │ └── output.rs ├── mod.rs ├── network.rs └── stream │ ├── disposition.rs │ ├── mod.rs │ ├── stream.rs │ └── stream_mut.rs ├── lib.rs ├── macros.rs ├── software ├── mod.rs ├── resampling │ ├── context.rs │ ├── delay.rs │ ├── dither.rs │ ├── engine.rs │ ├── extensions.rs │ ├── filter.rs │ ├── flag.rs │ └── mod.rs └── scaling │ ├── color_space.rs │ ├── context.rs │ ├── extensions.rs │ ├── filter.rs │ ├── flag.rs │ ├── mod.rs │ ├── support.rs │ └── vector.rs ├── util ├── channel_layout │ ├── channel.rs │ ├── channel_custom.rs │ ├── iter.rs │ ├── layout.rs │ ├── mask.rs │ ├── mod.rs │ └── order.rs ├── chroma │ ├── location.rs │ └── mod.rs ├── color │ ├── mod.rs │ ├── primaries.rs │ ├── range.rs │ ├── space.rs │ └── transfer_characteristic.rs ├── dictionary │ ├── immutable.rs │ ├── iter.rs │ ├── mod.rs │ ├── mutable.rs │ └── owned.rs ├── error.rs ├── format │ ├── mod.rs │ ├── pixel.rs │ └── sample.rs ├── frame │ ├── audio.rs │ ├── flag.rs │ ├── mod.rs │ ├── side_data.rs │ └── video.rs ├── interrupt.rs ├── log │ ├── flag.rs │ ├── level.rs │ └── mod.rs ├── mathematics │ ├── mod.rs │ ├── rescale.rs │ └── rounding.rs ├── media.rs ├── mod.rs ├── option │ ├── mod.rs │ └── traits.rs ├── picture.rs ├── rational.rs └── time.rs └── utils.rs /.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 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | 3 | contact_links: 4 | - name: Questions about usage 5 | url: https://github.com/shssoichiro/ffmpeg-the-third/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 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: create release 2 | on: 3 | push: 4 | tags: 5 | - "v*" 6 | jobs: 7 | release: 8 | name: Create release 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Generate release notes 13 | run: | 14 | cat >release_notes.md <` params by reference 20 | - Refactor crate::codec 21 | - Replace util::Range with std::ops::RangeBounds 22 | - Refactor format::{Input, Output} 23 | - Add utils for ptr->&str conversion 24 | - Refactor crate::format 25 | - Add `rustc-check-cfg` for `ff*api*{...}` features 26 | - Add `rustc-check-cfg` for `ffmpeg_x_y` features 27 | - print clang version in build 28 | - Use libc::c_char over i8 29 | - Use clang for inspecting macros instead of compiling the binary. 30 | 31 | ## Version 2.0.1 32 | 33 | - Add Copy, Clone, Eq, Debug to some bitflags types that had lost them 34 | 35 | ## Version 2.0.0 36 | 37 | - [Breaking] Replace avutil macros with const fns 38 | - [Feature] Support ffmpeg 7 39 | - [Feature] Add ChannelLayout API 40 | - Many changes to the build process 41 | - Fix avfiltergraph input/output 42 | - Migrate to Rust edition 2021 (only 3 years late) 43 | - Fix PadIter in FFmpeg pre-5.0 44 | 45 | ## Version 1.2.2 46 | 47 | - Do a better job of fixing ffmpeg 6.0 support :) 48 | 49 | ## Version 1.2.1 50 | 51 | - Fix ffmpeg 4.x support that was broken in 1.2.0 52 | 53 | ## Version 1.2.0 54 | 55 | - Add ffmpeg 6.0 support 56 | 57 | ## _sys_ Version 1.1.0 58 | 59 | - Add ffmpeg 6.0 support 60 | 61 | ## _sys_ Version 1.0.2 62 | 63 | - Fix building against clang 16 by using latest bindgen crate 64 | 65 | ## _sys_ Version 1.0.1 66 | 67 | - Fix static building after a change in ffmpeg's branch naming structure broke it 68 | 69 | ## Version 1.1.2 70 | 71 | No changes from 1.1.1. 72 | 73 | ## Version 1.1.1 74 | 75 | - Fix compilation on some non-x64 platforms 76 | 77 | ## Version 1.1.0 78 | 79 | - Add `serialize` feature, off by default, which derives `serde::{Serialize, Deserialize}` for as many types as possible 80 | 81 | ## Version 1.0.1 82 | 83 | - Remove the "ffmpeg4.x" features that were supposed to have been removed when ffmpeg5 was released. 84 | This is _technically_ a breaking change, but I'm publishing it so quickly after the initial release of this fork that hopefully nobody is depending on those old, deprecated features that you should have removed ages ago anyway. 85 | 86 | ## Version 1.0.0 87 | 88 | - Fork from https://github.com/zmwangx/rust-ffmpeg 89 | - Fix building against git ffmpeg by making enums non-exhaustive 90 | - Reset versioning. The new versioning scheme makes the crate version independent of the ffmpeg version, 91 | but tags the ffmpeg version as semver metadata. When including this crate as a dependency, you only need 92 | to specify the crate version itself e.g. "1.0.0". The tagged ffmpeg version is merely informative and indicates 93 | the latest stable ffmpeg at the time the version of the crate was released, which is also the version that 94 | this crate is tested against. 95 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/v/ffmpeg-the-third.svg)](https://crates.io/crates/ffmpeg-the-third) 2 | [![docs.rs](https://docs.rs/ffmpeg-the-third/badge.svg)](https://docs.rs/ffmpeg-the-third/) 3 | [![build](https://github.com/shssoichiro/ffmpeg-the-third/workflows/build/badge.svg)](https://github.com/shssoichiro/ffmpeg-the-third/actions) 4 | 5 | This is a fork of the abandoned [ffmpeg-next](https://crates.io/crates/ffmpeg-next) crate which is a fork of the abandoned [ffmpeg](https://crates.io/crates/ffmpeg) crate. 6 | 7 | Currently supported FFmpeg versions: 4.2 - 7.1. 8 | 9 | Versions that are considered [old and unmaintained](https://ffmpeg.org/olddownload.html) by FFmpeg like 5.0 or 6.0 usually work, but are not actively tested during development. 10 | 11 | ## Usage 12 | 13 | Build instructions can be found on the [wiki](https://github.com/zmwangx/rust-ffmpeg/wiki/Notes-on-building). API documentation for this crate can be found on [docs.rs](https://docs.rs/ffmpeg-the-third/). 14 | 15 | _See [CHANGELOG.md](CHANGELOG.md) for information on version upgrades._ 16 | 17 | ### FFmpeg documentation 18 | 19 | - [FFmpeg user manual](https://ffmpeg.org/ffmpeg-all.html) 20 | - [FFmpeg Doxygen](https://ffmpeg.org/doxygen/trunk/) 21 | 22 | ## Contributing 23 | 24 | Issues and PRs are welcome. 25 | 26 | If you have significant, demonstrable experience in Rust and multimedia-related programming, please let me know, I'll be more than happy to invite you as a collaborator. 27 | 28 | ## Minimum supported Rust version (MSRV) 29 | 30 | Both `ffmpeg-the-third` and `ffmpeg-sys-the-third` currently require a Rust version of 1.65.0 or higher. Increases in MSRV will result in a semver MINOR version increase. 31 | 32 | If you are having issues compiling this crate on your version of Rust, there are two tools you can use to help find MSRV-compatible dependency versions: 33 | 34 | - Install a nightly Rust toolchain and run `cargo +nightly update -Zmsrv-policy`. This will automatically resolve dependencies to versions that are compatible with the `rust-version` in `Cargo.toml`. 35 | - Check the `Cargo.lock.MSRV` in this repository. It contains dependency versions that are known to compile on the MSRV. In the simplest case, you can just `cp Cargo.lock.MSRV Cargo.lock`. For more complex dependency graphs, you might need to manually select compatible versions from the `Cargo.lock.MSRV`. 36 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn main() { 4 | for (name, _value) in env::vars() { 5 | println!("{name}"); 6 | 7 | if name.starts_with("DEP_FFMPEG_CHECK_") { 8 | println!( 9 | r#"cargo:rustc-check-cfg=cfg(feature, values("{}"))"#, 10 | name["DEP_FFMPEG_CHECK_".len()..name.len()].to_lowercase() 11 | ); 12 | } else if name.starts_with("DEP_FFMPEG_") { 13 | println!( 14 | r#"cargo:rustc-cfg=feature="{}""#, 15 | name["DEP_FFMPEG_".len()..name.len()].to_lowercase() 16 | ); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/chapters.rs: -------------------------------------------------------------------------------- 1 | use ffmpeg_the_third as ffmpeg; 2 | 3 | use std::env; 4 | 5 | fn main() { 6 | ffmpeg::init().unwrap(); 7 | 8 | match ffmpeg::format::input(env::args().nth(1).expect("missing input file name")) { 9 | Ok(ictx) => { 10 | println!("Nb chapters: {}", ictx.nb_chapters()); 11 | 12 | for chapter in ictx.chapters() { 13 | println!("chapter id {}:", chapter.id()); 14 | println!("\ttime_base: {}", chapter.time_base()); 15 | println!("\tstart: {}", chapter.start()); 16 | println!("\tend: {}", chapter.end()); 17 | 18 | for (k, v) in chapter.metadata().iter() { 19 | println!("\t{k}: {v}"); 20 | } 21 | } 22 | 23 | let mut octx = ffmpeg::format::output("test.mkv").expect("Couldn't open test file"); 24 | 25 | for chapter in ictx.chapters() { 26 | let title = match chapter.metadata().get("title") { 27 | Some(title) => String::from(title), 28 | None => String::new(), 29 | }; 30 | 31 | match octx.add_chapter( 32 | chapter.id(), 33 | chapter.time_base(), 34 | chapter.start(), 35 | chapter.end(), 36 | &title, 37 | ) { 38 | Ok(chapter) => println!("Added chapter with id {} to output", chapter.id()), 39 | Err(error) => { 40 | println!("Error adding chapter with id: {} - {}", chapter.id(), error) 41 | } 42 | } 43 | } 44 | 45 | println!("\nOuput: nb chapters: {}", octx.nb_chapters()); 46 | for chapter in octx.chapters() { 47 | println!("chapter id {}:", chapter.id()); 48 | println!("\ttime_base: {}", chapter.time_base()); 49 | println!("\tstart: {}", chapter.start()); 50 | println!("\tend: {}", chapter.end()); 51 | for (k, v) in chapter.metadata().iter() { 52 | println!("\t{k}: {v}"); 53 | } 54 | } 55 | } 56 | 57 | Err(error) => println!("error: {error}"), 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /examples/codec-info.rs: -------------------------------------------------------------------------------- 1 | use ffmpeg_the_third as ffmpeg; 2 | 3 | use std::env; 4 | 5 | fn main() { 6 | ffmpeg::init().unwrap(); 7 | 8 | for arg in env::args().skip(1) { 9 | if let Some(codec) = ffmpeg::decoder::find_by_name(&arg) { 10 | println!("type: decoder"); 11 | println!("\t id: {:?}", codec.id()); 12 | println!("\t name: {}", codec.name()); 13 | println!("\t description: {}", codec.description()); 14 | println!("\t medium: {:?}", codec.medium()); 15 | println!("\t capabilities: {:?}", codec.capabilities()); 16 | 17 | if let Some(profiles) = codec.profiles() { 18 | println!("\t profiles: {:?}", profiles.collect::>()); 19 | } else { 20 | println!("\t profiles: none"); 21 | } 22 | 23 | if let Some(video) = codec.video() { 24 | if let Some(rates) = video.rates() { 25 | println!("\t rates: {:?}", rates.collect::>()); 26 | } else { 27 | println!("\t rates: any"); 28 | } 29 | 30 | if let Some(formats) = video.formats() { 31 | println!("\t formats: {:?}", formats.collect::>()); 32 | } else { 33 | println!("\t formats: any"); 34 | } 35 | } 36 | 37 | if let Some(audio) = codec.audio() { 38 | if let Some(rates) = audio.rates() { 39 | println!("\t rates: {:?}", rates.collect::>()); 40 | } else { 41 | println!("\t rates: any"); 42 | } 43 | 44 | if let Some(formats) = audio.formats() { 45 | println!("\t formats: {:?}", formats.collect::>()); 46 | } else { 47 | println!("\t formats: any"); 48 | } 49 | 50 | #[cfg(feature = "ffmpeg_5_1")] 51 | if let Some(layouts) = audio.ch_layouts() { 52 | println!("\t channel_layouts: {:?}", layouts.collect::>()); 53 | } else { 54 | println!("\t channel_layouts: any"); 55 | } 56 | } 57 | 58 | println!("\t max_lowres: {:?}", codec.max_lowres()); 59 | } 60 | 61 | if let Some(codec) = ffmpeg::encoder::find_by_name(&arg) { 62 | println!(); 63 | println!("type: encoder"); 64 | println!("\t id: {:?}", codec.id()); 65 | println!("\t name: {}", codec.name()); 66 | println!("\t description: {}", codec.description()); 67 | println!("\t medium: {:?}", codec.medium()); 68 | println!("\t capabilities: {:?}", codec.capabilities()); 69 | 70 | if let Some(profiles) = codec.profiles() { 71 | println!("\t profiles: {:?}", profiles.collect::>()); 72 | } 73 | 74 | if let Some(video) = codec.video() { 75 | if let Some(rates) = video.rates() { 76 | println!("\t rates: {:?}", rates.collect::>()); 77 | } else { 78 | println!("\t rates: any"); 79 | } 80 | 81 | if let Some(formats) = video.formats() { 82 | println!("\t formats: {:?}", formats.collect::>()); 83 | } else { 84 | println!("\t formats: any"); 85 | } 86 | } 87 | 88 | if let Some(audio) = codec.audio() { 89 | if let Some(rates) = audio.rates() { 90 | println!("\t rates: {:?}", rates.collect::>()); 91 | } else { 92 | println!("\t rates: any"); 93 | } 94 | 95 | if let Some(formats) = audio.formats() { 96 | println!("\t formats: {:?}", formats.collect::>()); 97 | } else { 98 | println!("\t formats: any"); 99 | } 100 | 101 | #[cfg(feature = "ffmpeg_5_1")] 102 | if let Some(layouts) = audio.ch_layouts() { 103 | println!("\t channel_layouts: {:?}", layouts.collect::>()); 104 | } else { 105 | println!("\t channel_layouts: any"); 106 | } 107 | } 108 | 109 | println!("\t max_lowres: {:?}", codec.max_lowres()); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /examples/dump-frames.rs: -------------------------------------------------------------------------------- 1 | use ffmpeg_the_third as ffmpeg; 2 | 3 | use crate::ffmpeg::format::{input, Pixel}; 4 | use crate::ffmpeg::media::Type; 5 | use crate::ffmpeg::software::scaling::{context::Context, flag::Flags}; 6 | use crate::ffmpeg::util::frame::video::Video; 7 | use std::env; 8 | use std::fs::File; 9 | use std::io::prelude::*; 10 | 11 | fn main() -> Result<(), ffmpeg::Error> { 12 | ffmpeg::init().unwrap(); 13 | 14 | if let Ok(mut ictx) = input(env::args().nth(1).expect("Cannot open file.")) { 15 | let input = ictx 16 | .streams() 17 | .best(Type::Video) 18 | .ok_or(ffmpeg::Error::StreamNotFound)?; 19 | let video_stream_index = input.index(); 20 | 21 | let mut context_decoder = 22 | ffmpeg::codec::context::Context::from_parameters(input.parameters())?; 23 | 24 | if let Ok(parallelism) = std::thread::available_parallelism() { 25 | context_decoder.set_threading(ffmpeg::threading::Config { 26 | kind: ffmpeg::threading::Type::Frame, 27 | count: parallelism.get(), 28 | #[cfg(not(feature = "ffmpeg_6_0"))] 29 | safe: false, 30 | }); 31 | } 32 | 33 | let mut decoder = context_decoder.decoder().video()?; 34 | 35 | let mut scaler = Context::get( 36 | decoder.format(), 37 | decoder.width(), 38 | decoder.height(), 39 | Pixel::RGB24, 40 | decoder.width(), 41 | decoder.height(), 42 | Flags::BILINEAR, 43 | )?; 44 | 45 | let mut frame_index = 0; 46 | 47 | let mut receive_and_process_decoded_frames = 48 | |decoder: &mut ffmpeg::decoder::Video| -> Result<(), ffmpeg::Error> { 49 | let mut decoded = Video::empty(); 50 | while decoder.receive_frame(&mut decoded).is_ok() { 51 | let mut rgb_frame = Video::empty(); 52 | scaler.run(&decoded, &mut rgb_frame)?; 53 | save_file(&rgb_frame, frame_index).unwrap(); 54 | frame_index += 1; 55 | } 56 | Ok(()) 57 | }; 58 | 59 | for (stream, packet) in ictx.packets().filter_map(Result::ok) { 60 | if stream.index() == video_stream_index { 61 | decoder.send_packet(&packet)?; 62 | receive_and_process_decoded_frames(&mut decoder)?; 63 | } 64 | } 65 | decoder.send_eof()?; 66 | receive_and_process_decoded_frames(&mut decoder)?; 67 | } 68 | 69 | Ok(()) 70 | } 71 | 72 | fn save_file(frame: &Video, index: usize) -> std::result::Result<(), std::io::Error> { 73 | let mut file = File::create(format!("frame{index}.ppm"))?; 74 | file.write_all(format!("P6\n{} {}\n255\n", frame.width(), frame.height()).as_bytes())?; 75 | file.write_all(frame.data(0))?; 76 | Ok(()) 77 | } 78 | -------------------------------------------------------------------------------- /examples/metadata.rs: -------------------------------------------------------------------------------- 1 | use ffmpeg_the_third as ffmpeg; 2 | 3 | use std::env; 4 | 5 | fn main() -> Result<(), ffmpeg::Error> { 6 | ffmpeg::init().unwrap(); 7 | 8 | match ffmpeg::format::input(env::args().nth(1).expect("missing file")) { 9 | Ok(context) => { 10 | for (k, v) in context.metadata().iter() { 11 | println!("{k}: {v}"); 12 | } 13 | 14 | if let Some(stream) = context.streams().best(ffmpeg::media::Type::Video) { 15 | println!("Best video stream index: {}", stream.index()); 16 | } 17 | 18 | if let Some(stream) = context.streams().best(ffmpeg::media::Type::Audio) { 19 | println!("Best audio stream index: {}", stream.index()); 20 | } 21 | 22 | if let Some(stream) = context.streams().best(ffmpeg::media::Type::Subtitle) { 23 | println!("Best subtitle stream index: {}", stream.index()); 24 | } 25 | 26 | println!( 27 | "duration (seconds): {:.2}", 28 | context.duration() as f64 / f64::from(ffmpeg::ffi::AV_TIME_BASE) 29 | ); 30 | 31 | for stream in context.streams() { 32 | println!("stream index {}:", stream.index()); 33 | println!("\ttime_base: {}", stream.time_base()); 34 | println!("\tstart_time: {}", stream.start_time()); 35 | println!("\tduration (stream timebase): {}", stream.duration()); 36 | println!( 37 | "\tduration (seconds): {:.2}", 38 | stream.duration() as f64 * f64::from(stream.time_base()) 39 | ); 40 | println!("\tframes: {}", stream.frames()); 41 | println!("\tdisposition: {:?}", stream.disposition()); 42 | println!("\tdiscard: {:?}", stream.discard()); 43 | println!("\trate: {}", stream.rate()); 44 | 45 | let codec = ffmpeg::codec::context::Context::from_parameters(stream.parameters())?; 46 | println!("\tmedium: {:?}", codec.medium()); 47 | println!("\tid: {:?}", codec.id()); 48 | 49 | if codec.medium() == ffmpeg::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::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.format: {:?}", audio.format()); 77 | println!("\taudio.align: {}", audio.align()); 78 | #[cfg(feature = "ffmpeg_5_1")] 79 | println!("\taudio.ch_layout: {:?}", audio.ch_layout()); 80 | 81 | #[cfg(not(feature = "ffmpeg_5_1"))] 82 | println!("\taudio.channels: {}", audio.channels()); 83 | #[cfg(not(feature = "ffmpeg_5_1"))] 84 | println!("\taudio.channel_layout: {:?}", audio.channel_layout()); 85 | } 86 | } 87 | } 88 | } 89 | 90 | Err(error) => println!("error: {error}"), 91 | } 92 | Ok(()) 93 | } 94 | -------------------------------------------------------------------------------- /examples/remux.rs: -------------------------------------------------------------------------------- 1 | use ffmpeg_the_third as ffmpeg; 2 | 3 | use std::env; 4 | 5 | use crate::ffmpeg::{codec, encoder, format, log, media, Rational}; 6 | 7 | fn main() { 8 | let input_file = env::args().nth(1).expect("missing input file"); 9 | let output_file = env::args().nth(2).expect("missing output file"); 10 | 11 | ffmpeg::init().unwrap(); 12 | log::set_level(log::Level::Warning); 13 | 14 | let mut ictx = format::input(&input_file).unwrap(); 15 | let mut octx = format::output(&output_file).unwrap(); 16 | 17 | let mut stream_mapping = vec![0; ictx.nb_streams() as _]; 18 | let mut ist_time_bases = vec![Rational(0, 1); ictx.nb_streams() as _]; 19 | let mut ost_index = 0; 20 | for (ist_index, ist) in ictx.streams().enumerate() { 21 | let ist_medium = ist.parameters().medium(); 22 | if ist_medium != media::Type::Audio 23 | && ist_medium != media::Type::Video 24 | && ist_medium != media::Type::Subtitle 25 | { 26 | stream_mapping[ist_index] = -1; 27 | continue; 28 | } 29 | stream_mapping[ist_index] = ost_index; 30 | ist_time_bases[ist_index] = ist.time_base(); 31 | ost_index += 1; 32 | let mut ost = octx.add_stream(encoder::find(codec::Id::None)).unwrap(); 33 | ost.set_parameters(ist.parameters()); 34 | // We need to set codec_tag to 0 lest we run into incompatible codec tag 35 | // issues when muxing into a different container format. Unfortunately 36 | // there's no high level API to do this (yet). 37 | unsafe { 38 | (*ost.parameters_mut().as_mut_ptr()).codec_tag = 0; 39 | } 40 | } 41 | 42 | octx.set_metadata(ictx.metadata().to_owned()); 43 | octx.write_header().unwrap(); 44 | 45 | for (stream, mut packet) in ictx.packets().filter_map(Result::ok) { 46 | let ist_index = stream.index(); 47 | let ost_index = stream_mapping[ist_index]; 48 | if ost_index < 0 { 49 | continue; 50 | } 51 | let ost = octx.stream(ost_index as _).unwrap(); 52 | packet.rescale_ts(ist_time_bases[ist_index], ost.time_base()); 53 | packet.set_position(-1); 54 | packet.set_stream(ost_index as _); 55 | packet.write_interleaved(&mut octx).unwrap(); 56 | } 57 | 58 | octx.write_trailer().unwrap(); 59 | } 60 | -------------------------------------------------------------------------------- /ffmpeg-sys-the-third/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | /tmp 4 | -------------------------------------------------------------------------------- /ffmpeg-sys-the-third/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ffmpeg-sys-the-third" 3 | version = "3.0.0+ffmpeg-7.1" 4 | links = "ffmpeg" 5 | edition = "2021" 6 | 7 | authors = [ 8 | "meh. ", 9 | "Zhiming Wang ", 10 | "Josh Holmer ", 11 | ] 12 | license = "WTFPL" 13 | 14 | include = ["LICENSE", "build.rs", "src/"] 15 | 16 | description = "FFI bindings to FFmpeg" 17 | repository = "https://github.com/shssoichiro/ffmpeg-the-third" 18 | keywords = ["audio", "video"] 19 | categories = ["multimedia", "external-ffi-bindings"] 20 | 21 | # When changing MSRV: Also update build.yml and README.md 22 | rust-version = "1.65.0" 23 | 24 | [lib] 25 | # Disable doctests as a workaround for https://github.com/rust-lang/rust-bindgen/issues/1313 26 | doctest = false 27 | 28 | [dependencies] 29 | libc = "0.2" 30 | 31 | [build-dependencies] 32 | cc = "1.0" 33 | pkg-config = "0.3" 34 | bindgen = { version = "0.69", default-features = false, features = ["runtime"] } 35 | clang = { version = "2.0.0", features = ["clang_3_9", "runtime"] } 36 | 37 | [target.'cfg(target_env = "msvc")'.build-dependencies] 38 | vcpkg = "0.2" 39 | 40 | [features] 41 | default = [ 42 | "avcodec", 43 | "avdevice", 44 | "avfilter", 45 | "avformat", 46 | "swresample", 47 | "swscale", 48 | "non-exhaustive-enums", 49 | ] 50 | 51 | static = [] 52 | 53 | # mark enums in generated bindings as #[non_exhaustive] 54 | non-exhaustive-enums = [] 55 | 56 | # components 57 | avcodec = [] 58 | avdevice = ["avformat"] 59 | avfilter = [] 60 | avformat = ["avcodec"] 61 | postproc = [] 62 | swresample = [] 63 | swscale = [] 64 | 65 | # 66 | # Build-related features 67 | # 68 | 69 | # build FFmpeg when building this crate 70 | build = ["static"] 71 | 72 | # licensing 73 | build-license-gpl = ["build"] 74 | build-license-nonfree = ["build"] 75 | build-license-version3 = ["build"] 76 | 77 | # misc 78 | build-drm = ["build"] 79 | build-nvenc = ["build"] 80 | build-pic = ["build"] 81 | build-zlib = ["build"] 82 | 83 | # ssl 84 | build-lib-gnutls = ["build"] 85 | build-lib-openssl = ["build"] 86 | 87 | # filters 88 | build-lib-fontconfig = ["build"] 89 | build-lib-frei0r = ["build"] 90 | build-lib-ladspa = ["build"] 91 | build-lib-ass = ["build"] 92 | build-lib-freetype = ["build"] 93 | build-lib-freebidi = ["build"] 94 | build-lib-opencv = ["build"] 95 | build-lib-vmaf = ["build"] 96 | 97 | # encoders/decoders 98 | build-lib-aacplus = ["build"] 99 | build-lib-celt = ["build"] 100 | build-lib-codec2 = ["build"] 101 | build-lib-dav1d = ["build"] 102 | build-lib-davs2 = ["build"] 103 | build-lib-dcadec = ["build"] 104 | build-lib-faac = ["build"] 105 | build-lib-fdk-aac = ["build"] 106 | build-lib-gsm = ["build"] 107 | build-lib-ilbc = ["build"] 108 | build-lib-jxl = ["build"] 109 | build-lib-kvazaar = ["build"] 110 | build-lib-lc3 = ["build"] 111 | build-lib-lcevc-dec = ["build"] 112 | build-lib-mp3lame = ["build"] 113 | build-lib-opencore-amrnb = ["build"] 114 | build-lib-opencore-amrwb = ["build"] 115 | build-lib-openh264 = ["build"] 116 | build-lib-openjpeg = ["build"] 117 | build-lib-opus = ["build"] 118 | build-lib-rav1e = ["build"] 119 | build-lib-schroedinger = ["build"] 120 | build-lib-shine = ["build"] 121 | build-lib-snappy = ["build"] 122 | build-lib-speex = ["build"] 123 | build-lib-stagefright-h264 = ["build"] 124 | build-lib-svtav1 = ["build"] 125 | build-lib-theora = ["build"] 126 | build-lib-twolame = ["build"] 127 | build-lib-uavs3d = ["build"] 128 | build-lib-utvideo = ["build"] 129 | build-lib-vo-aacenc = ["build"] 130 | build-lib-vo-amrwbenc = ["build"] 131 | build-lib-vorbis = ["build"] 132 | build-lib-vpx = ["build"] 133 | build-lib-vvenc = ["build"] 134 | build-lib-wavpack = ["build"] 135 | build-lib-webp = ["build"] 136 | build-lib-x264 = ["build"] 137 | build-lib-x265 = ["build"] 138 | build-lib-xeve = ["build"] 139 | build-lib-xevd = ["build"] 140 | build-lib-xavs = ["build"] 141 | build-lib-xavs2 = ["build"] 142 | build-lib-avs = ["build"] 143 | build-lib-xvid = ["build"] 144 | 145 | # protocols 146 | build-lib-smbclient = ["build"] 147 | build-lib-ssh = ["build"] 148 | -------------------------------------------------------------------------------- /ffmpeg-sys-the-third/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /ffmpeg-sys-the-third/README.md: -------------------------------------------------------------------------------- 1 | [![ffmpeg-sys-the-third on crates.io](https://img.shields.io/crates/v/ffmpeg-sys-the-third?cacheSeconds=3600)](https://crates.io/crates/ffmpeg-sys-the-third) 2 | [![build](https://github.com/shssoichiro/ffmpeg-the-third-sys/workflows/build/badge.svg)](https://github.com/shssoichiro/ffmpeg-the-third-sys/actions) 3 | 4 | This is a fork of the abandoned [ffmpeg-sys](https://github.com/meh/rust-ffmpeg-sys) crate. You can find this crate as [ffmpeg-sys-the-third](https://crates.io/crates/ffmpeg-sys-the-third) on crates.io. 5 | 6 | This crate contains low level bindings to FFmpeg. You're probably interested in the high level bindings instead: [ffmpeg-next](https://github.com/shssoichiro/ffmpeg-the-third). 7 | 8 | A word on versioning: The crate version includes the **maximum supported** FFmpeg version for each release. E.g. `ffmpeg-sys-the-third@2.0.0+ffmpeg-7.0` supports *up to* FFmpeg 7.0. The minimum supported FFmpeg version at the moment is 4.2. 9 | 10 | ## Feature flags 11 | 12 | In addition to feature flags declared in `Cargo.toml`, this crate performs various compile-time version and feature detections and exposes the results in additional flags. These flags are briefly documented below; run `cargo build -vv` to view more details. 13 | 14 | - `ffmpeg__` flags, e.g. `ffmpeg_4_4`, indicating the FFmpeg installation being compiled against is at least version `.`. Currently available: 15 | 16 | - "ffmpeg_4_3" 17 | - "ffmpeg_4_4" 18 | - "ffmpeg_5_0" 19 | - "ffmpeg_5_1" 20 | - "ffmpeg_6_0" 21 | - "ffmpeg_6_1" 22 | - "ffmpeg_7_0" 23 | - "ffmpeg_7_1" 24 | 25 | - `avcodec_version_greater_than__`, e.g., `avcodec_version_greater_than_58_90`. The name should be self-explanatory. 26 | 27 | - `ff_api_`, e.g. `ff_api_vaapi`, corresponding to whether their respective uppercase deprecation guards evaluate to true. 28 | 29 | - `ff_api__is_defined`, e.g. `ff_api_vappi_is_defined`, similar to above except these are enabled as long as the corresponding deprecation guards are defined. 30 | -------------------------------------------------------------------------------- /ffmpeg-sys-the-third/src/avutil/error.rs: -------------------------------------------------------------------------------- 1 | use libc::{c_char, c_int, size_t}; 2 | 3 | // Note: FFmpeg's AVERROR and AVUNERROR are conditionally defined based on 4 | // whether EDOM is positive, claiming that "Some platforms have E* and errno 5 | // already negated". This can be traced to a commit in 2007 where "some 6 | // platforms" were specifically identified as BeOS (so maybe also Haiku?): 7 | // https://github.com/FFmpeg/FFmpeg/commit/8fa36ae09dddb1b639b4df5d505c0dbcf4e916e4 8 | // constness is more valuable than BeOS support, so if someone really needs it, 9 | // send a patch with cfg_attr. 10 | 11 | #[inline(always)] 12 | pub const fn AVERROR(e: c_int) -> c_int { 13 | -e 14 | } 15 | 16 | #[inline(always)] 17 | pub const fn AVUNERROR(e: c_int) -> c_int { 18 | -e 19 | } 20 | 21 | #[inline(always)] 22 | pub const fn MKTAG(a: u8, b: u8, c: u8, d: u8) -> u32 { 23 | u32::from_le_bytes([a, b, c, d]) 24 | } 25 | 26 | #[inline(always)] 27 | pub const fn MKBETAG(a: u8, b: u8, c: u8, d: u8) -> u32 { 28 | u32::from_be_bytes([a, b, c, d]) 29 | } 30 | 31 | #[inline(always)] 32 | pub const fn FFERRTAG(a: u8, b: u8, c: u8, d: u8) -> c_int { 33 | let tag = MKTAG(a, b, c, d); 34 | assert!(c_int::MAX as u32 >= tag, "error tag must fit inside c_int"); 35 | 36 | -(tag as c_int) 37 | } 38 | 39 | pub const AVERROR_BSF_NOT_FOUND: c_int = FFERRTAG(0xF8, b'B', b'S', b'F'); 40 | pub const AVERROR_BUG: c_int = FFERRTAG(b'B', b'U', b'G', b'!'); 41 | pub const AVERROR_BUFFER_TOO_SMALL: c_int = FFERRTAG(b'B', b'U', b'F', b'S'); 42 | pub const AVERROR_DECODER_NOT_FOUND: c_int = FFERRTAG(0xF8, b'D', b'E', b'C'); 43 | pub const AVERROR_DEMUXER_NOT_FOUND: c_int = FFERRTAG(0xF8, b'D', b'E', b'M'); 44 | pub const AVERROR_ENCODER_NOT_FOUND: c_int = FFERRTAG(0xF8, b'E', b'N', b'C'); 45 | pub const AVERROR_EOF: c_int = FFERRTAG(b'E', b'O', b'F', b' '); 46 | pub const AVERROR_EXIT: c_int = FFERRTAG(b'E', b'X', b'I', b'T'); 47 | pub const AVERROR_EXTERNAL: c_int = FFERRTAG(b'E', b'X', b'T', b' '); 48 | pub const AVERROR_FILTER_NOT_FOUND: c_int = FFERRTAG(0xF8, b'F', b'I', b'L'); 49 | pub const AVERROR_INVALIDDATA: c_int = FFERRTAG(b'I', b'N', b'D', b'A'); 50 | pub const AVERROR_MUXER_NOT_FOUND: c_int = FFERRTAG(0xF8, b'M', b'U', b'X'); 51 | pub const AVERROR_OPTION_NOT_FOUND: c_int = FFERRTAG(0xF8, b'O', b'P', b'T'); 52 | pub const AVERROR_PATCHWELCOME: c_int = FFERRTAG(b'P', b'A', b'W', b'E'); 53 | pub const AVERROR_PROTOCOL_NOT_FOUND: c_int = FFERRTAG(0xF8, b'P', b'R', b'O'); 54 | 55 | pub const AVERROR_STREAM_NOT_FOUND: c_int = FFERRTAG(0xF8, b'S', b'T', b'R'); 56 | 57 | pub const AVERROR_BUG2: c_int = FFERRTAG(b'B', b'U', b'G', b' '); 58 | pub const AVERROR_UNKNOWN: c_int = FFERRTAG(b'U', b'N', b'K', b'N'); 59 | 60 | pub const AVERROR_HTTP_BAD_REQUEST: c_int = FFERRTAG(0xF8, b'4', b'0', b'0'); 61 | pub const AVERROR_HTTP_UNAUTHORIZED: c_int = FFERRTAG(0xF8, b'4', b'0', b'1'); 62 | pub const AVERROR_HTTP_FORBIDDEN: c_int = FFERRTAG(0xF8, b'4', b'0', b'3'); 63 | pub const AVERROR_HTTP_NOT_FOUND: c_int = FFERRTAG(0xF8, b'4', b'0', b'4'); 64 | pub const AVERROR_HTTP_OTHER_4XX: c_int = FFERRTAG(0xF8, b'4', b'X', b'X'); 65 | pub const AVERROR_HTTP_SERVER_ERROR: c_int = FFERRTAG(0xF8, b'5', b'X', b'X'); 66 | 67 | #[inline(always)] 68 | pub unsafe fn av_make_error_string( 69 | errbuf: *mut c_char, 70 | errbuf_size: size_t, 71 | errnum: c_int, 72 | ) -> *mut c_char { 73 | crate::av_strerror(errnum, errbuf, errbuf_size); 74 | 75 | errbuf 76 | } 77 | -------------------------------------------------------------------------------- /ffmpeg-sys-the-third/src/avutil/mod.rs: -------------------------------------------------------------------------------- 1 | mod error; 2 | pub use self::error::*; 3 | 4 | mod util; 5 | pub use self::util::*; 6 | 7 | mod rational; 8 | pub use self::rational::*; 9 | 10 | mod pixfmt; 11 | pub use self::pixfmt::*; 12 | 13 | #[cfg(feature = "ffmpeg_5_1")] 14 | mod channel_layout; 15 | #[cfg(feature = "ffmpeg_5_1")] 16 | pub use self::channel_layout::*; 17 | -------------------------------------------------------------------------------- /ffmpeg-sys-the-third/src/avutil/rational.rs: -------------------------------------------------------------------------------- 1 | use crate::AVRational; 2 | use libc::{c_double, c_int}; 3 | 4 | #[inline(always)] 5 | pub unsafe fn av_make_q(num: c_int, den: c_int) -> AVRational { 6 | AVRational { num, den } 7 | } 8 | 9 | #[inline(always)] 10 | pub unsafe fn av_cmp_q(a: AVRational, b: AVRational) -> c_int { 11 | let tmp = i64::from(a.num) * i64::from(b.den) - i64::from(b.num) * i64::from(a.den); 12 | 13 | if tmp != 0 { 14 | (((tmp ^ i64::from(a.den) ^ i64::from(b.den)) >> 63) | 1) as c_int 15 | } else if b.den != 0 && a.den != 0 { 16 | 0 17 | } else if a.num != 0 && b.num != 0 { 18 | ((i64::from(a.num) >> 31) - (i64::from(b.num) >> 31)) as c_int 19 | } else { 20 | c_int::MIN 21 | } 22 | } 23 | 24 | #[inline(always)] 25 | pub unsafe fn av_q2d(a: AVRational) -> c_double { 26 | f64::from(a.num) / f64::from(a.den) 27 | } 28 | 29 | #[inline(always)] 30 | pub unsafe fn av_inv_q(q: AVRational) -> AVRational { 31 | AVRational { 32 | num: q.den, 33 | den: q.num, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ffmpeg-sys-the-third/src/avutil/util.rs: -------------------------------------------------------------------------------- 1 | use crate::{AVRational, AV_TIME_BASE}; 2 | 3 | pub const AV_NOPTS_VALUE: i64 = 0x8000000000000000u64 as i64; 4 | pub const AV_TIME_BASE_Q: AVRational = AVRational { 5 | num: 1, 6 | den: AV_TIME_BASE, 7 | }; 8 | -------------------------------------------------------------------------------- /ffmpeg-sys-the-third/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | #![allow(clippy::approx_constant)] 5 | #![allow(clippy::missing_safety_doc)] 6 | #![allow(clippy::redundant_static_lifetimes)] 7 | #![allow(clippy::too_many_arguments)] 8 | #![allow(clippy::type_complexity)] 9 | 10 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 11 | 12 | mod avutil; 13 | pub use crate::avutil::*; 14 | -------------------------------------------------------------------------------- /src/as_ptr.rs: -------------------------------------------------------------------------------- 1 | pub trait AsPtr { 2 | /// Returns a *const raw pointer to the underlying FFmpeg type. 3 | fn as_ptr(&self) -> *const T; 4 | } 5 | 6 | pub trait AsMutPtr { 7 | /// Returns a *mut raw pointer to the underlying FFmpeg type. 8 | fn as_mut_ptr(&mut self) -> *mut T; 9 | } 10 | -------------------------------------------------------------------------------- /src/codec/audio_service.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::AVAudioServiceType::*; 2 | use crate::ffi::*; 3 | #[cfg(feature = "serialize")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 7 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 8 | pub enum AudioService { 9 | Main, 10 | Effects, 11 | VisuallyImpaired, 12 | HearingImpaired, 13 | Dialogue, 14 | Commentary, 15 | Emergency, 16 | VoiceOver, 17 | Karaoke, 18 | } 19 | 20 | impl From for AudioService { 21 | fn from(value: AVAudioServiceType) -> Self { 22 | match value { 23 | AV_AUDIO_SERVICE_TYPE_MAIN => AudioService::Main, 24 | AV_AUDIO_SERVICE_TYPE_EFFECTS => AudioService::Effects, 25 | AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED => AudioService::VisuallyImpaired, 26 | AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED => AudioService::HearingImpaired, 27 | AV_AUDIO_SERVICE_TYPE_DIALOGUE => AudioService::Dialogue, 28 | AV_AUDIO_SERVICE_TYPE_COMMENTARY => AudioService::Commentary, 29 | AV_AUDIO_SERVICE_TYPE_EMERGENCY => AudioService::Emergency, 30 | AV_AUDIO_SERVICE_TYPE_VOICE_OVER => AudioService::VoiceOver, 31 | AV_AUDIO_SERVICE_TYPE_KARAOKE => AudioService::Karaoke, 32 | AV_AUDIO_SERVICE_TYPE_NB => AudioService::Main, 33 | 34 | #[cfg(feature = "non-exhaustive-enums")] 35 | _ => unimplemented!(), 36 | } 37 | } 38 | } 39 | 40 | impl From for AVAudioServiceType { 41 | fn from(value: AudioService) -> AVAudioServiceType { 42 | match value { 43 | AudioService::Main => AV_AUDIO_SERVICE_TYPE_MAIN, 44 | AudioService::Effects => AV_AUDIO_SERVICE_TYPE_EFFECTS, 45 | AudioService::VisuallyImpaired => AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED, 46 | AudioService::HearingImpaired => AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED, 47 | AudioService::Dialogue => AV_AUDIO_SERVICE_TYPE_DIALOGUE, 48 | AudioService::Commentary => AV_AUDIO_SERVICE_TYPE_COMMENTARY, 49 | AudioService::Emergency => AV_AUDIO_SERVICE_TYPE_EMERGENCY, 50 | AudioService::VoiceOver => AV_AUDIO_SERVICE_TYPE_VOICE_OVER, 51 | AudioService::Karaoke => AV_AUDIO_SERVICE_TYPE_KARAOKE, 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/codec/capabilities.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_uint; 3 | 4 | bitflags::bitflags! { 5 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 6 | pub struct Capabilities: c_uint { 7 | const DRAW_HORIZ_BAND = AV_CODEC_CAP_DRAW_HORIZ_BAND; 8 | const DR1 = AV_CODEC_CAP_DR1; 9 | #[cfg(not(feature = "ffmpeg_6_0"))] 10 | const TRUNCATED = AV_CODEC_CAP_TRUNCATED; 11 | const DELAY = AV_CODEC_CAP_DELAY; 12 | const SMALL_LAST_FRAME = AV_CODEC_CAP_SMALL_LAST_FRAME; 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 | #[cfg(not(feature = "ffmpeg_6_0"))] 20 | const AUTO_THREADS = AV_CODEC_CAP_AUTO_THREADS; 21 | #[cfg(feature = "ffmpeg_6_0")] 22 | const OTHER_THREADS = AV_CODEC_CAP_OTHER_THREADS; 23 | const VARIABLE_FRAME_SIZE = AV_CODEC_CAP_VARIABLE_FRAME_SIZE; 24 | #[cfg(not(feature = "ffmpeg_6_0"))] 25 | const INTRA_ONLY = AV_CODEC_CAP_INTRA_ONLY; 26 | #[cfg(not(feature = "ffmpeg_6_0"))] 27 | const LOSSLESS = AV_CODEC_CAP_LOSSLESS; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/codec/compliance.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | #[cfg(feature = "serialize")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 7 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 8 | pub enum Compliance { 9 | VeryStrict, 10 | Strict, 11 | Normal, 12 | Unofficial, 13 | Experimental, 14 | } 15 | 16 | impl From for Compliance { 17 | fn from(value: c_int) -> Self { 18 | match value { 19 | FF_COMPLIANCE_VERY_STRICT => Compliance::VeryStrict, 20 | FF_COMPLIANCE_STRICT => Compliance::Strict, 21 | FF_COMPLIANCE_NORMAL => Compliance::Normal, 22 | FF_COMPLIANCE_UNOFFICIAL => Compliance::Unofficial, 23 | FF_COMPLIANCE_EXPERIMENTAL => Compliance::Experimental, 24 | 25 | _ => Compliance::Normal, 26 | } 27 | } 28 | } 29 | 30 | impl From for c_int { 31 | fn from(value: Compliance) -> c_int { 32 | match value { 33 | Compliance::VeryStrict => FF_COMPLIANCE_VERY_STRICT, 34 | Compliance::Strict => FF_COMPLIANCE_STRICT, 35 | Compliance::Normal => FF_COMPLIANCE_NORMAL, 36 | Compliance::Unofficial => FF_COMPLIANCE_UNOFFICIAL, 37 | Compliance::Experimental => FF_COMPLIANCE_EXPERIMENTAL, 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/codec/debug.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags::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 | const DCT_COEFF = FF_DEBUG_DCT_COEFF; 12 | const SKIP = FF_DEBUG_SKIP; 13 | const STARTCODE = FF_DEBUG_STARTCODE; 14 | const ER = FF_DEBUG_ER; 15 | const MMCO = FF_DEBUG_MMCO; 16 | const BUGS = FF_DEBUG_BUGS; 17 | const BUFFERS = FF_DEBUG_BUFFERS; 18 | const THREADS = FF_DEBUG_THREADS; 19 | const NOMC = FF_DEBUG_NOMC; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/codec/decoder/check.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags::bitflags! { 5 | pub struct Check: c_int { 6 | const CRC = AV_EF_CRCCHECK; 7 | const BITSTREAM = 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/codec/decoder/conceal.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags::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/decoder.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | use std::ptr; 3 | 4 | use super::{Audio, Check, Conceal, Opened, Subtitle, Video}; 5 | use crate::codec::{traits, Context}; 6 | use crate::ffi::*; 7 | use crate::{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 | -------------------------------------------------------------------------------- /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 crate::codec::Context; 27 | use crate::codec::Id; 28 | use crate::ffi::*; 29 | use crate::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()); 38 | Codec::from_raw(ptr) 39 | } 40 | } 41 | 42 | pub fn find_by_name(name: &str) -> Option { 43 | unsafe { 44 | let name = CString::new(name).unwrap(); 45 | let ptr = avcodec_find_decoder_by_name(name.as_ptr()); 46 | 47 | Codec::from_raw(ptr) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /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 crate::codec::{Context, Profile}; 6 | use crate::ffi::*; 7 | use crate::{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 bit_rate(&self) -> usize { 66 | unsafe { (*self.as_ptr()).bit_rate as usize } 67 | } 68 | 69 | pub fn delay(&self) -> usize { 70 | unsafe { (*self.as_ptr()).delay as usize } 71 | } 72 | 73 | pub fn profile(&self) -> Profile { 74 | unsafe { Profile::from((self.id(), (*self.as_ptr()).profile)) } 75 | } 76 | 77 | pub fn frame_rate(&self) -> Option { 78 | unsafe { 79 | let value = (*self.as_ptr()).framerate; 80 | 81 | if value == (AVRational { num: 0, den: 1 }) { 82 | None 83 | } else { 84 | Some(Rational::from(value)) 85 | } 86 | } 87 | } 88 | 89 | pub fn flush(&mut self) { 90 | unsafe { 91 | avcodec_flush_buffers(self.as_mut_ptr()); 92 | } 93 | } 94 | } 95 | 96 | impl Drop for Opened { 97 | fn drop(&mut self) { 98 | unsafe { 99 | avcodec_close(self.as_mut_ptr()); 100 | } 101 | } 102 | } 103 | 104 | impl Deref for Opened { 105 | type Target = Decoder; 106 | 107 | fn deref(&self) -> &::Target { 108 | &self.0 109 | } 110 | } 111 | 112 | impl DerefMut for Opened { 113 | fn deref_mut(&mut self) -> &mut ::Target { 114 | &mut self.0 115 | } 116 | } 117 | 118 | impl AsRef for Opened { 119 | fn as_ref(&self) -> &Context { 120 | self 121 | } 122 | } 123 | 124 | impl AsMut for Opened { 125 | fn as_mut(&mut self) -> &mut Context { 126 | &mut self.0 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/codec/decoder/slice.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags::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/codec/decoder/subtitle.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | use crate::ffi::*; 4 | use libc::c_int; 5 | 6 | use super::Opened; 7 | use crate::codec::Context; 8 | use crate::{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 crate::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/codec/decoder/video.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | #[cfg(not(feature = "ffmpeg_5_0"))] 4 | use crate::ffi::*; 5 | use libc::c_int; 6 | 7 | use super::{slice, Opened}; 8 | use crate::codec::Context; 9 | use crate::color; 10 | #[cfg(not(feature = "ffmpeg_5_0"))] 11 | use crate::frame; 12 | use crate::util::chroma; 13 | use crate::util::format; 14 | #[cfg(not(feature = "ffmpeg_5_0"))] 15 | use crate::{packet, Error}; 16 | use crate::{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 | #[cfg(not(feature = "ffmpeg_7_0"))] 88 | pub fn set_slice_count(&mut self, value: usize) { 89 | unsafe { 90 | (*self.as_mut_ptr()).slice_count = value as c_int; 91 | } 92 | } 93 | 94 | pub fn set_slice_flags(&mut self, value: slice::Flags) { 95 | unsafe { 96 | (*self.as_mut_ptr()).slice_flags = value.bits(); 97 | } 98 | } 99 | 100 | pub fn skip_top(&mut self, value: usize) { 101 | unsafe { 102 | (*self.as_mut_ptr()).skip_top = value as c_int; 103 | } 104 | } 105 | 106 | pub fn skip_bottom(&mut self, value: usize) { 107 | unsafe { 108 | (*self.as_mut_ptr()).skip_bottom = value as c_int; 109 | } 110 | } 111 | 112 | pub fn references(&self) -> usize { 113 | unsafe { (*self.as_ptr()).refs as usize } 114 | } 115 | 116 | pub fn set_field_order(&mut self, value: FieldOrder) { 117 | unsafe { 118 | (*self.as_mut_ptr()).field_order = value.into(); 119 | } 120 | } 121 | 122 | // intra_matrix 123 | // inter_matrix 124 | 125 | pub fn intra_dc_precision(&self) -> u8 { 126 | unsafe { (*self.as_ptr()).intra_dc_precision as u8 } 127 | } 128 | 129 | pub fn max_bit_rate(&self) -> usize { 130 | unsafe { (*self.as_ptr()).rc_max_rate as usize } 131 | } 132 | } 133 | 134 | impl Deref for Video { 135 | type Target = Opened; 136 | 137 | fn deref(&self) -> &::Target { 138 | &self.0 139 | } 140 | } 141 | 142 | impl DerefMut for Video { 143 | fn deref_mut(&mut self) -> &mut ::Target { 144 | &mut self.0 145 | } 146 | } 147 | 148 | impl AsRef for Video { 149 | fn as_ref(&self) -> &Context { 150 | self 151 | } 152 | } 153 | 154 | impl AsMut for Video { 155 | fn as_mut(&mut self) -> &mut Context { 156 | &mut self.0 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/codec/discard.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::AVDiscard::*; 2 | use crate::ffi::*; 3 | #[cfg(feature = "serialize")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 7 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 8 | pub enum Discard { 9 | None, 10 | Default, 11 | NonReference, 12 | Bidirectional, 13 | NonIntra, 14 | NonKey, 15 | All, 16 | } 17 | 18 | impl From for Discard { 19 | fn from(value: AVDiscard) -> Self { 20 | match value { 21 | AVDISCARD_NONE => Discard::None, 22 | AVDISCARD_DEFAULT => Discard::Default, 23 | AVDISCARD_NONREF => Discard::NonReference, 24 | AVDISCARD_BIDIR => Discard::Bidirectional, 25 | AVDISCARD_NONINTRA => Discard::NonIntra, 26 | AVDISCARD_NONKEY => Discard::NonKey, 27 | AVDISCARD_ALL => Discard::All, 28 | 29 | #[cfg(feature = "non-exhaustive-enums")] 30 | _ => unimplemented!(), 31 | } 32 | } 33 | } 34 | 35 | impl From for AVDiscard { 36 | fn from(value: Discard) -> AVDiscard { 37 | match value { 38 | Discard::None => AVDISCARD_NONE, 39 | Discard::Default => AVDISCARD_DEFAULT, 40 | Discard::NonReference => AVDISCARD_NONREF, 41 | Discard::Bidirectional => AVDISCARD_BIDIR, 42 | Discard::NonIntra => AVDISCARD_NONINTRA, 43 | Discard::NonKey => AVDISCARD_NONKEY, 44 | Discard::All => AVDISCARD_ALL, 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/codec/encoder/comparison.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | #[cfg(feature = "serialize")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 7 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 8 | pub enum Comparison { 9 | SAD, 10 | SSE, 11 | SATD, 12 | DCT, 13 | PSNR, 14 | BIT, 15 | RD, 16 | ZERO, 17 | VSAD, 18 | VSSE, 19 | NSSE, 20 | W53, 21 | W97, 22 | DCTMAX, 23 | DCT264, 24 | CHROMA, 25 | } 26 | 27 | impl From for Comparison { 28 | fn from(value: c_int) -> Comparison { 29 | match value { 30 | FF_CMP_SAD => Comparison::SAD, 31 | FF_CMP_SSE => Comparison::SSE, 32 | FF_CMP_SATD => Comparison::SATD, 33 | FF_CMP_DCT => Comparison::DCT, 34 | FF_CMP_PSNR => Comparison::PSNR, 35 | FF_CMP_BIT => Comparison::BIT, 36 | FF_CMP_RD => Comparison::RD, 37 | FF_CMP_ZERO => Comparison::ZERO, 38 | FF_CMP_VSAD => Comparison::VSAD, 39 | FF_CMP_VSSE => Comparison::VSSE, 40 | FF_CMP_NSSE => Comparison::NSSE, 41 | FF_CMP_W53 => Comparison::W53, 42 | FF_CMP_W97 => Comparison::W97, 43 | FF_CMP_DCTMAX => Comparison::DCTMAX, 44 | FF_CMP_DCT264 => Comparison::DCT264, 45 | FF_CMP_CHROMA => Comparison::CHROMA, 46 | 47 | _ => Comparison::ZERO, 48 | } 49 | } 50 | } 51 | 52 | impl From for c_int { 53 | fn from(value: Comparison) -> c_int { 54 | match value { 55 | Comparison::SAD => FF_CMP_SAD, 56 | Comparison::SSE => FF_CMP_SSE, 57 | Comparison::SATD => FF_CMP_SATD, 58 | Comparison::DCT => FF_CMP_DCT, 59 | Comparison::PSNR => FF_CMP_PSNR, 60 | Comparison::BIT => FF_CMP_BIT, 61 | Comparison::RD => FF_CMP_RD, 62 | Comparison::ZERO => FF_CMP_ZERO, 63 | Comparison::VSAD => FF_CMP_VSAD, 64 | Comparison::VSSE => FF_CMP_VSSE, 65 | Comparison::NSSE => FF_CMP_NSSE, 66 | Comparison::W53 => FF_CMP_W53, 67 | Comparison::W97 => FF_CMP_W97, 68 | Comparison::DCTMAX => FF_CMP_DCTMAX, 69 | Comparison::DCT264 => FF_CMP_DCT264, 70 | Comparison::CHROMA => FF_CMP_CHROMA, 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/codec/encoder/decision.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | #[cfg(feature = "serialize")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 7 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 8 | pub enum Decision { 9 | Simple, 10 | Bits, 11 | RateDistortion, 12 | } 13 | 14 | impl From for Decision { 15 | fn from(value: c_int) -> Decision { 16 | match value { 17 | FF_MB_DECISION_SIMPLE => Decision::Simple, 18 | FF_MB_DECISION_BITS => Decision::Bits, 19 | FF_MB_DECISION_RD => Decision::RateDistortion, 20 | 21 | _ => Decision::Simple, 22 | } 23 | } 24 | } 25 | 26 | impl From for c_int { 27 | fn from(value: Decision) -> c_int { 28 | match value { 29 | Decision::Simple => FF_MB_DECISION_SIMPLE, 30 | Decision::Bits => FF_MB_DECISION_BITS, 31 | Decision::RateDistortion => FF_MB_DECISION_RD, 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /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 crate::codec::Context; 30 | use crate::codec::Id; 31 | use crate::ffi::*; 32 | use crate::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()); 41 | Codec::from_raw(ptr) 42 | } 43 | } 44 | 45 | pub fn find_by_name(name: &str) -> Option { 46 | unsafe { 47 | let name = CString::new(name).unwrap(); 48 | let ptr = avcodec_find_encoder_by_name(name.as_ptr()); 49 | Codec::from_raw(ptr) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/codec/encoder/motion_estimation.rs: -------------------------------------------------------------------------------- 1 | use libc::c_int; 2 | #[cfg(feature = "serialize")] 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 6 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 7 | pub enum MotionEstimation { 8 | Zero, 9 | Full, 10 | Log, 11 | Phods, 12 | Epzs, 13 | X1, 14 | Hex, 15 | Umh, 16 | Iter, 17 | Tesa, 18 | } 19 | 20 | impl From for MotionEstimation { 21 | fn from(value: c_int) -> MotionEstimation { 22 | match value { 23 | 1 => MotionEstimation::Zero, 24 | 2 => MotionEstimation::Full, 25 | 3 => MotionEstimation::Log, 26 | 4 => MotionEstimation::Phods, 27 | 5 => MotionEstimation::Epzs, 28 | 6 => MotionEstimation::X1, 29 | 7 => MotionEstimation::Hex, 30 | 8 => MotionEstimation::Umh, 31 | 9 => MotionEstimation::Iter, 32 | 10 => MotionEstimation::Tesa, 33 | 34 | _ => MotionEstimation::Zero, 35 | } 36 | } 37 | } 38 | 39 | impl From for c_int { 40 | fn from(value: MotionEstimation) -> c_int { 41 | match value { 42 | MotionEstimation::Zero => 1, 43 | MotionEstimation::Full => 2, 44 | MotionEstimation::Log => 3, 45 | MotionEstimation::Phods => 4, 46 | MotionEstimation::Epzs => 5, 47 | MotionEstimation::X1 => 6, 48 | MotionEstimation::Hex => 7, 49 | MotionEstimation::Umh => 8, 50 | MotionEstimation::Iter => 9, 51 | MotionEstimation::Tesa => 10, 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/codec/encoder/prediction.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | #[cfg(feature = "serialize")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 7 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 8 | pub enum Prediction { 9 | Left, 10 | Plane, 11 | Median, 12 | } 13 | 14 | impl From for Prediction { 15 | fn from(value: c_int) -> Prediction { 16 | match value { 17 | FF_PRED_LEFT => Prediction::Left, 18 | FF_PRED_PLANE => Prediction::Plane, 19 | FF_PRED_MEDIAN => Prediction::Median, 20 | 21 | _ => Prediction::Left, 22 | } 23 | } 24 | } 25 | 26 | impl From for c_int { 27 | fn from(value: Prediction) -> c_int { 28 | match value { 29 | Prediction::Left => FF_PRED_LEFT, 30 | Prediction::Plane => FF_PRED_PLANE, 31 | Prediction::Median => FF_PRED_MEDIAN, 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/codec/encoder/subtitle.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | use std::ptr; 3 | 4 | use crate::ffi::*; 5 | use libc::c_int; 6 | 7 | use super::Encoder as Super; 8 | use crate::codec::{traits, Context}; 9 | use crate::{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: &crate::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/codec/field_order.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::AVFieldOrder::*; 2 | use crate::ffi::*; 3 | #[cfg(feature = "serialize")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 7 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 8 | pub enum FieldOrder { 9 | Unknown, 10 | Progressive, 11 | TT, 12 | BB, 13 | TB, 14 | BT, 15 | } 16 | 17 | impl From for FieldOrder { 18 | fn from(value: AVFieldOrder) -> Self { 19 | match value { 20 | AV_FIELD_UNKNOWN => FieldOrder::Unknown, 21 | AV_FIELD_PROGRESSIVE => FieldOrder::Progressive, 22 | AV_FIELD_TT => FieldOrder::TT, 23 | AV_FIELD_BB => FieldOrder::BB, 24 | AV_FIELD_TB => FieldOrder::TB, 25 | AV_FIELD_BT => FieldOrder::BT, 26 | 27 | #[cfg(feature = "non-exhaustive-enums")] 28 | _ => unimplemented!(), 29 | } 30 | } 31 | } 32 | 33 | impl From for AVFieldOrder { 34 | fn from(value: FieldOrder) -> AVFieldOrder { 35 | match value { 36 | FieldOrder::Unknown => AV_FIELD_UNKNOWN, 37 | FieldOrder::Progressive => AV_FIELD_PROGRESSIVE, 38 | FieldOrder::TT => AV_FIELD_TT, 39 | FieldOrder::BB => AV_FIELD_BB, 40 | FieldOrder::TB => AV_FIELD_TB, 41 | FieldOrder::BT => AV_FIELD_BT, 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/codec/flag.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_uint; 3 | 4 | bitflags::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 | #[cfg(not(feature = "ffmpeg_6_0"))] 16 | const TRUNCATED = AV_CODEC_FLAG_TRUNCATED; 17 | const INTERLACED_DCT = AV_CODEC_FLAG_INTERLACED_DCT; 18 | const LOW_DELAY = AV_CODEC_FLAG_LOW_DELAY; 19 | const GLOBAL_HEADER = AV_CODEC_FLAG_GLOBAL_HEADER; 20 | const BITEXACT = AV_CODEC_FLAG_BITEXACT; 21 | const AC_PRED = AV_CODEC_FLAG_AC_PRED; 22 | const LOOP_FILTER = AV_CODEC_FLAG_LOOP_FILTER; 23 | const INTERLACED_ME = AV_CODEC_FLAG_INTERLACED_ME; 24 | const CLOSED_GOP = AV_CODEC_FLAG_CLOSED_GOP; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /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 descriptor; 15 | pub use self::descriptor::CodecDescriptor; 16 | 17 | pub mod discard; 18 | 19 | pub mod config; 20 | 21 | pub mod context; 22 | pub use self::context::Context; 23 | 24 | pub mod capabilities; 25 | pub use self::capabilities::Capabilities; 26 | 27 | pub mod codec; 28 | pub use self::codec::{Audio, Codec, Video}; 29 | 30 | pub mod parameters; 31 | pub use self::parameters::{Parameters, ParametersMut, ParametersRef}; 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 props; 46 | pub use self::props::CodecProperties; 47 | 48 | pub mod threading; 49 | 50 | pub mod decoder; 51 | pub mod encoder; 52 | pub mod traits; 53 | 54 | use crate::ffi::*; 55 | use crate::utils; 56 | 57 | pub fn version() -> u32 { 58 | unsafe { avcodec_version() } 59 | } 60 | 61 | pub fn configuration() -> &'static str { 62 | unsafe { utils::str_from_c_ptr(avcodec_configuration()) } 63 | } 64 | 65 | pub fn license() -> &'static str { 66 | unsafe { utils::str_from_c_ptr(avcodec_license()) } 67 | } 68 | -------------------------------------------------------------------------------- /src/codec/packet/borrow.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ptr; 3 | 4 | use super::Ref; 5 | use crate::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/packet/flag.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags::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/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 | -------------------------------------------------------------------------------- /src/codec/packet/traits.rs: -------------------------------------------------------------------------------- 1 | use crate::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/codec/parameters/borrowed.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::ptr::NonNull; 3 | 4 | use crate::ffi::*; 5 | use crate::AsPtr; 6 | 7 | pub struct ParametersRef<'p> { 8 | ptr: NonNull, 9 | _marker: PhantomData<&'p AVCodecParameters>, 10 | } 11 | 12 | impl<'p> ParametersRef<'p> { 13 | /// # Safety 14 | /// 15 | /// Ensure that 16 | /// - `ptr` is either null or valid, 17 | /// - the shared borrow represented by `ptr` follows Rust borrow rules and 18 | /// - the lifetime of the returned struct is correctly bounded. 19 | pub unsafe fn from_raw(ptr: *const AVCodecParameters) -> Option { 20 | NonNull::new(ptr as *mut _).map(|ptr| Self { 21 | ptr, 22 | _marker: PhantomData, 23 | }) 24 | } 25 | 26 | /// Exposes a pointer to the contained [`AVCodecParameters`] for FFI purposes. 27 | /// 28 | /// This is guaranteed to be a non-null pointer. 29 | pub fn as_ptr(&self) -> *const AVCodecParameters { 30 | self.ptr.as_ptr() 31 | } 32 | } 33 | 34 | impl<'p> AsPtr for ParametersRef<'p> { 35 | fn as_ptr(&self) -> *const AVCodecParameters { 36 | self.ptr.as_ptr() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/codec/parameters/borrowed_mut.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::ptr::NonNull; 3 | 4 | use crate::ffi::*; 5 | use crate::{AsMutPtr, AsPtr}; 6 | 7 | pub struct ParametersMut<'p> { 8 | ptr: NonNull, 9 | _marker: PhantomData<&'p mut AVCodecParameters>, 10 | } 11 | 12 | impl<'p> ParametersMut<'p> { 13 | /// # Safety 14 | /// 15 | /// Ensure that 16 | /// - `ptr` is either null or valid, 17 | /// - the mutable borrow represented by `ptr` follows Rust borrow rules and 18 | /// - the lifetime of the returned struct is correctly bounded. 19 | pub unsafe fn from_raw(ptr: *mut AVCodecParameters) -> Option { 20 | NonNull::new(ptr as *mut _).map(|ptr| Self { 21 | ptr, 22 | _marker: PhantomData, 23 | }) 24 | } 25 | 26 | /// Exposes a pointer to the contained [`AVCodecParameters`] for FFI purposes. 27 | /// 28 | /// This is guaranteed to be a non-null pointer. 29 | pub fn as_ptr(&self) -> *const AVCodecParameters { 30 | self.ptr.as_ptr() 31 | } 32 | 33 | /// Exposes a mutable pointer to the contained [`AVCodecParameters`] for FFI purposes. 34 | /// 35 | /// This is guaranteed to be a non-null pointer. 36 | pub fn as_mut_ptr(&mut self) -> *mut AVCodecParameters { 37 | self.ptr.as_ptr() 38 | } 39 | } 40 | 41 | impl<'p> AsPtr for ParametersMut<'p> { 42 | fn as_ptr(&self) -> *const AVCodecParameters { 43 | self.as_ptr() 44 | } 45 | } 46 | 47 | impl<'p> AsMutPtr for ParametersMut<'p> { 48 | fn as_mut_ptr(&mut self) -> *mut AVCodecParameters { 49 | self.as_mut_ptr() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/codec/parameters/common.rs: -------------------------------------------------------------------------------- 1 | use crate::macros::impl_for_many; 2 | 3 | use super::{Parameters, ParametersMut, ParametersRef}; 4 | use crate::chroma::Location; 5 | use crate::codec::Id; 6 | use crate::color; 7 | use crate::media; 8 | use crate::{FieldOrder, Rational}; 9 | 10 | #[cfg(feature = "ffmpeg_5_1")] 11 | use crate::ChannelLayout; 12 | 13 | impl_for_many! { 14 | impl for Parameters, ParametersRef<'p>, ParametersMut<'p> { 15 | pub fn medium(&self) -> media::Type { 16 | unsafe { (*self.as_ptr()).codec_type.into() } 17 | } 18 | 19 | pub fn id(&self) -> Id { 20 | unsafe { (*self.as_ptr()).codec_id.into() } 21 | } 22 | 23 | // TODO: codec_tag 24 | // TODO: extradata 25 | // TODO: coded_side_data 26 | // TODO: format (needs From for format::Pixel and format::Sample) 27 | 28 | pub fn bit_rate(&self) -> i64 { 29 | unsafe { (*self.as_ptr()).bit_rate } 30 | } 31 | 32 | pub fn bits_per_coded_sample(&self) -> u32 { 33 | unsafe { (*self.as_ptr()).bits_per_coded_sample as u32 } 34 | } 35 | 36 | pub fn bits_per_raw_sample(&self) -> u32 { 37 | unsafe { (*self.as_ptr()).bits_per_raw_sample as u32 } 38 | } 39 | 40 | pub fn profile(&self) -> i32 { 41 | unsafe { (*self.as_ptr()).profile as i32 } 42 | } 43 | 44 | pub fn level(&self) -> i32 { 45 | unsafe { (*self.as_ptr()).level as i32 } 46 | } 47 | 48 | /// Video only 49 | pub fn width(&self) -> u32 { 50 | unsafe { (*self.as_ptr()).width as u32 } 51 | } 52 | 53 | /// Video only 54 | pub fn height(&self) -> u32 { 55 | unsafe { (*self.as_ptr()).height as u32 } 56 | } 57 | 58 | /// Video only 59 | pub fn sample_aspect_ratio(&self) -> Rational { 60 | unsafe { (*self.as_ptr()).sample_aspect_ratio.into() } 61 | } 62 | 63 | /// Video only 64 | #[cfg(feature = "ffmpeg_6_1")] 65 | pub fn framerate(&self) -> Rational { 66 | unsafe { (*self.as_ptr()).framerate.into() } 67 | } 68 | 69 | /// Video only 70 | pub fn field_order(&self) -> FieldOrder { 71 | unsafe { (*self.as_ptr()).field_order.into() } 72 | } 73 | 74 | /// Video only 75 | pub fn color_range(&self) -> color::Range { 76 | unsafe { (*self.as_ptr()).color_range.into() } 77 | } 78 | 79 | /// Video only 80 | pub fn color_primaries(&self) -> color::Primaries { 81 | unsafe { (*self.as_ptr()).color_primaries.into() } 82 | } 83 | 84 | /// Video only 85 | pub fn color_transfer_characteristic(&self) -> color::TransferCharacteristic { 86 | unsafe { (*self.as_ptr()).color_trc.into() } 87 | } 88 | 89 | /// Video only 90 | pub fn color_space(&self) -> color::Space { 91 | unsafe { (*self.as_ptr()).color_space.into() } 92 | } 93 | 94 | /// Video only 95 | pub fn chroma_location(&self) -> Location { 96 | unsafe { (*self.as_ptr()).chroma_location.into() } 97 | } 98 | 99 | /// Video only 100 | pub fn video_delay(&self) -> i32 { 101 | unsafe { (*self.as_ptr()).video_delay as i32 } 102 | } 103 | 104 | /// Audio only 105 | #[cfg(feature = "ffmpeg_5_1")] 106 | pub fn ch_layout(&self) -> ChannelLayout { 107 | unsafe { ChannelLayout::from(&(*self.as_ptr()).ch_layout) } 108 | } 109 | 110 | /// Audio only 111 | pub fn sample_rate(&self) -> u32 { 112 | unsafe { (*self.as_ptr()).sample_rate as u32 } 113 | } 114 | 115 | /// Audio only 116 | pub fn block_align(&self) -> u32 { 117 | unsafe { (*self.as_ptr()).block_align as u32 } 118 | } 119 | 120 | /// Audio only 121 | pub fn frame_size(&self) -> u32 { 122 | unsafe { (*self.as_ptr()).frame_size as u32 } 123 | } 124 | 125 | /// Audio only 126 | pub fn initial_padding(&self) -> u32 { 127 | unsafe { (*self.as_ptr()).initial_padding as u32 } 128 | } 129 | 130 | /// Audio only 131 | pub fn trailing_padding(&self) -> u32 { 132 | unsafe { (*self.as_ptr()).trailing_padding as u32 } 133 | } 134 | 135 | /// Audio only 136 | pub fn seek_preroll(&self) -> u32 { 137 | unsafe { (*self.as_ptr()).seek_preroll as u32 } 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/codec/parameters/mod.rs: -------------------------------------------------------------------------------- 1 | mod borrowed; 2 | mod borrowed_mut; 3 | mod common; 4 | mod owned; 5 | 6 | pub use borrowed::ParametersRef; 7 | pub use borrowed_mut::ParametersMut; 8 | pub use owned::Parameters; 9 | -------------------------------------------------------------------------------- /src/codec/parameters/owned.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::NonNull; 2 | 3 | use crate::codec::Context; 4 | use crate::ffi::*; 5 | use crate::{AsMutPtr, AsPtr}; 6 | 7 | pub struct Parameters { 8 | ptr: NonNull, 9 | } 10 | 11 | unsafe impl Send for Parameters {} 12 | 13 | impl Parameters { 14 | /// # Safety 15 | /// 16 | /// Ensure that 17 | /// - it is valid for the returned struct to take ownership of the [`AVCodecParameters`] 18 | /// and that 19 | /// - `ptr` is not used to break Rust's ownership rules after calling this function. 20 | pub unsafe fn from_raw(ptr: *mut AVCodecParameters) -> Option { 21 | NonNull::new(ptr).map(|ptr| Self { ptr }) 22 | } 23 | 24 | /// Exposes a pointer to the contained [`AVCodecParameters`] for FFI purposes. 25 | /// 26 | /// This is guaranteed to be a non-null pointer. 27 | pub fn as_ptr(&self) -> *const AVCodecParameters { 28 | self.ptr.as_ptr() 29 | } 30 | 31 | /// Exposes a mutable pointer to the contained [`AVCodecParameters`] for FFI purposes. 32 | /// 33 | /// This is guaranteed to be a non-null pointer. 34 | pub fn as_mut_ptr(&mut self) -> *mut AVCodecParameters { 35 | self.ptr.as_ptr() 36 | } 37 | } 38 | 39 | impl Parameters { 40 | /// Allocates a new set of codec parameters set to default values. 41 | pub fn new() -> Self { 42 | let ptr = unsafe { avcodec_parameters_alloc() }; 43 | 44 | Self { 45 | ptr: NonNull::new(ptr).expect("can allocate AVCodecParameters"), 46 | } 47 | } 48 | } 49 | 50 | impl Default for Parameters { 51 | fn default() -> Self { 52 | Self::new() 53 | } 54 | } 55 | 56 | impl Drop for Parameters { 57 | fn drop(&mut self) { 58 | unsafe { 59 | avcodec_parameters_free(&mut self.as_mut_ptr()); 60 | } 61 | } 62 | } 63 | 64 | impl Clone for Parameters { 65 | fn clone(&self) -> Self { 66 | let mut ctx = Parameters::new(); 67 | ctx.clone_from(self); 68 | 69 | ctx 70 | } 71 | 72 | fn clone_from(&mut self, source: &Self) { 73 | unsafe { 74 | avcodec_parameters_copy(self.as_mut_ptr(), source.as_ptr()); 75 | } 76 | } 77 | } 78 | 79 | impl AsPtr for Parameters { 80 | fn as_ptr(&self) -> *const AVCodecParameters { 81 | self.as_ptr() 82 | } 83 | } 84 | 85 | impl AsMutPtr for Parameters { 86 | fn as_mut_ptr(&mut self) -> *mut AVCodecParameters { 87 | self.as_mut_ptr() 88 | } 89 | } 90 | 91 | impl> From for Parameters { 92 | fn from(context: C) -> Parameters { 93 | let mut parameters = Parameters::new(); 94 | let context = context.as_ref(); 95 | unsafe { 96 | avcodec_parameters_from_context(parameters.as_mut_ptr(), context.as_ptr()); 97 | } 98 | parameters 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/codec/props.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags::bitflags! { 5 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 6 | pub struct CodecProperties: c_int { 7 | const INTRA_ONLY = AV_CODEC_PROP_INTRA_ONLY; 8 | const LOSSY = AV_CODEC_PROP_LOSSY; 9 | const LOSSLESS = AV_CODEC_PROP_LOSSLESS; 10 | const REORDER = AV_CODEC_PROP_REORDER; 11 | #[cfg(feature = "ffmpeg_6_1")] 12 | const FIELDS = AV_CODEC_PROP_FIELDS; 13 | 14 | const BITMAP_SUB = AV_CODEC_PROP_BITMAP_SUB; 15 | const TEXT_SUB = AV_CODEC_PROP_TEXT_SUB; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/codec/subtitle/flag.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags::bitflags! { 5 | pub struct Flags: c_int { 6 | const FORCED = AV_SUBTITLE_FLAG_FORCED; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/codec/subtitle/rect.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use super::{Flags, Type}; 4 | use crate::ffi::*; 5 | use crate::utils; 6 | #[cfg(not(feature = "ffmpeg_5_0"))] 7 | use crate::{format, Picture}; 8 | 9 | pub enum Rect<'a> { 10 | None(*const AVSubtitleRect), 11 | Bitmap(Bitmap<'a>), 12 | Text(Text<'a>), 13 | Ass(Ass<'a>), 14 | } 15 | 16 | impl<'a> Rect<'a> { 17 | pub unsafe fn wrap(ptr: *const AVSubtitleRect) -> Self { 18 | match Type::from((*ptr).type_) { 19 | Type::None => Rect::None(ptr), 20 | Type::Bitmap => Rect::Bitmap(Bitmap::wrap(ptr)), 21 | Type::Text => Rect::Text(Text::wrap(ptr)), 22 | Type::Ass => Rect::Ass(Ass::wrap(ptr)), 23 | } 24 | } 25 | 26 | pub unsafe fn as_ptr(&self) -> *const AVSubtitleRect { 27 | match *self { 28 | Rect::None(ptr) => ptr, 29 | Rect::Bitmap(ref b) => b.as_ptr(), 30 | Rect::Text(ref t) => t.as_ptr(), 31 | Rect::Ass(ref a) => a.as_ptr(), 32 | } 33 | } 34 | } 35 | 36 | impl<'a> Rect<'a> { 37 | pub fn flags(&self) -> Flags { 38 | unsafe { 39 | Flags::from_bits_truncate(match *self { 40 | Rect::None(ptr) => (*ptr).flags, 41 | Rect::Bitmap(ref b) => (*b.as_ptr()).flags, 42 | Rect::Text(ref t) => (*t.as_ptr()).flags, 43 | Rect::Ass(ref a) => (*a.as_ptr()).flags, 44 | }) 45 | } 46 | } 47 | } 48 | 49 | pub struct Bitmap<'a> { 50 | ptr: *const AVSubtitleRect, 51 | 52 | _marker: PhantomData<&'a ()>, 53 | } 54 | 55 | impl<'a> Bitmap<'a> { 56 | pub unsafe fn wrap(ptr: *const AVSubtitleRect) -> Self { 57 | Bitmap { 58 | ptr, 59 | _marker: PhantomData, 60 | } 61 | } 62 | 63 | pub unsafe fn as_ptr(&self) -> *const AVSubtitleRect { 64 | self.ptr 65 | } 66 | } 67 | 68 | impl<'a> Bitmap<'a> { 69 | pub fn x(&self) -> usize { 70 | unsafe { (*self.as_ptr()).x as usize } 71 | } 72 | 73 | pub fn y(&self) -> usize { 74 | unsafe { (*self.as_ptr()).y as usize } 75 | } 76 | 77 | pub fn width(&self) -> u32 { 78 | unsafe { (*self.as_ptr()).w as u32 } 79 | } 80 | 81 | pub fn height(&self) -> u32 { 82 | unsafe { (*self.as_ptr()).h as u32 } 83 | } 84 | 85 | pub fn colors(&self) -> usize { 86 | unsafe { (*self.as_ptr()).nb_colors as usize } 87 | } 88 | 89 | // XXX: must split Picture and PictureMut 90 | #[cfg(not(feature = "ffmpeg_5_0"))] 91 | pub fn picture(&self, format: format::Pixel) -> Picture<'a> { 92 | unsafe { 93 | Picture::wrap( 94 | &(*self.as_ptr()).pict as *const _ as *mut _, 95 | format, 96 | (*self.as_ptr()).w as u32, 97 | (*self.as_ptr()).h as u32, 98 | ) 99 | } 100 | } 101 | } 102 | 103 | pub struct Text<'a> { 104 | ptr: *const AVSubtitleRect, 105 | 106 | _marker: PhantomData<&'a ()>, 107 | } 108 | 109 | impl<'a> Text<'a> { 110 | pub unsafe fn wrap(ptr: *const AVSubtitleRect) -> Self { 111 | Text { 112 | ptr, 113 | _marker: PhantomData, 114 | } 115 | } 116 | 117 | pub unsafe fn as_ptr(&self) -> *const AVSubtitleRect { 118 | self.ptr 119 | } 120 | } 121 | 122 | impl<'a> Text<'a> { 123 | pub fn get(&self) -> &str { 124 | unsafe { utils::str_from_c_ptr((*self.as_ptr()).text) } 125 | } 126 | } 127 | 128 | pub struct Ass<'a> { 129 | ptr: *const AVSubtitleRect, 130 | 131 | _marker: PhantomData<&'a ()>, 132 | } 133 | 134 | impl<'a> Ass<'a> { 135 | pub unsafe fn wrap(ptr: *const AVSubtitleRect) -> Self { 136 | Ass { 137 | ptr, 138 | _marker: PhantomData, 139 | } 140 | } 141 | 142 | pub unsafe fn as_ptr(&self) -> *const AVSubtitleRect { 143 | self.ptr 144 | } 145 | } 146 | 147 | impl<'a> Ass<'a> { 148 | pub fn get(&self) -> &str { 149 | unsafe { utils::str_from_c_ptr((*self.as_ptr()).ass) } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/codec/threading.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | #[cfg(feature = "serialize")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 7 | pub struct Config { 8 | pub kind: Type, 9 | pub count: usize, 10 | #[cfg(not(feature = "ffmpeg_6_0"))] 11 | pub safe: bool, 12 | } 13 | 14 | impl Config { 15 | pub fn kind(value: Type) -> Self { 16 | Config { 17 | kind: value, 18 | ..Default::default() 19 | } 20 | } 21 | 22 | pub fn count(value: usize) -> Self { 23 | Config { 24 | count: value, 25 | ..Default::default() 26 | } 27 | } 28 | 29 | #[cfg(not(feature = "ffmpeg_6_0"))] 30 | pub fn safe(value: bool) -> Self { 31 | Config { 32 | safe: value, 33 | ..Default::default() 34 | } 35 | } 36 | } 37 | 38 | impl Default for Config { 39 | fn default() -> Self { 40 | Config { 41 | kind: Type::None, 42 | count: 0, 43 | #[cfg(not(feature = "ffmpeg_6_0"))] 44 | safe: false, 45 | } 46 | } 47 | } 48 | 49 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 50 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 51 | pub enum Type { 52 | None, 53 | Frame, 54 | Slice, 55 | } 56 | 57 | impl From for Type { 58 | fn from(value: c_int) -> Type { 59 | match value { 60 | FF_THREAD_FRAME => Type::Frame, 61 | FF_THREAD_SLICE => Type::Slice, 62 | 63 | _ => Type::None, 64 | } 65 | } 66 | } 67 | 68 | impl From for c_int { 69 | fn from(value: Type) -> c_int { 70 | match value { 71 | Type::None => 0, 72 | Type::Frame => FF_THREAD_FRAME, 73 | Type::Slice => FF_THREAD_SLICE, 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/codec/traits.rs: -------------------------------------------------------------------------------- 1 | use super::codec::UnknownType; 2 | use super::{decoder, encoder}; 3 | use crate::codec::Id; 4 | use crate::Codec; 5 | 6 | pub trait Decoder { 7 | fn decoder(self) -> Option>; 8 | } 9 | 10 | impl Decoder for &str { 11 | fn decoder(self) -> Option> { 12 | decoder::find_by_name(self) 13 | } 14 | } 15 | 16 | impl Decoder for Id { 17 | fn decoder(self) -> Option> { 18 | decoder::find(self) 19 | } 20 | } 21 | 22 | impl Decoder for Codec { 23 | fn decoder(self) -> Option> { 24 | if self.is_decoder() { 25 | Some(self) 26 | } else { 27 | None 28 | } 29 | } 30 | } 31 | 32 | impl Decoder for Option> { 33 | fn decoder(self) -> Option> { 34 | self.and_then(|c| c.decoder()) 35 | } 36 | } 37 | 38 | pub trait Encoder { 39 | fn encoder(self) -> Option>; 40 | } 41 | 42 | impl Encoder for &str { 43 | fn encoder(self) -> Option> { 44 | encoder::find_by_name(self) 45 | } 46 | } 47 | 48 | impl Encoder for Id { 49 | fn encoder(self) -> Option> { 50 | encoder::find(self) 51 | } 52 | } 53 | 54 | impl Encoder for Codec { 55 | fn encoder(self) -> Option> { 56 | if self.is_encoder() { 57 | Some(self) 58 | } else { 59 | None 60 | } 61 | } 62 | } 63 | 64 | impl Encoder for Option> { 65 | fn encoder(self) -> Option> { 66 | self.and_then(|c| c.encoder()) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/device/extensions.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::ptr; 3 | 4 | use crate::device; 5 | use crate::ffi::*; 6 | use crate::format::context::common::Context; 7 | use crate::Error; 8 | use libc::c_int; 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/device/input.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use crate::ffi::*; 4 | use crate::format; 5 | 6 | pub struct AudioIter(*const AVInputFormat); 7 | 8 | impl Iterator for AudioIter { 9 | type Item = format::Input; 10 | 11 | fn next(&mut self) -> Option<::Item> { 12 | unsafe { 13 | let inner = self.0; 14 | 15 | // Pre-5.0 FFmpeg uses a non-const pointer here 16 | #[cfg(not(feature = "ffmpeg_5_0"))] 17 | let inner = inner as *mut _; 18 | 19 | let ptr = av_input_audio_device_next(inner); 20 | 21 | if let Some(input) = format::Input::from_raw(ptr) { 22 | self.0 = ptr; 23 | Some(input) 24 | } else { 25 | None 26 | } 27 | } 28 | } 29 | } 30 | 31 | pub fn audio() -> AudioIter { 32 | AudioIter(ptr::null()) 33 | } 34 | 35 | pub struct VideoIter(*const AVInputFormat); 36 | 37 | impl Iterator for VideoIter { 38 | type Item = format::Input; 39 | 40 | fn next(&mut self) -> Option<::Item> { 41 | unsafe { 42 | let inner = self.0; 43 | 44 | // Pre-5.0 FFmpeg uses a non-const pointer here 45 | #[cfg(not(feature = "ffmpeg_5_0"))] 46 | let inner = inner as *mut _; 47 | 48 | let ptr = av_input_video_device_next(inner); 49 | 50 | if let Some(input) = format::Input::from_raw(ptr) { 51 | self.0 = ptr; 52 | Some(input) 53 | } else { 54 | None 55 | } 56 | } 57 | } 58 | } 59 | 60 | pub fn video() -> VideoIter { 61 | VideoIter(ptr::null()) 62 | } 63 | -------------------------------------------------------------------------------- /src/device/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod extensions; 2 | pub mod input; 3 | pub mod output; 4 | 5 | use std::marker::PhantomData; 6 | 7 | use crate::ffi::*; 8 | use crate::utils; 9 | 10 | pub struct Info<'a> { 11 | ptr: *mut AVDeviceInfo, 12 | 13 | _marker: PhantomData<&'a ()>, 14 | } 15 | 16 | impl<'a> Info<'a> { 17 | pub unsafe fn wrap(ptr: *mut AVDeviceInfo) -> Self { 18 | Info { 19 | ptr, 20 | _marker: PhantomData, 21 | } 22 | } 23 | 24 | pub unsafe fn as_ptr(&self) -> *const AVDeviceInfo { 25 | self.ptr as *const _ 26 | } 27 | 28 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVDeviceInfo { 29 | self.ptr 30 | } 31 | } 32 | 33 | impl<'a> Info<'a> { 34 | pub fn name(&self) -> &str { 35 | unsafe { utils::str_from_c_ptr((*self.as_ptr()).device_name) } 36 | } 37 | 38 | pub fn description(&self) -> &str { 39 | unsafe { utils::str_from_c_ptr((*self.as_ptr()).device_description) } 40 | } 41 | } 42 | 43 | pub fn register_all() { 44 | unsafe { 45 | avdevice_register_all(); 46 | } 47 | } 48 | 49 | pub fn version() -> u32 { 50 | unsafe { avdevice_version() } 51 | } 52 | 53 | pub fn configuration() -> &'static str { 54 | unsafe { utils::str_from_c_ptr(avdevice_configuration()) } 55 | } 56 | 57 | pub fn license() -> &'static str { 58 | unsafe { utils::str_from_c_ptr(avdevice_license()) } 59 | } 60 | -------------------------------------------------------------------------------- /src/device/output.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use crate::ffi::*; 4 | use crate::format; 5 | 6 | pub struct AudioIter(*const AVOutputFormat); 7 | 8 | impl Iterator for AudioIter { 9 | type Item = format::Output; 10 | 11 | fn next(&mut self) -> Option<::Item> { 12 | unsafe { 13 | let inner = self.0; 14 | 15 | // Pre-5.0 FFmpeg uses a non-const pointer here 16 | #[cfg(not(feature = "ffmpeg_5_0"))] 17 | let inner = inner as *mut _; 18 | 19 | let ptr = av_output_audio_device_next(inner); 20 | 21 | if let Some(output) = format::Output::from_raw(ptr) { 22 | self.0 = ptr; 23 | Some(output) 24 | } else { 25 | None 26 | } 27 | } 28 | } 29 | } 30 | 31 | pub fn audio() -> AudioIter { 32 | AudioIter(ptr::null_mut()) 33 | } 34 | 35 | pub struct VideoIter(*const AVOutputFormat); 36 | 37 | impl Iterator for VideoIter { 38 | type Item = format::Output; 39 | 40 | fn next(&mut self) -> Option<::Item> { 41 | unsafe { 42 | let inner = self.0; 43 | 44 | // Pre-5.0 FFmpeg uses a non-const pointer here 45 | #[cfg(not(feature = "ffmpeg_5_0"))] 46 | let inner = inner as *mut _; 47 | 48 | let ptr = av_output_video_device_next(inner); 49 | 50 | if let Some(output) = format::Output::from_raw(ptr) { 51 | self.0 = ptr; 52 | Some(output) 53 | } else { 54 | None 55 | } 56 | } 57 | } 58 | } 59 | 60 | pub fn video() -> VideoIter { 61 | VideoIter(ptr::null_mut()) 62 | } 63 | -------------------------------------------------------------------------------- /src/filter/context/context.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use super::{Sink, Source}; 4 | use crate::ffi::*; 5 | use crate::{format, option, AsMutPtr, AsPtr}; 6 | 7 | #[cfg(feature = "ffmpeg_5_1")] 8 | use crate::ChannelLayout; 9 | 10 | #[cfg(not(feature = "ffmpeg_7_0"))] 11 | use crate::ChannelLayoutMask; 12 | 13 | pub struct Context<'a> { 14 | ptr: *mut AVFilterContext, 15 | 16 | _marker: PhantomData<&'a ()>, 17 | } 18 | 19 | impl<'a> Context<'a> { 20 | pub unsafe fn wrap(ptr: *mut AVFilterContext) -> Self { 21 | Context { 22 | ptr, 23 | _marker: PhantomData, 24 | } 25 | } 26 | 27 | pub unsafe fn as_ptr(&self) -> *const AVFilterContext { 28 | self.ptr as *const _ 29 | } 30 | 31 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFilterContext { 32 | self.ptr 33 | } 34 | } 35 | 36 | impl<'a> Context<'a> { 37 | pub fn source(&'a mut self) -> Source<'a> { 38 | unsafe { Source::wrap(self) } 39 | } 40 | 41 | pub fn sink(&'a mut self) -> Sink<'a> { 42 | unsafe { Sink::wrap(self) } 43 | } 44 | 45 | pub fn set_pixel_format(&mut self, value: format::Pixel) { 46 | let _ = option::Settable::set::(self, "pix_fmts", &value.into()); 47 | } 48 | 49 | pub fn set_sample_format(&mut self, value: format::Sample) { 50 | let _ = option::Settable::set::(self, "sample_fmts", &value.into()); 51 | } 52 | 53 | pub fn set_sample_rate(&mut self, value: u32) { 54 | let _ = option::Settable::set(self, "sample_rates", &i64::from(value)); 55 | } 56 | 57 | #[cfg(not(feature = "ffmpeg_7_0"))] 58 | pub fn set_channel_layout(&mut self, value: ChannelLayoutMask) { 59 | let _ = option::Settable::set(self, "channel_layouts", &value.bits()); 60 | } 61 | 62 | #[cfg(feature = "ffmpeg_5_1")] 63 | pub fn set_ch_layout(&mut self, value: ChannelLayout) { 64 | let _ = option::Settable::set_str(self, "channel_layouts", &value.description()); 65 | } 66 | } 67 | 68 | impl<'a> AsPtr for Context<'a> { 69 | fn as_ptr(&self) -> *const AVFilterContext { 70 | self.ptr as *const _ 71 | } 72 | } 73 | 74 | impl<'a> AsMutPtr for Context<'a> { 75 | fn as_mut_ptr(&mut self) -> *mut AVFilterContext { 76 | self.ptr as *mut _ 77 | } 78 | } 79 | 80 | impl<'a> option::Settable for Context<'a> {} 81 | -------------------------------------------------------------------------------- /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/filter/context/sink.rs: -------------------------------------------------------------------------------- 1 | use super::Context; 2 | use crate::ffi::*; 3 | use crate::{Error, Frame}; 4 | use libc::c_int; 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/filter/context/source.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use super::Context; 4 | use crate::ffi::*; 5 | use crate::{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/filter.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use super::{Flags, Pad}; 4 | use crate::ffi::*; 5 | use crate::utils; 6 | 7 | pub struct Filter { 8 | ptr: *mut AVFilter, 9 | } 10 | 11 | impl Filter { 12 | pub unsafe fn wrap(ptr: *mut AVFilter) -> Self { 13 | Filter { ptr } 14 | } 15 | 16 | pub unsafe fn as_ptr(&self) -> *const AVFilter { 17 | self.ptr as *const _ 18 | } 19 | 20 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFilter { 21 | self.ptr 22 | } 23 | } 24 | 25 | impl Filter { 26 | pub fn name(&self) -> &str { 27 | unsafe { utils::str_from_c_ptr((*self.as_ptr()).name) } 28 | } 29 | 30 | pub fn description(&self) -> Option<&str> { 31 | unsafe { utils::optional_str_from_c_ptr((*self.as_ptr()).description) } 32 | } 33 | 34 | pub fn inputs(&self) -> Option { 35 | unsafe { 36 | let ptr = (*self.as_ptr()).inputs; 37 | 38 | if ptr.is_null() { 39 | None 40 | } else { 41 | #[cfg(feature = "ffmpeg_5_0")] 42 | let count = (*self.as_ptr()).nb_inputs as isize; 43 | #[cfg(not(feature = "ffmpeg_5_0"))] 44 | let count = avfilter_pad_count(ptr) as isize; 45 | 46 | Some(PadIter::new(ptr, count)) 47 | } 48 | } 49 | } 50 | 51 | pub fn outputs(&self) -> Option { 52 | unsafe { 53 | let ptr = (*self.as_ptr()).outputs; 54 | 55 | if ptr.is_null() { 56 | None 57 | } else { 58 | #[cfg(feature = "ffmpeg_5_0")] 59 | let count = (*self.as_ptr()).nb_outputs as isize; 60 | #[cfg(not(feature = "ffmpeg_5_0"))] 61 | let count = avfilter_pad_count(ptr) as isize; 62 | 63 | Some(PadIter::new(ptr, count)) 64 | } 65 | } 66 | } 67 | 68 | pub fn flags(&self) -> Flags { 69 | unsafe { Flags::from_bits_truncate((*self.as_ptr()).flags) } 70 | } 71 | } 72 | 73 | pub struct PadIter<'a> { 74 | ptr: *const AVFilterPad, 75 | count: isize, 76 | cur: isize, 77 | 78 | _marker: PhantomData<&'a ()>, 79 | } 80 | 81 | impl<'a> PadIter<'a> { 82 | pub fn new(ptr: *const AVFilterPad, count: isize) -> Self { 83 | PadIter { 84 | ptr, 85 | count, 86 | cur: 0, 87 | _marker: PhantomData, 88 | } 89 | } 90 | } 91 | 92 | impl<'a> Iterator for PadIter<'a> { 93 | type Item = Pad<'a>; 94 | 95 | fn next(&mut self) -> Option { 96 | unsafe { 97 | if self.cur >= self.count { 98 | return None; 99 | } 100 | 101 | let pad = Pad::wrap(self.ptr, self.cur); 102 | self.cur += 1; 103 | 104 | Some(pad) 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/filter/flag.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags::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/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::CString; 17 | 18 | use crate::ffi::*; 19 | use crate::utils; 20 | #[cfg(not(feature = "ffmpeg_5_0"))] 21 | use crate::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 { utils::str_from_c_ptr(avfilter_configuration()) } 46 | } 47 | 48 | pub fn license() -> &'static str { 49 | unsafe { utils::str_from_c_ptr(avfilter_license()) } 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/filter/pad.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use crate::ffi::*; 4 | use crate::media; 5 | use crate::utils; 6 | 7 | pub struct Pad<'a> { 8 | ptr: *const AVFilterPad, 9 | idx: isize, 10 | 11 | _marker: PhantomData<&'a ()>, 12 | } 13 | 14 | impl<'a> Pad<'a> { 15 | pub unsafe fn wrap(ptr: *const AVFilterPad, idx: isize) -> Self { 16 | Pad { 17 | ptr, 18 | idx, 19 | _marker: PhantomData, 20 | } 21 | } 22 | 23 | pub unsafe fn as_ptr(&self) -> *const AVFilterPad { 24 | self.ptr 25 | } 26 | 27 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFilterPad { 28 | self.ptr as *mut _ 29 | } 30 | } 31 | 32 | impl<'a> Pad<'a> { 33 | pub fn name(&self) -> Option<&str> { 34 | unsafe { 35 | let ptr = avfilter_pad_get_name(self.ptr, self.idx as i32); 36 | utils::optional_str_from_c_ptr(ptr) 37 | } 38 | } 39 | 40 | pub fn medium(&self) -> media::Type { 41 | unsafe { media::Type::from(avfilter_pad_get_type(self.ptr, self.idx as i32)) } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/format/chapter/chapter.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use crate::{DictionaryRef, Rational}; 3 | 4 | use crate::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 | unsafe { (*self.as_ptr()).id as i64 } 30 | } 31 | 32 | pub fn time_base(&self) -> Rational { 33 | unsafe { Rational::from((*self.as_ptr()).time_base) } 34 | } 35 | 36 | pub fn start(&self) -> i64 { 37 | unsafe { (*self.as_ptr()).start } 38 | } 39 | 40 | pub fn end(&self) -> i64 { 41 | unsafe { (*self.as_ptr()).end } 42 | } 43 | 44 | pub fn metadata(&self) -> DictionaryRef { 45 | unsafe { DictionaryRef::wrap((*self.as_ptr()).metadata) } 46 | } 47 | } 48 | 49 | impl<'a> PartialEq for Chapter<'a> { 50 | fn eq(&self, other: &Self) -> bool { 51 | unsafe { self.as_ptr() == other.as_ptr() } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/format/chapter/chapter_mut.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ops::Deref; 3 | 4 | use super::Chapter; 5 | use crate::ffi::*; 6 | use crate::format::context::common::Context; 7 | use crate::{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 | -------------------------------------------------------------------------------- /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/format/context/destructor.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | #[cfg(feature = "serialize")] 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Copy, Clone, Debug)] 6 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 7 | pub enum Mode { 8 | Input, 9 | Output, 10 | } 11 | 12 | pub struct Destructor { 13 | ptr: *mut AVFormatContext, 14 | mode: Mode, 15 | } 16 | 17 | impl Destructor { 18 | pub unsafe fn new(ptr: *mut AVFormatContext, mode: Mode) -> Self { 19 | Destructor { ptr, mode } 20 | } 21 | } 22 | 23 | impl Drop for Destructor { 24 | fn drop(&mut self) { 25 | unsafe { 26 | match self.mode { 27 | Mode::Input => avformat_close_input(&mut self.ptr), 28 | 29 | Mode::Output => { 30 | avio_close((*self.ptr).pb); 31 | avformat_free_context(self.ptr); 32 | } 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /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/format/flag.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags::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 | const GLOBAL_HEADER = AVFMT_GLOBALHEADER; 10 | const NO_TIMESTAMPS = AVFMT_NOTIMESTAMPS; 11 | const GENERIC_INDEX = AVFMT_GENERIC_INDEX; 12 | const TS_DISCONT = AVFMT_TS_DISCONT; 13 | const VARIABLE_FPS = AVFMT_VARIABLE_FPS; 14 | const NO_DIMENSIONS = AVFMT_NODIMENSIONS; 15 | const NO_STREAMS = AVFMT_NOSTREAMS; 16 | const NO_BINSEARCH = AVFMT_NOBINSEARCH; 17 | const NO_GENSEARCH = AVFMT_NOGENSEARCH; 18 | const NO_BYTE_SEEK = AVFMT_NO_BYTE_SEEK; 19 | const ALLOW_FLUSH = AVFMT_ALLOW_FLUSH; 20 | const TS_NONSTRICT = AVFMT_TS_NONSTRICT; 21 | const TS_NEGATIVE = AVFMT_TS_NEGATIVE; 22 | const SEEK_TO_PTS = AVFMT_SEEK_TO_PTS; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/format/format/input.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::NonNull; 2 | 3 | use crate::ffi::*; 4 | use crate::utils; 5 | 6 | use super::Flags; 7 | 8 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 9 | pub struct Input { 10 | ptr: NonNull, 11 | } 12 | 13 | impl Input { 14 | pub unsafe fn from_raw(ptr: *const AVInputFormat) -> Option { 15 | NonNull::new(ptr as *mut _).map(|ptr| Self { ptr }) 16 | } 17 | 18 | pub fn as_ptr(self) -> *const AVInputFormat { 19 | self.ptr.as_ptr() 20 | } 21 | 22 | pub fn name(self) -> &'static str { 23 | unsafe { utils::str_from_c_ptr((*self.as_ptr()).name) } 24 | } 25 | 26 | pub fn description(self) -> &'static str { 27 | unsafe { utils::optional_str_from_c_ptr((*self.as_ptr()).long_name).unwrap_or("") } 28 | } 29 | 30 | pub fn flags(self) -> Flags { 31 | unsafe { Flags::from_bits_truncate((*self.as_ptr()).flags) } 32 | } 33 | 34 | pub fn extensions(self) -> Vec<&'static str> { 35 | unsafe { 36 | let ptr = (*self.as_ptr()).extensions; 37 | 38 | if ptr.is_null() { 39 | Vec::new() 40 | } else { 41 | utils::str_from_c_ptr(ptr).split(',').collect() 42 | } 43 | } 44 | } 45 | 46 | pub fn mime_types(self) -> Vec<&'static str> { 47 | unsafe { 48 | let ptr = (*self.as_ptr()).mime_type; 49 | 50 | if ptr.is_null() { 51 | Vec::new() 52 | } else { 53 | utils::str_from_c_ptr(ptr).split(',').collect() 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/format/format/iter.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::null_mut; 2 | 3 | use crate::ffi::*; 4 | use crate::format::format::{Input, Output}; 5 | use libc::c_void; 6 | 7 | pub struct DemuxerIter { 8 | ptr: *mut c_void, 9 | } 10 | 11 | impl DemuxerIter { 12 | pub fn new() -> Self { 13 | Self { ptr: null_mut() } 14 | } 15 | } 16 | 17 | impl Default for DemuxerIter { 18 | fn default() -> Self { 19 | Self::new() 20 | } 21 | } 22 | 23 | impl Iterator for DemuxerIter { 24 | type Item = Input; 25 | 26 | fn next(&mut self) -> Option { 27 | unsafe { 28 | let next = av_demuxer_iterate(&mut self.ptr); 29 | Input::from_raw(next) 30 | } 31 | } 32 | } 33 | 34 | pub struct MuxerIter { 35 | ptr: *mut c_void, 36 | } 37 | 38 | impl MuxerIter { 39 | pub fn new() -> Self { 40 | Self { ptr: null_mut() } 41 | } 42 | } 43 | 44 | impl Default for MuxerIter { 45 | fn default() -> Self { 46 | Self::new() 47 | } 48 | } 49 | 50 | impl Iterator for MuxerIter { 51 | type Item = Output; 52 | 53 | fn next(&mut self) -> Option { 54 | unsafe { 55 | let next = av_muxer_iterate(&mut self.ptr); 56 | Output::from_raw(next) 57 | } 58 | } 59 | } 60 | 61 | #[cfg(test)] 62 | mod test { 63 | use super::*; 64 | 65 | #[test] 66 | fn muxer_iter() { 67 | for f in MuxerIter::new() { 68 | println!("{}:", f.name()); 69 | println!("\t{}", f.description()); 70 | println!("\t{:?}", f.extensions()); 71 | println!("\t{:?}", f.mime_types()); 72 | } 73 | } 74 | 75 | #[test] 76 | fn demuxer_iter() { 77 | for f in DemuxerIter::new() { 78 | println!("{}:", f.name()); 79 | println!("\t{}", f.description()); 80 | println!("\t{:?}", f.extensions()); 81 | println!("\t{:?}", f.mime_types()); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/format/format/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod flag; 2 | pub use self::flag::Flags; 3 | 4 | mod input; 5 | pub use self::input::Input; 6 | 7 | mod output; 8 | pub use self::output::Output; 9 | 10 | mod iter; 11 | pub use self::iter::{DemuxerIter, MuxerIter}; 12 | 13 | pub fn list_demuxers() -> DemuxerIter { 14 | DemuxerIter::new() 15 | } 16 | 17 | pub fn list_muxers() -> MuxerIter { 18 | MuxerIter::new() 19 | } 20 | -------------------------------------------------------------------------------- /src/format/format/output.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use std::ffi::CString; 4 | use std::ptr::{self, NonNull}; 5 | 6 | use super::Flags; 7 | use crate::ffi::*; 8 | use crate::{codec, media, utils}; 9 | 10 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 11 | pub struct Output { 12 | ptr: NonNull, 13 | } 14 | 15 | impl Output { 16 | pub unsafe fn from_raw(ptr: *const AVOutputFormat) -> Option { 17 | NonNull::new(ptr as *mut _).map(|ptr| Self { ptr }) 18 | } 19 | 20 | pub fn as_ptr(self) -> *const AVOutputFormat { 21 | self.ptr.as_ptr() 22 | } 23 | 24 | pub fn name(self) -> &'static str { 25 | unsafe { utils::str_from_c_ptr((*self.as_ptr()).name) } 26 | } 27 | 28 | pub fn description(self) -> &'static str { 29 | unsafe { utils::optional_str_from_c_ptr((*self.as_ptr()).long_name).unwrap_or("") } 30 | } 31 | 32 | pub fn flags(self) -> Flags { 33 | unsafe { Flags::from_bits_truncate((*self.as_ptr()).flags) } 34 | } 35 | 36 | pub fn extensions(self) -> Vec<&'static str> { 37 | unsafe { 38 | let ptr = (*self.as_ptr()).extensions; 39 | 40 | if ptr.is_null() { 41 | Vec::new() 42 | } else { 43 | utils::str_from_c_ptr(ptr).split(',').collect() 44 | } 45 | } 46 | } 47 | 48 | pub fn mime_types(self) -> Vec<&'static str> { 49 | unsafe { 50 | let ptr = (*self.as_ptr()).mime_type; 51 | 52 | if ptr.is_null() { 53 | Vec::new() 54 | } else { 55 | utils::str_from_c_ptr(ptr).split(',').collect() 56 | } 57 | } 58 | } 59 | 60 | pub fn codec>(self, path: P, kind: media::Type) -> codec::Id { 61 | // XXX: use to_cstring when stable 62 | let path = CString::new(path.as_ref().to_str().unwrap()).unwrap(); 63 | 64 | unsafe { 65 | codec::Id::from(av_guess_codec( 66 | self.as_ptr() as *mut _, 67 | ptr::null(), 68 | path.as_ptr(), 69 | ptr::null(), 70 | kind.into(), 71 | )) 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/format/network.rs: -------------------------------------------------------------------------------- 1 | use crate::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/format/stream/disposition.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags::bitflags! { 5 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 6 | pub struct Disposition: c_int { 7 | const DEFAULT = AV_DISPOSITION_DEFAULT; 8 | const DUB = AV_DISPOSITION_DUB; 9 | const ORIGINAL = AV_DISPOSITION_ORIGINAL; 10 | const COMMENT = AV_DISPOSITION_COMMENT; 11 | const LYRICS = AV_DISPOSITION_LYRICS; 12 | const KARAOKE = AV_DISPOSITION_KARAOKE; 13 | const FORCED = AV_DISPOSITION_FORCED; 14 | const HEARING_IMPAIRED = AV_DISPOSITION_HEARING_IMPAIRED; 15 | const VISUAL_IMPAIRED = AV_DISPOSITION_VISUAL_IMPAIRED; 16 | const CLEAN_EFFECTS = AV_DISPOSITION_CLEAN_EFFECTS; 17 | const ATTACHED_PIC = AV_DISPOSITION_ATTACHED_PIC; 18 | const CAPTIONS = AV_DISPOSITION_CAPTIONS; 19 | const DESCRIPTIONS = AV_DISPOSITION_DESCRIPTIONS; 20 | const METADATA = AV_DISPOSITION_METADATA; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /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/format/stream/stream.rs: -------------------------------------------------------------------------------- 1 | use super::Disposition; 2 | use crate::codec::{self, packet}; 3 | use crate::ffi::*; 4 | use crate::format::context::common::Context; 5 | use crate::{DictionaryRef, Discard, Rational}; 6 | use libc::c_int; 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::ParametersRef { 35 | unsafe { 36 | codec::ParametersRef::from_raw((*self.as_ptr()).codecpar).expect("codecpar is non-null") 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/format/stream/stream_mut.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ops::Deref; 3 | 4 | use super::Stream; 5 | use crate::ffi::*; 6 | use crate::format::context::common::Context; 7 | use crate::AsPtr; 8 | use crate::{codec, Dictionary, Rational}; 9 | 10 | pub struct StreamMut<'a> { 11 | context: &'a mut Context, 12 | index: usize, 13 | 14 | immutable: Stream<'a>, 15 | } 16 | 17 | impl<'a> StreamMut<'a> { 18 | pub unsafe fn wrap(context: &mut Context, index: usize) -> StreamMut { 19 | StreamMut { 20 | context: mem::transmute_copy(&context), 21 | index, 22 | 23 | immutable: Stream::wrap(mem::transmute_copy(&context), index), 24 | } 25 | } 26 | 27 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVStream { 28 | *(*self.context.as_mut_ptr()).streams.add(self.index) 29 | } 30 | } 31 | 32 | impl<'a> StreamMut<'a> { 33 | pub fn set_time_base>(&mut self, value: R) { 34 | unsafe { 35 | (*self.as_mut_ptr()).time_base = value.into().into(); 36 | } 37 | } 38 | 39 | pub fn set_rate>(&mut self, value: R) { 40 | unsafe { 41 | (*self.as_mut_ptr()).r_frame_rate = value.into().into(); 42 | } 43 | } 44 | 45 | pub fn set_avg_frame_rate>(&mut self, value: R) { 46 | unsafe { 47 | (*self.as_mut_ptr()).avg_frame_rate = value.into().into(); 48 | } 49 | } 50 | 51 | pub fn parameters_mut(&mut self) -> codec::ParametersMut { 52 | unsafe { 53 | codec::ParametersMut::from_raw((*self.as_mut_ptr()).codecpar) 54 | .expect("codecpar is non-null") 55 | } 56 | } 57 | 58 | pub fn set_parameters>(&mut self, parameters: P) { 59 | unsafe { 60 | avcodec_parameters_copy((*self.as_mut_ptr()).codecpar, parameters.as_ptr()); 61 | } 62 | } 63 | 64 | pub fn copy_parameters_from_context(&mut self, ctx: &codec::Context) { 65 | unsafe { 66 | avcodec_parameters_from_context((*self.as_mut_ptr()).codecpar, ctx.as_ptr()); 67 | } 68 | } 69 | 70 | pub fn set_metadata(&mut self, metadata: Dictionary) { 71 | unsafe { 72 | let metadata = metadata.disown(); 73 | (*self.as_mut_ptr()).metadata = metadata; 74 | } 75 | } 76 | } 77 | 78 | impl<'a> Deref for StreamMut<'a> { 79 | type Target = Stream<'a>; 80 | 81 | fn deref(&self) -> &Self::Target { 82 | &self.immutable 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | #![allow(clippy::module_inception)] 3 | // FFI Types may differ across platforms, making casts necessary 4 | #![allow(clippy::unnecessary_cast)] 5 | // This lint sometimes suggests worse code. See rust-lang/rust-clippy#13514 6 | #![allow(clippy::needless_lifetimes)] 7 | // TODO: Add safety docs and remove this #[allow] 8 | #![allow(clippy::missing_safety_doc)] 9 | 10 | pub use ffmpeg_sys_the_third as sys; 11 | pub use ffmpeg_sys_the_third as ffi; 12 | 13 | pub mod util; 14 | #[cfg(feature = "ffmpeg_5_1")] 15 | pub use crate::util::channel_layout::{ 16 | Channel, ChannelCustom, ChannelLayout, ChannelLayoutIter, ChannelOrder, 17 | }; 18 | pub use crate::util::{ 19 | channel_layout::{self, ChannelLayoutMask}, 20 | chroma, color, dictionary, 21 | dictionary::Mut as DictionaryMut, 22 | dictionary::Owned as Dictionary, 23 | dictionary::Ref as DictionaryRef, 24 | error::{self, Error}, 25 | frame::{self, Frame}, 26 | log, 27 | mathematics::{self, rescale, Rescale, Rounding}, 28 | media, option, picture, 29 | rational::{self, Rational}, 30 | time, 31 | }; 32 | 33 | #[cfg(feature = "format")] 34 | pub mod format; 35 | #[cfg(feature = "format")] 36 | pub use crate::format::{ 37 | chapter::{Chapter, ChapterMut}, 38 | stream::{Stream, StreamMut}, 39 | }; 40 | 41 | #[cfg(feature = "codec")] 42 | pub mod codec; 43 | #[cfg(all(feature = "codec", not(feature = "ffmpeg_5_0")))] 44 | pub use crate::codec::picture::Picture; 45 | #[cfg(feature = "codec")] 46 | pub use crate::codec::{ 47 | audio_service::AudioService, 48 | codec::Codec, 49 | decoder, 50 | discard::Discard, 51 | encoder, 52 | field_order::FieldOrder, 53 | packet::{self, Packet}, 54 | subtitle::{self, Subtitle}, 55 | threading, 56 | }; 57 | 58 | #[cfg(feature = "device")] 59 | pub mod device; 60 | 61 | #[cfg(feature = "filter")] 62 | pub mod filter; 63 | #[cfg(feature = "filter")] 64 | pub use crate::filter::Filter; 65 | 66 | pub mod software; 67 | 68 | mod as_ptr; 69 | pub use as_ptr::{AsMutPtr, AsPtr}; 70 | 71 | pub(crate) mod macros; 72 | pub(crate) mod utils; 73 | 74 | fn init_error() { 75 | util::error::register_all(); 76 | } 77 | 78 | #[cfg(not(feature = "format"))] 79 | fn init_format() {} 80 | 81 | #[cfg(feature = "device")] 82 | fn init_device() { 83 | device::register_all(); 84 | } 85 | 86 | #[cfg(not(feature = "device"))] 87 | fn init_device() {} 88 | 89 | #[cfg(all(feature = "filter", not(feature = "ffmpeg_5_0")))] 90 | fn init_filter() { 91 | filter::register_all(); 92 | } 93 | 94 | #[cfg(not(feature = "filter"))] 95 | fn init_filter() {} 96 | 97 | pub fn init() -> Result<(), Error> { 98 | init_error(); 99 | init_device(); 100 | #[cfg(not(feature = "ffmpeg_5_0"))] 101 | init_filter(); 102 | 103 | Ok(()) 104 | } 105 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | macro_rules! impl_for_one { 2 | // ref/mut with lifetime 3 | ($wrapper:ident, $lt:lifetime, $func:item) => { 4 | impl<$lt> $wrapper<$lt> { 5 | $func 6 | } 7 | }; 8 | // owned without lifetime 9 | ($wrapper:ident, $func:item) => { 10 | impl $wrapper { 11 | $func 12 | } 13 | }; 14 | } 15 | 16 | macro_rules! impl_for_many { 17 | { impl for $($wrapper:ident$(<$lt:lifetime>)?),+ {} } => {}; 18 | { 19 | impl for $($wrapper:ident$(<$lt:lifetime>)?),+ { 20 | $func:item 21 | $($tt:tt)* 22 | } 23 | } => { 24 | $( 25 | $crate::macros::impl_for_one!($wrapper$(, $lt)?, $func); 26 | )+ 27 | 28 | impl_for_many!{ 29 | impl for $($wrapper$(<$lt>)?),+ { 30 | $($tt)* 31 | } 32 | } 33 | }; 34 | } 35 | 36 | pub(crate) use impl_for_many; 37 | pub(crate) use impl_for_one; 38 | -------------------------------------------------------------------------------- /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: crate::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: crate::format::Pixel, 22 | output: crate::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(all(feature = "software-resampling", not(feature = "ffmpeg_7_0")))] 39 | #[inline] 40 | pub fn resampler( 41 | (in_format, in_layout, in_rate): (crate::format::Sample, crate::ChannelLayoutMask, u32), 42 | (out_format, out_layout, out_rate): (crate::format::Sample, crate::ChannelLayoutMask, u32), 43 | ) -> Result { 44 | resampling::Context::get( 45 | in_format, in_layout, in_rate, out_format, out_layout, out_rate, 46 | ) 47 | } 48 | 49 | #[cfg(all(feature = "software-resampling", feature = "ffmpeg_5_1"))] 50 | #[inline] 51 | pub fn resampler2( 52 | (in_format, in_layout, in_rate): (crate::format::Sample, crate::ChannelLayout, u32), 53 | (out_format, out_layout, out_rate): (crate::format::Sample, crate::ChannelLayout, u32), 54 | ) -> Result { 55 | resampling::Context::get2( 56 | in_format, in_layout, in_rate, out_format, out_layout, out_rate, 57 | ) 58 | } 59 | -------------------------------------------------------------------------------- /src/software/resampling/delay.rs: -------------------------------------------------------------------------------- 1 | use super::Context; 2 | use crate::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/software/resampling/dither.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::SwrDitherType::*; 2 | use crate::ffi::*; 3 | #[cfg(feature = "serialize")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] 7 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 8 | pub enum Dither { 9 | None, 10 | Rectangular, 11 | Triangular, 12 | TriangularHighPass, 13 | 14 | NoiseShapingLipshitz, 15 | NoiseShapingFWeighted, 16 | NoiseShapingModifiedEWeighted, 17 | NoiseShapingImprovedEWeighted, 18 | NoiseShapingShibata, 19 | NoiseShapingLowShibata, 20 | NoiseShapingHighShibata, 21 | } 22 | 23 | impl From for Dither { 24 | fn from(value: SwrDitherType) -> Dither { 25 | match value { 26 | SWR_DITHER_NONE => Dither::None, 27 | SWR_DITHER_RECTANGULAR => Dither::Rectangular, 28 | SWR_DITHER_TRIANGULAR => Dither::Triangular, 29 | SWR_DITHER_TRIANGULAR_HIGHPASS => Dither::TriangularHighPass, 30 | 31 | SWR_DITHER_NS => Dither::None, 32 | SWR_DITHER_NS_LIPSHITZ => Dither::NoiseShapingLipshitz, 33 | SWR_DITHER_NS_F_WEIGHTED => Dither::NoiseShapingFWeighted, 34 | SWR_DITHER_NS_MODIFIED_E_WEIGHTED => Dither::NoiseShapingModifiedEWeighted, 35 | SWR_DITHER_NS_IMPROVED_E_WEIGHTED => Dither::NoiseShapingImprovedEWeighted, 36 | SWR_DITHER_NS_SHIBATA => Dither::NoiseShapingShibata, 37 | SWR_DITHER_NS_LOW_SHIBATA => Dither::NoiseShapingLowShibata, 38 | SWR_DITHER_NS_HIGH_SHIBATA => Dither::NoiseShapingHighShibata, 39 | SWR_DITHER_NB => Dither::None, 40 | 41 | #[cfg(feature = "non-exhaustive-enums")] 42 | _ => unimplemented!(), 43 | } 44 | } 45 | } 46 | 47 | impl From for SwrDitherType { 48 | fn from(value: Dither) -> SwrDitherType { 49 | match value { 50 | Dither::None => SWR_DITHER_NONE, 51 | Dither::Rectangular => SWR_DITHER_RECTANGULAR, 52 | Dither::Triangular => SWR_DITHER_TRIANGULAR, 53 | Dither::TriangularHighPass => SWR_DITHER_TRIANGULAR_HIGHPASS, 54 | 55 | Dither::NoiseShapingLipshitz => SWR_DITHER_NS_LIPSHITZ, 56 | Dither::NoiseShapingFWeighted => SWR_DITHER_NS_F_WEIGHTED, 57 | Dither::NoiseShapingModifiedEWeighted => SWR_DITHER_NS_MODIFIED_E_WEIGHTED, 58 | Dither::NoiseShapingImprovedEWeighted => SWR_DITHER_NS_IMPROVED_E_WEIGHTED, 59 | Dither::NoiseShapingShibata => SWR_DITHER_NS_SHIBATA, 60 | Dither::NoiseShapingLowShibata => SWR_DITHER_NS_LOW_SHIBATA, 61 | Dither::NoiseShapingHighShibata => SWR_DITHER_NS_HIGH_SHIBATA, 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/software/resampling/engine.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use crate::sys::SwrEngine::*; 3 | #[cfg(feature = "serialize")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] 7 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 8 | pub enum Engine { 9 | Software, 10 | SoundExchange, 11 | } 12 | 13 | impl From for Engine { 14 | fn from(value: SwrEngine) -> Engine { 15 | match value { 16 | SWR_ENGINE_SWR => Engine::Software, 17 | SWR_ENGINE_SOXR => Engine::SoundExchange, 18 | SWR_ENGINE_NB => Engine::Software, 19 | 20 | #[cfg(feature = "non-exhaustive-enums")] 21 | _ => unimplemented!(), 22 | } 23 | } 24 | } 25 | 26 | impl From for SwrEngine { 27 | fn from(value: Engine) -> SwrEngine { 28 | match value { 29 | Engine::Software => SWR_ENGINE_SWR, 30 | Engine::SoundExchange => SWR_ENGINE_SOXR, 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/software/resampling/extensions.rs: -------------------------------------------------------------------------------- 1 | use super::Context; 2 | use crate::util::format; 3 | use crate::{decoder, frame, Error}; 4 | 5 | #[cfg(feature = "ffmpeg_5_1")] 6 | use crate::ChannelLayout; 7 | 8 | #[cfg(not(feature = "ffmpeg_7_0"))] 9 | use crate::ChannelLayoutMask; 10 | 11 | impl frame::Audio { 12 | #[cfg(not(feature = "ffmpeg_7_0"))] 13 | #[inline] 14 | pub fn resampler( 15 | &self, 16 | format: format::Sample, 17 | channel_layout: ChannelLayoutMask, 18 | rate: u32, 19 | ) -> Result { 20 | Context::get( 21 | self.format(), 22 | self.channel_layout(), 23 | unsafe { (*self.as_ptr()).sample_rate as u32 }, 24 | format, 25 | channel_layout, 26 | rate, 27 | ) 28 | } 29 | 30 | #[cfg(feature = "ffmpeg_5_1")] 31 | #[inline] 32 | pub fn resampler2( 33 | &self, 34 | format: format::Sample, 35 | ch_layout: ChannelLayout, 36 | rate: u32, 37 | ) -> Result { 38 | Context::get2( 39 | self.format(), 40 | self.ch_layout(), 41 | unsafe { (*self.as_ptr()).sample_rate as u32 }, 42 | format, 43 | ch_layout, 44 | rate, 45 | ) 46 | } 47 | } 48 | 49 | impl decoder::Audio { 50 | #[cfg(not(feature = "ffmpeg_7_0"))] 51 | #[inline] 52 | pub fn resampler( 53 | &self, 54 | format: format::Sample, 55 | channel_layout: ChannelLayoutMask, 56 | rate: u32, 57 | ) -> Result { 58 | Context::get( 59 | self.format(), 60 | self.channel_layout(), 61 | self.rate(), 62 | format, 63 | channel_layout, 64 | rate, 65 | ) 66 | } 67 | 68 | #[cfg(feature = "ffmpeg_5_1")] 69 | #[inline] 70 | pub fn resampler2( 71 | &self, 72 | format: format::Sample, 73 | ch_layout: ChannelLayout, 74 | rate: u32, 75 | ) -> Result { 76 | Context::get2( 77 | self.format(), 78 | self.ch_layout(), 79 | self.rate(), 80 | format, 81 | ch_layout, 82 | rate, 83 | ) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/software/resampling/filter.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::SwrFilterType::*; 2 | use crate::ffi::*; 3 | #[cfg(feature = "serialize")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] 7 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 8 | pub enum Filter { 9 | Cubic, 10 | BlackmanNuttall, 11 | Kaiser, 12 | } 13 | 14 | impl From for Filter { 15 | fn from(value: SwrFilterType) -> Filter { 16 | match value { 17 | SWR_FILTER_TYPE_CUBIC => Filter::Cubic, 18 | SWR_FILTER_TYPE_BLACKMAN_NUTTALL => Filter::BlackmanNuttall, 19 | SWR_FILTER_TYPE_KAISER => Filter::Kaiser, 20 | 21 | #[cfg(feature = "non-exhaustive-enums")] 22 | _ => unimplemented!(), 23 | } 24 | } 25 | } 26 | 27 | impl From for SwrFilterType { 28 | fn from(value: Filter) -> SwrFilterType { 29 | match value { 30 | Filter::Cubic => SWR_FILTER_TYPE_CUBIC, 31 | Filter::BlackmanNuttall => SWR_FILTER_TYPE_BLACKMAN_NUTTALL, 32 | Filter::Kaiser => SWR_FILTER_TYPE_KAISER, 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/software/resampling/flag.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags::bitflags! { 5 | pub struct Flags: c_int { 6 | const FORCE = SWR_FLAG_RESAMPLE; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /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 crate::ffi::*; 22 | use crate::utils; 23 | 24 | pub fn version() -> u32 { 25 | unsafe { swresample_version() } 26 | } 27 | 28 | pub fn configuration() -> &'static str { 29 | unsafe { utils::str_from_c_ptr(swresample_configuration()) } 30 | } 31 | 32 | pub fn license() -> &'static str { 33 | unsafe { utils::str_from_c_ptr(swresample_license()) } 34 | } 35 | -------------------------------------------------------------------------------- /src/software/scaling/color_space.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | #[cfg(feature = "serialize")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 7 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 8 | pub enum ColorSpace { 9 | Default, 10 | 11 | ITU709, 12 | FCC, 13 | ITU601, 14 | ITU624, 15 | SMPTE170M, 16 | SMPTE240M, 17 | } 18 | 19 | impl From for ColorSpace { 20 | fn from(value: c_int) -> ColorSpace { 21 | match value { 22 | SWS_CS_ITU709 => ColorSpace::ITU709, 23 | SWS_CS_FCC => ColorSpace::FCC, 24 | SWS_CS_DEFAULT => ColorSpace::Default, 25 | SWS_CS_SMPTE240M => ColorSpace::SMPTE240M, 26 | 27 | _ => ColorSpace::Default, 28 | } 29 | } 30 | } 31 | 32 | impl From for c_int { 33 | fn from(value: ColorSpace) -> c_int { 34 | match value { 35 | ColorSpace::Default => SWS_CS_DEFAULT, 36 | ColorSpace::ITU709 => SWS_CS_ITU709, 37 | ColorSpace::FCC => SWS_CS_FCC, 38 | ColorSpace::ITU601 => SWS_CS_ITU601, 39 | ColorSpace::ITU624 => SWS_CS_ITU624, 40 | ColorSpace::SMPTE170M => SWS_CS_SMPTE170M, 41 | ColorSpace::SMPTE240M => SWS_CS_SMPTE240M, 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/software/scaling/extensions.rs: -------------------------------------------------------------------------------- 1 | use super::{Context, Flags}; 2 | use crate::util::format; 3 | #[cfg(not(feature = "ffmpeg_5_0"))] 4 | use crate::Picture; 5 | use crate::{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/software/scaling/filter.rs: -------------------------------------------------------------------------------- 1 | use super::Vector; 2 | use crate::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/software/scaling/flag.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags::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/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 crate::ffi::*; 21 | use crate::utils; 22 | 23 | pub fn version() -> u32 { 24 | unsafe { swscale_version() } 25 | } 26 | 27 | pub fn configuration() -> &'static str { 28 | unsafe { utils::str_from_c_ptr(swscale_configuration()) } 29 | } 30 | 31 | pub fn license() -> &'static str { 32 | unsafe { utils::str_from_c_ptr(swscale_license()) } 33 | } 34 | -------------------------------------------------------------------------------- /src/software/scaling/support.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use crate::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 | -------------------------------------------------------------------------------- /src/software/scaling/vector.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::slice; 3 | 4 | use crate::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/channel_layout/channel_custom.rs: -------------------------------------------------------------------------------- 1 | use libc::c_char; 2 | 3 | use crate::ffi::{AVChannel, AVChannelCustom}; 4 | 5 | use super::Channel; 6 | 7 | /// Wrapper around [`AVChannelCustom`][crate::ffi::AVChannelCustom]. 8 | /// 9 | /// This struct does not support reading or writing user data via the opaque pointer. 10 | #[derive(Debug, Clone, PartialEq, Eq)] 11 | #[repr(transparent)] 12 | pub struct ChannelCustom(AVChannelCustom); 13 | 14 | impl ChannelCustom { 15 | pub fn new(id: Channel) -> Self { 16 | Self(AVChannelCustom { 17 | id: AVChannel::from(id), 18 | name: [0; 16], 19 | opaque: std::ptr::null_mut(), 20 | }) 21 | } 22 | 23 | pub fn named>(id: Channel, name: S) -> Self { 24 | let name = name.as_ref(); 25 | let name = to_char_array(name.as_bytes()); 26 | 27 | Self(AVChannelCustom { 28 | id: AVChannel::from(id), 29 | name, 30 | opaque: std::ptr::null_mut(), 31 | }) 32 | } 33 | } 34 | 35 | fn to_char_array(bytes: &[u8]) -> [c_char; 16] { 36 | let mut result = [0; 16]; 37 | 38 | // Only take the first 15 bytes, leaving at least one NUL byte 39 | for (b, r) in bytes.iter().take(15).zip(&mut result) { 40 | *r = *b as c_char; 41 | } 42 | 43 | result 44 | } 45 | 46 | impl From for ChannelCustom { 47 | fn from(value: AVChannelCustom) -> Self { 48 | Self(value) 49 | } 50 | } 51 | 52 | impl From for AVChannelCustom { 53 | fn from(value: ChannelCustom) -> Self { 54 | value.0 55 | } 56 | } 57 | 58 | #[cfg(test)] 59 | mod test { 60 | use super::*; 61 | use std::alloc::Layout; 62 | 63 | #[test] 64 | fn is_repr_transparent() { 65 | // ChannelLayout::map relies on this being true. 66 | assert_eq!( 67 | Layout::new::(), 68 | Layout::new::() 69 | ); 70 | } 71 | 72 | #[test] 73 | fn new() { 74 | let custom = ChannelCustom::new(Channel::FrontRight); 75 | assert_eq!(custom.0.id, AVChannel::AV_CHAN_FRONT_RIGHT); 76 | assert_eq!(custom.0.name, [0; 16]); 77 | assert!(custom.0.opaque.is_null()); 78 | } 79 | 80 | #[test] 81 | fn named() { 82 | // "Bottom front ri\0" 83 | let c_str_name = [ 84 | 66, 111, 116, 116, 111, 109, 32, 102, 114, 111, 110, 116, 32, 114, 105, 0, 85 | ]; 86 | 87 | let custom = ChannelCustom::named(Channel::BottomFrontRight, "Bottom front right"); 88 | assert_eq!(custom.0.id, AVChannel::AV_CHAN_BOTTOM_FRONT_RIGHT); 89 | assert_eq!(custom.0.name, c_str_name); 90 | assert!(custom.0.opaque.is_null()); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/util/channel_layout/iter.rs: -------------------------------------------------------------------------------- 1 | use ffmpeg_sys_the_third::av_channel_layout_standard; 2 | use libc::c_void; 3 | 4 | use super::ChannelLayout; 5 | 6 | pub struct ChannelLayoutIter { 7 | opaque: *mut c_void, 8 | } 9 | 10 | impl ChannelLayoutIter { 11 | pub const fn new() -> Self { 12 | Self { 13 | opaque: std::ptr::null_mut(), 14 | } 15 | } 16 | } 17 | 18 | impl Default for ChannelLayoutIter { 19 | fn default() -> Self { 20 | Self::new() 21 | } 22 | } 23 | 24 | impl Iterator for ChannelLayoutIter { 25 | type Item = ChannelLayout<'static>; 26 | 27 | fn next(&mut self) -> Option { 28 | unsafe { 29 | av_channel_layout_standard(&mut self.opaque as _) 30 | .as_ref() 31 | .map(ChannelLayout::from) 32 | } 33 | } 34 | } 35 | 36 | #[cfg(test)] 37 | mod test { 38 | use super::*; 39 | 40 | #[test] 41 | fn iter() { 42 | let iter = ChannelLayoutIter::new(); 43 | let count = iter.count(); 44 | println!("{count}"); 45 | assert!(count > 0); 46 | 47 | let mut iter = ChannelLayoutIter::new(); 48 | assert!(iter.all(|ch| ch.is_valid())); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/util/channel_layout/mod.rs: -------------------------------------------------------------------------------- 1 | mod mask; 2 | pub use mask::*; 3 | 4 | #[cfg(feature = "ffmpeg_5_1")] 5 | mod channel_custom; 6 | #[cfg(feature = "ffmpeg_5_1")] 7 | pub use channel_custom::*; 8 | 9 | #[cfg(feature = "ffmpeg_5_1")] 10 | mod channel; 11 | #[cfg(feature = "ffmpeg_5_1")] 12 | pub use channel::*; 13 | 14 | #[cfg(feature = "ffmpeg_5_1")] 15 | mod iter; 16 | #[cfg(feature = "ffmpeg_5_1")] 17 | pub use iter::*; 18 | 19 | #[cfg(feature = "ffmpeg_5_1")] 20 | mod layout; 21 | #[cfg(feature = "ffmpeg_5_1")] 22 | pub use layout::*; 23 | 24 | #[cfg(feature = "ffmpeg_5_1")] 25 | mod order; 26 | #[cfg(feature = "ffmpeg_5_1")] 27 | pub use order::*; 28 | -------------------------------------------------------------------------------- /src/util/channel_layout/order.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::AVChannelOrder; 2 | 3 | use AVChannelOrder::*; 4 | use ChannelOrder::*; 5 | 6 | /// Specifies an order for audio channels. 7 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 8 | pub enum ChannelOrder { 9 | /// No channel order. Only the channel count is specified. 10 | Unspecified, 11 | 12 | /// Native channel order, i.e. the channels are in the same order in which they 13 | /// are defined in the [`Channel`][super::Channel] enum. This supports up to 63 channels. 14 | Native, 15 | 16 | /// The channel order does not correspond to any predefined order and is stored as an 17 | /// explicit map. This can be used to support layouts with more than 64 channels or with 18 | /// empty channels at arbitrary positions. 19 | Custom, 20 | 21 | /// The audio is represented as the decomposition of the sound field into spherical harmonics. 22 | Ambisonic, 23 | } 24 | 25 | impl From for ChannelOrder { 26 | fn from(value: AVChannelOrder) -> Self { 27 | match value { 28 | AV_CHANNEL_ORDER_UNSPEC => Unspecified, 29 | AV_CHANNEL_ORDER_NATIVE => Native, 30 | AV_CHANNEL_ORDER_CUSTOM => Custom, 31 | AV_CHANNEL_ORDER_AMBISONIC => Ambisonic, 32 | #[cfg(feature = "ffmpeg_7_0")] 33 | // Not part of the API, should never be used 34 | FF_CHANNEL_ORDER_NB => unreachable!(), 35 | #[cfg(feature = "non-exhaustive-enums")] 36 | _ => unimplemented!(), 37 | } 38 | } 39 | } 40 | 41 | impl From for AVChannelOrder { 42 | fn from(value: ChannelOrder) -> Self { 43 | match value { 44 | Unspecified => AV_CHANNEL_ORDER_UNSPEC, 45 | Native => AV_CHANNEL_ORDER_NATIVE, 46 | Custom => AV_CHANNEL_ORDER_CUSTOM, 47 | Ambisonic => AV_CHANNEL_ORDER_AMBISONIC, 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/util/chroma/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod location; 2 | pub use self::location::Location; 3 | -------------------------------------------------------------------------------- /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/util/color/primaries.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::AVColorPrimaries::*; 2 | use crate::ffi::*; 3 | use crate::utils; 4 | #[cfg(feature = "serialize")] 5 | use serde::{Deserialize, Serialize}; 6 | 7 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 8 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 9 | pub enum Primaries { 10 | Reserved0, 11 | BT709, 12 | Unspecified, 13 | Reserved, 14 | BT470M, 15 | 16 | BT470BG, 17 | SMPTE170M, 18 | SMPTE240M, 19 | Film, 20 | BT2020, 21 | 22 | SMPTE428, 23 | SMPTE431, 24 | SMPTE432, 25 | #[cfg(not(feature = "ffmpeg_4_3"))] 26 | JEDEC_P22, 27 | #[cfg(feature = "ffmpeg_4_3")] 28 | EBU3213, 29 | } 30 | 31 | impl Primaries { 32 | #[cfg(feature = "ffmpeg_4_3")] 33 | pub const JEDEC_P22: Primaries = Primaries::EBU3213; 34 | 35 | pub fn name(&self) -> Option<&'static str> { 36 | if *self == Primaries::Unspecified { 37 | return None; 38 | } 39 | unsafe { 40 | let ptr = av_color_primaries_name((*self).into()); 41 | utils::optional_str_from_c_ptr(ptr) 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 | #[cfg(feature = "non-exhaustive-enums")] 71 | _ => unimplemented!(), 72 | } 73 | } 74 | } 75 | 76 | impl From for AVColorPrimaries { 77 | fn from(value: Primaries) -> AVColorPrimaries { 78 | match value { 79 | Primaries::Reserved0 => AVCOL_PRI_RESERVED0, 80 | Primaries::BT709 => AVCOL_PRI_BT709, 81 | Primaries::Unspecified => AVCOL_PRI_UNSPECIFIED, 82 | Primaries::Reserved => AVCOL_PRI_RESERVED, 83 | Primaries::BT470M => AVCOL_PRI_BT470M, 84 | 85 | Primaries::BT470BG => AVCOL_PRI_BT470BG, 86 | Primaries::SMPTE170M => AVCOL_PRI_SMPTE170M, 87 | Primaries::SMPTE240M => AVCOL_PRI_SMPTE240M, 88 | Primaries::Film => AVCOL_PRI_FILM, 89 | Primaries::BT2020 => AVCOL_PRI_BT2020, 90 | 91 | Primaries::SMPTE428 => AVCOL_PRI_SMPTE428, 92 | Primaries::SMPTE431 => AVCOL_PRI_SMPTE431, 93 | Primaries::SMPTE432 => AVCOL_PRI_SMPTE432, 94 | #[cfg(not(feature = "ffmpeg_4_3"))] 95 | Primaries::JEDEC_P22 => AVCOL_PRI_JEDEC_P22, 96 | #[cfg(feature = "ffmpeg_4_3")] 97 | Primaries::EBU3213 => AVCOL_PRI_EBU3213, 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/util/color/range.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::AVColorRange::*; 2 | use crate::ffi::*; 3 | use crate::utils; 4 | #[cfg(feature = "serialize")] 5 | use serde::{Deserialize, Serialize}; 6 | 7 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 8 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 9 | pub enum Range { 10 | Unspecified, 11 | MPEG, 12 | JPEG, 13 | } 14 | 15 | impl Range { 16 | pub fn name(&self) -> Option<&'static str> { 17 | if *self == Range::Unspecified { 18 | return None; 19 | } 20 | unsafe { 21 | let ptr = av_color_range_name((*self).into()); 22 | utils::optional_str_from_c_ptr(ptr) 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 | #[cfg(feature = "non-exhaustive-enums")] 36 | _ => unimplemented!(), 37 | } 38 | } 39 | } 40 | 41 | impl From for AVColorRange { 42 | fn from(value: Range) -> AVColorRange { 43 | match value { 44 | Range::Unspecified => AVCOL_RANGE_UNSPECIFIED, 45 | Range::MPEG => AVCOL_RANGE_MPEG, 46 | Range::JPEG => AVCOL_RANGE_JPEG, 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/util/color/space.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::AVColorSpace::*; 2 | use crate::ffi::*; 3 | use crate::utils; 4 | #[cfg(feature = "serialize")] 5 | use serde::{Deserialize, Serialize}; 6 | 7 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 8 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 9 | pub enum Space { 10 | RGB, 11 | BT709, 12 | Unspecified, 13 | Reserved, 14 | FCC, 15 | BT470BG, 16 | SMPTE170M, 17 | SMPTE240M, 18 | YCGCO, 19 | BT2020NCL, 20 | BT2020CL, 21 | SMPTE2085, 22 | 23 | ChromaDerivedNCL, 24 | ChromaDerivedCL, 25 | ICTCP, 26 | 27 | #[cfg(feature = "ffmpeg_7_1")] 28 | IPTC2, 29 | #[cfg(feature = "ffmpeg_7_1")] 30 | YCGCORE, 31 | #[cfg(feature = "ffmpeg_7_1")] 32 | YCGCORO, 33 | } 34 | 35 | impl Space { 36 | pub const YCOCG: Space = Space::YCGCO; 37 | 38 | pub fn name(&self) -> Option<&'static str> { 39 | if *self == Space::Unspecified { 40 | return None; 41 | } 42 | unsafe { 43 | let ptr = av_color_space_name((*self).into()); 44 | utils::optional_str_from_c_ptr(ptr) 45 | } 46 | } 47 | } 48 | 49 | impl From for Space { 50 | fn from(value: AVColorSpace) -> Self { 51 | match value { 52 | AVCOL_SPC_RGB => Space::RGB, 53 | AVCOL_SPC_BT709 => Space::BT709, 54 | AVCOL_SPC_UNSPECIFIED => Space::Unspecified, 55 | AVCOL_SPC_RESERVED => Space::Reserved, 56 | AVCOL_SPC_FCC => Space::FCC, 57 | AVCOL_SPC_BT470BG => Space::BT470BG, 58 | AVCOL_SPC_SMPTE170M => Space::SMPTE170M, 59 | AVCOL_SPC_SMPTE240M => Space::SMPTE240M, 60 | AVCOL_SPC_YCGCO => Space::YCGCO, 61 | AVCOL_SPC_BT2020_NCL => Space::BT2020NCL, 62 | AVCOL_SPC_BT2020_CL => Space::BT2020CL, 63 | AVCOL_SPC_SMPTE2085 => Space::SMPTE2085, 64 | AVCOL_SPC_NB => Space::Unspecified, 65 | 66 | AVCOL_SPC_CHROMA_DERIVED_NCL => Space::ChromaDerivedNCL, 67 | AVCOL_SPC_CHROMA_DERIVED_CL => Space::ChromaDerivedCL, 68 | AVCOL_SPC_ICTCP => Space::ICTCP, 69 | 70 | #[cfg(feature = "ffmpeg_7_1")] 71 | AVCOL_SPC_IPT_C2 => Space::IPTC2, 72 | #[cfg(feature = "ffmpeg_7_1")] 73 | AVCOL_SPC_YCGCO_RE => Space::YCGCORE, 74 | #[cfg(feature = "ffmpeg_7_1")] 75 | AVCOL_SPC_YCGCO_RO => Space::YCGCORO, 76 | 77 | #[cfg(feature = "non-exhaustive-enums")] 78 | _ => unimplemented!(), 79 | } 80 | } 81 | } 82 | 83 | impl From for AVColorSpace { 84 | fn from(value: Space) -> AVColorSpace { 85 | match value { 86 | Space::RGB => AVCOL_SPC_RGB, 87 | Space::BT709 => AVCOL_SPC_BT709, 88 | Space::Unspecified => AVCOL_SPC_UNSPECIFIED, 89 | Space::Reserved => AVCOL_SPC_RESERVED, 90 | Space::FCC => AVCOL_SPC_FCC, 91 | Space::BT470BG => AVCOL_SPC_BT470BG, 92 | Space::SMPTE170M => AVCOL_SPC_SMPTE170M, 93 | Space::SMPTE240M => AVCOL_SPC_SMPTE240M, 94 | Space::YCGCO => AVCOL_SPC_YCGCO, 95 | Space::BT2020NCL => AVCOL_SPC_BT2020_NCL, 96 | Space::BT2020CL => AVCOL_SPC_BT2020_CL, 97 | Space::SMPTE2085 => AVCOL_SPC_SMPTE2085, 98 | 99 | Space::ChromaDerivedNCL => AVCOL_SPC_CHROMA_DERIVED_NCL, 100 | Space::ChromaDerivedCL => AVCOL_SPC_CHROMA_DERIVED_CL, 101 | Space::ICTCP => AVCOL_SPC_ICTCP, 102 | 103 | #[cfg(feature = "ffmpeg_7_1")] 104 | Space::IPTC2 => AVCOL_SPC_IPT_C2, 105 | #[cfg(feature = "ffmpeg_7_1")] 106 | Space::YCGCORE => AVCOL_SPC_YCGCO_RE, 107 | #[cfg(feature = "ffmpeg_7_1")] 108 | Space::YCGCORO => AVCOL_SPC_YCGCO_RO, 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/util/color/transfer_characteristic.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::AVColorTransferCharacteristic::*; 2 | use crate::ffi::*; 3 | use crate::utils; 4 | #[cfg(feature = "serialize")] 5 | use serde::{Deserialize, Serialize}; 6 | 7 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 8 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 9 | pub enum TransferCharacteristic { 10 | Reserved0, 11 | BT709, 12 | Unspecified, 13 | Reserved, 14 | GAMMA22, 15 | GAMMA28, 16 | SMPTE170M, 17 | SMPTE240M, 18 | Linear, 19 | Log, 20 | LogSqrt, 21 | IEC61966_2_4, 22 | BT1361_ECG, 23 | IEC61966_2_1, 24 | BT2020_10, 25 | BT2020_12, 26 | SMPTE2084, 27 | SMPTE428, 28 | ARIB_STD_B67, 29 | } 30 | 31 | impl TransferCharacteristic { 32 | pub fn name(&self) -> Option<&'static str> { 33 | if *self == TransferCharacteristic::Unspecified { 34 | return None; 35 | } 36 | unsafe { 37 | let ptr = av_color_transfer_name((*self).into()); 38 | utils::optional_str_from_c_ptr(ptr) 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 | #[cfg(feature = "non-exhaustive-enums")] 68 | _ => unimplemented!(), 69 | } 70 | } 71 | } 72 | 73 | impl From for AVColorTransferCharacteristic { 74 | fn from(value: TransferCharacteristic) -> AVColorTransferCharacteristic { 75 | match value { 76 | TransferCharacteristic::Reserved0 => AVCOL_TRC_RESERVED0, 77 | TransferCharacteristic::BT709 => AVCOL_TRC_BT709, 78 | TransferCharacteristic::Unspecified => AVCOL_TRC_UNSPECIFIED, 79 | TransferCharacteristic::Reserved => AVCOL_TRC_RESERVED, 80 | TransferCharacteristic::GAMMA22 => AVCOL_TRC_GAMMA22, 81 | TransferCharacteristic::GAMMA28 => AVCOL_TRC_GAMMA28, 82 | TransferCharacteristic::SMPTE170M => AVCOL_TRC_SMPTE170M, 83 | TransferCharacteristic::SMPTE240M => AVCOL_TRC_SMPTE240M, 84 | TransferCharacteristic::Linear => AVCOL_TRC_LINEAR, 85 | TransferCharacteristic::Log => AVCOL_TRC_LOG, 86 | TransferCharacteristic::LogSqrt => AVCOL_TRC_LOG_SQRT, 87 | TransferCharacteristic::IEC61966_2_4 => AVCOL_TRC_IEC61966_2_4, 88 | TransferCharacteristic::BT1361_ECG => AVCOL_TRC_BT1361_ECG, 89 | TransferCharacteristic::IEC61966_2_1 => AVCOL_TRC_IEC61966_2_1, 90 | TransferCharacteristic::BT2020_10 => AVCOL_TRC_BT2020_10, 91 | TransferCharacteristic::BT2020_12 => AVCOL_TRC_BT2020_12, 92 | TransferCharacteristic::SMPTE2084 => AVCOL_TRC_SMPTE2084, 93 | TransferCharacteristic::SMPTE428 => AVCOL_TRC_SMPTE428, 94 | TransferCharacteristic::ARIB_STD_B67 => AVCOL_TRC_ARIB_STD_B67, 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /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 crate::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/util/dictionary/iter.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CString; 2 | use std::marker::PhantomData; 3 | use std::ptr; 4 | 5 | use crate::ffi::*; 6 | use crate::utils; 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 = utils::str_from_c_ptr((*entry).key); 36 | let val = utils::str_from_c_ptr((*entry).value); 37 | 38 | self.cur = entry; 39 | 40 | Some((key, val)) 41 | } else { 42 | None 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /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::Dictionary::new(); 17 | 18 | $( 19 | dict.set($key, $value); 20 | )* 21 | 22 | dict 23 | } 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /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 crate::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/util/dictionary/owned.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::ops::{Deref, DerefMut}; 3 | use std::ptr; 4 | 5 | use super::mutable; 6 | use crate::ffi::*; 7 | 8 | pub struct Owned<'a> { 9 | inner: mutable::Ref<'a>, 10 | } 11 | 12 | impl<'a> Default for Owned<'a> { 13 | fn default() -> Self { 14 | Self::new() 15 | } 16 | } 17 | 18 | impl<'a> Owned<'a> { 19 | pub unsafe fn own(ptr: *mut AVDictionary) -> Self { 20 | Owned { 21 | inner: mutable::Ref::wrap(ptr), 22 | } 23 | } 24 | 25 | pub unsafe fn disown(mut self) -> *mut AVDictionary { 26 | let result = self.inner.as_mut_ptr(); 27 | self.inner = mutable::Ref::wrap(ptr::null_mut()); 28 | 29 | result 30 | } 31 | } 32 | 33 | impl<'a> Owned<'a> { 34 | pub fn new() -> Self { 35 | unsafe { 36 | Owned { 37 | inner: mutable::Ref::wrap(ptr::null_mut()), 38 | } 39 | } 40 | } 41 | } 42 | 43 | impl<'a, 'b> FromIterator<(&'b str, &'b str)> for Owned<'a> { 44 | fn from_iter>(iterator: T) -> Self { 45 | let mut result = Owned::new(); 46 | 47 | for (key, value) in iterator { 48 | result.set(key, value); 49 | } 50 | 51 | result 52 | } 53 | } 54 | 55 | impl<'a, 'b> FromIterator<&'b (&'b str, &'b str)> for Owned<'a> { 56 | fn from_iter>(iterator: T) -> Self { 57 | let mut result = Owned::new(); 58 | 59 | for &(key, value) in iterator { 60 | result.set(key, value); 61 | } 62 | 63 | result 64 | } 65 | } 66 | 67 | impl<'a> FromIterator<(String, String)> for Owned<'a> { 68 | fn from_iter>(iterator: T) -> Self { 69 | let mut result = Owned::new(); 70 | 71 | for (key, value) in iterator { 72 | result.set(&key, &value); 73 | } 74 | 75 | result 76 | } 77 | } 78 | 79 | impl<'a, 'b> FromIterator<&'b (String, String)> for Owned<'a> { 80 | fn from_iter>(iterator: T) -> Self { 81 | let mut result = Owned::new(); 82 | 83 | for (key, value) in iterator { 84 | result.set(key, value); 85 | } 86 | 87 | result 88 | } 89 | } 90 | 91 | impl<'a> Deref for Owned<'a> { 92 | type Target = mutable::Ref<'a>; 93 | 94 | fn deref(&self) -> &Self::Target { 95 | &self.inner 96 | } 97 | } 98 | 99 | impl<'a> DerefMut for Owned<'a> { 100 | fn deref_mut(&mut self) -> &mut Self::Target { 101 | &mut self.inner 102 | } 103 | } 104 | 105 | impl<'a> Clone for Owned<'a> { 106 | fn clone(&self) -> Self { 107 | let mut dictionary = Owned::new(); 108 | dictionary.clone_from(self); 109 | 110 | dictionary 111 | } 112 | 113 | fn clone_from(&mut self, source: &Self) { 114 | unsafe { 115 | let mut ptr = self.as_mut_ptr(); 116 | av_dict_copy(&mut ptr, source.as_ptr(), 0); 117 | self.inner = mutable::Ref::wrap(ptr); 118 | } 119 | } 120 | } 121 | 122 | impl<'a> Drop for Owned<'a> { 123 | fn drop(&mut self) { 124 | unsafe { 125 | av_dict_free(&mut self.inner.as_mut_ptr()); 126 | } 127 | } 128 | } 129 | 130 | impl<'a> fmt::Debug for Owned<'a> { 131 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 132 | self.inner.fmt(fmt) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /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/util/frame/flag.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags::bitflags! { 5 | pub struct Flags: c_int { 6 | const CORRUPT = AV_FRAME_FLAG_CORRUPT; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/util/interrupt.rs: -------------------------------------------------------------------------------- 1 | use std::panic; 2 | use std::process; 3 | 4 | use crate::ffi::*; 5 | use libc::{c_int, c_void}; 6 | 7 | pub struct Interrupt { 8 | pub interrupt: AVIOInterruptCB, 9 | } 10 | 11 | #[allow(clippy::needless_borrow)] 12 | extern "C" fn callback(opaque: *mut c_void) -> c_int 13 | where 14 | F: FnMut() -> bool, 15 | { 16 | match panic::catch_unwind(|| (unsafe { &mut *(opaque as *mut F) })()) { 17 | Ok(ret) => ret as c_int, 18 | Err(_) => process::abort(), 19 | } 20 | } 21 | 22 | pub fn new(opaque: Box) -> Interrupt 23 | where 24 | F: FnMut() -> bool, 25 | { 26 | let interrupt_cb = AVIOInterruptCB { 27 | callback: Some(callback::), 28 | opaque: Box::into_raw(opaque) as *mut c_void, 29 | }; 30 | Interrupt { 31 | interrupt: interrupt_cb, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/util/log/flag.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags::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/util/log/level.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use libc::c_int; 3 | #[cfg(feature = "serialize")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 7 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 8 | pub enum Level { 9 | Quiet, 10 | Panic, 11 | Fatal, 12 | Error, 13 | Warning, 14 | Info, 15 | Verbose, 16 | Debug, 17 | Trace, 18 | } 19 | 20 | pub struct LevelError; 21 | 22 | impl TryFrom for Level { 23 | type Error = &'static str; 24 | 25 | fn try_from(value: c_int) -> Result { 26 | match value { 27 | AV_LOG_QUIET => Ok(Level::Quiet), 28 | AV_LOG_PANIC => Ok(Level::Panic), 29 | AV_LOG_FATAL => Ok(Level::Fatal), 30 | AV_LOG_ERROR => Ok(Level::Error), 31 | AV_LOG_WARNING => Ok(Level::Warning), 32 | AV_LOG_INFO => Ok(Level::Info), 33 | AV_LOG_VERBOSE => Ok(Level::Verbose), 34 | AV_LOG_DEBUG => Ok(Level::Debug), 35 | AV_LOG_TRACE => Ok(Level::Trace), 36 | _ => Err("illegal log level"), 37 | } 38 | } 39 | } 40 | 41 | impl From for c_int { 42 | fn from(value: Level) -> c_int { 43 | match value { 44 | Level::Quiet => AV_LOG_QUIET, 45 | Level::Panic => AV_LOG_PANIC, 46 | Level::Fatal => AV_LOG_FATAL, 47 | Level::Error => AV_LOG_ERROR, 48 | Level::Warning => AV_LOG_WARNING, 49 | Level::Info => AV_LOG_INFO, 50 | Level::Verbose => AV_LOG_VERBOSE, 51 | Level::Debug => AV_LOG_DEBUG, 52 | Level::Trace => AV_LOG_TRACE, 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /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 crate::ffi::*; 8 | 9 | pub fn set_level(value: Level) { 10 | unsafe { av_log_set_level(value.into()) } 11 | } 12 | 13 | pub fn get_level() -> Result { 14 | unsafe { av_log_get_level().try_into() } 15 | } 16 | 17 | pub fn set_flags(value: Flags) { 18 | unsafe { av_log_set_flags(value.bits()) } 19 | } 20 | 21 | pub fn get_flags() -> Flags { 22 | unsafe { Flags::from_bits_truncate(av_log_get_flags()) } 23 | } 24 | -------------------------------------------------------------------------------- /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/util/mathematics/rescale.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use crate::{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/util/mathematics/rounding.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::AVRounding::*; 2 | use crate::ffi::*; 3 | #[cfg(feature = "serialize")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 7 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 8 | pub enum Rounding { 9 | Zero, 10 | Infinity, 11 | Down, 12 | Up, 13 | NearInfinity, 14 | PassMinMax, 15 | } 16 | 17 | impl From for Rounding { 18 | #[inline(always)] 19 | fn from(value: AVRounding) -> Self { 20 | match value { 21 | AV_ROUND_ZERO => Rounding::Zero, 22 | AV_ROUND_INF => Rounding::Infinity, 23 | AV_ROUND_DOWN => Rounding::Down, 24 | AV_ROUND_UP => Rounding::Up, 25 | AV_ROUND_NEAR_INF => Rounding::NearInfinity, 26 | AV_ROUND_PASS_MINMAX => Rounding::PassMinMax, 27 | 28 | #[cfg(feature = "non-exhaustive-enums")] 29 | _ => unimplemented!(), 30 | } 31 | } 32 | } 33 | 34 | impl From for AVRounding { 35 | #[inline(always)] 36 | fn from(value: Rounding) -> AVRounding { 37 | match value { 38 | Rounding::Zero => AV_ROUND_ZERO, 39 | Rounding::Infinity => AV_ROUND_INF, 40 | Rounding::Down => AV_ROUND_DOWN, 41 | Rounding::Up => AV_ROUND_UP, 42 | Rounding::NearInfinity => AV_ROUND_NEAR_INF, 43 | Rounding::PassMinMax => AV_ROUND_PASS_MINMAX, 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/util/media.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::AVMediaType::*; 2 | use crate::ffi::*; 3 | #[cfg(feature = "serialize")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 7 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 8 | pub enum Type { 9 | Unknown, 10 | Video, 11 | Audio, 12 | Data, 13 | Subtitle, 14 | Attachment, 15 | } 16 | 17 | impl From for Type { 18 | #[inline(always)] 19 | fn from(value: AVMediaType) -> Self { 20 | match value { 21 | AVMEDIA_TYPE_UNKNOWN => Type::Unknown, 22 | AVMEDIA_TYPE_VIDEO => Type::Video, 23 | AVMEDIA_TYPE_AUDIO => Type::Audio, 24 | AVMEDIA_TYPE_DATA => Type::Data, 25 | AVMEDIA_TYPE_SUBTITLE => Type::Subtitle, 26 | AVMEDIA_TYPE_ATTACHMENT => Type::Attachment, 27 | AVMEDIA_TYPE_NB => Type::Unknown, 28 | 29 | #[cfg(feature = "non-exhaustive-enums")] 30 | _ => unimplemented!(), 31 | } 32 | } 33 | } 34 | 35 | impl From for AVMediaType { 36 | #[inline(always)] 37 | fn from(value: Type) -> AVMediaType { 38 | match value { 39 | Type::Unknown => AVMEDIA_TYPE_UNKNOWN, 40 | Type::Video => AVMEDIA_TYPE_VIDEO, 41 | Type::Audio => AVMEDIA_TYPE_AUDIO, 42 | Type::Data => AVMEDIA_TYPE_DATA, 43 | Type::Subtitle => AVMEDIA_TYPE_SUBTITLE, 44 | Type::Attachment => AVMEDIA_TYPE_ATTACHMENT, 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod channel_layout; 2 | pub mod chroma; 3 | pub mod color; 4 | pub mod dictionary; 5 | pub mod error; 6 | pub mod format; 7 | pub mod frame; 8 | pub mod interrupt; 9 | pub mod log; 10 | pub mod mathematics; 11 | pub mod media; 12 | pub mod option; 13 | pub mod picture; 14 | pub mod rational; 15 | pub mod time; 16 | 17 | use crate::ffi::*; 18 | use crate::utils; 19 | 20 | #[inline(always)] 21 | pub fn version() -> u32 { 22 | unsafe { avutil_version() } 23 | } 24 | 25 | #[inline(always)] 26 | pub fn configuration() -> &'static str { 27 | unsafe { utils::str_from_c_ptr(avutil_configuration()) } 28 | } 29 | 30 | #[inline(always)] 31 | pub fn license() -> &'static str { 32 | unsafe { utils::str_from_c_ptr(avutil_license()) } 33 | } 34 | -------------------------------------------------------------------------------- /src/util/option/mod.rs: -------------------------------------------------------------------------------- 1 | mod traits; 2 | pub use self::traits::{Gettable, Iterable, Settable}; 3 | 4 | use crate::ffi::*; 5 | use libc::c_uint; 6 | #[cfg(feature = "serialize")] 7 | use serde::{Deserialize, Serialize}; 8 | 9 | bitflags::bitflags! { 10 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 11 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 12 | pub struct Type: c_uint { 13 | const FLAGS = AVOptionType::AV_OPT_TYPE_FLAGS.0 as c_uint; 14 | const INT = AVOptionType::AV_OPT_TYPE_INT.0 as c_uint; 15 | const INT64 = AVOptionType::AV_OPT_TYPE_INT64.0 as c_uint; 16 | const DOUBLE = AVOptionType::AV_OPT_TYPE_DOUBLE.0 as c_uint; 17 | const FLOAT = AVOptionType::AV_OPT_TYPE_FLOAT.0 as c_uint; 18 | const STRING = AVOptionType::AV_OPT_TYPE_STRING.0 as c_uint; 19 | const RATIONAL = AVOptionType::AV_OPT_TYPE_RATIONAL.0 as c_uint; 20 | /// `offset` must point to a pointer immediately followed by an int 21 | /// for the length. 22 | const BINARY = AVOptionType::AV_OPT_TYPE_BINARY.0 as c_uint; 23 | const DICTIONARY = AVOptionType::AV_OPT_TYPE_DICT.0 as c_uint; 24 | const CONSTANT = AVOptionType::AV_OPT_TYPE_CONST.0 as c_uint; 25 | /// `offset` must point to two consecutive ints 26 | const IMAGE_SIZE = AVOptionType::AV_OPT_TYPE_IMAGE_SIZE.0 as c_uint; 27 | const PIXEL_FORMAT = AVOptionType::AV_OPT_TYPE_PIXEL_FMT.0 as c_uint; 28 | const SAMPLE_FORMAT = AVOptionType::AV_OPT_TYPE_SAMPLE_FMT.0 as c_uint; 29 | /// `offset` must point to AVRational 30 | const VIDEO_RATE = AVOptionType::AV_OPT_TYPE_VIDEO_RATE.0 as c_uint; 31 | const DURATION = AVOptionType::AV_OPT_TYPE_DURATION.0 as c_uint; 32 | const COLOR = AVOptionType::AV_OPT_TYPE_COLOR.0 as c_uint; 33 | #[cfg(not(feature = "ffmpeg_7_0"))] 34 | const CHANNEL_LAYOUT = AVOptionType::AV_OPT_TYPE_CHANNEL_LAYOUT.0 as c_uint; 35 | #[cfg(feature = "ffmpeg_5_1")] 36 | const CHLAYOUT = AVOptionType::AV_OPT_TYPE_CHLAYOUT.0 as c_uint; 37 | const C_ULONG = AVOptionType::AV_OPT_TYPE_UINT64.0 as c_uint; 38 | const BOOL = AVOptionType::AV_OPT_TYPE_BOOL.0 as c_uint; 39 | 40 | /// May be combined with another regular option type to declare an 41 | /// array option. 42 | /// 43 | /// For array options, `AVOption.offset` should refer to a pointer 44 | /// corresponding to the option type. The pointer should be immediately 45 | /// followed by an unsigned int that will store the number of elements 46 | /// in the array. 47 | #[cfg(feature = "ffmpeg_7_0")] 48 | const FLAG_ARRAY = AVOptionType::AV_OPT_TYPE_FLAG_ARRAY.0 as c_uint; 49 | } 50 | } 51 | 52 | impl Default for Type { 53 | fn default() -> Self { 54 | Self::empty() 55 | } 56 | } 57 | 58 | impl From for Type { 59 | fn from(value: AVOptionType) -> Self { 60 | Self::from_bits_retain(value.0 as c_uint) 61 | } 62 | } 63 | 64 | impl From for AVOptionType { 65 | fn from(value: Type) -> AVOptionType { 66 | // cast to whichever type the C enum uses 67 | AVOptionType(value.bits() as _) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /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 crate::ffi::*; 7 | use crate::util::format; 8 | use crate::{AsMutPtr, AsPtr, Error, Rational}; 9 | use libc::c_int; 10 | 11 | #[cfg(not(feature = "ffmpeg_7_0"))] 12 | use crate::ChannelLayoutMask; 13 | 14 | macro_rules! check { 15 | ($expr:expr) => { 16 | match $expr { 17 | 0 => Ok(()), 18 | e => Err(Error::from(e)), 19 | } 20 | }; 21 | } 22 | 23 | pub trait Settable: AsPtr + AsMutPtr { 24 | fn set(&mut self, name: &str, value: &V) -> Result<(), Error> { 25 | unsafe { 26 | let name = CString::new(name).unwrap(); 27 | 28 | check!(av_opt_set_bin( 29 | self.as_mut_ptr() as *mut _, 30 | name.as_ptr(), 31 | value as *const _ as *const _, 32 | mem::size_of::() as c_int, 33 | AV_OPT_SEARCH_CHILDREN 34 | )) 35 | } 36 | } 37 | 38 | fn set_str(&mut self, name: &str, value: &str) -> Result<(), Error> { 39 | unsafe { 40 | let name = CString::new(name).unwrap(); 41 | let value = CString::new(value).unwrap(); 42 | 43 | check!(av_opt_set( 44 | self.as_mut_ptr() as *mut _, 45 | name.as_ptr(), 46 | value.as_ptr(), 47 | AV_OPT_SEARCH_CHILDREN 48 | )) 49 | } 50 | } 51 | 52 | fn set_int(&mut self, name: &str, value: i64) -> Result<(), Error> { 53 | unsafe { 54 | let name = CString::new(name).unwrap(); 55 | 56 | check!(av_opt_set_int( 57 | self.as_mut_ptr() as *mut _, 58 | name.as_ptr(), 59 | value, 60 | AV_OPT_SEARCH_CHILDREN 61 | )) 62 | } 63 | } 64 | 65 | fn set_double(&mut self, name: &str, value: f64) -> Result<(), Error> { 66 | unsafe { 67 | let name = CString::new(name).unwrap(); 68 | 69 | check!(av_opt_set_double( 70 | self.as_mut_ptr() as *mut _, 71 | name.as_ptr(), 72 | value, 73 | AV_OPT_SEARCH_CHILDREN 74 | )) 75 | } 76 | } 77 | 78 | fn set_rational>(&mut self, name: &str, value: V) -> Result<(), Error> { 79 | unsafe { 80 | let name = CString::new(name).unwrap(); 81 | 82 | check!(av_opt_set_q( 83 | self.as_mut_ptr() as *mut _, 84 | name.as_ptr(), 85 | value.into().into(), 86 | AV_OPT_SEARCH_CHILDREN 87 | )) 88 | } 89 | } 90 | 91 | fn set_image_size(&mut self, name: &str, w: u32, h: u32) -> Result<(), Error> { 92 | unsafe { 93 | let name = CString::new(name).unwrap(); 94 | 95 | check!(av_opt_set_image_size( 96 | self.as_mut_ptr() as *mut _, 97 | name.as_ptr(), 98 | w as c_int, 99 | h as c_int, 100 | AV_OPT_SEARCH_CHILDREN 101 | )) 102 | } 103 | } 104 | 105 | fn set_pixel_format(&mut self, name: &str, format: format::Pixel) -> Result<(), Error> { 106 | unsafe { 107 | let name = CString::new(name).unwrap(); 108 | 109 | check!(av_opt_set_pixel_fmt( 110 | self.as_mut_ptr() as *mut _, 111 | name.as_ptr(), 112 | format.into(), 113 | AV_OPT_SEARCH_CHILDREN 114 | )) 115 | } 116 | } 117 | 118 | fn set_sample_format(&mut self, name: &str, format: format::Sample) -> Result<(), Error> { 119 | unsafe { 120 | let name = CString::new(name).unwrap(); 121 | 122 | check!(av_opt_set_sample_fmt( 123 | self.as_mut_ptr() as *mut _, 124 | name.as_ptr(), 125 | format.into(), 126 | AV_OPT_SEARCH_CHILDREN 127 | )) 128 | } 129 | } 130 | 131 | #[cfg(not(feature = "ffmpeg_7_0"))] 132 | fn set_channel_layout(&mut self, name: &str, layout: ChannelLayoutMask) -> Result<(), Error> { 133 | unsafe { 134 | let name = CString::new(name).unwrap(); 135 | 136 | check!(av_opt_set_channel_layout( 137 | self.as_mut_ptr() as *mut _, 138 | name.as_ptr(), 139 | layout.bits() as i64, 140 | AV_OPT_SEARCH_CHILDREN 141 | )) 142 | } 143 | } 144 | } 145 | 146 | pub trait Gettable: AsPtr + AsMutPtr {} 147 | 148 | pub trait Iterable: AsPtr + AsMutPtr {} 149 | -------------------------------------------------------------------------------- /src/util/picture.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::AVPictureType::*; 2 | use crate::ffi::*; 3 | #[cfg(feature = "serialize")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 7 | #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] 8 | pub enum Type { 9 | None, 10 | I, 11 | P, 12 | B, 13 | S, 14 | SI, 15 | SP, 16 | BI, 17 | } 18 | 19 | impl From for Type { 20 | #[inline(always)] 21 | fn from(value: AVPictureType) -> Type { 22 | match value { 23 | AV_PICTURE_TYPE_NONE => Type::None, 24 | AV_PICTURE_TYPE_I => Type::I, 25 | AV_PICTURE_TYPE_P => Type::P, 26 | AV_PICTURE_TYPE_B => Type::B, 27 | AV_PICTURE_TYPE_S => Type::S, 28 | AV_PICTURE_TYPE_SI => Type::SI, 29 | AV_PICTURE_TYPE_SP => Type::SP, 30 | AV_PICTURE_TYPE_BI => Type::BI, 31 | 32 | #[cfg(feature = "non-exhaustive-enums")] 33 | _ => unimplemented!(), 34 | } 35 | } 36 | } 37 | 38 | impl From for AVPictureType { 39 | #[inline(always)] 40 | fn from(value: Type) -> AVPictureType { 41 | match value { 42 | Type::None => AV_PICTURE_TYPE_NONE, 43 | Type::I => AV_PICTURE_TYPE_I, 44 | Type::P => AV_PICTURE_TYPE_P, 45 | Type::B => AV_PICTURE_TYPE_B, 46 | Type::S => AV_PICTURE_TYPE_S, 47 | Type::SI => AV_PICTURE_TYPE_SI, 48 | Type::SP => AV_PICTURE_TYPE_SP, 49 | Type::BI => AV_PICTURE_TYPE_BI, 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/util/time.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::*; 2 | use crate::Error; 3 | 4 | #[inline(always)] 5 | pub fn current() -> i64 { 6 | unsafe { av_gettime() as i64 } 7 | } 8 | 9 | #[inline(always)] 10 | pub fn relative() -> i64 { 11 | unsafe { av_gettime_relative() as i64 } 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 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | //! Internal utils, not related to `avutil` 2 | 3 | use std::ffi::CStr; 4 | 5 | /// `ptr` must be non-null and valid. 6 | /// Ensure that the returned lifetime is correctly bounded. 7 | #[inline] 8 | pub unsafe fn str_from_c_ptr<'s>(ptr: *const libc::c_char) -> &'s str { 9 | unsafe { std::str::from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) } 10 | } 11 | 12 | /// `ptr` must be null or valid. 13 | /// Ensure that the returned lifetime is correctly bounded. 14 | #[inline] 15 | pub unsafe fn optional_str_from_c_ptr<'s>(ptr: *const libc::c_char) -> Option<&'s str> { 16 | if ptr.is_null() { 17 | None 18 | } else { 19 | Some(str_from_c_ptr(ptr)) 20 | } 21 | } 22 | --------------------------------------------------------------------------------