Skip to main content

cidre/av/asset/
writer_input.rs

1use crate::{arc, av::MediaType, cv, define_cls, define_obj_type, ns, objc};
2
3#[cfg(feature = "cm")]
4use crate::cm;
5
6#[cfg(all(feature = "blocks", feature = "dispatch"))]
7use crate::{blocks, dispatch};
8
9define_obj_type!(pub WriterInput(ns::Id));
10
11impl arc::A<WriterInput> {
12    #[objc::msg_send(initWithMediaType:outputSettings:)]
13    pub unsafe fn init_media_type_output_settings_throws(
14        self,
15        media_type: &MediaType,
16        output_settings: Option<&ns::Dictionary<ns::String, ns::Id>>,
17    ) -> arc::R<WriterInput>;
18
19    #[cfg(feature = "cm")]
20    #[objc::msg_send(initWithMediaType:outputSettings:sourceFormatHint:)]
21    pub unsafe fn with_media_type_output_settings_source_format_hint_throws(
22        self,
23        media_type: &MediaType,
24        output_settings: Option<&ns::Dictionary<ns::String, ns::Id>>,
25        source_format_hint: Option<&cm::FormatDesc>,
26    ) -> arc::R<WriterInput>;
27}
28
29impl WriterInput {
30    define_cls!(AV_ASSET_WRITER_INPUT);
31
32    pub unsafe fn with_media_type_output_settings_throws(
33        media_type: &MediaType,
34        output_settings: Option<&ns::Dictionary<ns::String, ns::Id>>,
35    ) -> arc::R<WriterInput> {
36        unsafe { Self::alloc().init_media_type_output_settings_throws(media_type, output_settings) }
37    }
38
39    pub fn with_media_type_and_output_settings<'ear>(
40        media_type: &MediaType,
41        output_settings: Option<&ns::Dictionary<ns::String, ns::Id>>,
42    ) -> ns::ExResult<'ear, arc::R<WriterInput>> {
43        ns::try_catch(|| unsafe {
44            Self::alloc().init_media_type_output_settings_throws(media_type, output_settings)
45        })
46    }
47
48    #[cfg(feature = "cm")]
49    pub unsafe fn with_media_type_output_settings_source_format_hint_throws(
50        media_type: &MediaType,
51        output_settings: Option<&ns::Dictionary<ns::String, ns::Id>>,
52        source_format_hint: Option<&cm::FormatDesc>,
53    ) -> arc::R<WriterInput> {
54        unsafe {
55            Self::alloc().with_media_type_output_settings_source_format_hint_throws(
56                media_type,
57                output_settings,
58                source_format_hint,
59            )
60        }
61    }
62
63    #[cfg(feature = "cm")]
64    pub fn with_media_type_output_settings_source_format_hint<'ear>(
65        media_type: &MediaType,
66        output_settings: Option<&ns::Dictionary<ns::String, ns::Id>>,
67        source_format_hint: Option<&cm::FormatDesc>,
68    ) -> ns::ExResult<'ear, arc::R<WriterInput>> {
69        ns::try_catch(|| unsafe {
70            Self::with_media_type_output_settings_source_format_hint_throws(
71                media_type,
72                output_settings,
73                source_format_hint,
74            )
75        })
76    }
77
78    #[cfg(feature = "cm")]
79    pub unsafe fn with_media_type_format_hint_throws(
80        media_type: &MediaType,
81        source_format_hint: &cm::FormatDesc,
82    ) -> arc::R<WriterInput> {
83        unsafe {
84            Self::alloc().with_media_type_output_settings_source_format_hint_throws(
85                media_type,
86                None,
87                Some(source_format_hint),
88            )
89        }
90    }
91
92    pub unsafe fn with_media_type_throws(media_type: &MediaType) -> arc::R<WriterInput> {
93        unsafe { Self::with_media_type_output_settings_throws(media_type, None) }
94    }
95
96    pub fn with_media_type<'ear>(
97        media_type: &MediaType,
98    ) -> ns::ExResult<'ear, arc::R<WriterInput>> {
99        ns::try_catch(|| unsafe { Self::with_media_type_throws(media_type) })
100    }
101
102    #[objc::msg_send(mediaType)]
103    pub fn media_type(&self) -> arc::R<MediaType>;
104
105    #[objc::msg_send(outputSettings)]
106    pub fn output_settings(&self) -> Option<arc::R<ns::Dictionary<ns::String, ns::Id>>>;
107
108    #[objc::msg_send(isReadyForMoreMediaData)]
109    pub fn is_ready_for_more_media_data(&self) -> bool;
110
111    #[objc::msg_send(expectsMediaDataInRealTime)]
112    pub fn expects_media_data_in_real_time(&self) -> bool;
113
114    #[objc::msg_send(setExpectsMediaDataInRealTime:)]
115    pub fn set_expects_media_data_in_real_time(&mut self, value: bool);
116
117    #[objc::msg_send(markAsFinished)]
118    pub fn mark_as_finished(&mut self);
119
120    /// Order the samples you append according to storage requirements.
121    /// For example, if you’re working with sample buffers containing compressed video,
122    /// order and append them according to their decode timestamp. The system uses the
123    /// timing information in the sample buffer relative to the time you set in the
124    /// call to startSessionAtSourceTime: to determine the timing of samples in the
125    /// output file.
126    ///
127    /// If this method returns NO,
128    /// check the value of the asset writer’s status property to determine whether
129    /// the writing operation’s status is complete, failed, or canceled.
130    /// If the status is AVAssetWriterStatusFailed, the asset writer’s error property
131    /// contains an error object that describes the failure.
132    ///
133    /// This method throws an exception if the sample buffer's media type does not match the asset writer input's media type.
134    #[cfg(feature = "cm")]
135    #[objc::msg_send(appendSampleBuffer:)]
136    pub unsafe fn append_sample_buf_throws(&mut self, buffer: &cm::SampleBuf) -> bool;
137
138    #[cfg(feature = "cm")]
139    pub fn append_sample_buf<'ear>(&mut self, buffer: &cm::SampleBuf) -> ns::ExResult<'ear, bool> {
140        ns::try_catch(|| unsafe { self.append_sample_buf_throws(buffer) })
141    }
142
143    #[cfg(all(feature = "blocks", feature = "dispatch"))]
144    #[objc::msg_send(requestMediaDataWhenReadyOnQueue:usingBlock:)]
145    pub unsafe fn request_media_data_when_ready_on_queue_throws(
146        &self,
147        queue: &dispatch::Queue,
148        block: &mut blocks::CompletionBlock,
149    );
150
151    #[cfg(all(feature = "blocks", feature = "dispatch"))]
152    pub fn request_media_data_when_ready_on_queue<'ear>(
153        &self,
154        queue: &dispatch::Queue,
155        block: &mut blocks::CompletionBlock,
156    ) -> ns::ExResult<'ear> {
157        ns::try_catch(|| unsafe {
158            self.request_media_data_when_ready_on_queue_throws(queue, block)
159        })
160    }
161}
162
163define_obj_type!(
164    #[doc(alias = "AVAssetWriterInputPixelBufferAdaptor")]
165    pub WriterInputPixelBufAdaptor(ns::Id)
166);
167
168impl arc::A<WriterInputPixelBufAdaptor> {
169    #[objc::msg_send(initWithAssetWriterInput:sourcePixelBufferAttributes:)]
170    pub unsafe fn init_with_asset_writer_input_throws(
171        self,
172        input: &WriterInput,
173        src_pixel_buf_attrs: Option<ns::Dictionary<ns::String, ns::Id>>,
174    ) -> arc::R<WriterInputPixelBufAdaptor>;
175}
176
177impl WriterInputPixelBufAdaptor {
178    define_cls!(AV_ASSET_WRITER_INPUT_PIXEL_BUFFER_ADAPTOR);
179
180    pub fn with_input_writer<'ear>(
181        input: &WriterInput,
182        src_pixel_buf_attrs: Option<ns::Dictionary<ns::String, ns::Id>>,
183    ) -> ns::ExResult<'ear, arc::R<Self>> {
184        ns::try_catch(|| unsafe {
185            Self::alloc().init_with_asset_writer_input_throws(input, src_pixel_buf_attrs)
186        })
187    }
188
189    /// The asset writer input to which the receiver should append pixel buffers.
190    #[objc::msg_send(assetWriterInput)]
191    pub fn asset_writer_input(&self) -> arc::R<WriterInput>;
192
193    /// The pixel buffer attributes of pixel buffers that will be vended by the receiver's cv::PixelBufPool.
194    #[objc::msg_send(sourcePixelBufferAttributes)]
195    pub fn src_pixel_buf_attrs(&self) -> Option<arc::R<ns::Dictionary<ns::String, ns::Id>>>;
196
197    /// A pixel buffer pool that will vend and efficiently recycle cv::PixelBuf objects that can be appended to the receiver.
198    #[objc::msg_send(pixelBufferPool)]
199    pub fn pixel_buf_pool(&self) -> Option<&cv::PixelBufPool>;
200
201    /// This method throws an exception if the presentation time is is non-numeric (see cm::Time::is_numeric()) or if "ready_for_more_media_data" is false.
202    #[cfg(feature = "cm")]
203    #[objc::msg_send(appendPixelBuffer:withPresentationTime:)]
204    pub unsafe fn append_pixel_buf_with_pts_throws(
205        &mut self,
206        buf: &cv::PixelBuf,
207        pts: cm::Time,
208    ) -> bool;
209
210    #[cfg(feature = "cm")]
211    pub fn append_pixel_buf_with_pts<'ear>(
212        &mut self,
213        buf: &cv::PixelBuf,
214        pts: cm::Time,
215    ) -> ns::ExResult<'ear, bool> {
216        ns::try_catch(|| unsafe { self.append_pixel_buf_with_pts_throws(buf, pts) })
217    }
218}
219
220unsafe extern "C" {
221    static AV_ASSET_WRITER_INPUT: &'static objc::Class<WriterInput>;
222    static AV_ASSET_WRITER_INPUT_PIXEL_BUFFER_ADAPTOR:
223        &'static objc::Class<WriterInputPixelBufAdaptor>;
224}
225
226#[cfg(test)]
227mod tests {
228    use crate::{av, objc};
229
230    #[test]
231    fn basics() {
232        let input = av::asset::WriterInput::with_media_type(av::MediaType::video())
233            .expect("failed to create writer");
234        assert_eq!(input.media_type().as_ref(), av::MediaType::video());
235        av::asset::WriterInput::with_media_type(av::MediaType::muxed()).unwrap();
236        av::asset::WriterInput::with_media_type(av::MediaType::audio()).unwrap();
237        av::asset::WriterInput::with_media_type(av::MediaType::text()).unwrap();
238        av::asset::WriterInput::with_media_type(av::MediaType::closed_caption()).unwrap();
239        av::asset::WriterInput::with_media_type(av::MediaType::subtitle()).unwrap();
240        av::asset::WriterInput::with_media_type(av::MediaType::depth_data()).unwrap();
241        let input = av::asset::WriterInput::with_media_type(av::MediaType::timecode()).unwrap();
242        println!("{:?}", input);
243    }
244
245    #[test]
246    fn pixel_buf_adapter() {
247        let input = av::asset::WriterInput::with_media_type(av::MediaType::video())
248            .expect("failed to create writer");
249        let adapter = av::asset::WriterInputPixelBufAdaptor::with_input_writer(&input, None)
250            .expect("failed to create pixel buf adapter");
251
252        assert!(adapter.pixel_buf_pool().is_none());
253
254        objc::ar_pool(|| {
255            let _ = av::asset::WriterInputPixelBufAdaptor::with_input_writer(&input, None)
256                .expect_err("should throw exception");
257        })
258    }
259}