├── .github ├── ISSUE_TEMPLATE │ ├── bug.md │ ├── config.yml │ └── feature.md └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── 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 └── src ├── codec ├── audio.rs ├── audio_service.rs ├── capabilities.rs ├── codec.rs ├── compliance.rs ├── context.rs ├── debug.rs ├── decoder │ ├── audio.rs │ ├── check.rs │ ├── conceal.rs │ ├── decoder.rs │ ├── mod.rs │ ├── opened.rs │ ├── slice.rs │ ├── subtitle.rs │ └── video.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.rs ├── picture.rs ├── profile.rs ├── subtitle │ ├── flag.rs │ ├── mod.rs │ ├── rect.rs │ └── rect_mut.rs ├── threading.rs ├── traits.rs └── video.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 ├── 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.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 ├── legacy_channel_layout.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 ├── range.rs ├── rational.rs ├── rounding.rs └── time.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/zmwangx/rust-ffmpeg/discussions 6 | about: If you have a question about usage, please use discussions instead of opening a (non-)issue. Note that I (solo maintainer short on time) unfortunately might not be able to respond, though I try to be helpful when time permits. 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New feature request 3 | about: If you have a feature request, your best bet is probably a PR. However, for anything nontrivial, please open an issue to discuss it first. 4 | --- 5 | 6 | *Please discuss your new feature before implementing it if it's nontrivial. Adding a small method is probably trivial, anything larger than that, maybe not. Note that API stability is paramount.* 7 | -------------------------------------------------------------------------------- /.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 <`, now `Result<(), Error>`. (#25) 20 | 21 | 4.3.8 22 | ----- 23 | - software::resampling: add Context::get_with for specifying additional options. (#41) 24 | 25 | 4.3.7 26 | ----- 27 | 28 | - codec: fix codec description potential null ptr issue. (#36) 29 | 30 | 4.3.6 31 | ----- 32 | 33 | - util: fix Windows compatibility due to unavailable errnos. (#30) 34 | 35 | 4.3.5 36 | ----- 37 | 38 | - util: add `util::log` module to expose FFmpeg's logging facilities. 39 | 40 | - filter: add method `Source::close()` to expose `av_buffersrc_close`. (#23) 41 | 42 | - codec: add new encoding/decoding APIs `send_frame()` / `send_eof()`, `receive_packet()` to `encoder::{Audio, Video}` and `send_packet()` / `send_eof()`, `receive_frame()` to `decoder::{Audio, Video}` based on modern send/receive APIs (instead of `avcodec_decode_video2()` / `avcodec_decode_audio4()` / `avcodec_encode_video2()` /`avcodec_encode_audio2()` which have been deprecated since FFmpeg 3.1). Users should consider switching to the new APIs. See [documentation in `libavcodec/avcodec.h`](https://github.com/FFmpeg/FFmpeg/blob/n4.3.1/libavcodec/avcodec.h#L84-L196) for details. (#28) 43 | 44 | - util: introduce new `Error` variant `Error::Other { errno }` for wrapped POSIX error codes (see the `AVERROR` macro in `libavutil/error.h`), and reexport common POSIX error codes under `util::error`. (#24) 45 | 46 | 4.3.4 47 | ----- 48 | 49 | - crate: FFmpeg version detection is now automatic, obseleting feature flags `ffmpeg4`, `ffmpeg41`, `ffmpeg42` and `ffmpeg43`. The flags are kept as noop for now, will be removed in 5.0. 50 | -------------------------------------------------------------------------------- /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-next.svg)](https://crates.io/crates/ffmpeg-next) 2 | [![docs.rs](https://docs.rs/ffmpeg-next/badge.svg)](https://docs.rs/ffmpeg-next/) 3 | [![build](https://github.com/zmwangx/rust-ffmpeg/workflows/build/badge.svg)](https://github.com/zmwangx/rust-ffmpeg/actions) 4 | 5 | This is a fork of the abandoned [ffmpeg](https://crates.io/crates/ffmpeg) crate by [meh.](https://github.com/meh/rust-ffmpeg). 6 | 7 | This crate is currently in maintenance mode, and aims to be compatible with all of FFmpeg's versions from 3.4 8 | (currently from 3.4 til 7.0) 9 | 10 | Build instructions can be found on the [wiki](https://github.com/zmwangx/rust-ffmpeg/wiki/Notes-on-building). 11 | 12 | Documentation: 13 | 14 | - [docs.rs](https://docs.rs/ffmpeg-next/); 15 | - [FFmpeg user manual](https://ffmpeg.org/ffmpeg-all.html); 16 | - [FFmpeg Doxygen](https://ffmpeg.org/doxygen/trunk/). 17 | 18 | *Note on upgrading to v4.3.4 or later: v4.3.4 introduced automatic FFmpeg version detection, obsoleting feature flags `ffmpeg4`, `ffmpeg41`, `ffmpeg42` and `ffmpeg43`. If you manually specify any of these features, now is the time to remove them; if you use `ffmpeg43` through the `default` feature, it's still on for backward-compatibility but it has turned into a no-op, and you don't need to do anything. Deprecation plan: `ffmpeg43` will be dropped from default features come 4.4, and all these features will be removed come 5.0.* 19 | 20 | *See [CHANGELOG.md](CHANGELOG.md) for other information on version upgrades.* 21 | 22 | A word on versioning: major and minor versions of this crate track major and minor versions of FFmpeg, e.g. 4.2.x of this crate has been updated to support the 4.2.x series of FFmpeg. Patch level is reserved for changes to this crate and does not track FFmpeg patch versions. Since we can only freely bump the patch level, versioning of this crate differs from semver: minor versions may behave like semver major versions and introduce backward-incompatible changes; patch versions may behave like semver minor versions and introduce new APIs. Please peg the version you use accordingly. 23 | 24 | **Please realize that this crate is in maintenance-only mode for the most part.** Which means I'll try my best to ensure the crate compiles against all release branches of FFmpeg 3.4 and later (only the latest patch release of each release branch is officially supported) and fix reported bugs, but if a new FFmpeg version brings new APIs that require significant effort to port to Rust, you might have to send me a PR (and just to be clear, I can't really guarantee I'll have the time to review). Any PR to improve existing API is unlikely to be merged, unfortunately. 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 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn main() { 4 | for (name, value) in env::vars() { 5 | if name.starts_with("DEP_FFMPEG_") { 6 | if value == "true" { 7 | println!( 8 | r#"cargo:rustc-cfg=feature="{}""#, 9 | name["DEP_FFMPEG_".len()..name.len()].to_lowercase() 10 | ); 11 | } 12 | println!( 13 | r#"cargo:rustc-check-cfg=cfg(feature, values("{}"))"#, 14 | name["DEP_FFMPEG_".len()..name.len()].to_lowercase() 15 | ); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/chapters.rs: -------------------------------------------------------------------------------- 1 | extern crate ffmpeg_next 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 | extern crate ffmpeg_next 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 Ok(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 Ok(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 | if let Some(layouts) = audio.channel_layouts() { 51 | println!("\t channel_layouts: {:?}", layouts.collect::>()); 52 | } else { 53 | println!("\t channel_layouts: any"); 54 | } 55 | } 56 | 57 | println!("\t max_lowres: {:?}", codec.max_lowres()); 58 | } 59 | 60 | if let Some(codec) = ffmpeg::encoder::find_by_name(&arg) { 61 | println!(); 62 | println!("type: encoder"); 63 | println!("\t id: {:?}", codec.id()); 64 | println!("\t name: {}", codec.name()); 65 | println!("\t description: {}", codec.description()); 66 | println!("\t medium: {:?}", codec.medium()); 67 | println!("\t capabilities: {:?}", codec.capabilities()); 68 | 69 | if let Some(profiles) = codec.profiles() { 70 | println!("\t profiles: {:?}", profiles.collect::>()); 71 | } 72 | 73 | if let Ok(video) = codec.video() { 74 | if let Some(rates) = video.rates() { 75 | println!("\t rates: {:?}", rates.collect::>()); 76 | } else { 77 | println!("\t rates: any"); 78 | } 79 | 80 | if let Some(formats) = video.formats() { 81 | println!("\t formats: {:?}", formats.collect::>()); 82 | } else { 83 | println!("\t formats: any"); 84 | } 85 | } 86 | 87 | if let Ok(audio) = codec.audio() { 88 | if let Some(rates) = audio.rates() { 89 | println!("\t rates: {:?}", rates.collect::>()); 90 | } else { 91 | println!("\t rates: any"); 92 | } 93 | 94 | if let Some(formats) = audio.formats() { 95 | println!("\t formats: {:?}", formats.collect::>()); 96 | } else { 97 | println!("\t formats: any"); 98 | } 99 | 100 | if let Some(layouts) = audio.channel_layouts() { 101 | println!("\t channel_layouts: {:?}", layouts.collect::>()); 102 | } else { 103 | println!("\t channel_layouts: any"); 104 | } 105 | } 106 | 107 | println!("\t max_lowres: {:?}", codec.max_lowres()); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /examples/dump-frames.rs: -------------------------------------------------------------------------------- 1 | extern crate ffmpeg_next as ffmpeg; 2 | 3 | use ffmpeg::format::{input, Pixel}; 4 | use ffmpeg::media::Type; 5 | use ffmpeg::software::scaling::{context::Context, flag::Flags}; 6 | use 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 context_decoder = ffmpeg::codec::context::Context::from_parameters(input.parameters())?; 22 | let mut decoder = context_decoder.decoder().video()?; 23 | 24 | let mut scaler = Context::get( 25 | decoder.format(), 26 | decoder.width(), 27 | decoder.height(), 28 | Pixel::RGB24, 29 | decoder.width(), 30 | decoder.height(), 31 | Flags::BILINEAR, 32 | )?; 33 | 34 | let mut frame_index = 0; 35 | 36 | let mut receive_and_process_decoded_frames = 37 | |decoder: &mut ffmpeg::decoder::Video| -> Result<(), ffmpeg::Error> { 38 | let mut decoded = Video::empty(); 39 | while decoder.receive_frame(&mut decoded).is_ok() { 40 | let mut rgb_frame = Video::empty(); 41 | scaler.run(&decoded, &mut rgb_frame)?; 42 | save_file(&rgb_frame, frame_index).unwrap(); 43 | frame_index += 1; 44 | } 45 | Ok(()) 46 | }; 47 | 48 | for (stream, packet) in ictx.packets() { 49 | if stream.index() == video_stream_index { 50 | decoder.send_packet(&packet)?; 51 | receive_and_process_decoded_frames(&mut decoder)?; 52 | } 53 | } 54 | decoder.send_eof()?; 55 | receive_and_process_decoded_frames(&mut decoder)?; 56 | } 57 | 58 | Ok(()) 59 | } 60 | 61 | fn save_file(frame: &Video, index: usize) -> std::result::Result<(), std::io::Error> { 62 | let mut file = File::create(format!("frame{}.ppm", index))?; 63 | file.write_all(format!("P6\n{} {}\n255\n", frame.width(), frame.height()).as_bytes())?; 64 | file.write_all(frame.data(0))?; 65 | Ok(()) 66 | } 67 | -------------------------------------------------------------------------------- /examples/metadata.rs: -------------------------------------------------------------------------------- 1 | extern crate ffmpeg_next 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.channels: {}", audio.channels()); 77 | println!("\taudio.format: {:?}", audio.format()); 78 | println!("\taudio.frames: {}", audio.frames()); 79 | println!("\taudio.align: {}", audio.align()); 80 | println!("\taudio.channel_layout: {:?}", audio.channel_layout()); 81 | } 82 | } 83 | } 84 | } 85 | 86 | Err(error) => println!("error: {}", error), 87 | } 88 | Ok(()) 89 | } 90 | -------------------------------------------------------------------------------- /examples/remux.rs: -------------------------------------------------------------------------------- 1 | extern crate ffmpeg_next as ffmpeg; 2 | 3 | use std::env; 4 | 5 | use 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().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() { 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 | -------------------------------------------------------------------------------- /src/codec/audio.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use super::codec::Codec; 4 | use ffi::*; 5 | use {format, ChannelLayout}; 6 | 7 | #[derive(PartialEq, Eq, Copy, Clone)] 8 | pub struct Audio { 9 | codec: Codec, 10 | } 11 | 12 | impl Audio { 13 | pub unsafe fn new(codec: Codec) -> Audio { 14 | Audio { codec } 15 | } 16 | } 17 | 18 | impl Audio { 19 | pub fn rates(&self) -> Option { 20 | unsafe { 21 | if (*self.as_ptr()).supported_samplerates.is_null() { 22 | None 23 | } else { 24 | Some(RateIter::new((*self.codec.as_ptr()).supported_samplerates)) 25 | } 26 | } 27 | } 28 | 29 | pub fn formats(&self) -> Option { 30 | unsafe { 31 | if (*self.codec.as_ptr()).sample_fmts.is_null() { 32 | None 33 | } else { 34 | Some(FormatIter::new((*self.codec.as_ptr()).sample_fmts)) 35 | } 36 | } 37 | } 38 | 39 | pub fn channel_layouts(&self) -> Option { 40 | unsafe { 41 | #[cfg(not(feature = "ffmpeg_7_0"))] 42 | let ptr = (*self.codec.as_ptr()).channel_layouts; 43 | 44 | #[cfg(feature = "ffmpeg_7_0")] 45 | let ptr = (*self.codec.as_ptr()).ch_layouts; 46 | 47 | if ptr.is_null() { 48 | None 49 | } else { 50 | Some(ChannelLayoutIter::new(ptr)) 51 | } 52 | } 53 | } 54 | } 55 | 56 | impl Deref for Audio { 57 | type Target = Codec; 58 | 59 | fn deref(&self) -> &Self::Target { 60 | &self.codec 61 | } 62 | } 63 | 64 | pub struct RateIter { 65 | ptr: *const i32, 66 | } 67 | 68 | impl RateIter { 69 | pub fn new(ptr: *const i32) -> Self { 70 | RateIter { ptr } 71 | } 72 | } 73 | 74 | impl Iterator for RateIter { 75 | type Item = i32; 76 | 77 | fn next(&mut self) -> Option<::Item> { 78 | unsafe { 79 | if *self.ptr == 0 { 80 | return None; 81 | } 82 | 83 | let rate = *self.ptr; 84 | self.ptr = self.ptr.offset(1); 85 | 86 | Some(rate) 87 | } 88 | } 89 | } 90 | 91 | pub struct FormatIter { 92 | ptr: *const AVSampleFormat, 93 | } 94 | 95 | impl FormatIter { 96 | pub fn new(ptr: *const AVSampleFormat) -> Self { 97 | FormatIter { ptr } 98 | } 99 | } 100 | 101 | impl Iterator for FormatIter { 102 | type Item = format::Sample; 103 | 104 | fn next(&mut self) -> Option<::Item> { 105 | unsafe { 106 | if *self.ptr == AVSampleFormat::AV_SAMPLE_FMT_NONE { 107 | return None; 108 | } 109 | 110 | let format = (*self.ptr).into(); 111 | self.ptr = self.ptr.offset(1); 112 | 113 | Some(format) 114 | } 115 | } 116 | } 117 | 118 | #[cfg(not(feature = "ffmpeg_7_0"))] 119 | type ChannelLayoutType = u64; 120 | #[cfg(feature = "ffmpeg_7_0")] 121 | type ChannelLayoutType = AVChannelLayout; 122 | 123 | pub struct ChannelLayoutIter { 124 | ptr: *const ChannelLayoutType, 125 | } 126 | 127 | impl ChannelLayoutIter { 128 | pub fn new(ptr: *const ChannelLayoutType) -> Self { 129 | ChannelLayoutIter { ptr } 130 | } 131 | 132 | pub fn best(self, max: i32) -> ChannelLayout { 133 | self.fold(ChannelLayout::MONO, |acc, cur| { 134 | if cur.channels() > acc.channels() && cur.channels() <= max as _ { 135 | cur 136 | } else { 137 | acc 138 | } 139 | }) 140 | } 141 | } 142 | 143 | impl Iterator for ChannelLayoutIter { 144 | type Item = ChannelLayout; 145 | 146 | fn next(&mut self) -> Option<::Item> { 147 | unsafe { 148 | #[cfg(not(feature = "ffmpeg_7_0"))] 149 | if *self.ptr == 0 { 150 | return None; 151 | } 152 | 153 | #[cfg(feature = "ffmpeg_7_0")] 154 | if self.ptr.is_null() || (*self.ptr).u.mask == 0 { 155 | return None; 156 | } 157 | 158 | #[cfg(not(feature = "ffmpeg_7_0"))] 159 | let layout = ChannelLayout::from_bits_truncate(*self.ptr); 160 | 161 | #[cfg(feature = "ffmpeg_7_0")] 162 | let layout = ChannelLayout::from(*self.ptr); 163 | 164 | self.ptr = self.ptr.offset(1); 165 | 166 | Some(layout) 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/codec/audio_service.rs: -------------------------------------------------------------------------------- 1 | use ffi::AVAudioServiceType::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum AudioService { 6 | Main, 7 | Effects, 8 | VisuallyImpaired, 9 | HearingImpaired, 10 | Dialogue, 11 | Commentary, 12 | Emergency, 13 | VoiceOver, 14 | Karaoke, 15 | } 16 | 17 | impl From for AudioService { 18 | fn from(value: AVAudioServiceType) -> Self { 19 | match value { 20 | AV_AUDIO_SERVICE_TYPE_MAIN => AudioService::Main, 21 | AV_AUDIO_SERVICE_TYPE_EFFECTS => AudioService::Effects, 22 | AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED => AudioService::VisuallyImpaired, 23 | AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED => AudioService::HearingImpaired, 24 | AV_AUDIO_SERVICE_TYPE_DIALOGUE => AudioService::Dialogue, 25 | AV_AUDIO_SERVICE_TYPE_COMMENTARY => AudioService::Commentary, 26 | AV_AUDIO_SERVICE_TYPE_EMERGENCY => AudioService::Emergency, 27 | AV_AUDIO_SERVICE_TYPE_VOICE_OVER => AudioService::VoiceOver, 28 | AV_AUDIO_SERVICE_TYPE_KARAOKE => AudioService::Karaoke, 29 | AV_AUDIO_SERVICE_TYPE_NB => AudioService::Main, 30 | } 31 | } 32 | } 33 | 34 | impl From for AVAudioServiceType { 35 | fn from(value: AudioService) -> AVAudioServiceType { 36 | match value { 37 | AudioService::Main => AV_AUDIO_SERVICE_TYPE_MAIN, 38 | AudioService::Effects => AV_AUDIO_SERVICE_TYPE_EFFECTS, 39 | AudioService::VisuallyImpaired => AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED, 40 | AudioService::HearingImpaired => AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED, 41 | AudioService::Dialogue => AV_AUDIO_SERVICE_TYPE_DIALOGUE, 42 | AudioService::Commentary => AV_AUDIO_SERVICE_TYPE_COMMENTARY, 43 | AudioService::Emergency => AV_AUDIO_SERVICE_TYPE_EMERGENCY, 44 | AudioService::VoiceOver => AV_AUDIO_SERVICE_TYPE_VOICE_OVER, 45 | AudioService::Karaoke => AV_AUDIO_SERVICE_TYPE_KARAOKE, 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/codec/capabilities.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_uint; 3 | 4 | bitflags! { 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 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 | #[cfg(not(feature = "ffmpeg_4_0"))] 14 | const HWACCEL_VDPAU = AV_CODEC_CAP_HWACCEL_VDPAU; 15 | const SUBFRAMES = AV_CODEC_CAP_SUBFRAMES; 16 | const EXPERIMENTAL = AV_CODEC_CAP_EXPERIMENTAL; 17 | const CHANNEL_CONF = AV_CODEC_CAP_CHANNEL_CONF; 18 | const FRAME_THREADS = AV_CODEC_CAP_FRAME_THREADS; 19 | const SLICE_THREADS = AV_CODEC_CAP_SLICE_THREADS; 20 | const PARAM_CHANGE = AV_CODEC_CAP_PARAM_CHANGE; 21 | #[cfg(not(feature = "ffmpeg_6_0"))] 22 | const AUTO_THREADS = AV_CODEC_CAP_AUTO_THREADS; 23 | #[cfg(feature = "ffmpeg_6_0")] 24 | const OTHER_THREADS = AV_CODEC_CAP_OTHER_THREADS; 25 | const VARIABLE_FRAME_SIZE = AV_CODEC_CAP_VARIABLE_FRAME_SIZE; 26 | #[cfg(not(feature = "ffmpeg_6_0"))] 27 | const INTRA_ONLY = AV_CODEC_CAP_INTRA_ONLY; 28 | #[cfg(not(feature = "ffmpeg_6_0"))] 29 | const LOSSLESS = AV_CODEC_CAP_LOSSLESS; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/codec/codec.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::str::from_utf8_unchecked; 3 | 4 | use super::{Audio, Capabilities, Id, Profile, Video}; 5 | use ffi::*; 6 | use {media, Error}; 7 | 8 | #[derive(PartialEq, Eq, Copy, Clone)] 9 | pub struct Codec { 10 | ptr: *const AVCodec, 11 | } 12 | 13 | unsafe impl Send for Codec {} 14 | unsafe impl Sync for Codec {} 15 | 16 | impl Codec { 17 | pub unsafe fn wrap(ptr: *const AVCodec) -> Self { 18 | Codec { ptr } 19 | } 20 | 21 | pub unsafe fn as_ptr(&self) -> *const AVCodec { 22 | self.ptr as *const _ 23 | } 24 | } 25 | 26 | impl Codec { 27 | pub fn is_encoder(&self) -> bool { 28 | unsafe { av_codec_is_encoder(self.as_ptr()) != 0 } 29 | } 30 | 31 | pub fn is_decoder(&self) -> bool { 32 | unsafe { av_codec_is_decoder(self.as_ptr()) != 0 } 33 | } 34 | 35 | pub fn name(&self) -> &str { 36 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).name).to_bytes()) } 37 | } 38 | 39 | pub fn description(&self) -> &str { 40 | unsafe { 41 | let long_name = (*self.as_ptr()).long_name; 42 | if long_name.is_null() { 43 | "" 44 | } else { 45 | from_utf8_unchecked(CStr::from_ptr(long_name).to_bytes()) 46 | } 47 | } 48 | } 49 | 50 | pub fn medium(&self) -> media::Type { 51 | unsafe { media::Type::from((*self.as_ptr()).type_) } 52 | } 53 | 54 | pub fn id(&self) -> Id { 55 | unsafe { Id::from((*self.as_ptr()).id) } 56 | } 57 | 58 | pub fn is_video(&self) -> bool { 59 | self.medium() == media::Type::Video 60 | } 61 | 62 | pub fn video(self) -> Result { 63 | unsafe { 64 | if self.medium() == media::Type::Video { 65 | Ok(Video::new(self)) 66 | } else { 67 | Err(Error::InvalidData) 68 | } 69 | } 70 | } 71 | 72 | pub fn is_audio(&self) -> bool { 73 | self.medium() == media::Type::Audio 74 | } 75 | 76 | pub fn audio(self) -> Result { 77 | unsafe { 78 | if self.medium() == media::Type::Audio { 79 | Ok(Audio::new(self)) 80 | } else { 81 | Err(Error::InvalidData) 82 | } 83 | } 84 | } 85 | 86 | pub fn max_lowres(&self) -> i32 { 87 | unsafe { (*self.as_ptr()).max_lowres.into() } 88 | } 89 | 90 | pub fn capabilities(&self) -> Capabilities { 91 | unsafe { Capabilities::from_bits_truncate((*self.as_ptr()).capabilities as u32) } 92 | } 93 | 94 | pub fn profiles(&self) -> Option { 95 | unsafe { 96 | if (*self.as_ptr()).profiles.is_null() { 97 | None 98 | } else { 99 | Some(ProfileIter::new(self.id(), (*self.as_ptr()).profiles)) 100 | } 101 | } 102 | } 103 | } 104 | 105 | pub struct ProfileIter { 106 | id: Id, 107 | ptr: *const AVProfile, 108 | } 109 | 110 | impl ProfileIter { 111 | pub fn new(id: Id, ptr: *const AVProfile) -> Self { 112 | ProfileIter { id, ptr } 113 | } 114 | } 115 | 116 | impl Iterator for ProfileIter { 117 | type Item = Profile; 118 | 119 | fn next(&mut self) -> Option<::Item> { 120 | unsafe { 121 | if (*self.ptr).profile == FF_PROFILE_UNKNOWN { 122 | return None; 123 | } 124 | 125 | let profile = Profile::from((self.id, (*self.ptr).profile)); 126 | self.ptr = self.ptr.offset(1); 127 | 128 | Some(profile) 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/codec/compliance.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Compliance { 6 | VeryStrict, 7 | Strict, 8 | Normal, 9 | Unofficial, 10 | Experimental, 11 | } 12 | 13 | impl From for Compliance { 14 | fn from(value: c_int) -> Self { 15 | match value { 16 | FF_COMPLIANCE_VERY_STRICT => Compliance::VeryStrict, 17 | FF_COMPLIANCE_STRICT => Compliance::Strict, 18 | FF_COMPLIANCE_NORMAL => Compliance::Normal, 19 | FF_COMPLIANCE_UNOFFICIAL => Compliance::Unofficial, 20 | FF_COMPLIANCE_EXPERIMENTAL => Compliance::Experimental, 21 | 22 | _ => Compliance::Normal, 23 | } 24 | } 25 | } 26 | 27 | impl From for c_int { 28 | fn from(value: Compliance) -> c_int { 29 | match value { 30 | Compliance::VeryStrict => FF_COMPLIANCE_VERY_STRICT, 31 | Compliance::Strict => FF_COMPLIANCE_STRICT, 32 | Compliance::Normal => FF_COMPLIANCE_NORMAL, 33 | Compliance::Unofficial => FF_COMPLIANCE_UNOFFICIAL, 34 | Compliance::Experimental => FF_COMPLIANCE_EXPERIMENTAL, 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/codec/debug.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 6 | pub struct Debug: c_int { 7 | const PICT_INFO = FF_DEBUG_PICT_INFO; 8 | const RC = FF_DEBUG_RC; 9 | const BITSTREAM = FF_DEBUG_BITSTREAM; 10 | const MB_TYPE = FF_DEBUG_MB_TYPE; 11 | const QP = FF_DEBUG_QP; 12 | #[cfg(not(feature = "ffmpeg_4_0"))] 13 | const MV = FF_DEBUG_MV; 14 | const DCT_COEFF = FF_DEBUG_DCT_COEFF; 15 | const SKIP = FF_DEBUG_SKIP; 16 | const STARTCODE = FF_DEBUG_STARTCODE; 17 | #[cfg(not(feature = "ffmpeg_4_0"))] 18 | const PTS = FF_DEBUG_PTS; 19 | const ER = FF_DEBUG_ER; 20 | const MMCO = FF_DEBUG_MMCO; 21 | const BUGS = FF_DEBUG_BUGS; 22 | #[cfg(not(feature = "ffmpeg_4_0"))] 23 | const VIS_QP = FF_DEBUG_VIS_QP; 24 | #[cfg(not(feature = "ffmpeg_4_0"))] 25 | const VIS_MB_TYPE = FF_DEBUG_VIS_MB_TYPE; 26 | const BUFFERS = FF_DEBUG_BUFFERS; 27 | const THREADS = FF_DEBUG_THREADS; 28 | const NOMC = FF_DEBUG_NOMC; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/codec/decoder/check.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 6 | pub struct Check: c_int { 7 | const CRC = AV_EF_CRCCHECK; 8 | const BISTREAM = AV_EF_BITSTREAM; 9 | const BUFFER = AV_EF_BUFFER; 10 | const EXPLODE = AV_EF_EXPLODE; 11 | 12 | const IGNORE_ERROR = AV_EF_IGNORE_ERR; 13 | const CAREFUL = AV_EF_CAREFUL; 14 | const COMPLIANT = AV_EF_COMPLIANT; 15 | const AGGRESSIVE = AV_EF_AGGRESSIVE; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/codec/decoder/conceal.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 6 | pub struct Conceal: c_int { 7 | const GUESS_MVS = FF_EC_GUESS_MVS; 8 | const DEBLOCK = FF_EC_DEBLOCK; 9 | const FAVOR_INTER = FF_EC_FAVOR_INTER; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/codec/decoder/decoder.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | use std::ptr; 3 | 4 | use super::{Audio, Check, Conceal, Opened, Subtitle, Video}; 5 | use codec::{traits, Context}; 6 | use ffi::*; 7 | use {Dictionary, Discard, Error, Rational}; 8 | 9 | pub struct Decoder(pub Context); 10 | 11 | impl Decoder { 12 | pub fn open(mut self) -> Result { 13 | unsafe { 14 | match avcodec_open2(self.as_mut_ptr(), ptr::null(), ptr::null_mut()) { 15 | 0 => Ok(Opened(self)), 16 | e => Err(Error::from(e)), 17 | } 18 | } 19 | } 20 | 21 | pub fn open_as(mut self, codec: D) -> Result { 22 | unsafe { 23 | if let Some(codec) = codec.decoder() { 24 | match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) { 25 | 0 => Ok(Opened(self)), 26 | e => Err(Error::from(e)), 27 | } 28 | } else { 29 | Err(Error::DecoderNotFound) 30 | } 31 | } 32 | } 33 | 34 | pub fn open_as_with( 35 | mut self, 36 | codec: D, 37 | options: Dictionary, 38 | ) -> Result { 39 | unsafe { 40 | if let Some(codec) = codec.decoder() { 41 | let mut opts = options.disown(); 42 | let res = avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), &mut opts); 43 | 44 | Dictionary::own(opts); 45 | 46 | match res { 47 | 0 => Ok(Opened(self)), 48 | e => Err(Error::from(e)), 49 | } 50 | } else { 51 | Err(Error::DecoderNotFound) 52 | } 53 | } 54 | } 55 | 56 | pub fn video(self) -> Result { 57 | if let Some(codec) = self.codec() { 58 | self.open_as(codec).and_then(|o| o.video()) 59 | } else if let Some(codec) = super::find(self.id()) { 60 | self.open_as(codec).and_then(|o| o.video()) 61 | } else { 62 | Err(Error::DecoderNotFound) 63 | } 64 | } 65 | 66 | pub fn audio(self) -> Result { 67 | if let Some(codec) = self.codec() { 68 | self.open_as(codec).and_then(|o| o.audio()) 69 | } else if let Some(codec) = super::find(self.id()) { 70 | self.open_as(codec).and_then(|o| o.audio()) 71 | } else { 72 | Err(Error::DecoderNotFound) 73 | } 74 | } 75 | 76 | pub fn subtitle(self) -> Result { 77 | if let Some(codec) = super::find(self.id()) { 78 | self.open_as(codec).and_then(|o| o.subtitle()) 79 | } else { 80 | Err(Error::DecoderNotFound) 81 | } 82 | } 83 | 84 | pub fn conceal(&mut self, value: Conceal) { 85 | unsafe { 86 | (*self.as_mut_ptr()).error_concealment = value.bits(); 87 | } 88 | } 89 | 90 | pub fn check(&mut self, value: Check) { 91 | unsafe { 92 | (*self.as_mut_ptr()).err_recognition = value.bits(); 93 | } 94 | } 95 | 96 | pub fn skip_loop_filter(&mut self, value: Discard) { 97 | unsafe { 98 | (*self.as_mut_ptr()).skip_loop_filter = value.into(); 99 | } 100 | } 101 | 102 | pub fn skip_idct(&mut self, value: Discard) { 103 | unsafe { 104 | (*self.as_mut_ptr()).skip_idct = value.into(); 105 | } 106 | } 107 | 108 | pub fn skip_frame(&mut self, value: Discard) { 109 | unsafe { 110 | (*self.as_mut_ptr()).skip_frame = value.into(); 111 | } 112 | } 113 | 114 | pub fn packet_time_base(&self) -> Rational { 115 | unsafe { Rational::from((*self.as_ptr()).pkt_timebase) } 116 | } 117 | 118 | pub fn set_packet_time_base>(&mut self, value: R) { 119 | unsafe { 120 | (*self.as_mut_ptr()).pkt_timebase = value.into().into(); 121 | } 122 | } 123 | } 124 | 125 | impl Deref for Decoder { 126 | type Target = Context; 127 | 128 | fn deref(&self) -> &::Target { 129 | &self.0 130 | } 131 | } 132 | 133 | impl DerefMut for Decoder { 134 | fn deref_mut(&mut self) -> &mut ::Target { 135 | &mut self.0 136 | } 137 | } 138 | 139 | impl AsRef for Decoder { 140 | fn as_ref(&self) -> &Context { 141 | self 142 | } 143 | } 144 | 145 | impl AsMut for Decoder { 146 | fn as_mut(&mut self) -> &mut Context { 147 | &mut self.0 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/codec/decoder/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod decoder; 2 | pub use self::decoder::Decoder; 3 | 4 | pub mod video; 5 | pub use self::video::Video; 6 | 7 | pub mod audio; 8 | pub use self::audio::Audio; 9 | 10 | pub mod subtitle; 11 | pub use self::subtitle::Subtitle; 12 | 13 | pub mod slice; 14 | 15 | pub mod conceal; 16 | pub use self::conceal::Conceal; 17 | 18 | pub mod check; 19 | pub use self::check::Check; 20 | 21 | pub mod opened; 22 | pub use self::opened::Opened; 23 | 24 | use std::ffi::CString; 25 | 26 | use codec::Context; 27 | use codec::Id; 28 | use ffi::*; 29 | use Codec; 30 | 31 | pub fn new() -> Decoder { 32 | Context::new().decoder() 33 | } 34 | 35 | pub fn find(id: Id) -> Option { 36 | unsafe { 37 | // We get a clippy warning in 4.4 but not in 5.0 and newer, so we allow that cast to not complicate the code 38 | #[allow(clippy::unnecessary_cast)] 39 | let ptr = avcodec_find_decoder(id.into()) as *mut AVCodec; 40 | 41 | if ptr.is_null() { 42 | None 43 | } else { 44 | Some(Codec::wrap(ptr)) 45 | } 46 | } 47 | } 48 | 49 | pub fn find_by_name(name: &str) -> Option { 50 | unsafe { 51 | let name = CString::new(name).unwrap(); 52 | #[allow(clippy::unnecessary_cast)] 53 | let ptr = avcodec_find_decoder_by_name(name.as_ptr()) as *mut AVCodec; 54 | 55 | if ptr.is_null() { 56 | None 57 | } else { 58 | Some(Codec::wrap(ptr)) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/codec/decoder/opened.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | use std::ptr; 3 | 4 | use super::{Audio, Decoder, Subtitle, Video}; 5 | use codec::{Context, Profile}; 6 | use ffi::*; 7 | use {media, packet, Error, Frame, Rational}; 8 | 9 | pub struct Opened(pub Decoder); 10 | 11 | impl Opened { 12 | pub fn video(self) -> Result { 13 | if self.medium() == media::Type::Video { 14 | Ok(Video(self)) 15 | } else { 16 | Err(Error::InvalidData) 17 | } 18 | } 19 | 20 | pub fn audio(self) -> Result { 21 | if self.medium() == media::Type::Audio { 22 | Ok(Audio(self)) 23 | } else { 24 | Err(Error::InvalidData) 25 | } 26 | } 27 | 28 | pub fn subtitle(self) -> Result { 29 | if self.medium() == media::Type::Subtitle { 30 | Ok(Subtitle(self)) 31 | } else { 32 | Err(Error::InvalidData) 33 | } 34 | } 35 | 36 | pub fn send_packet(&mut self, packet: &P) -> Result<(), Error> { 37 | unsafe { 38 | match avcodec_send_packet(self.as_mut_ptr(), packet.as_ptr()) { 39 | e if e < 0 => Err(Error::from(e)), 40 | _ => Ok(()), 41 | } 42 | } 43 | } 44 | 45 | /// Sends a NULL packet to the decoder to signal end of stream and enter 46 | /// draining mode. 47 | pub fn send_eof(&mut self) -> Result<(), Error> { 48 | unsafe { 49 | match avcodec_send_packet(self.as_mut_ptr(), ptr::null()) { 50 | e if e < 0 => Err(Error::from(e)), 51 | _ => Ok(()), 52 | } 53 | } 54 | } 55 | 56 | pub fn receive_frame(&mut self, frame: &mut Frame) -> Result<(), Error> { 57 | unsafe { 58 | match avcodec_receive_frame(self.as_mut_ptr(), frame.as_mut_ptr()) { 59 | e if e < 0 => Err(Error::from(e)), 60 | _ => Ok(()), 61 | } 62 | } 63 | } 64 | 65 | pub fn 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 ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 6 | pub struct Flags: c_int { 7 | const CODED_ORDER = SLICE_FLAG_CODED_ORDER; 8 | const ALLOW_FIELD = SLICE_FLAG_ALLOW_FIELD; 9 | const ALLOW_PLANE = SLICE_FLAG_ALLOW_PLANE; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/codec/decoder/subtitle.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | use ffi::*; 4 | use libc::c_int; 5 | 6 | use super::Opened; 7 | use codec::Context; 8 | use {packet, Error}; 9 | 10 | pub struct Subtitle(pub Opened); 11 | 12 | impl Subtitle { 13 | pub fn decode( 14 | &mut self, 15 | packet: &P, 16 | out: &mut ::Subtitle, 17 | ) -> Result { 18 | unsafe { 19 | let mut got: c_int = 0; 20 | 21 | match avcodec_decode_subtitle2( 22 | self.as_mut_ptr(), 23 | out.as_mut_ptr(), 24 | &mut got, 25 | packet.as_ptr() as *mut _, 26 | ) { 27 | e if e < 0 => Err(Error::from(e)), 28 | _ => Ok(got != 0), 29 | } 30 | } 31 | } 32 | } 33 | 34 | impl Deref for Subtitle { 35 | type Target = Opened; 36 | 37 | fn deref(&self) -> &::Target { 38 | &self.0 39 | } 40 | } 41 | 42 | impl DerefMut for Subtitle { 43 | fn deref_mut(&mut self) -> &mut ::Target { 44 | &mut self.0 45 | } 46 | } 47 | 48 | impl AsRef for Subtitle { 49 | fn as_ref(&self) -> &Context { 50 | self 51 | } 52 | } 53 | 54 | impl AsMut for Subtitle { 55 | fn as_mut(&mut self) -> &mut Context { 56 | &mut self.0 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/codec/decoder/video.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | #[cfg(not(feature = "ffmpeg_5_0"))] 4 | use ffi::*; 5 | use libc::c_int; 6 | 7 | use super::{slice, Opened}; 8 | use codec::Context; 9 | use color; 10 | #[cfg(not(feature = "ffmpeg_5_0"))] 11 | use frame; 12 | use util::chroma; 13 | use util::format; 14 | #[cfg(not(feature = "ffmpeg_5_0"))] 15 | use {packet, Error}; 16 | use {FieldOrder, Rational}; 17 | 18 | pub struct Video(pub Opened); 19 | 20 | impl Video { 21 | #[deprecated( 22 | since = "4.4.0", 23 | note = "Underlying API avcodec_decode_video2 has been deprecated since FFmpeg 3.1; \ 24 | consider switching to send_packet() and receive_frame()" 25 | )] 26 | #[cfg(not(feature = "ffmpeg_5_0"))] 27 | pub fn decode( 28 | &mut self, 29 | packet: &P, 30 | out: &mut frame::Video, 31 | ) -> Result { 32 | unsafe { 33 | let mut got: c_int = 0; 34 | 35 | match avcodec_decode_video2( 36 | self.as_mut_ptr(), 37 | out.as_mut_ptr(), 38 | &mut got, 39 | packet.as_ptr(), 40 | ) { 41 | e if e < 0 => Err(Error::from(e)), 42 | _ => Ok(got != 0), 43 | } 44 | } 45 | } 46 | 47 | pub fn width(&self) -> u32 { 48 | unsafe { (*self.as_ptr()).width as u32 } 49 | } 50 | 51 | pub fn height(&self) -> u32 { 52 | unsafe { (*self.as_ptr()).height as u32 } 53 | } 54 | 55 | pub fn format(&self) -> format::Pixel { 56 | unsafe { format::Pixel::from((*self.as_ptr()).pix_fmt) } 57 | } 58 | 59 | pub fn has_b_frames(&self) -> bool { 60 | unsafe { (*self.as_ptr()).has_b_frames != 0 } 61 | } 62 | 63 | pub fn aspect_ratio(&self) -> Rational { 64 | unsafe { Rational::from((*self.as_ptr()).sample_aspect_ratio) } 65 | } 66 | 67 | pub fn color_space(&self) -> color::Space { 68 | unsafe { color::Space::from((*self.as_ptr()).colorspace) } 69 | } 70 | 71 | pub fn color_range(&self) -> color::Range { 72 | unsafe { color::Range::from((*self.as_ptr()).color_range) } 73 | } 74 | 75 | pub fn color_primaries(&self) -> color::Primaries { 76 | unsafe { color::Primaries::from((*self.as_ptr()).color_primaries) } 77 | } 78 | 79 | pub fn color_transfer_characteristic(&self) -> color::TransferCharacteristic { 80 | unsafe { color::TransferCharacteristic::from((*self.as_ptr()).color_trc) } 81 | } 82 | 83 | pub fn chroma_location(&self) -> chroma::Location { 84 | unsafe { chroma::Location::from((*self.as_ptr()).chroma_sample_location) } 85 | } 86 | 87 | #[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 ffi::AVDiscard::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Discard { 6 | None, 7 | Default, 8 | NonReference, 9 | Bidirectional, 10 | NonIntra, 11 | NonKey, 12 | All, 13 | } 14 | 15 | impl From for Discard { 16 | fn from(value: AVDiscard) -> Self { 17 | match value { 18 | AVDISCARD_NONE => Discard::None, 19 | AVDISCARD_DEFAULT => Discard::Default, 20 | AVDISCARD_NONREF => Discard::NonReference, 21 | AVDISCARD_BIDIR => Discard::Bidirectional, 22 | AVDISCARD_NONINTRA => Discard::NonIntra, 23 | AVDISCARD_NONKEY => Discard::NonKey, 24 | AVDISCARD_ALL => Discard::All, 25 | } 26 | } 27 | } 28 | 29 | impl From for AVDiscard { 30 | fn from(value: Discard) -> AVDiscard { 31 | match value { 32 | Discard::None => AVDISCARD_NONE, 33 | Discard::Default => AVDISCARD_DEFAULT, 34 | Discard::NonReference => AVDISCARD_NONREF, 35 | Discard::Bidirectional => AVDISCARD_BIDIR, 36 | Discard::NonIntra => AVDISCARD_NONINTRA, 37 | Discard::NonKey => AVDISCARD_NONKEY, 38 | Discard::All => AVDISCARD_ALL, 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/codec/encoder/comparison.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Comparison { 6 | SAD, 7 | SSE, 8 | SATD, 9 | DCT, 10 | PSNR, 11 | BIT, 12 | RD, 13 | ZERO, 14 | VSAD, 15 | VSSE, 16 | NSSE, 17 | W53, 18 | W97, 19 | DCTMAX, 20 | DCT264, 21 | CHROMA, 22 | } 23 | 24 | impl From for Comparison { 25 | fn from(value: c_int) -> Comparison { 26 | match value { 27 | FF_CMP_SAD => Comparison::SAD, 28 | FF_CMP_SSE => Comparison::SSE, 29 | FF_CMP_SATD => Comparison::SATD, 30 | FF_CMP_DCT => Comparison::DCT, 31 | FF_CMP_PSNR => Comparison::PSNR, 32 | FF_CMP_BIT => Comparison::BIT, 33 | FF_CMP_RD => Comparison::RD, 34 | FF_CMP_ZERO => Comparison::ZERO, 35 | FF_CMP_VSAD => Comparison::VSAD, 36 | FF_CMP_VSSE => Comparison::VSSE, 37 | FF_CMP_NSSE => Comparison::NSSE, 38 | FF_CMP_W53 => Comparison::W53, 39 | FF_CMP_W97 => Comparison::W97, 40 | FF_CMP_DCTMAX => Comparison::DCTMAX, 41 | FF_CMP_DCT264 => Comparison::DCT264, 42 | FF_CMP_CHROMA => Comparison::CHROMA, 43 | 44 | _ => Comparison::ZERO, 45 | } 46 | } 47 | } 48 | 49 | impl From for c_int { 50 | fn from(value: Comparison) -> c_int { 51 | match value { 52 | Comparison::SAD => FF_CMP_SAD, 53 | Comparison::SSE => FF_CMP_SSE, 54 | Comparison::SATD => FF_CMP_SATD, 55 | Comparison::DCT => FF_CMP_DCT, 56 | Comparison::PSNR => FF_CMP_PSNR, 57 | Comparison::BIT => FF_CMP_BIT, 58 | Comparison::RD => FF_CMP_RD, 59 | Comparison::ZERO => FF_CMP_ZERO, 60 | Comparison::VSAD => FF_CMP_VSAD, 61 | Comparison::VSSE => FF_CMP_VSSE, 62 | Comparison::NSSE => FF_CMP_NSSE, 63 | Comparison::W53 => FF_CMP_W53, 64 | Comparison::W97 => FF_CMP_W97, 65 | Comparison::DCTMAX => FF_CMP_DCTMAX, 66 | Comparison::DCT264 => FF_CMP_DCT264, 67 | Comparison::CHROMA => FF_CMP_CHROMA, 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/codec/encoder/decision.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Decision { 6 | Simple, 7 | Bits, 8 | RateDistortion, 9 | } 10 | 11 | impl From for Decision { 12 | fn from(value: c_int) -> Decision { 13 | match value { 14 | FF_MB_DECISION_SIMPLE => Decision::Simple, 15 | FF_MB_DECISION_BITS => Decision::Bits, 16 | FF_MB_DECISION_RD => Decision::RateDistortion, 17 | 18 | _ => Decision::Simple, 19 | } 20 | } 21 | } 22 | 23 | impl From for c_int { 24 | fn from(value: Decision) -> c_int { 25 | match value { 26 | Decision::Simple => FF_MB_DECISION_SIMPLE, 27 | Decision::Bits => FF_MB_DECISION_BITS, 28 | Decision::RateDistortion => FF_MB_DECISION_RD, 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/codec/encoder/encoder.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | use std::ptr; 3 | 4 | use ffi::*; 5 | use libc::c_int; 6 | 7 | use super::{audio, subtitle, video}; 8 | use codec::Context; 9 | use {media, packet, Error, Frame}; 10 | 11 | pub struct Encoder(pub Context); 12 | 13 | impl Encoder { 14 | pub fn video(mut self) -> Result { 15 | match self.medium() { 16 | media::Type::Unknown => { 17 | unsafe { 18 | (*self.as_mut_ptr()).codec_type = media::Type::Video.into(); 19 | } 20 | 21 | Ok(video::Video(self)) 22 | } 23 | 24 | media::Type::Video => Ok(video::Video(self)), 25 | 26 | _ => Err(Error::InvalidData), 27 | } 28 | } 29 | 30 | pub fn audio(mut self) -> Result { 31 | match self.medium() { 32 | media::Type::Unknown => { 33 | unsafe { 34 | (*self.as_mut_ptr()).codec_type = media::Type::Audio.into(); 35 | } 36 | 37 | Ok(audio::Audio(self)) 38 | } 39 | 40 | media::Type::Audio => Ok(audio::Audio(self)), 41 | 42 | _ => Err(Error::InvalidData), 43 | } 44 | } 45 | 46 | pub fn subtitle(mut self) -> Result { 47 | match self.medium() { 48 | media::Type::Unknown => { 49 | unsafe { 50 | (*self.as_mut_ptr()).codec_type = media::Type::Subtitle.into(); 51 | } 52 | 53 | Ok(subtitle::Subtitle(self)) 54 | } 55 | 56 | media::Type::Subtitle => Ok(subtitle::Subtitle(self)), 57 | 58 | _ => Err(Error::InvalidData), 59 | } 60 | } 61 | 62 | pub fn send_frame(&mut self, frame: &Frame) -> Result<(), Error> { 63 | unsafe { 64 | match avcodec_send_frame(self.as_mut_ptr(), frame.as_ptr()) { 65 | e if e < 0 => Err(Error::from(e)), 66 | _ => Ok(()), 67 | } 68 | } 69 | } 70 | 71 | /// Sends a NULL packet to the encoder to signal end of stream and enter 72 | /// draining mode. 73 | pub fn send_eof(&mut self) -> Result<(), Error> { 74 | unsafe { self.send_frame(&Frame::wrap(ptr::null_mut())) } 75 | } 76 | 77 | pub fn receive_packet(&mut self, packet: &mut P) -> Result<(), Error> { 78 | unsafe { 79 | match avcodec_receive_packet(self.as_mut_ptr(), packet.as_mut_ptr()) { 80 | e if e < 0 => Err(Error::from(e)), 81 | _ => Ok(()), 82 | } 83 | } 84 | } 85 | 86 | pub fn set_bit_rate(&mut self, value: usize) { 87 | unsafe { 88 | (*self.as_mut_ptr()).bit_rate = value as i64; 89 | } 90 | } 91 | 92 | pub fn set_max_bit_rate(&mut self, value: usize) { 93 | unsafe { 94 | (*self.as_mut_ptr()).rc_max_rate = value as i64; 95 | } 96 | } 97 | 98 | pub fn set_tolerance(&mut self, value: usize) { 99 | unsafe { 100 | (*self.as_mut_ptr()).bit_rate_tolerance = value as c_int; 101 | } 102 | } 103 | 104 | pub fn set_quality(&mut self, value: usize) { 105 | unsafe { 106 | (*self.as_mut_ptr()).global_quality = value as c_int; 107 | } 108 | } 109 | 110 | pub fn set_compression(&mut self, value: Option) { 111 | unsafe { 112 | if let Some(value) = value { 113 | (*self.as_mut_ptr()).compression_level = value as c_int; 114 | } else { 115 | (*self.as_mut_ptr()).compression_level = -1; 116 | } 117 | } 118 | } 119 | } 120 | 121 | impl Deref for Encoder { 122 | type Target = Context; 123 | 124 | fn deref(&self) -> &::Target { 125 | &self.0 126 | } 127 | } 128 | 129 | impl DerefMut for Encoder { 130 | fn deref_mut(&mut self) -> &mut ::Target { 131 | &mut self.0 132 | } 133 | } 134 | 135 | impl AsRef for Encoder { 136 | fn as_ref(&self) -> &Context { 137 | self 138 | } 139 | } 140 | 141 | impl AsMut for Encoder { 142 | fn as_mut(&mut self) -> &mut Context { 143 | &mut *self 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/codec/encoder/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod encoder; 2 | pub use self::encoder::Encoder; 3 | 4 | pub mod video; 5 | pub use self::video::Encoder as Video; 6 | 7 | pub mod audio; 8 | pub use self::audio::Encoder as Audio; 9 | 10 | pub mod subtitle; 11 | pub use self::subtitle::Encoder as Subtitle; 12 | 13 | pub mod motion_estimation; 14 | pub use self::motion_estimation::MotionEstimation; 15 | 16 | #[cfg(not(feature = "ffmpeg_5_0"))] 17 | pub mod prediction; 18 | #[cfg(not(feature = "ffmpeg_5_0"))] 19 | pub use self::prediction::Prediction; 20 | 21 | pub mod comparison; 22 | pub use self::comparison::Comparison; 23 | 24 | pub mod decision; 25 | pub use self::decision::Decision; 26 | 27 | use std::ffi::CString; 28 | 29 | use codec::Context; 30 | use codec::Id; 31 | use ffi::*; 32 | use Codec; 33 | 34 | pub fn new() -> Encoder { 35 | Context::new().encoder() 36 | } 37 | 38 | pub fn find(id: Id) -> Option { 39 | unsafe { 40 | // We get a clippy warning in 4.4 but not in 5.0 and newer, so we allow that cast to not complicate the code 41 | #[allow(clippy::unnecessary_cast)] 42 | let ptr = avcodec_find_encoder(id.into()) as *mut AVCodec; 43 | 44 | if ptr.is_null() { 45 | None 46 | } else { 47 | Some(Codec::wrap(ptr)) 48 | } 49 | } 50 | } 51 | 52 | pub fn find_by_name(name: &str) -> Option { 53 | unsafe { 54 | let name = CString::new(name).unwrap(); 55 | #[allow(clippy::unnecessary_cast)] 56 | let ptr = avcodec_find_encoder_by_name(name.as_ptr()) as *mut AVCodec; 57 | 58 | if ptr.is_null() { 59 | None 60 | } else { 61 | Some(Codec::wrap(ptr)) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/codec/encoder/motion_estimation.rs: -------------------------------------------------------------------------------- 1 | use libc::c_int; 2 | 3 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 4 | pub enum MotionEstimation { 5 | Zero, 6 | Full, 7 | Log, 8 | Phods, 9 | Epzs, 10 | X1, 11 | Hex, 12 | Umh, 13 | Iter, 14 | Tesa, 15 | } 16 | 17 | impl From for MotionEstimation { 18 | fn from(value: c_int) -> MotionEstimation { 19 | match value { 20 | 1 => MotionEstimation::Zero, 21 | 2 => MotionEstimation::Full, 22 | 3 => MotionEstimation::Log, 23 | 4 => MotionEstimation::Phods, 24 | 5 => MotionEstimation::Epzs, 25 | 6 => MotionEstimation::X1, 26 | 7 => MotionEstimation::Hex, 27 | 8 => MotionEstimation::Umh, 28 | 9 => MotionEstimation::Iter, 29 | 10 => MotionEstimation::Tesa, 30 | 31 | _ => MotionEstimation::Zero, 32 | } 33 | } 34 | } 35 | 36 | impl From for c_int { 37 | fn from(value: MotionEstimation) -> c_int { 38 | match value { 39 | MotionEstimation::Zero => 1, 40 | MotionEstimation::Full => 2, 41 | MotionEstimation::Log => 3, 42 | MotionEstimation::Phods => 4, 43 | MotionEstimation::Epzs => 5, 44 | MotionEstimation::X1 => 6, 45 | MotionEstimation::Hex => 7, 46 | MotionEstimation::Umh => 8, 47 | MotionEstimation::Iter => 9, 48 | MotionEstimation::Tesa => 10, 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/codec/encoder/prediction.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Prediction { 6 | Left, 7 | Plane, 8 | Median, 9 | } 10 | 11 | impl From for Prediction { 12 | fn from(value: c_int) -> Prediction { 13 | match value { 14 | FF_PRED_LEFT => Prediction::Left, 15 | FF_PRED_PLANE => Prediction::Plane, 16 | FF_PRED_MEDIAN => Prediction::Median, 17 | 18 | _ => Prediction::Left, 19 | } 20 | } 21 | } 22 | 23 | impl From for c_int { 24 | fn from(value: Prediction) -> c_int { 25 | match value { 26 | Prediction::Left => FF_PRED_LEFT, 27 | Prediction::Plane => FF_PRED_PLANE, 28 | Prediction::Median => FF_PRED_MEDIAN, 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/codec/encoder/subtitle.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | use std::ptr; 3 | 4 | use ffi::*; 5 | use libc::c_int; 6 | 7 | use super::Encoder as Super; 8 | use codec::{traits, Context}; 9 | use {Dictionary, Error}; 10 | 11 | pub struct Subtitle(pub Super); 12 | 13 | impl Subtitle { 14 | pub fn open(mut self) -> Result { 15 | unsafe { 16 | match avcodec_open2(self.as_mut_ptr(), ptr::null(), ptr::null_mut()) { 17 | 0 => Ok(Encoder(self)), 18 | e => Err(Error::from(e)), 19 | } 20 | } 21 | } 22 | 23 | pub fn open_as(mut self, codec: E) -> Result { 24 | unsafe { 25 | if let Some(codec) = codec.encoder() { 26 | match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) { 27 | 0 => Ok(Encoder(self)), 28 | e => Err(Error::from(e)), 29 | } 30 | } else { 31 | Err(Error::EncoderNotFound) 32 | } 33 | } 34 | } 35 | 36 | pub fn open_as_with( 37 | mut self, 38 | codec: E, 39 | options: Dictionary, 40 | ) -> Result { 41 | unsafe { 42 | if let Some(codec) = codec.encoder() { 43 | let mut opts = options.disown(); 44 | let res = avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), &mut opts); 45 | 46 | Dictionary::own(opts); 47 | 48 | match res { 49 | 0 => Ok(Encoder(self)), 50 | e => Err(Error::from(e)), 51 | } 52 | } else { 53 | Err(Error::EncoderNotFound) 54 | } 55 | } 56 | } 57 | } 58 | 59 | impl Deref for Subtitle { 60 | type Target = Super; 61 | 62 | fn deref(&self) -> &::Target { 63 | &self.0 64 | } 65 | } 66 | 67 | impl DerefMut for Subtitle { 68 | fn deref_mut(&mut self) -> &mut ::Target { 69 | &mut self.0 70 | } 71 | } 72 | 73 | impl AsRef for Subtitle { 74 | fn as_ref(&self) -> &Context { 75 | self 76 | } 77 | } 78 | 79 | impl AsMut for Subtitle { 80 | fn as_mut(&mut self) -> &mut Context { 81 | &mut self.0 82 | } 83 | } 84 | 85 | pub struct Encoder(pub Subtitle); 86 | 87 | impl Encoder { 88 | pub fn encode(&mut self, subtitle: &::Subtitle, out: &mut [u8]) -> Result { 89 | unsafe { 90 | match avcodec_encode_subtitle( 91 | self.0.as_mut_ptr(), 92 | out.as_mut_ptr(), 93 | out.len() as c_int, 94 | subtitle.as_ptr(), 95 | ) { 96 | e if e < 0 => Err(Error::from(e)), 97 | _ => Ok(true), 98 | } 99 | } 100 | } 101 | } 102 | 103 | impl Deref for Encoder { 104 | type Target = Subtitle; 105 | 106 | fn deref(&self) -> &::Target { 107 | &self.0 108 | } 109 | } 110 | 111 | impl DerefMut for Encoder { 112 | fn deref_mut(&mut self) -> &mut ::Target { 113 | &mut self.0 114 | } 115 | } 116 | 117 | impl AsRef for Encoder { 118 | fn as_ref(&self) -> &Context { 119 | self 120 | } 121 | } 122 | 123 | impl AsMut for Encoder { 124 | fn as_mut(&mut self) -> &mut Context { 125 | &mut self.0 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/codec/field_order.rs: -------------------------------------------------------------------------------- 1 | use ffi::AVFieldOrder::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum FieldOrder { 6 | Unknown, 7 | Progressive, 8 | TT, 9 | BB, 10 | TB, 11 | BT, 12 | } 13 | 14 | impl From for FieldOrder { 15 | fn from(value: AVFieldOrder) -> Self { 16 | match value { 17 | AV_FIELD_UNKNOWN => FieldOrder::Unknown, 18 | AV_FIELD_PROGRESSIVE => FieldOrder::Progressive, 19 | AV_FIELD_TT => FieldOrder::TT, 20 | AV_FIELD_BB => FieldOrder::BB, 21 | AV_FIELD_TB => FieldOrder::TB, 22 | AV_FIELD_BT => FieldOrder::BT, 23 | } 24 | } 25 | } 26 | 27 | impl From for AVFieldOrder { 28 | fn from(value: FieldOrder) -> AVFieldOrder { 29 | match value { 30 | FieldOrder::Unknown => AV_FIELD_UNKNOWN, 31 | FieldOrder::Progressive => AV_FIELD_PROGRESSIVE, 32 | FieldOrder::TT => AV_FIELD_TT, 33 | FieldOrder::BB => AV_FIELD_BB, 34 | FieldOrder::TB => AV_FIELD_TB, 35 | FieldOrder::BT => AV_FIELD_BT, 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/codec/flag.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_uint; 3 | 4 | bitflags! { 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 6 | pub struct Flags: c_uint { 7 | const UNALIGNED = AV_CODEC_FLAG_UNALIGNED; 8 | const QSCALE = AV_CODEC_FLAG_QSCALE; 9 | const _4MV = AV_CODEC_FLAG_4MV; 10 | const OUTPUT_CORRUPT = AV_CODEC_FLAG_OUTPUT_CORRUPT; 11 | const QPEL = AV_CODEC_FLAG_QPEL; 12 | const PASS1 = AV_CODEC_FLAG_PASS1; 13 | const PASS2 = AV_CODEC_FLAG_PASS2; 14 | const GRAY = AV_CODEC_FLAG_GRAY; 15 | const PSNR = AV_CODEC_FLAG_PSNR; 16 | #[cfg(not(feature = "ffmpeg_6_0"))] 17 | const TRUNCATED = AV_CODEC_FLAG_TRUNCATED; 18 | const INTERLACED_DCT = AV_CODEC_FLAG_INTERLACED_DCT; 19 | const LOW_DELAY = AV_CODEC_FLAG_LOW_DELAY; 20 | const GLOBAL_HEADER = AV_CODEC_FLAG_GLOBAL_HEADER; 21 | const BITEXACT = AV_CODEC_FLAG_BITEXACT; 22 | const AC_PRED = AV_CODEC_FLAG_AC_PRED; 23 | const LOOP_FILTER = AV_CODEC_FLAG_LOOP_FILTER; 24 | const INTERLACED_ME = AV_CODEC_FLAG_INTERLACED_ME; 25 | const CLOSED_GOP = AV_CODEC_FLAG_CLOSED_GOP; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/codec/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod flag; 2 | pub use self::flag::Flags; 3 | 4 | pub mod id; 5 | pub use self::id::Id; 6 | 7 | pub mod packet; 8 | 9 | pub mod subtitle; 10 | 11 | #[cfg(not(feature = "ffmpeg_5_0"))] 12 | pub mod picture; 13 | 14 | pub mod discard; 15 | 16 | pub mod context; 17 | pub use self::context::Context; 18 | 19 | pub mod capabilities; 20 | pub use self::capabilities::Capabilities; 21 | 22 | pub mod codec; 23 | 24 | pub mod parameters; 25 | pub use self::parameters::Parameters; 26 | 27 | pub mod video; 28 | pub use self::video::Video; 29 | 30 | pub mod audio; 31 | pub use self::audio::Audio; 32 | 33 | pub mod audio_service; 34 | pub mod field_order; 35 | 36 | pub mod compliance; 37 | pub use self::compliance::Compliance; 38 | 39 | pub mod debug; 40 | pub use self::debug::Debug; 41 | 42 | pub mod profile; 43 | pub use self::profile::Profile; 44 | 45 | pub mod threading; 46 | 47 | pub mod decoder; 48 | pub mod encoder; 49 | pub mod traits; 50 | 51 | use std::ffi::CStr; 52 | use std::str::from_utf8_unchecked; 53 | 54 | use ffi::*; 55 | 56 | pub fn version() -> u32 { 57 | unsafe { avcodec_version() } 58 | } 59 | 60 | pub fn configuration() -> &'static str { 61 | unsafe { from_utf8_unchecked(CStr::from_ptr(avcodec_configuration()).to_bytes()) } 62 | } 63 | 64 | pub fn license() -> &'static str { 65 | unsafe { from_utf8_unchecked(CStr::from_ptr(avcodec_license()).to_bytes()) } 66 | } 67 | -------------------------------------------------------------------------------- /src/codec/packet/borrow.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ptr; 3 | 4 | use super::Ref; 5 | use ffi::*; 6 | use libc::c_int; 7 | 8 | pub struct Borrow<'a> { 9 | packet: AVPacket, 10 | data: &'a [u8], 11 | } 12 | 13 | impl<'a> Borrow<'a> { 14 | pub fn new(data: &[u8]) -> Borrow { 15 | unsafe { 16 | let mut packet: AVPacket = mem::zeroed(); 17 | 18 | packet.data = data.as_ptr() as *mut _; 19 | packet.size = data.len() as c_int; 20 | 21 | Borrow { packet, data } 22 | } 23 | } 24 | 25 | #[inline] 26 | pub fn size(&self) -> usize { 27 | self.packet.size as usize 28 | } 29 | 30 | #[inline] 31 | pub fn data(&self) -> Option<&[u8]> { 32 | Some(self.data) 33 | } 34 | } 35 | 36 | impl<'a> Ref for Borrow<'a> { 37 | fn as_ptr(&self) -> *const AVPacket { 38 | &self.packet 39 | } 40 | } 41 | 42 | impl<'a> Drop for Borrow<'a> { 43 | fn drop(&mut self) { 44 | unsafe { 45 | self.packet.data = ptr::null_mut(); 46 | self.packet.size = 0; 47 | 48 | av_packet_unref(&mut self.packet); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/codec/packet/flag.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 6 | pub struct Flags: c_int { 7 | const KEY = AV_PKT_FLAG_KEY; 8 | const CORRUPT = AV_PKT_FLAG_CORRUPT; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /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 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.rs: -------------------------------------------------------------------------------- 1 | use std::any::Any; 2 | use std::rc::Rc; 3 | 4 | use super::{Context, Id}; 5 | use ffi::*; 6 | use media; 7 | 8 | pub struct Parameters { 9 | ptr: *mut AVCodecParameters, 10 | owner: Option>, 11 | } 12 | 13 | unsafe impl Send for Parameters {} 14 | 15 | impl Parameters { 16 | pub unsafe fn wrap(ptr: *mut AVCodecParameters, owner: Option>) -> Self { 17 | Parameters { ptr, owner } 18 | } 19 | 20 | pub unsafe fn as_ptr(&self) -> *const AVCodecParameters { 21 | self.ptr as *const _ 22 | } 23 | 24 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVCodecParameters { 25 | self.ptr 26 | } 27 | } 28 | 29 | impl Parameters { 30 | pub fn new() -> Self { 31 | unsafe { 32 | Parameters { 33 | ptr: avcodec_parameters_alloc(), 34 | owner: None, 35 | } 36 | } 37 | } 38 | 39 | pub fn medium(&self) -> media::Type { 40 | unsafe { media::Type::from((*self.as_ptr()).codec_type) } 41 | } 42 | 43 | pub fn id(&self) -> Id { 44 | unsafe { Id::from((*self.as_ptr()).codec_id) } 45 | } 46 | } 47 | 48 | impl Default for Parameters { 49 | fn default() -> Self { 50 | Self::new() 51 | } 52 | } 53 | 54 | impl Drop for Parameters { 55 | fn drop(&mut self) { 56 | unsafe { 57 | if self.owner.is_none() { 58 | avcodec_parameters_free(&mut self.as_mut_ptr()); 59 | } 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> From for Parameters { 80 | fn from(context: C) -> Parameters { 81 | let mut parameters = Parameters::new(); 82 | let context = context.as_ref(); 83 | unsafe { 84 | avcodec_parameters_from_context(parameters.as_mut_ptr(), context.as_ptr()); 85 | } 86 | parameters 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/codec/subtitle/flag.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 6 | pub struct Flags: c_int { 7 | const FORCED = AV_SUBTITLE_FLAG_FORCED; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/codec/subtitle/rect.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::marker::PhantomData; 3 | use std::str::from_utf8_unchecked; 4 | 5 | use super::{Flags, Type}; 6 | use ffi::*; 7 | #[cfg(not(feature = "ffmpeg_5_0"))] 8 | use {format, Picture}; 9 | 10 | pub enum Rect<'a> { 11 | None(*const AVSubtitleRect), 12 | Bitmap(Bitmap<'a>), 13 | Text(Text<'a>), 14 | Ass(Ass<'a>), 15 | } 16 | 17 | impl<'a> Rect<'a> { 18 | pub unsafe fn wrap(ptr: *const AVSubtitleRect) -> Self { 19 | match Type::from((*ptr).type_) { 20 | Type::None => Rect::None(ptr), 21 | Type::Bitmap => Rect::Bitmap(Bitmap::wrap(ptr)), 22 | Type::Text => Rect::Text(Text::wrap(ptr)), 23 | Type::Ass => Rect::Ass(Ass::wrap(ptr)), 24 | } 25 | } 26 | 27 | pub unsafe fn as_ptr(&self) -> *const AVSubtitleRect { 28 | match *self { 29 | Rect::None(ptr) => ptr, 30 | Rect::Bitmap(ref b) => b.as_ptr(), 31 | Rect::Text(ref t) => t.as_ptr(), 32 | Rect::Ass(ref a) => a.as_ptr(), 33 | } 34 | } 35 | } 36 | 37 | impl<'a> Rect<'a> { 38 | pub fn flags(&self) -> Flags { 39 | unsafe { 40 | Flags::from_bits_truncate(match *self { 41 | Rect::None(ptr) => (*ptr).flags, 42 | Rect::Bitmap(ref b) => (*b.as_ptr()).flags, 43 | Rect::Text(ref t) => (*t.as_ptr()).flags, 44 | Rect::Ass(ref a) => (*a.as_ptr()).flags, 45 | }) 46 | } 47 | } 48 | } 49 | 50 | pub struct Bitmap<'a> { 51 | ptr: *const AVSubtitleRect, 52 | 53 | _marker: PhantomData<&'a ()>, 54 | } 55 | 56 | impl<'a> Bitmap<'a> { 57 | pub unsafe fn wrap(ptr: *const AVSubtitleRect) -> Self { 58 | Bitmap { 59 | ptr, 60 | _marker: PhantomData, 61 | } 62 | } 63 | 64 | pub unsafe fn as_ptr(&self) -> *const AVSubtitleRect { 65 | self.ptr 66 | } 67 | } 68 | 69 | impl<'a> Bitmap<'a> { 70 | pub fn x(&self) -> usize { 71 | unsafe { (*self.as_ptr()).x as usize } 72 | } 73 | 74 | pub fn y(&self) -> usize { 75 | unsafe { (*self.as_ptr()).y as usize } 76 | } 77 | 78 | pub fn width(&self) -> u32 { 79 | unsafe { (*self.as_ptr()).w as u32 } 80 | } 81 | 82 | pub fn height(&self) -> u32 { 83 | unsafe { (*self.as_ptr()).h as u32 } 84 | } 85 | 86 | pub fn colors(&self) -> usize { 87 | unsafe { (*self.as_ptr()).nb_colors as usize } 88 | } 89 | 90 | // XXX: must split Picture and PictureMut 91 | #[cfg(not(feature = "ffmpeg_5_0"))] 92 | pub fn picture(&self, format: format::Pixel) -> Picture<'a> { 93 | unsafe { 94 | Picture::wrap( 95 | &(*self.as_ptr()).pict as *const _ as *mut _, 96 | format, 97 | (*self.as_ptr()).w as u32, 98 | (*self.as_ptr()).h as u32, 99 | ) 100 | } 101 | } 102 | } 103 | 104 | pub struct Text<'a> { 105 | ptr: *const AVSubtitleRect, 106 | 107 | _marker: PhantomData<&'a ()>, 108 | } 109 | 110 | impl<'a> Text<'a> { 111 | pub unsafe fn wrap(ptr: *const AVSubtitleRect) -> Self { 112 | Text { 113 | ptr, 114 | _marker: PhantomData, 115 | } 116 | } 117 | 118 | pub unsafe fn as_ptr(&self) -> *const AVSubtitleRect { 119 | self.ptr 120 | } 121 | } 122 | 123 | impl<'a> Text<'a> { 124 | pub fn get(&self) -> &str { 125 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).text).to_bytes()) } 126 | } 127 | } 128 | 129 | pub struct Ass<'a> { 130 | ptr: *const AVSubtitleRect, 131 | 132 | _marker: PhantomData<&'a ()>, 133 | } 134 | 135 | impl<'a> Ass<'a> { 136 | pub unsafe fn wrap(ptr: *const AVSubtitleRect) -> Self { 137 | Ass { 138 | ptr, 139 | _marker: PhantomData, 140 | } 141 | } 142 | 143 | pub unsafe fn as_ptr(&self) -> *const AVSubtitleRect { 144 | self.ptr 145 | } 146 | } 147 | 148 | impl<'a> Ass<'a> { 149 | pub fn get(&self) -> &str { 150 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).ass).to_bytes()) } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/codec/subtitle/rect_mut.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CString; 2 | use std::ops::Deref; 3 | 4 | use super::{Ass, Bitmap, Flags, Text, Type}; 5 | use ffi::*; 6 | use libc::c_int; 7 | 8 | pub enum RectMut<'a> { 9 | None(*mut AVSubtitleRect), 10 | Bitmap(BitmapMut<'a>), 11 | Text(TextMut<'a>), 12 | Ass(AssMut<'a>), 13 | } 14 | 15 | impl<'a> RectMut<'a> { 16 | pub unsafe fn wrap(ptr: *mut AVSubtitleRect) -> Self { 17 | match Type::from((*ptr).type_) { 18 | Type::None => RectMut::None(ptr), 19 | Type::Bitmap => RectMut::Bitmap(BitmapMut::wrap(ptr)), 20 | Type::Text => RectMut::Text(TextMut::wrap(ptr)), 21 | Type::Ass => RectMut::Ass(AssMut::wrap(ptr)), 22 | } 23 | } 24 | 25 | pub unsafe fn as_ptr(&self) -> *const AVSubtitleRect { 26 | match *self { 27 | RectMut::None(ptr) => ptr as *const _, 28 | RectMut::Bitmap(ref b) => b.as_ptr(), 29 | RectMut::Text(ref t) => t.as_ptr(), 30 | RectMut::Ass(ref a) => a.as_ptr(), 31 | } 32 | } 33 | 34 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVSubtitleRect { 35 | match *self { 36 | RectMut::None(ptr) => ptr, 37 | RectMut::Bitmap(ref mut b) => b.as_mut_ptr(), 38 | RectMut::Text(ref mut t) => t.as_mut_ptr(), 39 | RectMut::Ass(ref mut a) => a.as_mut_ptr(), 40 | } 41 | } 42 | } 43 | 44 | impl<'a> RectMut<'a> { 45 | pub fn flags(&self) -> Flags { 46 | unsafe { 47 | Flags::from_bits_truncate(match *self { 48 | RectMut::None(ptr) => (*ptr).flags, 49 | RectMut::Bitmap(ref b) => (*b.as_ptr()).flags, 50 | RectMut::Text(ref t) => (*t.as_ptr()).flags, 51 | RectMut::Ass(ref a) => (*a.as_ptr()).flags, 52 | }) 53 | } 54 | } 55 | } 56 | 57 | pub struct BitmapMut<'a> { 58 | immutable: Bitmap<'a>, 59 | } 60 | 61 | impl<'a> BitmapMut<'a> { 62 | pub unsafe fn wrap(ptr: *mut AVSubtitleRect) -> Self { 63 | BitmapMut { 64 | immutable: Bitmap::wrap(ptr as *const _), 65 | } 66 | } 67 | 68 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVSubtitleRect { 69 | self.as_ptr() as *mut _ 70 | } 71 | } 72 | 73 | impl<'a> BitmapMut<'a> { 74 | pub fn set_x(&mut self, value: usize) { 75 | unsafe { 76 | (*self.as_mut_ptr()).x = value as c_int; 77 | } 78 | } 79 | 80 | pub fn set_y(&mut self, value: usize) { 81 | unsafe { 82 | (*self.as_mut_ptr()).y = value as c_int; 83 | } 84 | } 85 | 86 | pub fn set_width(&mut self, value: u32) { 87 | unsafe { 88 | (*self.as_mut_ptr()).w = value as c_int; 89 | } 90 | } 91 | 92 | pub fn set_height(&mut self, value: u32) { 93 | unsafe { 94 | (*self.as_mut_ptr()).h = value as c_int; 95 | } 96 | } 97 | 98 | pub fn set_colors(&mut self, value: usize) { 99 | unsafe { 100 | (*self.as_mut_ptr()).nb_colors = value as c_int; 101 | } 102 | } 103 | } 104 | 105 | impl<'a> Deref for BitmapMut<'a> { 106 | type Target = Bitmap<'a>; 107 | 108 | fn deref(&self) -> &Self::Target { 109 | &self.immutable 110 | } 111 | } 112 | 113 | pub struct TextMut<'a> { 114 | immutable: Text<'a>, 115 | } 116 | 117 | impl<'a> TextMut<'a> { 118 | pub unsafe fn wrap(ptr: *mut AVSubtitleRect) -> Self { 119 | TextMut { 120 | immutable: Text::wrap(ptr as *const _), 121 | } 122 | } 123 | 124 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVSubtitleRect { 125 | self.as_ptr() as *mut _ 126 | } 127 | } 128 | 129 | impl<'a> TextMut<'a> { 130 | pub fn set(&mut self, value: &str) { 131 | let value = CString::new(value).unwrap(); 132 | 133 | unsafe { 134 | (*self.as_mut_ptr()).text = av_strdup(value.as_ptr()); 135 | } 136 | } 137 | } 138 | 139 | impl<'a> Deref for TextMut<'a> { 140 | type Target = Text<'a>; 141 | 142 | fn deref(&self) -> &Self::Target { 143 | &self.immutable 144 | } 145 | } 146 | 147 | pub struct AssMut<'a> { 148 | immutable: Ass<'a>, 149 | } 150 | 151 | impl<'a> AssMut<'a> { 152 | pub unsafe fn wrap(ptr: *mut AVSubtitleRect) -> Self { 153 | AssMut { 154 | immutable: Ass::wrap(ptr), 155 | } 156 | } 157 | 158 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVSubtitleRect { 159 | self.as_ptr() as *mut _ 160 | } 161 | } 162 | 163 | impl<'a> AssMut<'a> { 164 | pub fn set(&mut self, value: &str) { 165 | let value = CString::new(value).unwrap(); 166 | 167 | unsafe { 168 | (*self.as_mut_ptr()).ass = av_strdup(value.as_ptr()); 169 | } 170 | } 171 | } 172 | 173 | impl<'a> Deref for AssMut<'a> { 174 | type Target = Ass<'a>; 175 | 176 | fn deref(&self) -> &Self::Target { 177 | &self.immutable 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/codec/threading.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub struct Config { 6 | pub kind: Type, 7 | pub count: usize, 8 | #[cfg(not(feature = "ffmpeg_6_0"))] 9 | pub safe: bool, 10 | } 11 | 12 | impl Config { 13 | pub fn kind(value: Type) -> Self { 14 | Config { 15 | kind: value, 16 | ..Default::default() 17 | } 18 | } 19 | 20 | pub fn count(value: usize) -> Self { 21 | Config { 22 | count: value, 23 | ..Default::default() 24 | } 25 | } 26 | 27 | #[cfg(not(feature = "ffmpeg_6_0"))] 28 | pub fn safe(value: bool) -> Self { 29 | Config { 30 | safe: value, 31 | ..Default::default() 32 | } 33 | } 34 | } 35 | 36 | impl Default for Config { 37 | fn default() -> Self { 38 | Config { 39 | kind: Type::None, 40 | count: 0, 41 | #[cfg(not(feature = "ffmpeg_6_0"))] 42 | safe: false, 43 | } 44 | } 45 | } 46 | 47 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 48 | pub enum Type { 49 | None, 50 | Frame, 51 | Slice, 52 | } 53 | 54 | impl From for Type { 55 | fn from(value: c_int) -> Type { 56 | match value { 57 | FF_THREAD_FRAME => Type::Frame, 58 | FF_THREAD_SLICE => Type::Slice, 59 | 60 | _ => Type::None, 61 | } 62 | } 63 | } 64 | 65 | impl From for c_int { 66 | fn from(value: Type) -> c_int { 67 | match value { 68 | Type::None => 0, 69 | Type::Frame => FF_THREAD_FRAME, 70 | Type::Slice => FF_THREAD_SLICE, 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/codec/traits.rs: -------------------------------------------------------------------------------- 1 | use super::{decoder, encoder}; 2 | use codec::{Audio, Id, Video}; 3 | use Codec; 4 | 5 | pub trait Decoder { 6 | fn decoder(self) -> Option; 7 | } 8 | 9 | impl<'a> Decoder for &'a str { 10 | fn decoder(self) -> Option { 11 | decoder::find_by_name(self) 12 | } 13 | } 14 | 15 | impl Decoder for Id { 16 | fn decoder(self) -> Option { 17 | decoder::find(self) 18 | } 19 | } 20 | 21 | impl Decoder for Codec { 22 | fn decoder(self) -> Option { 23 | if self.is_decoder() { 24 | Some(self) 25 | } else { 26 | None 27 | } 28 | } 29 | } 30 | 31 | impl Decoder for Option { 32 | fn decoder(self) -> Option { 33 | self.and_then(|c| c.decoder()) 34 | } 35 | } 36 | 37 | impl Decoder for Audio { 38 | fn decoder(self) -> Option { 39 | if self.is_decoder() { 40 | Some(*self) 41 | } else { 42 | None 43 | } 44 | } 45 | } 46 | 47 | impl Decoder for Video { 48 | fn decoder(self) -> Option { 49 | if self.is_decoder() { 50 | Some(*self) 51 | } else { 52 | None 53 | } 54 | } 55 | } 56 | 57 | pub trait Encoder { 58 | fn encoder(self) -> Option; 59 | } 60 | 61 | impl<'a> Encoder for &'a str { 62 | fn encoder(self) -> Option { 63 | encoder::find_by_name(self) 64 | } 65 | } 66 | 67 | impl Encoder for Id { 68 | fn encoder(self) -> Option { 69 | encoder::find(self) 70 | } 71 | } 72 | 73 | impl Encoder for Codec { 74 | fn encoder(self) -> Option { 75 | if self.is_encoder() { 76 | Some(self) 77 | } else { 78 | None 79 | } 80 | } 81 | } 82 | 83 | impl Encoder for Option { 84 | fn encoder(self) -> Option { 85 | self.and_then(|c| c.encoder()) 86 | } 87 | } 88 | 89 | impl Encoder for Audio { 90 | fn encoder(self) -> Option { 91 | if self.is_encoder() { 92 | Some(*self) 93 | } else { 94 | None 95 | } 96 | } 97 | } 98 | 99 | impl Encoder for Video { 100 | fn encoder(self) -> Option { 101 | if self.is_encoder() { 102 | Some(*self) 103 | } else { 104 | None 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/codec/video.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use super::codec::Codec; 4 | use ffi::*; 5 | use {format, Rational}; 6 | 7 | #[derive(PartialEq, Eq, Copy, Clone)] 8 | pub struct Video { 9 | codec: Codec, 10 | } 11 | 12 | impl Video { 13 | pub unsafe fn new(codec: Codec) -> Video { 14 | Video { codec } 15 | } 16 | } 17 | 18 | impl Video { 19 | pub fn rates(&self) -> Option { 20 | unsafe { 21 | if (*self.codec.as_ptr()).supported_framerates.is_null() { 22 | None 23 | } else { 24 | Some(RateIter::new((*self.codec.as_ptr()).supported_framerates)) 25 | } 26 | } 27 | } 28 | 29 | pub fn formats(&self) -> Option { 30 | unsafe { 31 | if (*self.codec.as_ptr()).pix_fmts.is_null() { 32 | None 33 | } else { 34 | Some(FormatIter::new((*self.codec.as_ptr()).pix_fmts)) 35 | } 36 | } 37 | } 38 | } 39 | 40 | impl Deref for Video { 41 | type Target = Codec; 42 | 43 | fn deref(&self) -> &Self::Target { 44 | &self.codec 45 | } 46 | } 47 | 48 | pub struct RateIter { 49 | ptr: *const AVRational, 50 | } 51 | 52 | impl RateIter { 53 | pub fn new(ptr: *const AVRational) -> Self { 54 | RateIter { ptr } 55 | } 56 | } 57 | 58 | impl Iterator for RateIter { 59 | type Item = Rational; 60 | 61 | fn next(&mut self) -> Option<::Item> { 62 | unsafe { 63 | if (*self.ptr).num == 0 && (*self.ptr).den == 0 { 64 | return None; 65 | } 66 | 67 | let rate = (*self.ptr).into(); 68 | self.ptr = self.ptr.offset(1); 69 | 70 | Some(rate) 71 | } 72 | } 73 | } 74 | 75 | pub struct FormatIter { 76 | ptr: *const AVPixelFormat, 77 | } 78 | 79 | impl FormatIter { 80 | pub fn new(ptr: *const AVPixelFormat) -> Self { 81 | FormatIter { ptr } 82 | } 83 | } 84 | 85 | impl Iterator for FormatIter { 86 | type Item = format::Pixel; 87 | 88 | fn next(&mut self) -> Option<::Item> { 89 | unsafe { 90 | if *self.ptr == AVPixelFormat::AV_PIX_FMT_NONE { 91 | return None; 92 | } 93 | 94 | let format = (*self.ptr).into(); 95 | self.ptr = self.ptr.offset(1); 96 | 97 | Some(format) 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/device/extensions.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::ptr; 3 | 4 | use device; 5 | use ffi::*; 6 | use format::context::common::Context; 7 | use libc::c_int; 8 | use Error; 9 | 10 | impl Context { 11 | pub fn devices(&self) -> Result { 12 | unsafe { DeviceIter::wrap(self.as_ptr()) } 13 | } 14 | } 15 | 16 | pub struct DeviceIter<'a> { 17 | ptr: *mut AVDeviceInfoList, 18 | cur: c_int, 19 | 20 | _marker: PhantomData<&'a ()>, 21 | } 22 | 23 | impl<'a> DeviceIter<'a> { 24 | pub unsafe fn wrap(ctx: *const AVFormatContext) -> Result { 25 | let mut ptr: *mut AVDeviceInfoList = ptr::null_mut(); 26 | 27 | match avdevice_list_devices(ctx as *mut _, &mut ptr) { 28 | n if n < 0 => Err(Error::from(n)), 29 | 30 | _ => Ok(DeviceIter { 31 | ptr, 32 | cur: 0, 33 | _marker: PhantomData, 34 | }), 35 | } 36 | } 37 | } 38 | 39 | impl<'a> DeviceIter<'a> { 40 | pub fn default(&self) -> usize { 41 | unsafe { (*self.ptr).default_device as usize } 42 | } 43 | } 44 | 45 | impl<'a> Drop for DeviceIter<'a> { 46 | fn drop(&mut self) { 47 | unsafe { 48 | avdevice_free_list_devices(&mut self.ptr); 49 | } 50 | } 51 | } 52 | 53 | impl<'a> Iterator for DeviceIter<'a> { 54 | type Item = device::Info<'a>; 55 | 56 | fn next(&mut self) -> Option<::Item> { 57 | unsafe { 58 | if self.cur >= (*self.ptr).nb_devices { 59 | None 60 | } else { 61 | self.cur += 1; 62 | Some(device::Info::wrap( 63 | *(*self.ptr).devices.offset((self.cur - 1) as isize), 64 | )) 65 | } 66 | } 67 | } 68 | 69 | fn size_hint(&self) -> (usize, Option) { 70 | unsafe { 71 | let length = (*self.ptr).nb_devices as usize; 72 | 73 | (length - self.cur as usize, Some(length - self.cur as usize)) 74 | } 75 | } 76 | } 77 | 78 | impl<'a> ExactSizeIterator for DeviceIter<'a> {} 79 | -------------------------------------------------------------------------------- /src/device/input.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use ffi::*; 4 | use format; 5 | use Format; 6 | 7 | pub struct AudioIter(*mut AVInputFormat); 8 | 9 | impl Iterator for AudioIter { 10 | type Item = Format; 11 | 12 | fn next(&mut self) -> Option<::Item> { 13 | unsafe { 14 | // We get a clippy warning in 4.4 but not in 5.0 and newer, so we allow that cast to not complicate the code 15 | #[allow(clippy::unnecessary_cast)] 16 | let ptr = av_input_audio_device_next(self.0) as *mut AVInputFormat; 17 | 18 | if ptr.is_null() && !self.0.is_null() { 19 | None 20 | } else { 21 | self.0 = ptr; 22 | 23 | Some(Format::Input(format::Input::wrap(ptr))) 24 | } 25 | } 26 | } 27 | } 28 | 29 | pub fn audio() -> AudioIter { 30 | AudioIter(ptr::null_mut()) 31 | } 32 | 33 | pub struct VideoIter(*mut AVInputFormat); 34 | 35 | impl Iterator for VideoIter { 36 | type Item = Format; 37 | 38 | fn next(&mut self) -> Option<::Item> { 39 | unsafe { 40 | // We get a clippy warning in 4.4 but not in 5.0 and newer, so we allow that cast to not complicate the code 41 | #[allow(clippy::unnecessary_cast)] 42 | let ptr = av_input_video_device_next(self.0) as *mut AVInputFormat; 43 | 44 | if ptr.is_null() && !self.0.is_null() { 45 | None 46 | } else { 47 | self.0 = ptr; 48 | 49 | Some(Format::Input(format::Input::wrap(ptr))) 50 | } 51 | } 52 | } 53 | } 54 | 55 | pub fn video() -> VideoIter { 56 | VideoIter(ptr::null_mut()) 57 | } 58 | -------------------------------------------------------------------------------- /src/device/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod extensions; 2 | pub mod input; 3 | pub mod output; 4 | 5 | use std::ffi::CStr; 6 | use std::marker::PhantomData; 7 | use std::str::from_utf8_unchecked; 8 | 9 | use ffi::*; 10 | 11 | pub struct Info<'a> { 12 | ptr: *mut AVDeviceInfo, 13 | 14 | _marker: PhantomData<&'a ()>, 15 | } 16 | 17 | impl<'a> Info<'a> { 18 | pub unsafe fn wrap(ptr: *mut AVDeviceInfo) -> Self { 19 | Info { 20 | ptr, 21 | _marker: PhantomData, 22 | } 23 | } 24 | 25 | pub unsafe fn as_ptr(&self) -> *const AVDeviceInfo { 26 | self.ptr as *const _ 27 | } 28 | 29 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVDeviceInfo { 30 | self.ptr 31 | } 32 | } 33 | 34 | impl<'a> Info<'a> { 35 | pub fn name(&self) -> &str { 36 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).device_name).to_bytes()) } 37 | } 38 | 39 | pub fn description(&self) -> &str { 40 | unsafe { 41 | from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).device_description).to_bytes()) 42 | } 43 | } 44 | } 45 | 46 | pub fn register_all() { 47 | unsafe { 48 | avdevice_register_all(); 49 | } 50 | } 51 | 52 | pub fn version() -> u32 { 53 | unsafe { avdevice_version() } 54 | } 55 | 56 | pub fn configuration() -> &'static str { 57 | unsafe { from_utf8_unchecked(CStr::from_ptr(avdevice_configuration()).to_bytes()) } 58 | } 59 | 60 | pub fn license() -> &'static str { 61 | unsafe { from_utf8_unchecked(CStr::from_ptr(avdevice_license()).to_bytes()) } 62 | } 63 | -------------------------------------------------------------------------------- /src/device/output.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use ffi::*; 4 | use format; 5 | use Format; 6 | 7 | pub struct AudioIter(*mut AVOutputFormat); 8 | 9 | impl Iterator for AudioIter { 10 | type Item = Format; 11 | 12 | fn next(&mut self) -> Option<::Item> { 13 | unsafe { 14 | // We get a clippy warning in 4.4 but not in 5.0 and newer, so we allow that cast to not complicate the code 15 | #[allow(clippy::unnecessary_cast)] 16 | let ptr = av_output_audio_device_next(self.0) as *mut AVOutputFormat; 17 | 18 | if ptr.is_null() && !self.0.is_null() { 19 | None 20 | } else { 21 | self.0 = ptr; 22 | 23 | Some(Format::Output(format::Output::wrap(ptr))) 24 | } 25 | } 26 | } 27 | } 28 | 29 | pub fn audio() -> AudioIter { 30 | AudioIter(ptr::null_mut()) 31 | } 32 | 33 | pub struct VideoIter(*mut AVOutputFormat); 34 | 35 | impl Iterator for VideoIter { 36 | type Item = Format; 37 | 38 | fn next(&mut self) -> Option<::Item> { 39 | unsafe { 40 | // We get a clippy warning in 4.4 but not in 5.0 and newer, so we allow that cast to not complicate the code 41 | #[allow(clippy::unnecessary_cast)] 42 | let ptr = av_output_video_device_next(self.0) as *mut AVOutputFormat; 43 | 44 | if ptr.is_null() && !self.0.is_null() { 45 | None 46 | } else { 47 | self.0 = ptr; 48 | 49 | Some(Format::Output(format::Output::wrap(ptr))) 50 | } 51 | } 52 | } 53 | } 54 | 55 | pub fn video() -> VideoIter { 56 | VideoIter(ptr::null_mut()) 57 | } 58 | -------------------------------------------------------------------------------- /src/filter/context/context.rs: -------------------------------------------------------------------------------- 1 | use super::{Sink, Source}; 2 | use ffi::*; 3 | use libc::c_void; 4 | use {format, option, ChannelLayout}; 5 | 6 | pub struct Context { 7 | ptr: *mut AVFilterContext, 8 | } 9 | 10 | impl Context { 11 | pub unsafe fn wrap(ptr: *mut AVFilterContext) -> Self { 12 | Context { ptr } 13 | } 14 | 15 | pub unsafe fn as_ptr(&self) -> *const AVFilterContext { 16 | self.ptr as *const _ 17 | } 18 | 19 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFilterContext { 20 | self.ptr 21 | } 22 | } 23 | 24 | impl Context { 25 | pub fn source(&mut self) -> Source { 26 | unsafe { Source::wrap(self) } 27 | } 28 | 29 | pub fn sink(&mut self) -> Sink { 30 | unsafe { Sink::wrap(self) } 31 | } 32 | 33 | pub fn set_pixel_format(&mut self, value: format::Pixel) { 34 | let _ = option::Settable::set::(self, "pix_fmts", &value.into()); 35 | } 36 | 37 | pub fn set_sample_format(&mut self, value: format::Sample) { 38 | let _ = option::Settable::set::(self, "sample_fmts", &value.into()); 39 | } 40 | 41 | pub fn set_sample_rate(&mut self, value: u32) { 42 | let _ = option::Settable::set(self, "sample_rates", &i64::from(value)); 43 | } 44 | 45 | pub fn set_channel_layout(&mut self, value: ChannelLayout) { 46 | #[cfg(not(feature = "ffmpeg_7_0"))] 47 | { 48 | let _ = option::Settable::set(self, "channel_layouts", &value.bits()); 49 | } 50 | #[cfg(feature = "ffmpeg_7_0")] 51 | { 52 | let _ = option::Settable::set_channel_layout(self, "channel_layouts", value); 53 | } 54 | } 55 | 56 | pub fn link(&mut self, srcpad: u32, dst: &mut Self, dstpad: u32) { 57 | unsafe { avfilter_link(self.as_mut_ptr(), srcpad, dst.as_mut_ptr(), dstpad) }; 58 | } 59 | } 60 | 61 | unsafe impl option::Target for Context { 62 | fn as_ptr(&self) -> *const c_void { 63 | self.ptr as *const _ 64 | } 65 | 66 | fn as_mut_ptr(&mut self) -> *mut c_void { 67 | self.ptr as *mut _ 68 | } 69 | } 70 | 71 | impl option::Settable for Context {} 72 | -------------------------------------------------------------------------------- /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 ffi::*; 3 | use libc::c_int; 4 | use {Error, Frame, Rational}; 5 | 6 | pub struct Sink<'a> { 7 | ctx: &'a mut Context, 8 | } 9 | 10 | impl<'a> Sink<'a> { 11 | pub unsafe fn wrap(ctx: &'a mut Context) -> Self { 12 | Self { 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 | pub fn time_base(&self) -> Rational { 46 | unsafe { av_buffersink_get_time_base(self.ctx.as_ptr()) }.into() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/filter/context/source.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use super::Context; 4 | use ffi::*; 5 | use {Error, Frame}; 6 | 7 | pub struct Source<'a> { 8 | ctx: &'a mut Context, 9 | } 10 | 11 | impl<'a> Source<'a> { 12 | pub unsafe fn wrap(ctx: &'a mut Context) -> Self { 13 | Self { 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::ffi::CStr; 2 | use std::marker::PhantomData; 3 | use std::str::from_utf8_unchecked; 4 | 5 | use super::{Flags, Pad}; 6 | use ffi::*; 7 | 8 | pub struct Filter { 9 | ptr: *mut AVFilter, 10 | } 11 | 12 | impl Filter { 13 | pub unsafe fn wrap(ptr: *mut AVFilter) -> Self { 14 | Filter { ptr } 15 | } 16 | 17 | pub unsafe fn as_ptr(&self) -> *const AVFilter { 18 | self.ptr as *const _ 19 | } 20 | 21 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFilter { 22 | self.ptr 23 | } 24 | } 25 | 26 | impl Filter { 27 | pub fn name(&self) -> &str { 28 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).name).to_bytes()) } 29 | } 30 | 31 | pub fn description(&self) -> Option<&str> { 32 | unsafe { 33 | let ptr = (*self.as_ptr()).description; 34 | 35 | if ptr.is_null() { 36 | None 37 | } else { 38 | Some(from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) 39 | } 40 | } 41 | } 42 | 43 | pub fn inputs(&self) -> Option { 44 | unsafe { 45 | let ptr = (*self.as_ptr()).inputs; 46 | 47 | if ptr.is_null() { 48 | None 49 | } else { 50 | #[cfg(not(feature = "ffmpeg_6_0"))] 51 | let nb_inputs = avfilter_pad_count((*self.as_ptr()).inputs) as isize; 52 | #[cfg(feature = "ffmpeg_6_0")] 53 | let nb_inputs = (*self.as_ptr()).nb_inputs as isize; 54 | 55 | Some(PadIter::new((*self.as_ptr()).inputs, nb_inputs)) 56 | } 57 | } 58 | } 59 | 60 | pub fn outputs(&self) -> Option { 61 | unsafe { 62 | let ptr = (*self.as_ptr()).outputs; 63 | 64 | if ptr.is_null() { 65 | None 66 | } else { 67 | #[cfg(not(feature = "ffmpeg_6_0"))] 68 | let nb_outputs = avfilter_pad_count((*self.as_ptr()).outputs) as isize; 69 | #[cfg(feature = "ffmpeg_6_0")] 70 | let nb_outputs = (*self.as_ptr()).nb_outputs as isize; 71 | 72 | Some(PadIter::new((*self.as_ptr()).outputs, nb_outputs)) 73 | } 74 | } 75 | } 76 | 77 | pub fn flags(&self) -> Flags { 78 | unsafe { Flags::from_bits_truncate((*self.as_ptr()).flags) } 79 | } 80 | } 81 | 82 | pub struct PadIter<'a> { 83 | ptr: *const AVFilterPad, 84 | count: isize, 85 | cur: isize, 86 | 87 | _marker: PhantomData<&'a ()>, 88 | } 89 | 90 | impl<'a> PadIter<'a> { 91 | pub fn new(ptr: *const AVFilterPad, count: isize) -> Self { 92 | PadIter { 93 | ptr, 94 | count, 95 | cur: 0, 96 | _marker: PhantomData, 97 | } 98 | } 99 | } 100 | 101 | impl<'a> Iterator for PadIter<'a> { 102 | type Item = Pad<'a>; 103 | 104 | fn next(&mut self) -> Option { 105 | unsafe { 106 | if self.cur >= self.count { 107 | return None; 108 | } 109 | 110 | let pad = Pad::wrap(self.ptr, self.cur); 111 | self.cur += 1; 112 | 113 | Some(pad) 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/filter/flag.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 6 | pub struct Flags: c_int { 7 | const DYNAMIC_INPUTS = AVFILTER_FLAG_DYNAMIC_INPUTS; 8 | const DYNAMIC_OUTPUTS = AVFILTER_FLAG_DYNAMIC_OUTPUTS; 9 | const SLICE_THREADS = AVFILTER_FLAG_SLICE_THREADS; 10 | const SUPPORT_TIMELINE_GENERIC = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC; 11 | const SUPPORT_TIMELINE_INTERNAL = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL; 12 | const SUPPORT_TIMELINE = AVFILTER_FLAG_SUPPORT_TIMELINE; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/filter/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod flag; 2 | pub use self::flag::Flags; 3 | 4 | pub mod pad; 5 | pub use self::pad::Pad; 6 | 7 | pub mod filter; 8 | pub use self::filter::Filter; 9 | 10 | pub mod context; 11 | pub use self::context::{Context, Sink, Source}; 12 | 13 | pub mod graph; 14 | pub use self::graph::Graph; 15 | 16 | use std::ffi::{CStr, CString}; 17 | use std::str::from_utf8_unchecked; 18 | 19 | use ffi::*; 20 | #[cfg(not(feature = "ffmpeg_5_0"))] 21 | use Error; 22 | 23 | #[cfg(not(feature = "ffmpeg_5_0"))] 24 | pub fn register_all() { 25 | unsafe { 26 | avfilter_register_all(); 27 | } 28 | } 29 | 30 | #[cfg(not(feature = "ffmpeg_5_0"))] 31 | pub fn register(filter: &Filter) -> Result<(), Error> { 32 | unsafe { 33 | match avfilter_register(filter.as_ptr() as *mut _) { 34 | 0 => Ok(()), 35 | _ => Err(Error::InvalidData), 36 | } 37 | } 38 | } 39 | 40 | pub fn version() -> u32 { 41 | unsafe { avfilter_version() } 42 | } 43 | 44 | pub fn configuration() -> &'static str { 45 | unsafe { from_utf8_unchecked(CStr::from_ptr(avfilter_configuration()).to_bytes()) } 46 | } 47 | 48 | pub fn license() -> &'static str { 49 | unsafe { from_utf8_unchecked(CStr::from_ptr(avfilter_license()).to_bytes()) } 50 | } 51 | 52 | pub fn find(name: &str) -> Option { 53 | unsafe { 54 | let name = CString::new(name).unwrap(); 55 | let ptr = avfilter_get_by_name(name.as_ptr()); 56 | 57 | if ptr.is_null() { 58 | None 59 | } else { 60 | Some(Filter::wrap(ptr as *mut _)) 61 | } 62 | } 63 | } 64 | 65 | #[cfg(test)] 66 | mod tests { 67 | use super::*; 68 | 69 | #[test] 70 | fn test_paditer() { 71 | #[cfg(not(feature = "ffmpeg_5_0"))] 72 | register_all(); 73 | assert_eq!( 74 | find("overlay") 75 | .unwrap() 76 | .inputs() 77 | .unwrap() 78 | .map(|input| input.name().unwrap().to_string()) 79 | .collect::>(), 80 | vec!("main", "overlay") 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/filter/pad.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::marker::PhantomData; 3 | use std::str::from_utf8_unchecked; 4 | 5 | use ffi::*; 6 | use media; 7 | 8 | pub struct Pad<'a> { 9 | ptr: *const AVFilterPad, 10 | idx: isize, 11 | 12 | _marker: PhantomData<&'a ()>, 13 | } 14 | 15 | impl<'a> Pad<'a> { 16 | pub unsafe fn wrap(ptr: *const AVFilterPad, idx: isize) -> Self { 17 | Pad { 18 | ptr, 19 | idx, 20 | _marker: PhantomData, 21 | } 22 | } 23 | 24 | pub unsafe fn as_ptr(&self) -> *const AVFilterPad { 25 | self.ptr 26 | } 27 | 28 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFilterPad { 29 | self.ptr as *mut _ 30 | } 31 | } 32 | 33 | impl<'a> Pad<'a> { 34 | pub fn name(&self) -> Option<&str> { 35 | unsafe { 36 | let ptr = avfilter_pad_get_name(self.ptr, self.idx as i32); 37 | 38 | if ptr.is_null() { 39 | None 40 | } else { 41 | Some(from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) 42 | } 43 | } 44 | } 45 | 46 | pub fn medium(&self) -> media::Type { 47 | unsafe { media::Type::from(avfilter_pad_get_type(self.ptr, self.idx as i32)) } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/format/chapter/chapter.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use {DictionaryRef, Rational}; 3 | 4 | use format::context::common::Context; 5 | 6 | // WARNING: index refers to the offset in the chapters array (starting from 0) 7 | // it is not necessarly equal to the id (which may start at 1) 8 | pub struct Chapter<'a> { 9 | context: &'a Context, 10 | index: usize, 11 | } 12 | 13 | impl<'a> Chapter<'a> { 14 | pub unsafe fn wrap(context: &Context, index: usize) -> Chapter { 15 | Chapter { context, index } 16 | } 17 | 18 | pub unsafe fn as_ptr(&self) -> *const AVChapter { 19 | *(*self.context.as_ptr()).chapters.add(self.index) 20 | } 21 | } 22 | 23 | impl<'a> Chapter<'a> { 24 | pub fn index(&self) -> usize { 25 | self.index 26 | } 27 | 28 | pub fn id(&self) -> i64 { 29 | #[allow(clippy::unnecessary_cast)] 30 | unsafe { 31 | (*self.as_ptr()).id as i64 32 | } 33 | } 34 | 35 | pub fn time_base(&self) -> Rational { 36 | unsafe { Rational::from((*self.as_ptr()).time_base) } 37 | } 38 | 39 | pub fn start(&self) -> i64 { 40 | unsafe { (*self.as_ptr()).start } 41 | } 42 | 43 | pub fn end(&self) -> i64 { 44 | unsafe { (*self.as_ptr()).end } 45 | } 46 | 47 | pub fn metadata(&self) -> DictionaryRef { 48 | unsafe { DictionaryRef::wrap((*self.as_ptr()).metadata) } 49 | } 50 | } 51 | 52 | impl<'a> PartialEq for Chapter<'a> { 53 | fn eq(&self, other: &Self) -> bool { 54 | unsafe { self.as_ptr() == other.as_ptr() } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/format/chapter/chapter_mut.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ops::Deref; 3 | 4 | use super::Chapter; 5 | use ffi::*; 6 | use format::context::common::Context; 7 | use {Dictionary, DictionaryMut, Rational}; 8 | 9 | // WARNING: index refers to the offset in the chapters array (starting from 0) 10 | // it is not necessarly equal to the id (which may start at 1) 11 | pub struct ChapterMut<'a> { 12 | context: &'a mut Context, 13 | index: usize, 14 | 15 | immutable: Chapter<'a>, 16 | } 17 | 18 | impl<'a> ChapterMut<'a> { 19 | pub unsafe fn wrap(context: &mut Context, index: usize) -> ChapterMut { 20 | ChapterMut { 21 | context: mem::transmute_copy(&context), 22 | index, 23 | 24 | immutable: Chapter::wrap(mem::transmute_copy(&context), index), 25 | } 26 | } 27 | 28 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVChapter { 29 | *(*self.context.as_mut_ptr()).chapters.add(self.index) 30 | } 31 | } 32 | 33 | impl<'a> ChapterMut<'a> { 34 | pub fn set_id(&mut self, value: i64) { 35 | unsafe { 36 | (*self.as_mut_ptr()).id = value as _; 37 | } 38 | } 39 | 40 | pub fn set_time_base>(&mut self, value: R) { 41 | unsafe { 42 | (*self.as_mut_ptr()).time_base = value.into().into(); 43 | } 44 | } 45 | 46 | pub fn set_start(&mut self, value: i64) { 47 | unsafe { 48 | (*self.as_mut_ptr()).start = value; 49 | } 50 | } 51 | 52 | pub fn set_end(&mut self, value: i64) { 53 | unsafe { 54 | (*self.as_mut_ptr()).end = value; 55 | } 56 | } 57 | 58 | pub fn set_metadata, V: AsRef>(&mut self, key: K, value: V) { 59 | // dictionary.set() allocates the AVDictionary the first time a key/value is inserted 60 | // so we want to update the metadata dictionary afterwards 61 | unsafe { 62 | let mut dictionary = Dictionary::own(self.metadata().as_mut_ptr()); 63 | dictionary.set(key.as_ref(), value.as_ref()); 64 | (*self.as_mut_ptr()).metadata = dictionary.disown(); 65 | } 66 | } 67 | 68 | pub fn metadata(&mut self) -> DictionaryMut { 69 | unsafe { DictionaryMut::wrap((*self.as_mut_ptr()).metadata) } 70 | } 71 | } 72 | 73 | impl<'a> Deref for ChapterMut<'a> { 74 | type Target = Chapter<'a>; 75 | 76 | fn deref(&self) -> &Self::Target { 77 | &self.immutable 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /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 ffi::*; 2 | 3 | #[derive(Copy, Clone, Debug)] 4 | pub enum Mode { 5 | Input, 6 | Output, 7 | } 8 | 9 | pub struct Destructor { 10 | ptr: *mut AVFormatContext, 11 | mode: Mode, 12 | } 13 | 14 | impl Destructor { 15 | pub unsafe fn new(ptr: *mut AVFormatContext, mode: Mode) -> Self { 16 | Destructor { ptr, mode } 17 | } 18 | } 19 | 20 | impl Drop for Destructor { 21 | fn drop(&mut self) { 22 | unsafe { 23 | match self.mode { 24 | Mode::Input => avformat_close_input(&mut self.ptr), 25 | 26 | Mode::Output => { 27 | avio_close((*self.ptr).pb); 28 | avformat_free_context(self.ptr); 29 | } 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/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 ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 6 | pub struct Flags: c_int { 7 | const NO_FILE = AVFMT_NOFILE; 8 | const NEED_NUMBER = AVFMT_NEEDNUMBER; 9 | const SHOW_IDS = AVFMT_SHOW_IDS; 10 | #[cfg(not(feature = "ffmpeg_4_0"))] 11 | const RAW_PICTURE = AVFMT_RAWPICTURE; 12 | const GLOBAL_HEADER = AVFMT_GLOBALHEADER; 13 | const NO_TIMESTAMPS = AVFMT_NOTIMESTAMPS; 14 | const GENERIC_INDEX = AVFMT_GENERIC_INDEX; 15 | const TS_DISCONT = AVFMT_TS_DISCONT; 16 | const VARIABLE_FPS = AVFMT_VARIABLE_FPS; 17 | const NO_DIMENSIONS = AVFMT_NODIMENSIONS; 18 | const NO_STREAMS = AVFMT_NOSTREAMS; 19 | const NO_BINSEARCH = AVFMT_NOBINSEARCH; 20 | const NO_GENSEARCH = AVFMT_NOGENSEARCH; 21 | const NO_BYTE_SEEK = AVFMT_NO_BYTE_SEEK; 22 | const ALLOW_FLUSH = AVFMT_ALLOW_FLUSH; 23 | const TS_NONSTRICT = AVFMT_TS_NONSTRICT; 24 | const TS_NEGATIVE = AVFMT_TS_NEGATIVE; 25 | const SEEK_TO_PTS = AVFMT_SEEK_TO_PTS; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/format/format/input.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::str::from_utf8_unchecked; 3 | 4 | use ffi::*; 5 | 6 | pub struct Input { 7 | ptr: *mut AVInputFormat, 8 | } 9 | 10 | impl Input { 11 | pub unsafe fn wrap(ptr: *mut AVInputFormat) -> Self { 12 | Input { ptr } 13 | } 14 | 15 | pub unsafe fn as_ptr(&self) -> *const AVInputFormat { 16 | self.ptr as *const _ 17 | } 18 | 19 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVInputFormat { 20 | self.ptr 21 | } 22 | } 23 | 24 | impl Input { 25 | pub fn name(&self) -> &str { 26 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).name).to_bytes()) } 27 | } 28 | 29 | pub fn description(&self) -> &str { 30 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).long_name).to_bytes()) } 31 | } 32 | 33 | pub fn extensions(&self) -> Vec<&str> { 34 | unsafe { 35 | let ptr = (*self.as_ptr()).extensions; 36 | 37 | if ptr.is_null() { 38 | Vec::new() 39 | } else { 40 | from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) 41 | .split(',') 42 | .collect() 43 | } 44 | } 45 | } 46 | 47 | pub fn mime_types(&self) -> Vec<&str> { 48 | unsafe { 49 | let ptr = (*self.as_ptr()).mime_type; 50 | 51 | if ptr.is_null() { 52 | Vec::new() 53 | } else { 54 | from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) 55 | .split(',') 56 | .collect() 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/format/format/iter.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use super::{Format, Input, Output}; 4 | use ffi::*; 5 | 6 | pub struct Iter { 7 | input: *mut AVInputFormat, 8 | output: *mut AVOutputFormat, 9 | step: Step, 10 | } 11 | 12 | enum Step { 13 | Input, 14 | Output, 15 | Done, 16 | } 17 | 18 | impl Iter { 19 | pub fn new() -> Self { 20 | Iter { 21 | input: ptr::null_mut(), 22 | output: ptr::null_mut(), 23 | step: Step::Input, 24 | } 25 | } 26 | } 27 | 28 | impl Default for Iter { 29 | fn default() -> Self { 30 | Self::new() 31 | } 32 | } 33 | 34 | impl Iterator for Iter { 35 | type Item = Format; 36 | 37 | fn next(&mut self) -> Option<::Item> { 38 | unsafe { 39 | match self.step { 40 | Step::Input => { 41 | let ptr = av_iformat_next(self.input); 42 | 43 | if ptr.is_null() && !self.input.is_null() { 44 | self.step = Step::Output; 45 | 46 | self.next() 47 | } else { 48 | self.input = ptr; 49 | 50 | Some(Format::Input(Input::wrap(ptr))) 51 | } 52 | } 53 | 54 | Step::Output => { 55 | let ptr = av_oformat_next(self.output); 56 | 57 | if ptr.is_null() && !self.output.is_null() { 58 | self.step = Step::Done; 59 | 60 | self.next() 61 | } else { 62 | self.output = ptr; 63 | 64 | Some(Format::Output(Output::wrap(ptr))) 65 | } 66 | } 67 | 68 | Step::Done => None, 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/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 | #[cfg(not(feature = "ffmpeg_5_0"))] 11 | mod iter; 12 | #[cfg(not(feature = "ffmpeg_5_0"))] 13 | pub use self::iter::Iter; 14 | 15 | pub enum Format { 16 | Input(Input), 17 | Output(Output), 18 | } 19 | 20 | impl Format { 21 | pub fn name(&self) -> &str { 22 | match *self { 23 | Format::Input(ref f) => f.name(), 24 | Format::Output(ref f) => f.name(), 25 | } 26 | } 27 | 28 | pub fn description(&self) -> &str { 29 | match *self { 30 | Format::Input(ref f) => f.description(), 31 | Format::Output(ref f) => f.description(), 32 | } 33 | } 34 | 35 | pub fn extensions(&self) -> Vec<&str> { 36 | match *self { 37 | Format::Input(ref f) => f.extensions(), 38 | Format::Output(ref f) => f.extensions(), 39 | } 40 | } 41 | 42 | pub fn mime_types(&self) -> Vec<&str> { 43 | match *self { 44 | Format::Input(ref f) => f.mime_types(), 45 | Format::Output(ref f) => f.mime_types(), 46 | } 47 | } 48 | } 49 | 50 | #[cfg(not(feature = "ffmpeg_5_0"))] 51 | pub fn list() -> Iter { 52 | Iter::new() 53 | } 54 | -------------------------------------------------------------------------------- /src/format/format/output.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use std::ffi::{CStr, CString}; 4 | use std::ptr; 5 | use std::str::from_utf8_unchecked; 6 | 7 | use super::Flags; 8 | use ffi::*; 9 | use {codec, media}; 10 | 11 | pub struct Output { 12 | ptr: *mut AVOutputFormat, 13 | } 14 | 15 | impl Output { 16 | pub unsafe fn wrap(ptr: *mut AVOutputFormat) -> Self { 17 | Output { ptr } 18 | } 19 | 20 | pub unsafe fn as_ptr(&self) -> *const AVOutputFormat { 21 | self.ptr as *const _ 22 | } 23 | 24 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVOutputFormat { 25 | self.ptr 26 | } 27 | } 28 | 29 | impl Output { 30 | pub fn name(&self) -> &str { 31 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).name).to_bytes()) } 32 | } 33 | 34 | pub fn description(&self) -> &str { 35 | unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).long_name).to_bytes()) } 36 | } 37 | 38 | pub fn extensions(&self) -> Vec<&str> { 39 | unsafe { 40 | let ptr = (*self.as_ptr()).extensions; 41 | 42 | if ptr.is_null() { 43 | Vec::new() 44 | } else { 45 | from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) 46 | .split(',') 47 | .collect() 48 | } 49 | } 50 | } 51 | 52 | pub fn mime_types(&self) -> Vec<&str> { 53 | unsafe { 54 | let ptr = (*self.as_ptr()).mime_type; 55 | 56 | if ptr.is_null() { 57 | Vec::new() 58 | } else { 59 | from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) 60 | .split(',') 61 | .collect() 62 | } 63 | } 64 | } 65 | 66 | pub fn codec + ?Sized>(&self, path: &P, kind: media::Type) -> codec::Id { 67 | // XXX: use to_cstring when stable 68 | let path = CString::new(path.as_ref().as_os_str().to_str().unwrap()).unwrap(); 69 | 70 | unsafe { 71 | codec::Id::from(av_guess_codec( 72 | self.as_ptr() as *mut _, 73 | ptr::null(), 74 | path.as_ptr(), 75 | ptr::null(), 76 | kind.into(), 77 | )) 78 | } 79 | } 80 | 81 | pub fn flags(&self) -> Flags { 82 | unsafe { Flags::from_bits_truncate((*self.as_ptr()).flags) } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/format/network.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | 3 | pub fn init() { 4 | unsafe { 5 | avformat_network_init(); 6 | } 7 | } 8 | 9 | pub fn deinit() { 10 | unsafe { 11 | avformat_network_deinit(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/format/stream/disposition.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 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 | #[cfg(feature = "ffmpeg_7_1")] 22 | const MULTILAYER = AV_DISPOSITION_MULTILAYER; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /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 codec::{self, packet}; 3 | use ffi::*; 4 | use format::context::common::Context; 5 | use libc::c_int; 6 | use {DictionaryRef, Discard, Rational}; 7 | 8 | #[derive(Debug)] 9 | pub struct Stream<'a> { 10 | context: &'a Context, 11 | index: usize, 12 | } 13 | 14 | impl<'a> Stream<'a> { 15 | pub unsafe fn wrap(context: &Context, index: usize) -> Stream { 16 | Stream { context, index } 17 | } 18 | 19 | pub unsafe fn as_ptr(&self) -> *const AVStream { 20 | *(*self.context.as_ptr()).streams.add(self.index) 21 | } 22 | } 23 | 24 | impl<'a> Stream<'a> { 25 | pub fn id(&self) -> i32 { 26 | unsafe { (*self.as_ptr()).id } 27 | } 28 | 29 | #[cfg(not(feature = "ffmpeg_5_0"))] 30 | pub fn codec(&self) -> codec::Context { 31 | unsafe { codec::Context::wrap((*self.as_ptr()).codec, Some(self.context.destructor())) } 32 | } 33 | 34 | pub fn parameters(&self) -> codec::Parameters { 35 | unsafe { 36 | codec::Parameters::wrap((*self.as_ptr()).codecpar, Some(self.context.destructor())) 37 | } 38 | } 39 | 40 | pub fn index(&self) -> usize { 41 | unsafe { (*self.as_ptr()).index as usize } 42 | } 43 | 44 | pub fn time_base(&self) -> Rational { 45 | unsafe { Rational::from((*self.as_ptr()).time_base) } 46 | } 47 | 48 | pub fn start_time(&self) -> i64 { 49 | unsafe { (*self.as_ptr()).start_time } 50 | } 51 | 52 | pub fn duration(&self) -> i64 { 53 | unsafe { (*self.as_ptr()).duration } 54 | } 55 | 56 | pub fn frames(&self) -> i64 { 57 | unsafe { (*self.as_ptr()).nb_frames } 58 | } 59 | 60 | pub fn disposition(&self) -> Disposition { 61 | unsafe { Disposition::from_bits_truncate((*self.as_ptr()).disposition) } 62 | } 63 | 64 | pub fn discard(&self) -> Discard { 65 | unsafe { Discard::from((*self.as_ptr()).discard) } 66 | } 67 | 68 | pub fn side_data(&self) -> SideDataIter { 69 | SideDataIter::new(self) 70 | } 71 | 72 | pub fn rate(&self) -> Rational { 73 | unsafe { Rational::from((*self.as_ptr()).r_frame_rate) } 74 | } 75 | 76 | pub fn avg_frame_rate(&self) -> Rational { 77 | unsafe { Rational::from((*self.as_ptr()).avg_frame_rate) } 78 | } 79 | 80 | pub fn metadata(&self) -> DictionaryRef { 81 | unsafe { DictionaryRef::wrap((*self.as_ptr()).metadata) } 82 | } 83 | } 84 | 85 | impl<'a> PartialEq for Stream<'a> { 86 | fn eq(&self, other: &Self) -> bool { 87 | unsafe { self.as_ptr() == other.as_ptr() } 88 | } 89 | } 90 | 91 | impl<'a> Eq for Stream<'a> {} 92 | 93 | pub struct SideDataIter<'a> { 94 | stream: &'a Stream<'a>, 95 | current: c_int, 96 | } 97 | 98 | impl<'a> SideDataIter<'a> { 99 | pub fn new<'sd, 's: 'sd>(stream: &'s Stream) -> SideDataIter<'sd> { 100 | SideDataIter { stream, current: 0 } 101 | } 102 | } 103 | 104 | impl<'a> Iterator for SideDataIter<'a> { 105 | type Item = packet::SideData<'a>; 106 | 107 | fn next(&mut self) -> Option<::Item> { 108 | unsafe { 109 | if self.current >= (*self.stream.as_ptr()).nb_side_data { 110 | return None; 111 | } 112 | 113 | self.current += 1; 114 | 115 | Some(packet::SideData::wrap( 116 | (*self.stream.as_ptr()) 117 | .side_data 118 | .offset((self.current - 1) as isize), 119 | )) 120 | } 121 | } 122 | 123 | fn size_hint(&self) -> (usize, Option) { 124 | unsafe { 125 | let length = (*self.stream.as_ptr()).nb_side_data as usize; 126 | 127 | ( 128 | length - self.current as usize, 129 | Some(length - self.current as usize), 130 | ) 131 | } 132 | } 133 | } 134 | 135 | impl<'a> ExactSizeIterator for SideDataIter<'a> {} 136 | -------------------------------------------------------------------------------- /src/format/stream/stream_mut.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ops::Deref; 3 | 4 | use super::Stream; 5 | use ffi::*; 6 | use format::context::common::Context; 7 | use {codec, Dictionary, Rational}; 8 | 9 | pub struct StreamMut<'a> { 10 | context: &'a mut Context, 11 | index: usize, 12 | 13 | immutable: Stream<'a>, 14 | } 15 | 16 | impl<'a> StreamMut<'a> { 17 | pub unsafe fn wrap(context: &mut Context, index: usize) -> StreamMut { 18 | StreamMut { 19 | context: mem::transmute_copy(&context), 20 | index, 21 | 22 | immutable: Stream::wrap(mem::transmute_copy(&context), index), 23 | } 24 | } 25 | 26 | pub unsafe fn as_mut_ptr(&mut self) -> *mut AVStream { 27 | *(*self.context.as_mut_ptr()).streams.add(self.index) 28 | } 29 | } 30 | 31 | impl<'a> StreamMut<'a> { 32 | pub fn set_time_base>(&mut self, value: R) { 33 | unsafe { 34 | (*self.as_mut_ptr()).time_base = value.into().into(); 35 | } 36 | } 37 | 38 | pub fn set_rate>(&mut self, value: R) { 39 | unsafe { 40 | (*self.as_mut_ptr()).r_frame_rate = value.into().into(); 41 | } 42 | } 43 | 44 | pub fn set_avg_frame_rate>(&mut self, value: R) { 45 | unsafe { 46 | (*self.as_mut_ptr()).avg_frame_rate = value.into().into(); 47 | } 48 | } 49 | 50 | pub fn set_parameters>(&mut self, parameters: P) { 51 | let parameters = parameters.into(); 52 | 53 | unsafe { 54 | avcodec_parameters_copy((*self.as_mut_ptr()).codecpar, parameters.as_ptr()); 55 | } 56 | } 57 | 58 | pub fn set_metadata(&mut self, metadata: Dictionary) { 59 | unsafe { 60 | let metadata = metadata.disown(); 61 | (*self.as_mut_ptr()).metadata = metadata; 62 | } 63 | } 64 | } 65 | 66 | impl<'a> Deref for StreamMut<'a> { 67 | type Target = Stream<'a>; 68 | 69 | fn deref(&self) -> &Self::Target { 70 | &self.immutable 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | #![allow(clippy::missing_safety_doc)] 3 | #![allow(clippy::module_inception)] 4 | #![allow(clippy::too_many_arguments)] 5 | 6 | #[macro_use] 7 | extern crate bitflags; 8 | pub extern crate ffmpeg_sys_next as sys; 9 | #[cfg(feature = "image")] 10 | extern crate image; 11 | extern crate libc; 12 | 13 | pub use sys as ffi; 14 | 15 | #[macro_use] 16 | pub mod util; 17 | pub use util::channel_layout::{self, ChannelLayout}; 18 | pub use util::chroma; 19 | pub use util::color; 20 | pub use util::dictionary; 21 | pub use util::dictionary::Mut as DictionaryMut; 22 | pub use util::dictionary::Owned as Dictionary; 23 | pub use util::dictionary::Ref as DictionaryRef; 24 | pub use util::error::{self, Error}; 25 | pub use util::frame::{self, Frame}; 26 | pub use util::log; 27 | pub use util::mathematics::{self, rescale, Rescale, Rounding}; 28 | pub use util::media; 29 | pub use util::option; 30 | pub use util::picture; 31 | pub use util::rational::{self, Rational}; 32 | pub use util::time; 33 | 34 | #[cfg(feature = "format")] 35 | pub mod format; 36 | #[cfg(feature = "format")] 37 | pub use format::chapter::{Chapter, ChapterMut}; 38 | #[cfg(feature = "format")] 39 | pub use format::format::Format; 40 | #[cfg(feature = "format")] 41 | pub use format::stream::{Stream, StreamMut}; 42 | 43 | #[cfg(feature = "codec")] 44 | pub mod codec; 45 | #[cfg(feature = "codec")] 46 | pub use codec::audio_service::AudioService; 47 | #[cfg(feature = "codec")] 48 | pub use codec::codec::Codec; 49 | #[cfg(feature = "codec")] 50 | pub use codec::discard::Discard; 51 | #[cfg(feature = "codec")] 52 | pub use codec::field_order::FieldOrder; 53 | #[cfg(feature = "codec")] 54 | pub use codec::packet::{self, Packet}; 55 | #[cfg(all(feature = "codec", not(feature = "ffmpeg_5_0")))] 56 | pub use codec::picture::Picture; 57 | #[cfg(feature = "codec")] 58 | pub use codec::subtitle::{self, Subtitle}; 59 | #[cfg(feature = "codec")] 60 | pub use codec::threading; 61 | #[cfg(feature = "codec")] 62 | pub use codec::{decoder, encoder}; 63 | 64 | #[cfg(feature = "device")] 65 | pub mod device; 66 | 67 | #[cfg(feature = "filter")] 68 | pub mod filter; 69 | #[cfg(feature = "filter")] 70 | pub use filter::Filter; 71 | 72 | pub mod software; 73 | 74 | fn init_error() { 75 | util::error::register_all(); 76 | } 77 | 78 | #[cfg(all(feature = "format", not(feature = "ffmpeg_5_0")))] 79 | fn init_format() { 80 | format::register_all(); 81 | } 82 | 83 | #[cfg(not(feature = "format"))] 84 | fn init_format() {} 85 | 86 | #[cfg(feature = "device")] 87 | fn init_device() { 88 | device::register_all(); 89 | } 90 | 91 | #[cfg(not(feature = "device"))] 92 | fn init_device() {} 93 | 94 | #[cfg(all(feature = "filter", not(feature = "ffmpeg_5_0")))] 95 | fn init_filter() { 96 | filter::register_all(); 97 | } 98 | 99 | #[cfg(not(feature = "filter"))] 100 | fn init_filter() {} 101 | 102 | #[cfg_attr( 103 | any(feature = "ffmpeg4", feature = "ffmpeg41", feature = "ffmpeg42"), 104 | deprecated( 105 | note = "features ffmpeg4/ffmpeg41/ffmpeg42/ffmpeg43 are now auto-detected \ 106 | and will be removed in a future version" 107 | ) 108 | )] 109 | pub fn init() -> Result<(), Error> { 110 | init_error(); 111 | #[cfg(not(feature = "ffmpeg_5_0"))] 112 | init_format(); 113 | init_device(); 114 | #[cfg(not(feature = "ffmpeg_5_0"))] 115 | init_filter(); 116 | 117 | Ok(()) 118 | } 119 | -------------------------------------------------------------------------------- /src/software/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "software-scaling")] 2 | pub mod scaling; 3 | 4 | #[cfg(feature = "software-scaling")] 5 | #[inline] 6 | pub fn scaler( 7 | format: ::format::Pixel, 8 | flags: scaling::Flags, 9 | (in_width, in_height): (u32, u32), 10 | (out_width, out_height): (u32, u32), 11 | ) -> Result { 12 | scaling::Context::get( 13 | format, in_width, in_height, format, out_width, out_height, flags, 14 | ) 15 | } 16 | 17 | #[cfg(feature = "software-scaling")] 18 | #[inline] 19 | pub fn converter( 20 | (width, height): (u32, u32), 21 | input: ::format::Pixel, 22 | output: ::format::Pixel, 23 | ) -> Result { 24 | scaling::Context::get( 25 | input, 26 | width, 27 | height, 28 | output, 29 | width, 30 | height, 31 | scaling::flag::Flags::FAST_BILINEAR, 32 | ) 33 | } 34 | 35 | #[cfg(feature = "software-resampling")] 36 | pub mod resampling; 37 | 38 | #[cfg(feature = "software-resampling")] 39 | #[inline] 40 | pub fn resampler( 41 | (in_format, in_layout, in_rate): (::format::Sample, ::ChannelLayout, u32), 42 | (out_format, out_layout, out_rate): (::format::Sample, ::ChannelLayout, u32), 43 | ) -> Result { 44 | resampling::Context::get( 45 | in_format, in_layout, in_rate, out_format, out_layout, out_rate, 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /src/software/resampling/delay.rs: -------------------------------------------------------------------------------- 1 | use super::Context; 2 | use ffi::*; 3 | 4 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] 5 | pub struct Delay { 6 | pub seconds: i64, 7 | pub milliseconds: i64, 8 | pub input: i64, 9 | pub output: i64, 10 | } 11 | 12 | impl Delay { 13 | pub fn from(context: &Context) -> Self { 14 | unsafe { 15 | Delay { 16 | seconds: swr_get_delay(context.as_ptr() as *mut _, 1), 17 | milliseconds: swr_get_delay(context.as_ptr() as *mut _, 1000), 18 | input: swr_get_delay(context.as_ptr() as *mut _, i64::from(context.input().rate)), 19 | output: swr_get_delay(context.as_ptr() as *mut _, i64::from(context.output().rate)), 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/software/resampling/dither.rs: -------------------------------------------------------------------------------- 1 | use ffi::SwrDitherType::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] 5 | pub enum Dither { 6 | None, 7 | Rectangular, 8 | Triangular, 9 | TriangularHighPass, 10 | 11 | NoiseShapingLipshitz, 12 | NoiseShapingFWeighted, 13 | NoiseShapingModifiedEWeighted, 14 | NoiseShapingImprovedEWeighted, 15 | NoiseShapingShibata, 16 | NoiseShapingLowShibata, 17 | NoiseShapingHighShibata, 18 | } 19 | 20 | impl From for Dither { 21 | fn from(value: SwrDitherType) -> Dither { 22 | match value { 23 | SWR_DITHER_NONE => Dither::None, 24 | SWR_DITHER_RECTANGULAR => Dither::Rectangular, 25 | SWR_DITHER_TRIANGULAR => Dither::Triangular, 26 | SWR_DITHER_TRIANGULAR_HIGHPASS => Dither::TriangularHighPass, 27 | 28 | SWR_DITHER_NS => Dither::None, 29 | SWR_DITHER_NS_LIPSHITZ => Dither::NoiseShapingLipshitz, 30 | SWR_DITHER_NS_F_WEIGHTED => Dither::NoiseShapingFWeighted, 31 | SWR_DITHER_NS_MODIFIED_E_WEIGHTED => Dither::NoiseShapingModifiedEWeighted, 32 | SWR_DITHER_NS_IMPROVED_E_WEIGHTED => Dither::NoiseShapingImprovedEWeighted, 33 | SWR_DITHER_NS_SHIBATA => Dither::NoiseShapingShibata, 34 | SWR_DITHER_NS_LOW_SHIBATA => Dither::NoiseShapingLowShibata, 35 | SWR_DITHER_NS_HIGH_SHIBATA => Dither::NoiseShapingHighShibata, 36 | SWR_DITHER_NB => Dither::None, 37 | } 38 | } 39 | } 40 | 41 | impl From for SwrDitherType { 42 | fn from(value: Dither) -> SwrDitherType { 43 | match value { 44 | Dither::None => SWR_DITHER_NONE, 45 | Dither::Rectangular => SWR_DITHER_RECTANGULAR, 46 | Dither::Triangular => SWR_DITHER_TRIANGULAR, 47 | Dither::TriangularHighPass => SWR_DITHER_TRIANGULAR_HIGHPASS, 48 | 49 | Dither::NoiseShapingLipshitz => SWR_DITHER_NS_LIPSHITZ, 50 | Dither::NoiseShapingFWeighted => SWR_DITHER_NS_F_WEIGHTED, 51 | Dither::NoiseShapingModifiedEWeighted => SWR_DITHER_NS_MODIFIED_E_WEIGHTED, 52 | Dither::NoiseShapingImprovedEWeighted => SWR_DITHER_NS_IMPROVED_E_WEIGHTED, 53 | Dither::NoiseShapingShibata => SWR_DITHER_NS_SHIBATA, 54 | Dither::NoiseShapingLowShibata => SWR_DITHER_NS_LOW_SHIBATA, 55 | Dither::NoiseShapingHighShibata => SWR_DITHER_NS_HIGH_SHIBATA, 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/software/resampling/engine.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use sys::SwrEngine::*; 3 | 4 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] 5 | pub enum Engine { 6 | Software, 7 | SoundExchange, 8 | } 9 | 10 | impl From for Engine { 11 | fn from(value: SwrEngine) -> Engine { 12 | match value { 13 | SWR_ENGINE_SWR => Engine::Software, 14 | SWR_ENGINE_SOXR => Engine::SoundExchange, 15 | SWR_ENGINE_NB => Engine::Software, 16 | } 17 | } 18 | } 19 | 20 | impl From for SwrEngine { 21 | fn from(value: Engine) -> SwrEngine { 22 | match value { 23 | Engine::Software => SWR_ENGINE_SWR, 24 | Engine::SoundExchange => SWR_ENGINE_SOXR, 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/software/resampling/extensions.rs: -------------------------------------------------------------------------------- 1 | use super::Context; 2 | use util::format; 3 | use {decoder, frame, ChannelLayout, Error}; 4 | 5 | impl frame::Audio { 6 | #[inline] 7 | pub fn resampler( 8 | &self, 9 | format: format::Sample, 10 | channel_layout: ChannelLayout, 11 | rate: u32, 12 | ) -> Result { 13 | Context::get( 14 | self.format(), 15 | self.channel_layout(), 16 | unsafe { (*self.as_ptr()).sample_rate as u32 }, 17 | format, 18 | channel_layout, 19 | rate, 20 | ) 21 | } 22 | } 23 | 24 | impl decoder::Audio { 25 | #[inline] 26 | pub fn resampler( 27 | &self, 28 | format: format::Sample, 29 | channel_layout: ChannelLayout, 30 | rate: u32, 31 | ) -> Result { 32 | Context::get( 33 | self.format(), 34 | self.channel_layout(), 35 | self.rate(), 36 | format, 37 | channel_layout, 38 | rate, 39 | ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/software/resampling/filter.rs: -------------------------------------------------------------------------------- 1 | use ffi::SwrFilterType::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] 5 | pub enum Filter { 6 | Cubic, 7 | BlackmanNuttall, 8 | Kaiser, 9 | } 10 | 11 | impl From for Filter { 12 | fn from(value: SwrFilterType) -> Filter { 13 | match value { 14 | SWR_FILTER_TYPE_CUBIC => Filter::Cubic, 15 | SWR_FILTER_TYPE_BLACKMAN_NUTTALL => Filter::BlackmanNuttall, 16 | SWR_FILTER_TYPE_KAISER => Filter::Kaiser, 17 | } 18 | } 19 | } 20 | 21 | impl From for SwrFilterType { 22 | fn from(value: Filter) -> SwrFilterType { 23 | match value { 24 | Filter::Cubic => SWR_FILTER_TYPE_CUBIC, 25 | Filter::BlackmanNuttall => SWR_FILTER_TYPE_BLACKMAN_NUTTALL, 26 | Filter::Kaiser => SWR_FILTER_TYPE_KAISER, 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/software/resampling/flag.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 6 | pub struct Flags: c_int { 7 | const FORCE = SWR_FLAG_RESAMPLE; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/software/resampling/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod flag; 2 | pub use self::flag::Flags; 3 | 4 | pub mod dither; 5 | pub use self::dither::Dither; 6 | 7 | pub mod engine; 8 | pub use self::engine::Engine; 9 | 10 | pub mod filter; 11 | pub use self::filter::Filter; 12 | 13 | pub mod delay; 14 | pub use self::delay::Delay; 15 | 16 | pub mod context; 17 | pub use self::context::Context; 18 | 19 | mod extensions; 20 | 21 | use std::ffi::CStr; 22 | use std::str::from_utf8_unchecked; 23 | 24 | use ffi::*; 25 | 26 | pub fn version() -> u32 { 27 | unsafe { swresample_version() } 28 | } 29 | 30 | pub fn configuration() -> &'static str { 31 | unsafe { from_utf8_unchecked(CStr::from_ptr(swresample_configuration()).to_bytes()) } 32 | } 33 | 34 | pub fn license() -> &'static str { 35 | unsafe { from_utf8_unchecked(CStr::from_ptr(swresample_license()).to_bytes()) } 36 | } 37 | -------------------------------------------------------------------------------- /src/software/scaling/color_space.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum ColorSpace { 6 | Default, 7 | 8 | ITU709, 9 | FCC, 10 | ITU601, 11 | ITU624, 12 | SMPTE170M, 13 | SMPTE240M, 14 | } 15 | 16 | impl From for ColorSpace { 17 | fn from(value: c_int) -> ColorSpace { 18 | match value { 19 | SWS_CS_ITU709 => ColorSpace::ITU709, 20 | SWS_CS_FCC => ColorSpace::FCC, 21 | SWS_CS_DEFAULT => ColorSpace::Default, 22 | SWS_CS_SMPTE240M => ColorSpace::SMPTE240M, 23 | 24 | _ => ColorSpace::Default, 25 | } 26 | } 27 | } 28 | 29 | impl From for c_int { 30 | fn from(value: ColorSpace) -> c_int { 31 | match value { 32 | ColorSpace::Default => SWS_CS_DEFAULT, 33 | ColorSpace::ITU709 => SWS_CS_ITU709, 34 | ColorSpace::FCC => SWS_CS_FCC, 35 | ColorSpace::ITU601 => SWS_CS_ITU601, 36 | ColorSpace::ITU624 => SWS_CS_ITU624, 37 | ColorSpace::SMPTE170M => SWS_CS_SMPTE170M, 38 | ColorSpace::SMPTE240M => SWS_CS_SMPTE240M, 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/software/scaling/context.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use super::Flags; 4 | use ffi::*; 5 | use libc::c_int; 6 | use util::format; 7 | use {frame, Error}; 8 | 9 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] 10 | pub struct Definition { 11 | pub format: format::Pixel, 12 | pub width: u32, 13 | pub height: u32, 14 | } 15 | 16 | pub struct Context { 17 | ptr: *mut SwsContext, 18 | 19 | input: Definition, 20 | output: Definition, 21 | } 22 | 23 | impl Context { 24 | #[inline(always)] 25 | pub unsafe fn as_ptr(&self) -> *const SwsContext { 26 | self.ptr as *const _ 27 | } 28 | 29 | #[inline(always)] 30 | pub unsafe fn as_mut_ptr(&mut self) -> *mut SwsContext { 31 | self.ptr 32 | } 33 | } 34 | 35 | impl Context { 36 | pub fn get( 37 | src_format: format::Pixel, 38 | src_w: u32, 39 | src_h: u32, 40 | dst_format: format::Pixel, 41 | dst_w: u32, 42 | dst_h: u32, 43 | flags: Flags, 44 | ) -> Result { 45 | unsafe { 46 | let ptr = sws_getContext( 47 | src_w as c_int, 48 | src_h as c_int, 49 | src_format.into(), 50 | dst_w as c_int, 51 | dst_h as c_int, 52 | dst_format.into(), 53 | flags.bits(), 54 | ptr::null_mut(), 55 | ptr::null_mut(), 56 | ptr::null_mut(), 57 | ); 58 | 59 | if !ptr.is_null() { 60 | Ok(Context { 61 | ptr, 62 | 63 | input: Definition { 64 | format: src_format, 65 | width: src_w, 66 | height: src_h, 67 | }, 68 | 69 | output: Definition { 70 | format: dst_format, 71 | width: dst_w, 72 | height: dst_h, 73 | }, 74 | }) 75 | } else { 76 | Err(Error::InvalidData) 77 | } 78 | } 79 | } 80 | 81 | pub fn cached( 82 | &mut self, 83 | src_format: format::Pixel, 84 | src_w: u32, 85 | src_h: u32, 86 | dst_format: format::Pixel, 87 | dst_w: u32, 88 | dst_h: u32, 89 | flags: Flags, 90 | ) { 91 | self.input = Definition { 92 | format: src_format, 93 | width: src_w, 94 | height: src_h, 95 | }; 96 | 97 | self.output = Definition { 98 | format: dst_format, 99 | width: dst_w, 100 | height: dst_h, 101 | }; 102 | 103 | unsafe { 104 | self.ptr = sws_getCachedContext( 105 | self.as_mut_ptr(), 106 | src_w as c_int, 107 | src_h as c_int, 108 | src_format.into(), 109 | dst_w as c_int, 110 | dst_h as c_int, 111 | dst_format.into(), 112 | flags.bits(), 113 | ptr::null_mut(), 114 | ptr::null_mut(), 115 | ptr::null(), 116 | ); 117 | } 118 | } 119 | 120 | #[inline] 121 | pub fn input(&self) -> &Definition { 122 | &self.input 123 | } 124 | 125 | #[inline] 126 | pub fn output(&self) -> &Definition { 127 | &self.output 128 | } 129 | 130 | pub fn run(&mut self, input: &frame::Video, output: &mut frame::Video) -> Result<(), Error> { 131 | if input.format() != self.input.format 132 | || input.width() != self.input.width 133 | || input.height() != self.input.height 134 | { 135 | return Err(Error::InputChanged); 136 | } 137 | 138 | unsafe { 139 | if output.is_empty() { 140 | output.alloc(self.output.format, self.output.width, self.output.height); 141 | } 142 | } 143 | 144 | if output.format() != self.output.format 145 | || output.width() != self.output.width 146 | || output.height() != self.output.height 147 | { 148 | return Err(Error::OutputChanged); 149 | } 150 | 151 | unsafe { 152 | sws_scale( 153 | self.as_mut_ptr(), 154 | (*input.as_ptr()).data.as_ptr() as *const *const _, 155 | (*input.as_ptr()).linesize.as_ptr() as *const _, 156 | 0, 157 | self.input.height as c_int, 158 | (*output.as_mut_ptr()).data.as_ptr(), 159 | (*output.as_mut_ptr()).linesize.as_ptr() as *mut _, 160 | ); 161 | } 162 | 163 | Ok(()) 164 | } 165 | } 166 | 167 | impl Drop for Context { 168 | fn drop(&mut self) { 169 | unsafe { 170 | sws_freeContext(self.as_mut_ptr()); 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/software/scaling/extensions.rs: -------------------------------------------------------------------------------- 1 | use super::{Context, Flags}; 2 | use util::format; 3 | #[cfg(not(feature = "ffmpeg_5_0"))] 4 | use Picture; 5 | use {decoder, frame, Error}; 6 | 7 | #[cfg(not(feature = "ffmpeg_5_0"))] 8 | impl<'a> Picture<'a> { 9 | #[inline] 10 | pub fn scaler(&self, width: u32, height: u32, flags: Flags) -> Result { 11 | Context::get( 12 | self.format(), 13 | self.width(), 14 | self.height(), 15 | self.format(), 16 | width, 17 | height, 18 | flags, 19 | ) 20 | } 21 | 22 | #[inline] 23 | pub fn converter(&self, format: format::Pixel) -> Result { 24 | Context::get( 25 | self.format(), 26 | self.width(), 27 | self.height(), 28 | format, 29 | self.width(), 30 | self.height(), 31 | Flags::FAST_BILINEAR, 32 | ) 33 | } 34 | } 35 | 36 | impl frame::Video { 37 | #[inline] 38 | pub fn scaler(&self, width: u32, height: u32, flags: Flags) -> Result { 39 | Context::get( 40 | self.format(), 41 | self.width(), 42 | self.height(), 43 | self.format(), 44 | width, 45 | height, 46 | flags, 47 | ) 48 | } 49 | 50 | #[inline] 51 | pub fn converter(&self, format: format::Pixel) -> Result { 52 | Context::get( 53 | self.format(), 54 | self.width(), 55 | self.height(), 56 | format, 57 | self.width(), 58 | self.height(), 59 | Flags::FAST_BILINEAR, 60 | ) 61 | } 62 | } 63 | 64 | impl decoder::Video { 65 | #[inline] 66 | pub fn scaler(&self, width: u32, height: u32, flags: Flags) -> Result { 67 | Context::get( 68 | self.format(), 69 | self.width(), 70 | self.height(), 71 | self.format(), 72 | width, 73 | height, 74 | flags, 75 | ) 76 | } 77 | 78 | #[inline] 79 | pub fn converter(&self, format: format::Pixel) -> Result { 80 | Context::get( 81 | self.format(), 82 | self.width(), 83 | self.height(), 84 | format, 85 | self.width(), 86 | self.height(), 87 | Flags::FAST_BILINEAR, 88 | ) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/software/scaling/filter.rs: -------------------------------------------------------------------------------- 1 | use super::Vector; 2 | use ffi::*; 3 | 4 | pub struct Filter { 5 | ptr: *mut SwsFilter, 6 | } 7 | 8 | impl Filter { 9 | pub unsafe fn as_ptr(&self) -> *const SwsFilter { 10 | self.ptr as *const _ 11 | } 12 | 13 | pub unsafe fn as_mut_ptr(&mut self) -> *mut SwsFilter { 14 | self.ptr 15 | } 16 | } 17 | 18 | impl Filter { 19 | pub fn get( 20 | luma_g_blur: f32, 21 | chroma_g_blur: f32, 22 | luma_sharpen: f32, 23 | chroma_sharpen: f32, 24 | chroma_h_shift: f32, 25 | chroma_v_shift: f32, 26 | ) -> Self { 27 | unsafe { 28 | Filter { 29 | ptr: sws_getDefaultFilter( 30 | luma_g_blur, 31 | chroma_g_blur, 32 | luma_sharpen, 33 | chroma_sharpen, 34 | chroma_h_shift, 35 | chroma_v_shift, 36 | 0, 37 | ), 38 | } 39 | } 40 | } 41 | 42 | pub fn new() -> Self { 43 | Self::get(0.0, 0.0, 0.0, 0.0, 0.0, 0.0) 44 | } 45 | 46 | pub fn luma_horizontal(&self) -> Vector { 47 | unsafe { Vector::wrap((*self.as_ptr()).lumH) } 48 | } 49 | 50 | pub fn luma_horizontal_mut(&mut self) -> Vector { 51 | unsafe { Vector::wrap((*self.as_mut_ptr()).lumH) } 52 | } 53 | 54 | pub fn luma_vertical(&self) -> Vector { 55 | unsafe { Vector::wrap((*self.as_ptr()).lumV) } 56 | } 57 | 58 | pub fn luma_vertical_mut(&mut self) -> Vector { 59 | unsafe { Vector::wrap((*self.as_mut_ptr()).lumV) } 60 | } 61 | 62 | pub fn chroma_horizontal(&self) -> Vector { 63 | unsafe { Vector::wrap((*self.as_ptr()).lumV) } 64 | } 65 | 66 | pub fn chroma_horizontal_mut(&mut self) -> Vector { 67 | unsafe { Vector::wrap((*self.as_mut_ptr()).lumV) } 68 | } 69 | 70 | pub fn chroma_vertical(&self) -> Vector { 71 | unsafe { Vector::wrap((*self.as_ptr()).lumV) } 72 | } 73 | 74 | pub fn chroma_vertical_mut(&mut self) -> Vector { 75 | unsafe { Vector::wrap((*self.as_mut_ptr()).lumV) } 76 | } 77 | } 78 | 79 | impl Default for Filter { 80 | fn default() -> Self { 81 | Self::new() 82 | } 83 | } 84 | 85 | impl Drop for Filter { 86 | fn drop(&mut self) { 87 | unsafe { 88 | sws_freeFilter(self.as_mut_ptr()); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/software/scaling/flag.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 6 | pub struct Flags: c_int { 7 | const FAST_BILINEAR = SWS_FAST_BILINEAR; 8 | const BILINEAR = SWS_BILINEAR; 9 | const BICUBIC = SWS_BICUBIC; 10 | const X = SWS_X; 11 | const POINT = SWS_POINT; 12 | const AREA = SWS_AREA; 13 | const BICUBLIN = SWS_BICUBLIN; 14 | const GAUSS = SWS_GAUSS; 15 | const SINC = SWS_SINC; 16 | const LANCZOS = SWS_LANCZOS; 17 | const SPLINE = SWS_SPLINE; 18 | const SRC_V_CHR_DROP_MASK = SWS_SRC_V_CHR_DROP_MASK; 19 | const SRC_V_CHR_DROP_SHIFT = SWS_SRC_V_CHR_DROP_SHIFT; 20 | const PARAM_DEFAULT = SWS_PARAM_DEFAULT; 21 | const PRINT_INFO = SWS_PRINT_INFO; 22 | const FULL_CHR_H_INT = SWS_FULL_CHR_H_INT; 23 | const FULL_CHR_H_INP = SWS_FULL_CHR_H_INP; 24 | const DIRECT_BGR = SWS_DIRECT_BGR; 25 | const ACCURATE_RND = SWS_ACCURATE_RND; 26 | const BITEXACT = SWS_BITEXACT; 27 | const ERROR_DIFFUSION = SWS_ERROR_DIFFUSION; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/software/scaling/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod flag; 2 | pub use self::flag::Flags; 3 | 4 | pub mod color_space; 5 | pub use self::color_space::ColorSpace; 6 | 7 | pub mod support; 8 | 9 | pub mod vector; 10 | pub use self::vector::Vector; 11 | 12 | pub mod filter; 13 | pub use self::filter::Filter; 14 | 15 | pub mod context; 16 | pub use self::context::Context; 17 | 18 | mod extensions; 19 | 20 | use std::ffi::CStr; 21 | use std::str::from_utf8_unchecked; 22 | 23 | use ffi::*; 24 | 25 | pub fn version() -> u32 { 26 | unsafe { swscale_version() } 27 | } 28 | 29 | pub fn configuration() -> &'static str { 30 | unsafe { from_utf8_unchecked(CStr::from_ptr(swscale_configuration()).to_bytes()) } 31 | } 32 | 33 | pub fn license() -> &'static str { 34 | unsafe { from_utf8_unchecked(CStr::from_ptr(swscale_license()).to_bytes()) } 35 | } 36 | -------------------------------------------------------------------------------- /src/software/scaling/support.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use util::format; 3 | 4 | pub fn input(format: format::Pixel) -> bool { 5 | unsafe { sws_isSupportedInput(format.into()) != 0 } 6 | } 7 | 8 | pub fn output(format: format::Pixel) -> bool { 9 | unsafe { sws_isSupportedOutput(format.into()) != 0 } 10 | } 11 | 12 | pub fn endianness_conversion(format: format::Pixel) -> bool { 13 | unsafe { sws_isSupportedEndiannessConversion(format.into()) != 0 } 14 | } 15 | -------------------------------------------------------------------------------- /src/software/scaling/vector.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::slice; 3 | 4 | use ffi::*; 5 | use libc::{c_double, c_int}; 6 | 7 | pub struct Vector<'a> { 8 | ptr: *mut SwsVector, 9 | 10 | _own: bool, 11 | _marker: PhantomData<&'a ()>, 12 | } 13 | 14 | impl<'a> Vector<'a> { 15 | pub unsafe fn wrap(ptr: *mut SwsVector) -> Self { 16 | Vector { 17 | ptr, 18 | _own: false, 19 | _marker: PhantomData, 20 | } 21 | } 22 | 23 | pub unsafe fn as_ptr(&self) -> *const SwsVector { 24 | self.ptr as *const _ 25 | } 26 | 27 | pub unsafe fn as_mut_ptr(&mut self) -> *mut SwsVector { 28 | self.ptr 29 | } 30 | } 31 | 32 | impl<'a> Vector<'a> { 33 | pub fn new(length: usize) -> Self { 34 | unsafe { 35 | Vector { 36 | ptr: sws_allocVec(length as c_int), 37 | _own: true, 38 | _marker: PhantomData, 39 | } 40 | } 41 | } 42 | 43 | pub fn gaussian(variance: f64, quality: f64) -> Self { 44 | unsafe { 45 | Vector { 46 | ptr: sws_getGaussianVec(variance as c_double, quality as c_double), 47 | _own: true, 48 | _marker: PhantomData, 49 | } 50 | } 51 | } 52 | 53 | #[cfg(not(feature = "ffmpeg_5_0"))] 54 | pub fn value(value: f64, length: usize) -> Self { 55 | unsafe { 56 | Vector { 57 | ptr: sws_getConstVec(value as c_double, length as c_int), 58 | _own: true, 59 | _marker: PhantomData, 60 | } 61 | } 62 | } 63 | 64 | #[cfg(not(feature = "ffmpeg_5_0"))] 65 | pub fn identity() -> Self { 66 | unsafe { 67 | Vector { 68 | ptr: sws_getIdentityVec(), 69 | _own: true, 70 | _marker: PhantomData, 71 | } 72 | } 73 | } 74 | 75 | pub fn scale(&mut self, scalar: f64) { 76 | unsafe { 77 | sws_scaleVec(self.as_mut_ptr(), scalar as c_double); 78 | } 79 | } 80 | 81 | pub fn normalize(&mut self, height: f64) { 82 | unsafe { 83 | sws_normalizeVec(self.as_mut_ptr(), height as c_double); 84 | } 85 | } 86 | 87 | #[cfg(not(feature = "ffmpeg_5_0"))] 88 | pub fn conv(&mut self, other: &Vector) { 89 | unsafe { 90 | sws_convVec(self.as_mut_ptr(), other.as_ptr() as *mut _); 91 | } 92 | } 93 | 94 | #[cfg(not(feature = "ffmpeg_5_0"))] 95 | pub fn add(&mut self, other: &Vector) { 96 | unsafe { 97 | sws_addVec(self.as_mut_ptr(), other.as_ptr() as *mut _); 98 | } 99 | } 100 | 101 | #[cfg(not(feature = "ffmpeg_5_0"))] 102 | pub fn sub(&mut self, other: &Vector) { 103 | unsafe { 104 | sws_subVec(self.as_mut_ptr(), other.as_ptr() as *mut _); 105 | } 106 | } 107 | 108 | #[cfg(not(feature = "ffmpeg_5_0"))] 109 | pub fn shift(&mut self, value: usize) { 110 | unsafe { 111 | sws_shiftVec(self.as_mut_ptr(), value as c_int); 112 | } 113 | } 114 | 115 | pub fn coefficients(&self) -> &[f64] { 116 | unsafe { slice::from_raw_parts((*self.as_ptr()).coeff, (*self.as_ptr()).length as usize) } 117 | } 118 | 119 | pub fn coefficients_mut(&self) -> &[f64] { 120 | unsafe { 121 | slice::from_raw_parts_mut((*self.as_ptr()).coeff, (*self.as_ptr()).length as usize) 122 | } 123 | } 124 | } 125 | 126 | #[cfg(not(feature = "ffmpeg_5_0"))] 127 | impl<'a> Clone for Vector<'a> { 128 | fn clone(&self) -> Self { 129 | unsafe { 130 | Vector { 131 | ptr: sws_cloneVec(self.as_ptr() as *mut _), 132 | _own: true, 133 | _marker: PhantomData, 134 | } 135 | } 136 | } 137 | } 138 | 139 | impl<'a> Drop for Vector<'a> { 140 | fn drop(&mut self) { 141 | unsafe { 142 | if self._own { 143 | sws_freeVec(self.as_mut_ptr()); 144 | } 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/util/channel_layout.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | 3 | #[repr(transparent)] 4 | #[derive(Copy, Clone)] 5 | pub struct ChannelLayout(pub AVChannelLayout); 6 | 7 | impl PartialEq for ChannelLayout { 8 | // TODO this can actually return an error if < 0 9 | fn eq(&self, other: &Self) -> bool { 10 | unsafe { 11 | av_channel_layout_compare( 12 | &self.0 as *const AVChannelLayout, 13 | &other.0 as *const AVChannelLayout, 14 | ) == 0 15 | } 16 | } 17 | } 18 | impl Eq for ChannelLayout {} 19 | 20 | impl std::fmt::Debug for ChannelLayout { 21 | fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { 22 | let mut s = fmt.debug_struct("ChannelLayout"); 23 | s.field("is_empty", &self.is_empty()); 24 | s.field("channels", &self.channels()); 25 | s.field("u.mask", &unsafe { self.0.u.mask }); 26 | s.finish() 27 | } 28 | } 29 | 30 | macro_rules! define_layout { 31 | ($name:ident, $nb:expr, $mask:expr) => { 32 | pub const $name: ChannelLayout = ChannelLayout(AVChannelLayout { 33 | order: AVChannelOrder::AV_CHANNEL_ORDER_NATIVE, 34 | nb_channels: $nb, 35 | u: AVChannelLayout__bindgen_ty_1 { mask: $mask }, 36 | opaque: std::ptr::null_mut(), 37 | }); 38 | }; 39 | } 40 | 41 | impl ChannelLayout { 42 | define_layout!(MONO, 1, AV_CH_LAYOUT_MONO); 43 | define_layout!(STEREO, 2, AV_CH_LAYOUT_STEREO); 44 | define_layout!(_2POINT1, 3, AV_CH_LAYOUT_2POINT1); 45 | define_layout!(_2_1, 3, AV_CH_LAYOUT_2_1); 46 | define_layout!(SURROUND, 3, AV_CH_LAYOUT_SURROUND); 47 | define_layout!(_3POINT1, 4, AV_CH_LAYOUT_3POINT1); 48 | define_layout!(_4POINT0, 4, AV_CH_LAYOUT_4POINT0); 49 | define_layout!(_4POINT1, 5, AV_CH_LAYOUT_4POINT1); 50 | define_layout!(_2_2, 4, AV_CH_LAYOUT_2_2); 51 | define_layout!(QUAD, 4, AV_CH_LAYOUT_QUAD); 52 | define_layout!(_5POINT0, 5, AV_CH_LAYOUT_5POINT0); 53 | define_layout!(_5POINT1, 6, AV_CH_LAYOUT_5POINT1); 54 | define_layout!(_5POINT0_BACK, 5, AV_CH_LAYOUT_5POINT0_BACK); 55 | define_layout!(_5POINT1_BACK, 6, AV_CH_LAYOUT_5POINT1_BACK); 56 | define_layout!(_6POINT0, 6, AV_CH_LAYOUT_6POINT0); 57 | define_layout!(_6POINT0_FRONT, 6, AV_CH_LAYOUT_6POINT0_FRONT); 58 | define_layout!(_3POINT1POINT2, 6, AV_CH_LAYOUT_3POINT1POINT2); 59 | define_layout!(HEXAGONAL, 6, AV_CH_LAYOUT_HEXAGONAL); 60 | define_layout!(_6POINT1, 7, AV_CH_LAYOUT_6POINT1); 61 | define_layout!(_6POINT1_BACK, 7, AV_CH_LAYOUT_6POINT1_BACK); 62 | define_layout!(_6POINT1_FRONT, 7, AV_CH_LAYOUT_6POINT1_FRONT); 63 | define_layout!(_7POINT0, 7, AV_CH_LAYOUT_7POINT0); 64 | define_layout!(_7POINT0_FRONT, 7, AV_CH_LAYOUT_7POINT0_FRONT); 65 | define_layout!(_7POINT1, 8, AV_CH_LAYOUT_7POINT1); 66 | define_layout!(_7POINT1_WIDE, 8, AV_CH_LAYOUT_7POINT1_WIDE); 67 | define_layout!(_7POINT1_WIDE_BACK, 8, AV_CH_LAYOUT_7POINT1_WIDE_BACK); 68 | define_layout!(_5POINT1POINT2_BACK, 8, AV_CH_LAYOUT_5POINT1POINT2_BACK); 69 | define_layout!(OCTAGONAL, 8, AV_CH_LAYOUT_OCTAGONAL); 70 | define_layout!(CUBE, 8, AV_CH_LAYOUT_CUBE); 71 | define_layout!(_5POINT1POINT4_BACK, 10, AV_CH_LAYOUT_5POINT1POINT4_BACK); 72 | define_layout!(_7POINT1POINT2, 10, AV_CH_LAYOUT_7POINT1POINT2); 73 | define_layout!(_7POINT1POINT4_BACK, 12, AV_CH_LAYOUT_7POINT1POINT4_BACK); 74 | define_layout!(_7POINT2POINT3, 12, AV_CH_LAYOUT_7POINT2POINT3); 75 | define_layout!(_9POINT1POINT4_BACK, 14, AV_CH_LAYOUT_9POINT1POINT4_BACK); 76 | define_layout!(HEXADECAGONAL, 16, AV_CH_LAYOUT_HEXADECAGONAL); 77 | define_layout!(STEREO_DOWNMIX, 2, AV_CH_LAYOUT_STEREO_DOWNMIX); 78 | define_layout!(_22POINT2, 24, AV_CH_LAYOUT_22POINT2); 79 | define_layout!(_7POINT1_TOP_BACK, 8, AV_CH_LAYOUT_5POINT1POINT2_BACK); 80 | 81 | #[inline] 82 | pub fn channels(&self) -> i32 { 83 | self.0.nb_channels 84 | } 85 | 86 | #[inline] 87 | pub fn bits(&self) -> u64 { 88 | unsafe { self.0.u.mask } 89 | } 90 | 91 | pub fn default(number: i32) -> ChannelLayout { 92 | unsafe { 93 | let mut channel_layout = std::mem::zeroed(); 94 | av_channel_layout_default(&mut channel_layout, number); 95 | ChannelLayout(channel_layout) 96 | } 97 | } 98 | 99 | // See https://ffmpeg.org/doxygen/trunk/group__lavu__audio__channels.html#gaa4a685b5c38835392552a7f96ee24a3e, 100 | // AV_CH_UNUSED 101 | pub fn is_empty(&self) -> bool { 102 | self.0.order == AVChannelOrder::AV_CHANNEL_ORDER_UNSPEC 103 | } 104 | } 105 | 106 | impl From for ChannelLayout { 107 | fn from(value: AVChannelLayout) -> Self { 108 | Self(value) 109 | } 110 | } 111 | 112 | impl From for AVChannelLayout { 113 | fn from(value: ChannelLayout) -> Self { 114 | value.0 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/util/chroma/location.rs: -------------------------------------------------------------------------------- 1 | use ffi::AVChromaLocation::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Location { 6 | Unspecified, 7 | Left, 8 | Center, 9 | TopLeft, 10 | Top, 11 | BottomLeft, 12 | Bottom, 13 | } 14 | 15 | impl From for Location { 16 | fn from(value: AVChromaLocation) -> Self { 17 | match value { 18 | AVCHROMA_LOC_UNSPECIFIED => Location::Unspecified, 19 | AVCHROMA_LOC_LEFT => Location::Left, 20 | AVCHROMA_LOC_CENTER => Location::Center, 21 | AVCHROMA_LOC_TOPLEFT => Location::TopLeft, 22 | AVCHROMA_LOC_TOP => Location::Top, 23 | AVCHROMA_LOC_BOTTOMLEFT => Location::BottomLeft, 24 | AVCHROMA_LOC_BOTTOM => Location::Bottom, 25 | AVCHROMA_LOC_NB => Location::Unspecified, 26 | } 27 | } 28 | } 29 | 30 | impl From for AVChromaLocation { 31 | fn from(value: Location) -> AVChromaLocation { 32 | match value { 33 | Location::Unspecified => AVCHROMA_LOC_UNSPECIFIED, 34 | Location::Left => AVCHROMA_LOC_LEFT, 35 | Location::Center => AVCHROMA_LOC_CENTER, 36 | Location::TopLeft => AVCHROMA_LOC_TOPLEFT, 37 | Location::Top => AVCHROMA_LOC_TOP, 38 | Location::BottomLeft => AVCHROMA_LOC_BOTTOMLEFT, 39 | Location::Bottom => AVCHROMA_LOC_BOTTOM, 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/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 std::ffi::CStr; 2 | use std::str::from_utf8_unchecked; 3 | 4 | use ffi::AVColorPrimaries::*; 5 | use ffi::*; 6 | 7 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 8 | pub enum Primaries { 9 | Reserved0, 10 | BT709, 11 | Unspecified, 12 | Reserved, 13 | BT470M, 14 | 15 | BT470BG, 16 | SMPTE170M, 17 | SMPTE240M, 18 | Film, 19 | BT2020, 20 | 21 | SMPTE428, 22 | SMPTE431, 23 | SMPTE432, 24 | #[cfg(not(feature = "ffmpeg_4_3"))] 25 | JEDEC_P22, 26 | #[cfg(feature = "ffmpeg_4_3")] 27 | EBU3213, 28 | } 29 | 30 | impl Primaries { 31 | #[cfg(feature = "ffmpeg_4_3")] 32 | pub const JEDEC_P22: Primaries = Primaries::EBU3213; 33 | 34 | pub fn name(&self) -> Option<&'static str> { 35 | if *self == Primaries::Unspecified { 36 | return None; 37 | } 38 | unsafe { 39 | let ptr = av_color_primaries_name((*self).into()); 40 | ptr.as_ref() 41 | .map(|ptr| from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) 42 | } 43 | } 44 | } 45 | 46 | impl From for Primaries { 47 | fn from(value: AVColorPrimaries) -> Primaries { 48 | match value { 49 | AVCOL_PRI_RESERVED0 => Primaries::Reserved0, 50 | AVCOL_PRI_BT709 => Primaries::BT709, 51 | AVCOL_PRI_UNSPECIFIED => Primaries::Unspecified, 52 | AVCOL_PRI_RESERVED => Primaries::Reserved, 53 | AVCOL_PRI_BT470M => Primaries::BT470M, 54 | 55 | AVCOL_PRI_BT470BG => Primaries::BT470BG, 56 | AVCOL_PRI_SMPTE170M => Primaries::SMPTE170M, 57 | AVCOL_PRI_SMPTE240M => Primaries::SMPTE240M, 58 | AVCOL_PRI_FILM => Primaries::Film, 59 | AVCOL_PRI_BT2020 => Primaries::BT2020, 60 | AVCOL_PRI_NB => Primaries::Reserved0, 61 | 62 | AVCOL_PRI_SMPTE428 => Primaries::SMPTE428, 63 | AVCOL_PRI_SMPTE431 => Primaries::SMPTE431, 64 | AVCOL_PRI_SMPTE432 => Primaries::SMPTE432, 65 | #[cfg(not(feature = "ffmpeg_4_3"))] 66 | AVCOL_PRI_JEDEC_P22 => Primaries::JEDEC_P22, 67 | #[cfg(feature = "ffmpeg_4_3")] 68 | AVCOL_PRI_EBU3213 => Primaries::EBU3213, 69 | } 70 | } 71 | } 72 | 73 | impl From for AVColorPrimaries { 74 | fn from(value: Primaries) -> AVColorPrimaries { 75 | match value { 76 | Primaries::Reserved0 => AVCOL_PRI_RESERVED0, 77 | Primaries::BT709 => AVCOL_PRI_BT709, 78 | Primaries::Unspecified => AVCOL_PRI_UNSPECIFIED, 79 | Primaries::Reserved => AVCOL_PRI_RESERVED, 80 | Primaries::BT470M => AVCOL_PRI_BT470M, 81 | 82 | Primaries::BT470BG => AVCOL_PRI_BT470BG, 83 | Primaries::SMPTE170M => AVCOL_PRI_SMPTE170M, 84 | Primaries::SMPTE240M => AVCOL_PRI_SMPTE240M, 85 | Primaries::Film => AVCOL_PRI_FILM, 86 | Primaries::BT2020 => AVCOL_PRI_BT2020, 87 | 88 | Primaries::SMPTE428 => AVCOL_PRI_SMPTE428, 89 | Primaries::SMPTE431 => AVCOL_PRI_SMPTE431, 90 | Primaries::SMPTE432 => AVCOL_PRI_SMPTE432, 91 | #[cfg(not(feature = "ffmpeg_4_3"))] 92 | Primaries::JEDEC_P22 => AVCOL_PRI_JEDEC_P22, 93 | #[cfg(feature = "ffmpeg_4_3")] 94 | Primaries::EBU3213 => AVCOL_PRI_EBU3213, 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/util/color/range.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::str::from_utf8_unchecked; 3 | 4 | use ffi::AVColorRange::*; 5 | use ffi::*; 6 | 7 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 8 | pub enum Range { 9 | Unspecified, 10 | MPEG, 11 | JPEG, 12 | } 13 | 14 | impl Range { 15 | pub fn name(&self) -> Option<&'static str> { 16 | if *self == Range::Unspecified { 17 | return None; 18 | } 19 | unsafe { 20 | let ptr = av_color_range_name((*self).into()); 21 | ptr.as_ref() 22 | .map(|ptr| from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) 23 | } 24 | } 25 | } 26 | 27 | impl From for Range { 28 | fn from(value: AVColorRange) -> Self { 29 | match value { 30 | AVCOL_RANGE_UNSPECIFIED => Range::Unspecified, 31 | AVCOL_RANGE_MPEG => Range::MPEG, 32 | AVCOL_RANGE_JPEG => Range::JPEG, 33 | AVCOL_RANGE_NB => Range::Unspecified, 34 | } 35 | } 36 | } 37 | 38 | impl From for AVColorRange { 39 | fn from(value: Range) -> AVColorRange { 40 | match value { 41 | Range::Unspecified => AVCOL_RANGE_UNSPECIFIED, 42 | Range::MPEG => AVCOL_RANGE_MPEG, 43 | Range::JPEG => AVCOL_RANGE_JPEG, 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/util/color/space.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::str::from_utf8_unchecked; 3 | 4 | use ffi::AVColorSpace::*; 5 | use ffi::*; 6 | 7 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 8 | pub enum Space { 9 | RGB, 10 | BT709, 11 | Unspecified, 12 | Reserved, 13 | FCC, 14 | BT470BG, 15 | SMPTE170M, 16 | SMPTE240M, 17 | YCGCO, 18 | BT2020NCL, 19 | BT2020CL, 20 | SMPTE2085, 21 | 22 | ChromaDerivedNCL, 23 | ChromaDerivedCL, 24 | ICTCP, 25 | 26 | #[cfg(feature = "ffmpeg_7_1")] 27 | IPT_C2, 28 | #[cfg(feature = "ffmpeg_7_1")] 29 | YCGCO_RE, 30 | #[cfg(feature = "ffmpeg_7_1")] 31 | YCGCO_RO, 32 | } 33 | 34 | impl Space { 35 | pub const YCOCG: Space = Space::YCGCO; 36 | 37 | pub fn name(&self) -> Option<&'static str> { 38 | if *self == Space::Unspecified { 39 | return None; 40 | } 41 | unsafe { 42 | let ptr = av_color_space_name((*self).into()); 43 | ptr.as_ref() 44 | .map(|ptr| from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) 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::IPT_C2, 72 | #[cfg(feature = "ffmpeg_7_1")] 73 | AVCOL_SPC_YCGCO_RE => Space::YCGCO_RE, 74 | #[cfg(feature = "ffmpeg_7_1")] 75 | AVCOL_SPC_YCGCO_RO => Space::YCGCO_RO, 76 | } 77 | } 78 | } 79 | 80 | impl From for AVColorSpace { 81 | fn from(value: Space) -> AVColorSpace { 82 | match value { 83 | Space::RGB => AVCOL_SPC_RGB, 84 | Space::BT709 => AVCOL_SPC_BT709, 85 | Space::Unspecified => AVCOL_SPC_UNSPECIFIED, 86 | Space::Reserved => AVCOL_SPC_RESERVED, 87 | Space::FCC => AVCOL_SPC_FCC, 88 | Space::BT470BG => AVCOL_SPC_BT470BG, 89 | Space::SMPTE170M => AVCOL_SPC_SMPTE170M, 90 | Space::SMPTE240M => AVCOL_SPC_SMPTE240M, 91 | Space::YCGCO => AVCOL_SPC_YCGCO, 92 | Space::BT2020NCL => AVCOL_SPC_BT2020_NCL, 93 | Space::BT2020CL => AVCOL_SPC_BT2020_CL, 94 | Space::SMPTE2085 => AVCOL_SPC_SMPTE2085, 95 | 96 | Space::ChromaDerivedNCL => AVCOL_SPC_CHROMA_DERIVED_NCL, 97 | Space::ChromaDerivedCL => AVCOL_SPC_CHROMA_DERIVED_CL, 98 | Space::ICTCP => AVCOL_SPC_ICTCP, 99 | 100 | #[cfg(feature = "ffmpeg_7_1")] 101 | Space::IPT_C2 => AVCOL_SPC_IPT_C2, 102 | #[cfg(feature = "ffmpeg_7_1")] 103 | Space::YCGCO_RE => AVCOL_SPC_YCGCO_RE, 104 | #[cfg(feature = "ffmpeg_7_1")] 105 | Space::YCGCO_RO => AVCOL_SPC_YCGCO_RO, 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/util/color/transfer_characteristic.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::str::from_utf8_unchecked; 3 | 4 | use ffi::AVColorTransferCharacteristic::*; 5 | use ffi::*; 6 | 7 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 8 | pub enum TransferCharacteristic { 9 | Reserved0, 10 | BT709, 11 | Unspecified, 12 | Reserved, 13 | GAMMA22, 14 | GAMMA28, 15 | SMPTE170M, 16 | SMPTE240M, 17 | Linear, 18 | Log, 19 | LogSqrt, 20 | IEC61966_2_4, 21 | BT1361_ECG, 22 | IEC61966_2_1, 23 | BT2020_10, 24 | BT2020_12, 25 | SMPTE2084, 26 | SMPTE428, 27 | ARIB_STD_B67, 28 | } 29 | 30 | impl TransferCharacteristic { 31 | pub fn name(&self) -> Option<&'static str> { 32 | if *self == TransferCharacteristic::Unspecified { 33 | return None; 34 | } 35 | unsafe { 36 | let ptr = av_color_transfer_name((*self).into()); 37 | ptr.as_ref() 38 | .map(|ptr| from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) 39 | } 40 | } 41 | } 42 | 43 | impl From for TransferCharacteristic { 44 | fn from(value: AVColorTransferCharacteristic) -> TransferCharacteristic { 45 | match value { 46 | AVCOL_TRC_RESERVED0 => TransferCharacteristic::Reserved0, 47 | AVCOL_TRC_BT709 => TransferCharacteristic::BT709, 48 | AVCOL_TRC_UNSPECIFIED => TransferCharacteristic::Unspecified, 49 | AVCOL_TRC_RESERVED => TransferCharacteristic::Reserved, 50 | AVCOL_TRC_GAMMA22 => TransferCharacteristic::GAMMA22, 51 | AVCOL_TRC_GAMMA28 => TransferCharacteristic::GAMMA28, 52 | AVCOL_TRC_SMPTE170M => TransferCharacteristic::SMPTE170M, 53 | AVCOL_TRC_SMPTE240M => TransferCharacteristic::SMPTE240M, 54 | AVCOL_TRC_LINEAR => TransferCharacteristic::Linear, 55 | AVCOL_TRC_LOG => TransferCharacteristic::Log, 56 | AVCOL_TRC_LOG_SQRT => TransferCharacteristic::LogSqrt, 57 | AVCOL_TRC_IEC61966_2_4 => TransferCharacteristic::IEC61966_2_4, 58 | AVCOL_TRC_BT1361_ECG => TransferCharacteristic::BT1361_ECG, 59 | AVCOL_TRC_IEC61966_2_1 => TransferCharacteristic::IEC61966_2_1, 60 | AVCOL_TRC_BT2020_10 => TransferCharacteristic::BT2020_10, 61 | AVCOL_TRC_BT2020_12 => TransferCharacteristic::BT2020_12, 62 | AVCOL_TRC_NB => TransferCharacteristic::Reserved0, 63 | AVCOL_TRC_SMPTE2084 => TransferCharacteristic::SMPTE2084, 64 | AVCOL_TRC_SMPTE428 => TransferCharacteristic::SMPTE428, 65 | AVCOL_TRC_ARIB_STD_B67 => TransferCharacteristic::ARIB_STD_B67, 66 | } 67 | } 68 | } 69 | 70 | impl From for AVColorTransferCharacteristic { 71 | fn from(value: TransferCharacteristic) -> AVColorTransferCharacteristic { 72 | match value { 73 | TransferCharacteristic::Reserved0 => AVCOL_TRC_RESERVED0, 74 | TransferCharacteristic::BT709 => AVCOL_TRC_BT709, 75 | TransferCharacteristic::Unspecified => AVCOL_TRC_UNSPECIFIED, 76 | TransferCharacteristic::Reserved => AVCOL_TRC_RESERVED, 77 | TransferCharacteristic::GAMMA22 => AVCOL_TRC_GAMMA22, 78 | TransferCharacteristic::GAMMA28 => AVCOL_TRC_GAMMA28, 79 | TransferCharacteristic::SMPTE170M => AVCOL_TRC_SMPTE170M, 80 | TransferCharacteristic::SMPTE240M => AVCOL_TRC_SMPTE240M, 81 | TransferCharacteristic::Linear => AVCOL_TRC_LINEAR, 82 | TransferCharacteristic::Log => AVCOL_TRC_LOG, 83 | TransferCharacteristic::LogSqrt => AVCOL_TRC_LOG_SQRT, 84 | TransferCharacteristic::IEC61966_2_4 => AVCOL_TRC_IEC61966_2_4, 85 | TransferCharacteristic::BT1361_ECG => AVCOL_TRC_BT1361_ECG, 86 | TransferCharacteristic::IEC61966_2_1 => AVCOL_TRC_IEC61966_2_1, 87 | TransferCharacteristic::BT2020_10 => AVCOL_TRC_BT2020_10, 88 | TransferCharacteristic::BT2020_12 => AVCOL_TRC_BT2020_12, 89 | TransferCharacteristic::SMPTE2084 => AVCOL_TRC_SMPTE2084, 90 | TransferCharacteristic::SMPTE428 => AVCOL_TRC_SMPTE428, 91 | TransferCharacteristic::ARIB_STD_B67 => AVCOL_TRC_ARIB_STD_B67, 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/util/dictionary/immutable.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{CStr, CString}; 2 | use std::fmt; 3 | use std::marker::PhantomData; 4 | use std::ptr; 5 | use std::str::from_utf8_unchecked; 6 | 7 | use super::{Iter, Owned}; 8 | use ffi::*; 9 | 10 | pub struct Ref<'a> { 11 | ptr: *const AVDictionary, 12 | 13 | _marker: PhantomData<&'a ()>, 14 | } 15 | 16 | impl<'a> Ref<'a> { 17 | pub unsafe fn wrap(ptr: *const AVDictionary) -> Self { 18 | Ref { 19 | ptr, 20 | _marker: PhantomData, 21 | } 22 | } 23 | 24 | pub unsafe fn as_ptr(&self) -> *const AVDictionary { 25 | self.ptr 26 | } 27 | } 28 | 29 | impl<'a> Ref<'a> { 30 | pub fn get(&'a self, key: &str) -> Option<&'a str> { 31 | unsafe { 32 | let key = CString::new(key).unwrap(); 33 | let entry = av_dict_get(self.as_ptr(), key.as_ptr(), ptr::null_mut(), 0); 34 | 35 | if entry.is_null() { 36 | None 37 | } else { 38 | Some(from_utf8_unchecked( 39 | CStr::from_ptr((*entry).value).to_bytes(), 40 | )) 41 | } 42 | } 43 | } 44 | 45 | pub fn iter(&self) -> Iter { 46 | unsafe { Iter::new(self.as_ptr()) } 47 | } 48 | 49 | pub fn to_owned<'b>(&self) -> Owned<'b> { 50 | self.iter().collect() 51 | } 52 | } 53 | 54 | impl<'a> IntoIterator for &'a Ref<'a> { 55 | type Item = (&'a str, &'a str); 56 | type IntoIter = Iter<'a>; 57 | 58 | fn into_iter(self) -> Self::IntoIter { 59 | self.iter() 60 | } 61 | } 62 | 63 | impl<'a> fmt::Debug for Ref<'a> { 64 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 65 | fmt.debug_map().entries(self.iter()).finish() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/util/dictionary/iter.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{CStr, CString}; 2 | use std::marker::PhantomData; 3 | use std::ptr; 4 | use std::str::from_utf8_unchecked; 5 | 6 | use ffi::*; 7 | 8 | pub struct Iter<'a> { 9 | ptr: *const AVDictionary, 10 | cur: *mut AVDictionaryEntry, 11 | 12 | _marker: PhantomData<&'a ()>, 13 | } 14 | 15 | impl<'a> Iter<'a> { 16 | pub fn new(dictionary: *const AVDictionary) -> Self { 17 | Iter { 18 | ptr: dictionary, 19 | cur: ptr::null_mut(), 20 | 21 | _marker: PhantomData, 22 | } 23 | } 24 | } 25 | 26 | impl<'a> Iterator for Iter<'a> { 27 | type Item = (&'a str, &'a str); 28 | 29 | fn next(&mut self) -> Option<::Item> { 30 | unsafe { 31 | let empty = CString::new("").unwrap(); 32 | let entry = av_dict_get(self.ptr, empty.as_ptr(), self.cur, AV_DICT_IGNORE_SUFFIX); 33 | 34 | if !entry.is_null() { 35 | let key = from_utf8_unchecked(CStr::from_ptr((*entry).key).to_bytes()); 36 | let val = from_utf8_unchecked(CStr::from_ptr((*entry).value).to_bytes()); 37 | 38 | self.cur = entry; 39 | 40 | Some((key, val)) 41 | } else { 42 | None 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/util/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 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::iter::FromIterator; 3 | use std::ops::{Deref, DerefMut}; 4 | use std::ptr; 5 | 6 | use super::mutable; 7 | use ffi::*; 8 | 9 | pub struct Owned<'a> { 10 | inner: mutable::Ref<'a>, 11 | } 12 | 13 | impl<'a> Default for Owned<'a> { 14 | fn default() -> Self { 15 | Self::new() 16 | } 17 | } 18 | 19 | impl<'a> Owned<'a> { 20 | pub unsafe fn own(ptr: *mut AVDictionary) -> Self { 21 | Owned { 22 | inner: mutable::Ref::wrap(ptr), 23 | } 24 | } 25 | 26 | pub unsafe fn disown(mut self) -> *mut AVDictionary { 27 | let result = self.inner.as_mut_ptr(); 28 | self.inner = mutable::Ref::wrap(ptr::null_mut()); 29 | 30 | result 31 | } 32 | } 33 | 34 | impl<'a> Owned<'a> { 35 | pub fn new() -> Self { 36 | unsafe { 37 | Owned { 38 | inner: mutable::Ref::wrap(ptr::null_mut()), 39 | } 40 | } 41 | } 42 | } 43 | 44 | impl<'a, 'b> FromIterator<(&'b str, &'b str)> for Owned<'a> { 45 | fn from_iter>(iterator: T) -> Self { 46 | let mut result = Owned::new(); 47 | 48 | for (key, value) in iterator { 49 | result.set(key, value); 50 | } 51 | 52 | result 53 | } 54 | } 55 | 56 | impl<'a, 'b> FromIterator<&'b (&'b str, &'b str)> for Owned<'a> { 57 | fn from_iter>(iterator: T) -> Self { 58 | let mut result = Owned::new(); 59 | 60 | for &(key, value) in iterator { 61 | result.set(key, value); 62 | } 63 | 64 | result 65 | } 66 | } 67 | 68 | impl<'a> FromIterator<(String, String)> for Owned<'a> { 69 | fn from_iter>(iterator: T) -> Self { 70 | let mut result = Owned::new(); 71 | 72 | for (key, value) in iterator { 73 | result.set(&key, &value); 74 | } 75 | 76 | result 77 | } 78 | } 79 | 80 | impl<'a, 'b> FromIterator<&'b (String, String)> for Owned<'a> { 81 | fn from_iter>(iterator: T) -> Self { 82 | let mut result = Owned::new(); 83 | 84 | for (key, value) in iterator { 85 | result.set(key, value); 86 | } 87 | 88 | result 89 | } 90 | } 91 | 92 | impl<'a> Deref for Owned<'a> { 93 | type Target = mutable::Ref<'a>; 94 | 95 | fn deref(&self) -> &Self::Target { 96 | &self.inner 97 | } 98 | } 99 | 100 | impl<'a> DerefMut for Owned<'a> { 101 | fn deref_mut(&mut self) -> &mut Self::Target { 102 | &mut self.inner 103 | } 104 | } 105 | 106 | impl<'a> Clone for Owned<'a> { 107 | fn clone(&self) -> Self { 108 | let mut dictionary = Owned::new(); 109 | dictionary.clone_from(self); 110 | 111 | dictionary 112 | } 113 | 114 | fn clone_from(&mut self, source: &Self) { 115 | unsafe { 116 | let mut ptr = self.as_mut_ptr(); 117 | av_dict_copy(&mut ptr, source.as_ptr(), 0); 118 | self.inner = mutable::Ref::wrap(ptr); 119 | } 120 | } 121 | } 122 | 123 | impl<'a> Drop for Owned<'a> { 124 | fn drop(&mut self) { 125 | unsafe { 126 | av_dict_free(&mut self.inner.as_mut_ptr()); 127 | } 128 | } 129 | } 130 | 131 | impl<'a> fmt::Debug for Owned<'a> { 132 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 133 | self.inner.fmt(fmt) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/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 ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 6 | pub struct Flags: c_int { 7 | const CORRUPT = AV_FRAME_FLAG_CORRUPT; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/util/interrupt.rs: -------------------------------------------------------------------------------- 1 | use std::panic; 2 | use std::process; 3 | 4 | use ffi::*; 5 | use libc::{c_int, c_void}; 6 | 7 | pub struct Interrupt { 8 | pub interrupt: AVIOInterruptCB, 9 | } 10 | 11 | extern "C" fn callback(opaque: *mut c_void) -> c_int 12 | where 13 | F: FnMut() -> bool, 14 | { 15 | // Clippy suggests to remove &mut, but it doesn't compile then (move occurs because value has type `F`, which does not implement the `Copy` trait) 16 | #[allow(clippy::needless_borrow)] 17 | match panic::catch_unwind(|| (unsafe { &mut *(opaque as *mut F) })()) { 18 | Ok(ret) => ret as c_int, 19 | Err(_) => process::abort(), 20 | } 21 | } 22 | 23 | pub fn new(opaque: Box) -> Interrupt 24 | where 25 | F: FnMut() -> bool, 26 | { 27 | let interrupt_cb = AVIOInterruptCB { 28 | callback: Some(callback::), 29 | opaque: Box::into_raw(opaque) as *mut c_void, 30 | }; 31 | Interrupt { 32 | interrupt: interrupt_cb, 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/util/legacy_channel_layout.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_ulonglong; 3 | 4 | bitflags! { 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 6 | pub struct ChannelLayout: c_ulonglong { 7 | const FRONT_LEFT = AV_CH_FRONT_LEFT; 8 | const FRONT_RIGHT = AV_CH_FRONT_RIGHT; 9 | const FRONT_CENTER = AV_CH_FRONT_CENTER; 10 | const LOW_FREQUENCY = AV_CH_LOW_FREQUENCY; 11 | const BACK_LEFT = AV_CH_BACK_LEFT; 12 | const BACK_RIGHT = AV_CH_BACK_RIGHT; 13 | const FRONT_LEFT_OF_CENTER = AV_CH_FRONT_LEFT_OF_CENTER; 14 | const FRONT_RIGHT_OF_CENTER = AV_CH_FRONT_RIGHT_OF_CENTER; 15 | const BACK_CENTER = AV_CH_BACK_CENTER; 16 | const SIDE_LEFT = AV_CH_SIDE_LEFT; 17 | const SIDE_RIGHT = AV_CH_SIDE_RIGHT; 18 | const TOP_CENTER = AV_CH_TOP_CENTER; 19 | const TOP_FRONT_LEFT = AV_CH_TOP_FRONT_LEFT; 20 | const TOP_FRONT_CENTER = AV_CH_TOP_FRONT_CENTER; 21 | const TOP_FRONT_RIGHT = AV_CH_TOP_FRONT_RIGHT; 22 | const TOP_BACK_LEFT = AV_CH_TOP_BACK_LEFT; 23 | const TOP_BACK_CENTER = AV_CH_TOP_BACK_CENTER; 24 | const TOP_BACK_RIGHT = AV_CH_TOP_BACK_RIGHT; 25 | const STEREO_LEFT = AV_CH_STEREO_LEFT; 26 | const STEREO_RIGHT = AV_CH_STEREO_RIGHT; 27 | const WIDE_LEFT = AV_CH_WIDE_LEFT; 28 | const WIDE_RIGHT = AV_CH_WIDE_RIGHT; 29 | const SURROUND_DIRECT_LEFT = AV_CH_SURROUND_DIRECT_LEFT; 30 | const SURROUND_DIRECT_RIGHT = AV_CH_SURROUND_DIRECT_RIGHT; 31 | const LOW_FREQUENCY_2 = AV_CH_LOW_FREQUENCY_2; 32 | 33 | const MONO = AV_CH_LAYOUT_MONO; 34 | const STEREO = AV_CH_LAYOUT_STEREO; 35 | const _2POINT1 = AV_CH_LAYOUT_2POINT1; 36 | const _2_1 = AV_CH_LAYOUT_2_1; 37 | const SURROUND = AV_CH_LAYOUT_SURROUND; 38 | const _3POINT1 = AV_CH_LAYOUT_3POINT1; 39 | const _4POINT0 = AV_CH_LAYOUT_4POINT0; 40 | const _4POINT1 = AV_CH_LAYOUT_4POINT1; 41 | const _2_2 = AV_CH_LAYOUT_2_2; 42 | const QUAD = AV_CH_LAYOUT_QUAD; 43 | const _5POINT0 = AV_CH_LAYOUT_5POINT0; 44 | const _5POINT1 = AV_CH_LAYOUT_5POINT1; 45 | const _5POINT0_BACK = AV_CH_LAYOUT_5POINT0_BACK; 46 | const _5POINT1_BACK = AV_CH_LAYOUT_5POINT1_BACK; 47 | const _6POINT0 = AV_CH_LAYOUT_6POINT0; 48 | const _6POINT0_FRONT = AV_CH_LAYOUT_6POINT0_FRONT; 49 | const HEXAGONAL = AV_CH_LAYOUT_HEXAGONAL; 50 | const _6POINT1 = AV_CH_LAYOUT_6POINT1; 51 | const _6POINT1_BACK = AV_CH_LAYOUT_6POINT1_BACK; 52 | const _6POINT1_FRONT = AV_CH_LAYOUT_6POINT1_FRONT; 53 | const _7POINT0 = AV_CH_LAYOUT_7POINT0; 54 | const _7POINT0_FRONT = AV_CH_LAYOUT_7POINT0_FRONT; 55 | const _7POINT1 = AV_CH_LAYOUT_7POINT1; 56 | const _7POINT1_WIDE = AV_CH_LAYOUT_7POINT1_WIDE; 57 | const _7POINT1_WIDE_BACK = AV_CH_LAYOUT_7POINT1_WIDE_BACK; 58 | const OCTAGONAL = AV_CH_LAYOUT_OCTAGONAL; 59 | const HEXADECAGONAL = AV_CH_LAYOUT_HEXADECAGONAL; 60 | const STEREO_DOWNMIX = AV_CH_LAYOUT_STEREO_DOWNMIX; 61 | 62 | #[cfg(feature = "ffmpeg_6_1")] 63 | const _3POINT1POINT2 = AV_CH_LAYOUT_3POINT1POINT2; 64 | #[cfg(feature = "ffmpeg_6_1")] 65 | const _5POINT1POINT2_BACK = AV_CH_LAYOUT_5POINT1POINT2_BACK; 66 | #[cfg(feature = "ffmpeg_6_1")] 67 | const _5POINT1POINT4_BACK = AV_CH_LAYOUT_5POINT1POINT4_BACK; 68 | #[cfg(feature = "ffmpeg_6_1")] 69 | const _7POINT1POINT2 = AV_CH_LAYOUT_7POINT1POINT2; 70 | #[cfg(feature = "ffmpeg_6_1")] 71 | const _7POINT1POINT4_BACK = AV_CH_LAYOUT_7POINT1POINT4_BACK; 72 | #[cfg(feature = "ffmpeg_6_1")] 73 | const CUBE = AV_CH_LAYOUT_CUBE; 74 | } 75 | } 76 | 77 | impl ChannelLayout { 78 | #[inline] 79 | pub fn channels(&self) -> i32 { 80 | unsafe { av_get_channel_layout_nb_channels(self.bits()) } 81 | } 82 | 83 | pub fn default(number: i32) -> ChannelLayout { 84 | unsafe { 85 | ChannelLayout::from_bits_truncate(av_get_default_channel_layout(number) as c_ulonglong) 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/util/log/flag.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use libc::c_int; 3 | 4 | bitflags! { 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 6 | pub struct Flags: c_int { 7 | const SKIP_REPEATED = AV_LOG_SKIP_REPEATED; 8 | const PRINT_LEVEL = AV_LOG_PRINT_LEVEL; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/util/log/level.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | 3 | use ffi::*; 4 | use libc::c_int; 5 | 6 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 7 | pub enum Level { 8 | Quiet, 9 | Panic, 10 | Fatal, 11 | Error, 12 | Warning, 13 | Info, 14 | Verbose, 15 | Debug, 16 | Trace, 17 | } 18 | 19 | pub struct LevelError; 20 | 21 | impl TryFrom for Level { 22 | type Error = &'static str; 23 | 24 | fn try_from(value: c_int) -> Result { 25 | match value { 26 | AV_LOG_QUIET => Ok(Level::Quiet), 27 | AV_LOG_PANIC => Ok(Level::Panic), 28 | AV_LOG_FATAL => Ok(Level::Fatal), 29 | AV_LOG_ERROR => Ok(Level::Error), 30 | AV_LOG_WARNING => Ok(Level::Warning), 31 | AV_LOG_INFO => Ok(Level::Info), 32 | AV_LOG_VERBOSE => Ok(Level::Verbose), 33 | AV_LOG_DEBUG => Ok(Level::Debug), 34 | AV_LOG_TRACE => Ok(Level::Trace), 35 | _ => Err("illegal log level"), 36 | } 37 | } 38 | } 39 | 40 | impl From for c_int { 41 | fn from(value: Level) -> c_int { 42 | match value { 43 | Level::Quiet => AV_LOG_QUIET, 44 | Level::Panic => AV_LOG_PANIC, 45 | Level::Fatal => AV_LOG_FATAL, 46 | Level::Error => AV_LOG_ERROR, 47 | Level::Warning => AV_LOG_WARNING, 48 | Level::Info => AV_LOG_INFO, 49 | Level::Verbose => AV_LOG_VERBOSE, 50 | Level::Debug => AV_LOG_DEBUG, 51 | Level::Trace => AV_LOG_TRACE, 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/util/log/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod level; 2 | pub use self::level::Level; 3 | 4 | pub mod flag; 5 | pub use self::flag::Flags; 6 | 7 | use ffi::*; 8 | use std::convert::TryInto; 9 | 10 | pub fn set_level(value: Level) { 11 | unsafe { av_log_set_level(value.into()) } 12 | } 13 | 14 | pub fn get_level() -> Result { 15 | unsafe { av_log_get_level().try_into() } 16 | } 17 | 18 | pub fn set_flags(value: Flags) { 19 | unsafe { av_log_set_flags(value.bits()) } 20 | } 21 | 22 | pub fn get_flags() -> Flags { 23 | unsafe { Flags::from_bits_truncate(av_log_get_flags()) } 24 | } 25 | -------------------------------------------------------------------------------- /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 ffi::*; 2 | use {Rational, Rounding}; 3 | 4 | pub const TIME_BASE: Rational = Rational(AV_TIME_BASE_Q.num, AV_TIME_BASE_Q.den); 5 | 6 | pub trait Rescale { 7 | fn rescale(&self, source: S, destination: D) -> i64 8 | where 9 | S: Into, 10 | D: Into; 11 | 12 | fn rescale_with(&self, source: S, destination: D, rounding: Rounding) -> i64 13 | where 14 | S: Into, 15 | D: Into; 16 | } 17 | 18 | impl + Clone> Rescale for T { 19 | fn rescale(&self, source: S, destination: D) -> i64 20 | where 21 | S: Into, 22 | D: Into, 23 | { 24 | unsafe { 25 | av_rescale_q( 26 | self.clone().into(), 27 | source.into().into(), 28 | destination.into().into(), 29 | ) 30 | } 31 | } 32 | 33 | fn rescale_with(&self, source: S, destination: D, rounding: Rounding) -> i64 34 | where 35 | S: Into, 36 | D: Into, 37 | { 38 | unsafe { 39 | av_rescale_q_rnd( 40 | self.clone().into(), 41 | source.into().into(), 42 | destination.into().into(), 43 | rounding.into(), 44 | ) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/util/mathematics/rounding.rs: -------------------------------------------------------------------------------- 1 | use ffi::AVRounding::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Rounding { 6 | Zero, 7 | Infinity, 8 | Down, 9 | Up, 10 | NearInfinity, 11 | PassMinMax, 12 | } 13 | 14 | impl From for Rounding { 15 | #[inline(always)] 16 | fn from(value: AVRounding) -> Self { 17 | match value { 18 | AV_ROUND_ZERO => Rounding::Zero, 19 | AV_ROUND_INF => Rounding::Infinity, 20 | AV_ROUND_DOWN => Rounding::Down, 21 | AV_ROUND_UP => Rounding::Up, 22 | AV_ROUND_NEAR_INF => Rounding::NearInfinity, 23 | AV_ROUND_PASS_MINMAX => Rounding::PassMinMax, 24 | } 25 | } 26 | } 27 | 28 | impl From for AVRounding { 29 | #[inline(always)] 30 | fn from(value: Rounding) -> AVRounding { 31 | match value { 32 | Rounding::Zero => AV_ROUND_ZERO, 33 | Rounding::Infinity => AV_ROUND_INF, 34 | Rounding::Down => AV_ROUND_DOWN, 35 | Rounding::Up => AV_ROUND_UP, 36 | Rounding::NearInfinity => AV_ROUND_NEAR_INF, 37 | Rounding::PassMinMax => AV_ROUND_PASS_MINMAX, 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/util/media.rs: -------------------------------------------------------------------------------- 1 | use ffi::AVMediaType::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Type { 6 | Unknown, 7 | Video, 8 | Audio, 9 | Data, 10 | Subtitle, 11 | Attachment, 12 | } 13 | 14 | impl From for Type { 15 | #[inline(always)] 16 | fn from(value: AVMediaType) -> Self { 17 | match value { 18 | AVMEDIA_TYPE_UNKNOWN => Type::Unknown, 19 | AVMEDIA_TYPE_VIDEO => Type::Video, 20 | AVMEDIA_TYPE_AUDIO => Type::Audio, 21 | AVMEDIA_TYPE_DATA => Type::Data, 22 | AVMEDIA_TYPE_SUBTITLE => Type::Subtitle, 23 | AVMEDIA_TYPE_ATTACHMENT => Type::Attachment, 24 | AVMEDIA_TYPE_NB => Type::Unknown, 25 | } 26 | } 27 | } 28 | 29 | impl From for AVMediaType { 30 | #[inline(always)] 31 | fn from(value: Type) -> AVMediaType { 32 | match value { 33 | Type::Unknown => AVMEDIA_TYPE_UNKNOWN, 34 | Type::Video => AVMEDIA_TYPE_VIDEO, 35 | Type::Audio => AVMEDIA_TYPE_AUDIO, 36 | Type::Data => AVMEDIA_TYPE_DATA, 37 | Type::Subtitle => AVMEDIA_TYPE_SUBTITLE, 38 | Type::Attachment => AVMEDIA_TYPE_ATTACHMENT, 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | pub mod dictionary; 3 | pub mod chroma; 4 | pub mod color; 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 range; 15 | pub mod rational; 16 | pub mod time; 17 | 18 | #[cfg_attr(feature = "ffmpeg_7_0", path = "channel_layout.rs")] 19 | #[cfg_attr(not(feature = "ffmpeg_7_0"), path = "legacy_channel_layout.rs")] 20 | pub mod channel_layout; 21 | 22 | use std::ffi::CStr; 23 | use std::str::from_utf8_unchecked; 24 | 25 | use ffi::*; 26 | 27 | #[inline(always)] 28 | pub fn version() -> u32 { 29 | unsafe { avutil_version() } 30 | } 31 | 32 | #[inline(always)] 33 | pub fn configuration() -> &'static str { 34 | unsafe { from_utf8_unchecked(CStr::from_ptr(avutil_configuration()).to_bytes()) } 35 | } 36 | 37 | #[inline(always)] 38 | pub fn license() -> &'static str { 39 | unsafe { from_utf8_unchecked(CStr::from_ptr(avutil_license()).to_bytes()) } 40 | } 41 | -------------------------------------------------------------------------------- /src/util/option/mod.rs: -------------------------------------------------------------------------------- 1 | mod traits; 2 | pub use self::traits::{Gettable, Iterable, Settable, Target}; 3 | 4 | use ffi::AVOptionType::*; 5 | use ffi::*; 6 | 7 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] 8 | pub enum Type { 9 | Flags, 10 | Int, 11 | Int64, 12 | Double, 13 | Float, 14 | String, 15 | Rational, 16 | Binary, 17 | Dictionary, 18 | Constant, 19 | 20 | ImageSize, 21 | PixelFormat, 22 | SampleFormat, 23 | VideoRate, 24 | Duration, 25 | Color, 26 | ChannelLayout, 27 | #[cfg(feature = "ffmpeg_7_0")] 28 | FlagArray, 29 | c_ulong, 30 | bool, 31 | 32 | #[cfg(feature = "ffmpeg_7_1")] 33 | UInt, 34 | } 35 | 36 | impl From for Type { 37 | fn from(value: AVOptionType) -> Self { 38 | match value { 39 | AV_OPT_TYPE_FLAGS => Type::Flags, 40 | AV_OPT_TYPE_INT => Type::Int, 41 | AV_OPT_TYPE_INT64 => Type::Int64, 42 | AV_OPT_TYPE_DOUBLE => Type::Double, 43 | AV_OPT_TYPE_FLOAT => Type::Float, 44 | AV_OPT_TYPE_STRING => Type::String, 45 | AV_OPT_TYPE_RATIONAL => Type::Rational, 46 | AV_OPT_TYPE_BINARY => Type::Binary, 47 | AV_OPT_TYPE_DICT => Type::Dictionary, 48 | AV_OPT_TYPE_CONST => Type::Constant, 49 | AV_OPT_TYPE_UINT64 => Type::c_ulong, 50 | AV_OPT_TYPE_BOOL => Type::bool, 51 | 52 | AV_OPT_TYPE_IMAGE_SIZE => Type::ImageSize, 53 | AV_OPT_TYPE_PIXEL_FMT => Type::PixelFormat, 54 | AV_OPT_TYPE_SAMPLE_FMT => Type::SampleFormat, 55 | AV_OPT_TYPE_VIDEO_RATE => Type::VideoRate, 56 | AV_OPT_TYPE_DURATION => Type::Duration, 57 | AV_OPT_TYPE_COLOR => Type::Color, 58 | #[cfg(not(feature = "ffmpeg_7_0"))] 59 | AV_OPT_TYPE_CHANNEL_LAYOUT => Type::ChannelLayout, 60 | #[cfg(feature = "ffmpeg_5_1")] 61 | AV_OPT_TYPE_CHLAYOUT => Type::ChannelLayout, 62 | #[cfg(feature = "ffmpeg_7_0")] 63 | AV_OPT_TYPE_FLAG_ARRAY => Type::FlagArray, 64 | 65 | #[cfg(feature = "ffmpeg_7_1")] 66 | AV_OPT_TYPE_UINT => Type::UInt, 67 | } 68 | } 69 | } 70 | 71 | impl From for AVOptionType { 72 | fn from(value: Type) -> AVOptionType { 73 | match value { 74 | Type::Flags => AV_OPT_TYPE_FLAGS, 75 | Type::Int => AV_OPT_TYPE_INT, 76 | Type::Int64 => AV_OPT_TYPE_INT64, 77 | Type::Double => AV_OPT_TYPE_DOUBLE, 78 | Type::Float => AV_OPT_TYPE_FLOAT, 79 | Type::String => AV_OPT_TYPE_STRING, 80 | Type::Rational => AV_OPT_TYPE_RATIONAL, 81 | Type::Binary => AV_OPT_TYPE_BINARY, 82 | Type::Dictionary => AV_OPT_TYPE_DICT, 83 | Type::Constant => AV_OPT_TYPE_CONST, 84 | Type::c_ulong => AV_OPT_TYPE_UINT64, 85 | Type::bool => AV_OPT_TYPE_BOOL, 86 | 87 | Type::ImageSize => AV_OPT_TYPE_IMAGE_SIZE, 88 | Type::PixelFormat => AV_OPT_TYPE_PIXEL_FMT, 89 | Type::SampleFormat => AV_OPT_TYPE_SAMPLE_FMT, 90 | Type::VideoRate => AV_OPT_TYPE_VIDEO_RATE, 91 | Type::Duration => AV_OPT_TYPE_DURATION, 92 | Type::Color => AV_OPT_TYPE_COLOR, 93 | #[cfg(not(feature = "ffmpeg_7_0"))] 94 | Type::ChannelLayout => AV_OPT_TYPE_CHANNEL_LAYOUT, 95 | #[cfg(feature = "ffmpeg_7_0")] 96 | Type::ChannelLayout => AV_OPT_TYPE_CHLAYOUT, 97 | #[cfg(feature = "ffmpeg_7_0")] 98 | Type::FlagArray => AV_OPT_TYPE_FLAG_ARRAY, 99 | 100 | #[cfg(feature = "ffmpeg_7_1")] 101 | Type::UInt => AV_OPT_TYPE_UINT, 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/util/picture.rs: -------------------------------------------------------------------------------- 1 | use ffi::AVPictureType::*; 2 | use ffi::*; 3 | 4 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 5 | pub enum Type { 6 | None, 7 | I, 8 | P, 9 | B, 10 | S, 11 | SI, 12 | SP, 13 | BI, 14 | } 15 | 16 | impl From for Type { 17 | #[inline(always)] 18 | fn from(value: AVPictureType) -> Type { 19 | match value { 20 | AV_PICTURE_TYPE_NONE => Type::None, 21 | AV_PICTURE_TYPE_I => Type::I, 22 | AV_PICTURE_TYPE_P => Type::P, 23 | AV_PICTURE_TYPE_B => Type::B, 24 | AV_PICTURE_TYPE_S => Type::S, 25 | AV_PICTURE_TYPE_SI => Type::SI, 26 | AV_PICTURE_TYPE_SP => Type::SP, 27 | AV_PICTURE_TYPE_BI => Type::BI, 28 | } 29 | } 30 | } 31 | 32 | impl From for AVPictureType { 33 | #[inline(always)] 34 | fn from(value: Type) -> AVPictureType { 35 | match value { 36 | Type::None => AV_PICTURE_TYPE_NONE, 37 | Type::I => AV_PICTURE_TYPE_I, 38 | Type::P => AV_PICTURE_TYPE_P, 39 | Type::B => AV_PICTURE_TYPE_B, 40 | Type::S => AV_PICTURE_TYPE_S, 41 | Type::SI => AV_PICTURE_TYPE_SI, 42 | Type::SP => AV_PICTURE_TYPE_SP, 43 | Type::BI => AV_PICTURE_TYPE_BI, 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/util/range.rs: -------------------------------------------------------------------------------- 1 | use std::ops; 2 | 3 | pub trait Range { 4 | fn start(&self) -> Option<&T> { 5 | None 6 | } 7 | 8 | fn end(&self) -> Option<&T> { 9 | None 10 | } 11 | } 12 | 13 | impl Range for ops::Range { 14 | fn start(&self) -> Option<&T> { 15 | Some(&self.start) 16 | } 17 | 18 | fn end(&self) -> Option<&T> { 19 | Some(&self.end) 20 | } 21 | } 22 | 23 | impl Range for ops::RangeTo { 24 | fn end(&self) -> Option<&T> { 25 | Some(&self.end) 26 | } 27 | } 28 | 29 | impl Range for ops::RangeFrom { 30 | fn start(&self) -> Option<&T> { 31 | Some(&self.start) 32 | } 33 | } 34 | 35 | impl Range for ops::RangeFull {} 36 | -------------------------------------------------------------------------------- /src/util/rounding.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmwangx/rust-ffmpeg/a7b50dd5f909e071affffe238d434d184da30aee/src/util/rounding.rs -------------------------------------------------------------------------------- /src/util/time.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use Error; 3 | 4 | #[inline(always)] 5 | pub fn current() -> i64 { 6 | unsafe { av_gettime() } 7 | } 8 | 9 | #[inline(always)] 10 | pub fn relative() -> i64 { 11 | unsafe { av_gettime_relative() } 12 | } 13 | 14 | #[inline(always)] 15 | pub fn is_monotonic() -> bool { 16 | unsafe { av_gettime_relative_is_monotonic() != 0 } 17 | } 18 | 19 | #[inline(always)] 20 | pub fn sleep(usec: u32) -> Result<(), Error> { 21 | unsafe { 22 | match av_usleep(usec) { 23 | 0 => Ok(()), 24 | e => Err(Error::from(e)), 25 | } 26 | } 27 | } 28 | --------------------------------------------------------------------------------