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