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 {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_chapter<R: Into<Rational>, S: AsRef<str>>( |
94 | &mut self, |
95 | id: i64, |
96 | time_base: R, |
97 | start: i64, |
98 | end: i64, |
99 | title: S, |
100 | ) -> Result<ChapterMut, Error> { |
101 | // avpriv_new_chapter is private (libavformat/internal.h) |
102 | |
103 | if start > end { |
104 | return Err(Error::InvalidData); |
105 | } |
106 | |
107 | let mut existing = None; |
108 | for chapter in self.chapters() { |
109 | if chapter.id() == id { |
110 | existing = Some(chapter.index()); |
111 | break; |
112 | } |
113 | } |
114 | |
115 | let index = match existing { |
116 | Some(index) => index, |
117 | None => unsafe { |
118 | let ptr = av_mallocz(size_of::<AVChapter>()) |
119 | .as_mut() |
120 | .ok_or(Error::Bug)?; |
121 | let mut nb_chapters = (*self.as_ptr()).nb_chapters as i32; |
122 | |
123 | // chapters array will be freed by `avformat_free_context` |
124 | av_dynarray_add( |
125 | &mut (*self.as_mut_ptr()).chapters as *mut _ as *mut libc::c_void, |
126 | &mut nb_chapters, |
127 | ptr, |
128 | ); |
129 | |
130 | if nb_chapters > 0 { |
131 | (*self.as_mut_ptr()).nb_chapters = nb_chapters as u32; |
132 | let index = (*self.ctx.as_ptr()).nb_chapters - 1; |
133 | index as usize |
134 | } else { |
135 | // failed to add the chapter |
136 | av_freep(ptr); |
137 | return Err(Error::Bug); |
138 | } |
139 | }, |
140 | }; |
141 | |
142 | let mut chapter = self.chapter_mut(index).ok_or(Error::Bug)?; |
143 | |
144 | chapter.set_id(id); |
145 | chapter.set_time_base(time_base); |
146 | chapter.set_start(start); |
147 | chapter.set_end(end); |
148 | chapter.set_metadata("title" , title); |
149 | |
150 | Ok(chapter) |
151 | } |
152 | |
153 | pub fn set_metadata(&mut self, dictionary: Dictionary) { |
154 | unsafe { |
155 | (*self.as_mut_ptr()).metadata = dictionary.disown(); |
156 | } |
157 | } |
158 | } |
159 | |
160 | impl Deref for Output { |
161 | type Target = Context; |
162 | |
163 | fn deref(&self) -> &Self::Target { |
164 | &self.ctx |
165 | } |
166 | } |
167 | |
168 | impl DerefMut for Output { |
169 | fn deref_mut(&mut self) -> &mut Self::Target { |
170 | &mut self.ctx |
171 | } |
172 | } |
173 | |
174 | pub fn dump(ctx: &Output, index: i32, url: Option<&str>) { |
175 | let url: Option = url.map(|u: &str| CString::new(u).unwrap()); |
176 | |
177 | unsafe { |
178 | av_dump_format( |
179 | ctx.as_ptr() as *mut _, |
180 | index, |
181 | url.unwrap_or_else(|| CString::new("" ).unwrap()).as_ptr(), |
182 | 1, |
183 | ); |
184 | } |
185 | } |
186 | |