1use std::{
2 ffi::{c_void, CStr},
3 os::raw::c_int,
4 ptr::{self, NonNull},
5};
6
7use crate::{
8 avcodec::{
9 AVCodecParameters, AVCodecParametersMut, AVCodecParametersRef, AVCodecRef, AVPacket,
10 },
11 avformat::{AVIOContext, AVIOContextCustom, AVIOContextURL},
12 avutil::{AVDictionary, AVDictionaryMut, AVDictionaryRef, AVRational},
13 error::{Result, RsmpegError},
14 ffi,
15 shared::*,
16};
17
18pub enum AVIOContextContainer {
20 Url(AVIOContextURL),
21 Custom(AVIOContextCustom),
22}
23
24impl AVIOContextContainer {
25 pub(crate) fn as_mut_ptr(&mut self) -> *mut ffi::AVIOContext {
27 match self {
28 Self::Url(ctx) => ctx.as_mut_ptr(),
29 Self::Custom(ctx) => ctx.as_mut_ptr(),
30 }
31 }
32}
33
34wrap! {
35 AVFormatContextInput: ffi::AVFormatContext,
36 io_context: Option<AVIOContextContainer> = None,
37}
38
39#[bon::bon]
40impl AVFormatContextInput {
41 #[builder(finish_fn = open)]
61 pub fn builder(
62 url: Option<&CStr>,
64 format: Option<&AVInputFormat>,
66 options: Option<&mut Option<AVDictionary>>,
68 mut io_context: Option<AVIOContextContainer>,
70 ) -> Result<Self> {
71 let mut input_format_context = {
72 let input_format_context = unsafe { ffi::avformat_alloc_context() };
73
74 if let Some(io_context) = io_context.as_mut() {
75 unsafe {
76 (*input_format_context).pb = match io_context {
77 AVIOContextContainer::Url(ctx) => ctx.as_mut_ptr(),
78 AVIOContextContainer::Custom(ctx) => ctx.as_mut_ptr(),
79 };
80 }
81 };
82
83 input_format_context
84 };
85
86 let url_ptr = url.map(|x| x.as_ptr()).unwrap_or(std::ptr::null());
87 let fmt_ptr = format.map(|x| x.as_ptr()).unwrap_or_else(std::ptr::null);
88
89 let mut dummy_options = None;
90 let options = options.unwrap_or(&mut dummy_options);
91 let mut options_ptr = options
92 .as_mut()
93 .map(|x| x.as_mut_ptr())
94 .unwrap_or_else(std::ptr::null_mut);
95
96 unsafe {
97 ffi::avformat_open_input(
98 &mut input_format_context,
99 url_ptr,
100 fmt_ptr,
101 &mut options_ptr,
102 )
103 }
104 .upgrade()
105 .map_err(RsmpegError::OpenInputError)?;
106
107 let mut new_options = options_ptr
109 .upgrade()
110 .map(|x| unsafe { AVDictionary::from_raw(x) });
111 std::mem::swap(options, &mut new_options);
112 std::mem::forget(new_options);
113
114 let mut context = unsafe { Self::from_raw(NonNull::new(input_format_context).unwrap()) };
117 context.io_context = io_context;
118
119 unsafe { ffi::avformat_find_stream_info(context.as_mut_ptr(), ptr::null_mut()) }
120 .upgrade()
121 .map_err(RsmpegError::FindStreamInfoError)?;
122
123 Ok(context)
124 }
125
126 pub fn open(url: &CStr) -> Result<Self> {
133 Self::builder().url(url).open()
134 }
135
136 pub fn from_io_context(io_context: AVIOContextContainer) -> Result<Self> {
141 Self::builder().io_context(io_context).open()
142 }
143
144 pub fn dump(&mut self, index: usize, filename: &CStr) -> Result<()> {
148 unsafe {
149 ffi::av_dump_format(self.as_mut_ptr(), index as i32, filename.as_ptr(), 0);
151 }
152 Ok(())
153 }
154
155 pub fn read_packet(&mut self) -> Result<Option<AVPacket>> {
164 let mut packet = AVPacket::new();
165 match unsafe { ffi::av_read_frame(self.as_mut_ptr(), packet.as_mut_ptr()) }.upgrade() {
166 Ok(_) => Ok(Some(packet)),
167 Err(ffi::AVERROR_EOF) => Ok(None),
168 Err(x) => Err(x)?,
169 }
170 }
171
172 pub fn seek(&mut self, stream_index: i32, timestamp: i64, flags: i32) -> Result<()> {
174 unsafe { ffi::av_seek_frame(self.as_mut_ptr(), stream_index, timestamp, flags) }
175 .upgrade()?;
176 Ok(())
177 }
178
179 pub fn find_best_stream(
182 &self,
183 media_type: ffi::AVMediaType,
184 ) -> Result<Option<(usize, AVCodecRef<'static>)>> {
185 let dec = ptr::null_mut();
188 let mut dec = dec as _;
189
190 match unsafe {
195 ffi::av_find_best_stream(self.as_ptr() as *mut _, media_type, -1, -1, &mut dec, 0)
196 }
197 .upgrade()
198 {
199 Ok(index) => Ok(Some((index as usize, unsafe {
200 AVCodecRef::from_raw(NonNull::new(dec as *mut _).unwrap())
201 }))),
202 Err(ffi::AVERROR_STREAM_NOT_FOUND) => Ok(None),
203 Err(e) => Err(RsmpegError::AVError(e)),
204 }
205 }
206}
207
208impl<'stream> AVFormatContextInput {
209 pub fn streams(&'stream self) -> &'stream [AVStreamRef<'stream>] {
211 let streams = self.streams as *const *const ffi::AVStream as *const AVStreamRef<'stream>;
219 let len = self.nb_streams as usize;
221
222 #[cfg(debug_assertions)]
224 {
225 let arr = unsafe {
226 std::slice::from_raw_parts(self.streams as *const *const ffi::AVStream, len)
227 };
228 for ptr in arr {
229 assert!(!ptr.is_null());
230 }
231 }
232
233 unsafe { std::slice::from_raw_parts(streams, len) }
234 }
235
236 pub fn streams_mut(&'stream mut self) -> &'stream mut [AVStreamMut<'stream>] {
238 let streams = self.streams as *mut AVStreamMut<'stream>;
246 let len = self.nb_streams as usize;
248
249 #[cfg(debug_assertions)]
251 {
252 let arr = unsafe {
253 std::slice::from_raw_parts(self.streams as *const *const ffi::AVStream, len)
254 };
255 for ptr in arr {
256 assert!(!ptr.is_null());
257 }
258 }
259
260 unsafe { std::slice::from_raw_parts_mut(streams, len) }
261 }
262
263 pub fn iformat(&'stream self) -> AVInputFormatRef<'stream> {
265 unsafe { AVInputFormatRef::from_raw(NonNull::new(self.iformat as *mut _).unwrap()) }
268 }
269
270 pub fn metadata(&'stream self) -> Option<AVDictionaryRef<'stream>> {
274 NonNull::new(self.metadata).map(|x| unsafe { AVDictionaryRef::from_raw(x) })
278 }
279}
280
281impl Drop for AVFormatContextInput {
282 fn drop(&mut self) {
283 let mut context = self.as_mut_ptr();
284 unsafe { ffi::avformat_close_input(&mut context) }
285 }
286}
287
288wrap! {
289 AVFormatContextOutput: ffi::AVFormatContext,
290 io_context: Option<AVIOContextContainer> = None,
291}
292
293impl AVFormatContextOutput {
294 pub fn create(filename: &CStr) -> Result<Self> {
297 Self::builder().filename(filename).build()
298 }
299}
300
301#[bon::bon]
302impl AVFormatContextOutput {
303 #[builder]
305 pub fn builder(
306 oformat: Option<&AVOutputFormat>,
307 format_name: Option<&CStr>,
308 filename: Option<&CStr>,
309 options: Option<&mut Option<AVDictionary>>,
310 io_context: Option<AVIOContextContainer>,
311 ) -> Result<Self> {
312 let mut output_format_context = ptr::null_mut();
313 let oformat = oformat
314 .map(AVOutputFormat::as_ptr)
315 .unwrap_or_else(|| ptr::null_mut());
316 let format_name = format_name
317 .map(CStr::as_ptr)
318 .unwrap_or_else(|| ptr::null_mut());
319 let filename_ptr = filename
320 .map(CStr::as_ptr)
321 .unwrap_or_else(|| ptr::null_mut());
322
323 unsafe {
325 ffi::avformat_alloc_output_context2(
326 &mut output_format_context,
327 oformat,
328 format_name,
329 filename_ptr,
330 )
331 }
332 .upgrade()?;
333
334 let mut output_format_context =
335 unsafe { Self::from_raw(NonNull::new(output_format_context).unwrap()) };
336
337 if output_format_context.oformat().flags & ffi::AVFMT_NOFILE as i32 == 0 {
345 let io_context = if let Some(io_context) = io_context {
347 Some(io_context)
348 } else if let Some(filename) = filename {
349 Some(AVIOContextContainer::Url(AVIOContextURL::open(
350 filename,
351 ffi::AVIO_FLAG_WRITE,
352 options,
353 )?))
354 } else {
355 None
356 };
357 if let Some(mut io_context) = io_context {
358 unsafe {
359 output_format_context.deref_mut().pb = io_context.as_mut_ptr();
360 }
361 output_format_context.io_context = Some(io_context);
362 }
363 }
364
365 Ok(output_format_context)
366 }
367
368 pub fn write_header(&mut self, dict: &mut Option<AVDictionary>) -> Result<()> {
376 let mut dict_ptr = dict
377 .take()
378 .map(|x| x.into_raw().as_ptr())
379 .unwrap_or_else(ptr::null_mut);
380
381 let result = unsafe { ffi::avformat_write_header(self.as_mut_ptr(), &mut dict_ptr as _) };
382
383 *dict = dict_ptr
385 .upgrade()
386 .map(|x| unsafe { AVDictionary::from_raw(x) });
387
388 result.upgrade()?;
389
390 Ok(())
391 }
392
393 pub fn write_trailer(&mut self) -> Result<()> {
396 unsafe { ffi::av_write_trailer(self.as_mut_ptr()) }.upgrade()?;
397 Ok(())
398 }
399
400 pub fn dump(&mut self, index: i32, filename: &CStr) -> Result<()> {
404 unsafe {
405 ffi::av_dump_format(self.as_mut_ptr(), index, filename.as_ptr(), 1);
407 }
408 Ok(())
409 }
410
411 pub fn write_frame(&mut self, packet: &mut AVPacket) -> Result<()> {
419 unsafe { ffi::av_write_frame(self.as_mut_ptr(), packet.as_mut_ptr()) }.upgrade()?;
420 Ok(())
421 }
422
423 pub fn interleaved_write_frame(&mut self, packet: &mut AVPacket) -> Result<()> {
430 unsafe { ffi::av_interleaved_write_frame(self.as_mut_ptr(), packet.as_mut_ptr()) }
431 .upgrade()?;
432 Ok(())
433 }
434}
435
436impl<'stream> AVFormatContextOutput {
437 pub fn streams(&'stream self) -> &'stream [AVStreamRef<'stream>] {
439 let streams = self.streams as *const *const ffi::AVStream as *const AVStreamRef<'stream>;
447 let len = self.nb_streams as usize;
449
450 #[cfg(debug_assertions)]
452 {
453 let arr = unsafe {
454 std::slice::from_raw_parts(self.streams as *const *const ffi::AVStream, len)
455 };
456 for ptr in arr {
457 assert!(!ptr.is_null());
458 }
459 }
460
461 unsafe { std::slice::from_raw_parts(streams, len) }
462 }
463
464 pub fn oformat(&self) -> AVOutputFormatRef<'static> {
466 unsafe { AVOutputFormatRef::from_raw(NonNull::new(self.oformat as *mut _).unwrap()) }
469 }
470
471 pub fn set_oformat(&mut self, format: AVOutputFormatRef<'static>) {
473 unsafe {
475 self.deref_mut().oformat = format.as_ptr() as _;
476 }
477 }
478
479 pub fn new_stream(&'stream mut self) -> AVStreamMut<'stream> {
482 let new_stream = unsafe { ffi::avformat_new_stream(self.as_mut_ptr(), ptr::null()) }
485 .upgrade()
486 .unwrap();
487
488 unsafe { AVStreamMut::from_raw(new_stream) }
489 }
490}
491
492impl Drop for AVFormatContextOutput {
493 fn drop(&mut self) {
494 if unsafe { *self.oformat }.flags & ffi::AVFMT_NOFILE as i32 == 0 {
497 if let Some(pb) = NonNull::new(self.pb) {
498 let _ = unsafe { AVIOContext::from_raw(pb) };
499 }
500 }
501
502 unsafe {
503 ffi::avformat_free_context(self.as_mut_ptr());
504 }
505 }
506}
507
508wrap_ref!(AVInputFormat: ffi::AVInputFormat);
509
510impl AVInputFormat {
511 pub fn find(short_name: &CStr) -> Option<AVInputFormatRef<'static>> {
513 unsafe { ffi::av_find_input_format(short_name.as_ptr()) }
514 .upgrade()
515 .map(|x| unsafe { AVInputFormatRef::from_raw(x) })
516 }
517
518 pub fn iterate() -> AVInputFormatIter {
520 AVInputFormatIter {
521 opaque: ptr::null_mut(),
522 }
523 }
524
525 pub fn name(&self) -> &CStr {
528 unsafe { CStr::from_ptr(self.name) }
529 }
530
531 pub fn long_name(&self) -> &CStr {
535 unsafe { CStr::from_ptr(self.long_name) }
536 }
537}
538
539pub struct AVInputFormatIter {
540 opaque: *mut c_void,
541}
542
543impl Iterator for AVInputFormatIter {
544 type Item = AVInputFormatRef<'static>;
545
546 fn next(&mut self) -> Option<Self::Item> {
547 unsafe { ffi::av_demuxer_iterate(&mut self.opaque) }
548 .upgrade()
549 .map(|x| unsafe { AVInputFormatRef::from_raw(x) })
550 }
551}
552
553wrap_ref!(AVOutputFormat: ffi::AVOutputFormat);
554
555impl AVOutputFormat {
556 pub fn guess_format(
560 short_name: Option<&CStr>,
561 filename: Option<&CStr>,
562 mime_type: Option<&CStr>,
563 ) -> Option<AVOutputFormatRef<'static>> {
564 let short_name = short_name.map(|x| x.as_ptr()).unwrap_or_else(ptr::null);
565 let filename = filename.map(|x| x.as_ptr()).unwrap_or_else(ptr::null);
566 let mime_type = mime_type.map(|x| x.as_ptr()).unwrap_or_else(ptr::null);
567
568 unsafe { ffi::av_guess_format(short_name, filename, mime_type) }
569 .upgrade()
570 .map(|x| unsafe { AVOutputFormatRef::from_raw(x) })
571 }
572
573 pub fn iterate() -> AVOutputFormatIter {
575 AVOutputFormatIter {
576 opaque: ptr::null_mut(),
577 }
578 }
579
580 pub fn name(&self) -> &CStr {
583 unsafe { CStr::from_ptr(self.name) }
584 }
585
586 pub fn long_name(&self) -> &CStr {
590 unsafe { CStr::from_ptr(self.long_name) }
591 }
592}
593
594pub struct AVOutputFormatIter {
595 opaque: *mut c_void,
596}
597
598impl Iterator for AVOutputFormatIter {
599 type Item = AVOutputFormatRef<'static>;
600
601 fn next(&mut self) -> Option<Self::Item> {
602 unsafe { ffi::av_muxer_iterate(&mut self.opaque) }
603 .upgrade()
604 .map(|x| unsafe { AVOutputFormatRef::from_raw(x) })
605 }
606}
607
608wrap_ref_mut!(#[repr(transparent)] AVStream: ffi::AVStream);
609settable!(AVStream {
610 avg_frame_rate: AVRational,
611 discard: i32,
612 disposition: c_int,
613 duration: i64,
614 event_flags: c_int,
615 sample_aspect_ratio: AVRational,
616 time_base: AVRational,
617});
618
619impl AVStream {
620 pub fn guess_framerate(&self) -> Option<AVRational> {
624 Some(unsafe {
625 ffi::av_guess_frame_rate(ptr::null_mut(), self.as_ptr() as *mut _, ptr::null_mut())
629 })
630 }
631}
632
633impl<'stream> AVStream {
634 pub fn codecpar(&'stream self) -> AVCodecParametersRef<'stream> {
636 unsafe { AVCodecParametersRef::from_raw(NonNull::new(self.codecpar).unwrap()) }
638 }
639
640 pub fn codecpar_mut(&'stream mut self) -> AVCodecParametersMut<'stream> {
642 unsafe { AVCodecParametersMut::from_raw(NonNull::new(self.codecpar).unwrap()) }
643 }
644
645 pub fn set_codecpar(&mut self, parameters: AVCodecParameters) {
647 if let Some(codecpar) = self.codecpar.upgrade() {
652 let _ = unsafe { AVCodecParameters::from_raw(codecpar) };
653 }
654 unsafe {
655 self.deref_mut().codecpar = parameters.into_raw().as_ptr();
656 }
657 }
658
659 pub fn metadata(&'stream self) -> Option<AVDictionaryRef<'stream>> {
661 NonNull::new(self.metadata).map(|x| unsafe { AVDictionaryRef::from_raw(x) })
662 }
663
664 pub fn metadata_mut(&'stream mut self) -> Option<AVDictionaryMut<'stream>> {
666 NonNull::new(self.metadata).map(|x| unsafe { AVDictionaryMut::from_raw(x) })
667 }
668
669 pub fn set_metadata(&mut self, dict: Option<AVDictionary>) {
671 let _ = NonNull::new(self.metadata).map(|x| unsafe { AVDictionary::from_raw(x) });
673
674 unsafe {
676 self.deref_mut().metadata = dict
677 .map(|x| x.into_raw().as_ptr())
678 .unwrap_or(ptr::null_mut());
679 }
680 }
681}
682
683#[cfg(test)]
684mod test {
685 use super::*;
686
687 #[test]
688 fn test_find_input_format() {
689 let name = c"mpeg";
690 let filter_ref = AVInputFormat::find(name).unwrap();
691 assert_eq!(filter_ref.long_name(), c"MPEG-PS (MPEG-2 Program Stream)");
692
693 let name = c"asf";
694 let filter_ref = AVInputFormat::find(name).unwrap();
695 assert_eq!(
696 filter_ref.long_name(),
697 c"ASF (Advanced / Active Streaming Format)"
698 );
699
700 let name = c"__random__";
701 assert!(AVInputFormat::find(name).is_none());
702 }
703
704 #[test]
705 fn test_iterate_input_formats() {
706 let inputs = AVInputFormat::iterate()
707 .map(|x| x.name().to_string_lossy().into_owned())
708 .collect::<Vec<_>>();
709 dbg!(&inputs);
710 assert!(!inputs.is_empty());
711 assert!(inputs.contains(&"mpeg".to_string()));
712 assert!(inputs.contains(&"asf".to_string()));
713 }
714
715 #[test]
716 fn test_iterate_output_formats() {
717 let outputs = AVOutputFormat::iterate()
718 .map(|x| x.name().to_string_lossy().into_owned())
719 .collect::<Vec<_>>();
720 dbg!(&outputs);
721 assert!(!outputs.is_empty());
722 assert!(outputs.contains(&"mpeg".to_string()));
723 assert!(outputs.contains(&"asf".to_string()));
724 }
725}