From fe6a9b33244633b2946af8c6f7d5ced97f844c55 Mon Sep 17 00:00:00 2001 From: Zhiming Wang Date: Sun, 9 Aug 2020 13:59:18 +0800 Subject: [PATCH] Add CHANGELOG.md --- CHANGELOG.md | 24 +++++ examples/fps-filter.rs | 222 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 246 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 examples/fps-filter.rs diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..52bb91e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,24 @@ +4.4.0 (planned) +--------------- + +- crate: `ffmpeg43` feature flag (noop since 4.3.4) has been dropped from default features. + +- codec: deprecate APIs based on deprecated (since FFmpeg 3.1) `avcodec_decode_video2()` / `avcodec_decode_audio4()` / `avcodec_encode_video2()` /`avcodec_encode_audio2()` -- `decoder::Video::decode()`, `decode::Audio::decode()`, `encoder::Video::encode()` and `encoder::Audio::decode()`. Users should migrate to `send_packet()` / `send_eof()`, `receive_frame()`, `send_frame()` / `send_eof()`, and `receive_packet()` APIs instead, which are based on the modern send/receive APIs. See [documentation in `libavcodec/avcodec.h`](https://github.com/FFmpeg/FFmpeg/blob/n4.3.1/libavcodec/avcodec.h#L84-L196) for details. (#28) + +- codec: fix signature of `Packet::write_interleaved`; previously `Result`, now `Result<(), Error>`. (#25) + +4.3.5 (planned) +--------------- + +- 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/examples/fps-filter.rs b/examples/fps-filter.rs new file mode 100644 index 0000000..03a2ae2 --- /dev/null +++ b/examples/fps-filter.rs @@ -0,0 +1,222 @@ +extern crate ffmpeg_next as ffmpeg; + +use ffmpeg::*; +use std::env; + +const DEFAULT_INPUT: &str = "input.gif"; +const DEFAULT_OUTPUT: &str = "output.gif"; +const DEFAULT_TARGET_FPS: f64 = 25.0; + +fn main() { + let input_file = env::args() + .nth(1) + .unwrap_or_else(|| DEFAULT_INPUT.to_string()); + let output_file = env::args() + .nth(2) + .unwrap_or_else(|| DEFAULT_OUTPUT.to_string()); + let target_fps: f64 = env::args() + .nth(3) + .unwrap_or_else(|| DEFAULT_TARGET_FPS.to_string()) + .parse() + .unwrap(); + + init() + .map_err(|e| format!("Unable to initialize ffmpeg: {}", e)) + .unwrap(); + util::log::set_level(util::log::Level::Trace); + let mut input_context = format::input(&input_file) + .map_err(|e| format!("Unable to open input file: {}", e)) + .unwrap(); + let mut output_context = format::output(&output_file) + .map_err(|e| format!("Unable to open output file: {}", e)) + .unwrap(); + + let stream = input_context + .streams() + .best(media::Type::Video) + .ok_or("The file has no video tracks") + .unwrap(); + let stream_index = stream.index(); + let mut decoder = stream + .codec() + .decoder() + .video() + .map_err(|e| format!("Unable to decode the codec used in the video: {}", e)) + .unwrap(); + + let format_aspect_ratio = |sar: util::rational::Rational| match sar.numerator() { + 0 => "1".to_string(), + _ => format!("{}/{}", sar.numerator(), sar.denominator()), + }; + let buffer_args = format!( + "width={}:height={}:pix_fmt={}:time_base={}:sar={}", + decoder.width(), + decoder.height(), + decoder.format().descriptor().unwrap().name(), + stream.time_base(), + format_aspect_ratio(decoder.aspect_ratio()), + ); + let mut filter = filter::Graph::new(); + filter + .add(&filter::find("buffer").unwrap(), "in", &buffer_args) + .unwrap(); + filter + .add(&filter::find("buffersink").unwrap(), "out", "") + .unwrap(); + filter + .output("in", 0) + .unwrap() + .input("out", 0) + .unwrap() + .parse(&format!("fps=fps={},format=bgr8", target_fps)[..]) + .unwrap(); + filter.validate().unwrap(); + + let codec = ffmpeg::encoder::find( + output_context + .format() + .codec(&DEFAULT_OUTPUT, media::Type::Video), + ) + .expect("Unable to find encoder") + .video() + .unwrap(); + let mut output_stream = output_context.add_stream(codec).unwrap(); + let mut encoder = output_stream.codec().encoder().video().unwrap(); + encoder.set_format(format::Pixel::BGR8); + encoder.set_width(decoder.width()); + encoder.set_height(decoder.height()); + encoder.set_time_base((1, 100)); + output_stream.set_time_base((1, 100)); + let mut encoder = encoder.open_as(codec).unwrap(); + output_stream.set_parameters(&encoder); + output_context.write_header().unwrap(); + + let mut input_frame_count = 0; + let mut output_frame_count = 0; + let mut input_pts = 0; + let mut output_pts = 0; + + let mut write_frame = |rgba_encoded: &mut Packet| { + rgba_encoded.set_stream(0); + rgba_encoded.set_pts(Option::from(output_pts)); + rgba_encoded.write_interleaved(&mut output_context).unwrap(); + output_pts += (1.0 / target_fps * 100.0) as i64; + output_frame_count += 1; + }; + + let mut process_encoded_packets = |encoder: &mut encoder::Video| { + let mut rgba_encoded = Packet::empty(); + while encoder.receive_packet(&mut rgba_encoded).is_ok() { + write_frame(&mut rgba_encoded); + } + }; + + let mut process_decoded_frames = + |decoder: &mut decoder::Video, encoder: &mut encoder::Video| { + let mut input_frame = frame::Video::empty(); + while match decoder.receive_frame(&mut input_frame) { + Ok(_) => true, + Err(e) => { + if e != (Error::Other { + errno: error::EAGAIN, + }) { + eprintln!("receive_frame error: {}", e); + } + false + } + } { + input_frame_count += 1; + let mut rgba_frame = frame::Video::empty(); + filter + .get("in") + .unwrap() + .source() + .add(&input_frame) + .unwrap(); + while filter + .get("out") + .unwrap() + .sink() + .frame(&mut rgba_frame) + .is_ok() + { + encoder.send_frame(&rgba_frame).unwrap(); + process_encoded_packets(encoder); + } + } + }; + + for (s, packet) in input_context.packets() { + if s.index() != stream_index { + continue; + } + input_pts += packet.duration(); + decoder.send_packet(&packet).unwrap(); + process_decoded_frames(&mut decoder, &mut encoder); + // if !decoder.decode(&packet, &mut input_frame).unwrap() { + // continue; + // } + // input_frame_count += 1; + + // let mut rgba_frame = util::frame::video::Video::empty(); + // let mut rgba_encoded = Packet::empty(); + // filter + // .get("in") + // .unwrap() + // .source() + // .add(&input_frame) + // .unwrap(); + // while filter + // .get("out") + // .unwrap() + // .sink() + // .frame(&mut rgba_frame) + // .is_ok() + // { + // if let Ok(true) = encoder.encode(&rgba_frame, &mut rgba_encoded) { + // write_frame(&mut rgba_encoded); + // } + // } + } + decoder.send_eof().unwrap(); + process_decoded_frames(&mut decoder, &mut encoder); + + filter.get("in").unwrap().source().close(input_pts).unwrap(); + let mut rgba_frame = frame::Video::empty(); + while filter + .get("out") + .unwrap() + .sink() + .frame(&mut rgba_frame) + .is_ok() + { + encoder.send_frame(&rgba_frame).unwrap(); + process_encoded_packets(&mut encoder); + } + + encoder.send_eof().unwrap(); + process_encoded_packets(&mut encoder); + + // // let mut rgba_frame = util::frame::video::Video::empty(); + // // let mut rgba_encoded = Packet::empty(); + // filter.get("in").unwrap().source().close(input_pts).unwrap(); + // while filter + // .get("out") + // .unwrap() + // .sink() + // .frame(&mut rgba_frame) + // .is_ok() + // { + // if let Ok(true) = encoder.encode(&rgba_frame, &mut rgba_encoded) { + // write_frame(&mut rgba_encoded); + // } + // } + // if let Ok(true) = encoder.flush(&mut rgba_encoded) { + // write_frame(&mut rgba_encoded); + // } + output_context.write_trailer().unwrap(); + + println!("Total input frames: {}", input_frame_count); + println!("Total output frames: {}", output_frame_count); + println!("Total duration: {:.2}s", output_pts as f64 / 100.0); +}