scuffle_ffmpeg/
lib.rs

1//! A crate designed to provide a simple interface to the native ffmpeg c-bindings.
2#![cfg_attr(feature = "docs", doc = "\n\nSee the [changelog][changelog] for a full release history.")]
3#![cfg_attr(feature = "docs", doc = "## Feature flags")]
4#![cfg_attr(feature = "docs", doc = document_features::document_features!())]
5//! ## Why do we need this?
6//!
7//! This crate aims to provide a simple-safe interface to the native ffmpeg c-bindings.
8//!
9//! Currently this crate only supports the latest versions of ffmpeg (7.x.x).
10//!
11//! ## How is this different from other ffmpeg crates?
12//!
13//! The other main ffmpeg crate is [ffmpeg-next](https://github.com/zmwangx/rust-ffmpeg).
14//!
15//! This crate adds a few features and has a safer API. Notably it adds the ability to provide an in-memory decode / encode buffer.
16//!
17//! ## Examples
18//!
19//! ### Decoding a audio/video file
20//!
21//! ```rust
22//! # use std::path::PathBuf;
23//! # use scuffle_ffmpeg::AVMediaType;
24//! # fn test_fn() -> Result<(), Box<dyn std::error::Error>> {
25//! # let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../assets").join("avc_aac.mp4");
26//! // 1. Store the input of the file from the path `path`
27//! // this can be any seekable io stream (std::io::Read + std::io::Seek)
28//! // if you don't have seek, you can just use Input::new(std::io::Read) (no seeking support)
29//! let mut input = scuffle_ffmpeg::io::Input::seekable(std::fs::File::open(path)?)?;
30//! // 2. Get the streams from the input
31//! let streams = input.streams();
32//!
33//! dbg!(&streams);
34//!
35//! // 3. Find the best audio & video streams.
36//! let best_video_stream = streams.best(AVMediaType::Video).expect("no video stream found");
37//! let best_audio_stream = streams.best(AVMediaType::Audio).expect("no audio stream found");
38//!
39//! dbg!(&best_video_stream);
40//! dbg!(&best_audio_stream);
41//!
42//! // 4. Create a decoder for each stream
43//! let mut video_decoder = scuffle_ffmpeg::decoder::Decoder::new(&best_video_stream)?
44//!     .video()
45//!     .expect("not an video decoder");
46//! let mut audio_decoder = scuffle_ffmpeg::decoder::Decoder::new(&best_audio_stream)?
47//!     .audio()
48//!     .expect("not an audio decoder");
49//!
50//! dbg!(&video_decoder);
51//! dbg!(&audio_decoder);
52//!
53//! // 5. Get the stream index of the video and audio streams.
54//! let video_stream_index = best_video_stream.index();
55//! let audio_stream_index = best_audio_stream.index();
56//!
57//! // 6. Iterate over the packets in the input.
58//! for packet in input.packets() {
59//!     let packet = packet?;
60//!     // 7. Send the packet to the respective decoder.
61//!     // 8. Receive the frame from the decoder.
62//!     if packet.stream_index() == video_stream_index {
63//!         video_decoder.send_packet(&packet)?;
64//!         while let Some(frame) = video_decoder.receive_frame()? {
65//!             dbg!(&frame);
66//!         }
67//!     } else if packet.stream_index() == audio_stream_index {
68//!         audio_decoder.send_packet(&packet)?;
69//!         while let Some(frame) = audio_decoder.receive_frame()? {
70//!             dbg!(&frame);
71//!         }
72//!     }
73//! }
74//!
75//! // 9. Send the EOF to the decoders.
76//! video_decoder.send_eof()?;
77//! audio_decoder.send_eof()?;
78//!
79//! // 10. Receive the remaining frames from the decoders.
80//! while let Some(frame) = video_decoder.receive_frame()? {
81//!     dbg!(&frame);
82//! }
83//!
84//! while let Some(frame) = audio_decoder.receive_frame()? {
85//!     dbg!(&frame);
86//! }
87//! # Ok(())
88//! # }
89//! # test_fn().expect("failed to run test");
90//! ```
91//!
92//! ### Re-encoding a audio/video file
93//!
94//! ```rust
95//! # use std::path::PathBuf;
96//! # use scuffle_ffmpeg::{AVMediaType, AVCodecID};
97//! # use scuffle_ffmpeg::encoder::{AudioEncoderSettings, VideoEncoderSettings};
98//! # use scuffle_ffmpeg::io::OutputOptions;
99//! # use scuffle_ffmpeg::frame::AudioChannelLayout;
100//! #
101//! # fn test_fn() -> Result<(), Box<dyn std::error::Error>> {
102//! # let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../assets").join("avc_aac.mp4");
103//! // 1. Create an input for reading. In this case we open it from a std::fs::File, however
104//! // it can be from any seekable io stream (std::io::Read + std::io::Seek) for example a std::io::Cursor.
105//! // It can also be a non-seekable stream in that case you can use Input::new(std::io::Read)
106//! let input = scuffle_ffmpeg::io::Input::seekable(std::fs::File::open(path)?)?;
107//!
108//! // 2. Get the streams from the input.
109//! let streams = input.streams();
110//!
111//! // 3. Find the best audio & video streams.
112//! let best_video_stream = streams.best(AVMediaType::Video).expect("no video stream found");
113//! let best_audio_stream = streams.best(AVMediaType::Audio).expect("no audio stream found");
114//!
115//! // 4. Create a decoder for each stream
116//! let mut video_decoder = scuffle_ffmpeg::decoder::Decoder::new(&best_video_stream)?
117//!     .video()
118//!     .expect("not an video decoder");
119//! let mut audio_decoder = scuffle_ffmpeg::decoder::Decoder::new(&best_audio_stream)?
120//!     .audio()
121//!     .expect("not an audio decoder");
122//!
123//! // 5. Create an output for writing. In this case we use a std::io::Cursor,
124//! // however it can be any seekable io stream (std::io::Read + std::io::Seek)
125//! // for example a std::io::Cursor. It can also be a non-seekable stream
126//! // in that case you can use Output::new(std::io::Read)
127//! let mut output = scuffle_ffmpeg::io::Output::seekable(
128//!     std::io::Cursor::new(Vec::new()),
129//!     OutputOptions::builder().format_name("mp4")?.build(),
130//! )?;
131//!
132//! // 6. Find encoders for the streams by name or codec
133//! let x264 = scuffle_ffmpeg::codec::EncoderCodec::by_name("libx264")
134//!     .expect("no h264 encoder found");
135//! let aac = scuffle_ffmpeg::codec::EncoderCodec::new(AVCodecID::Aac)
136//!     .expect("no aac encoder found");
137//!
138//! // 7. Setup the settings for each encoder
139//! let video_settings = VideoEncoderSettings::builder()
140//!     .width(video_decoder.width())
141//!     .height(video_decoder.height())
142//!     .frame_rate(video_decoder.frame_rate())
143//!     .pixel_format(video_decoder.pixel_format())
144//!     .build();
145//!
146//! let audio_settings = AudioEncoderSettings::builder()
147//!     .sample_rate(audio_decoder.sample_rate())
148//!     .ch_layout(AudioChannelLayout::new(
149//!         audio_decoder.channels()
150//!     ).expect("invalid channel layout"))
151//!     .sample_fmt(audio_decoder.sample_format())
152//!     .build();
153//!
154//! // 8. Initialize the encoders
155//! let mut video_encoder = scuffle_ffmpeg::encoder::Encoder::new(
156//!     x264,
157//!     &mut output,
158//!     best_video_stream.time_base(),
159//!     best_video_stream.time_base(),
160//!     video_settings,
161//! ).expect("not an video encoder");
162//! let mut audio_encoder = scuffle_ffmpeg::encoder::Encoder::new(
163//!     aac,
164//!     &mut output,
165//!     best_audio_stream.time_base(),
166//!     best_audio_stream.time_base(),
167//!     audio_settings,
168//! ).expect("not an audio encoder");
169//!
170//! // 9. Write the header to the output.
171//! output.write_header()?;
172//!
173//! loop {
174//!     let mut audio_done = false;
175//!     let mut video_done = false;
176//!
177//!     // 10. Receive the frame from the decoders.
178//!     // 11. Send the frame to the encoders.
179//!     // 12. Receive the packet from the encoders.
180//!     // 13. Write the packet to the output.
181//!
182//!     if let Some(frame) = audio_decoder.receive_frame()? {
183//!         audio_encoder.send_frame(&frame)?;
184//!         while let Some(packet) = audio_encoder.receive_packet()? {
185//!             output.write_packet(&packet)?;
186//!         }
187//!     } else {
188//!         audio_done = true;
189//!     }
190//!
191//!     if let Some(frame) = video_decoder.receive_frame()? {
192//!         video_encoder.send_frame(&frame)?;
193//!         while let Some(packet) = video_encoder.receive_packet()? {
194//!             output.write_packet(&packet)?;
195//!         }
196//!     } else {
197//!         video_done = true;
198//!     }
199//!
200//!     // 14. Break the loop if both the audio and video are done.
201//!     if audio_done && video_done {
202//!         break;
203//!     }
204//! }
205//!
206//! // 15. Send the EOF to the decoders.
207//! video_decoder.send_eof()?;
208//! audio_decoder.send_eof()?;
209//!
210//! // 16. Receive the remaining packets from the encoders.
211//! while let Some(packet) = video_encoder.receive_packet()? {
212//!     output.write_packet(&packet)?;
213//! }
214//!
215//! while let Some(packet) = audio_encoder.receive_packet()? {
216//!     output.write_packet(&packet)?;
217//! }
218//!
219//! // 17. Write the trailer to the output.
220//! output.write_trailer()?;
221//!
222//! // 18. Do something with the output data (write to disk, upload to s3, etc).
223//! let output_data = output.into_inner();
224//! # drop(output_data);
225//! # Ok(())
226//! # }
227//! # test_fn().expect("failed to run test");
228//! ```
229//!
230//! ## License
231//!
232//! This project is licensed under the MIT or Apache-2.0 license.
233//! You can choose between one of them if you use this work.
234//!
235//! `SPDX-License-Identifier: MIT OR Apache-2.0`
236#![cfg_attr(all(coverage_nightly, test), feature(coverage_attribute))]
237#![cfg_attr(docsrs, feature(doc_auto_cfg))]
238#![deny(missing_docs)]
239#![deny(unreachable_pub)]
240#![deny(clippy::undocumented_unsafe_blocks)]
241#![deny(clippy::multiple_unsafe_ops_per_block)]
242
243/// Codec specific functionality.
244pub mod codec;
245/// Constants.
246pub mod consts;
247/// Decoder specific functionality.
248pub mod decoder;
249/// Dictionary specific functionality.
250pub mod dict;
251/// Encoder specific functionality.
252pub mod encoder;
253/// Error handling.
254pub mod error;
255/// Filter graph specific functionality.
256pub mod filter_graph;
257/// Frame specific functionality.
258pub mod frame;
259/// Input/Output specific functionality.
260pub mod io;
261/// Logging specific functionality.
262pub mod log;
263/// Packet specific functionality.
264pub mod packet;
265/// Rational number specific functionality.
266pub mod rational;
267/// [`frame::AudioFrame`] resampling and format conversion.
268pub mod resampler;
269/// Scaler specific functionality.
270pub mod scaler;
271/// Stream specific functionality.
272pub mod stream;
273/// Utility functionality.
274pub mod utils;
275
276pub use rusty_ffmpeg::ffi;
277
278mod smart_object;
279
280mod enums;
281
282pub use enums::*;
283
284/// Changelogs generated by [scuffle_changelog]
285#[cfg(feature = "docs")]
286#[scuffle_changelog::changelog]
287pub mod changelog {}