1use std::ptr::NonNull;
2
3use crate::codec::EncoderCodec;
4use crate::dict::Dictionary;
5use crate::error::{FfmpegError, FfmpegErrorCode};
6use crate::ffi::*;
7use crate::frame::{AudioChannelLayout, GenericFrame};
8use crate::io::Output;
9use crate::packet::Packet;
10use crate::rational::Rational;
11use crate::smart_object::SmartPtr;
12use crate::{AVFormatFlags, AVPixelFormat, AVSampleFormat};
13
14pub struct Encoder {
16 incoming_time_base: Rational,
17 outgoing_time_base: Rational,
18 encoder: SmartPtr<AVCodecContext>,
19 stream_index: i32,
20 previous_dts: i64,
21}
22
23unsafe impl Send for Encoder {}
25
26#[derive(bon::Builder)]
28pub struct VideoEncoderSettings {
29 width: i32,
30 height: i32,
31 frame_rate: Rational,
32 pixel_format: AVPixelFormat,
33 gop_size: Option<i32>,
34 qmax: Option<i32>,
35 qmin: Option<i32>,
36 thread_count: Option<i32>,
37 thread_type: Option<i32>,
38 sample_aspect_ratio: Option<Rational>,
39 bitrate: Option<i64>,
40 rc_min_rate: Option<i64>,
41 rc_max_rate: Option<i64>,
42 rc_buffer_size: Option<i32>,
43 max_b_frames: Option<i32>,
44 codec_specific_options: Option<Dictionary>,
45 flags: Option<i32>,
46 flags2: Option<i32>,
47}
48
49impl VideoEncoderSettings {
50 fn apply(self, encoder: &mut AVCodecContext) -> Result<(), FfmpegError> {
51 if self.width <= 0 || self.height <= 0 || self.frame_rate.numerator <= 0 || self.pixel_format == AVPixelFormat::None
52 {
53 return Err(FfmpegError::Arguments(
54 "width, height, frame_rate and pixel_format must be set",
55 ));
56 }
57
58 encoder.width = self.width;
59 encoder.height = self.height;
60 encoder.pix_fmt = self.pixel_format.into();
61 encoder.sample_aspect_ratio = self
62 .sample_aspect_ratio
63 .map(Into::into)
64 .unwrap_or(encoder.sample_aspect_ratio);
65 encoder.framerate = self.frame_rate.into();
66 encoder.thread_count = self.thread_count.unwrap_or(encoder.thread_count);
67 encoder.thread_type = self.thread_type.unwrap_or(encoder.thread_type);
68 encoder.gop_size = self.gop_size.unwrap_or(encoder.gop_size);
69 encoder.qmax = self.qmax.unwrap_or(encoder.qmax);
70 encoder.qmin = self.qmin.unwrap_or(encoder.qmin);
71 encoder.bit_rate = self.bitrate.unwrap_or(encoder.bit_rate);
72 encoder.rc_min_rate = self.rc_min_rate.unwrap_or(encoder.rc_min_rate);
73 encoder.rc_max_rate = self.rc_max_rate.unwrap_or(encoder.rc_max_rate);
74 encoder.rc_buffer_size = self.rc_buffer_size.unwrap_or(encoder.rc_buffer_size);
75 encoder.max_b_frames = self.max_b_frames.unwrap_or(encoder.max_b_frames);
76 encoder.flags = self.flags.unwrap_or(encoder.flags);
77 encoder.flags2 = self.flags2.unwrap_or(encoder.flags2);
78
79 Ok(())
80 }
81}
82
83#[derive(bon::Builder)]
85pub struct AudioEncoderSettings {
86 sample_rate: i32,
87 ch_layout: AudioChannelLayout,
88 sample_fmt: AVSampleFormat,
89 thread_count: Option<i32>,
90 thread_type: Option<i32>,
91 bitrate: Option<i64>,
92 rc_min_rate: Option<i64>,
93 rc_max_rate: Option<i64>,
94 rc_buffer_size: Option<i32>,
95 codec_specific_options: Option<Dictionary>,
96 flags: Option<i32>,
97 flags2: Option<i32>,
98}
99
100impl AudioEncoderSettings {
101 fn apply(self, encoder: &mut AVCodecContext) -> Result<(), FfmpegError> {
102 if self.sample_rate <= 0 || self.sample_fmt == AVSampleFormat::None {
103 return Err(FfmpegError::Arguments(
104 "sample_rate, channel_layout and sample_fmt must be set",
105 ));
106 }
107
108 encoder.sample_rate = self.sample_rate;
109 self.ch_layout.apply(&mut encoder.ch_layout);
110 encoder.sample_fmt = self.sample_fmt.into();
111 encoder.thread_count = self.thread_count.unwrap_or(encoder.thread_count);
112 encoder.thread_type = self.thread_type.unwrap_or(encoder.thread_type);
113 encoder.bit_rate = self.bitrate.unwrap_or(encoder.bit_rate);
114 encoder.rc_min_rate = self.rc_min_rate.unwrap_or(encoder.rc_min_rate);
115 encoder.rc_max_rate = self.rc_max_rate.unwrap_or(encoder.rc_max_rate);
116 encoder.rc_buffer_size = self.rc_buffer_size.unwrap_or(encoder.rc_buffer_size);
117 encoder.flags = self.flags.unwrap_or(encoder.flags);
118 encoder.flags2 = self.flags2.unwrap_or(encoder.flags2);
119
120 Ok(())
121 }
122}
123
124pub enum EncoderSettings {
126 Video(VideoEncoderSettings),
128 Audio(AudioEncoderSettings),
130}
131
132impl EncoderSettings {
133 fn apply(self, encoder: &mut AVCodecContext) -> Result<(), FfmpegError> {
134 match self {
135 EncoderSettings::Video(video_settings) => video_settings.apply(encoder),
136 EncoderSettings::Audio(audio_settings) => audio_settings.apply(encoder),
137 }
138 }
139
140 const fn codec_specific_options(&mut self) -> Option<&mut Dictionary> {
141 match self {
142 EncoderSettings::Video(video_settings) => video_settings.codec_specific_options.as_mut(),
143 EncoderSettings::Audio(audio_settings) => audio_settings.codec_specific_options.as_mut(),
144 }
145 }
146}
147
148impl From<VideoEncoderSettings> for EncoderSettings {
149 fn from(settings: VideoEncoderSettings) -> Self {
150 EncoderSettings::Video(settings)
151 }
152}
153
154impl From<AudioEncoderSettings> for EncoderSettings {
155 fn from(settings: AudioEncoderSettings) -> Self {
156 EncoderSettings::Audio(settings)
157 }
158}
159
160impl Encoder {
161 pub fn new<T: Send + Sync>(
163 codec: EncoderCodec,
164 output: &mut Output<T>,
165 incoming_time_base: impl Into<Rational>,
166 outgoing_time_base: impl Into<Rational>,
167 settings: impl Into<EncoderSettings>,
168 ) -> Result<Self, FfmpegError> {
169 if codec.as_ptr().is_null() {
170 return Err(FfmpegError::NoEncoder);
171 }
172
173 let mut settings = settings.into();
174
175 let global_header = output
176 .output_flags()
177 .is_some_and(|flags| flags & AVFormatFlags::GlobalHeader != 0);
178
179 let destructor = |ptr: &mut *mut AVCodecContext| {
180 unsafe { avcodec_free_context(ptr) };
182 };
183
184 let encoder = unsafe { avcodec_alloc_context3(codec.as_ptr()) };
186
187 let mut encoder = unsafe { SmartPtr::wrap_non_null(encoder, destructor) }.ok_or(FfmpegError::Alloc)?;
189
190 let mut ost = output.add_stream(None).ok_or(FfmpegError::NoStream)?;
191
192 let encoder_mut = encoder.as_deref_mut_except();
193
194 let incoming_time_base = incoming_time_base.into();
195 let outgoing_time_base = outgoing_time_base.into();
196
197 encoder_mut.time_base = incoming_time_base.into();
198
199 let mut codec_options = settings.codec_specific_options().cloned();
200
201 let codec_options_ptr = codec_options
202 .as_mut()
203 .map(|options| options.as_mut_ptr_ref() as *mut *mut _)
204 .unwrap_or(std::ptr::null_mut());
205
206 settings.apply(encoder_mut)?;
207
208 if global_header {
209 encoder_mut.flags |= AV_CODEC_FLAG_GLOBAL_HEADER as i32;
210 }
211
212 FfmpegErrorCode(unsafe { avcodec_open2(encoder_mut, codec.as_ptr(), codec_options_ptr) }).result()?;
215
216 let ost_mut = unsafe { NonNull::new(ost.as_mut_ptr()).ok_or(FfmpegError::NoStream)?.as_mut() };
218
219 FfmpegErrorCode(unsafe { avcodec_parameters_from_context(ost_mut.codecpar, encoder_mut) }).result()?;
222
223 ost.set_time_base(outgoing_time_base);
224
225 Ok(Self {
226 incoming_time_base,
227 outgoing_time_base,
228 encoder,
229 stream_index: ost.index(),
230 previous_dts: i64::MIN,
231 })
232 }
233
234 pub fn send_eof(&mut self) -> Result<(), FfmpegError> {
236 FfmpegErrorCode(unsafe { avcodec_send_frame(self.encoder.as_mut_ptr(), std::ptr::null()) }).result()?;
238 Ok(())
239 }
240
241 pub fn send_frame(&mut self, frame: &GenericFrame) -> Result<(), FfmpegError> {
243 FfmpegErrorCode(unsafe { avcodec_send_frame(self.encoder.as_mut_ptr(), frame.as_ptr()) }).result()?;
245 Ok(())
246 }
247
248 pub fn receive_packet(&mut self) -> Result<Option<Packet>, FfmpegError> {
250 let mut packet = Packet::new()?;
251
252 let ret = FfmpegErrorCode(unsafe { avcodec_receive_packet(self.encoder.as_mut_ptr(), packet.as_mut_ptr()) });
254
255 match ret {
256 FfmpegErrorCode::Eagain | FfmpegErrorCode::Eof => Ok(None),
257 code if code.is_success() => {
258 if cfg!(debug_assertions) {
259 debug_assert!(
260 packet.dts().is_some(),
261 "packet dts is none, this should never happen, please report this bug"
262 );
263 let packet_dts = packet.dts().unwrap();
264 debug_assert!(
265 packet_dts >= self.previous_dts,
266 "packet dts is less than previous dts: {} >= {}",
267 packet_dts,
268 self.previous_dts
269 );
270 self.previous_dts = packet_dts;
271 }
272
273 packet.convert_timebase(self.incoming_time_base, self.outgoing_time_base);
274 packet.set_stream_index(self.stream_index);
275 Ok(Some(packet))
276 }
277 code => Err(FfmpegError::Code(code)),
278 }
279 }
280
281 pub const fn stream_index(&self) -> i32 {
283 self.stream_index
284 }
285
286 pub const fn incoming_time_base(&self) -> Rational {
288 self.incoming_time_base
289 }
290
291 pub const fn outgoing_time_base(&self) -> Rational {
293 self.outgoing_time_base
294 }
295}
296
297#[cfg(test)]
298#[cfg_attr(all(test, coverage_nightly), coverage(off))]
299mod tests {
300 use rusty_ffmpeg::ffi::AVRational;
301 use scuffle_bytes_util::zero_copy::Deserialize;
302 use scuffle_bytes_util::{BytesCow, IoResultExt};
303
304 use crate::codec::EncoderCodec;
305 use crate::decoder::Decoder;
306 use crate::dict::Dictionary;
307 use crate::encoder::{AudioChannelLayout, AudioEncoderSettings, Encoder, EncoderSettings, VideoEncoderSettings};
308 use crate::error::FfmpegError;
309 use crate::ffi::AVCodecContext;
310 use crate::io::{Input, Output, OutputOptions};
311 use crate::rational::Rational;
312 use crate::{AVChannelOrder, AVCodecID, AVMediaType, AVPixelFormat, AVSampleFormat, file_path};
313
314 #[test]
315 fn test_video_encoder_apply() {
316 let width = 1920;
317 let height = 1080;
318 let frame_rate = 30;
319 let pixel_format = AVPixelFormat::Yuv420p;
320 let sample_aspect_ratio = 1;
321 let gop_size = 12;
322 let qmax = 31;
323 let qmin = 1;
324 let thread_count = 4;
325 let thread_type = 2;
326 let bitrate = 8_000;
327 let rc_min_rate = 500_000;
328 let rc_max_rate = 2_000_000;
329 let rc_buffer_size = 1024;
330 let max_b_frames = 3;
331 let mut codec_specific_options = Dictionary::new();
332 codec_specific_options.set("preset", "ultrafast").unwrap();
333 codec_specific_options.set("crf", "23").unwrap();
334 let flags = 0x01;
335 let flags2 = 0x02;
336
337 let settings = VideoEncoderSettings::builder()
338 .width(width)
339 .height(height)
340 .frame_rate(frame_rate.into())
341 .pixel_format(pixel_format)
342 .sample_aspect_ratio(sample_aspect_ratio.into())
343 .gop_size(gop_size)
344 .qmax(qmax)
345 .qmin(qmin)
346 .thread_count(thread_count)
347 .thread_type(thread_type)
348 .bitrate(bitrate)
349 .rc_min_rate(rc_min_rate)
350 .rc_max_rate(rc_max_rate)
351 .rc_buffer_size(rc_buffer_size)
352 .max_b_frames(max_b_frames)
353 .codec_specific_options(codec_specific_options)
354 .flags(flags)
355 .flags2(flags2)
356 .build();
357
358 assert_eq!(settings.width, width);
359 assert_eq!(settings.height, height);
360 assert_eq!(settings.frame_rate, frame_rate.into());
361 assert_eq!(settings.pixel_format, pixel_format);
362 assert_eq!(settings.sample_aspect_ratio, Some(sample_aspect_ratio.into()));
363 assert_eq!(settings.gop_size, Some(gop_size));
364 assert_eq!(settings.qmax, Some(qmax));
365 assert_eq!(settings.qmin, Some(qmin));
366 assert_eq!(settings.thread_count, Some(thread_count));
367 assert_eq!(settings.thread_type, Some(thread_type));
368 assert_eq!(settings.bitrate, Some(bitrate));
369 assert_eq!(settings.rc_min_rate, Some(rc_min_rate));
370 assert_eq!(settings.rc_max_rate, Some(rc_max_rate));
371 assert_eq!(settings.rc_buffer_size, Some(rc_buffer_size));
372 assert_eq!(settings.max_b_frames, Some(max_b_frames));
373 assert!(settings.codec_specific_options.is_some());
374 let actual_codec_specific_options = settings.codec_specific_options.as_ref().unwrap();
375 assert_eq!(actual_codec_specific_options.get(c"preset"), Some(c"ultrafast"));
376 assert_eq!(actual_codec_specific_options.get(c"crf"), Some(c"23"));
377 assert_eq!(settings.flags, Some(flags));
378 assert_eq!(settings.flags2, Some(flags2));
379
380 let mut encoder = unsafe { std::mem::zeroed::<AVCodecContext>() };
382 let result = settings.apply(&mut encoder);
383 assert!(result.is_ok(), "Failed to apply settings: {:?}", result.err());
384
385 assert_eq!(encoder.width, width);
386 assert_eq!(encoder.height, height);
387 assert_eq!(AVPixelFormat(encoder.pix_fmt), pixel_format);
388 assert_eq!(Rational::from(encoder.sample_aspect_ratio), sample_aspect_ratio.into());
389 assert_eq!(Rational::from(encoder.framerate), frame_rate.into());
390 assert_eq!(encoder.thread_count, thread_count);
391 assert_eq!(encoder.thread_type, thread_type);
392 assert_eq!(encoder.gop_size, gop_size);
393 assert_eq!(encoder.qmax, qmax);
394 assert_eq!(encoder.qmin, qmin);
395 assert_eq!(encoder.bit_rate, bitrate);
396 assert_eq!(encoder.rc_min_rate, rc_min_rate);
397 assert_eq!(encoder.rc_max_rate, rc_max_rate);
398 assert_eq!(encoder.rc_buffer_size, rc_buffer_size);
399 assert_eq!(encoder.max_b_frames, max_b_frames);
400 assert_eq!(encoder.flags, flags);
401 assert_eq!(encoder.flags2, flags2);
402 }
403
404 #[test]
405 fn test_video_encoder_settings_apply_error() {
406 let settings = VideoEncoderSettings::builder()
407 .width(0)
408 .height(0)
409 .pixel_format(AVPixelFormat::Yuv420p)
410 .frame_rate(0.into())
411 .build();
412 let mut encoder = unsafe { std::mem::zeroed::<AVCodecContext>() };
414 let result = settings.apply(&mut encoder);
415
416 assert!(result.is_err());
417 assert_eq!(
418 result.unwrap_err(),
419 FfmpegError::Arguments("width, height, frame_rate and pixel_format must be set")
420 );
421 }
422
423 #[test]
424 fn test_audio_encoder_apply() {
425 let sample_rate = 44100;
426 let channel_count = 2;
427 let sample_fmt = AVSampleFormat::S16;
428 let thread_count = 4;
429 let thread_type = 1;
430 let bitrate = 128_000;
431 let rc_min_rate = 64_000;
432 let rc_max_rate = 256_000;
433 let rc_buffer_size = 1024;
434 let flags = 0x01;
435 let flags2 = 0x02;
436
437 let mut codec_specific_options = Dictionary::new();
438 codec_specific_options
439 .set(c"profile", c"high")
440 .expect("Failed to set profile");
441
442 let settings = AudioEncoderSettings::builder()
443 .sample_rate(sample_rate)
444 .ch_layout(AudioChannelLayout::new(channel_count).expect("channel_count is a valid value"))
445 .sample_fmt(sample_fmt)
446 .thread_count(thread_count)
447 .thread_type(thread_type)
448 .bitrate(bitrate)
449 .rc_min_rate(rc_min_rate)
450 .rc_max_rate(rc_max_rate)
451 .rc_buffer_size(rc_buffer_size)
452 .codec_specific_options(codec_specific_options)
453 .flags(flags)
454 .flags2(flags2)
455 .build();
456
457 assert_eq!(settings.sample_rate, sample_rate);
458 assert_eq!(settings.ch_layout.channel_count(), 2);
459 assert_eq!(settings.sample_fmt, sample_fmt);
460 assert_eq!(settings.thread_count, Some(thread_count));
461 assert_eq!(settings.thread_type, Some(thread_type));
462 assert_eq!(settings.bitrate, Some(bitrate));
463 assert_eq!(settings.rc_min_rate, Some(rc_min_rate));
464 assert_eq!(settings.rc_max_rate, Some(rc_max_rate));
465 assert_eq!(settings.rc_buffer_size, Some(rc_buffer_size));
466 assert!(settings.codec_specific_options.is_some());
467
468 let actual_codec_specific_options = settings.codec_specific_options.unwrap();
469 assert_eq!(actual_codec_specific_options.get(c"profile"), Some(c"high"));
470
471 assert_eq!(settings.flags, Some(flags));
472 assert_eq!(settings.flags2, Some(flags2));
473 }
474
475 #[test]
476 fn test_ch_layout_valid_layout() {
477 let channel_layout = unsafe {
479 AudioChannelLayout::wrap(crate::ffi::AVChannelLayout {
480 order: AVChannelOrder::Native.into(),
481 nb_channels: 2,
482 u: crate::ffi::AVChannelLayout__bindgen_ty_1 { mask: 0b11 },
483 opaque: std::ptr::null_mut(),
484 })
485 };
486
487 channel_layout.validate().expect("channel_layout is a valid value");
488 }
489
490 #[test]
491 fn test_ch_layout_invalid_layout() {
492 let channel_layout = unsafe {
494 AudioChannelLayout::wrap(crate::ffi::AVChannelLayout {
495 order: AVChannelOrder::Unspecified.into(),
496 nb_channels: 0,
497 u: crate::ffi::AVChannelLayout__bindgen_ty_1 { mask: 0 },
498 opaque: std::ptr::null_mut(),
499 })
500 };
501 let result: Result<(), FfmpegError> = channel_layout.validate();
502 assert_eq!(result.unwrap_err(), FfmpegError::Arguments("invalid channel layout"));
503 }
504
505 #[test]
506 fn test_audio_encoder_settings_apply_error() {
507 let settings = AudioEncoderSettings::builder()
508 .sample_rate(0)
509 .sample_fmt(AVSampleFormat::None)
510 .ch_layout(AudioChannelLayout::new(2).expect("channel_count is a valid value"))
511 .build();
512
513 let mut encoder = unsafe { std::mem::zeroed::<AVCodecContext>() };
515 let result = settings.apply(&mut encoder);
516
517 assert!(result.is_err());
518 assert_eq!(
519 result.unwrap_err(),
520 FfmpegError::Arguments("sample_rate, channel_layout and sample_fmt must be set")
521 );
522 }
523
524 #[test]
525 fn test_encoder_settings_apply_video() {
526 let sample_aspect_ratio = AVRational { num: 1, den: 1 };
527 let video_settings = VideoEncoderSettings::builder()
528 .width(1920)
529 .height(1080)
530 .frame_rate(30.into())
531 .pixel_format(AVPixelFormat::Yuv420p)
532 .sample_aspect_ratio(sample_aspect_ratio.into())
533 .gop_size(12)
534 .build();
535
536 let mut encoder = unsafe { std::mem::zeroed::<AVCodecContext>() };
538 let encoder_settings = EncoderSettings::Video(video_settings);
539 let result = encoder_settings.apply(&mut encoder);
540
541 assert!(result.is_ok(), "Failed to apply video settings: {:?}", result.err());
542 assert_eq!(encoder.width, 1920);
543 assert_eq!(encoder.height, 1080);
544 assert_eq!(AVPixelFormat(encoder.pix_fmt), AVPixelFormat::Yuv420p);
545 assert_eq!(Rational::from(encoder.sample_aspect_ratio), sample_aspect_ratio.into());
546 }
547
548 #[test]
549 fn test_encoder_settings_apply_audio() {
550 let audio_settings = AudioEncoderSettings::builder()
551 .sample_rate(44100)
552 .sample_fmt(AVSampleFormat::Fltp)
553 .ch_layout(AudioChannelLayout::new(2).expect("channel_count is a valid value"))
554 .thread_count(4)
555 .build();
556
557 let mut encoder = unsafe { std::mem::zeroed::<AVCodecContext>() };
559 let encoder_settings = EncoderSettings::Audio(audio_settings);
560 let result = encoder_settings.apply(&mut encoder);
561
562 assert!(result.is_ok(), "Failed to apply audio settings: {:?}", result.err());
563 assert_eq!(encoder.sample_rate, 44100);
564 assert_eq!(AVSampleFormat(encoder.sample_fmt), AVSampleFormat::Fltp);
565 assert_eq!(encoder.thread_count, 4);
566 }
567
568 #[test]
569 fn test_encoder_settings_codec_specific_options() {
570 let mut video_codec_options = Dictionary::new();
571 video_codec_options.set(c"preset", c"fast").expect("Failed to set preset");
572
573 let video_settings = VideoEncoderSettings::builder()
574 .width(8)
575 .height(8)
576 .frame_rate(30.into())
577 .pixel_format(AVPixelFormat::Yuv420p)
578 .codec_specific_options(video_codec_options.clone())
579 .build();
580 let mut encoder_settings = EncoderSettings::Video(video_settings);
581 let options = encoder_settings.codec_specific_options();
582
583 assert!(options.is_some());
584 assert_eq!(options.unwrap().get(c"preset"), Some(c"fast"));
585
586 let mut audio_codec_options = Dictionary::new();
587 audio_codec_options.set(c"bitrate", c"128k").expect("Failed to set bitrate");
588 let audio_settings = AudioEncoderSettings::builder()
589 .sample_rate(44100)
590 .sample_fmt(AVSampleFormat::Fltp)
591 .ch_layout(AudioChannelLayout::new(2).expect("channel_count is a valid value"))
592 .thread_count(4)
593 .codec_specific_options(audio_codec_options)
594 .build();
595 let mut encoder_settings = EncoderSettings::Audio(audio_settings);
596 let options = encoder_settings.codec_specific_options();
597
598 assert!(options.is_some());
599 assert_eq!(options.unwrap().get(c"bitrate"), Some(c"128k"));
600 }
601
602 #[test]
603 fn test_from_video_encoder_settings() {
604 let sample_aspect_ratio = AVRational { num: 1, den: 1 };
605 let video_settings = VideoEncoderSettings::builder()
606 .width(1920)
607 .height(1080)
608 .frame_rate(30.into())
609 .pixel_format(AVPixelFormat::Yuv420p)
610 .sample_aspect_ratio(sample_aspect_ratio.into())
611 .gop_size(12)
612 .build();
613 let encoder_settings: EncoderSettings = video_settings.into();
614
615 if let EncoderSettings::Video(actual_video_settings) = encoder_settings {
616 assert_eq!(actual_video_settings.width, 1920);
617 assert_eq!(actual_video_settings.height, 1080);
618 assert_eq!(actual_video_settings.frame_rate, 30.into());
619 assert_eq!(actual_video_settings.pixel_format, AVPixelFormat::Yuv420p);
620 assert_eq!(actual_video_settings.sample_aspect_ratio, Some(sample_aspect_ratio.into()));
621 assert_eq!(actual_video_settings.gop_size, Some(12));
622 } else {
623 panic!("Expected EncoderSettings::Video variant");
624 }
625 }
626
627 #[test]
628 fn test_from_audio_encoder_settings() {
629 let audio_settings = AudioEncoderSettings::builder()
630 .sample_rate(44100)
631 .sample_fmt(AVSampleFormat::Fltp)
632 .ch_layout(AudioChannelLayout::new(2).expect("channel_count is a valid value"))
633 .thread_count(4)
634 .build();
635 let encoder_settings: EncoderSettings = audio_settings.into();
636
637 if let EncoderSettings::Audio(actual_audio_settings) = encoder_settings {
638 assert_eq!(actual_audio_settings.sample_rate, 44100);
639 assert_eq!(actual_audio_settings.sample_fmt, AVSampleFormat::Fltp);
640 assert_eq!(actual_audio_settings.thread_count, Some(4));
641 } else {
642 panic!("Expected EncoderSettings::Audio variant");
643 }
644 }
645
646 #[test]
647 fn test_encoder_new_with_null_codec() {
648 let codec = EncoderCodec::empty();
649 let data = std::io::Cursor::new(Vec::new());
650 let options = OutputOptions::builder().format_name("mp4").unwrap().build();
651 let mut output = Output::new(data, options).expect("Failed to create Output");
652 let incoming_time_base = AVRational { num: 1, den: 1000 };
653 let outgoing_time_base = AVRational { num: 1, den: 1000 };
654 let settings = VideoEncoderSettings::builder()
655 .width(0)
656 .height(0)
657 .pixel_format(AVPixelFormat::Yuv420p)
658 .frame_rate(0.into())
659 .build();
660 let result = Encoder::new(codec, &mut output, incoming_time_base, outgoing_time_base, settings);
661
662 assert!(matches!(result, Err(FfmpegError::NoEncoder)));
663 }
664
665 #[test]
666 fn test_encoder_new_success() {
667 let codec = EncoderCodec::new(AVCodecID::Mpeg4);
668 assert!(codec.is_some(), "Failed to find MPEG-4 encoder");
669 let data = std::io::Cursor::new(Vec::new());
670 let options = OutputOptions::builder().format_name("mp4").unwrap().build();
671 let mut output = Output::new(data, options).expect("Failed to create Output");
672 let incoming_time_base = AVRational { num: 1, den: 1000 };
673 let outgoing_time_base = AVRational { num: 1, den: 1000 };
674 let settings = VideoEncoderSettings::builder()
675 .width(1920)
676 .height(1080)
677 .frame_rate(30.into())
678 .pixel_format(AVPixelFormat::Yuv420p)
679 .build();
680 let result = Encoder::new(codec.unwrap(), &mut output, incoming_time_base, outgoing_time_base, settings);
681
682 assert!(result.is_ok(), "Encoder creation failed: {:?}", result.err());
683
684 let encoder = result.unwrap();
685 assert_eq!(encoder.incoming_time_base, Rational::static_new::<1, 1000>());
686 assert_eq!(encoder.outgoing_time_base, Rational::static_new::<1, 1000>());
687 assert_eq!(encoder.stream_index, 0);
688 }
689
690 #[test]
691 fn test_send_eof() {
692 let codec = EncoderCodec::new(AVCodecID::Mpeg4).expect("Failed to find MPEG-4 encoder");
693 let data = std::io::Cursor::new(Vec::new());
694 let options = OutputOptions::builder().format_name("mp4").unwrap().build();
695 let mut output = Output::new(data, options).expect("Failed to create Output");
696 let video_settings = VideoEncoderSettings::builder()
697 .width(640)
698 .height(480)
699 .frame_rate(30.into())
700 .pixel_format(AVPixelFormat::Yuv420p)
701 .build();
702 let mut encoder = Encoder::new(
703 codec,
704 &mut output,
705 AVRational { num: 1, den: 1000 },
706 AVRational { num: 1, den: 1000 },
707 video_settings,
708 )
709 .expect("Failed to create encoder");
710
711 let result = encoder.send_eof();
712 assert!(result.is_ok(), "send_eof returned an error: {:?}", result.err());
713 assert!(encoder.send_eof().is_err(), "send_eof should return an error");
714 }
715
716 #[test]
717 fn test_encoder_getters() {
718 let codec = EncoderCodec::new(AVCodecID::Mpeg4).expect("Failed to find MPEG-4 encoder");
719 let data = std::io::Cursor::new(Vec::new());
720 let options = OutputOptions::builder().format_name("mp4").unwrap().build();
721 let mut output = Output::new(data, options).expect("Failed to create Output");
722 let incoming_time_base = AVRational { num: 1, den: 1000 };
723 let outgoing_time_base = AVRational { num: 1, den: 1000 };
724 let video_settings = VideoEncoderSettings::builder()
725 .width(640)
726 .height(480)
727 .frame_rate(30.into())
728 .pixel_format(AVPixelFormat::Yuv420p)
729 .build();
730 let encoder = Encoder::new(codec, &mut output, incoming_time_base, outgoing_time_base, video_settings)
731 .expect("Failed to create encoder");
732
733 let stream_index = encoder.stream_index();
734 assert_eq!(stream_index, 0, "Unexpected stream index: expected 0, got {stream_index}");
735
736 let actual_incoming_time_base = encoder.incoming_time_base();
737 assert_eq!(
738 actual_incoming_time_base,
739 incoming_time_base.into(),
740 "Unexpected incoming_time_base: expected {incoming_time_base:?}, got {actual_incoming_time_base:?}"
741 );
742
743 let actual_outgoing_time_base = encoder.outgoing_time_base();
744 assert_eq!(
745 actual_outgoing_time_base,
746 outgoing_time_base.into(),
747 "Unexpected outgoing_time_base: expected {outgoing_time_base:?}, got {actual_outgoing_time_base:?}"
748 );
749 }
750
751 #[test]
752 fn test_encoder_encode_video() {
753 let mut input = Input::open(file_path("avc_aac.mp4")).expect("Failed to open input file");
754 let streams = input.streams();
755 let video_stream = streams.best(AVMediaType::Video).expect("No video stream found");
756 let mut decoder = Decoder::new(&video_stream)
757 .expect("Failed to create decoder")
758 .video()
759 .expect("Failed to create video decoder");
760 let mut output = Output::seekable(
761 std::io::Cursor::new(Vec::new()),
762 OutputOptions::builder().format_name("mp4").unwrap().build(),
763 )
764 .expect("Failed to create Output");
765 let mut encoder = Encoder::new(
766 EncoderCodec::new(AVCodecID::Mpeg4).expect("Failed to find MPEG-4 encoder"),
767 &mut output,
768 AVRational { num: 1, den: 1000 },
769 video_stream.time_base(),
770 VideoEncoderSettings::builder()
771 .width(decoder.width())
772 .height(decoder.height())
773 .frame_rate(decoder.frame_rate())
774 .pixel_format(decoder.pixel_format())
775 .build(),
776 )
777 .expect("Failed to create encoder");
778
779 output.write_header().expect("Failed to write header");
780
781 let input_stream_index = video_stream.index();
782
783 while let Some(packet) = input.receive_packet().expect("Failed to receive packet") {
784 if packet.stream_index() == input_stream_index {
785 decoder.send_packet(&packet).expect("Failed to send packet");
786 while let Some(frame) = decoder.receive_frame().expect("Failed to receive frame") {
787 encoder.send_frame(&frame).expect("Failed to send frame");
788 while let Some(packet) = encoder.receive_packet().expect("Failed to receive packet") {
789 output.write_packet(&packet).expect("Failed to write packet");
790 }
791 }
792 }
793 }
794
795 encoder.send_eof().expect("Failed to send EOF");
796 while let Some(packet) = encoder.receive_packet().expect("Failed to receive packet") {
797 output.write_packet(&packet).expect("Failed to write packet");
798 }
799
800 output.write_trailer().expect("Failed to write trailer");
801
802 let data = output.into_inner().into_inner();
803 let mut reader = scuffle_bytes_util::zero_copy::Slice::from(&data[..]);
804
805 let mut boxes = Vec::new();
806 loop {
807 let Some(mut any_box) = isobmff::UnknownBox::deserialize(&mut reader)
808 .eof_to_none()
809 .expect("failed to demux box")
810 else {
811 break;
812 };
813
814 if any_box.header.box_type.is_four_cc(b"mdat") || any_box.header.box_type.is_four_cc(b"moov") {
815 any_box.data = BytesCow::new();
816 }
817
818 boxes.push(any_box);
819 }
820 insta::assert_debug_snapshot!("test_encoder_encode_video", &boxes);
821 }
822
823 #[test]
825 fn test_pr_248() {
826 let mut output = Output::seekable(
827 std::io::Cursor::new(Vec::new()),
828 OutputOptions::builder().format_name("mp4").unwrap().build(),
829 )
830 .expect("Failed to create Output");
831
832 let mut settings = Dictionary::new();
833 settings.set(c"key", c"value").expect("Failed to set Dictionary entry");
834
835 let codec = EncoderCodec::new(AVCodecID::Mpeg4).expect("Missing MPEG-4 codec");
836
837 Encoder::new(
838 codec,
839 &mut output,
840 AVRational { num: 1, den: 100 },
841 AVRational { num: 1, den: 100 },
842 VideoEncoderSettings::builder()
843 .width(16)
844 .height(16)
845 .frame_rate(30.into())
846 .pixel_format(AVPixelFormat::Yuv420p)
847 .codec_specific_options(settings)
848 .build(),
849 )
850 .expect("Failed to create new Encoder");
851 }
852}