diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..db098a45 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,107 @@ +name: build +on: + push: + pull_request: + schedule: + - cron: "0 0 * * *" +jobs: + build-test-lint-linux: + name: Linux - FFmpeg ${{ matrix.ffmpeg_version }} - build, test and lint + runs-on: ubuntu-latest + container: jrottenberg/ffmpeg:${{ matrix.ffmpeg_version }}-ubuntu + strategy: + matrix: + ffmpeg_version: ["3.4", "4.0", "4.1", "4.2", "4.3"] + fail-fast: false + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: | + apt update + apt install -y --no-install-recommends clang curl pkg-config + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + components: rustfmt, clippy + - name: Build + run: | + cargo build --examples + - name: Test + run: | + cargo test --examples + - name: Lint + run: | + cargo clippy --examples -- -D warnings + - name: Check format + run: | + cargo fmt -- --check + + build-test-lint-macos: + name: macOS - FFmpeg latest - build, test and lint + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: | + brew install ffmpeg pkg-config + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + components: rustfmt, clippy + - name: Build + run: | + cargo build --examples + - name: Test + run: | + cargo test --examples + - name: Lint + run: | + cargo clippy --examples -- -D warnings + - name: Check format + run: | + cargo fmt -- --check + + build-test-lint-windows: + name: Windows - FFmpeg ${{ matrix.ffmpeg_version }} - build, test and lint + runs-on: windows-latest + strategy: + matrix: + include: + - ffmpeg_version: latest + ffmpeg_download_url: https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full-shared.7z + fail-fast: false + env: + FFMPEG_DOWNLOAD_URL: ${{ matrix.ffmpeg_download_url }} + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: | + $VCINSTALLDIR = $(& "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath) + Add-Content $env:GITHUB_ENV "LIBCLANG_PATH=${VCINSTALLDIR}\VC\Tools\LLVM\x64\bin`n" + Invoke-WebRequest "${env:FFMPEG_DOWNLOAD_URL}" -OutFile ffmpeg-release-full-shared.7z + 7z x ffmpeg-release-full-shared.7z + mkdir ffmpeg + mv ffmpeg-*/* ffmpeg/ + Add-Content $env:GITHUB_ENV "FFMPEG_DIR=${pwd}\ffmpeg`n" + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + components: rustfmt, clippy + - name: Build + run: | + cargo build --examples + - name: Test + run: | + cargo test --examples + - name: Lint + run: | + cargo clippy --examples -- -D warnings + - name: Check format + run: | + cargo fmt -- --check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..370c48ed --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,26 @@ +name: create release +on: + push: + tags: + - "v*" +jobs: + release: + name: Create release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Generate release notes + run: | + cat >release_notes.md <`, now `Result<(), Error>`. (#25) + +4.3.7 +----- + +- codec: fix codec description potential null ptr issue. (#36) + +4.3.6 +----- + +- util: fix Windows compatibility due to unavailable errnos. (#30) + +4.3.5 +----- + +- util: add `util::log` module to expose FFmpeg's logging facilities. + +- filter: add method `Source::close()` to expose `av_buffersrc_close`. (#23) + +- 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) + +- 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) + +4.3.4 +----- + +- 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. diff --git a/Cargo.toml b/Cargo.toml index 57d5f11f..cd6fdc6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,19 +1,34 @@ [package] -name = "ffmpeg" -version = "0.2.0-alpha.2" +name = "ffmpeg-next" +version = "4.4.0-dev" build = "build.rs" +edition = "2018" -authors = ["meh. "] +authors = ["meh. ", "Zhiming Wang "] license = "WTFPL" -description = "Safe FFmpeg wrapper" -repository = "https://github.com/meh/rust-ffmpeg" -keywords = ["audio", "video"] +description = "Safe FFmpeg wrapper (FFmpeg 4 compatible fork of the ffmpeg crate)" +documentation = "https://docs.rs/ffmpeg-next" +homepage = "https://github.com/zmwangx/rust-ffmpeg#readme" +repository = "https://github.com/zmwangx/rust-ffmpeg" +readme = "README.md" +keywords = ["ffmpeg", "multimedia", "video", "audio"] +categories = ["multimedia"] [features] -default = ["codec", "device", "filter", "format", "resampling", "software-resampling", "software-scaling"] +default = ["codec", "device", "filter", "format", "software-resampling", "software-scaling"] + +# ffmpeg are obsolete features kept for backward compatibility purposes and +# don't do anything anymore (equivalents are automatically specified through +# compile-time detection in ffmpeg-sys). Deprecation plan: all these +# features will be removed come 5.0. +ffmpeg43 = [] +ffmpeg42 = [] +ffmpeg41 = [] +ffmpeg4 = [] static = ["ffmpeg-sys/static"] +# Currently the version built by ffmpeg-sys is 4.1 build = ["static", "ffmpeg-sys/build"] # licensing @@ -86,14 +101,26 @@ postprocessing = ["ffmpeg-sys/postproc"] software-resampling = ["ffmpeg-sys/swresample"] software-scaling = ["ffmpeg-sys/swscale", "codec"] +# platforms +rpi = [] + [dependencies] libc = "0.2" -bitflags = "0.9" +bitflags = "1.2" +thiserror = "1" + +[dependencies.serde] +version = "1.0.0" +optional = true +features = ["derive"] [dependencies.image] version = "0.12" optional = true [dependencies.ffmpeg-sys] -version = "3.3" +version = "4.3.5" default-features = false + +[patch.crates-io] +ffmpeg-sys = { path = "../rust-ffmpeg-sys" } diff --git a/README.md b/README.md new file mode 100644 index 00000000..38d7f445 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +[![crates.io](https://img.shields.io/crates/v/ffmpeg-next.svg)](https://crates.io/crates/ffmpeg-next) +[![docs.rs](https://docs.rs/ffmpeg-next/badge.svg)](https://docs.rs/ffmpeg-next/) +[![build](https://github.com/zmwangx/rust-ffmpeg/workflows/build/badge.svg)](https://github.com/zmwangx/rust-ffmpeg/actions) + +This is a fork of the abandoned [ffmpeg](https://crates.io/crates/ffmpeg) crate by [meh.](https://github.com/meh/rust-ffmpeg). + +Currently supported FFmpeg versions: 3.4.x through 4.3.x. + +Build instructions can be found on the [wiki](https://github.com/zmwangx/rust-ffmpeg/wiki/Notes-on-building). + +Documentation: + +- [docs.rs](https://docs.rs/ffmpeg-next/); +- [FFmpeg user manual](https://ffmpeg.org/ffmpeg-all.html); +- [FFmpeg Doxygen](https://ffmpeg.org/doxygen/trunk/). + +*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.* + +*See [CHANGELOG.md](CHANGELOG.md) for other information on version upgrades.* + +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. + +**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. + +🤝 **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.** 🤝 diff --git a/examples/chapters.rs b/examples/chapters.rs index 02e64787..a2a06133 100644 --- a/examples/chapters.rs +++ b/examples/chapters.rs @@ -1,4 +1,4 @@ -extern crate ffmpeg; +extern crate ffmpeg_next as ffmpeg; use std::env; diff --git a/examples/codec-info.rs b/examples/codec-info.rs index 82877cae..1cd8e863 100644 --- a/examples/codec-info.rs +++ b/examples/codec-info.rs @@ -1,4 +1,4 @@ -extern crate ffmpeg; +extern crate ffmpeg_next as ffmpeg; use std::env; @@ -16,20 +16,23 @@ fn main() { if let Some(profiles) = codec.profiles() { println!("\t profiles: {:?}", profiles.collect::>()); - } else { + } + else { println!("\t profiles: none"); } if let Ok(video) = codec.video() { if let Some(rates) = video.rates() { println!("\t rates: {:?}", rates.collect::>()); - } else { + } + else { println!("\t rates: any"); } if let Some(formats) = video.formats() { println!("\t formats: {:?}", formats.collect::>()); - } else { + } + else { println!("\t formats: any"); } } @@ -37,19 +40,22 @@ fn main() { if let Ok(audio) = codec.audio() { if let Some(rates) = audio.rates() { println!("\t rates: {:?}", rates.collect::>()); - } else { + } + else { println!("\t rates: any"); } if let Some(formats) = audio.formats() { println!("\t formats: {:?}", formats.collect::>()); - } else { + } + else { println!("\t formats: any"); } if let Some(layouts) = audio.channel_layouts() { println!("\t channel_layouts: {:?}", layouts.collect::>()); - } else { + } + else { println!("\t channel_layouts: any"); } } @@ -73,13 +79,15 @@ fn main() { if let Ok(video) = codec.video() { if let Some(rates) = video.rates() { println!("\t rates: {:?}", rates.collect::>()); - } else { + } + else { println!("\t rates: any"); } if let Some(formats) = video.formats() { println!("\t formats: {:?}", formats.collect::>()); - } else { + } + else { println!("\t formats: any"); } } @@ -87,19 +95,22 @@ fn main() { if let Ok(audio) = codec.audio() { if let Some(rates) = audio.rates() { println!("\t rates: {:?}", rates.collect::>()); - } else { + } + else { println!("\t rates: any"); } if let Some(formats) = audio.formats() { println!("\t formats: {:?}", formats.collect::>()); - } else { + } + else { println!("\t formats: any"); } if let Some(layouts) = audio.channel_layouts() { println!("\t channel_layouts: {:?}", layouts.collect::>()); - } else { + } + else { println!("\t channel_layouts: any"); } } diff --git a/examples/dump-frames.rs b/examples/dump-frames.rs new file mode 100644 index 00000000..db520b4f --- /dev/null +++ b/examples/dump-frames.rs @@ -0,0 +1,66 @@ +extern crate ffmpeg_next as ffmpeg; + +use crate::ffmpeg::{ + format::{input, Pixel}, + media::Type, + software::scaling::{context::Context, flag::Flags}, + util::frame::video::Video, +}; +use std::{env, fs::File, io::prelude::*}; + +fn main() -> Result<(), ffmpeg::Error> { + ffmpeg::init().unwrap(); + + if let Ok(mut ictx) = input(&env::args().nth(1).expect("Cannot open file.")) { + let input = ictx + .streams() + .best(Type::Video) + .ok_or(ffmpeg::Error::StreamNotFound)?; + let video_stream_index = input.index(); + + let mut decoder = input.codec().decoder().video()?; + + let mut scaler = Context::get( + decoder.format(), + decoder.width(), + decoder.height(), + Pixel::RGB24, + decoder.width(), + decoder.height(), + Flags::BILINEAR, + )?; + + let mut frame_index = 0; + + let mut receive_and_process_decoded_frames = + |decoder: &mut ffmpeg::decoder::Video| -> Result<(), ffmpeg::Error> { + let mut decoded = Video::empty(); + while decoder.receive_frame(&mut decoded).is_ok() { + let mut rgb_frame = Video::empty(); + scaler.run(&decoded, &mut rgb_frame)?; + save_file(&rgb_frame, frame_index).unwrap(); + frame_index += 1; + } + Ok(()) + }; + + let mut packets = ictx.packets(); + while let Some(Ok((stream, packet))) = packets.next() { + if stream.index() == video_stream_index { + decoder.send_packet(&packet)?; + receive_and_process_decoded_frames(&mut decoder)?; + } + } + decoder.send_eof()?; + receive_and_process_decoded_frames(&mut decoder)?; + } + + Ok(()) +} + +fn save_file(frame: &Video, index: usize) -> std::result::Result<(), std::io::Error> { + let mut file = File::create(format!("frame{}.ppm", index))?; + file.write_all(format!("P6\n{} {}\n255\n", frame.width(), frame.height()).as_bytes())?; + file.write_all(frame.data(0))?; + Ok(()) +} diff --git a/examples/metadata.rs b/examples/metadata.rs index 1f8c8844..5d51620d 100644 --- a/examples/metadata.rs +++ b/examples/metadata.rs @@ -1,4 +1,4 @@ -extern crate ffmpeg; +extern crate ffmpeg_next as ffmpeg; use std::env; @@ -67,7 +67,8 @@ fn main() { println!("\tvideo.references: {}", video.references()); println!("\tvideo.intra_dc_precision: {}", video.intra_dc_precision()); } - } else if codec.medium() == ffmpeg::media::Type::Audio { + } + else if codec.medium() == ffmpeg::media::Type::Audio { if let Ok(audio) = codec.decoder().audio() { println!("\tbit_rate: {}", audio.bit_rate()); println!("\tmax_rate: {}", audio.max_bit_rate()); diff --git a/examples/remux.rs b/examples/remux.rs new file mode 100644 index 00000000..9d36707f --- /dev/null +++ b/examples/remux.rs @@ -0,0 +1,60 @@ +extern crate ffmpeg_next as ffmpeg; + +use std::env; + +use crate::ffmpeg::{codec, encoder, format, log, media, Rational}; + +fn main() { + let input_file = env::args().nth(1).expect("missing input file"); + let output_file = env::args().nth(2).expect("missing output file"); + + ffmpeg::init().unwrap(); + log::set_level(log::Level::Warning); + + let mut ictx = format::input(&input_file).unwrap(); + let mut octx = format::output(&output_file).unwrap(); + + let mut stream_mapping = vec![0; ictx.nb_streams() as _]; + let mut ist_time_bases = vec![Rational(0, 1); ictx.nb_streams() as _]; + let mut ost_index = 0; + for (ist_index, ist) in ictx.streams().enumerate() { + let ist_medium = ist.codec().medium(); + if ist_medium != media::Type::Audio + && ist_medium != media::Type::Video + && ist_medium != media::Type::Subtitle + { + stream_mapping[ist_index] = -1; + continue; + } + stream_mapping[ist_index] = ost_index; + ist_time_bases[ist_index] = ist.time_base(); + ost_index += 1; + let mut ost = octx.add_stream(encoder::find(codec::Id::None)).unwrap(); + ost.set_parameters(ist.parameters()); + // We need to set codec_tag to 0 lest we run into incompatible codec tag + // issues when muxing into a different container format. Unfortunately + // there's no high level API to do this (yet). + unsafe { + (*ost.parameters().as_mut_ptr()).codec_tag = 0; + } + } + + octx.set_metadata(ictx.metadata().to_owned()); + octx.write_header().unwrap(); + + let mut packets = ictx.packets(); + while let Some(Ok((stream, mut packet))) = packets.next() { + let ist_index = stream.index(); + let ost_index = stream_mapping[ist_index]; + if ost_index < 0 { + continue; + } + let ost = octx.stream(ost_index as _).unwrap(); + packet.rescale_ts(ist_time_bases[ist_index], ost.time_base()); + packet.set_position(-1); + packet.set_stream(ost_index as _); + packet.write_interleaved(&mut octx).unwrap(); + } + + octx.write_trailer().unwrap(); +} diff --git a/examples/transcode-audio.rs b/examples/transcode-audio.rs index 0d416a18..0d4f5e5f 100644 --- a/examples/transcode-audio.rs +++ b/examples/transcode-audio.rs @@ -1,10 +1,8 @@ -extern crate ffmpeg; +extern crate ffmpeg_next as ffmpeg; -use std::env; -use std::path::Path; +use std::{env, path::Path}; -use ffmpeg::{codec, filter, format, frame, media}; -use ffmpeg::{rescale, Rescale}; +use crate::ffmpeg::{codec, filter, format, frame, media, rescale, Rescale}; fn filter( spec: &str, @@ -40,7 +38,7 @@ fn filter( if let Some(codec) = encoder.codec() { if !codec .capabilities() - .contains(ffmpeg::codec::capabilities::VARIABLE_FRAME_SIZE) + .contains(ffmpeg::codec::capabilities::Capabilities::VARIABLE_FRAME_SIZE) { filter .get("out") @@ -58,6 +56,8 @@ struct Transcoder { filter: filter::Graph, decoder: codec::decoder::Audio, encoder: codec::encoder::Audio, + in_time_base: ffmpeg::Rational, + out_time_base: ffmpeg::Rational, } fn transcoder>( @@ -66,16 +66,18 @@ fn transcoder>( path: &P, filter_spec: &str, ) -> Result { - let input = ictx.streams() + let input = ictx + .streams() .best(media::Type::Audio) .expect("could not find best audio stream"); let mut decoder = input.codec().decoder().audio()?; let codec = ffmpeg::encoder::find(octx.format().codec(path, media::Type::Audio)) .expect("failed to find encoder") .audio()?; - let global = octx.format() + let global = octx + .format() .flags() - .contains(ffmpeg::format::flag::GLOBAL_HEADER); + .contains(ffmpeg::format::flag::Flags::GLOBAL_HEADER); decoder.set_parameters(input.parameters())?; @@ -85,10 +87,10 @@ fn transcoder>( let channel_layout = codec .channel_layouts() .map(|cls| cls.best(decoder.channel_layout().channels())) - .unwrap_or(ffmpeg::channel_layout::STEREO); + .unwrap_or(ffmpeg::channel_layout::ChannelLayout::STEREO); if global { - encoder.set_flags(ffmpeg::codec::flag::GLOBAL_HEADER); + encoder.set_flags(ffmpeg::codec::flag::Flags::GLOBAL_HEADER); } encoder.set_rate(decoder.rate() as i32); @@ -112,22 +114,89 @@ fn transcoder>( let filter = filter(filter_spec, &decoder, &encoder)?; + let in_time_base = decoder.time_base(); + let out_time_base = output.time_base(); + Ok(Transcoder { stream: input.index(), - filter: filter, - decoder: decoder, - encoder: encoder, + filter, + decoder, + encoder, + in_time_base, + out_time_base, }) } -// Transcode the `best` audio stream of the input file into a the output file while applying a -// given filter. If no filter was specified the stream gets copied (`anull` filter). +impl Transcoder { + fn send_frame_to_encoder(&mut self, frame: &ffmpeg::Frame) { + self.encoder.send_frame(frame).unwrap(); + } + + fn send_eof_to_encoder(&mut self) { + self.encoder.send_eof().unwrap(); + } + + fn receive_and_process_encoded_packets(&mut self, octx: &mut format::context::Output) { + let mut encoded = ffmpeg::Packet::empty(); + while self.encoder.receive_packet(&mut encoded).is_ok() { + encoded.set_stream(0); + encoded.rescale_ts(self.in_time_base, self.out_time_base); + encoded.write_interleaved(octx).unwrap(); + } + } + + fn add_frame_to_filter(&mut self, frame: &ffmpeg::Frame) { + self.filter.get("in").unwrap().source().add(frame).unwrap(); + } + + fn flush_filter(&mut self) { + self.filter.get("in").unwrap().source().flush().unwrap(); + } + + fn get_and_process_filtered_frames(&mut self, octx: &mut format::context::Output) { + let mut filtered = frame::Audio::empty(); + while self + .filter + .get("out") + .unwrap() + .sink() + .frame(&mut filtered) + .is_ok() + { + self.send_frame_to_encoder(&filtered); + self.receive_and_process_encoded_packets(octx); + } + } + + fn send_packet_to_decoder(&mut self, packet: &ffmpeg::Packet) { + self.decoder.send_packet(packet).unwrap(); + } + + fn send_eof_to_decoder(&mut self) { + self.decoder.send_eof().unwrap(); + } + + fn receive_and_process_decoded_frames(&mut self, octx: &mut format::context::Output) { + let mut decoded = frame::Audio::empty(); + while self.decoder.receive_frame(&mut decoded).is_ok() { + let timestamp = decoded.timestamp(); + decoded.set_pts(timestamp); + self.add_frame_to_filter(&decoded); + self.get_and_process_filtered_frames(octx); + } + } +} + +// Transcode the `best` audio stream of the input file into a the output file +// while applying a given filter. If no filter was specified the stream gets +// copied (`anull` filter). // // Example 1: Transcode *.mp3 file to *.wmv while speeding it up // transcode-audio in.mp3 out.wmv "atempo=1.2" // // Example 2: Overlay an audio file -// transcode-audio in.mp3 out.mp3 "amovie=overlay.mp3 [ov]; [in][ov] amerge [out]" +// transcode-audio in.mp3 out.mp3 "amovie=overlay.mp3 [ov]; [in][ov] amerge +// [out]" // // Example 3: Seek to a specified position (in seconds) // transcode-audio in.mp3 out.mp3 anull 30 @@ -154,72 +223,23 @@ fn main() { octx.set_metadata(ictx.metadata().to_owned()); octx.write_header().unwrap(); - let in_time_base = transcoder.decoder.time_base(); - let out_time_base = octx.stream(0).unwrap().time_base(); - - let mut decoded = frame::Audio::empty(); - let mut encoded = ffmpeg::Packet::empty(); - - for (stream, mut packet) in ictx.packets() { + let mut packets = ictx.packets(); + while let Some(Ok((stream, mut packet))) = packets.next() { if stream.index() == transcoder.stream { - packet.rescale_ts(stream.time_base(), in_time_base); - - if let Ok(true) = transcoder.decoder.decode(&packet, &mut decoded) { - let timestamp = decoded.timestamp(); - decoded.set_pts(timestamp); - - transcoder - .filter - .get("in") - .unwrap() - .source() - .add(&decoded) - .unwrap(); - - while let Ok(..) = transcoder - .filter - .get("out") - .unwrap() - .sink() - .frame(&mut decoded) - { - if let Ok(true) = transcoder.encoder.encode(&decoded, &mut encoded) { - encoded.set_stream(0); - encoded.rescale_ts(in_time_base, out_time_base); - encoded.write_interleaved(&mut octx).unwrap(); - } - } - } + packet.rescale_ts(stream.time_base(), transcoder.in_time_base); + transcoder.send_packet_to_decoder(&packet); + transcoder.receive_and_process_decoded_frames(&mut octx); } } - transcoder - .filter - .get("in") - .unwrap() - .source() - .flush() - .unwrap(); - - while let Ok(..) = transcoder - .filter - .get("out") - .unwrap() - .sink() - .frame(&mut decoded) - { - if let Ok(true) = transcoder.encoder.encode(&decoded, &mut encoded) { - encoded.set_stream(0); - encoded.rescale_ts(in_time_base, out_time_base); - encoded.write_interleaved(&mut octx).unwrap(); - } - } + transcoder.send_eof_to_decoder(); + transcoder.receive_and_process_decoded_frames(&mut octx); - if let Ok(true) = transcoder.encoder.flush(&mut encoded) { - encoded.set_stream(0); - encoded.rescale_ts(in_time_base, out_time_base); - encoded.write_interleaved(&mut octx).unwrap(); - } + transcoder.flush_filter(); + transcoder.get_and_process_filtered_frames(&mut octx); + + transcoder.send_eof_to_encoder(); + transcoder.receive_and_process_encoded_packets(&mut octx); octx.write_trailer().unwrap(); } diff --git a/examples/transcode-x264.rs b/examples/transcode-x264.rs new file mode 100644 index 00000000..2b83442f --- /dev/null +++ b/examples/transcode-x264.rs @@ -0,0 +1,265 @@ +// Given an input file, transcode all video streams into H.264 (using libx264) +// while copying audio and subtitle streams. +// +// Invocation: +// +// transcode-x264 [] +// +// is a comma-delimited list of key=val. default is "preset=medium". +// See https://ffmpeg.org/ffmpeg-codecs.html#libx264_002c-libx264rgb and +// https://trac.ffmpeg.org/wiki/Encode/H.264 for available and commonly used +// options. +// +// Examples: +// +// transcode-x264 input.flv output.mp4 +// transcode-x264 input.mkv output.mkv 'preset=veryslow,crf=18' + +extern crate ffmpeg_next as ffmpeg; + +use std::{collections::HashMap, env, time::Instant}; + +use crate::ffmpeg::{ + codec, decoder, encoder, format, frame, log, media, picture, Dictionary, Packet, Rational, +}; + +const DEFAULT_X264_OPTS: &str = "preset=medium"; + +struct Transcoder { + ost_index: usize, + decoder: decoder::Video, + encoder: encoder::video::Video, + logging_enabled: bool, + frame_count: usize, + last_log_frame_count: usize, + starting_time: Instant, + last_log_time: Instant, +} + +impl Transcoder { + fn new( + ist: &format::stream::Stream, + octx: &mut format::context::Output, + ost_index: usize, + x264_opts: Dictionary, + enable_logging: bool, + ) -> Result { + let global_header = octx.format().flags().contains(format::Flags::GLOBAL_HEADER); + let decoder = ist.codec().decoder().video()?; + let mut ost = octx.add_stream(encoder::find(codec::Id::H264))?; + let mut encoder = ost.codec().encoder().video()?; + encoder.set_height(decoder.height()); + encoder.set_width(decoder.width()); + encoder.set_aspect_ratio(decoder.aspect_ratio()); + encoder.set_format(decoder.format()); + encoder.set_frame_rate(decoder.frame_rate()); + encoder.set_time_base(decoder.frame_rate().unwrap().invert()); + if global_header { + encoder.set_flags(codec::Flags::GLOBAL_HEADER); + } + encoder + .open_with(x264_opts) + .expect("error opening libx264 encoder with supplied settings"); + encoder = ost.codec().encoder().video()?; + ost.set_parameters(encoder); + Ok(Self { + ost_index, + decoder, + encoder: ost.codec().encoder().video()?, + logging_enabled: enable_logging, + frame_count: 0, + last_log_frame_count: 0, + starting_time: Instant::now(), + last_log_time: Instant::now(), + }) + } + + fn send_packet_to_decoder(&mut self, packet: &Packet) { + self.decoder.send_packet(packet).unwrap(); + } + + fn send_eof_to_decoder(&mut self) { + self.decoder.send_eof().unwrap(); + } + + fn receive_and_process_decoded_frames( + &mut self, + octx: &mut format::context::Output, + ost_time_base: Rational, + ) { + let mut frame = frame::Video::empty(); + while self.decoder.receive_frame(&mut frame).is_ok() { + self.frame_count += 1; + let timestamp = frame.timestamp(); + self.log_progress(f64::from( + Rational(timestamp.unwrap_or(0) as i32, 1) * self.decoder.time_base(), + )); + frame.set_pts(timestamp); + frame.set_kind(picture::Type::None); + self.send_frame_to_encoder(&frame); + self.receive_and_process_encoded_packets(octx, ost_time_base); + } + } + + fn send_frame_to_encoder(&mut self, frame: &frame::Video) { + self.encoder.send_frame(frame).unwrap(); + } + + fn send_eof_to_encoder(&mut self) { + self.encoder.send_eof().unwrap(); + } + + fn receive_and_process_encoded_packets( + &mut self, + octx: &mut format::context::Output, + ost_time_base: Rational, + ) { + let mut encoded = Packet::empty(); + while self.encoder.receive_packet(&mut encoded).is_ok() { + encoded.set_stream(self.ost_index); + encoded.rescale_ts(self.decoder.time_base(), ost_time_base); + encoded.write_interleaved(octx).unwrap(); + } + } + + fn log_progress(&mut self, timestamp: f64) { + if !self.logging_enabled + || (self.frame_count - self.last_log_frame_count < 100 + && self.last_log_time.elapsed().as_secs_f64() < 1.0) + { + return; + } + eprintln!( + "time elpased: \t{:8.2}\tframe count: {:8}\ttimestamp: {:8.2}", + self.starting_time.elapsed().as_secs_f64(), + self.frame_count, + timestamp + ); + self.last_log_frame_count = self.frame_count; + self.last_log_time = Instant::now(); + } +} + +fn parse_opts<'a>(s: String) -> Option> { + let mut dict = Dictionary::new(); + for keyval in s.split_terminator(',') { + let tokens: Vec<&str> = keyval.split('=').collect(); + match tokens[..] { + [key, val] => dict.set(key, val), + _ => return None, + } + } + Some(dict) +} + +fn main() { + let input_file = env::args().nth(1).expect("missing input file"); + let output_file = env::args().nth(2).expect("missing output file"); + let x264_opts = parse_opts( + env::args() + .nth(3) + .unwrap_or_else(|| DEFAULT_X264_OPTS.to_string()), + ) + .expect("invalid x264 options string"); + + eprintln!("x264 options: {:?}", x264_opts); + + ffmpeg::init().unwrap(); + log::set_level(log::Level::Info); + + let mut ictx = format::input(&input_file).unwrap(); + let mut octx = format::output(&output_file).unwrap(); + + format::context::input::dump(&ictx, 0, Some(&input_file)); + + let best_video_stream_index = ictx + .streams() + .best(media::Type::Video) + .map(|stream| stream.index()); + let mut stream_mapping: Vec = vec![0; ictx.nb_streams() as _]; + let mut ist_time_bases = vec![Rational(0, 0); ictx.nb_streams() as _]; + let mut ost_time_bases = vec![Rational(0, 0); ictx.nb_streams() as _]; + let mut transcoders = HashMap::new(); + let mut ost_index = 0; + for (ist_index, ist) in ictx.streams().enumerate() { + let ist_medium = ist.codec().medium(); + if ist_medium != media::Type::Audio + && ist_medium != media::Type::Video + && ist_medium != media::Type::Subtitle + { + stream_mapping[ist_index] = -1; + continue; + } + stream_mapping[ist_index] = ost_index; + ist_time_bases[ist_index] = ist.time_base(); + if ist_medium == media::Type::Video { + // Initialize transcoder for video stream. + transcoders.insert( + ist_index, + Transcoder::new( + &ist, + &mut octx, + ost_index as _, + x264_opts.to_owned(), + Some(ist_index) == best_video_stream_index, + ) + .unwrap(), + ); + } + else { + // Set up for stream copy for non-video stream. + let mut ost = octx.add_stream(encoder::find(codec::Id::None)).unwrap(); + ost.set_parameters(ist.parameters()); + // We need to set codec_tag to 0 lest we run into incompatible codec tag + // issues when muxing into a different container format. Unfortunately + // there's no high level API to do this (yet). + unsafe { + (*ost.parameters().as_mut_ptr()).codec_tag = 0; + } + } + ost_index += 1; + } + + octx.set_metadata(ictx.metadata().to_owned()); + format::context::output::dump(&octx, 0, Some(&output_file)); + octx.write_header().unwrap(); + + for (ost_index, _) in octx.streams().enumerate() { + ost_time_bases[ost_index] = octx.stream(ost_index as _).unwrap().time_base(); + } + + let mut packets = ictx.packets(); + while let Some(Ok((stream, mut packet))) = packets.next() { + let ist_index = stream.index(); + let ost_index = stream_mapping[ist_index]; + if ost_index < 0 { + continue; + } + let ost_time_base = ost_time_bases[ost_index as usize]; + match transcoders.get_mut(&ist_index) { + Some(transcoder) => { + packet.rescale_ts(stream.time_base(), transcoder.decoder.time_base()); + transcoder.send_packet_to_decoder(&packet); + transcoder.receive_and_process_decoded_frames(&mut octx, ost_time_base); + } + None => { + // Do stream copy on non-video streams. + packet.rescale_ts(ist_time_bases[ist_index], ost_time_base); + packet.set_position(-1); + packet.set_stream(ost_index as _); + packet.write_interleaved(&mut octx).unwrap(); + } + } + } + + // Flush encoders and decoders. + for (ost_index, transcoder) in transcoders.iter_mut() { + let ost_time_base = ost_time_bases[*ost_index]; + transcoder.send_eof_to_decoder(); + transcoder.receive_and_process_decoded_frames(&mut octx, ost_time_base); + transcoder.send_eof_to_encoder(); + transcoder.receive_and_process_encoded_packets(&mut octx, ost_time_base); + } + + octx.write_trailer().unwrap(); +} diff --git a/shell.nix b/shell.nix new file mode 100644 index 00000000..024bb7bd --- /dev/null +++ b/shell.nix @@ -0,0 +1,21 @@ +let + mozilla = import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz); +in + +with (import { + overlays = [mozilla]; +}); + +mkShell { + name = "ffmpeg-sys"; + + buildInputs = [ + # For building. + clang rustChannels.stable.rust pkg-config ffmpeg + ]; + + RUST_BACKTRACE = 1; + RUSTFLAGS = "-C target-cpu=native"; + + LIBCLANG_PATH = "${llvmPackages.libclang}/lib"; +} diff --git a/src/codec/audio.rs b/src/codec/audio.rs index f2f5fdd8..d6eadd27 100644 --- a/src/codec/audio.rs +++ b/src/codec/audio.rs @@ -1,8 +1,7 @@ use std::ops::Deref; use super::codec::Codec; -use ffi::*; -use {format, ChannelLayout}; +use crate::{ffi::*, format, ChannelLayout}; #[derive(PartialEq, Eq, Copy, Clone)] pub struct Audio { @@ -11,7 +10,7 @@ pub struct Audio { impl Audio { pub unsafe fn new(codec: Codec) -> Audio { - Audio { codec: codec } + Audio { codec } } } @@ -20,7 +19,8 @@ impl Audio { unsafe { if (*self.as_ptr()).supported_samplerates.is_null() { None - } else { + } + else { Some(RateIter::new((*self.codec.as_ptr()).supported_samplerates)) } } @@ -30,7 +30,8 @@ impl Audio { unsafe { if (*self.codec.as_ptr()).sample_fmts.is_null() { None - } else { + } + else { Some(FormatIter::new((*self.codec.as_ptr()).sample_fmts)) } } @@ -40,7 +41,8 @@ impl Audio { unsafe { if (*self.codec.as_ptr()).channel_layouts.is_null() { None - } else { + } + else { Some(ChannelLayoutIter::new( (*self.codec.as_ptr()).channel_layouts, )) @@ -63,7 +65,7 @@ pub struct RateIter { impl RateIter { pub fn new(ptr: *const i32) -> Self { - RateIter { ptr: ptr } + RateIter { ptr } } } @@ -90,7 +92,7 @@ pub struct FormatIter { impl FormatIter { pub fn new(ptr: *const AVSampleFormat) -> Self { - FormatIter { ptr: ptr } + FormatIter { ptr } } } @@ -117,14 +119,15 @@ pub struct ChannelLayoutIter { impl ChannelLayoutIter { pub fn new(ptr: *const u64) -> Self { - ChannelLayoutIter { ptr: ptr } + ChannelLayoutIter { ptr } } pub fn best(self, max: i32) -> ChannelLayout { - self.fold(::channel_layout::MONO, |acc, cur| { + self.fold(ChannelLayout::MONO, |acc, cur| { if cur.channels() > acc.channels() && cur.channels() <= max { cur - } else { + } + else { acc } }) diff --git a/src/codec/audio_service.rs b/src/codec/audio_service.rs index e1ca3eb5..206a22b1 100644 --- a/src/codec/audio_service.rs +++ b/src/codec/audio_service.rs @@ -1,5 +1,4 @@ -use ffi::AVAudioServiceType::*; -use ffi::*; +use crate::ffi::{AVAudioServiceType::*, *}; #[derive(Eq, PartialEq, Clone, Copy, Debug)] pub enum AudioService { diff --git a/src/codec/capabilities.rs b/src/codec/capabilities.rs index 2a752de4..8663e7b0 100644 --- a/src/codec/capabilities.rs +++ b/src/codec/capabilities.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_uint; bitflags! { @@ -8,6 +8,7 @@ bitflags! { const TRUNCATED = AV_CODEC_CAP_TRUNCATED; const DELAY = AV_CODEC_CAP_DELAY; const SMALL_LAST_FRAME = AV_CODEC_CAP_SMALL_LAST_FRAME; + #[cfg(not(feature = "ffmpeg_4_0"))] const HWACCEL_VDPAU = AV_CODEC_CAP_HWACCEL_VDPAU; const SUBFRAMES = AV_CODEC_CAP_SUBFRAMES; const EXPERIMENTAL = AV_CODEC_CAP_EXPERIMENTAL; @@ -17,7 +18,9 @@ bitflags! { const PARAM_CHANGE = AV_CODEC_CAP_PARAM_CHANGE; const AUTO_THREADS = AV_CODEC_CAP_AUTO_THREADS; const VARIABLE_FRAME_SIZE = AV_CODEC_CAP_VARIABLE_FRAME_SIZE; + #[cfg_attr(feature = "ffmpeg_4_3", deprecated)] const INTRA_ONLY = AV_CODEC_CAP_INTRA_ONLY; + #[cfg_attr(feature = "ffmpeg_4_3", deprecated)] const LOSSLESS = AV_CODEC_CAP_LOSSLESS; } } diff --git a/src/codec/codec.rs b/src/codec/codec.rs index f7335241..0873bba3 100644 --- a/src/codec/codec.rs +++ b/src/codec/codec.rs @@ -1,9 +1,7 @@ -use std::ffi::CStr; -use std::str::from_utf8_unchecked; +use std::{ffi::CStr, str::from_utf8_unchecked}; use super::{Audio, Capabilities, Id, Profile, Video}; -use ffi::*; -use {media, Error}; +use crate::{ffi::*, media, Error}; #[derive(PartialEq, Eq, Copy, Clone)] pub struct Codec { @@ -15,7 +13,7 @@ unsafe impl Sync for Codec {} impl Codec { pub unsafe fn wrap(ptr: *mut AVCodec) -> Self { - Codec { ptr: ptr } + Codec { ptr } } pub unsafe fn as_ptr(&self) -> *const AVCodec { @@ -41,7 +39,15 @@ impl Codec { } pub fn description(&self) -> &str { - unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).long_name).to_bytes()) } + unsafe { + let long_name = (*self.as_ptr()).long_name; + if long_name.is_null() { + "" + } + else { + from_utf8_unchecked(CStr::from_ptr(long_name).to_bytes()) + } + } } pub fn medium(&self) -> media::Type { @@ -60,7 +66,8 @@ impl Codec { unsafe { if self.medium() == media::Type::Video { Ok(Video::new(self)) - } else { + } + else { Err(Error::InvalidData) } } @@ -74,7 +81,8 @@ impl Codec { unsafe { if self.medium() == media::Type::Audio { Ok(Audio::new(self)) - } else { + } + else { Err(Error::InvalidData) } } @@ -92,7 +100,8 @@ impl Codec { unsafe { if (*self.as_ptr()).profiles.is_null() { None - } else { + } + else { Some(ProfileIter::new(self.id(), (*self.as_ptr()).profiles)) } } @@ -106,7 +115,7 @@ pub struct ProfileIter { impl ProfileIter { pub fn new(id: Id, ptr: *const AVProfile) -> Self { - ProfileIter { id: id, ptr: ptr } + ProfileIter { id, ptr } } } diff --git a/src/codec/compliance.rs b/src/codec/compliance.rs index e6f1ec32..2c0644f5 100644 --- a/src/codec/compliance.rs +++ b/src/codec/compliance.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; #[derive(Eq, PartialEq, Clone, Copy, Debug)] diff --git a/src/codec/context.rs b/src/codec/context.rs index d30dd82f..2b8b6f43 100644 --- a/src/codec/context.rs +++ b/src/codec/context.rs @@ -1,27 +1,21 @@ -use std::ptr; -use std::rc::Rc; +use std::{ptr, rc::Rc}; -use super::decoder::Decoder; -use super::encoder::Encoder; -use super::{threading, Compliance, Debug, Flags, Id, Parameters}; -use ffi::*; +#[cfg(feature = "ffmpeg_3_1")] +use super::Parameters; +use super::{decoder::Decoder, encoder::Encoder, threading, Compliance, Debug, Flags, Id}; +use crate::{ffi::*, media, Codec, Error}; use libc::c_int; -use media; -use {Codec, Error}; pub struct Context { ptr: *mut AVCodecContext, - owner: Option>, + owner: Option>, } unsafe impl Send for Context {} impl Context { - pub unsafe fn wrap(ptr: *mut AVCodecContext, owner: Option>) -> Self { - Context { - ptr: ptr, - owner: owner, - } + pub unsafe fn wrap(ptr: *mut AVCodecContext, owner: Option>) -> Self { + Context { ptr, owner } } pub unsafe fn as_ptr(&self) -> *const AVCodecContext { @@ -55,7 +49,8 @@ impl Context { unsafe { if (*self.as_ptr()).codec.is_null() { None - } else { + } + else { Some(Codec::wrap((*self.as_ptr()).codec as *mut _)) } } @@ -105,6 +100,7 @@ impl Context { } } + #[cfg(feature = "ffmpeg_3_1")] pub fn set_parameters>(&mut self, parameters: P) -> Result<(), Error> { let parameters = parameters.into(); diff --git a/src/codec/debug.rs b/src/codec/debug.rs index 1b45c742..52132a64 100644 --- a/src/codec/debug.rs +++ b/src/codec/debug.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; bitflags! { @@ -8,15 +8,19 @@ bitflags! { const BITSTREAM = FF_DEBUG_BITSTREAM; const MB_TYPE = FF_DEBUG_MB_TYPE; const QP = FF_DEBUG_QP; + #[cfg(not(feature = "ffmpeg_4_0"))] const MV = FF_DEBUG_MV; const DCT_COEFF = FF_DEBUG_DCT_COEFF; const SKIP = FF_DEBUG_SKIP; const STARTCODE = FF_DEBUG_STARTCODE; + #[cfg(not(feature = "ffmpeg_4_0"))] const PTS = FF_DEBUG_PTS; const ER = FF_DEBUG_ER; const MMCO = FF_DEBUG_MMCO; const BUGS = FF_DEBUG_BUGS; + #[cfg(not(feature = "ffmpeg_4_0"))] const VIS_QP = FF_DEBUG_VIS_QP; + #[cfg(not(feature = "ffmpeg_4_0"))] const VIS_MB_TYPE = FF_DEBUG_VIS_MB_TYPE; const BUFFERS = FF_DEBUG_BUFFERS; const THREADS = FF_DEBUG_THREADS; diff --git a/src/codec/decoder/audio.rs b/src/codec/decoder/audio.rs index 70358bd9..c0054abe 100644 --- a/src/codec/decoder/audio.rs +++ b/src/codec/decoder/audio.rs @@ -1,17 +1,19 @@ use std::ops::{Deref, DerefMut}; -use ffi::*; +use crate::ffi::*; use libc::c_int; use super::Opened; -use codec::Context; -use frame; -use util::format; -use {packet, AudioService, ChannelLayout, Error}; +use crate::{codec::Context, frame, packet, util::format, AudioService, ChannelLayout, Error}; pub struct Audio(pub Opened); impl Audio { + #[deprecated( + since = "4.4.0", + note = "Underlying API avcodec_decode_audio4 has been deprecated since FFmpeg 3.1; \ + consider switching to send_packet() and receive_frame()" + )] pub fn decode( &mut self, packet: &P, diff --git a/src/codec/decoder/check.rs b/src/codec/decoder/check.rs index aae10eeb..c1eb866f 100644 --- a/src/codec/decoder/check.rs +++ b/src/codec/decoder/check.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; bitflags! { diff --git a/src/codec/decoder/conceal.rs b/src/codec/decoder/conceal.rs index 7d59918e..25d394ec 100644 --- a/src/codec/decoder/conceal.rs +++ b/src/codec/decoder/conceal.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; bitflags! { diff --git a/src/codec/decoder/decoder.rs b/src/codec/decoder/decoder.rs index 6990ab71..f7cd7eb0 100644 --- a/src/codec/decoder/decoder.rs +++ b/src/codec/decoder/decoder.rs @@ -1,10 +1,14 @@ -use std::ops::{Deref, DerefMut}; -use std::ptr; +use std::{ + ops::{Deref, DerefMut}, + ptr, +}; use super::{Audio, Check, Conceal, Opened, Subtitle, Video}; -use codec::{traits, Context}; -use ffi::*; -use {Dictionary, Discard, Error, Rational}; +use crate::{ + codec::{traits, Context}, + ffi::*, + Dictionary, Discard, Error, Rational, +}; pub struct Decoder(pub Context); @@ -25,7 +29,8 @@ impl Decoder { 0 => Ok(Opened(self)), e => Err(Error::from(e)), } - } else { + } + else { Err(Error::DecoderNotFound) } } @@ -47,7 +52,8 @@ impl Decoder { 0 => Ok(Opened(self)), e => Err(Error::from(e)), } - } else { + } + else { Err(Error::DecoderNotFound) } } @@ -56,7 +62,8 @@ impl Decoder { pub fn video(self) -> Result { if let Some(codec) = super::find(self.id()) { self.open_as(codec).and_then(|o| o.video()) - } else { + } + else { Err(Error::DecoderNotFound) } } @@ -64,7 +71,8 @@ impl Decoder { pub fn audio(self) -> Result { if let Some(codec) = super::find(self.id()) { self.open_as(codec).and_then(|o| o.audio()) - } else { + } + else { Err(Error::DecoderNotFound) } } @@ -72,7 +80,8 @@ impl Decoder { pub fn subtitle(self) -> Result { if let Some(codec) = super::find(self.id()) { self.open_as(codec).and_then(|o| o.subtitle()) - } else { + } + else { Err(Error::DecoderNotFound) } } diff --git a/src/codec/decoder/mod.rs b/src/codec/decoder/mod.rs index c942ba95..586a467a 100644 --- a/src/codec/decoder/mod.rs +++ b/src/codec/decoder/mod.rs @@ -23,10 +23,11 @@ pub use self::opened::Opened; use std::ffi::CString; -use codec::Context; -use codec::Id; -use ffi::*; -use Codec; +use crate::{ + codec::{Context, Id}, + ffi::*, + Codec, +}; pub fn new() -> Decoder { Context::new().decoder() @@ -38,7 +39,8 @@ pub fn find(id: Id) -> Option { if ptr.is_null() { None - } else { + } + else { Some(Codec::wrap(ptr)) } } @@ -51,7 +53,8 @@ pub fn find_by_name(name: &str) -> Option { if ptr.is_null() { None - } else { + } + else { Some(Codec::wrap(ptr)) } } diff --git a/src/codec/decoder/opened.rs b/src/codec/decoder/opened.rs index d1cf5d66..c63d52ec 100644 --- a/src/codec/decoder/opened.rs +++ b/src/codec/decoder/opened.rs @@ -1,10 +1,14 @@ -use std::ops::{Deref, DerefMut}; +use std::{ + ops::{Deref, DerefMut}, + ptr, +}; use super::{Audio, Decoder, Subtitle, Video}; -use codec::{Context, Profile}; -use ffi::*; -use media; -use {Error, Rational}; +use crate::{ + codec::{Context, Profile}, + ffi::*, + media, packet, Error, Frame, Rational, +}; pub struct Opened(pub Decoder); @@ -12,7 +16,8 @@ impl Opened { pub fn video(self) -> Result { if self.medium() == media::Type::Video { Ok(Video(self)) - } else { + } + else { Err(Error::InvalidData) } } @@ -20,7 +25,8 @@ impl Opened { pub fn audio(self) -> Result { if self.medium() == media::Type::Audio { Ok(Audio(self)) - } else { + } + else { Err(Error::InvalidData) } } @@ -28,11 +34,41 @@ impl Opened { pub fn subtitle(self) -> Result { if self.medium() == media::Type::Subtitle { Ok(Subtitle(self)) - } else { + } + else { Err(Error::InvalidData) } } + pub fn send_packet(&mut self, packet: &P) -> Result<(), Error> { + unsafe { + match avcodec_send_packet(self.as_mut_ptr(), packet.as_ptr()) { + e if e < 0 => Err(Error::from(e)), + _ => Ok(()), + } + } + } + + /// Sends a NULL packet to the decoder to signal end of stream and enter + /// draining mode. + pub fn send_eof(&mut self) -> Result<(), Error> { + unsafe { + match avcodec_send_packet(self.as_mut_ptr(), ptr::null()) { + e if e < 0 => Err(Error::from(e)), + _ => Ok(()), + } + } + } + + pub fn receive_frame(&mut self, frame: &mut Frame) -> Result<(), Error> { + unsafe { + match avcodec_receive_frame(self.as_mut_ptr(), frame.as_mut_ptr()) { + e if e < 0 => Err(Error::from(e)), + _ => Ok(()), + } + } + } + pub fn bit_rate(&self) -> usize { unsafe { (*self.as_ptr()).bit_rate as usize } } @@ -51,7 +87,8 @@ impl Opened { if value == (AVRational { num: 0, den: 1 }) { None - } else { + } + else { Some(Rational::from(value)) } } diff --git a/src/codec/decoder/slice.rs b/src/codec/decoder/slice.rs index 7e0c0dda..6e1601ca 100644 --- a/src/codec/decoder/slice.rs +++ b/src/codec/decoder/slice.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; bitflags! { diff --git a/src/codec/decoder/subtitle.rs b/src/codec/decoder/subtitle.rs index 1ccae827..ac86f4e5 100644 --- a/src/codec/decoder/subtitle.rs +++ b/src/codec/decoder/subtitle.rs @@ -1,11 +1,10 @@ use std::ops::{Deref, DerefMut}; -use ffi::*; +use crate::ffi::*; use libc::c_int; use super::Opened; -use codec::Context; -use {packet, Error}; +use crate::{codec::Context, packet, Error}; pub struct Subtitle(pub Opened); @@ -13,7 +12,7 @@ impl Subtitle { pub fn decode( &mut self, packet: &P, - out: &mut ::Subtitle, + out: &mut crate::Subtitle, ) -> Result { unsafe { let mut got: c_int = 0; diff --git a/src/codec/decoder/video.rs b/src/codec/decoder/video.rs index 5bf33083..db110c80 100644 --- a/src/codec/decoder/video.rs +++ b/src/codec/decoder/video.rs @@ -1,19 +1,24 @@ use std::ops::{Deref, DerefMut}; -use ffi::*; +use crate::ffi::*; use libc::c_int; use super::{slice, Opened}; -use codec::Context; -use color; -use frame; -use util::chroma; -use util::format; -use {packet, Error, FieldOrder, Rational}; +use crate::{ + codec::Context, + color, frame, packet, + util::{chroma, format}, + Error, FieldOrder, Rational, +}; pub struct Video(pub Opened); impl Video { + #[deprecated( + since = "4.4.0", + note = "Underlying API avcodec_decode_video2 has been deprecated since FFmpeg 3.1; \ + consider switching to send_packet() and receive_frame()" + )] pub fn decode( &mut self, packet: &P, diff --git a/src/codec/discard.rs b/src/codec/discard.rs index 4fb97fa4..3632db4a 100644 --- a/src/codec/discard.rs +++ b/src/codec/discard.rs @@ -1,5 +1,4 @@ -use ffi::AVDiscard::*; -use ffi::*; +use crate::ffi::{AVDiscard::*, *}; #[derive(Eq, PartialEq, Clone, Copy, Debug)] pub enum Discard { diff --git a/src/codec/encoder/audio.rs b/src/codec/encoder/audio.rs index 8e07a590..1ab84393 100644 --- a/src/codec/encoder/audio.rs +++ b/src/codec/encoder/audio.rs @@ -1,13 +1,18 @@ -use std::ops::{Deref, DerefMut}; -use std::ptr; +use std::{ + ops::{Deref, DerefMut}, + ptr, +}; -use ffi::*; +use crate::ffi::*; use libc::c_int; use super::Encoder as Super; -use codec::{traits, Context}; -use util::format; -use {frame, packet, ChannelLayout, Dictionary, Error}; +use crate::{ + codec::{traits, Context}, + frame, packet, + util::format, + ChannelLayout, Dictionary, Error, +}; pub struct Audio(pub Super); @@ -28,7 +33,8 @@ impl Audio { 0 => Ok(Encoder(self)), e => Err(Error::from(e)), } - } else { + } + else { Err(Error::EncoderNotFound) } } @@ -64,7 +70,8 @@ impl Audio { 0 => Ok(Encoder(self)), e => Err(Error::from(e)), } - } else { + } + else { Err(Error::EncoderNotFound) } } @@ -140,6 +147,11 @@ impl AsMut for Audio { pub struct Encoder(pub Audio); impl Encoder { + #[deprecated( + since = "4.4.0", + note = "Underlying API avcodec_encode_audio2 has been deprecated since FFmpeg 3.1; \ + consider switching to send_frame() and receive_packet()" + )] pub fn encode( &mut self, frame: &frame::Audio, @@ -164,6 +176,11 @@ impl Encoder { } } + #[deprecated( + since = "4.4.0", + note = "Underlying API avcodec_encode_audio2 has been deprecated since FFmpeg 3.1; \ + consider switching to send_eof() and receive_packet()" + )] pub fn flush(&mut self, out: &mut P) -> Result { unsafe { let mut got: c_int = 0; @@ -193,6 +210,12 @@ impl Deref for Encoder { } } +impl DerefMut for Encoder { + fn deref_mut(&mut self) -> &mut ::Target { + &mut self.0 + } +} + impl AsRef for Encoder { fn as_ref(&self) -> &Context { self diff --git a/src/codec/encoder/comparison.rs b/src/codec/encoder/comparison.rs index 503fd068..2c675562 100644 --- a/src/codec/encoder/comparison.rs +++ b/src/codec/encoder/comparison.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; #[derive(Eq, PartialEq, Clone, Copy, Debug)] diff --git a/src/codec/encoder/decision.rs b/src/codec/encoder/decision.rs index cb8c9034..152a076b 100644 --- a/src/codec/encoder/decision.rs +++ b/src/codec/encoder/decision.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; #[derive(Eq, PartialEq, Clone, Copy, Debug)] diff --git a/src/codec/encoder/encoder.rs b/src/codec/encoder/encoder.rs index edcd5271..3b6e8535 100644 --- a/src/codec/encoder/encoder.rs +++ b/src/codec/encoder/encoder.rs @@ -1,9 +1,13 @@ -use std::ops::{Deref, DerefMut}; +use std::{ + ops::{Deref, DerefMut}, + ptr, +}; + +use crate::ffi::*; +use libc::c_int; use super::{audio, subtitle, video}; -use codec::Context; -use libc::{c_int, int64_t}; -use {media, Error, Rational}; +use crate::{codec::Context, media, packet, Error, Frame, Rational}; pub struct Encoder(pub Context); @@ -56,15 +60,39 @@ impl Encoder { } } + pub fn send_frame(&mut self, frame: &Frame) -> Result<(), Error> { + unsafe { + match avcodec_send_frame(self.as_mut_ptr(), frame.as_ptr()) { + e if e < 0 => Err(Error::from(e)), + _ => Ok(()), + } + } + } + + /// Sends a NULL packet to the encoder to signal end of stream and enter + /// draining mode. + pub fn send_eof(&mut self) -> Result<(), Error> { + unsafe { self.send_frame(&Frame::wrap(ptr::null_mut())) } + } + + pub fn receive_packet(&mut self, packet: &mut P) -> Result<(), Error> { + unsafe { + match avcodec_receive_packet(self.as_mut_ptr(), packet.as_mut_ptr()) { + e if e < 0 => Err(Error::from(e)), + _ => Ok(()), + } + } + } + pub fn set_bit_rate(&mut self, value: usize) { unsafe { - (*self.as_mut_ptr()).bit_rate = value as int64_t; + (*self.as_mut_ptr()).bit_rate = value as i64; } } pub fn set_max_bit_rate(&mut self, value: usize) { unsafe { - (*self.as_mut_ptr()).rc_max_rate = value as int64_t; + (*self.as_mut_ptr()).rc_max_rate = value as i64; } } @@ -84,7 +112,8 @@ impl Encoder { unsafe { if let Some(value) = value { (*self.as_mut_ptr()).compression_level = value as c_int; - } else { + } + else { (*self.as_mut_ptr()).compression_level = -1; } } @@ -100,7 +129,8 @@ impl Encoder { unsafe { if let Some(value) = value { (*self.as_mut_ptr()).framerate = value.into().into(); - } else { + } + else { (*self.as_mut_ptr()).framerate.num = 0; (*self.as_mut_ptr()).framerate.den = 1; } diff --git a/src/codec/encoder/mod.rs b/src/codec/encoder/mod.rs index 7d934a60..b92f3fdc 100644 --- a/src/codec/encoder/mod.rs +++ b/src/codec/encoder/mod.rs @@ -24,10 +24,11 @@ pub use self::decision::Decision; use std::ffi::CString; -use codec::Context; -use codec::Id; -use ffi::*; -use Codec; +use crate::{ + codec::{Context, Id}, + ffi::*, + Codec, +}; pub fn new() -> Encoder { Context::new().encoder() @@ -39,7 +40,8 @@ pub fn find(id: Id) -> Option { if ptr.is_null() { None - } else { + } + else { Some(Codec::wrap(ptr)) } } @@ -52,7 +54,8 @@ pub fn find_by_name(name: &str) -> Option { if ptr.is_null() { None - } else { + } + else { Some(Codec::wrap(ptr)) } } diff --git a/src/codec/encoder/prediction.rs b/src/codec/encoder/prediction.rs index 3b227778..561d57a5 100644 --- a/src/codec/encoder/prediction.rs +++ b/src/codec/encoder/prediction.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; #[derive(Eq, PartialEq, Clone, Copy, Debug)] diff --git a/src/codec/encoder/subtitle.rs b/src/codec/encoder/subtitle.rs index 33ac17c4..c549c6e1 100644 --- a/src/codec/encoder/subtitle.rs +++ b/src/codec/encoder/subtitle.rs @@ -1,12 +1,16 @@ -use std::ops::{Deref, DerefMut}; -use std::ptr; +use std::{ + ops::{Deref, DerefMut}, + ptr, +}; -use ffi::*; +use crate::ffi::*; use libc::c_int; use super::Encoder as Super; -use codec::{traits, Context}; -use {Dictionary, Error}; +use crate::{ + codec::{traits, Context}, + Dictionary, Error, +}; pub struct Subtitle(pub Super); @@ -27,7 +31,8 @@ impl Subtitle { 0 => Ok(Encoder(self)), e => Err(Error::from(e)), } - } else { + } + else { Err(Error::EncoderNotFound) } } @@ -49,7 +54,8 @@ impl Subtitle { 0 => Ok(Encoder(self)), e => Err(Error::from(e)), } - } else { + } + else { Err(Error::EncoderNotFound) } } @@ -85,7 +91,7 @@ impl AsMut for Subtitle { pub struct Encoder(pub Subtitle); impl Encoder { - pub fn encode(&mut self, subtitle: &::Subtitle, out: &mut [u8]) -> Result { + pub fn encode(&mut self, subtitle: &crate::Subtitle, out: &mut [u8]) -> Result { unsafe { match avcodec_encode_subtitle( self.0.as_mut_ptr(), @@ -108,6 +114,12 @@ impl Deref for Encoder { } } +impl DerefMut for Encoder { + fn deref_mut(&mut self) -> &mut ::Target { + &mut self.0 + } +} + impl AsRef for Encoder { fn as_ref(&self) -> &Context { self diff --git a/src/codec/encoder/video.rs b/src/codec/encoder/video.rs index a21aaa1a..fa6bded8 100644 --- a/src/codec/encoder/video.rs +++ b/src/codec/encoder/video.rs @@ -1,13 +1,16 @@ -use std::ops::{Deref, DerefMut}; -use std::ptr; +use std::{ + ops::{Deref, DerefMut}, + ptr, +}; -use ffi::*; +use crate::ffi::*; use libc::{c_float, c_int}; -use super::Encoder as Super; -use super::{Comparison, Decision, MotionEstimation, Prediction}; -use codec::{traits, Context}; -use {color, format, frame, packet, Dictionary, Error, Rational}; +use super::{Comparison, Decision, Encoder as Super, MotionEstimation, Prediction}; +use crate::{ + codec::{traits, Context}, + color, format, frame, packet, Dictionary, Error, Rational, +}; pub struct Video(pub Super); @@ -30,7 +33,8 @@ impl Video { 0 => Ok(Encoder(self)), e => Err(Error::from(e)), } - } else { + } + else { Err(Error::EncoderNotFound) } } @@ -68,7 +72,8 @@ impl Video { 0 => Ok(Encoder(self)), e => Err(Error::from(e)), } - } else { + } + else { Err(Error::EncoderNotFound) } } @@ -292,7 +297,8 @@ impl Video { unsafe { if let Some(value) = value { (*self.as_mut_ptr()).intra_quant_bias = value as c_int; - } else { + } + else { (*self.as_mut_ptr()).intra_quant_bias = FF_DEFAULT_QUANT_BIAS; } } @@ -304,7 +310,8 @@ impl Video { unsafe { if let Some(value) = value { (*self.as_mut_ptr()).inter_quant_bias = value as c_int; - } else { + } + else { (*self.as_mut_ptr()).inter_quant_bias = FF_DEFAULT_QUANT_BIAS; } } @@ -415,6 +422,11 @@ impl AsMut for Video { pub struct Encoder(pub Video); impl Encoder { + #[deprecated( + since = "4.4.0", + note = "Underlying API avcodec_encode_video2 has been deprecated since FFmpeg 3.1; \ + consider switching to send_frame() and receive_packet()" + )] #[inline] pub fn encode( &mut self, @@ -422,7 +434,8 @@ impl Encoder { out: &mut P, ) -> Result { unsafe { - if self.format() != frame.format() || self.width() != frame.width() + if self.format() != frame.format() + || self.width() != frame.width() || self.height() != frame.height() { return Err(Error::InvalidData); @@ -442,6 +455,11 @@ impl Encoder { } } + #[deprecated( + since = "4.4.0", + note = "Underlying API avcodec_encode_video2 has been deprecated since FFmpeg 3.1; \ + consider switching to send_frame() and receive_packet()" + )] #[inline] pub fn flush(&mut self, out: &mut P) -> Result { unsafe { diff --git a/src/codec/field_order.rs b/src/codec/field_order.rs index 46dff814..45b4176d 100644 --- a/src/codec/field_order.rs +++ b/src/codec/field_order.rs @@ -1,5 +1,4 @@ -use ffi::AVFieldOrder::*; -use ffi::*; +use crate::ffi::{AVFieldOrder::*, *}; #[derive(Eq, PartialEq, Clone, Copy, Debug)] pub enum FieldOrder { diff --git a/src/codec/flag.rs b/src/codec/flag.rs index 186f5f6c..ce1f6a93 100644 --- a/src/codec/flag.rs +++ b/src/codec/flag.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_uint; bitflags! { diff --git a/src/codec/id.rs b/src/codec/id.rs index 28e923b9..278ada1a 100644 --- a/src/codec/id.rs +++ b/src/codec/id.rs @@ -1,9 +1,9 @@ -use std::ffi::CStr; -use std::str::from_utf8_unchecked; +use std::{ffi::CStr, str::from_utf8_unchecked}; -use ffi::AVCodecID::*; -use ffi::*; -use util::media; +use crate::{ + ffi::{AVCodecID::*, *}, + util::media, +}; #[allow(non_camel_case_types)] #[derive(Eq, PartialEq, Clone, Copy, Debug)] @@ -298,7 +298,6 @@ pub enum Id { ADPCM_G722, ADPCM_IMA_APC, ADPCM_VIMA, - VIMA, ADPCM_AFC, ADPCM_IMA_OKI, @@ -398,6 +397,8 @@ pub enum Id { ON2AVC, DSS_SP, + #[cfg(feature = "ffmpeg_4_0")] + CODEC2, FFWAVESYNTH, SONIC, SONIC_LS, @@ -481,9 +482,96 @@ pub enum Id { FITS, GREMLIN_DPCM, DOLBY_E, + + #[cfg(feature = "ffmpeg_4_0")] + APTX, + #[cfg(feature = "ffmpeg_4_0")] + APTX_HD, + #[cfg(feature = "ffmpeg_4_0")] + SBC, + + #[cfg(feature = "ffmpeg_4_1")] + AVS2, + #[cfg(feature = "ffmpeg_4_1")] + IMM4, + #[cfg(feature = "ffmpeg_4_1")] + PROSUMER, + #[cfg(feature = "ffmpeg_4_1")] + MWSC, + #[cfg(feature = "ffmpeg_4_1")] + WCMV, + #[cfg(feature = "ffmpeg_4_1")] + RASC, + #[cfg(feature = "ffmpeg_4_1")] + PCM_VIDC, + #[cfg(feature = "ffmpeg_4_1")] + ATRAC9, + #[cfg(feature = "ffmpeg_4_1")] + TTML, + + #[cfg(feature = "ffmpeg_4_2")] + HYMT, + #[cfg(feature = "ffmpeg_4_2")] + ARBC, + #[cfg(feature = "ffmpeg_4_2")] + AGM, + #[cfg(feature = "ffmpeg_4_2")] + LSCR, + #[cfg(feature = "ffmpeg_4_2")] + VP4, + #[cfg(feature = "ffmpeg_4_2")] + ADPCM_AGM, + #[cfg(feature = "ffmpeg_4_2")] + HCOM, + #[cfg(feature = "ffmpeg_4_2")] + ARIB_CAPTION, + + #[cfg(feature = "ffmpeg_4_3")] + IMM5, + #[cfg(feature = "ffmpeg_4_3")] + MVDV, + #[cfg(feature = "ffmpeg_4_3")] + MVHA, + #[cfg(feature = "ffmpeg_4_3")] + CDTOONS, + #[cfg(feature = "ffmpeg_4_3")] + MV30, + #[cfg(feature = "ffmpeg_4_3")] + NOTCHLC, + #[cfg(feature = "ffmpeg_4_3")] + PFM, + #[cfg(feature = "ffmpeg_4_3")] + ADPCM_ARGO, + #[cfg(feature = "ffmpeg_4_3")] + ADPCM_IMA_SSI, + #[cfg(feature = "ffmpeg_4_3")] + ADPCM_ZORK, + #[cfg(feature = "ffmpeg_4_3")] + ADPCM_IMA_APM, + #[cfg(feature = "ffmpeg_4_3")] + ADPCM_IMA_ALP, + #[cfg(feature = "ffmpeg_4_3")] + ADPCM_IMA_MTF, + #[cfg(feature = "ffmpeg_4_3")] + ADPCM_IMA_CUNNING, + #[cfg(feature = "ffmpeg_4_3")] + DERF_DPCM, + #[cfg(feature = "ffmpeg_4_3")] + ACELP_KELVIN, + #[cfg(feature = "ffmpeg_4_3")] + MPEGH_3D_AUDIO, + #[cfg(feature = "ffmpeg_4_3")] + SIREN, + #[cfg(feature = "ffmpeg_4_3")] + HCA, + #[cfg(feature = "ffmpeg_4_3")] + EPG, } impl Id { + #[cfg(feature = "ff_api_vima_decoder")] + pub const VIMA: Id = Id::ADPCM_VIMA; + pub fn medium(&self) -> media::Type { unsafe { media::Type::from(avcodec_get_type((*self).into())) } } @@ -498,7 +586,7 @@ impl From for Id { match value { AV_CODEC_ID_NONE => Id::None, - /* video codecs */ + // video codecs AV_CODEC_ID_MPEG1VIDEO => Id::MPEG1VIDEO, AV_CODEC_ID_MPEG2VIDEO => Id::MPEG2VIDEO, #[cfg(feature = "ff_api_xvmc")] @@ -716,7 +804,7 @@ impl From for Id { AV_CODEC_ID_SHEERVIDEO => Id::SHEERVIDEO, AV_CODEC_ID_YLC => Id::YLC, - /* various PCM "codecs" */ + // various PCM "codecs" AV_CODEC_ID_PCM_S16LE => Id::PCM_S16LE, AV_CODEC_ID_PCM_S16BE => Id::PCM_S16BE, AV_CODEC_ID_PCM_U16LE => Id::PCM_U16LE, @@ -752,7 +840,7 @@ impl From for Id { AV_CODEC_ID_PCM_S64LE => Id::PCM_S64LE, AV_CODEC_ID_PCM_S64BE => Id::PCM_S64BE, - /* various ADPCM codecs */ + // various ADPCM codecs AV_CODEC_ID_ADPCM_IMA_QT => Id::ADPCM_IMA_QT, AV_CODEC_ID_ADPCM_IMA_WAV => Id::ADPCM_IMA_WAV, AV_CODEC_ID_ADPCM_IMA_DK3 => Id::ADPCM_IMA_DK3, @@ -796,15 +884,15 @@ impl From for Id { AV_CODEC_ID_ADPCM_IMA_DAT4 => Id::ADPCM_IMA_DAT4, AV_CODEC_ID_ADPCM_MTAF => Id::ADPCM_MTAF, - /* AMR */ + // AMR AV_CODEC_ID_AMR_NB => Id::AMR_NB, AV_CODEC_ID_AMR_WB => Id::AMR_WB, - /* RealAudio codecs*/ + // RealAudio codecs AV_CODEC_ID_RA_144 => Id::RA_144, AV_CODEC_ID_RA_288 => Id::RA_288, - /* various DPCM codecs */ + // various DPCM codecs AV_CODEC_ID_ROQ_DPCM => Id::ROQ_DPCM, AV_CODEC_ID_INTERPLAY_DPCM => Id::INTERPLAY_DPCM, AV_CODEC_ID_XAN_DPCM => Id::XAN_DPCM, @@ -812,7 +900,7 @@ impl From for Id { AV_CODEC_ID_SDX2_DPCM => Id::SDX2_DPCM, - /* audio codecs */ + // audio codecs AV_CODEC_ID_MP2 => Id::MP2, AV_CODEC_ID_MP3 => Id::MP3, AV_CODEC_ID_AAC => Id::AAC, @@ -883,6 +971,8 @@ impl From for Id { AV_CODEC_ID_ON2AVC => Id::ON2AVC, AV_CODEC_ID_DSS_SP => Id::DSS_SP, + #[cfg(feature = "ffmpeg_4_0")] + AV_CODEC_ID_CODEC2 => Id::CODEC2, AV_CODEC_ID_FFWAVESYNTH => Id::FFWAVESYNTH, AV_CODEC_ID_SONIC => Id::SONIC, AV_CODEC_ID_SONIC_LS => Id::SONIC_LS, @@ -898,7 +988,7 @@ impl From for Id { AV_CODEC_ID_XMA2 => Id::XMA2, AV_CODEC_ID_DST => Id::DST, - /* subtitle codecs */ + // subtitle codecs AV_CODEC_ID_DVD_SUBTITLE => Id::DVD_SUBTITLE, AV_CODEC_ID_DVB_SUBTITLE => Id::DVB_SUBTITLE, AV_CODEC_ID_TEXT => Id::TEXT, @@ -925,7 +1015,7 @@ impl From for Id { AV_CODEC_ID_ASS => Id::ASS, AV_CODEC_ID_HDMV_TEXT_SUBTITLE => Id::HDMV_TEXT_SUBTITLE, - /* other specific kind of codecs (generally used for attachments) */ + // other specific kind of codecs (generally used for attachments) AV_CODEC_ID_TTF => Id::TTF, AV_CODEC_ID_SCTE_35 => Id::SCTE_35, @@ -965,6 +1055,90 @@ impl From for Id { AV_CODEC_ID_FITS => Id::FITS, AV_CODEC_ID_GREMLIN_DPCM => Id::GREMLIN_DPCM, AV_CODEC_ID_DOLBY_E => Id::DOLBY_E, + + #[cfg(feature = "ffmpeg_4_0")] + AV_CODEC_ID_APTX => Id::APTX, + #[cfg(feature = "ffmpeg_4_0")] + AV_CODEC_ID_APTX_HD => Id::APTX_HD, + #[cfg(feature = "ffmpeg_4_0")] + AV_CODEC_ID_SBC => Id::SBC, + + #[cfg(feature = "ffmpeg_4_1")] + AV_CODEC_ID_AVS2 => Id::AVS2, + #[cfg(feature = "ffmpeg_4_1")] + AV_CODEC_ID_IMM4 => Id::IMM4, + #[cfg(feature = "ffmpeg_4_1")] + AV_CODEC_ID_PROSUMER => Id::PROSUMER, + #[cfg(feature = "ffmpeg_4_1")] + AV_CODEC_ID_MWSC => Id::MWSC, + #[cfg(feature = "ffmpeg_4_1")] + AV_CODEC_ID_WCMV => Id::WCMV, + #[cfg(feature = "ffmpeg_4_1")] + AV_CODEC_ID_RASC => Id::RASC, + #[cfg(feature = "ffmpeg_4_1")] + AV_CODEC_ID_PCM_VIDC => Id::PCM_VIDC, + #[cfg(feature = "ffmpeg_4_1")] + AV_CODEC_ID_ATRAC9 => Id::ATRAC9, + #[cfg(feature = "ffmpeg_4_1")] + AV_CODEC_ID_TTML => Id::TTML, + + #[cfg(feature = "ffmpeg_4_2")] + AV_CODEC_ID_HYMT => Id::HYMT, + #[cfg(feature = "ffmpeg_4_2")] + AV_CODEC_ID_ARBC => Id::ARBC, + #[cfg(feature = "ffmpeg_4_2")] + AV_CODEC_ID_AGM => Id::AGM, + #[cfg(feature = "ffmpeg_4_2")] + AV_CODEC_ID_LSCR => Id::LSCR, + #[cfg(feature = "ffmpeg_4_2")] + AV_CODEC_ID_VP4 => Id::VP4, + #[cfg(feature = "ffmpeg_4_2")] + AV_CODEC_ID_ADPCM_AGM => Id::ADPCM_AGM, + #[cfg(feature = "ffmpeg_4_2")] + AV_CODEC_ID_HCOM => Id::HCOM, + #[cfg(feature = "ffmpeg_4_2")] + AV_CODEC_ID_ARIB_CAPTION => Id::ARIB_CAPTION, + + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_IMM5 => Id::IMM5, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_MVDV => Id::MVDV, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_MVHA => Id::MVHA, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_CDTOONS => Id::CDTOONS, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_MV30 => Id::MV30, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_NOTCHLC => Id::NOTCHLC, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_PFM => Id::PFM, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_ADPCM_ARGO => Id::ADPCM_ARGO, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_ADPCM_IMA_SSI => Id::ADPCM_IMA_SSI, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_ADPCM_ZORK => Id::ADPCM_ZORK, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_ADPCM_IMA_APM => Id::ADPCM_IMA_APM, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_ADPCM_IMA_ALP => Id::ADPCM_IMA_ALP, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_ADPCM_IMA_MTF => Id::ADPCM_IMA_MTF, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_ADPCM_IMA_CUNNING => Id::ADPCM_IMA_CUNNING, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_DERF_DPCM => Id::DERF_DPCM, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_ACELP_KELVIN => Id::ACELP_KELVIN, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_MPEGH_3D_AUDIO => Id::MPEGH_3D_AUDIO, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_SIREN => Id::SIREN, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_HCA => Id::HCA, + #[cfg(feature = "ffmpeg_4_3")] + AV_CODEC_ID_EPG => Id::EPG, } } } @@ -974,7 +1148,7 @@ impl Into for Id { match self { Id::None => AV_CODEC_ID_NONE, - /* video codecs */ + // video codecs Id::MPEG1VIDEO => AV_CODEC_ID_MPEG1VIDEO, Id::MPEG2VIDEO => AV_CODEC_ID_MPEG2VIDEO, #[cfg(feature = "ff_api_xvmc")] @@ -1194,7 +1368,7 @@ impl Into for Id { Id::SHEERVIDEO => AV_CODEC_ID_SHEERVIDEO, Id::YLC => AV_CODEC_ID_YLC, - /* various PCM "codecs" */ + // various PCM "codecs" Id::PCM_S16LE => AV_CODEC_ID_PCM_S16LE, Id::PCM_S16BE => AV_CODEC_ID_PCM_S16BE, Id::PCM_U16LE => AV_CODEC_ID_PCM_U16LE, @@ -1230,7 +1404,7 @@ impl Into for Id { Id::PCM_S64LE => AV_CODEC_ID_PCM_S64LE, Id::PCM_S64BE => AV_CODEC_ID_PCM_S64BE, - /* various ADPCM codecs */ + // various ADPCM codecs Id::ADPCM_IMA_QT => AV_CODEC_ID_ADPCM_IMA_QT, Id::ADPCM_IMA_WAV => AV_CODEC_ID_ADPCM_IMA_WAV, Id::ADPCM_IMA_DK3 => AV_CODEC_ID_ADPCM_IMA_DK3, @@ -1262,7 +1436,6 @@ impl Into for Id { Id::ADPCM_G722 => AV_CODEC_ID_ADPCM_G722, Id::ADPCM_IMA_APC => AV_CODEC_ID_ADPCM_IMA_APC, Id::ADPCM_VIMA => AV_CODEC_ID_ADPCM_VIMA, - Id::VIMA => AV_CODEC_ID_VIMA, Id::ADPCM_AFC => AV_CODEC_ID_ADPCM_AFC, Id::ADPCM_IMA_OKI => AV_CODEC_ID_ADPCM_IMA_OKI, @@ -1275,15 +1448,15 @@ impl Into for Id { Id::ADPCM_IMA_DAT4 => AV_CODEC_ID_ADPCM_IMA_DAT4, Id::ADPCM_MTAF => AV_CODEC_ID_ADPCM_MTAF, - /* AMR */ + // AMR Id::AMR_NB => AV_CODEC_ID_AMR_NB, Id::AMR_WB => AV_CODEC_ID_AMR_WB, - /* RealAudio codecs*/ + // RealAudio codecs Id::RA_144 => AV_CODEC_ID_RA_144, Id::RA_288 => AV_CODEC_ID_RA_288, - /* various DPCM codecs */ + // various DPCM codecs Id::ROQ_DPCM => AV_CODEC_ID_ROQ_DPCM, Id::INTERPLAY_DPCM => AV_CODEC_ID_INTERPLAY_DPCM, Id::XAN_DPCM => AV_CODEC_ID_XAN_DPCM, @@ -1291,7 +1464,7 @@ impl Into for Id { Id::SDX2_DPCM => AV_CODEC_ID_SDX2_DPCM, - /* audio codecs */ + // audio codecs Id::MP2 => AV_CODEC_ID_MP2, Id::MP3 => AV_CODEC_ID_MP3, Id::AAC => AV_CODEC_ID_AAC, @@ -1362,6 +1535,8 @@ impl Into for Id { Id::ON2AVC => AV_CODEC_ID_ON2AVC, Id::DSS_SP => AV_CODEC_ID_DSS_SP, + #[cfg(feature = "ffmpeg_4_0")] + Id::CODEC2 => AV_CODEC_ID_CODEC2, Id::FFWAVESYNTH => AV_CODEC_ID_FFWAVESYNTH, Id::SONIC => AV_CODEC_ID_SONIC, Id::SONIC_LS => AV_CODEC_ID_SONIC_LS, @@ -1377,7 +1552,7 @@ impl Into for Id { Id::XMA2 => AV_CODEC_ID_XMA2, Id::DST => AV_CODEC_ID_DST, - /* subtitle codecs */ + // subtitle codecs Id::DVD_SUBTITLE => AV_CODEC_ID_DVD_SUBTITLE, Id::DVB_SUBTITLE => AV_CODEC_ID_DVB_SUBTITLE, Id::TEXT => AV_CODEC_ID_TEXT, @@ -1404,7 +1579,7 @@ impl Into for Id { Id::ASS => AV_CODEC_ID_ASS, Id::HDMV_TEXT_SUBTITLE => AV_CODEC_ID_HDMV_TEXT_SUBTITLE, - /* other specific kind of codecs (generally used for attachments) */ + // other specific kind of codecs (generally used for attachments) Id::TTF => AV_CODEC_ID_TTF, Id::SCTE_35 => AV_CODEC_ID_SCTE_35, @@ -1445,6 +1620,90 @@ impl Into for Id { Id::FITS => AV_CODEC_ID_FITS, Id::GREMLIN_DPCM => AV_CODEC_ID_GREMLIN_DPCM, Id::DOLBY_E => AV_CODEC_ID_DOLBY_E, + + #[cfg(feature = "ffmpeg_4_0")] + Id::APTX => AV_CODEC_ID_APTX, + #[cfg(feature = "ffmpeg_4_0")] + Id::APTX_HD => AV_CODEC_ID_APTX_HD, + #[cfg(feature = "ffmpeg_4_0")] + Id::SBC => AV_CODEC_ID_SBC, + + #[cfg(feature = "ffmpeg_4_1")] + Id::AVS2 => AV_CODEC_ID_AVS2, + #[cfg(feature = "ffmpeg_4_1")] + Id::IMM4 => AV_CODEC_ID_IMM4, + #[cfg(feature = "ffmpeg_4_1")] + Id::PROSUMER => AV_CODEC_ID_PROSUMER, + #[cfg(feature = "ffmpeg_4_1")] + Id::MWSC => AV_CODEC_ID_MWSC, + #[cfg(feature = "ffmpeg_4_1")] + Id::WCMV => AV_CODEC_ID_WCMV, + #[cfg(feature = "ffmpeg_4_1")] + Id::RASC => AV_CODEC_ID_RASC, + #[cfg(feature = "ffmpeg_4_1")] + Id::PCM_VIDC => AV_CODEC_ID_PCM_VIDC, + #[cfg(feature = "ffmpeg_4_1")] + Id::ATRAC9 => AV_CODEC_ID_ATRAC9, + #[cfg(feature = "ffmpeg_4_1")] + Id::TTML => AV_CODEC_ID_TTML, + + #[cfg(feature = "ffmpeg_4_2")] + Id::HYMT => AV_CODEC_ID_HYMT, + #[cfg(feature = "ffmpeg_4_2")] + Id::ARBC => AV_CODEC_ID_ARBC, + #[cfg(feature = "ffmpeg_4_2")] + Id::AGM => AV_CODEC_ID_AGM, + #[cfg(feature = "ffmpeg_4_2")] + Id::LSCR => AV_CODEC_ID_LSCR, + #[cfg(feature = "ffmpeg_4_2")] + Id::VP4 => AV_CODEC_ID_VP4, + #[cfg(feature = "ffmpeg_4_2")] + Id::ADPCM_AGM => AV_CODEC_ID_ADPCM_AGM, + #[cfg(feature = "ffmpeg_4_2")] + Id::HCOM => AV_CODEC_ID_HCOM, + #[cfg(feature = "ffmpeg_4_2")] + Id::ARIB_CAPTION => AV_CODEC_ID_ARIB_CAPTION, + + #[cfg(feature = "ffmpeg_4_3")] + Id::IMM5 => AV_CODEC_ID_IMM5, + #[cfg(feature = "ffmpeg_4_3")] + Id::MVDV => AV_CODEC_ID_MVDV, + #[cfg(feature = "ffmpeg_4_3")] + Id::MVHA => AV_CODEC_ID_MVHA, + #[cfg(feature = "ffmpeg_4_3")] + Id::CDTOONS => AV_CODEC_ID_CDTOONS, + #[cfg(feature = "ffmpeg_4_3")] + Id::MV30 => AV_CODEC_ID_MV30, + #[cfg(feature = "ffmpeg_4_3")] + Id::NOTCHLC => AV_CODEC_ID_NOTCHLC, + #[cfg(feature = "ffmpeg_4_3")] + Id::PFM => AV_CODEC_ID_PFM, + #[cfg(feature = "ffmpeg_4_3")] + Id::ADPCM_ARGO => AV_CODEC_ID_ADPCM_ARGO, + #[cfg(feature = "ffmpeg_4_3")] + Id::ADPCM_IMA_SSI => AV_CODEC_ID_ADPCM_IMA_SSI, + #[cfg(feature = "ffmpeg_4_3")] + Id::ADPCM_ZORK => AV_CODEC_ID_ADPCM_ZORK, + #[cfg(feature = "ffmpeg_4_3")] + Id::ADPCM_IMA_APM => AV_CODEC_ID_ADPCM_IMA_APM, + #[cfg(feature = "ffmpeg_4_3")] + Id::ADPCM_IMA_ALP => AV_CODEC_ID_ADPCM_IMA_ALP, + #[cfg(feature = "ffmpeg_4_3")] + Id::ADPCM_IMA_MTF => AV_CODEC_ID_ADPCM_IMA_MTF, + #[cfg(feature = "ffmpeg_4_3")] + Id::ADPCM_IMA_CUNNING => AV_CODEC_ID_ADPCM_IMA_CUNNING, + #[cfg(feature = "ffmpeg_4_3")] + Id::DERF_DPCM => AV_CODEC_ID_DERF_DPCM, + #[cfg(feature = "ffmpeg_4_3")] + Id::ACELP_KELVIN => AV_CODEC_ID_ACELP_KELVIN, + #[cfg(feature = "ffmpeg_4_3")] + Id::MPEGH_3D_AUDIO => AV_CODEC_ID_MPEGH_3D_AUDIO, + #[cfg(feature = "ffmpeg_4_3")] + Id::SIREN => AV_CODEC_ID_SIREN, + #[cfg(feature = "ffmpeg_4_3")] + Id::HCA => AV_CODEC_ID_HCA, + #[cfg(feature = "ffmpeg_4_3")] + Id::EPG => AV_CODEC_ID_EPG, } } } diff --git a/src/codec/mod.rs b/src/codec/mod.rs index e7da3761..63c499cc 100644 --- a/src/codec/mod.rs +++ b/src/codec/mod.rs @@ -20,7 +20,9 @@ pub use self::capabilities::Capabilities; pub mod codec; +#[cfg(feature = "ffmpeg_3_1")] pub mod parameters; +#[cfg(feature = "ffmpeg_3_1")] pub use self::parameters::Parameters; pub mod video; @@ -47,10 +49,9 @@ pub mod decoder; pub mod encoder; pub mod traits; -use std::ffi::CStr; -use std::str::from_utf8_unchecked; +use std::{ffi::CStr, str::from_utf8_unchecked}; -use ffi::*; +use crate::ffi::*; pub fn version() -> u32 { unsafe { avcodec_version() } diff --git a/src/codec/packet/borrow.rs b/src/codec/packet/borrow.rs index 65c53867..a533ff2e 100644 --- a/src/codec/packet/borrow.rs +++ b/src/codec/packet/borrow.rs @@ -1,8 +1,7 @@ -use std::mem; -use std::ptr; +use std::{mem, ptr}; use super::Ref; -use ffi::*; +use crate::ffi::*; use libc::c_int; pub struct Borrow<'a> { @@ -18,10 +17,7 @@ impl<'a> Borrow<'a> { packet.data = data.as_ptr() as *mut _; packet.size = data.len() as c_int; - Borrow { - packet: packet, - data: data, - } + Borrow { packet, data } } } diff --git a/src/codec/packet/flag.rs b/src/codec/packet/flag.rs index dcbcfa54..2f523c2e 100644 --- a/src/codec/packet/flag.rs +++ b/src/codec/packet/flag.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; bitflags! { diff --git a/src/codec/packet/packet.rs b/src/codec/packet/packet.rs index a63bf9c5..29019683 100644 --- a/src/codec/packet/packet.rs +++ b/src/codec/packet/packet.rs @@ -1,11 +1,8 @@ -use std::marker::PhantomData; -use std::mem; -use std::slice; +use std::{marker::PhantomData, mem, slice}; -use super::{flag, Borrow, Flags, Mut, Ref, SideData}; -use ffi::*; +use super::{Borrow, Flags, Mut, Ref, SideData}; +use crate::{ffi::*, format, Error, Rational}; use libc::c_int; -use {format, Error, Rational}; pub struct Packet(AVPacket); @@ -99,12 +96,12 @@ impl Packet { #[inline] pub fn is_key(&self) -> bool { - self.flags().contains(flag::KEY) + self.flags().contains(Flags::KEY) } #[inline] pub fn is_corrupt(&self) -> bool { - self.flags().contains(flag::CORRUPT) + self.flags().contains(Flags::CORRUPT) } #[inline] @@ -127,9 +124,7 @@ impl Packet { #[inline] pub fn set_pts(&mut self, value: Option) { - unsafe { - (*self.as_mut_ptr()).pts = value.unwrap_or(AV_NOPTS_VALUE); - } + self.0.pts = value.unwrap_or(AV_NOPTS_VALUE); } #[inline] @@ -142,9 +137,7 @@ impl Packet { #[inline] pub fn set_dts(&mut self, value: Option) { - unsafe { - (*self.as_mut_ptr()).dts = value.unwrap_or(AV_NOPTS_VALUE); - } + self.0.dts = value.unwrap_or(AV_NOPTS_VALUE); } #[inline] @@ -157,11 +150,21 @@ impl Packet { self.0.duration as i64 } + #[inline] + pub fn set_duration(&mut self, value: i64) { + self.0.duration = value; + } + #[inline] pub fn position(&self) -> isize { self.0.pos as isize } + #[inline] + pub fn set_position(&mut self, value: isize) { + self.0.pos = value as i64 + } + #[inline] pub fn convergence(&self) -> isize { self.0.convergence_duration as isize @@ -177,7 +180,8 @@ impl Packet { unsafe { if self.0.data.is_null() { None - } else { + } + else { Some(slice::from_raw_parts(self.0.data, self.0.size as usize)) } } @@ -188,7 +192,8 @@ impl Packet { unsafe { if self.0.data.is_null() { None - } else { + } + else { Some(slice::from_raw_parts_mut(self.0.data, self.0.size as usize)) } } @@ -220,15 +225,14 @@ impl Packet { } #[inline] - pub fn write_interleaved(&self, format: &mut format::context::Output) -> Result { + pub fn write_interleaved(&self, format: &mut format::context::Output) -> Result<(), Error> { unsafe { if self.is_empty() { return Err(Error::InvalidData); } match av_interleaved_write_frame(format.as_mut_ptr(), self.as_ptr() as *mut _) { - 1 => Ok(true), - 0 => Ok(false), + 0 => Ok(()), e => Err(Error::from(e)), } } @@ -282,7 +286,7 @@ pub struct SideDataIter<'a> { impl<'a> SideDataIter<'a> { pub fn new(ptr: *const AVPacket) -> Self { SideDataIter { - ptr: ptr, + ptr, cur: 0, _marker: PhantomData, } @@ -296,7 +300,8 @@ impl<'a> Iterator for SideDataIter<'a> { unsafe { if self.cur >= (*self.ptr).side_data_elems { None - } else { + } + else { self.cur += 1; Some(SideData::wrap( (*self.ptr).side_data.offset((self.cur - 1) as isize), diff --git a/src/codec/packet/side_data.rs b/src/codec/packet/side_data.rs index 6dd5cb58..26a9e37f 100644 --- a/src/codec/packet/side_data.rs +++ b/src/codec/packet/side_data.rs @@ -1,9 +1,7 @@ -use std::marker::PhantomData; -use std::slice; +use std::{marker::PhantomData, slice}; use super::Packet; -use ffi::AVPacketSideDataType::*; -use ffi::*; +use crate::ffi::{AVPacketSideDataType::*, *}; #[derive(Eq, PartialEq, Copy, Clone, Debug)] pub enum Type { @@ -26,13 +24,32 @@ pub enum Type { WebVTTIdentifier, WebVTTSettings, MetadataUpdate, + #[cfg(feature = "ffmpeg_3_1")] MPEGTSStreamID, MasteringDisplayMetadata, + #[cfg(feature = "ffmpeg_3_3")] DataSpherical, DataNb, + #[cfg(feature = "ffmpeg_3_3")] ContentLightLevel, + #[cfg(feature = "ffmpeg_3_4")] A53CC, + + #[cfg(feature = "ffmpeg_4_0")] + EncryptionInitInfo, + #[cfg(feature = "ffmpeg_4_0")] + EncryptionInfo, + + #[cfg(feature = "ffmpeg_4_1")] + AFD, + + #[cfg(feature = "ffmpeg_4_3")] + PRFT, + #[cfg(feature = "ffmpeg_4_3")] + ICC_PROFILE, + #[cfg(feature = "ffmpeg_4_3")] + DOVI_CONF, } impl From for Type { @@ -57,13 +74,30 @@ impl From for Type { AV_PKT_DATA_WEBVTT_IDENTIFIER => Type::WebVTTIdentifier, AV_PKT_DATA_WEBVTT_SETTINGS => Type::WebVTTSettings, AV_PKT_DATA_METADATA_UPDATE => Type::MetadataUpdate, + #[cfg(feature = "ffmpeg_3_1")] AV_PKT_DATA_MPEGTS_STREAM_ID => Type::MPEGTSStreamID, AV_PKT_DATA_MASTERING_DISPLAY_METADATA => Type::MasteringDisplayMetadata, + #[cfg(feature = "ffmpeg_3_3")] AV_PKT_DATA_SPHERICAL => Type::DataSpherical, AV_PKT_DATA_NB => Type::DataNb, + #[cfg(feature = "ffmpeg_3_3")] AV_PKT_DATA_CONTENT_LIGHT_LEVEL => Type::ContentLightLevel, + #[cfg(feature = "ffmpeg_3_4")] AV_PKT_DATA_A53_CC => Type::A53CC, + #[cfg(feature = "ffmpeg_4_0")] + AV_PKT_DATA_ENCRYPTION_INIT_INFO => Type::EncryptionInitInfo, + #[cfg(feature = "ffmpeg_4_0")] + AV_PKT_DATA_ENCRYPTION_INFO => Type::EncryptionInfo, + #[cfg(feature = "ffmpeg_4_1")] + AV_PKT_DATA_AFD => Type::AFD, + + #[cfg(feature = "ffmpeg_4_3")] + AV_PKT_DATA_PRFT => Type::PRFT, + #[cfg(feature = "ffmpeg_4_3")] + AV_PKT_DATA_ICC_PROFILE => Type::ICC_PROFILE, + #[cfg(feature = "ffmpeg_4_3")] + AV_PKT_DATA_DOVI_CONF => Type::DOVI_CONF, } } } @@ -90,13 +124,30 @@ impl Into for Type { Type::WebVTTIdentifier => AV_PKT_DATA_WEBVTT_IDENTIFIER, Type::WebVTTSettings => AV_PKT_DATA_WEBVTT_SETTINGS, Type::MetadataUpdate => AV_PKT_DATA_METADATA_UPDATE, + #[cfg(feature = "ffmpeg_3_1")] Type::MPEGTSStreamID => AV_PKT_DATA_MPEGTS_STREAM_ID, Type::MasteringDisplayMetadata => AV_PKT_DATA_MASTERING_DISPLAY_METADATA, + #[cfg(feature = "ffmpeg_3_3")] Type::DataSpherical => AV_PKT_DATA_SPHERICAL, Type::DataNb => AV_PKT_DATA_NB, + #[cfg(feature = "ffmpeg_3_3")] Type::ContentLightLevel => AV_PKT_DATA_CONTENT_LIGHT_LEVEL, + #[cfg(feature = "ffmpeg_3_4")] Type::A53CC => AV_PKT_DATA_A53_CC, + #[cfg(feature = "ffmpeg_4_0")] + Type::EncryptionInitInfo => AV_PKT_DATA_ENCRYPTION_INIT_INFO, + #[cfg(feature = "ffmpeg_4_0")] + Type::EncryptionInfo => AV_PKT_DATA_ENCRYPTION_INFO, + #[cfg(feature = "ffmpeg_4_1")] + Type::AFD => AV_PKT_DATA_AFD, + + #[cfg(feature = "ffmpeg_4_3")] + Type::PRFT => AV_PKT_DATA_PRFT, + #[cfg(feature = "ffmpeg_4_3")] + Type::ICC_PROFILE => AV_PKT_DATA_ICC_PROFILE, + #[cfg(feature = "ffmpeg_4_3")] + Type::DOVI_CONF => AV_PKT_DATA_DOVI_CONF, } } } @@ -110,7 +161,7 @@ pub struct SideData<'a> { impl<'a> SideData<'a> { pub unsafe fn wrap(ptr: *mut AVPacketSideData) -> Self { SideData { - ptr: ptr, + ptr, _marker: PhantomData, } } diff --git a/src/codec/packet/traits.rs b/src/codec/packet/traits.rs index fb266dff..00eccda8 100644 --- a/src/codec/packet/traits.rs +++ b/src/codec/packet/traits.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; pub trait Ref { fn as_ptr(&self) -> *const AVPacket; diff --git a/src/codec/parameters.rs b/src/codec/parameters.rs index 2ce5b9dd..5f95607b 100644 --- a/src/codec/parameters.rs +++ b/src/codec/parameters.rs @@ -1,22 +1,18 @@ use std::rc::Rc; use super::{Context, Id}; -use ffi::*; -use media; +use crate::{ffi::*, media}; pub struct Parameters { ptr: *mut AVCodecParameters, - owner: Option>, + owner: Option>, } unsafe impl Send for Parameters {} impl Parameters { - pub unsafe fn wrap(ptr: *mut AVCodecParameters, owner: Option>) -> Self { - Parameters { - ptr: ptr, - owner: owner, - } + pub unsafe fn wrap(ptr: *mut AVCodecParameters, owner: Option>) -> Self { + Parameters { ptr, owner } } pub unsafe fn as_ptr(&self) -> *const AVCodecParameters { diff --git a/src/codec/picture.rs b/src/codec/picture.rs index d7ae3225..a76a7bbb 100644 --- a/src/codec/picture.rs +++ b/src/codec/picture.rs @@ -1,11 +1,7 @@ -use std::marker::PhantomData; -use std::mem; -use std::slice; +use std::{marker::PhantomData, mem, slice}; -use ffi::*; -use format; +use crate::{ffi::*, format, Error}; use libc::{c_int, size_t}; -use Error; pub struct Picture<'a> { ptr: *mut AVPicture, @@ -26,11 +22,11 @@ impl<'a> Picture<'a> { height: u32, ) -> Self { Picture { - ptr: ptr, + ptr, - format: format, - width: width, - height: height, + format, + width, + height, _own: false, _marker: PhantomData, @@ -62,11 +58,11 @@ impl<'a> Picture<'a> { match avpicture_alloc(ptr, format.into(), width as c_int, height as c_int) { 0 => Ok(Picture { - ptr: ptr, + ptr, - format: format, - width: width, - height: height, + format, + width, + height, _own: true, _marker: PhantomData, diff --git a/src/codec/profile.rs b/src/codec/profile.rs index c76b86c2..d6197775 100644 --- a/src/codec/profile.rs +++ b/src/codec/profile.rs @@ -1,5 +1,5 @@ use super::Id; -use ffi::*; +use crate::ffi::*; use libc::c_int; #[allow(non_camel_case_types)] diff --git a/src/codec/subtitle/flag.rs b/src/codec/subtitle/flag.rs index cd8b6244..ef119ec2 100644 --- a/src/codec/subtitle/flag.rs +++ b/src/codec/subtitle/flag.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; bitflags! { diff --git a/src/codec/subtitle/mod.rs b/src/codec/subtitle/mod.rs index 7d697c4d..f5be00f4 100644 --- a/src/codec/subtitle/mod.rs +++ b/src/codec/subtitle/mod.rs @@ -7,12 +7,10 @@ pub use self::rect::{Ass, Bitmap, Rect, Text}; mod rect_mut; pub use self::rect_mut::{AssMut, BitmapMut, RectMut, TextMut}; -use std::marker::PhantomData; -use std::mem; +use std::{marker::PhantomData, mem}; -use ffi::AVSubtitleType::*; -use ffi::*; -use libc::{c_uint, size_t, uint32_t}; +use crate::ffi::{AVSubtitleType::*, *}; +use libc::{c_uint, size_t}; #[derive(Eq, PartialEq, Clone, Copy, Debug)] pub enum Type { @@ -77,7 +75,7 @@ impl Subtitle { } pub fn set_start(&mut self, value: u32) { - self.0.start_display_time = value as uint32_t; + self.0.start_display_time = value; } pub fn end(&self) -> u32 { @@ -85,7 +83,7 @@ impl Subtitle { } pub fn set_end(&mut self, value: u32) { - self.0.end_display_time = value as uint32_t; + self.0.end_display_time = value; } pub fn rects(&self) -> RectIter { @@ -131,7 +129,7 @@ pub struct RectIter<'a> { impl<'a> RectIter<'a> { pub fn new(ptr: *const AVSubtitle) -> Self { RectIter { - ptr: ptr, + ptr, cur: 0, _marker: PhantomData, } @@ -145,11 +143,12 @@ impl<'a> Iterator for RectIter<'a> { unsafe { if self.cur >= (*self.ptr).num_rects { None - } else { + } + else { self.cur += 1; - Some(Rect::wrap(*(*self.ptr) - .rects - .offset((self.cur - 1) as isize))) + Some(Rect::wrap( + *(*self.ptr).rects.offset((self.cur - 1) as isize), + )) } } } @@ -175,7 +174,7 @@ pub struct RectMutIter<'a> { impl<'a> RectMutIter<'a> { pub fn new(ptr: *mut AVSubtitle) -> Self { RectMutIter { - ptr: ptr, + ptr, cur: 0, _marker: PhantomData, } @@ -189,11 +188,12 @@ impl<'a> Iterator for RectMutIter<'a> { unsafe { if self.cur >= (*self.ptr).num_rects { None - } else { + } + else { self.cur += 1; - Some(RectMut::wrap(*(*self.ptr) - .rects - .offset((self.cur - 1) as isize))) + Some(RectMut::wrap( + *(*self.ptr).rects.offset((self.cur - 1) as isize), + )) } } } diff --git a/src/codec/subtitle/rect.rs b/src/codec/subtitle/rect.rs index 0abafdcc..c8f9d3f8 100644 --- a/src/codec/subtitle/rect.rs +++ b/src/codec/subtitle/rect.rs @@ -1,10 +1,7 @@ -use std::ffi::CStr; -use std::marker::PhantomData; -use std::str::from_utf8_unchecked; +use std::{ffi::CStr, marker::PhantomData, str::from_utf8_unchecked}; use super::{Flags, Type}; -use ffi::*; -use {format, Picture}; +use crate::{ffi::*, format, Picture}; pub enum Rect<'a> { None(*const AVSubtitleRect), @@ -55,7 +52,7 @@ pub struct Bitmap<'a> { impl<'a> Bitmap<'a> { pub unsafe fn wrap(ptr: *const AVSubtitleRect) -> Self { Bitmap { - ptr: ptr, + ptr, _marker: PhantomData, } } @@ -108,7 +105,7 @@ pub struct Text<'a> { impl<'a> Text<'a> { pub unsafe fn wrap(ptr: *const AVSubtitleRect) -> Self { Text { - ptr: ptr, + ptr, _marker: PhantomData, } } @@ -133,7 +130,7 @@ pub struct Ass<'a> { impl<'a> Ass<'a> { pub unsafe fn wrap(ptr: *const AVSubtitleRect) -> Self { Ass { - ptr: ptr, + ptr, _marker: PhantomData, } } diff --git a/src/codec/subtitle/rect_mut.rs b/src/codec/subtitle/rect_mut.rs index 76ed5d2d..3963bffc 100644 --- a/src/codec/subtitle/rect_mut.rs +++ b/src/codec/subtitle/rect_mut.rs @@ -1,8 +1,7 @@ -use std::ffi::CString; -use std::ops::Deref; +use std::{ffi::CString, ops::Deref}; use super::{Ass, Bitmap, Flags, Text, Type}; -use ffi::*; +use crate::ffi::*; use libc::c_int; pub enum RectMut<'a> { diff --git a/src/codec/threading.rs b/src/codec/threading.rs index 4c3069d8..988c5fdf 100644 --- a/src/codec/threading.rs +++ b/src/codec/threading.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; #[derive(Eq, PartialEq, Clone, Copy, Debug)] diff --git a/src/codec/traits.rs b/src/codec/traits.rs index d6df3257..14a8a690 100644 --- a/src/codec/traits.rs +++ b/src/codec/traits.rs @@ -1,6 +1,8 @@ use super::{decoder, encoder}; -use codec::{Audio, Id, Video}; -use Codec; +use crate::{ + codec::{Audio, Id, Video}, + Codec, +}; pub trait Decoder { fn decoder(self) -> Option; @@ -22,7 +24,8 @@ impl Decoder for Codec { fn decoder(self) -> Option { if self.is_decoder() { Some(self) - } else { + } + else { None } } @@ -38,7 +41,8 @@ impl Decoder for Audio { fn decoder(self) -> Option { if self.is_decoder() { Some(*self) - } else { + } + else { None } } @@ -48,7 +52,8 @@ impl Decoder for Video { fn decoder(self) -> Option { if self.is_decoder() { Some(*self) - } else { + } + else { None } } @@ -74,7 +79,8 @@ impl Encoder for Codec { fn encoder(self) -> Option { if self.is_encoder() { Some(self) - } else { + } + else { None } } @@ -90,7 +96,8 @@ impl Encoder for Audio { fn encoder(self) -> Option { if self.is_encoder() { Some(*self) - } else { + } + else { None } } @@ -100,7 +107,8 @@ impl Encoder for Video { fn encoder(self) -> Option { if self.is_encoder() { Some(*self) - } else { + } + else { None } } diff --git a/src/codec/video.rs b/src/codec/video.rs index 8e9eb954..2501e2f8 100644 --- a/src/codec/video.rs +++ b/src/codec/video.rs @@ -1,8 +1,7 @@ use std::ops::Deref; use super::codec::Codec; -use ffi::*; -use {format, Rational}; +use crate::{ffi::*, format, Rational}; #[derive(PartialEq, Eq, Copy, Clone)] pub struct Video { @@ -11,7 +10,7 @@ pub struct Video { impl Video { pub unsafe fn new(codec: Codec) -> Video { - Video { codec: codec } + Video { codec } } } @@ -20,7 +19,8 @@ impl Video { unsafe { if (*self.codec.as_ptr()).supported_framerates.is_null() { None - } else { + } + else { Some(RateIter::new((*self.codec.as_ptr()).supported_framerates)) } } @@ -30,7 +30,8 @@ impl Video { unsafe { if (*self.codec.as_ptr()).pix_fmts.is_null() { None - } else { + } + else { Some(FormatIter::new((*self.codec.as_ptr()).pix_fmts)) } } @@ -51,7 +52,7 @@ pub struct RateIter { impl RateIter { pub fn new(ptr: *const AVRational) -> Self { - RateIter { ptr: ptr } + RateIter { ptr } } } @@ -78,7 +79,7 @@ pub struct FormatIter { impl FormatIter { pub fn new(ptr: *const AVPixelFormat) -> Self { - FormatIter { ptr: ptr } + FormatIter { ptr } } } diff --git a/src/device/extensions.rs b/src/device/extensions.rs index 4da0364f..b4f63950 100644 --- a/src/device/extensions.rs +++ b/src/device/extensions.rs @@ -1,11 +1,7 @@ -use std::marker::PhantomData; -use std::ptr; +use std::{marker::PhantomData, ptr}; -use device; -use ffi::*; -use format::context::common::Context; +use crate::{device, ffi::*, format::context::common::Context, Error}; use libc::c_int; -use Error; impl Context { pub fn devices(&self) -> Result { @@ -28,7 +24,7 @@ impl<'a> DeviceIter<'a> { n if n < 0 => Err(Error::from(n)), _ => Ok(DeviceIter { - ptr: ptr, + ptr, cur: 0, _marker: PhantomData, }), @@ -57,11 +53,12 @@ impl<'a> Iterator for DeviceIter<'a> { unsafe { if self.cur >= (*self.ptr).nb_devices { None - } else { + } + else { self.cur += 1; - Some(device::Info::wrap(*(*self.ptr) - .devices - .offset((self.cur - 1) as isize))) + Some(device::Info::wrap( + *(*self.ptr).devices.offset((self.cur - 1) as isize), + )) } } } diff --git a/src/device/input.rs b/src/device/input.rs index 548d2379..b8f2ad90 100644 --- a/src/device/input.rs +++ b/src/device/input.rs @@ -1,8 +1,6 @@ use std::ptr; -use ffi::*; -use format; -use Format; +use crate::{ffi::*, format, Format}; pub struct AudioIter(*mut AVInputFormat); @@ -15,7 +13,8 @@ impl Iterator for AudioIter { if ptr.is_null() && !self.0.is_null() { None - } else { + } + else { self.0 = ptr; Some(Format::Input(format::Input::wrap(ptr))) @@ -39,7 +38,8 @@ impl Iterator for VideoIter { if ptr.is_null() && !self.0.is_null() { None - } else { + } + else { self.0 = ptr; Some(Format::Input(format::Input::wrap(ptr))) diff --git a/src/device/mod.rs b/src/device/mod.rs index 792bcd59..8939ad53 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -2,11 +2,9 @@ pub mod extensions; pub mod input; pub mod output; -use std::ffi::CStr; -use std::marker::PhantomData; -use std::str::from_utf8_unchecked; +use std::{ffi::CStr, marker::PhantomData, str::from_utf8_unchecked}; -use ffi::*; +use crate::ffi::*; pub struct Info<'a> { ptr: *mut AVDeviceInfo, @@ -17,7 +15,7 @@ pub struct Info<'a> { impl<'a> Info<'a> { pub unsafe fn wrap(ptr: *mut AVDeviceInfo) -> Self { Info { - ptr: ptr, + ptr, _marker: PhantomData, } } diff --git a/src/device/output.rs b/src/device/output.rs index 995fef8d..7727f10c 100644 --- a/src/device/output.rs +++ b/src/device/output.rs @@ -1,8 +1,6 @@ use std::ptr; -use ffi::*; -use format; -use Format; +use crate::{ffi::*, format, Format}; pub struct AudioIter(*mut AVOutputFormat); @@ -15,7 +13,8 @@ impl Iterator for AudioIter { if ptr.is_null() && !self.0.is_null() { None - } else { + } + else { self.0 = ptr; Some(Format::Output(format::Output::wrap(ptr))) @@ -39,7 +38,8 @@ impl Iterator for VideoIter { if ptr.is_null() && !self.0.is_null() { None - } else { + } + else { self.0 = ptr; Some(Format::Output(format::Output::wrap(ptr))) diff --git a/src/filter/context/context.rs b/src/filter/context/context.rs index d2145dfd..5db4b773 100644 --- a/src/filter/context/context.rs +++ b/src/filter/context/context.rs @@ -1,9 +1,8 @@ use std::marker::PhantomData; use super::{Sink, Source}; -use ffi::*; +use crate::{ffi::*, format, option, ChannelLayout}; use libc::c_void; -use {format, option, ChannelLayout}; pub struct Context<'a> { ptr: *mut AVFilterContext, @@ -14,7 +13,7 @@ pub struct Context<'a> { impl<'a> Context<'a> { pub unsafe fn wrap(ptr: *mut AVFilterContext) -> Self { Context { - ptr: ptr, + ptr, _marker: PhantomData, } } diff --git a/src/filter/context/sink.rs b/src/filter/context/sink.rs index 1a79b938..67851323 100644 --- a/src/filter/context/sink.rs +++ b/src/filter/context/sink.rs @@ -1,7 +1,6 @@ use super::Context; -use ffi::*; +use crate::{ffi::*, Error, Frame}; use libc::c_int; -use {Error, Frame}; pub struct Sink<'a> { ctx: &'a mut Context<'a>, @@ -9,11 +8,12 @@ pub struct Sink<'a> { impl<'a> Sink<'a> { pub unsafe fn wrap<'b>(ctx: &'b mut Context<'b>) -> Sink<'b> { - Sink { ctx: ctx } + Sink { ctx } } } impl<'a> Sink<'a> { + #[cfg(feature = "ffmpeg_3_3")] pub fn frame(&mut self, frame: &mut Frame) -> Result<(), Error> { unsafe { match av_buffersink_get_frame(self.ctx.as_mut_ptr(), frame.as_mut_ptr()) { @@ -23,6 +23,7 @@ impl<'a> Sink<'a> { } } + #[cfg(feature = "ffmpeg_3_3")] pub fn samples(&mut self, frame: &mut Frame, samples: usize) -> Result<(), Error> { unsafe { match av_buffersink_get_samples( diff --git a/src/filter/context/source.rs b/src/filter/context/source.rs index 1b934f56..11c55181 100644 --- a/src/filter/context/source.rs +++ b/src/filter/context/source.rs @@ -1,8 +1,7 @@ use std::ptr; use super::Context; -use ffi::*; -use {Error, Frame}; +use crate::{ffi::*, Error, Frame}; pub struct Source<'a> { ctx: &'a mut Context<'a>, @@ -10,7 +9,7 @@ pub struct Source<'a> { impl<'a> Source<'a> { pub unsafe fn wrap<'b>(ctx: &'b mut Context<'b>) -> Source<'b> { - Source { ctx: ctx } + Source { ctx } } } @@ -31,4 +30,13 @@ impl<'a> Source<'a> { pub fn flush(&mut self) -> Result<(), Error> { unsafe { self.add(&Frame::wrap(ptr::null_mut())) } } + + pub fn close(&mut self, pts: i64) -> Result<(), Error> { + unsafe { + match av_buffersrc_close(self.ctx.as_mut_ptr(), pts, 0) { + 0 => Ok(()), + e => Err(Error::from(e)), + } + } + } } diff --git a/src/filter/filter.rs b/src/filter/filter.rs index fa0ff92b..62405576 100644 --- a/src/filter/filter.rs +++ b/src/filter/filter.rs @@ -1,9 +1,7 @@ -use std::ffi::CStr; -use std::marker::PhantomData; -use std::str::from_utf8_unchecked; +use std::{ffi::CStr, marker::PhantomData, str::from_utf8_unchecked}; use super::{Flags, Pad}; -use ffi::*; +use crate::ffi::*; pub struct Filter { ptr: *mut AVFilter, @@ -11,7 +9,7 @@ pub struct Filter { impl Filter { pub unsafe fn wrap(ptr: *mut AVFilter) -> Self { - Filter { ptr: ptr } + Filter { ptr } } pub unsafe fn as_ptr(&self) -> *const AVFilter { @@ -34,7 +32,8 @@ impl Filter { if ptr.is_null() { None - } else { + } + else { Some(from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) } } @@ -46,7 +45,8 @@ impl Filter { if ptr.is_null() { None - } else { + } + else { Some(PadIter::new((*self.as_ptr()).inputs)) } } @@ -58,7 +58,8 @@ impl Filter { if ptr.is_null() { None - } else { + } + else { Some(PadIter::new((*self.as_ptr()).outputs)) } } @@ -79,7 +80,7 @@ pub struct PadIter<'a> { impl<'a> PadIter<'a> { pub fn new(ptr: *const AVFilterPad) -> Self { PadIter { - ptr: ptr, + ptr, cur: 0, _marker: PhantomData, } @@ -95,7 +96,7 @@ impl<'a> Iterator for PadIter<'a> { return None; } - let pad = Pad::wrap(self.ptr.offset(self.cur)); + let pad = Pad::wrap(self.ptr, self.cur); self.cur += 1; Some(pad) diff --git a/src/filter/flag.rs b/src/filter/flag.rs index 0ffc2fec..3d2cb127 100644 --- a/src/filter/flag.rs +++ b/src/filter/flag.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; bitflags! { diff --git a/src/filter/graph.rs b/src/filter/graph.rs index c2ae4416..e68d938b 100644 --- a/src/filter/graph.rs +++ b/src/filter/graph.rs @@ -1,11 +1,12 @@ -use std::ffi::{CStr, CString}; -use std::ptr; -use std::str::from_utf8_unchecked; +use std::{ + ffi::{CStr, CString}, + ptr, + str::from_utf8_unchecked, +}; use super::{Context, Filter}; -use ffi::*; +use crate::{ffi::*, Error}; use libc::c_int; -use Error; pub struct Graph { ptr: *mut AVFilterGraph, @@ -16,7 +17,7 @@ unsafe impl Sync for Graph {} impl Graph { pub unsafe fn wrap(ptr: *mut AVFilterGraph) -> Self { - Graph { ptr: ptr } + Graph { ptr } } pub unsafe fn as_ptr(&self) -> *const AVFilterGraph { @@ -88,7 +89,8 @@ impl Graph { if ptr.is_null() { None - } else { + } + else { Some(Context::wrap(ptr)) } } @@ -136,7 +138,7 @@ pub struct Parser<'a> { impl<'a> Parser<'a> { pub fn new(graph: &mut Graph) -> Parser { Parser { - graph: graph, + graph, inputs: ptr::null_mut(), outputs: ptr::null_mut(), } @@ -160,7 +162,8 @@ impl<'a> Parser<'a> { if self.inputs.is_null() { self.inputs = input; - } else { + } + else { (*self.inputs).next = input; } } @@ -186,7 +189,8 @@ impl<'a> Parser<'a> { if self.outputs.is_null() { self.outputs = output; - } else { + } + else { (*self.outputs).next = output; } } diff --git a/src/filter/mod.rs b/src/filter/mod.rs index bcb5aeac..8f1fa639 100644 --- a/src/filter/mod.rs +++ b/src/filter/mod.rs @@ -13,11 +13,12 @@ pub use self::context::{Context, Sink, Source}; pub mod graph; pub use self::graph::Graph; -use std::ffi::{CStr, CString}; -use std::str::from_utf8_unchecked; +use std::{ + ffi::{CStr, CString}, + str::from_utf8_unchecked, +}; -use ffi::*; -use Error; +use crate::{ffi::*, Error}; pub fn register_all() { unsafe { @@ -53,8 +54,28 @@ pub fn find(name: &str) -> Option { if ptr.is_null() { None - } else { - Some(Filter::wrap(ptr)) } + else { + Some(Filter::wrap(ptr as *mut _)) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_paditer() { + register_all(); + assert_eq!( + find("overlay") + .unwrap() + .inputs() + .unwrap() + .map(|input| input.name().unwrap().to_string()) + .collect::>(), + vec!("main", "overlay") + ); } } diff --git a/src/filter/pad.rs b/src/filter/pad.rs index 84912281..761ea17c 100644 --- a/src/filter/pad.rs +++ b/src/filter/pad.rs @@ -1,20 +1,19 @@ -use std::ffi::CStr; -use std::marker::PhantomData; -use std::str::from_utf8_unchecked; +use std::{ffi::CStr, marker::PhantomData, str::from_utf8_unchecked}; -use ffi::*; -use media; +use crate::{ffi::*, media}; pub struct Pad<'a> { ptr: *const AVFilterPad, + idx: isize, _marker: PhantomData<&'a ()>, } impl<'a> Pad<'a> { - pub unsafe fn wrap(ptr: *const AVFilterPad) -> Self { + pub unsafe fn wrap(ptr: *const AVFilterPad, idx: isize) -> Self { Pad { - ptr: ptr, + ptr, + idx, _marker: PhantomData, } } @@ -31,17 +30,18 @@ impl<'a> Pad<'a> { impl<'a> Pad<'a> { pub fn name(&self) -> Option<&str> { unsafe { - let ptr = avfilter_pad_get_name(self.ptr, 0); + let ptr = avfilter_pad_get_name(self.ptr, self.idx as i32); if ptr.is_null() { None - } else { + } + else { Some(from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) } } } pub fn medium(&self) -> media::Type { - unsafe { media::Type::from(avfilter_pad_get_type(self.ptr, 0)) } + unsafe { media::Type::from(avfilter_pad_get_type(self.ptr, self.idx as i32)) } } } diff --git a/src/format/chapter/chapter.rs b/src/format/chapter/chapter.rs index 7d30ff46..7d74dc9f 100644 --- a/src/format/chapter/chapter.rs +++ b/src/format/chapter/chapter.rs @@ -1,7 +1,6 @@ -use ffi::*; -use {DictionaryRef, Rational}; +use crate::{ffi::*, DictionaryRef, Rational}; -use format::context::common::Context; +use crate::format::context::common::Context; // WARNING: index refers to the offset in the chapters array (starting from 0) // it is not necessarly equal to the id (which may start at 1) @@ -12,16 +11,11 @@ pub struct Chapter<'a> { impl<'a> Chapter<'a> { pub unsafe fn wrap(context: &Context, index: usize) -> Chapter { - Chapter { - context: context, - index: index, - } + Chapter { context, index } } pub unsafe fn as_ptr(&self) -> *const AVChapter { - *(*self.context.as_ptr()) - .chapters - .offset(self.index as isize) + *(*self.context.as_ptr()).chapters.add(self.index) } } diff --git a/src/format/chapter/chapter_mut.rs b/src/format/chapter/chapter_mut.rs index 99a3a0f2..200ed1e1 100644 --- a/src/format/chapter/chapter_mut.rs +++ b/src/format/chapter/chapter_mut.rs @@ -1,10 +1,7 @@ -use std::mem; -use std::ops::Deref; +use std::{mem, ops::Deref}; use super::Chapter; -use ffi::*; -use format::context::common::Context; -use {Dictionary, DictionaryMut, Rational}; +use crate::{ffi::*, format::context::common::Context, Dictionary, DictionaryMut, Rational}; // WARNING: index refers to the offset in the chapters array (starting from 0) // it is not necessarly equal to the id (which may start at 1) @@ -19,16 +16,14 @@ impl<'a> ChapterMut<'a> { pub unsafe fn wrap(context: &mut Context, index: usize) -> ChapterMut { ChapterMut { context: mem::transmute_copy(&context), - index: index, + index, immutable: Chapter::wrap(mem::transmute_copy(&context), index), } } pub unsafe fn as_mut_ptr(&mut self) -> *mut AVChapter { - *(*self.context.as_mut_ptr()) - .chapters - .offset(self.index as isize) + *(*self.context.as_mut_ptr()).chapters.add(self.index) } } @@ -58,8 +53,8 @@ impl<'a> ChapterMut<'a> { } pub fn set_metadata, V: AsRef>(&mut self, key: K, value: V) { - // dictionary.set() allocates the AVDictionary the first time a key/value is inserted - // so we want to update the metadata dictionary afterwards + // dictionary.set() allocates the AVDictionary the first time a key/value is + // inserted so we want to update the metadata dictionary afterwards unsafe { let mut dictionary = Dictionary::own(self.metadata().as_mut_ptr()); dictionary.set(key.as_ref(), value.as_ref()); diff --git a/src/format/context/common.rs b/src/format/context/common.rs index 4f503663..b7b88f34 100644 --- a/src/format/context/common.rs +++ b/src/format/context/common.rs @@ -1,11 +1,8 @@ -use std::mem; -use std::ptr; -use std::rc::Rc; +use std::{fmt, mem, ptr, rc::Rc}; use super::destructor::{self, Destructor}; -use ffi::*; +use crate::{ffi::*, media, Chapter, ChapterMut, DictionaryRef, Stream, StreamMut}; use libc::{c_int, c_uint}; -use {media, Chapter, ChapterMut, DictionaryRef, Stream, StreamMut}; pub struct Context { ptr: *mut AVFormatContext, @@ -17,7 +14,7 @@ unsafe impl Send for Context {} impl Context { pub unsafe fn wrap(ptr: *mut AVFormatContext, mode: destructor::Mode) -> Self { Context { - ptr: ptr, + ptr, dtor: Rc::new(Destructor::new(ptr, mode)), } } @@ -36,14 +33,20 @@ impl Context { } impl Context { + #[inline] + pub fn nb_streams(&self) -> u32 { + unsafe { (*self.as_ptr()).nb_streams } + } + pub fn stream<'a, 'b>(&'a self, index: usize) -> Option> where 'a: 'b, { unsafe { - if index >= (*self.as_ptr()).nb_streams as usize { + if index >= self.nb_streams() as usize { None - } else { + } + else { Some(Stream::wrap(self, index)) } } @@ -54,9 +57,10 @@ impl Context { 'a: 'b, { unsafe { - if index >= (*self.as_ptr()).nb_streams as usize { + if index >= self.nb_streams() as usize { None - } else { + } + else { Some(StreamMut::wrap(self, index)) } } @@ -70,10 +74,15 @@ impl Context { StreamIterMut::new(self) } + pub fn bit_rate(&self) -> i64 { + unsafe { (*self.as_ptr()).bit_rate } + } + pub fn duration(&self) -> i64 { unsafe { (*self.as_ptr()).duration } } + #[inline] pub fn nb_chapters(&self) -> u32 { unsafe { (*self.as_ptr()).nb_chapters } } @@ -83,9 +92,10 @@ impl Context { 'a: 'b, { unsafe { - if index >= (*self.as_ptr()).nb_chapters as usize { + if index >= self.nb_chapters() as usize { None - } else { + } + else { Some(Chapter::wrap(self, index)) } } @@ -96,9 +106,10 @@ impl Context { 'a: 'b, { unsafe { - if index >= (*self.as_ptr()).nb_chapters as usize { + if index >= self.nb_chapters() as usize { None - } else { + } + else { Some(ChapterMut::wrap(self, index)) } } @@ -127,7 +138,7 @@ pub struct Best<'a> { impl<'a> Best<'a> { pub unsafe fn new<'b, 'c: 'b>(context: &'c Context) -> Best<'b> { Best { - context: context, + context, wanted: -1, related: -1, @@ -167,7 +178,8 @@ impl<'a> Best<'a> { if index >= 0 && !decoder.is_null() { Some(Stream::wrap(self.context, index as usize)) - } else { + } + else { None } } @@ -182,7 +194,7 @@ pub struct StreamIter<'a> { impl<'a> StreamIter<'a> { pub fn new<'s, 'c: 's>(context: &'c Context) -> StreamIter<'s> { StreamIter { - context: context, + context, current: 0, } } @@ -218,7 +230,7 @@ impl<'a> Iterator for StreamIter<'a> { fn next(&mut self) -> Option<::Item> { unsafe { - if self.current >= (*self.context.as_ptr()).nb_streams { + if self.current >= self.context.nb_streams() { return None; } @@ -229,14 +241,12 @@ impl<'a> Iterator for StreamIter<'a> { } fn size_hint(&self) -> (usize, Option) { - unsafe { - let length = (*self.context.as_ptr()).nb_streams as usize; + let length = self.context.nb_streams() as usize; - ( - length - self.current as usize, - Some(length - self.current as usize), - ) - } + ( + length - self.current as usize, + Some(length - self.current as usize), + ) } } @@ -250,7 +260,7 @@ pub struct StreamIterMut<'a> { impl<'a> StreamIterMut<'a> { pub fn new<'s, 'c: 's>(context: &'c mut Context) -> StreamIterMut<'s> { StreamIterMut { - context: context, + context, current: 0, } } @@ -260,13 +270,12 @@ impl<'a> Iterator for StreamIterMut<'a> { type Item = StreamMut<'a>; fn next(&mut self) -> Option<::Item> { - unsafe { - if self.current >= (*self.context.as_ptr()).nb_streams { - return None; - } - - self.current += 1; + if self.current >= self.context.nb_streams() { + return None; + } + self.current += 1; + unsafe { Some(StreamMut::wrap( mem::transmute_copy(&self.context), (self.current - 1) as usize, @@ -275,14 +284,12 @@ impl<'a> Iterator for StreamIterMut<'a> { } fn size_hint(&self) -> (usize, Option) { - unsafe { - let length = (*self.context.as_ptr()).nb_streams as usize; + let length = self.context.nb_streams() as usize; - ( - length - self.current as usize, - Some(length - self.current as usize), - ) - } + ( + length - self.current as usize, + Some(length - self.current as usize), + ) } } @@ -296,7 +303,7 @@ pub struct ChapterIter<'a> { impl<'a> ChapterIter<'a> { pub fn new<'s, 'c: 's>(context: &'c Context) -> ChapterIter<'s> { ChapterIter { - context: context, + context, current: 0, } } @@ -339,7 +346,7 @@ pub struct ChapterIterMut<'a> { impl<'a> ChapterIterMut<'a> { pub fn new<'s, 'c: 's>(context: &'c mut Context) -> ChapterIterMut<'s> { ChapterIterMut { - context: context, + context, current: 0, } } @@ -376,3 +383,14 @@ impl<'a> Iterator for ChapterIterMut<'a> { } impl<'a> ExactSizeIterator for ChapterIterMut<'a> {} + +impl fmt::Debug for Context { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let mut s = fmt.debug_struct("AVFormatContext"); + s.field("bit_rate", &self.bit_rate()); + s.field("duration", &self.duration()); + s.field("nb_chapters", &self.nb_chapters()); + s.field("nb_streams", &self.nb_streams()); + s.finish() + } +} diff --git a/src/format/context/destructor.rs b/src/format/context/destructor.rs index cf2a8855..8f22130b 100644 --- a/src/format/context/destructor.rs +++ b/src/format/context/destructor.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; #[derive(Copy, Clone, Debug)] pub enum Mode { @@ -13,10 +13,7 @@ pub struct Destructor { impl Destructor { pub unsafe fn new(ptr: *mut AVFormatContext, mode: Mode) -> Self { - Destructor { - ptr: ptr, - mode: mode, - } + Destructor { ptr, mode } } } diff --git a/src/format/context/input.rs b/src/format/context/input.rs index 542455c8..5b3b0989 100644 --- a/src/format/context/input.rs +++ b/src/format/context/input.rs @@ -1,17 +1,21 @@ -use std::ffi::CString; -use std::mem; -use std::ops::{Deref, DerefMut}; -use std::ptr; - -use super::common::Context; -use super::destructor; -use ffi::*; -use util::range::Range; -use {format, Codec, Error, Packet, Stream}; +use std::{ + ffi::CString, + mem, + ops::{Deref, DerefMut}, +}; + +use super::{common::Context, destructor}; +use crate::{ + ffi::*, + format::{self, io::Io}, + util::range::Range, + Codec, Error, Packet, Stream, +}; pub struct Input { ptr: *mut AVFormatContext, ctx: Context, + io: Option, } unsafe impl Send for Input {} @@ -19,8 +23,17 @@ unsafe impl Send for Input {} impl Input { pub unsafe fn wrap(ptr: *mut AVFormatContext) -> Self { Input { - ptr: ptr, + ptr, + ctx: Context::wrap(ptr, destructor::Mode::Input), + io: None, + } + } + + pub unsafe fn wrap_with(ptr: *mut AVFormatContext, io: Io) -> Self { + Input { + ptr, ctx: Context::wrap(ptr, destructor::Mode::Input), + io: Some(io), } } @@ -44,7 +57,8 @@ impl Input { if ptr.is_null() { None - } else { + } + else { Some(Codec::wrap(ptr)) } } @@ -56,7 +70,8 @@ impl Input { if ptr.is_null() { None - } else { + } + else { Some(Codec::wrap(ptr)) } } @@ -68,7 +83,8 @@ impl Input { if ptr.is_null() { None - } else { + } + else { Some(Codec::wrap(ptr)) } } @@ -80,7 +96,8 @@ impl Input { if ptr.is_null() { None - } else { + } + else { Some(Codec::wrap(ptr)) } } @@ -149,29 +166,27 @@ pub struct PacketIter<'a> { impl<'a> PacketIter<'a> { pub fn new(context: &mut Input) -> PacketIter { - PacketIter { context: context } + PacketIter { context } } } impl<'a> Iterator for PacketIter<'a> { - type Item = (Stream<'a>, Packet); + type Item = Result<(Stream<'a>, Packet), Error>; fn next(&mut self) -> Option<::Item> { let mut packet = Packet::empty(); - loop { - match packet.read(self.context) { - Ok(..) => unsafe { - return Some(( - Stream::wrap(mem::transmute_copy(&self.context), packet.stream()), - packet, - )); - }, + match packet.read(self.context) { + Err(Error::Eof) => None, - Err(Error::Eof) => return None, + Ok(..) => unsafe { + Some(Ok(( + Stream::wrap(mem::transmute_copy(&self.context), packet.stream()), + packet, + ))) + }, - Err(..) => (), - } + Err(err) => Some(Err(err)), } } } @@ -183,7 +198,7 @@ pub fn dump(ctx: &Input, index: i32, url: Option<&str>) { av_dump_format( ctx.as_ptr() as *mut _, index, - url.map(|u| u.as_ptr()).unwrap_or(ptr::null()), + url.unwrap_or_else(|| CString::new("").unwrap()).as_ptr(), 0, ); } diff --git a/src/format/context/mod.rs b/src/format/context/mod.rs index 5ef16673..96dd845d 100644 --- a/src/format/context/mod.rs +++ b/src/format/context/mod.rs @@ -19,11 +19,7 @@ unsafe impl Send for Context {} impl Context { pub fn is_input(&self) -> bool { - if let Context::Input(..) = *self { - true - } else { - false - } + matches!(*self, Context::Input(..)) } pub fn input(self) -> Input { @@ -35,11 +31,7 @@ impl Context { } pub fn is_output(&self) -> bool { - if let Context::Output(..) = *self { - true - } else { - false - } + matches!(*self, Context::Output(..)) } pub fn output(self) -> Output { diff --git a/src/format/context/output.rs b/src/format/context/output.rs index 4bda2a57..5d09bb99 100644 --- a/src/format/context/output.rs +++ b/src/format/context/output.rs @@ -1,19 +1,24 @@ -use std::ffi::CString; -use std::mem::size_of; -use std::ops::{Deref, DerefMut}; -use std::ptr; +use std::{ + ffi::CString, + mem::size_of, + ops::{Deref, DerefMut}, + ptr, +}; use libc; -use super::common::Context; -use super::destructor; -use codec::traits; -use ffi::*; -use {format, ChapterMut, Dictionary, Error, Rational, StreamMut}; +use super::{common::Context, destructor}; +use crate::{ + codec::traits, + ffi::*, + format::{self, io::Io}, + ChapterMut, Dictionary, Error, Rational, StreamMut, +}; pub struct Output { ptr: *mut AVFormatContext, ctx: Context, + io: Option, } unsafe impl Send for Output {} @@ -21,8 +26,17 @@ unsafe impl Send for Output {} impl Output { pub unsafe fn wrap(ptr: *mut AVFormatContext) -> Self { Output { - ptr: ptr, + ptr, ctx: Context::wrap(ptr, destructor::Mode::Output), + io: None, + } + } + + pub unsafe fn wrap_with(ptr: *mut AVFormatContext, io: Io) -> Self { + Output { + ptr, + ctx: Context::wrap(ptr, destructor::Mode::Output), + io: Some(io), } } @@ -72,11 +86,12 @@ impl Output { pub fn add_stream(&mut self, codec: E) -> Result { unsafe { - let codec = codec.encoder().ok_or(Error::EncoderNotFound)?; - let ptr = avformat_new_stream(self.as_mut_ptr(), codec.as_ptr()); + let codec = codec.encoder(); + let codec = codec.map_or(ptr::null(), |c| c.as_ptr()); + let ptr = avformat_new_stream(self.as_mut_ptr(), codec); if ptr.is_null() { - panic!("out of memory"); + return Err(Error::Unknown); } let index = (*self.ctx.as_ptr()).nb_streams - 1; @@ -126,7 +141,8 @@ impl Output { (*self.as_mut_ptr()).nb_chapters = nb_chapters as u32; let index = (*self.ctx.as_ptr()).nb_chapters - 1; index as usize - } else { + } + else { // failed to add the chapter av_freep(ptr); return Err(Error::Bug); @@ -173,7 +189,7 @@ pub fn dump(ctx: &Output, index: i32, url: Option<&str>) { av_dump_format( ctx.as_ptr() as *mut _, index, - url.map(|u| u.as_ptr()).unwrap_or(ptr::null()), + url.unwrap_or_else(|| CString::new("").unwrap()).as_ptr(), 1, ); } diff --git a/src/format/format/flag.rs b/src/format/format/flag.rs index 3309d32b..2383bd5e 100644 --- a/src/format/format/flag.rs +++ b/src/format/format/flag.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; bitflags! { @@ -6,6 +6,7 @@ bitflags! { const NO_FILE = AVFMT_NOFILE; const NEED_NUMBER = AVFMT_NEEDNUMBER; const SHOW_IDS = AVFMT_SHOW_IDS; + #[cfg(not(feature = "ffmpeg_4_0"))] const RAW_PICTURE = AVFMT_RAWPICTURE; const GLOBAL_HEADER = AVFMT_GLOBALHEADER; const NO_TIMESTAMPS = AVFMT_NOTIMESTAMPS; diff --git a/src/format/format/input.rs b/src/format/format/input.rs index 41842527..7b4be615 100644 --- a/src/format/format/input.rs +++ b/src/format/format/input.rs @@ -1,7 +1,6 @@ -use std::ffi::CStr; -use std::str::from_utf8_unchecked; +use std::{ffi::CStr, str::from_utf8_unchecked}; -use ffi::*; +use crate::ffi::*; pub struct Input { ptr: *mut AVInputFormat, @@ -9,7 +8,7 @@ pub struct Input { impl Input { pub unsafe fn wrap(ptr: *mut AVInputFormat) -> Self { - Input { ptr: ptr } + Input { ptr } } pub unsafe fn as_ptr(&self) -> *const AVInputFormat { @@ -36,7 +35,8 @@ impl Input { if ptr.is_null() { Vec::new() - } else { + } + else { from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) .split(',') .collect() @@ -50,7 +50,8 @@ impl Input { if ptr.is_null() { Vec::new() - } else { + } + else { from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) .split(',') .collect() diff --git a/src/format/format/iter.rs b/src/format/format/iter.rs index f631c5fa..12e954f5 100644 --- a/src/format/format/iter.rs +++ b/src/format/format/iter.rs @@ -1,7 +1,7 @@ use std::ptr; use super::{Format, Input, Output}; -use ffi::*; +use crate::ffi::*; pub struct Iter { input: *mut AVInputFormat, @@ -44,7 +44,8 @@ impl Iterator for Iter { self.step = Step::Output; self.next() - } else { + } + else { self.input = ptr; Some(Format::Input(Input::wrap(ptr))) @@ -58,7 +59,8 @@ impl Iterator for Iter { self.step = Step::Done; self.next() - } else { + } + else { self.output = ptr; Some(Format::Output(Output::wrap(ptr))) diff --git a/src/format/format/output.rs b/src/format/format/output.rs index e758ea93..0e968150 100644 --- a/src/format/format/output.rs +++ b/src/format/format/output.rs @@ -1,12 +1,13 @@ use std::path::Path; -use std::ffi::{CStr, CString}; -use std::ptr; -use std::str::from_utf8_unchecked; +use std::{ + ffi::{CStr, CString}, + ptr, + str::from_utf8_unchecked, +}; use super::Flags; -use ffi::*; -use {codec, media}; +use crate::{codec, ffi::*, media}; pub struct Output { ptr: *mut AVOutputFormat, @@ -14,7 +15,7 @@ pub struct Output { impl Output { pub unsafe fn wrap(ptr: *mut AVOutputFormat) -> Self { - Output { ptr: ptr } + Output { ptr } } pub unsafe fn as_ptr(&self) -> *const AVOutputFormat { @@ -41,7 +42,8 @@ impl Output { if ptr.is_null() { Vec::new() - } else { + } + else { from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) .split(',') .collect() @@ -55,7 +57,8 @@ impl Output { if ptr.is_null() { Vec::new() - } else { + } + else { from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) .split(',') .collect() diff --git a/src/format/io.rs b/src/format/io.rs new file mode 100644 index 00000000..bd8c9460 --- /dev/null +++ b/src/format/io.rs @@ -0,0 +1,198 @@ +use crate::{ + ffi::*, + format::context::{self, Context}, + Error, Format, +}; +use libc::{c_int, c_void, SEEK_CUR, SEEK_END, SEEK_SET}; +use std::{ + convert::TryInto, + io::{self, Read, Seek, SeekFrom, Write}, + ptr, slice, +}; + +pub trait Stream: Read + Write + Seek { + fn size(&self) -> io::Result { + Ok(0) + } +} + +#[derive(Debug)] +pub struct Io { + ptr: *mut AVIOContext, + proxy: *mut Box, +} + +impl From for Io { + fn from(value: F) -> Io { + Io::new(Box::new(value)) + } +} + +impl Io { + pub unsafe fn as_ptr(&self) -> *const AVIOContext { + self.ptr as *const _ + } + + pub unsafe fn as_mut_ptr(&mut self) -> *mut AVIOContext { + self.ptr + } +} + +#[no_mangle] +unsafe extern "C" fn read_packet(opaque: *mut c_void, buf: *mut u8, size: c_int) -> c_int { + let mut proxy = Box::>::from_raw(opaque.cast()); + let buffer = slice::from_raw_parts_mut(buf, size.try_into().unwrap()); + + let result = match proxy.read(buffer) { + Ok(0) => AVERROR_EOF, + Ok(size) => size.try_into().unwrap(), + Err(err) => err.raw_os_error().unwrap().try_into().unwrap(), + }; + + Box::into_raw(proxy); + result.try_into().unwrap() +} + +#[no_mangle] +unsafe extern "C" fn write_packet(opaque: *mut c_void, buf: *mut u8, size: c_int) -> c_int { + let mut proxy = Box::>::from_raw(opaque.cast()); + let buffer = slice::from_raw_parts(buf, size.try_into().unwrap()); + + let result = match proxy.write(buffer) { + Ok(size) => size, + Err(err) => err.raw_os_error().unwrap().try_into().unwrap(), + }; + + Box::into_raw(proxy); + result.try_into().unwrap() +} + +#[no_mangle] +unsafe extern "C" fn seek(opaque: *mut c_void, offset: i64, whence: c_int) -> i64 { + let mut proxy = Box::>::from_raw(opaque.cast()); + let whence = match whence { + AVSEEK_SIZE => { + let result = match proxy.size() { + Ok(size) => size, + Err(err) => err.raw_os_error().unwrap().try_into().unwrap(), + }; + Box::into_raw(proxy); + return result.try_into().unwrap(); + } + + SEEK_SET => SeekFrom::Start(offset.try_into().unwrap()), + SEEK_END => SeekFrom::End(offset.try_into().unwrap()), + SEEK_CUR => SeekFrom::Current(offset.try_into().unwrap()), + _ => unreachable!(), + }; + + let result = match proxy.seek(whence) { + Ok(size) => size, + Err(err) => err.raw_os_error().unwrap().try_into().unwrap(), + }; + + Box::into_raw(proxy); + result.try_into().unwrap() +} + +impl Io { + pub fn new(proxy: Box) -> Self { + Io::with_capacity(4096, proxy) + } + + pub fn with_capacity(size: usize, proxy: Box) -> Self { + unsafe { + let proxy = Box::into_raw(Box::new(proxy)); + let ptr = avio_alloc_context( + ptr::null_mut(), + 0, + AVIO_FLAG_WRITE & AVIO_FLAG_DIRECT, + proxy.cast(), + Some(read_packet), + Some(write_packet), + Some(seek), + ); + + Io { proxy, ptr } + } + } +} + +impl Drop for Io { + fn drop(&mut self) { + unsafe { + avio_context_free(&mut self.ptr); + } + } +} + +pub fn open>(io: I, format: &Format) -> Result { + unsafe { + let mut ps = avformat_alloc_context(); + let mut io = io.into(); + (*ps).pb = io.as_mut_ptr(); + + match *format { + Format::Input(ref format) => match avformat_open_input( + &mut ps, + ptr::null_mut(), + format.as_ptr() as *mut _, + ptr::null_mut(), + ) { + 0 => match avformat_find_stream_info(ps, ptr::null_mut()) { + r if r >= 0 => Ok(Context::Input(context::Input::wrap_with(ps, io))), + e => { + avformat_close_input(&mut ps); + Err(Error::from(e)) + } + }, + + e => Err(Error::from(e)), + }, + + Format::Output(ref format) => match avformat_alloc_output_context2( + &mut ps, + format.as_ptr() as *mut _, + ptr::null(), + ptr::null(), + ) { + 0 => Ok(Context::Output(context::Output::wrap_with(ps, io))), + e => Err(Error::from(e)), + }, + } + } +} + +pub fn input>(io: I) -> Result { + unsafe { + let mut ps = avformat_alloc_context(); + let mut io = io.into(); + (*ps).pb = io.as_mut_ptr(); + + match avformat_open_input(&mut ps, ptr::null_mut(), ptr::null_mut(), ptr::null_mut()) { + 0 => match avformat_find_stream_info(ps, ptr::null_mut()) { + r if r >= 0 => Ok(context::Input::wrap_with(ps, io)), + e => { + avformat_close_input(&mut ps); + Err(Error::from(e)) + } + }, + + e => Err(Error::from(e)), + } + } +} + +pub fn output>(io: I) -> Result { + unsafe { + let mut ps = avformat_alloc_context(); + let mut io = io.into(); + (*ps).pb = io.as_mut_ptr(); + + match avformat_alloc_output_context2(&mut ps, ptr::null_mut(), ptr::null(), ptr::null_mut()) + { + 0 => Ok(context::Output::wrap_with(ps, io)), + e => Err(Error::from(e)), + } + } +} diff --git a/src/format/mod.rs b/src/format/mod.rs index e9f28a65..7a06633b 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -1,6 +1,8 @@ -pub use util::format::{pixel, Pixel}; -pub use util::format::{sample, Sample}; -use util::interrupt; +pub use crate::util::format::{pixel, sample, Pixel, Sample}; +use crate::util::interrupt; + +pub mod io; +pub use self::io::Io; pub mod stream; @@ -10,18 +12,17 @@ pub mod context; pub use self::context::Context; pub mod format; -pub use self::format::{flag, Flags}; -pub use self::format::{list, Input, Output}; +pub use self::format::{flag, list, Flags, Input, Output}; pub mod network; -use std::ffi::{CStr, CString}; -use std::path::Path; -use std::ptr; -use std::str::from_utf8_unchecked; +use std::{ + ffi::{CStr, CString}, + ptr, + str::from_utf8_unchecked, +}; -use ffi::*; -use {Dictionary, Error, Format}; +use crate::{ffi::*, Dictionary, Error, Format}; pub fn register_all() { unsafe { @@ -53,16 +54,10 @@ pub fn license() -> &'static str { unsafe { from_utf8_unchecked(CStr::from_ptr(avformat_license()).to_bytes()) } } -// XXX: use to_cstring when stable -fn from_path>(path: &P) -> CString { - CString::new(path.as_ref().as_os_str().to_str().unwrap()).unwrap() -} - -// NOTE: this will be better with specialization or anonymous return types -pub fn open>(path: &P, format: &Format) -> Result { +pub fn open(path: impl AsRef, format: &Format) -> Result { unsafe { let mut ps = ptr::null_mut(); - let path = from_path(path); + let path = CString::new(path.as_ref()).unwrap(); match *format { Format::Input(ref format) => match avformat_open_input( @@ -96,14 +91,14 @@ pub fn open>(path: &P, format: &Format) -> Result } } -pub fn open_with>( - path: &P, +pub fn open_with( + path: impl AsRef, format: &Format, options: Dictionary, ) -> Result { unsafe { let mut ps = ptr::null_mut(); - let path = from_path(path); + let path = CString::new(path.as_ref()).unwrap(); let mut opts = options.disown(); match *format { @@ -144,10 +139,10 @@ pub fn open_with>( } } -pub fn input>(path: &P) -> Result { +pub fn input(path: impl AsRef) -> Result { unsafe { let mut ps = ptr::null_mut(); - let path = from_path(path); + let path = CString::new(path.as_ref()).unwrap(); match avformat_open_input(&mut ps, path.as_ptr(), ptr::null_mut(), ptr::null_mut()) { 0 => match avformat_find_stream_info(ps, ptr::null_mut()) { @@ -163,13 +158,13 @@ pub fn input>(path: &P) -> Result { } } -pub fn input_with_dictionary>( - path: &P, +pub fn input_with_dictionary( + path: impl AsRef, options: Dictionary, ) -> Result { unsafe { let mut ps = ptr::null_mut(); - let path = from_path(path); + let path = CString::new(path.as_ref()).unwrap(); let mut opts = options.disown(); let res = avformat_open_input(&mut ps, path.as_ptr(), ptr::null_mut(), &mut opts); @@ -189,16 +184,13 @@ pub fn input_with_dictionary>( } } -pub fn input_with_interrupt, F>( - path: &P, - closure: F, -) -> Result -where - F: FnMut() -> bool, -{ +pub fn input_with_interrupt( + path: impl AsRef, + closure: impl FnMut() -> bool, +) -> Result { unsafe { let mut ps = avformat_alloc_context(); - let path = from_path(path); + let path = CString::new(path.as_ref()).unwrap(); (*ps).interrupt_callback = interrupt::new(Box::new(closure)).interrupt; match avformat_open_input(&mut ps, path.as_ptr(), ptr::null_mut(), ptr::null_mut()) { @@ -215,10 +207,10 @@ where } } -pub fn output>(path: &P) -> Result { +pub fn output(path: impl AsRef) -> Result { unsafe { let mut ps = ptr::null_mut(); - let path = from_path(path); + let path = CString::new(path.as_ref()).unwrap(); match avformat_alloc_output_context2(&mut ps, ptr::null_mut(), ptr::null(), path.as_ptr()) { 0 => match avio_open(&mut (*ps).pb, path.as_ptr(), AVIO_FLAG_WRITE) { @@ -231,13 +223,10 @@ pub fn output>(path: &P) -> Result { } } -pub fn output_with>( - path: &P, - options: Dictionary, -) -> Result { +pub fn output_with(path: impl AsRef, options: Dictionary) -> Result { unsafe { let mut ps = ptr::null_mut(); - let path = from_path(path); + let path = CString::new(path.as_ref()).unwrap(); let mut opts = options.disown(); match avformat_alloc_output_context2(&mut ps, ptr::null_mut(), ptr::null(), path.as_ptr()) { @@ -263,10 +252,10 @@ pub fn output_with>( } } -pub fn output_as>(path: &P, format: &str) -> Result { +pub fn output_as(path: impl AsRef, format: &str) -> Result { unsafe { let mut ps = ptr::null_mut(); - let path = from_path(path); + let path = CString::new(path.as_ref()).unwrap(); let format = CString::new(format).unwrap(); match avformat_alloc_output_context2( @@ -285,14 +274,14 @@ pub fn output_as>(path: &P, format: &str) -> Result>( - path: &P, +pub fn output_as_with( + path: impl AsRef, format: &str, options: Dictionary, ) -> Result { unsafe { let mut ps = ptr::null_mut(); - let path = from_path(path); + let path = CString::new(path.as_ref()).unwrap(); let format = CString::new(format).unwrap(); let mut opts = options.disown(); diff --git a/src/format/network.rs b/src/format/network.rs index 066cba26..0d56f651 100644 --- a/src/format/network.rs +++ b/src/format/network.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; pub fn init() { unsafe { diff --git a/src/format/stream/disposition.rs b/src/format/stream/disposition.rs index 37c4690f..52966207 100644 --- a/src/format/stream/disposition.rs +++ b/src/format/stream/disposition.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; bitflags! { diff --git a/src/format/stream/stream.rs b/src/format/stream/stream.rs index c6a2d847..0cbda25d 100644 --- a/src/format/stream/stream.rs +++ b/src/format/stream/stream.rs @@ -1,10 +1,13 @@ use super::Disposition; -use codec::{self, packet}; -use ffi::*; -use format::context::common::Context; +use crate::{ + codec::{self, packet}, + ffi::*, + format::context::common::Context, + DictionaryRef, Discard, Rational, +}; use libc::c_int; -use {DictionaryRef, Discard, Rational}; +#[derive(Debug)] pub struct Stream<'a> { context: &'a Context, index: usize, @@ -12,14 +15,11 @@ pub struct Stream<'a> { impl<'a> Stream<'a> { pub unsafe fn wrap(context: &Context, index: usize) -> Stream { - Stream { - context: context, - index: index, - } + Stream { context, index } } pub unsafe fn as_ptr(&self) -> *const AVStream { - *(*self.context.as_ptr()).streams.offset(self.index as isize) + *(*self.context.as_ptr()).streams.add(self.index) } } @@ -32,10 +32,17 @@ impl<'a> Stream<'a> { unsafe { codec::Context::wrap((*self.as_ptr()).codec, Some(self.context.destructor())) } } + #[cfg(feature = "ffmpeg_3_1")] pub fn parameters(&self) -> codec::Parameters { + #[cfg(feature = "ffmpeg_3_1")] unsafe { codec::Parameters::wrap((*self.as_ptr()).codecpar, Some(self.context.destructor())) } + + #[cfg(not(feature = "ffmpeg_3_1"))] + unsafe { + codec::Parameters::wrap((*self.as_ptr()).codec, Some(self.context.destructor())) + } } pub fn index(&self) -> usize { @@ -98,10 +105,7 @@ pub struct SideDataIter<'a> { impl<'a> SideDataIter<'a> { pub fn new<'sd, 's: 'sd>(stream: &'s Stream) -> SideDataIter<'sd> { - SideDataIter { - stream: stream, - current: 0, - } + SideDataIter { stream, current: 0 } } } diff --git a/src/format/stream/stream_mut.rs b/src/format/stream/stream_mut.rs index 8062b61d..66103bd6 100644 --- a/src/format/stream/stream_mut.rs +++ b/src/format/stream/stream_mut.rs @@ -1,10 +1,7 @@ -use std::mem; -use std::ops::Deref; +use std::{mem, ops::Deref}; use super::Stream; -use ffi::*; -use format::context::common::Context; -use {codec, Dictionary, Rational}; +use crate::{codec, ffi::*, format::context::common::Context, Dictionary, Rational}; pub struct StreamMut<'a> { context: &'a mut Context, @@ -17,16 +14,14 @@ impl<'a> StreamMut<'a> { pub unsafe fn wrap(context: &mut Context, index: usize) -> StreamMut { StreamMut { context: mem::transmute_copy(&context), - index: index, + index, immutable: Stream::wrap(mem::transmute_copy(&context), index), } } pub unsafe fn as_mut_ptr(&mut self) -> *mut AVStream { - *(*self.context.as_mut_ptr()) - .streams - .offset(self.index as isize) + *(*self.context.as_mut_ptr()).streams.add(self.index) } } @@ -49,12 +44,19 @@ impl<'a> StreamMut<'a> { } } + #[cfg(feature = "ffmpeg_3_1")] pub fn set_parameters>(&mut self, parameters: P) { let parameters = parameters.into(); + #[cfg(feature = "ffmpeg_3_1")] unsafe { avcodec_parameters_copy((*self.as_mut_ptr()).codecpar, parameters.as_ptr()); } + + #[cfg(not(feature = "ffmpeg_3_1"))] + unsafe { + avcodec_parameters_copy((*self.as_mut_ptr()).codec, parameters.as_ptr()); + } } pub fn set_metadata(&mut self, metadata: Dictionary) { diff --git a/src/lib.rs b/src/lib.rs index 4276dcdb..dd994306 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,7 @@ #![allow(non_camel_case_types)] -#![cfg_attr(feature = "cargo-clippy", allow(inline_always))] +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::module_inception)] +#![allow(clippy::too_many_arguments)] #[macro_use] extern crate bitflags; @@ -8,55 +10,52 @@ pub extern crate ffmpeg_sys as sys; extern crate image; extern crate libc; -pub use sys as ffi; +pub use crate::sys as ffi; #[macro_use] pub mod util; -pub use util::channel_layout::{self, ChannelLayout}; -pub use util::chroma; -pub use util::color; -pub use util::dictionary; -pub use util::dictionary::Mut as DictionaryMut; -pub use util::dictionary::Owned as Dictionary; -pub use util::dictionary::Ref as DictionaryRef; -pub use util::error::Error; -pub use util::frame::{self, Frame}; -pub use util::mathematics::{self, rescale, Rescale, Rounding}; -pub use util::media; -pub use util::option; -pub use util::picture; -pub use util::rational::{self, Rational}; -pub use util::time; +pub use crate::util::{ + channel_layout::{self, ChannelLayout}, + chroma, color, dictionary, + dictionary::{Mut as DictionaryMut, Owned as Dictionary, Ref as DictionaryRef}, + error::{self, Error}, + frame::{self, Frame}, + log, + mathematics::{self, rescale, Rescale, Rounding}, + media, option, picture, + rational::{self, Rational}, + time, +}; #[cfg(feature = "format")] pub mod format; #[cfg(feature = "format")] -pub use format::chapter::{Chapter, ChapterMut}; +pub use crate::format::chapter::{Chapter, ChapterMut}; #[cfg(feature = "format")] -pub use format::format::Format; +pub use crate::format::format::Format; #[cfg(feature = "format")] -pub use format::stream::{Stream, StreamMut}; +pub use crate::format::stream::{Stream, StreamMut}; #[cfg(feature = "codec")] pub mod codec; #[cfg(feature = "codec")] -pub use codec::audio_service::AudioService; +pub use crate::codec::audio_service::AudioService; #[cfg(feature = "codec")] -pub use codec::codec::Codec; +pub use crate::codec::codec::Codec; #[cfg(feature = "codec")] -pub use codec::discard::Discard; +pub use crate::codec::discard::Discard; #[cfg(feature = "codec")] -pub use codec::field_order::FieldOrder; +pub use crate::codec::field_order::FieldOrder; #[cfg(feature = "codec")] -pub use codec::packet::{self, Packet}; +pub use crate::codec::packet::{self, Packet}; #[cfg(feature = "codec")] -pub use codec::picture::Picture; +pub use crate::codec::picture::Picture; #[cfg(feature = "codec")] -pub use codec::subtitle::{self, Subtitle}; +pub use crate::codec::subtitle::{self, Subtitle}; #[cfg(feature = "codec")] -pub use codec::threading; +pub use crate::codec::threading; #[cfg(feature = "codec")] -pub use codec::{decoder, encoder}; +pub use crate::codec::{decoder, encoder}; #[cfg(feature = "device")] pub mod device; @@ -64,7 +63,7 @@ pub mod device; #[cfg(feature = "filter")] pub mod filter; #[cfg(feature = "filter")] -pub use filter::Filter; +pub use crate::filter::Filter; pub mod software; @@ -96,6 +95,13 @@ fn init_filter() { #[cfg(not(feature = "filter"))] fn init_filter() {} +#[cfg_attr( + any(feature = "ffmpeg4", feature = "ffmpeg41", feature = "ffmpeg42"), + deprecated( + note = "features ffmpeg4/ffmpeg41/ffmpeg42/ffmpeg43 are now auto-detected \ + and will be removed in a future version" + ) +)] pub fn init() -> Result<(), Error> { init_error(); init_format(); diff --git a/src/software/mod.rs b/src/software/mod.rs index c6c08a87..69578057 100644 --- a/src/software/mod.rs +++ b/src/software/mod.rs @@ -4,11 +4,11 @@ pub mod scaling; #[cfg(feature = "software-scaling")] #[inline] pub fn scaler( - format: ::format::Pixel, + format: crate::format::Pixel, flags: scaling::Flags, (in_width, in_height): (u32, u32), (out_width, out_height): (u32, u32), -) -> Result { +) -> Result { scaling::Context::get( format, in_width, in_height, format, out_width, out_height, flags, ) @@ -18,9 +18,9 @@ pub fn scaler( #[inline] pub fn converter( (width, height): (u32, u32), - input: ::format::Pixel, - output: ::format::Pixel, -) -> Result { + input: crate::format::Pixel, + output: crate::format::Pixel, +) -> Result { scaling::Context::get( input, width, @@ -28,7 +28,7 @@ pub fn converter( output, width, height, - scaling::flag::FAST_BILINEAR, + scaling::flag::Flags::FAST_BILINEAR, ) } @@ -38,9 +38,9 @@ pub mod resampling; #[cfg(feature = "software-resampling")] #[inline] pub fn resampler( - (in_format, in_layout, in_rate): (::format::Sample, ::ChannelLayout, u32), - (out_format, out_layout, out_rate): (::format::Sample, ::ChannelLayout, u32), -) -> Result { + (in_format, in_layout, in_rate): (crate::format::Sample, crate::ChannelLayout, u32), + (out_format, out_layout, out_rate): (crate::format::Sample, crate::ChannelLayout, u32), +) -> Result { resampling::Context::get( in_format, in_layout, in_rate, out_format, out_layout, out_rate, ) diff --git a/src/software/resampling/context.rs b/src/software/resampling/context.rs index bae2bfd2..b3c94ae1 100644 --- a/src/software/resampling/context.rs +++ b/src/software/resampling/context.rs @@ -1,10 +1,8 @@ use std::ptr; use super::Delay; -use ffi::*; -use libc::{c_int, int64_t}; -use util::format; -use {frame, ChannelLayout, Error}; +use crate::{ffi::*, frame, util::format, ChannelLayout, Error}; +use libc::c_int; #[derive(Eq, PartialEq, Copy, Clone)] pub struct Definition { @@ -20,6 +18,8 @@ pub struct Context { output: Definition, } +unsafe impl Send for Context {} + impl Context { #[doc(hidden)] pub unsafe fn as_ptr(&self) -> *const SwrContext { @@ -45,22 +45,22 @@ impl Context { unsafe { let ptr = swr_alloc_set_opts( ptr::null_mut(), - dst_channel_layout.bits() as int64_t, + dst_channel_layout.bits() as i64, dst_format.into(), dst_rate as c_int, - src_channel_layout.bits() as int64_t, + src_channel_layout.bits() as i64, src_format.into(), src_rate as c_int, 0, ptr::null_mut(), ); - if ptr.is_null() { + if !ptr.is_null() { match swr_init(ptr) { e if e < 0 => Err(Error::from(e)), _ => Ok(Context { - ptr: ptr, + ptr, input: Definition { format: src_format, @@ -75,7 +75,8 @@ impl Context { }, }), } - } else { + } + else { Err(Error::InvalidData) } } @@ -103,7 +104,8 @@ impl Context { /// Run the resampler from the given input to the given output. /// - /// When there are internal frames to process it will return `Ok(Some(Delay { .. }))`. + /// When there are internal frames to process it will return `Ok(Some(Delay + /// { .. }))`. pub fn run( &mut self, input: &frame::Audio, diff --git a/src/software/resampling/delay.rs b/src/software/resampling/delay.rs index 2e27bb37..7e93ced7 100644 --- a/src/software/resampling/delay.rs +++ b/src/software/resampling/delay.rs @@ -1,5 +1,5 @@ use super::Context; -use ffi::*; +use crate::ffi::*; #[derive(PartialEq, Eq, Copy, Clone, Debug)] pub struct Delay { diff --git a/src/software/resampling/dither.rs b/src/software/resampling/dither.rs index 728d34a5..159a8d6a 100644 --- a/src/software/resampling/dither.rs +++ b/src/software/resampling/dither.rs @@ -1,5 +1,4 @@ -use ffi::SwrDitherType::*; -use ffi::*; +use crate::ffi::{SwrDitherType::*, *}; #[derive(Eq, PartialEq, Copy, Clone, Debug)] pub enum Dither { diff --git a/src/software/resampling/engine.rs b/src/software/resampling/engine.rs index 65acc923..790d39c2 100644 --- a/src/software/resampling/engine.rs +++ b/src/software/resampling/engine.rs @@ -1,5 +1,4 @@ -use ffi::*; -use sys::SwrEngine::*; +use crate::{ffi::*, sys::SwrEngine::*}; #[derive(Eq, PartialEq, Copy, Clone, Debug)] pub enum Engine { diff --git a/src/software/resampling/extensions.rs b/src/software/resampling/extensions.rs index 69b1aec8..8fa14ac2 100644 --- a/src/software/resampling/extensions.rs +++ b/src/software/resampling/extensions.rs @@ -1,6 +1,5 @@ use super::Context; -use util::format; -use {decoder, frame, ChannelLayout, Error}; +use crate::{decoder, frame, util::format, ChannelLayout, Error}; impl frame::Audio { #[inline] diff --git a/src/software/resampling/filter.rs b/src/software/resampling/filter.rs index 0b2b2680..4947cc89 100644 --- a/src/software/resampling/filter.rs +++ b/src/software/resampling/filter.rs @@ -1,5 +1,4 @@ -use ffi::SwrFilterType::*; -use ffi::*; +use crate::ffi::{SwrFilterType::*, *}; #[derive(Eq, PartialEq, Copy, Clone, Debug)] pub enum Filter { diff --git a/src/software/resampling/flag.rs b/src/software/resampling/flag.rs index 7f8f623b..51a64b73 100644 --- a/src/software/resampling/flag.rs +++ b/src/software/resampling/flag.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; bitflags! { diff --git a/src/software/resampling/mod.rs b/src/software/resampling/mod.rs index f536e4d6..717f02fd 100644 --- a/src/software/resampling/mod.rs +++ b/src/software/resampling/mod.rs @@ -18,10 +18,9 @@ pub use self::context::Context; mod extensions; -use std::ffi::CStr; -use std::str::from_utf8_unchecked; +use std::{ffi::CStr, str::from_utf8_unchecked}; -use ffi::*; +use crate::ffi::*; pub fn version() -> u32 { unsafe { swresample_version() } diff --git a/src/software/scaling/color_space.rs b/src/software/scaling/color_space.rs index bd34a108..6d37bbf8 100644 --- a/src/software/scaling/color_space.rs +++ b/src/software/scaling/color_space.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; #[derive(Eq, PartialEq, Clone, Copy, Debug)] diff --git a/src/software/scaling/context.rs b/src/software/scaling/context.rs index e0aa4d74..b4bfa97a 100644 --- a/src/software/scaling/context.rs +++ b/src/software/scaling/context.rs @@ -1,10 +1,8 @@ use std::ptr; use super::Flags; -use ffi::*; +use crate::{ffi::*, frame, util::format, Error}; use libc::c_int; -use util::format; -use {frame, Error}; #[derive(Eq, PartialEq, Copy, Clone, Debug)] pub struct Definition { @@ -56,9 +54,9 @@ impl Context { ptr::null_mut(), ); - if ptr.is_null() { + if !ptr.is_null() { Ok(Context { - ptr: ptr, + ptr, input: Definition { format: src_format, @@ -72,7 +70,8 @@ impl Context { height: dst_h, }, }) - } else { + } + else { Err(Error::InvalidData) } } @@ -128,7 +127,8 @@ impl Context { } pub fn run(&mut self, input: &frame::Video, output: &mut frame::Video) -> Result<(), Error> { - if input.format() != self.input.format || input.width() != self.input.width + if input.format() != self.input.format + || input.width() != self.input.width || input.height() != self.input.height { return Err(Error::InputChanged); @@ -140,7 +140,8 @@ impl Context { } } - if output.format() != self.output.format || output.width() != self.output.width + if output.format() != self.output.format + || output.width() != self.output.width || output.height() != self.output.height { return Err(Error::OutputChanged); @@ -152,8 +153,8 @@ impl Context { (*input.as_ptr()).data.as_ptr() as *const *const _, (*input.as_ptr()).linesize.as_ptr() as *const _, 0, - self.output.height as c_int, - (*output.as_mut_ptr()).data.as_ptr() as *const *const _, + self.input.height as c_int, + (*output.as_mut_ptr()).data.as_ptr() as *const *mut _, (*output.as_mut_ptr()).linesize.as_ptr() as *mut _, ); } diff --git a/src/software/scaling/extensions.rs b/src/software/scaling/extensions.rs index 20797137..f4c64e67 100644 --- a/src/software/scaling/extensions.rs +++ b/src/software/scaling/extensions.rs @@ -1,6 +1,5 @@ -use super::{flag, Context, Flags}; -use util::format; -use {decoder, frame, Error, Picture}; +use super::{Context, Flags}; +use crate::{decoder, frame, util::format, Error, Picture}; impl<'a> Picture<'a> { #[inline] @@ -25,7 +24,7 @@ impl<'a> Picture<'a> { format, self.width(), self.height(), - flag::FAST_BILINEAR, + Flags::FAST_BILINEAR, ) } } @@ -53,7 +52,7 @@ impl frame::Video { format, self.width(), self.height(), - flag::FAST_BILINEAR, + Flags::FAST_BILINEAR, ) } } @@ -81,7 +80,7 @@ impl decoder::Video { format, self.width(), self.height(), - flag::FAST_BILINEAR, + Flags::FAST_BILINEAR, ) } } diff --git a/src/software/scaling/filter.rs b/src/software/scaling/filter.rs index 847a71bf..2546d944 100644 --- a/src/software/scaling/filter.rs +++ b/src/software/scaling/filter.rs @@ -1,5 +1,5 @@ use super::Vector; -use ffi::*; +use crate::ffi::*; pub struct Filter { ptr: *mut SwsFilter, diff --git a/src/software/scaling/flag.rs b/src/software/scaling/flag.rs index f71958f0..14415aab 100644 --- a/src/software/scaling/flag.rs +++ b/src/software/scaling/flag.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; bitflags! { diff --git a/src/software/scaling/mod.rs b/src/software/scaling/mod.rs index bab8d538..de208922 100644 --- a/src/software/scaling/mod.rs +++ b/src/software/scaling/mod.rs @@ -17,10 +17,9 @@ pub use self::context::Context; mod extensions; -use std::ffi::CStr; -use std::str::from_utf8_unchecked; +use std::{ffi::CStr, str::from_utf8_unchecked}; -use ffi::*; +use crate::ffi::*; pub fn version() -> u32 { unsafe { swscale_version() } diff --git a/src/software/scaling/support.rs b/src/software/scaling/support.rs index a76fe72a..363842dc 100644 --- a/src/software/scaling/support.rs +++ b/src/software/scaling/support.rs @@ -1,5 +1,4 @@ -use ffi::*; -use util::format; +use crate::{ffi::*, util::format}; pub fn input(format: format::Pixel) -> bool { unsafe { sws_isSupportedInput(format.into()) != 0 } diff --git a/src/software/scaling/vector.rs b/src/software/scaling/vector.rs index b1e19ad3..7b8b1d7d 100644 --- a/src/software/scaling/vector.rs +++ b/src/software/scaling/vector.rs @@ -1,7 +1,6 @@ -use std::marker::PhantomData; -use std::slice; +use std::{marker::PhantomData, slice}; -use ffi::*; +use crate::ffi::*; use libc::{c_double, c_int}; pub struct Vector<'a> { @@ -14,7 +13,7 @@ pub struct Vector<'a> { impl<'a> Vector<'a> { pub unsafe fn wrap(ptr: *mut SwsVector) -> Self { Vector { - ptr: ptr, + ptr, _own: false, _marker: PhantomData, } diff --git a/src/util/channel_layout.rs b/src/util/channel_layout.rs index 402d0b72..9d833561 100644 --- a/src/util/channel_layout.rs +++ b/src/util/channel_layout.rs @@ -1,7 +1,9 @@ -use ffi::*; +use crate::ffi::*; use libc::c_ulonglong; bitflags! { + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub struct ChannelLayout: c_ulonglong { const FRONT_LEFT = AV_CH_FRONT_LEFT; const FRONT_RIGHT = AV_CH_FRONT_RIGHT; diff --git a/src/util/chroma/location.rs b/src/util/chroma/location.rs index 0c5e3e85..d792b8b9 100644 --- a/src/util/chroma/location.rs +++ b/src/util/chroma/location.rs @@ -1,7 +1,8 @@ -use ffi::AVChromaLocation::*; -use ffi::*; +use crate::ffi::{AVChromaLocation::*, *}; #[derive(Eq, PartialEq, Clone, Copy, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub enum Location { Unspecified, Left, diff --git a/src/util/color/primaries.rs b/src/util/color/primaries.rs index a9d2655f..6f55031c 100644 --- a/src/util/color/primaries.rs +++ b/src/util/color/primaries.rs @@ -1,7 +1,10 @@ -use ffi::AVColorPrimaries::*; -use ffi::*; +use std::{ffi::CStr, str::from_utf8_unchecked}; + +use crate::ffi::{AVColorPrimaries::*, *}; #[derive(Eq, PartialEq, Clone, Copy, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub enum Primaries { Reserved0, BT709, @@ -18,7 +21,26 @@ pub enum Primaries { SMPTE428, SMPTE431, SMPTE432, + #[cfg(not(feature = "ffmpeg_4_3"))] JEDEC_P22, + #[cfg(feature = "ffmpeg_4_3")] + EBU3213, +} + +impl Primaries { + #[cfg(feature = "ffmpeg_4_3")] + pub const JEDEC_P22: Primaries = Primaries::EBU3213; + + pub fn name(&self) -> Option<&'static str> { + if *self == Primaries::Unspecified { + return None; + } + unsafe { + let ptr = av_color_primaries_name((*self).into()); + ptr.as_ref() + .map(|ptr| from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) + } + } } impl From for Primaries { @@ -40,7 +62,12 @@ impl From for Primaries { AVCOL_PRI_SMPTE428 => Primaries::SMPTE428, AVCOL_PRI_SMPTE431 => Primaries::SMPTE431, AVCOL_PRI_SMPTE432 => Primaries::SMPTE432, + #[cfg(not(feature = "ffmpeg_4_3"))] AVCOL_PRI_JEDEC_P22 => Primaries::JEDEC_P22, + #[cfg(feature = "ffmpeg_4_3")] + AVCOL_PRI_EBU3213 => Primaries::EBU3213, + #[cfg(not(feature = "ffmpeg_4_3"))] + _ => unimplemented!(), } } } @@ -63,7 +90,10 @@ impl Into for Primaries { Primaries::SMPTE428 => AVCOL_PRI_SMPTE428, Primaries::SMPTE431 => AVCOL_PRI_SMPTE431, Primaries::SMPTE432 => AVCOL_PRI_SMPTE432, + #[cfg(not(feature = "ffmpeg_4_3"))] Primaries::JEDEC_P22 => AVCOL_PRI_JEDEC_P22, + #[cfg(feature = "ffmpeg_4_3")] + Primaries::EBU3213 => AVCOL_PRI_EBU3213, } } } diff --git a/src/util/color/range.rs b/src/util/color/range.rs index 6fc8aa99..e578b49a 100644 --- a/src/util/color/range.rs +++ b/src/util/color/range.rs @@ -1,13 +1,29 @@ -use ffi::AVColorRange::*; -use ffi::*; +use std::{ffi::CStr, str::from_utf8_unchecked}; + +use crate::ffi::{AVColorRange::*, *}; #[derive(Eq, PartialEq, Clone, Copy, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub enum Range { Unspecified, MPEG, JPEG, } +impl Range { + pub fn name(&self) -> Option<&'static str> { + if *self == Range::Unspecified { + return None; + } + unsafe { + let ptr = av_color_range_name((*self).into()); + ptr.as_ref() + .map(|ptr| from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) + } + } +} + impl From for Range { fn from(value: AVColorRange) -> Self { match value { diff --git a/src/util/color/space.rs b/src/util/color/space.rs index 33f80b35..b1dd3533 100644 --- a/src/util/color/space.rs +++ b/src/util/color/space.rs @@ -1,10 +1,10 @@ -use std::ffi::CStr; -use std::str::from_utf8_unchecked; +use std::{ffi::CStr, str::from_utf8_unchecked}; -use ffi::AVColorSpace::*; -use ffi::*; +use crate::ffi::{AVColorSpace::*, *}; #[derive(Eq, PartialEq, Clone, Copy, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub enum Space { RGB, BT709, @@ -14,7 +14,6 @@ pub enum Space { BT470BG, SMPTE170M, SMPTE240M, - YCOCG, YCGCO, BT2020NCL, BT2020CL, @@ -26,9 +25,16 @@ pub enum Space { } impl Space { - pub fn name(&self) -> &'static str { + pub const YCOCG: Space = Space::YCGCO; + + pub fn name(&self) -> Option<&'static str> { + if *self == Space::Unspecified { + return None; + } unsafe { - from_utf8_unchecked(CStr::from_ptr(av_get_colorspace_name((*self).into())).to_bytes()) + let ptr = av_color_space_name((*self).into()); + ptr.as_ref() + .map(|ptr| from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) } } } @@ -44,7 +50,7 @@ impl From for Space { AVCOL_SPC_BT470BG => Space::BT470BG, AVCOL_SPC_SMPTE170M => Space::SMPTE170M, AVCOL_SPC_SMPTE240M => Space::SMPTE240M, - AVCOL_SPC_YCOCG => Space::YCOCG, + AVCOL_SPC_YCGCO => Space::YCGCO, AVCOL_SPC_BT2020_NCL => Space::BT2020NCL, AVCOL_SPC_BT2020_CL => Space::BT2020CL, AVCOL_SPC_SMPTE2085 => Space::SMPTE2085, @@ -68,7 +74,6 @@ impl Into for Space { Space::BT470BG => AVCOL_SPC_BT470BG, Space::SMPTE170M => AVCOL_SPC_SMPTE170M, Space::SMPTE240M => AVCOL_SPC_SMPTE240M, - Space::YCOCG => AVCOL_SPC_YCOCG, Space::YCGCO => AVCOL_SPC_YCGCO, Space::BT2020NCL => AVCOL_SPC_BT2020_NCL, Space::BT2020CL => AVCOL_SPC_BT2020_CL, diff --git a/src/util/color/transfer_characteristic.rs b/src/util/color/transfer_characteristic.rs index 094a48b5..427856ff 100644 --- a/src/util/color/transfer_characteristic.rs +++ b/src/util/color/transfer_characteristic.rs @@ -1,7 +1,10 @@ -use ffi::AVColorTransferCharacteristic::*; -use ffi::*; +use std::{ffi::CStr, str::from_utf8_unchecked}; + +use crate::ffi::{AVColorTransferCharacteristic::*, *}; #[derive(Eq, PartialEq, Clone, Copy, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub enum TransferCharacteristic { Reserved0, BT709, @@ -24,6 +27,19 @@ pub enum TransferCharacteristic { ARIB_STD_B67, } +impl TransferCharacteristic { + pub fn name(&self) -> Option<&'static str> { + if *self == TransferCharacteristic::Unspecified { + return None; + } + unsafe { + let ptr = av_color_transfer_name((*self).into()); + ptr.as_ref() + .map(|ptr| from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) + } + } +} + impl From for TransferCharacteristic { fn from(value: AVColorTransferCharacteristic) -> TransferCharacteristic { match value { diff --git a/src/util/dictionary/immutable.rs b/src/util/dictionary/immutable.rs index 058547fb..cd7bf754 100644 --- a/src/util/dictionary/immutable.rs +++ b/src/util/dictionary/immutable.rs @@ -1,10 +1,13 @@ -use std::ffi::{CStr, CString}; -use std::marker::PhantomData; -use std::ptr; -use std::str::from_utf8_unchecked; +use std::{ + ffi::{CStr, CString}, + fmt, + marker::PhantomData, + ptr, + str::from_utf8_unchecked, +}; use super::{Iter, Owned}; -use ffi::*; +use crate::ffi::*; pub struct Ref<'a> { ptr: *const AVDictionary, @@ -15,7 +18,7 @@ pub struct Ref<'a> { impl<'a> Ref<'a> { pub unsafe fn wrap(ptr: *const AVDictionary) -> Self { Ref { - ptr: ptr, + ptr, _marker: PhantomData, } } @@ -33,7 +36,8 @@ impl<'a> Ref<'a> { if entry.is_null() { None - } else { + } + else { Some(from_utf8_unchecked( CStr::from_ptr((*entry).value).to_bytes(), )) @@ -51,10 +55,16 @@ impl<'a> Ref<'a> { } impl<'a> IntoIterator for &'a Ref<'a> { - type Item = (&'a str, &'a str); type IntoIter = Iter<'a>; + type Item = (&'a str, &'a str); fn into_iter(self) -> Self::IntoIter { self.iter() } } + +impl<'a> fmt::Debug for Ref<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_map().entries(self.iter()).finish() + } +} diff --git a/src/util/dictionary/iter.rs b/src/util/dictionary/iter.rs index 7466429d..9c9aa7a8 100644 --- a/src/util/dictionary/iter.rs +++ b/src/util/dictionary/iter.rs @@ -1,9 +1,11 @@ -use std::ffi::{CStr, CString}; -use std::marker::PhantomData; -use std::ptr; -use std::str::from_utf8_unchecked; +use std::{ + ffi::{CStr, CString}, + marker::PhantomData, + ptr, + str::from_utf8_unchecked, +}; -use ffi::*; +use crate::ffi::*; pub struct Iter<'a> { ptr: *const AVDictionary, @@ -38,7 +40,8 @@ impl<'a> Iterator for Iter<'a> { self.cur = entry; Some((key, val)) - } else { + } + else { None } } diff --git a/src/util/dictionary/mutable.rs b/src/util/dictionary/mutable.rs index 7e345fb1..b782c533 100644 --- a/src/util/dictionary/mutable.rs +++ b/src/util/dictionary/mutable.rs @@ -1,9 +1,7 @@ -use std::ffi::CString; -use std::marker::PhantomData; -use std::ops::Deref; +use std::{ffi::CString, fmt, marker::PhantomData, ops::Deref}; use super::immutable; -use ffi::*; +use crate::ffi::*; pub struct Ref<'a> { ptr: *mut AVDictionary, @@ -15,7 +13,7 @@ pub struct Ref<'a> { impl<'a> Ref<'a> { pub unsafe fn wrap(ptr: *mut AVDictionary) -> Self { Ref { - ptr: ptr, + ptr, imm: immutable::Ref::wrap(ptr), _marker: PhantomData, } @@ -50,3 +48,9 @@ impl<'a> Deref for Ref<'a> { &self.imm } } + +impl<'a> fmt::Debug for Ref<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.imm.fmt(fmt) + } +} diff --git a/src/util/dictionary/owned.rs b/src/util/dictionary/owned.rs index 00d06ed9..501e7fb8 100644 --- a/src/util/dictionary/owned.rs +++ b/src/util/dictionary/owned.rs @@ -1,9 +1,12 @@ -use std::iter::FromIterator; -use std::ops::{Deref, DerefMut}; -use std::ptr; +use std::{ + fmt, + iter::FromIterator, + ops::{Deref, DerefMut}, + ptr, +}; use super::mutable; -use ffi::*; +use crate::ffi::*; pub struct Owned<'a> { inner: mutable::Ref<'a>, @@ -126,3 +129,9 @@ impl<'a> Drop for Owned<'a> { } } } + +impl<'a> fmt::Debug for Owned<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.inner.fmt(fmt) + } +} diff --git a/src/util/error.rs b/src/util/error.rs index eb86a705..3ee46619 100644 --- a/src/util/error.rs +++ b/src/util/error.rs @@ -1,13 +1,26 @@ -use std::error; -use std::ffi::CStr; -use std::fmt; -use std::io; -use std::str::from_utf8_unchecked; +use std::{error, ffi::CStr, fmt, io, str::from_utf8_unchecked}; -use ffi::*; -use libc::c_int; +use crate::ffi::*; +use libc::{c_char, c_int}; -#[derive(Copy, Clone)] +// Export POSIX error codes so that users can do something like +// +// if error == (Error::Other { errno: EAGAIN }) { +// ... +// } +pub use libc::{ + E2BIG, EACCES, EADDRINUSE, EADDRNOTAVAIL, EAFNOSUPPORT, EAGAIN, EALREADY, EBADF, EBADMSG, + EBUSY, ECANCELED, ECHILD, ECONNABORTED, ECONNREFUSED, ECONNRESET, EDEADLK, EDESTADDRREQ, EDOM, + EEXIST, EFAULT, EFBIG, EHOSTUNREACH, EIDRM, EILSEQ, EINPROGRESS, EINTR, EINVAL, EIO, EISCONN, + EISDIR, ELOOP, EMFILE, EMLINK, EMSGSIZE, ENAMETOOLONG, ENETDOWN, ENETRESET, ENETUNREACH, + ENFILE, ENOBUFS, ENODATA, ENODEV, ENOENT, ENOEXEC, ENOLCK, ENOLINK, ENOMEM, ENOMSG, + ENOPROTOOPT, ENOSPC, ENOSR, ENOSTR, ENOSYS, ENOTCONN, ENOTDIR, ENOTEMPTY, ENOTRECOVERABLE, + ENOTSOCK, ENOTSUP, ENOTTY, ENXIO, EOPNOTSUPP, EOVERFLOW, EOWNERDEAD, EPERM, EPIPE, EPROTO, + EPROTONOSUPPORT, EPROTOTYPE, ERANGE, EROFS, ESPIPE, ESRCH, ETIME, ETIMEDOUT, ETXTBSY, + EWOULDBLOCK, EXDEV, +}; + +#[derive(Copy, Clone, PartialEq)] pub enum Error { Bug, Bug2, @@ -39,6 +52,11 @@ pub enum Error { HttpNotFound, HttpOther4xx, HttpServerError, + + /// For AVERROR(e) wrapping POSIX error codes, e.g. AVERROR(EAGAIN). + Other { + errno: c_int, + }, } impl From for Error { @@ -71,8 +89,9 @@ impl From for Error { AVERROR_HTTP_NOT_FOUND => Error::HttpNotFound, AVERROR_HTTP_OTHER_4XX => Error::HttpOther4xx, AVERROR_HTTP_SERVER_ERROR => Error::HttpServerError, - - _ => Error::Unknown, + e => Error::Other { + errno: AVUNERROR(e), + }, } } } @@ -107,10 +126,13 @@ impl Into for Error { Error::HttpNotFound => AVERROR_HTTP_NOT_FOUND, Error::HttpOther4xx => AVERROR_HTTP_OTHER_4XX, Error::HttpServerError => AVERROR_HTTP_SERVER_ERROR, + Error::Other { errno } => AVERROR(errno), } } } +impl error::Error for Error {} + impl From for io::Error { fn from(value: Error) -> io::Error { io::Error::new(io::ErrorKind::Other, value) @@ -119,7 +141,15 @@ impl From for io::Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - f.write_str(error::Error::description(self)) + f.write_str(unsafe { + from_utf8_unchecked( + CStr::from_ptr(match *self { + Error::Other { errno } => libc::strerror(errno), + _ => STRINGS[index(self)].as_ptr(), + }) + .to_bytes(), + ) + }) } } @@ -162,12 +192,13 @@ fn index(error: &Error) -> usize { Error::HttpNotFound => 24, Error::HttpOther4xx => 25, Error::HttpServerError => 26, + Error::Other { errno: _ } => (-1isize) as usize, } } // XXX: the length has to be synced with the number of errors -static mut STRINGS: [[i8; AV_ERROR_MAX_STRING_SIZE as usize]; 27] = - [[0i8; AV_ERROR_MAX_STRING_SIZE as usize]; 27]; +static mut STRINGS: [[c_char; AV_ERROR_MAX_STRING_SIZE]; 27] = + [[0 as c_char; AV_ERROR_MAX_STRING_SIZE]; 27]; pub fn register_all() { unsafe { @@ -312,8 +343,26 @@ pub fn register_all() { } } -impl error::Error for Error { - fn description(&self) -> &str { - unsafe { from_utf8_unchecked(CStr::from_ptr(STRINGS[index(self)].as_ptr()).to_bytes()) } +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_error_roundtrip() { + assert_eq!(Into::::into(Error::from(AVERROR_EOF)), AVERROR_EOF); + assert_eq!( + Into::::into(Error::from(AVERROR(EAGAIN))), + AVERROR(EAGAIN) + ); + assert_eq!(Error::from(AVERROR(EAGAIN)), Error::Other { errno: EAGAIN }); + } + + #[cfg(any(target_os = "linux", target_os = "macos"))] + #[test] + fn test_posix_error_string() { + assert_eq!( + Error::from(AVERROR(EAGAIN)).to_string(), + "Resource temporarily unavailable" + ) } } diff --git a/src/util/format/pixel.rs b/src/util/format/pixel.rs index c415f36a..ab8b0046 100644 --- a/src/util/format/pixel.rs +++ b/src/util/format/pixel.rs @@ -1,12 +1,15 @@ -use std::error; -use std::ffi::{CStr, CString, NulError}; -use std::fmt; -use std::str::{from_utf8_unchecked, FromStr}; +use std::{ + ffi::{CStr, CString, NulError}, + str::{from_utf8_unchecked, FromStr}, +}; -use ffi::AVPixelFormat::*; -use ffi::*; +use thiserror::Error; + +use crate::ffi::{AVPixelFormat::*, *}; #[derive(Eq, PartialEq, Copy, Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub enum Pixel { None, @@ -113,6 +116,7 @@ pub enum Pixel { YUV444P10LE, YUV422P9BE, YUV422P9LE, + #[cfg(not(feature = "ffmpeg_4_0"))] VDA_VLD, GBRP, @@ -157,6 +161,7 @@ pub enum Pixel { YVYU422, + #[cfg(not(feature = "ffmpeg_4_0"))] VDA, YA16BE, @@ -165,8 +170,10 @@ pub enum Pixel { QSV, MMAL, + #[cfg(feature = "ffmpeg_3_3")] D3D11VA_VLD, + #[cfg(feature = "ffmpeg_3_3")] CUDA, ZRGB, @@ -220,11 +227,8 @@ pub enum Pixel { VIDEOTOOLBOX, // --- defaults - #[cfg(feature = "ff_api_xvmc")] + #[cfg(not(feature = "ff_api_xvmc"))] XVMC, - Y400A, - GRAY8A, - GBR24P, RGB32, RGB32_1, @@ -295,13 +299,16 @@ pub enum Pixel { GBRAP10LE, GBRAP10BE, MEDIACODEC, + #[cfg(feature = "ffmpeg_3_3")] GRAY12BE, + #[cfg(feature = "ffmpeg_3_3")] GRAY12LE, GRAY10BE, GRAY10LE, P016LE, P016BE, + #[cfg(feature = "ffmpeg_3_3")] D3D11, GRAY9BE, GRAY9LE, @@ -310,6 +317,49 @@ pub enum Pixel { GBRAPF32BE, GBRAPF32LE, DRM_PRIME, + #[cfg(feature = "ffmpeg_4_0")] + OPENCL, + + #[cfg(feature = "ffmpeg_4_1")] + GRAY14BE, + #[cfg(feature = "ffmpeg_4_1")] + GRAY14LE, + #[cfg(feature = "ffmpeg_4_1")] + GRAYF32BE, + #[cfg(feature = "ffmpeg_4_1")] + GRAYF32LE, + + #[cfg(feature = "ffmpeg_4_2")] + YUVA422P12BE, + #[cfg(feature = "ffmpeg_4_2")] + YUVA422P12LE, + #[cfg(feature = "ffmpeg_4_2")] + YUVA444P12BE, + #[cfg(feature = "ffmpeg_4_2")] + YUVA444P12LE, + #[cfg(feature = "ffmpeg_4_2")] + NV24, + #[cfg(feature = "ffmpeg_4_2")] + NV42, + + #[cfg(feature = "ffmpeg_4_3")] + VULKAN, + #[cfg(feature = "ffmpeg_4_3")] + Y210BE, + #[cfg(feature = "ffmpeg_4_3")] + Y210LE, + #[cfg(feature = "rpi")] + RPI, + #[cfg(feature = "rpi")] + SAND128, + #[cfg(feature = "rpi")] + SAND64_10, + #[cfg(feature = "rpi")] + SAND64_16, + #[cfg(feature = "rpi")] + RPI4_8, + #[cfg(feature = "rpi")] + RPI4_10, } #[derive(Clone, Copy, PartialEq, Eq)] @@ -321,6 +371,12 @@ unsafe impl Send for Descriptor {} unsafe impl Sync for Descriptor {} impl Pixel { + pub const GBR24P: Pixel = Pixel::GBRP; + pub const GRAY8A: Pixel = Pixel::YA8; + #[cfg(feature = "ff_api_xvmc")] + pub const XVMC: Pixel = Pixel::XVMC_MPEG2_IDCT; + pub const Y400A: Pixel = Pixel::YA8; + pub fn descriptor(self) -> Option { unsafe { let ptr = av_pix_fmt_desc_get(self.into()); @@ -373,6 +429,8 @@ impl From for Pixel { AV_PIX_FMT_YUVJ420P => Pixel::YUVJ420P, AV_PIX_FMT_YUVJ422P => Pixel::YUVJ422P, AV_PIX_FMT_YUVJ444P => Pixel::YUVJ444P, + #[cfg(not(feature = "ff_api_xvmc"))] + AV_PIX_FMT_XVMC => Pixel::XVMC, #[cfg(feature = "ff_api_xvmc")] AV_PIX_FMT_XVMC_MPEG2_MC => Pixel::XVMC_MPEG2_MC, #[cfg(feature = "ff_api_xvmc")] @@ -459,6 +517,7 @@ impl From for Pixel { AV_PIX_FMT_YUV444P10LE => Pixel::YUV444P10LE, AV_PIX_FMT_YUV422P9BE => Pixel::YUV422P9BE, AV_PIX_FMT_YUV422P9LE => Pixel::YUV422P9LE, + #[cfg(not(feature = "ffmpeg_4_0"))] AV_PIX_FMT_VDA_VLD => Pixel::VDA_VLD, AV_PIX_FMT_GBRP => Pixel::GBRP, @@ -503,6 +562,7 @@ impl From for Pixel { AV_PIX_FMT_YVYU422 => Pixel::YVYU422, + #[cfg(not(feature = "ffmpeg_4_0"))] AV_PIX_FMT_VDA => Pixel::VDA, AV_PIX_FMT_YA16BE => Pixel::YA16BE, @@ -511,8 +571,10 @@ impl From for Pixel { AV_PIX_FMT_QSV => Pixel::QSV, AV_PIX_FMT_MMAL => Pixel::MMAL, + #[cfg(feature = "ffmpeg_3_3")] AV_PIX_FMT_D3D11VA_VLD => Pixel::D3D11VA_VLD, + #[cfg(feature = "ffmpeg_3_3")] AV_PIX_FMT_CUDA => Pixel::CUDA, AV_PIX_FMT_0RGB => Pixel::ZRGB, @@ -572,7 +634,9 @@ impl From for Pixel { AV_PIX_FMT_GBRAP10LE => Pixel::GBRAP10LE, AV_PIX_FMT_GBRAP10BE => Pixel::GBRAP10BE, AV_PIX_FMT_MEDIACODEC => Pixel::MEDIACODEC, + #[cfg(feature = "ffmpeg_3_3")] AV_PIX_FMT_GRAY12BE => Pixel::GRAY12BE, + #[cfg(feature = "ffmpeg_3_3")] AV_PIX_FMT_GRAY12LE => Pixel::GRAY12LE, AV_PIX_FMT_GRAY10BE => Pixel::GRAY10BE, AV_PIX_FMT_GRAY10LE => Pixel::GRAY10LE, @@ -581,6 +645,7 @@ impl From for Pixel { AV_PIX_FMT_NB => Pixel::None, + #[cfg(feature = "ffmpeg_3_3")] AV_PIX_FMT_D3D11 => Pixel::D3D11, AV_PIX_FMT_GRAY9BE => Pixel::GRAY9BE, AV_PIX_FMT_GRAY9LE => Pixel::GRAY9LE, @@ -589,6 +654,50 @@ impl From for Pixel { AV_PIX_FMT_GBRAPF32BE => Pixel::GBRAPF32BE, AV_PIX_FMT_GBRAPF32LE => Pixel::GBRAPF32LE, AV_PIX_FMT_DRM_PRIME => Pixel::DRM_PRIME, + #[cfg(feature = "ffmpeg_4_0")] + AV_PIX_FMT_OPENCL => Pixel::OPENCL, + + #[cfg(feature = "ffmpeg_4_1")] + AV_PIX_FMT_GRAY14BE => Pixel::GRAY14BE, + #[cfg(feature = "ffmpeg_4_1")] + AV_PIX_FMT_GRAY14LE => Pixel::GRAY14LE, + #[cfg(feature = "ffmpeg_4_1")] + AV_PIX_FMT_GRAYF32BE => Pixel::GRAYF32BE, + #[cfg(feature = "ffmpeg_4_1")] + AV_PIX_FMT_GRAYF32LE => Pixel::GRAYF32LE, + + #[cfg(feature = "ffmpeg_4_2")] + AV_PIX_FMT_YUVA422P12BE => Pixel::YUVA422P12BE, + #[cfg(feature = "ffmpeg_4_2")] + AV_PIX_FMT_YUVA422P12LE => Pixel::YUVA422P12LE, + #[cfg(feature = "ffmpeg_4_2")] + AV_PIX_FMT_YUVA444P12BE => Pixel::YUVA444P12BE, + #[cfg(feature = "ffmpeg_4_2")] + AV_PIX_FMT_YUVA444P12LE => Pixel::YUVA444P12LE, + #[cfg(feature = "ffmpeg_4_2")] + AV_PIX_FMT_NV24 => Pixel::NV24, + #[cfg(feature = "ffmpeg_4_2")] + AV_PIX_FMT_NV42 => Pixel::NV42, + + #[cfg(feature = "ffmpeg_4_3")] + AV_PIX_FMT_VULKAN => Pixel::VULKAN, + #[cfg(feature = "ffmpeg_4_3")] + AV_PIX_FMT_Y210BE => Pixel::Y210BE, + #[cfg(feature = "ffmpeg_4_3")] + AV_PIX_FMT_Y210LE => Pixel::Y210LE, + + #[cfg(feature = "rpi")] + AV_PIX_FMT_RPI => Pixel::RPI, + #[cfg(feature = "rpi")] + AV_PIX_FMT_SAND128 => Pixel::SAND128, + #[cfg(feature = "rpi")] + AV_PIX_FMT_SAND64_10 => Pixel::SAND64_10, + #[cfg(feature = "rpi")] + AV_PIX_FMT_SAND64_16 => Pixel::SAND64_16, + #[cfg(feature = "rpi")] + AV_PIX_FMT_RPI4_8 => Pixel::RPI4_8, + #[cfg(feature = "rpi")] + AV_PIX_FMT_RPI4_10 => Pixel::RPI4_10, } } } @@ -702,6 +811,7 @@ impl Into for Pixel { Pixel::YUV444P10LE => AV_PIX_FMT_YUV444P10LE, Pixel::YUV422P9BE => AV_PIX_FMT_YUV422P9BE, Pixel::YUV422P9LE => AV_PIX_FMT_YUV422P9LE, + #[cfg(not(feature = "ffmpeg_4_0"))] Pixel::VDA_VLD => AV_PIX_FMT_VDA_VLD, Pixel::GBRP => AV_PIX_FMT_GBRP, @@ -746,6 +856,7 @@ impl Into for Pixel { Pixel::YVYU422 => AV_PIX_FMT_YVYU422, + #[cfg(not(feature = "ffmpeg_4_0"))] Pixel::VDA => AV_PIX_FMT_VDA, Pixel::YA16BE => AV_PIX_FMT_YA16BE, @@ -754,8 +865,10 @@ impl Into for Pixel { Pixel::QSV => AV_PIX_FMT_QSV, Pixel::MMAL => AV_PIX_FMT_MMAL, + #[cfg(feature = "ffmpeg_3_3")] Pixel::D3D11VA_VLD => AV_PIX_FMT_D3D11VA_VLD, + #[cfg(feature = "ffmpeg_3_3")] Pixel::CUDA => AV_PIX_FMT_CUDA, Pixel::ZRGB => AV_PIX_FMT_0RGB, @@ -809,10 +922,8 @@ impl Into for Pixel { Pixel::VIDEOTOOLBOX => AV_PIX_FMT_VIDEOTOOLBOX, // --- defaults + #[cfg(not(feature = "ff_api_xvmc"))] Pixel::XVMC => AV_PIX_FMT_XVMC, - Pixel::Y400A => AV_PIX_FMT_Y400A, - Pixel::GRAY8A => AV_PIX_FMT_GRAY8A, - Pixel::GBR24P => AV_PIX_FMT_GBR24P, Pixel::RGB32 => AV_PIX_FMT_RGB32, Pixel::RGB32_1 => AV_PIX_FMT_RGB32_1, @@ -883,13 +994,16 @@ impl Into for Pixel { Pixel::GBRAP10LE => AV_PIX_FMT_GBRAP10LE, Pixel::GBRAP10BE => AV_PIX_FMT_GBRAP10BE, Pixel::MEDIACODEC => AV_PIX_FMT_MEDIACODEC, + #[cfg(feature = "ffmpeg_3_3")] Pixel::GRAY12BE => AV_PIX_FMT_GRAY12BE, + #[cfg(feature = "ffmpeg_3_3")] Pixel::GRAY12LE => AV_PIX_FMT_GRAY12LE, Pixel::GRAY10BE => AV_PIX_FMT_GRAY10BE, Pixel::GRAY10LE => AV_PIX_FMT_GRAY10LE, Pixel::P016LE => AV_PIX_FMT_P016LE, Pixel::P016BE => AV_PIX_FMT_P016BE, + #[cfg(feature = "ffmpeg_3_3")] Pixel::D3D11 => AV_PIX_FMT_D3D11, Pixel::GRAY9BE => AV_PIX_FMT_GRAY9BE, Pixel::GRAY9LE => AV_PIX_FMT_GRAY9LE, @@ -898,45 +1012,61 @@ impl Into for Pixel { Pixel::GBRAPF32BE => AV_PIX_FMT_GBRAPF32BE, Pixel::GBRAPF32LE => AV_PIX_FMT_GBRAPF32LE, Pixel::DRM_PRIME => AV_PIX_FMT_DRM_PRIME, + #[cfg(feature = "ffmpeg_4_0")] + Pixel::OPENCL => AV_PIX_FMT_OPENCL, + + #[cfg(feature = "ffmpeg_4_1")] + Pixel::GRAY14BE => AV_PIX_FMT_GRAY14BE, + #[cfg(feature = "ffmpeg_4_1")] + Pixel::GRAY14LE => AV_PIX_FMT_GRAY14LE, + #[cfg(feature = "ffmpeg_4_1")] + Pixel::GRAYF32BE => AV_PIX_FMT_GRAYF32BE, + #[cfg(feature = "ffmpeg_4_1")] + Pixel::GRAYF32LE => AV_PIX_FMT_GRAYF32LE, + + #[cfg(feature = "ffmpeg_4_2")] + Pixel::YUVA422P12BE => AV_PIX_FMT_YUVA422P12BE, + #[cfg(feature = "ffmpeg_4_2")] + Pixel::YUVA422P12LE => AV_PIX_FMT_YUVA422P12LE, + #[cfg(feature = "ffmpeg_4_2")] + Pixel::YUVA444P12BE => AV_PIX_FMT_YUVA444P12BE, + #[cfg(feature = "ffmpeg_4_2")] + Pixel::YUVA444P12LE => AV_PIX_FMT_YUVA444P12LE, + #[cfg(feature = "ffmpeg_4_2")] + Pixel::NV24 => AV_PIX_FMT_NV24, + #[cfg(feature = "ffmpeg_4_2")] + Pixel::NV42 => AV_PIX_FMT_NV42, + + #[cfg(feature = "ffmpeg_4_3")] + Pixel::VULKAN => AV_PIX_FMT_VULKAN, + #[cfg(feature = "ffmpeg_4_3")] + Pixel::Y210BE => AV_PIX_FMT_Y210BE, + #[cfg(feature = "ffmpeg_4_3")] + Pixel::Y210LE => AV_PIX_FMT_Y210LE, + + #[cfg(feature = "rpi")] + Pixel::RPI => AV_PIX_FMT_RPI, + #[cfg(feature = "rpi")] + Pixel::SAND128 => AV_PIX_FMT_SAND128, + #[cfg(feature = "rpi")] + Pixel::SAND64_10 => AV_PIX_FMT_SAND64_10, + #[cfg(feature = "rpi")] + Pixel::SAND64_16 => AV_PIX_FMT_SAND64_16, + #[cfg(feature = "rpi")] + Pixel::RPI4_8 => AV_PIX_FMT_RPI4_8, + #[cfg(feature = "rpi")] + Pixel::RPI4_10 => AV_PIX_FMT_RPI4_10, } } } -#[derive(Debug)] +#[derive(Error, Debug)] pub enum ParsePixelError { - NulError(NulError), - UnknownFormat, -} - -impl fmt::Display for ParsePixelError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ParsePixelError::NulError(ref e) => e.fmt(f), - ParsePixelError::UnknownFormat => write!(f, "unknown pixel format"), - } - } -} + #[error("NULL error")] + NulError(#[from] NulError), -impl error::Error for ParsePixelError { - fn description(&self) -> &str { - match *self { - ParsePixelError::NulError(ref e) => e.description(), - ParsePixelError::UnknownFormat => "unknown pixel format", - } - } - - fn cause(&self) -> Option<&error::Error> { - match *self { - ParsePixelError::NulError(ref e) => Some(e), - ParsePixelError::UnknownFormat => None, - } - } -} - -impl From for ParsePixelError { - fn from(x: NulError) -> ParsePixelError { - ParsePixelError::NulError(x) - } + #[error("unknown format")] + UnknownFormat, } impl FromStr for Pixel { @@ -949,7 +1079,8 @@ impl FromStr for Pixel { if format == Pixel::None { Err(ParsePixelError::UnknownFormat) - } else { + } + else { Ok(format) } } diff --git a/src/util/format/sample.rs b/src/util/format/sample.rs index c16bae55..e432a240 100644 --- a/src/util/format/sample.rs +++ b/src/util/format/sample.rs @@ -1,15 +1,16 @@ -use std::ffi::{CStr, CString}; -use std::mem; -use std::ops::Index; -use std::ptr; -use std::slice; -use std::str::from_utf8_unchecked; +use std::{ + ffi::{CStr, CString}, + ops::Index, + ptr, slice, + str::from_utf8_unchecked, +}; -use ffi::AVSampleFormat::*; -use ffi::*; -use libc::{c_int, uint8_t}; +use crate::ffi::{AVSampleFormat::*, *}; +use libc::{c_int, c_void}; #[derive(Eq, PartialEq, Copy, Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub enum Sample { None, @@ -21,6 +22,8 @@ pub enum Sample { F64(Type), } +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] #[derive(Eq, PartialEq, Copy, Clone, Debug)] pub enum Type { Packed, @@ -131,7 +134,7 @@ pub struct Buffer { pub samples: usize, pub align: bool, - buffer: *mut *mut uint8_t, + buffer: *mut *mut u8, size: c_int, } @@ -153,10 +156,10 @@ impl Buffer { pub fn new(format: Sample, channels: u16, samples: usize, align: bool) -> Self { unsafe { let mut buf = Buffer { - format: format, - channels: channels, - samples: samples, - align: align, + format, + channels, + samples, + align, buffer: ptr::null_mut(), size: 0, @@ -185,7 +188,7 @@ impl Index for Buffer { panic!("out of bounds"); } - unsafe { slice::from_raw_parts(*self.buffer.offset(index as isize), self.size as usize) } + unsafe { slice::from_raw_parts(*self.buffer.add(index), self.size as usize) } } } @@ -203,7 +206,7 @@ impl Clone for Buffer { unsafe { av_samples_copy( self.buffer, - mem::transmute(source.buffer), + source.buffer as *const *mut u8, 0, 0, source.samples as c_int, @@ -218,7 +221,7 @@ impl Drop for Buffer { #[inline] fn drop(&mut self) { unsafe { - av_freep(mem::transmute(self.buffer)); + av_freep(self.buffer as *mut c_void); } } } diff --git a/src/util/frame/audio.rs b/src/util/frame/audio.rs index 92a335f9..cab3d076 100644 --- a/src/util/frame/audio.rs +++ b/src/util/frame/audio.rs @@ -1,12 +1,12 @@ -use std::mem; -use std::ops::{Deref, DerefMut}; -use std::slice; +use std::{ + mem, + ops::{Deref, DerefMut}, + slice, +}; use super::Frame; -use ffi::*; -use libc::{c_int, c_ulonglong, int64_t}; -use util::format; -use ChannelLayout; +use crate::{ffi::*, util::format, ChannelLayout}; +use libc::{c_int, c_ulonglong}; #[derive(PartialEq, Eq)] pub struct Audio(Frame); @@ -48,7 +48,8 @@ impl Audio { unsafe { if (*self.as_ptr()).format == -1 { format::Sample::None - } else { + } + else { format::Sample::from(mem::transmute::<_, AVSampleFormat>((*self.as_ptr()).format)) } } @@ -64,14 +65,16 @@ impl Audio { #[inline] pub fn channel_layout(&self) -> ChannelLayout { unsafe { - ChannelLayout::from_bits_truncate(av_frame_get_channel_layout(self.as_ptr()) as c_ulonglong) + ChannelLayout::from_bits_truncate( + av_frame_get_channel_layout(self.as_ptr()) as c_ulonglong + ) } } #[inline] pub fn set_channel_layout(&mut self, value: ChannelLayout) { unsafe { - av_frame_set_channel_layout(self.as_mut_ptr(), value.bits() as int64_t); + av_frame_set_channel_layout(self.as_mut_ptr(), value.bits() as i64); } } @@ -131,7 +134,8 @@ impl Audio { if self.is_packed() { 1 - } else { + } + else { self.channels() as usize } } @@ -146,9 +150,7 @@ impl Audio { panic!("unsupported type"); } - unsafe { - slice::from_raw_parts(mem::transmute((*self.as_ptr()).data[index]), self.samples()) - } + unsafe { slice::from_raw_parts((*self.as_ptr()).data[index] as *const T, self.samples()) } } #[inline] @@ -162,10 +164,7 @@ impl Audio { } unsafe { - slice::from_raw_parts_mut( - mem::transmute((*self.as_mut_ptr()).data[index]), - self.samples(), - ) + slice::from_raw_parts_mut((*self.as_mut_ptr()).data[index] as *mut T, self.samples()) } } @@ -252,11 +251,7 @@ pub unsafe trait Sample { unsafe impl Sample for u8 { #[inline(always)] fn is_valid(format: format::Sample, _channels: u16) -> bool { - if let format::Sample::U8(..) = format { - true - } else { - false - } + matches!(format, format::Sample::U8(..)) } } @@ -305,11 +300,7 @@ unsafe impl Sample for (u8, u8, u8, u8, u8, u8, u8) { unsafe impl Sample for i16 { #[inline(always)] fn is_valid(format: format::Sample, _channels: u16) -> bool { - if let format::Sample::I16(..) = format { - true - } else { - false - } + matches!(format, format::Sample::I16(..)) } } @@ -358,11 +349,7 @@ unsafe impl Sample for (i16, i16, i16, i16, i16, i16, i16) { unsafe impl Sample for i32 { #[inline(always)] fn is_valid(format: format::Sample, _channels: u16) -> bool { - if let format::Sample::I32(..) = format { - true - } else { - false - } + matches!(format, format::Sample::I32(..)) } } @@ -411,11 +398,7 @@ unsafe impl Sample for (i32, i32, i32, i32, i32, i32, i32) { unsafe impl Sample for f32 { #[inline(always)] fn is_valid(format: format::Sample, _channels: u16) -> bool { - if let format::Sample::F32(..) = format { - true - } else { - false - } + matches!(format, format::Sample::F32(..)) } } @@ -464,11 +447,7 @@ unsafe impl Sample for (f32, f32, f32, f32, f32, f32, f32) { unsafe impl Sample for f64 { #[inline(always)] fn is_valid(format: format::Sample, _channels: u16) -> bool { - if let format::Sample::F64(..) = format { - true - } else { - false - } + matches!(format, format::Sample::F64(..)) } } diff --git a/src/util/frame/flag.rs b/src/util/frame/flag.rs index b14510fb..2b2c3a7c 100644 --- a/src/util/frame/flag.rs +++ b/src/util/frame/flag.rs @@ -1,4 +1,4 @@ -use ffi::*; +use crate::ffi::*; use libc::c_int; bitflags! { diff --git a/src/util/frame/mod.rs b/src/util/frame/mod.rs index 4c3eaca0..abbff4cc 100644 --- a/src/util/frame/mod.rs +++ b/src/util/frame/mod.rs @@ -10,9 +10,8 @@ pub use self::audio::Audio; pub mod flag; pub use self::flag::Flags; -use ffi::*; +use crate::{ffi::*, Dictionary, DictionaryRef}; use libc::c_int; -use {Dictionary, DictionaryRef}; #[derive(PartialEq, Eq, Copy, Clone, Debug)] pub struct Packet { @@ -37,10 +36,7 @@ unsafe impl Sync for Frame {} impl Frame { #[inline(always)] pub unsafe fn wrap(ptr: *mut AVFrame) -> Self { - Frame { - ptr: ptr, - _own: false, - } + Frame { ptr, _own: false } } #[inline(always)] @@ -75,11 +71,24 @@ impl Frame { #[inline] pub fn is_corrupt(&self) -> bool { - self.flags().contains(flag::CORRUPT) + self.flags().contains(Flags::CORRUPT) } #[inline] pub fn packet(&self) -> Packet { + #[cfg(feature = "ffmpeg_3_2")] + unsafe { + Packet { + duration: av_frame_get_pkt_duration(self.as_ptr()) as i64, + position: av_frame_get_pkt_pos(self.as_ptr()) as i64, + size: av_frame_get_pkt_size(self.as_ptr()) as usize, + + pts: (*self.as_ptr()).pts, + dts: (*self.as_ptr()).pkt_dts, + } + } + + #[cfg(not(feature = "ffmpeg_3_2"))] unsafe { Packet { duration: av_frame_get_pkt_duration(self.as_ptr()) as i64, @@ -148,7 +157,8 @@ impl Frame { if ptr.is_null() { None - } else { + } + else { Some(SideData::wrap(ptr)) } } @@ -161,7 +171,8 @@ impl Frame { if ptr.is_null() { None - } else { + } + else { Some(SideData::wrap(ptr)) } } diff --git a/src/util/frame/side_data.rs b/src/util/frame/side_data.rs index acc5024e..20f7da33 100644 --- a/src/util/frame/side_data.rs +++ b/src/util/frame/side_data.rs @@ -1,12 +1,10 @@ -use std::ffi::CStr; -use std::marker::PhantomData; -use std::slice; -use std::str::from_utf8_unchecked; +use std::{ffi::CStr, marker::PhantomData, slice, str::from_utf8_unchecked}; use super::Frame; -use ffi::AVFrameSideDataType::*; -use ffi::*; -use DictionaryRef; +use crate::{ + ffi::{AVFrameSideDataType::*, *}, + DictionaryRef, +}; #[derive(Eq, PartialEq, Copy, Clone, Debug)] pub enum Type { @@ -23,10 +21,29 @@ pub enum Type { AudioServiceType, MasteringDisplayMetadata, GOPTimecode, + #[cfg(feature = "ffmpeg_3_3")] Spherical, + #[cfg(feature = "ffmpeg_3_3")] ContentLightLevel, + #[cfg(feature = "ffmpeg_3_4")] IccProfile, + + #[cfg(feature = "ffmpeg_4_0")] + QPTableProperties, + #[cfg(feature = "ffmpeg_4_0")] + QPTableData, + + #[cfg(feature = "ffmpeg_4_1")] + S12M_TIMECODE, + + #[cfg(feature = "ffmpeg_4_2")] + DYNAMIC_HDR_PLUS, + #[cfg(feature = "ffmpeg_4_2")] + REGIONS_OF_INTEREST, + + #[cfg(feature = "ffmpeg_4_3")] + VIDEO_ENC_PARAMS, } impl Type { @@ -55,10 +72,29 @@ impl From for Type { AV_FRAME_DATA_AUDIO_SERVICE_TYPE => Type::AudioServiceType, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA => Type::MasteringDisplayMetadata, AV_FRAME_DATA_GOP_TIMECODE => Type::GOPTimecode, + #[cfg(feature = "ffmpeg_3_3")] AV_FRAME_DATA_SPHERICAL => Type::Spherical, + #[cfg(feature = "ffmpeg_3_3")] AV_FRAME_DATA_CONTENT_LIGHT_LEVEL => Type::ContentLightLevel, + #[cfg(feature = "ffmpeg_3_4")] AV_FRAME_DATA_ICC_PROFILE => Type::IccProfile, + + #[cfg(feature = "ffmpeg_4_0")] + AV_FRAME_DATA_QP_TABLE_PROPERTIES => Type::QPTableProperties, + #[cfg(feature = "ffmpeg_4_0")] + AV_FRAME_DATA_QP_TABLE_DATA => Type::QPTableData, + + #[cfg(feature = "ffmpeg_4_1")] + AV_FRAME_DATA_S12M_TIMECODE => Type::S12M_TIMECODE, + + #[cfg(feature = "ffmpeg_4_2")] + AV_FRAME_DATA_DYNAMIC_HDR_PLUS => Type::DYNAMIC_HDR_PLUS, + #[cfg(feature = "ffmpeg_4_2")] + AV_FRAME_DATA_REGIONS_OF_INTEREST => Type::REGIONS_OF_INTEREST, + + #[cfg(feature = "ffmpeg_4_3")] + AV_FRAME_DATA_VIDEO_ENC_PARAMS => Type::VIDEO_ENC_PARAMS, } } } @@ -80,10 +116,29 @@ impl Into for Type { Type::AudioServiceType => AV_FRAME_DATA_AUDIO_SERVICE_TYPE, Type::MasteringDisplayMetadata => AV_FRAME_DATA_MASTERING_DISPLAY_METADATA, Type::GOPTimecode => AV_FRAME_DATA_GOP_TIMECODE, + #[cfg(feature = "ffmpeg_3_3")] Type::Spherical => AV_FRAME_DATA_SPHERICAL, + #[cfg(feature = "ffmpeg_3_3")] Type::ContentLightLevel => AV_FRAME_DATA_CONTENT_LIGHT_LEVEL, + #[cfg(feature = "ffmpeg_3_4")] Type::IccProfile => AV_FRAME_DATA_ICC_PROFILE, + + #[cfg(feature = "ffmpeg_4_0")] + Type::QPTableProperties => AV_FRAME_DATA_QP_TABLE_PROPERTIES, + #[cfg(feature = "ffmpeg_4_0")] + Type::QPTableData => AV_FRAME_DATA_QP_TABLE_DATA, + + #[cfg(feature = "ffmpeg_4_1")] + Type::S12M_TIMECODE => AV_FRAME_DATA_S12M_TIMECODE, + + #[cfg(feature = "ffmpeg_4_2")] + Type::DYNAMIC_HDR_PLUS => AV_FRAME_DATA_DYNAMIC_HDR_PLUS, + #[cfg(feature = "ffmpeg_4_2")] + Type::REGIONS_OF_INTEREST => AV_FRAME_DATA_REGIONS_OF_INTEREST, + + #[cfg(feature = "ffmpeg_4_3")] + Type::VIDEO_ENC_PARAMS => AV_FRAME_DATA_VIDEO_ENC_PARAMS, } } } @@ -98,7 +153,7 @@ impl<'a> SideData<'a> { #[inline(always)] pub unsafe fn wrap(ptr: *mut AVFrameSideData) -> Self { SideData { - ptr: ptr, + ptr, _marker: PhantomData, } } diff --git a/src/util/frame/video.rs b/src/util/frame/video.rs index d62b41c8..327a0476 100644 --- a/src/util/frame/video.rs +++ b/src/util/frame/video.rs @@ -1,15 +1,18 @@ -use std::mem; -use std::ops::{Deref, DerefMut}; -use std::slice; +use std::{ + mem, + ops::{Deref, DerefMut}, + slice, +}; use super::Frame; -use color; -use ffi::*; +use crate::{ + color, + ffi::*, + picture, + util::{chroma, format}, + Rational, +}; use libc::c_int; -use picture; -use util::chroma; -use util::format; -use Rational; #[derive(PartialEq, Eq)] pub struct Video(Frame); @@ -51,7 +54,8 @@ impl Video { unsafe { if (*self.as_ptr()).format == -1 { format::Pixel::None - } else { + } + else { format::Pixel::from(mem::transmute::<_, AVPixelFormat>((*self.as_ptr()).format)) } } @@ -224,7 +228,8 @@ impl Video { if let Some(desc) = self.format().descriptor() { let s = desc.log2_chroma_w(); (self.width() + (1 << s) - 1) >> s - } else { + } + else { self.width() } } @@ -243,7 +248,8 @@ impl Video { if let Some(desc) = self.format().descriptor() { let s = desc.log2_chroma_h(); (self.height() + (1 << s) - 1) >> s - } else { + } + else { self.height() } } @@ -260,7 +266,7 @@ impl Video { unsafe { slice::from_raw_parts( - mem::transmute((*self.as_ptr()).data[index]), + (*self.as_ptr()).data[index] as *const T, self.stride(index) * self.plane_height(index) as usize / mem::size_of::(), ) } @@ -278,7 +284,7 @@ impl Video { unsafe { slice::from_raw_parts_mut( - mem::transmute((*self.as_mut_ptr()).data[index]), + (*self.as_mut_ptr()).data[index] as *mut T, self.stride(index) * self.plane_height(index) as usize / mem::size_of::(), ) } @@ -399,19 +405,27 @@ unsafe impl Component for (u8, u8, u8) { unsafe impl Component for [u8; 4] { #[inline(always)] fn is_valid(format: format::Pixel) -> bool { - format == format::Pixel::RGBA || format == format::Pixel::BGRA - || format == format::Pixel::ARGB || format == format::Pixel::ABGR - || format == format::Pixel::RGBZ || format == format::Pixel::BGRZ - || format == format::Pixel::ZRGB || format == format::Pixel::ZBGR + format == format::Pixel::RGBA + || format == format::Pixel::BGRA + || format == format::Pixel::ARGB + || format == format::Pixel::ABGR + || format == format::Pixel::RGBZ + || format == format::Pixel::BGRZ + || format == format::Pixel::ZRGB + || format == format::Pixel::ZBGR } } unsafe impl Component for (u8, u8, u8, u8) { #[inline(always)] fn is_valid(format: format::Pixel) -> bool { - format == format::Pixel::RGBA || format == format::Pixel::BGRA - || format == format::Pixel::ARGB || format == format::Pixel::ABGR - || format == format::Pixel::RGBZ || format == format::Pixel::BGRZ - || format == format::Pixel::ZRGB || format == format::Pixel::ZBGR + format == format::Pixel::RGBA + || format == format::Pixel::BGRA + || format == format::Pixel::ARGB + || format == format::Pixel::ABGR + || format == format::Pixel::RGBZ + || format == format::Pixel::BGRZ + || format == format::Pixel::ZRGB + || format == format::Pixel::ZBGR } } diff --git a/src/util/interrupt.rs b/src/util/interrupt.rs index 219b9ff7..d6e9eee6 100644 --- a/src/util/interrupt.rs +++ b/src/util/interrupt.rs @@ -1,7 +1,6 @@ -use std::panic; -use std::process; +use std::{panic, process}; -use ffi::*; +use crate::ffi::*; use libc::{c_int, c_void}; pub struct Interrupt { diff --git a/src/util/log/flag.rs b/src/util/log/flag.rs new file mode 100644 index 00000000..b88da197 --- /dev/null +++ b/src/util/log/flag.rs @@ -0,0 +1,9 @@ +use crate::ffi::*; +use libc::c_int; + +bitflags! { + pub struct Flags: c_int { + const SKIP_REPEATED = AV_LOG_SKIP_REPEATED; + const PRINT_LEVEL = AV_LOG_PRINT_LEVEL; + } +} diff --git a/src/util/log/level.rs b/src/util/log/level.rs new file mode 100644 index 00000000..633652a1 --- /dev/null +++ b/src/util/log/level.rs @@ -0,0 +1,54 @@ +use std::convert::TryFrom; + +use crate::ffi::*; +use libc::c_int; + +#[derive(Eq, PartialEq, Clone, Copy, Debug)] +pub enum Level { + Quiet, + Panic, + Fatal, + Error, + Warning, + Info, + Verbose, + Debug, + Trace, +} + +pub struct LevelError; + +impl TryFrom for Level { + type Error = &'static str; + + fn try_from(value: c_int) -> Result { + match value { + AV_LOG_QUIET => Ok(Level::Quiet), + AV_LOG_PANIC => Ok(Level::Panic), + AV_LOG_FATAL => Ok(Level::Fatal), + AV_LOG_ERROR => Ok(Level::Error), + AV_LOG_WARNING => Ok(Level::Warning), + AV_LOG_INFO => Ok(Level::Info), + AV_LOG_VERBOSE => Ok(Level::Verbose), + AV_LOG_DEBUG => Ok(Level::Debug), + AV_LOG_TRACE => Ok(Level::Trace), + _ => Err("illegal log level"), + } + } +} + +impl Into for Level { + fn into(self) -> c_int { + match self { + Level::Quiet => AV_LOG_QUIET, + Level::Panic => AV_LOG_PANIC, + Level::Fatal => AV_LOG_FATAL, + Level::Error => AV_LOG_ERROR, + Level::Warning => AV_LOG_WARNING, + Level::Info => AV_LOG_INFO, + Level::Verbose => AV_LOG_VERBOSE, + Level::Debug => AV_LOG_DEBUG, + Level::Trace => AV_LOG_TRACE, + } + } +} diff --git a/src/util/log/mod.rs b/src/util/log/mod.rs new file mode 100644 index 00000000..3c271f65 --- /dev/null +++ b/src/util/log/mod.rs @@ -0,0 +1,24 @@ +pub mod level; +pub use self::level::Level; + +pub mod flag; +pub use self::flag::Flags; + +use crate::ffi::*; +use std::convert::TryInto; + +pub fn set_level(value: Level) { + unsafe { av_log_set_level(value.into()) } +} + +pub fn get_level() -> Result { + unsafe { av_log_get_level().try_into() } +} + +pub fn set_flags(value: Flags) { + unsafe { av_log_set_flags(value.bits()) } +} + +pub fn get_flags() -> Flags { + unsafe { Flags::from_bits_truncate(av_log_get_flags()) } +} diff --git a/src/util/mathematics/rescale.rs b/src/util/mathematics/rescale.rs index 5c4a8be5..6ea52f63 100644 --- a/src/util/mathematics/rescale.rs +++ b/src/util/mathematics/rescale.rs @@ -1,5 +1,4 @@ -use ffi::*; -use {Rational, Rounding}; +use crate::{ffi::*, Rational, Rounding}; pub const TIME_BASE: Rational = Rational(AV_TIME_BASE_Q.num, AV_TIME_BASE_Q.den); diff --git a/src/util/mathematics/rounding.rs b/src/util/mathematics/rounding.rs index 6ed17da7..c6642f1e 100644 --- a/src/util/mathematics/rounding.rs +++ b/src/util/mathematics/rounding.rs @@ -1,5 +1,4 @@ -use ffi::AVRounding::*; -use ffi::*; +use crate::ffi::{AVRounding::*, *}; #[derive(Eq, PartialEq, Clone, Copy, Debug)] pub enum Rounding { diff --git a/src/util/media.rs b/src/util/media.rs index b57f5df5..fd5bc9c5 100644 --- a/src/util/media.rs +++ b/src/util/media.rs @@ -1,5 +1,4 @@ -use ffi::AVMediaType::*; -use ffi::*; +use crate::ffi::{AVMediaType::*, *}; #[derive(Eq, PartialEq, Clone, Copy, Debug)] pub enum Type { diff --git a/src/util/mod.rs b/src/util/mod.rs index 1a500773..d365bdd9 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -7,6 +7,7 @@ pub mod error; pub mod format; pub mod frame; pub mod interrupt; +pub mod log; pub mod mathematics; pub mod media; pub mod option; @@ -15,10 +16,9 @@ pub mod range; pub mod rational; pub mod time; -use std::ffi::CStr; -use std::str::from_utf8_unchecked; +use std::{ffi::CStr, str::from_utf8_unchecked}; -use ffi::*; +use crate::ffi::*; #[inline(always)] pub fn version() -> u32 { diff --git a/src/util/option/mod.rs b/src/util/option/mod.rs index c42a24bf..b3d713c7 100644 --- a/src/util/option/mod.rs +++ b/src/util/option/mod.rs @@ -1,8 +1,7 @@ mod traits; pub use self::traits::{Gettable, Iterable, Settable, Target}; -use ffi::AVOptionType::*; -use ffi::*; +use crate::ffi::{AVOptionType::*, *}; #[derive(PartialEq, Eq, Copy, Clone, Debug)] pub enum Type { diff --git a/src/util/option/traits.rs b/src/util/option/traits.rs index 80c43c20..dd8e4f88 100644 --- a/src/util/option/traits.rs +++ b/src/util/option/traits.rs @@ -1,12 +1,9 @@ //! NOTE: this will be much better once specialization comes -use std::ffi::CString; -use std::mem; +use std::{ffi::CString, mem}; -use ffi::*; -use libc::{c_int, c_void, int64_t}; -use util::format; -use {ChannelLayout, Error, Rational}; +use crate::{ffi::*, util::format, ChannelLayout, Error, Rational}; +use libc::{c_int, c_void}; macro_rules! check { ($expr:expr) => { @@ -58,7 +55,7 @@ pub trait Settable: Target { check!(av_opt_set_int( self.as_mut_ptr(), name.as_ptr(), - value as int64_t, + value, AV_OPT_SEARCH_CHILDREN )) } @@ -137,7 +134,7 @@ pub trait Settable: Target { check!(av_opt_set_channel_layout( self.as_mut_ptr(), name.as_ptr(), - layout.bits() as int64_t, + layout.bits() as i64, AV_OPT_SEARCH_CHILDREN )) } diff --git a/src/util/picture.rs b/src/util/picture.rs index 69aa99fb..b21aeac1 100644 --- a/src/util/picture.rs +++ b/src/util/picture.rs @@ -1,5 +1,4 @@ -use ffi::AVPictureType::*; -use ffi::*; +use crate::ffi::{AVPictureType::*, *}; #[derive(Eq, PartialEq, Clone, Copy, Debug)] pub enum Type { diff --git a/src/util/rational.rs b/src/util/rational.rs index 6fe82b8c..da826e74 100644 --- a/src/util/rational.rs +++ b/src/util/rational.rs @@ -1,11 +1,15 @@ -use std::cmp::Ordering; -use std::fmt; -use std::ops::{Add, Div, Mul, Sub}; +use std::{ + cmp::Ordering, + fmt, + ops::{Add, Div, Mul, Sub}, +}; -use ffi::*; +use crate::ffi::*; use libc::c_int; #[derive(Copy, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub struct Rational(pub i32, pub i32); impl Rational { @@ -48,7 +52,8 @@ impl Rational { if exact == 1 { Ok(Rational(dst_num, dst_den)) - } else { + } + else { Err(Rational(dst_num, dst_den)) } } diff --git a/src/util/time.rs b/src/util/time.rs index 373a5ea4..faca4c03 100644 --- a/src/util/time.rs +++ b/src/util/time.rs @@ -1,5 +1,4 @@ -use ffi::*; -use Error; +use crate::{ffi::*, Error}; #[inline(always)] pub fn current() -> i64 {