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: 0,
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 std::io::Write;
301
302 use bytes::{Buf, Bytes};
303 use rusty_ffmpeg::ffi::AVRational;
304 use sha2::Digest;
305
306 use crate::codec::EncoderCodec;
307 use crate::decoder::Decoder;
308 use crate::dict::Dictionary;
309 use crate::encoder::{AudioChannelLayout, AudioEncoderSettings, Encoder, EncoderSettings, VideoEncoderSettings};
310 use crate::error::FfmpegError;
311 use crate::ffi::AVCodecContext;
312 use crate::io::{Input, Output, OutputOptions};
313 use crate::rational::Rational;
314 use crate::{AVChannelOrder, AVCodecID, AVMediaType, AVPixelFormat, AVSampleFormat};
315
316 #[test]
317 fn test_video_encoder_apply() {
318 let width = 1920;
319 let height = 1080;
320 let frame_rate = 30;
321 let pixel_format = AVPixelFormat::Yuv420p;
322 let sample_aspect_ratio = 1;
323 let gop_size = 12;
324 let qmax = 31;
325 let qmin = 1;
326 let thread_count = 4;
327 let thread_type = 2;
328 let bitrate = 8_000;
329 let rc_min_rate = 500_000;
330 let rc_max_rate = 2_000_000;
331 let rc_buffer_size = 1024;
332 let max_b_frames = 3;
333 let mut codec_specific_options = Dictionary::new();
334 codec_specific_options.set("preset", "ultrafast").unwrap();
335 codec_specific_options.set("crf", "23").unwrap();
336 let flags = 0x01;
337 let flags2 = 0x02;
338
339 let settings = VideoEncoderSettings::builder()
340 .width(width)
341 .height(height)
342 .frame_rate(frame_rate.into())
343 .pixel_format(pixel_format)
344 .sample_aspect_ratio(sample_aspect_ratio.into())
345 .gop_size(gop_size)
346 .qmax(qmax)
347 .qmin(qmin)
348 .thread_count(thread_count)
349 .thread_type(thread_type)
350 .bitrate(bitrate)
351 .rc_min_rate(rc_min_rate)
352 .rc_max_rate(rc_max_rate)
353 .rc_buffer_size(rc_buffer_size)
354 .max_b_frames(max_b_frames)
355 .codec_specific_options(codec_specific_options)
356 .flags(flags)
357 .flags2(flags2)
358 .build();
359
360 assert_eq!(settings.width, width);
361 assert_eq!(settings.height, height);
362 assert_eq!(settings.frame_rate, frame_rate.into());
363 assert_eq!(settings.pixel_format, pixel_format);
364 assert_eq!(settings.sample_aspect_ratio, Some(sample_aspect_ratio.into()));
365 assert_eq!(settings.gop_size, Some(gop_size));
366 assert_eq!(settings.qmax, Some(qmax));
367 assert_eq!(settings.qmin, Some(qmin));
368 assert_eq!(settings.thread_count, Some(thread_count));
369 assert_eq!(settings.thread_type, Some(thread_type));
370 assert_eq!(settings.bitrate, Some(bitrate));
371 assert_eq!(settings.rc_min_rate, Some(rc_min_rate));
372 assert_eq!(settings.rc_max_rate, Some(rc_max_rate));
373 assert_eq!(settings.rc_buffer_size, Some(rc_buffer_size));
374 assert_eq!(settings.max_b_frames, Some(max_b_frames));
375 assert!(settings.codec_specific_options.is_some());
376 let actual_codec_specific_options = settings.codec_specific_options.as_ref().unwrap();
377 assert_eq!(actual_codec_specific_options.get(c"preset"), Some(c"ultrafast"));
378 assert_eq!(actual_codec_specific_options.get(c"crf"), Some(c"23"));
379 assert_eq!(settings.flags, Some(flags));
380 assert_eq!(settings.flags2, Some(flags2));
381
382 let mut encoder = unsafe { std::mem::zeroed::<AVCodecContext>() };
384 let result = settings.apply(&mut encoder);
385 assert!(result.is_ok(), "Failed to apply settings: {:?}", result.err());
386
387 assert_eq!(encoder.width, width);
388 assert_eq!(encoder.height, height);
389 assert_eq!(AVPixelFormat(encoder.pix_fmt), pixel_format);
390 assert_eq!(Rational::from(encoder.sample_aspect_ratio), sample_aspect_ratio.into());
391 assert_eq!(Rational::from(encoder.framerate), frame_rate.into());
392 assert_eq!(encoder.thread_count, thread_count);
393 assert_eq!(encoder.thread_type, thread_type);
394 assert_eq!(encoder.gop_size, gop_size);
395 assert_eq!(encoder.qmax, qmax);
396 assert_eq!(encoder.qmin, qmin);
397 assert_eq!(encoder.bit_rate, bitrate);
398 assert_eq!(encoder.rc_min_rate, rc_min_rate);
399 assert_eq!(encoder.rc_max_rate, rc_max_rate);
400 assert_eq!(encoder.rc_buffer_size, rc_buffer_size);
401 assert_eq!(encoder.max_b_frames, max_b_frames);
402 assert_eq!(encoder.flags, flags);
403 assert_eq!(encoder.flags2, flags2);
404 }
405
406 #[test]
407 fn test_video_encoder_settings_apply_error() {
408 let settings = VideoEncoderSettings::builder()
409 .width(0)
410 .height(0)
411 .pixel_format(AVPixelFormat::Yuv420p)
412 .frame_rate(0.into())
413 .build();
414 let mut encoder = unsafe { std::mem::zeroed::<AVCodecContext>() };
416 let result = settings.apply(&mut encoder);
417
418 assert!(result.is_err());
419 assert_eq!(
420 result.unwrap_err(),
421 FfmpegError::Arguments("width, height, frame_rate and pixel_format must be set")
422 );
423 }
424
425 #[test]
426 fn test_audio_encoder_apply() {
427 let sample_rate = 44100;
428 let channel_count = 2;
429 let sample_fmt = AVSampleFormat::S16;
430 let thread_count = 4;
431 let thread_type = 1;
432 let bitrate = 128_000;
433 let rc_min_rate = 64_000;
434 let rc_max_rate = 256_000;
435 let rc_buffer_size = 1024;
436 let flags = 0x01;
437 let flags2 = 0x02;
438
439 let mut codec_specific_options = Dictionary::new();
440 codec_specific_options
441 .set(c"profile", c"high")
442 .expect("Failed to set profile");
443
444 let settings = AudioEncoderSettings::builder()
445 .sample_rate(sample_rate)
446 .ch_layout(AudioChannelLayout::new(channel_count).expect("channel_count is a valid value"))
447 .sample_fmt(sample_fmt)
448 .thread_count(thread_count)
449 .thread_type(thread_type)
450 .bitrate(bitrate)
451 .rc_min_rate(rc_min_rate)
452 .rc_max_rate(rc_max_rate)
453 .rc_buffer_size(rc_buffer_size)
454 .codec_specific_options(codec_specific_options)
455 .flags(flags)
456 .flags2(flags2)
457 .build();
458
459 assert_eq!(settings.sample_rate, sample_rate);
460 assert_eq!(settings.ch_layout.channel_count(), 2);
461 assert_eq!(settings.sample_fmt, sample_fmt);
462 assert_eq!(settings.thread_count, Some(thread_count));
463 assert_eq!(settings.thread_type, Some(thread_type));
464 assert_eq!(settings.bitrate, Some(bitrate));
465 assert_eq!(settings.rc_min_rate, Some(rc_min_rate));
466 assert_eq!(settings.rc_max_rate, Some(rc_max_rate));
467 assert_eq!(settings.rc_buffer_size, Some(rc_buffer_size));
468 assert!(settings.codec_specific_options.is_some());
469
470 let actual_codec_specific_options = settings.codec_specific_options.unwrap();
471 assert_eq!(actual_codec_specific_options.get(c"profile"), Some(c"high"));
472
473 assert_eq!(settings.flags, Some(flags));
474 assert_eq!(settings.flags2, Some(flags2));
475 }
476
477 #[test]
478 fn test_ch_layout_valid_layout() {
479 let channel_layout = unsafe {
481 AudioChannelLayout::wrap(crate::ffi::AVChannelLayout {
482 order: AVChannelOrder::Native.into(),
483 nb_channels: 2,
484 u: crate::ffi::AVChannelLayout__bindgen_ty_1 { mask: 0b11 },
485 opaque: std::ptr::null_mut(),
486 })
487 };
488
489 channel_layout.validate().expect("channel_layout is a valid value");
490 }
491
492 #[test]
493 fn test_ch_layout_invalid_layout() {
494 let channel_layout = unsafe {
496 AudioChannelLayout::wrap(crate::ffi::AVChannelLayout {
497 order: AVChannelOrder::Unspecified.into(),
498 nb_channels: 0,
499 u: crate::ffi::AVChannelLayout__bindgen_ty_1 { mask: 0 },
500 opaque: std::ptr::null_mut(),
501 })
502 };
503 let result: Result<(), FfmpegError> = channel_layout.validate();
504 assert_eq!(result.unwrap_err(), FfmpegError::Arguments("invalid channel layout"));
505 }
506
507 #[test]
508 fn test_audio_encoder_settings_apply_error() {
509 let settings = AudioEncoderSettings::builder()
510 .sample_rate(0)
511 .sample_fmt(AVSampleFormat::None)
512 .ch_layout(AudioChannelLayout::new(2).expect("channel_count is a valid value"))
513 .build();
514
515 let mut encoder = unsafe { std::mem::zeroed::<AVCodecContext>() };
517 let result = settings.apply(&mut encoder);
518
519 assert!(result.is_err());
520 assert_eq!(
521 result.unwrap_err(),
522 FfmpegError::Arguments("sample_rate, channel_layout and sample_fmt must be set")
523 );
524 }
525
526 #[test]
527 fn test_encoder_settings_apply_video() {
528 let sample_aspect_ratio = AVRational { num: 1, den: 1 };
529 let video_settings = VideoEncoderSettings::builder()
530 .width(1920)
531 .height(1080)
532 .frame_rate(30.into())
533 .pixel_format(AVPixelFormat::Yuv420p)
534 .sample_aspect_ratio(sample_aspect_ratio.into())
535 .gop_size(12)
536 .build();
537
538 let mut encoder = unsafe { std::mem::zeroed::<AVCodecContext>() };
540 let encoder_settings = EncoderSettings::Video(video_settings);
541 let result = encoder_settings.apply(&mut encoder);
542
543 assert!(result.is_ok(), "Failed to apply video settings: {:?}", result.err());
544 assert_eq!(encoder.width, 1920);
545 assert_eq!(encoder.height, 1080);
546 assert_eq!(AVPixelFormat(encoder.pix_fmt), AVPixelFormat::Yuv420p);
547 assert_eq!(Rational::from(encoder.sample_aspect_ratio), sample_aspect_ratio.into());
548 }
549
550 #[test]
551 fn test_encoder_settings_apply_audio() {
552 let audio_settings = AudioEncoderSettings::builder()
553 .sample_rate(44100)
554 .sample_fmt(AVSampleFormat::Fltp)
555 .ch_layout(AudioChannelLayout::new(2).expect("channel_count is a valid value"))
556 .thread_count(4)
557 .build();
558
559 let mut encoder = unsafe { std::mem::zeroed::<AVCodecContext>() };
561 let encoder_settings = EncoderSettings::Audio(audio_settings);
562 let result = encoder_settings.apply(&mut encoder);
563
564 assert!(result.is_ok(), "Failed to apply audio settings: {:?}", result.err());
565 assert_eq!(encoder.sample_rate, 44100);
566 assert_eq!(AVSampleFormat(encoder.sample_fmt), AVSampleFormat::Fltp);
567 assert_eq!(encoder.thread_count, 4);
568 }
569
570 #[test]
571 fn test_encoder_settings_codec_specific_options() {
572 let mut video_codec_options = Dictionary::new();
573 video_codec_options.set(c"preset", c"fast").expect("Failed to set preset");
574
575 let video_settings = VideoEncoderSettings::builder()
576 .width(8)
577 .height(8)
578 .frame_rate(30.into())
579 .pixel_format(AVPixelFormat::Yuv420p)
580 .codec_specific_options(video_codec_options.clone())
581 .build();
582 let mut encoder_settings = EncoderSettings::Video(video_settings);
583 let options = encoder_settings.codec_specific_options();
584
585 assert!(options.is_some());
586 assert_eq!(options.unwrap().get(c"preset"), Some(c"fast"));
587
588 let mut audio_codec_options = Dictionary::new();
589 audio_codec_options.set(c"bitrate", c"128k").expect("Failed to set bitrate");
590 let audio_settings = AudioEncoderSettings::builder()
591 .sample_rate(44100)
592 .sample_fmt(AVSampleFormat::Fltp)
593 .ch_layout(AudioChannelLayout::new(2).expect("channel_count is a valid value"))
594 .thread_count(4)
595 .codec_specific_options(audio_codec_options)
596 .build();
597 let mut encoder_settings = EncoderSettings::Audio(audio_settings);
598 let options = encoder_settings.codec_specific_options();
599
600 assert!(options.is_some());
601 assert_eq!(options.unwrap().get(c"bitrate"), Some(c"128k"));
602 }
603
604 #[test]
605 fn test_from_video_encoder_settings() {
606 let sample_aspect_ratio = AVRational { num: 1, den: 1 };
607 let video_settings = VideoEncoderSettings::builder()
608 .width(1920)
609 .height(1080)
610 .frame_rate(30.into())
611 .pixel_format(AVPixelFormat::Yuv420p)
612 .sample_aspect_ratio(sample_aspect_ratio.into())
613 .gop_size(12)
614 .build();
615 let encoder_settings: EncoderSettings = video_settings.into();
616
617 if let EncoderSettings::Video(actual_video_settings) = encoder_settings {
618 assert_eq!(actual_video_settings.width, 1920);
619 assert_eq!(actual_video_settings.height, 1080);
620 assert_eq!(actual_video_settings.frame_rate, 30.into());
621 assert_eq!(actual_video_settings.pixel_format, AVPixelFormat::Yuv420p);
622 assert_eq!(actual_video_settings.sample_aspect_ratio, Some(sample_aspect_ratio.into()));
623 assert_eq!(actual_video_settings.gop_size, Some(12));
624 } else {
625 panic!("Expected EncoderSettings::Video variant");
626 }
627 }
628
629 #[test]
630 fn test_from_audio_encoder_settings() {
631 let audio_settings = AudioEncoderSettings::builder()
632 .sample_rate(44100)
633 .sample_fmt(AVSampleFormat::Fltp)
634 .ch_layout(AudioChannelLayout::new(2).expect("channel_count is a valid value"))
635 .thread_count(4)
636 .build();
637 let encoder_settings: EncoderSettings = audio_settings.into();
638
639 if let EncoderSettings::Audio(actual_audio_settings) = encoder_settings {
640 assert_eq!(actual_audio_settings.sample_rate, 44100);
641 assert_eq!(actual_audio_settings.sample_fmt, AVSampleFormat::Fltp);
642 assert_eq!(actual_audio_settings.thread_count, Some(4));
643 } else {
644 panic!("Expected EncoderSettings::Audio variant");
645 }
646 }
647
648 #[test]
649 fn test_encoder_new_with_null_codec() {
650 let codec = EncoderCodec::empty();
651 let data = std::io::Cursor::new(Vec::new());
652 let options = OutputOptions::builder().format_name("mp4").unwrap().build();
653 let mut output = Output::new(data, options).expect("Failed to create Output");
654 let incoming_time_base = AVRational { num: 1, den: 1000 };
655 let outgoing_time_base = AVRational { num: 1, den: 1000 };
656 let settings = VideoEncoderSettings::builder()
657 .width(0)
658 .height(0)
659 .pixel_format(AVPixelFormat::Yuv420p)
660 .frame_rate(0.into())
661 .build();
662 let result = Encoder::new(codec, &mut output, incoming_time_base, outgoing_time_base, settings);
663
664 assert!(matches!(result, Err(FfmpegError::NoEncoder)));
665 }
666
667 #[test]
668 fn test_encoder_new_success() {
669 let codec = EncoderCodec::new(AVCodecID::Mpeg4);
670 assert!(codec.is_some(), "Failed to find MPEG-4 encoder");
671 let data = std::io::Cursor::new(Vec::new());
672 let options = OutputOptions::builder().format_name("mp4").unwrap().build();
673 let mut output = Output::new(data, options).expect("Failed to create Output");
674 let incoming_time_base = AVRational { num: 1, den: 1000 };
675 let outgoing_time_base = AVRational { num: 1, den: 1000 };
676 let settings = VideoEncoderSettings::builder()
677 .width(1920)
678 .height(1080)
679 .frame_rate(30.into())
680 .pixel_format(AVPixelFormat::Yuv420p)
681 .build();
682 let result = Encoder::new(codec.unwrap(), &mut output, incoming_time_base, outgoing_time_base, settings);
683
684 assert!(result.is_ok(), "Encoder creation failed: {:?}", result.err());
685
686 let encoder = result.unwrap();
687 assert_eq!(encoder.incoming_time_base, Rational::static_new::<1, 1000>());
688 assert_eq!(encoder.outgoing_time_base, Rational::static_new::<1, 1000>());
689 assert_eq!(encoder.stream_index, 0);
690 }
691
692 #[test]
693 fn test_send_eof() {
694 let codec = EncoderCodec::new(AVCodecID::Mpeg4).expect("Failed to find MPEG-4 encoder");
695 let data = std::io::Cursor::new(Vec::new());
696 let options = OutputOptions::builder().format_name("mp4").unwrap().build();
697 let mut output = Output::new(data, options).expect("Failed to create Output");
698 let video_settings = VideoEncoderSettings::builder()
699 .width(640)
700 .height(480)
701 .frame_rate(30.into())
702 .pixel_format(AVPixelFormat::Yuv420p)
703 .build();
704 let mut encoder = Encoder::new(
705 codec,
706 &mut output,
707 AVRational { num: 1, den: 1000 },
708 AVRational { num: 1, den: 1000 },
709 video_settings,
710 )
711 .expect("Failed to create encoder");
712
713 let result = encoder.send_eof();
714 assert!(result.is_ok(), "send_eof returned an error: {:?}", result.err());
715 assert!(encoder.send_eof().is_err(), "send_eof should return an error");
716 }
717
718 #[test]
719 fn test_encoder_getters() {
720 let codec = EncoderCodec::new(AVCodecID::Mpeg4).expect("Failed to find MPEG-4 encoder");
721 let data = std::io::Cursor::new(Vec::new());
722 let options = OutputOptions::builder().format_name("mp4").unwrap().build();
723 let mut output = Output::new(data, options).expect("Failed to create Output");
724 let incoming_time_base = AVRational { num: 1, den: 1000 };
725 let outgoing_time_base = AVRational { num: 1, den: 1000 };
726 let video_settings = VideoEncoderSettings::builder()
727 .width(640)
728 .height(480)
729 .frame_rate(30.into())
730 .pixel_format(AVPixelFormat::Yuv420p)
731 .build();
732 let encoder = Encoder::new(codec, &mut output, incoming_time_base, outgoing_time_base, video_settings)
733 .expect("Failed to create encoder");
734
735 let stream_index = encoder.stream_index();
736 assert_eq!(stream_index, 0, "Unexpected stream index: expected 0, got {stream_index}");
737
738 let actual_incoming_time_base = encoder.incoming_time_base();
739 assert_eq!(
740 actual_incoming_time_base,
741 incoming_time_base.into(),
742 "Unexpected incoming_time_base: expected {incoming_time_base:?}, got {actual_incoming_time_base:?}"
743 );
744
745 let actual_outgoing_time_base = encoder.outgoing_time_base();
746 assert_eq!(
747 actual_outgoing_time_base,
748 outgoing_time_base.into(),
749 "Unexpected outgoing_time_base: expected {outgoing_time_base:?}, got {actual_outgoing_time_base:?}"
750 );
751 }
752
753 #[test]
754 fn test_encoder_encode_video() {
755 let mut input = Input::open("../../assets/avc_aac.mp4").expect("Failed to open input file");
756 let streams = input.streams();
757 let video_stream = streams.best(AVMediaType::Video).expect("No video stream found");
758 let mut decoder = Decoder::new(&video_stream)
759 .expect("Failed to create decoder")
760 .video()
761 .expect("Failed to create video decoder");
762 let mut output = Output::seekable(
763 std::io::Cursor::new(Vec::new()),
764 OutputOptions::builder().format_name("mp4").unwrap().build(),
765 )
766 .expect("Failed to create Output");
767 let mut encoder = Encoder::new(
768 EncoderCodec::new(AVCodecID::Mpeg4).expect("Failed to find MPEG-4 encoder"),
769 &mut output,
770 AVRational { num: 1, den: 1000 },
771 video_stream.time_base(),
772 VideoEncoderSettings::builder()
773 .width(decoder.width())
774 .height(decoder.height())
775 .frame_rate(decoder.frame_rate())
776 .pixel_format(decoder.pixel_format())
777 .build(),
778 )
779 .expect("Failed to create encoder");
780
781 output.write_header().expect("Failed to write header");
782
783 let input_stream_index = video_stream.index();
784
785 while let Some(packet) = input.receive_packet().expect("Failed to receive packet") {
786 if packet.stream_index() == input_stream_index {
787 decoder.send_packet(&packet).expect("Failed to send packet");
788 while let Some(frame) = decoder.receive_frame().expect("Failed to receive frame") {
789 encoder.send_frame(&frame).expect("Failed to send frame");
790 while let Some(packet) = encoder.receive_packet().expect("Failed to receive packet") {
791 output.write_packet(&packet).expect("Failed to write packet");
792 }
793 }
794 }
795 }
796
797 encoder.send_eof().expect("Failed to send EOF");
798 while let Some(packet) = encoder.receive_packet().expect("Failed to receive packet") {
799 output.write_packet(&packet).expect("Failed to write packet");
800 }
801
802 output.write_trailer().expect("Failed to write trailer");
803
804 let mut cursor = std::io::Cursor::new(Bytes::from(output.into_inner().into_inner()));
805 let mut boxes = Vec::new();
806 while cursor.has_remaining() {
807 let mut _box = scuffle_mp4::DynBox::demux(&mut cursor).expect("Failed to demux box");
808 match &mut _box {
809 scuffle_mp4::DynBox::Mdat(mdat) => {
810 mdat.data.iter_mut().for_each(|buf| {
811 let mut hash = sha2::Sha256::new();
812 hash.write_all(buf).unwrap();
813 *buf = Bytes::new();
814 });
815 }
816 scuffle_mp4::DynBox::Moov(moov) => {
817 moov.traks.iter_mut().for_each(|trak| {
818 trak.mdia.minf.stbl.stsd.entries.clear();
820 if let Some(stsz) = trak.mdia.minf.stbl.stsz.as_mut() {
821 stsz.samples.clear();
822 }
823 trak.mdia.minf.stbl.stco.entries.clear();
824 });
825 }
826 _ => {}
827 }
828 boxes.push(_box);
829 }
830 insta::assert_debug_snapshot!("test_encoder_encode_video", &boxes);
831 }
832
833 #[test]
835 fn test_pr_248() {
836 let mut output = Output::seekable(
837 std::io::Cursor::new(Vec::new()),
838 OutputOptions::builder().format_name("mp4").unwrap().build(),
839 )
840 .expect("Failed to create Output");
841
842 let mut settings = Dictionary::new();
843 settings.set(c"key", c"value").expect("Failed to set Dictionary entry");
844
845 let codec = EncoderCodec::new(AVCodecID::Mpeg4).expect("Missing MPEG-4 codec");
846
847 Encoder::new(
848 codec,
849 &mut output,
850 AVRational { num: 1, den: 100 },
851 AVRational { num: 1, den: 100 },
852 VideoEncoderSettings::builder()
853 .width(16)
854 .height(16)
855 .frame_rate(30.into())
856 .pixel_format(AVPixelFormat::Yuv420p)
857 .codec_specific_options(settings)
858 .build(),
859 )
860 .expect("Failed to create new Encoder");
861 }
862}