playa_ffmpeg/format/context/
output.rs

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