ffmpeg_the_third/format/context/
output.rs

1use std::ffi::CString;
2use std::mem::size_of;
3use std::ops::{Deref, DerefMut};
4use std::ptr;
5
6use super::common::Context;
7use super::destructor;
8use crate::codec::traits;
9use crate::ffi::*;
10use crate::{format, ChapterMut, Dictionary, Error, Rational, StreamMut};
11
12pub struct Output {
13    ptr: *mut AVFormatContext,
14    ctx: Context,
15}
16
17unsafe impl Send for Output {}
18
19impl Output {
20    pub unsafe fn wrap(ptr: *mut AVFormatContext) -> Self {
21        Output {
22            ptr,
23            ctx: Context::wrap(ptr, destructor::Mode::Output),
24        }
25    }
26
27    pub unsafe fn as_ptr(&self) -> *const AVFormatContext {
28        self.ptr as *const _
29    }
30
31    pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFormatContext {
32        self.ptr
33    }
34}
35
36impl Output {
37    pub fn format(&self) -> format::Output {
38        unsafe { format::Output::from_raw((*self.as_ptr()).oformat).expect("oformat is non-null") }
39    }
40
41    pub fn write_header(&mut self) -> Result<(), Error> {
42        unsafe {
43            match avformat_write_header(self.as_mut_ptr(), ptr::null_mut()) {
44                0 => Ok(()),
45                e => Err(Error::from(e)),
46            }
47        }
48    }
49
50    pub fn write_header_with(&mut self, options: Dictionary) -> Result<Dictionary<'_>, Error> {
51        unsafe {
52            let mut opts = options.disown();
53            let res = avformat_write_header(self.as_mut_ptr(), &mut opts);
54
55            match res {
56                0 => Ok(Dictionary::own(opts)),
57                e => Err(Error::from(e)),
58            }
59        }
60    }
61
62    pub fn write_trailer(&mut self) -> Result<(), Error> {
63        unsafe {
64            match av_write_trailer(self.as_mut_ptr()) {
65                0 => Ok(()),
66                e => Err(Error::from(e)),
67            }
68        }
69    }
70
71    pub fn add_stream<T, E: traits::Encoder<T>>(
72        &mut self,
73        codec: E,
74    ) -> Result<StreamMut<'_>, Error> {
75        unsafe {
76            let codec = codec.encoder();
77            let codec = codec.map_or(ptr::null(), |c| c.as_ptr());
78            let ptr = avformat_new_stream(self.as_mut_ptr(), codec);
79
80            if ptr.is_null() {
81                return Err(Error::Unknown);
82            }
83
84            let index = (*self.ctx.as_ptr()).nb_streams - 1;
85
86            Ok(StreamMut::wrap(&mut self.ctx, index as usize))
87        }
88    }
89
90    pub fn add_chapter<R: Into<Rational>, S: AsRef<str>>(
91        &mut self,
92        id: i64,
93        time_base: R,
94        start: i64,
95        end: i64,
96        title: S,
97    ) -> Result<ChapterMut<'_>, Error> {
98        // avpriv_new_chapter is private (libavformat/internal.h)
99
100        if start > end {
101            return Err(Error::InvalidData);
102        }
103
104        let mut existing = None;
105        for chapter in self.chapters() {
106            if chapter.id() == id {
107                existing = Some(chapter.index());
108                break;
109            }
110        }
111
112        let index = match existing {
113            Some(index) => index,
114            None => unsafe {
115                let ptr = av_mallocz(size_of::<AVChapter>())
116                    .as_mut()
117                    .ok_or(Error::Bug)?;
118                let mut nb_chapters = (*self.as_ptr()).nb_chapters as i32;
119
120                // chapters array will be freed by `avformat_free_context`
121                av_dynarray_add(
122                    &mut (*self.as_mut_ptr()).chapters as *mut _ as *mut libc::c_void,
123                    &mut nb_chapters,
124                    ptr,
125                );
126
127                if nb_chapters > 0 {
128                    (*self.as_mut_ptr()).nb_chapters = nb_chapters as u32;
129                    let index = (*self.ctx.as_ptr()).nb_chapters - 1;
130                    index as usize
131                } else {
132                    // failed to add the chapter
133                    av_freep(ptr);
134                    return Err(Error::Bug);
135                }
136            },
137        };
138
139        let mut chapter = self.chapter_mut(index).ok_or(Error::Bug)?;
140
141        chapter.set_id(id);
142        chapter.set_time_base(time_base);
143        chapter.set_start(start);
144        chapter.set_end(end);
145        chapter.set_metadata("title", title);
146
147        Ok(chapter)
148    }
149
150    pub fn set_metadata(&mut self, dictionary: Dictionary) {
151        unsafe {
152            (*self.as_mut_ptr()).metadata = dictionary.disown();
153        }
154    }
155}
156
157impl Deref for Output {
158    type Target = Context;
159
160    fn deref(&self) -> &Self::Target {
161        &self.ctx
162    }
163}
164
165impl DerefMut for Output {
166    fn deref_mut(&mut self) -> &mut Self::Target {
167        &mut self.ctx
168    }
169}
170
171pub fn dump(ctx: &Output, index: i32, url: Option<&str>) {
172    let url = url.map(|u| CString::new(u).unwrap());
173
174    unsafe {
175        av_dump_format(
176            ctx.as_ptr() as *mut _,
177            index,
178            url.unwrap_or_else(|| CString::new("").unwrap()).as_ptr(),
179            1,
180        );
181    }
182}