Skip to main content

ffmpeg_next/format/context/
output.rs

1use std::ffi::CString;
2use std::mem::size_of;
3use std::ops::{Deref, DerefMut};
4use std::ptr;
5
6use libc;
7
8use super::common::Context;
9use super::destructor;
10use crate::codec::traits;
11use crate::ffi::*;
12use crate::{ChapterMut, Dictionary, Error, Rational, StreamMut, codec, format};
13
14pub enum AvStreamInitStatus {
15    /// the codec had not already been fully initialized
16    InWriteHeader,
17    /// the codec had already been fully initialized
18    InInitOutput,
19}
20
21pub struct Output {
22    ptr: *mut AVFormatContext,
23    ctx: Context,
24}
25
26unsafe impl Send for Output {}
27
28impl Output {
29    pub unsafe fn wrap(ptr: *mut AVFormatContext) -> Self {
30        unsafe {
31            Output {
32                ptr,
33                ctx: Context::wrap(ptr, destructor::Mode::Output),
34            }
35        }
36    }
37    pub unsafe fn wrap_with_custom_io(
38        ptr: *mut AVFormatContext,
39        custom_io: format::context::StreamIo,
40    ) -> Self {
41        unsafe {
42            Output {
43                ptr,
44                ctx: Context::wrap(ptr, destructor::Mode::OutputCustomIo(custom_io)),
45            }
46        }
47    }
48
49    pub unsafe fn as_ptr(&self) -> *const AVFormatContext {
50        self.ptr as *const _
51    }
52
53    pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFormatContext {
54        self.ptr
55    }
56}
57
58impl Output {
59    pub fn format(&self) -> format::Output {
60        // We get a clippy warning in 4.4 but not in 5.0 and newer, so we allow that cast to not complicate the code
61        #[allow(clippy::unnecessary_cast)]
62        unsafe {
63            format::Output::wrap((*self.as_ptr()).oformat as *mut AVOutputFormat)
64        }
65    }
66
67    pub fn write_header(&mut self) -> Result<AvStreamInitStatus, Error> {
68        unsafe {
69            match avformat_write_header(self.as_mut_ptr(), ptr::null_mut()) {
70                0 => Ok(AvStreamInitStatus::InWriteHeader),
71                1 => Ok(AvStreamInitStatus::InInitOutput),
72                e => Err(Error::from(e)),
73            }
74        }
75    }
76
77    pub fn write_header_with(&mut self, options: Dictionary) -> Result<Dictionary<'_>, Error> {
78        unsafe {
79            let mut opts = options.disown();
80            let res = avformat_write_header(self.as_mut_ptr(), &mut opts);
81
82            let opts = Dictionary::own(opts);
83            match res {
84                0 => Ok(opts),
85                1 => Ok(opts),
86                e => Err(Error::from(e)),
87            }
88        }
89    }
90
91    pub fn write_trailer(&mut self) -> Result<(), Error> {
92        unsafe {
93            // Documented as 0-or-negative, but muxers can leak a positive
94            // internal byte count through it (movenc returns the `mfra`
95            // atom size on the fragmented path); ffmpeg's own CLI treats
96            // any >= 0 as success (`if ((ret = av_write_trailer(ofmt_ctx)) < 0)`).
97            match av_write_trailer(self.as_mut_ptr()) {
98                e if e >= 0 => Ok(()),
99                e => Err(Error::from(e)),
100            }
101        }
102    }
103
104    pub fn add_stream<E: traits::Encoder>(&mut self, codec: E) -> Result<StreamMut<'_>, Error> {
105        unsafe {
106            let codec = codec.encoder();
107            let codec = codec.map_or(ptr::null(), |c| c.as_ptr());
108            let ptr = avformat_new_stream(self.as_mut_ptr(), codec);
109
110            if ptr.is_null() {
111                return Err(Error::Unknown);
112            }
113
114            let index = (*self.ctx.as_ptr()).nb_streams - 1;
115
116            Ok(StreamMut::wrap(&mut self.ctx, index as usize))
117        }
118    }
119
120    pub fn add_stream_with(&mut self, context: &codec::Context) -> Result<StreamMut<'_>, Error> {
121        unsafe {
122            let ptr = avformat_new_stream(self.as_mut_ptr(), ptr::null());
123
124            if ptr.is_null() {
125                return Err(Error::Unknown);
126            }
127
128            match avcodec_parameters_from_context((*ptr).codecpar, context.as_ptr()) {
129                0 => (),
130                e => return Err(Error::from(e)),
131            }
132
133            let index = (*self.ctx.as_ptr()).nb_streams - 1;
134
135            Ok(StreamMut::wrap(&mut self.ctx, index as usize))
136        }
137    }
138
139    pub fn add_chapter<R: Into<Rational>, S: AsRef<str>>(
140        &mut self,
141        id: i64,
142        time_base: R,
143        start: i64,
144        end: i64,
145        title: S,
146    ) -> Result<ChapterMut<'_>, Error> {
147        // avpriv_new_chapter is private (libavformat/internal.h)
148
149        if start > end {
150            return Err(Error::InvalidData);
151        }
152
153        let mut existing = None;
154        for chapter in self.chapters() {
155            if chapter.id() == id {
156                existing = Some(chapter.index());
157                break;
158            }
159        }
160
161        let index = match existing {
162            Some(index) => index,
163            None => unsafe {
164                let ptr = av_mallocz(size_of::<AVChapter>())
165                    .as_mut()
166                    .ok_or(Error::Bug)?;
167                let mut nb_chapters = (*self.as_ptr()).nb_chapters as i32;
168
169                // chapters array will be freed by `avformat_free_context`
170                av_dynarray_add(
171                    &mut (*self.as_mut_ptr()).chapters as *mut _ as *mut libc::c_void,
172                    &mut nb_chapters,
173                    ptr,
174                );
175
176                if nb_chapters > 0 {
177                    (*self.as_mut_ptr()).nb_chapters = nb_chapters as u32;
178                    let index = (*self.ctx.as_ptr()).nb_chapters - 1;
179                    index as usize
180                } else {
181                    // failed to add the chapter
182                    av_freep(ptr);
183                    return Err(Error::Bug);
184                }
185            },
186        };
187
188        let mut chapter = self.chapter_mut(index).ok_or(Error::Bug)?;
189
190        chapter.set_id(id);
191        chapter.set_time_base(time_base);
192        chapter.set_start(start);
193        chapter.set_end(end);
194        chapter.set_metadata("title", title);
195
196        Ok(chapter)
197    }
198
199    pub fn set_metadata(&mut self, dictionary: Dictionary) {
200        unsafe {
201            (*self.as_mut_ptr()).metadata = dictionary.disown();
202        }
203    }
204}
205
206impl Deref for Output {
207    type Target = Context;
208
209    fn deref(&self) -> &Self::Target {
210        &self.ctx
211    }
212}
213
214impl DerefMut for Output {
215    fn deref_mut(&mut self) -> &mut Self::Target {
216        &mut self.ctx
217    }
218}
219
220pub fn dump(ctx: &Output, index: i32, url: Option<&str>) {
221    let url = url.map(|u| CString::new(u).unwrap());
222
223    unsafe {
224        av_dump_format(
225            ctx.as_ptr() as *mut _,
226            index,
227            url.unwrap_or_else(|| CString::new("").unwrap()).as_ptr(),
228            1,
229        );
230    }
231}