1use crate::avcodec::{
5 av_init_packet, moonfire_ffmpeg_packet_alloc, moonfire_ffmpeg_packet_free, AVCodecParameters,
6 AVPacket, InputCodecParameters, Packet,
7};
8use crate::avutil::{Dictionary, Error};
9use std::cell::RefCell;
10use std::convert::TryFrom;
11use std::ffi::CStr;
12use std::marker::PhantomData;
13use std::ptr;
14
15extern "C" {
17 pub(crate) fn avformat_version() -> libc::c_int;
18 pub(crate) fn avformat_configuration() -> *mut libc::c_char;
19
20 fn avformat_alloc_context() -> *mut AVFormatContext;
21
22 fn avio_alloc_context(
23 buffer: *const u8,
24 buffer_size: libc::c_int,
25 write_flag: libc::c_int,
26 opaque: *const libc::c_void,
27 read_packet: Option<
28 unsafe extern "C" fn(
29 opaque: *const libc::c_void,
30 buf: *mut u8,
31 buf_size: libc::c_int,
32 ) -> libc::c_int,
33 >,
34 write_packet: Option<
35 unsafe extern "C" fn(
36 opaque: *const libc::c_void,
37 buf: *const u8,
38 buf_size: libc::c_int,
39 ) -> libc::c_int,
40 >,
41 seek: Option<
42 unsafe extern "C" fn(
43 opaque: *const libc::c_void,
44 offset: i64,
45 whence: libc::c_int,
46 ) -> i64,
47 >,
48 ) -> *mut AVIOContext;
49 fn avio_context_free(s: *mut *mut AVIOContext);
50
51 fn avformat_open_input(
55 ctx: *mut *mut AVFormatContext,
56 url: *const libc::c_char,
57 fmt: *const AVInputFormat,
58 options: *mut Dictionary,
59 ) -> libc::c_int;
60 fn avformat_close_input(ctx: *mut *mut AVFormatContext);
61 fn avformat_find_stream_info(
62 ctx: *mut AVFormatContext,
63 options: *mut Dictionary,
64 ) -> libc::c_int;
65 fn av_read_frame(ctx: *mut AVFormatContext, p: *mut AVPacket) -> libc::c_int;
68 pub(crate) fn av_register_all();
69 pub(crate) fn avformat_network_init() -> libc::c_int;
70}
71
72extern "C" {
74 pub(crate) static moonfire_ffmpeg_compiled_libavformat_version: libc::c_int;
75
76 static moonfire_ffmpeg_avseek_force: libc::c_int;
77 static moonfire_ffmpeg_avseek_size: libc::c_int;
78 static moonfire_ffmpeg_seek_set: libc::c_int;
79 static moonfire_ffmpeg_seek_cur: libc::c_int;
80 static moonfire_ffmpeg_seek_end: libc::c_int;
81
82 fn moonfire_ffmpeg_fctx_streams(ctx: *const AVFormatContext) -> StreamsLen;
83 fn moonfire_ffmpeg_fctx_set_pb(ctx: *mut AVFormatContext, pb: *mut AVIOContext);
88
89 fn moonfire_ffmpeg_ioctx_set_direct(pb: *mut AVIOContext);
90
91 fn moonfire_ffmpeg_stream_codecpar(stream: *const AVStream) -> *const AVCodecParameters;
92 fn moonfire_ffmpeg_stream_duration(stream: *const AVStream) -> i64;
93 fn moonfire_ffmpeg_stream_time_base(stream: *const AVStream) -> crate::avutil::Rational;
94}
95
96#[repr(C)]
98struct AVFormatContext {
99 _private: [u8; 0],
100}
101#[repr(C)]
102struct AVIOContext {
103 _private: [u8; 0],
104}
105#[repr(C)]
106struct AVInputFormat {
107 _private: [u8; 0],
108}
109#[repr(C)]
110struct AVStream {
111 _private: [u8; 0],
112}
113
114pub struct InputFormatContext<'a> {
115 _io_ctx: PhantomData<&'a mut dyn IoContext>,
118 ctx: *mut AVFormatContext,
119 pkt: RefCell<*mut AVPacket>,
120}
121
122pub enum Whence {
124 Size,
126
127 Set,
129
130 Cur,
132
133 End,
135}
136
137pub trait IoContext {
140 fn readable(&self) -> bool {
142 false
143 }
144
145 fn writable(&self) -> bool {
147 false
148 }
149
150 fn seekable(&self) -> bool {
152 false
153 }
154
155 fn direct(&self) -> bool {
158 false
159 }
160
161 fn buf_len(&self) -> usize;
163
164 #[allow(unused_variables)]
166 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
167 Err(Error::enosys())
168 }
169
170 #[allow(unused_variables)]
172 fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
173 Err(Error::enosys())
174 }
175
176 #[allow(unused_variables)]
179 fn seek(&mut self, offset: i64, whence: Whence, force: bool) -> Result<u64, Error> {
180 Err(Error::enosys())
181 }
182}
183
184pub struct SliceIoContext<'a> {
186 slice: &'a [u8],
187 pos: usize,
188}
189
190impl<'a> SliceIoContext<'a> {
191 pub fn new(slice: &'a [u8]) -> Self {
192 Self { slice, pos: 0 }
193 }
194}
195
196impl<'a> IoContext for SliceIoContext<'a> {
197 fn readable(&self) -> bool {
198 true
199 }
200 fn writable(&self) -> bool {
201 false
202 }
203 fn seekable(&self) -> bool {
204 true
205 }
206 fn direct(&self) -> bool {
207 true
208 }
209 fn buf_len(&self) -> usize {
210 std::cmp::min(self.slice.len(), 4096)
211 }
212 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
213 let copy_len = std::cmp::min(buf.len(), self.slice.len() - self.pos);
214 buf[0..copy_len].copy_from_slice(&self.slice[self.pos..self.pos + copy_len]);
215 self.pos += copy_len;
216 Ok(copy_len)
217 }
218 fn seek(&mut self, offset: i64, whence: Whence, _force: bool) -> Result<u64, Error> {
219 let offset = usize::try_from(offset).map_err(|_| Error::invalid_data())?;
220 let new_pos = match whence {
221 Whence::Size => return Ok(u64::try_from(self.slice.len()).unwrap()),
222 Whence::Set => offset,
223 Whence::Cur => self
224 .pos
225 .checked_add(offset)
226 .ok_or_else(Error::invalid_data)?,
227 Whence::End => self
228 .pos
229 .checked_add(offset)
230 .ok_or_else(Error::invalid_data)?,
231 };
232 if new_pos > self.slice.len() {
233 return Err(Error::invalid_data());
234 }
235 self.pos = new_pos;
236 Ok(u64::try_from(self.pos).unwrap())
237 }
238}
239
240struct IoContextWrapper<'a> {
241 _ctx: Box<&'a mut dyn IoContext>,
244
245 avio_ctx: ptr::NonNull<AVIOContext>,
246}
247
248unsafe extern "C" fn ioctx_read_packet(
250 opaque: *const libc::c_void,
251 buf_data: *mut u8,
252 buf_len: libc::c_int,
253) -> libc::c_int {
254 let ctx: &mut &mut dyn IoContext = &mut *(opaque as *mut &mut dyn IoContext);
255 let buf = std::slice::from_raw_parts_mut(buf_data, usize::try_from(buf_len).unwrap());
256 match ctx.read(buf) {
257 Ok(l) => libc::c_int::try_from(l).unwrap(),
258 Err(e) => e.get(),
259 }
260}
261
262unsafe extern "C" fn ioctx_write_packet(
264 opaque: *const libc::c_void,
265 buf_data: *const u8,
266 buf_len: libc::c_int,
267) -> libc::c_int {
268 let ctx: &mut &mut dyn IoContext = &mut *(opaque as *mut &mut dyn IoContext);
269 let buf = std::slice::from_raw_parts(buf_data, usize::try_from(buf_len).unwrap());
270 match ctx.write(buf) {
271 Ok(l) => libc::c_int::try_from(l).unwrap(),
272 Err(e) => e.get(),
273 }
274}
275
276unsafe extern "C" fn ioctx_seek(
278 opaque: *const libc::c_void,
279 offset: i64,
280 whence: libc::c_int,
281) -> i64 {
282 let ctx: &mut &mut dyn IoContext = &mut *(opaque as *mut &mut dyn IoContext);
283 let avseek_force = moonfire_ffmpeg_avseek_force;
284 let force = (whence & avseek_force) != 0;
285 let w = whence & !avseek_force;
286 let whence = if (w & moonfire_ffmpeg_avseek_size) != 0 {
287 Whence::Size
288 } else if w == moonfire_ffmpeg_seek_set {
289 Whence::Set
290 } else if w == moonfire_ffmpeg_seek_cur {
291 Whence::Cur
292 } else if w == moonfire_ffmpeg_seek_end {
293 Whence::End
294 } else {
295 panic!("invalid whence {}", whence);
296 };
297 match ctx.seek(offset, whence, force) {
298 Ok(p) => i64::try_from(p).unwrap(),
299 Err(e) => i64::from(e.get()),
300 }
301}
302
303impl<'a> IoContextWrapper<'a> {
304 fn new(ctx: &'a mut dyn IoContext) -> Result<Self, Error> {
305 let ctx = Box::new(ctx);
306 let buf_len = ctx.buf_len();
307 let mut buf = crate::avutil::Alloc::new(buf_len)?;
308 let avio_ctx = ptr::NonNull::new(unsafe {
309 let opaque: &&mut dyn IoContext = &ctx;
310 avio_alloc_context(
311 buf.as_ptr() as *const u8,
312 i32::try_from(buf_len).unwrap(),
313 if ctx.writable() { 1 } else { 0 },
314 opaque as *const &mut dyn IoContext as *mut core::ffi::c_void,
315 if ctx.readable() {
316 Some(ioctx_read_packet)
317 } else {
318 None
319 },
320 if ctx.writable() {
321 Some(ioctx_write_packet)
322 } else {
323 None
324 },
325 if ctx.seekable() {
326 Some(ioctx_seek)
327 } else {
328 None
329 },
330 )
331 })
332 .ok_or_else(Error::enomem)?;
333 std::mem::forget(buf); if ctx.direct() {
335 unsafe { moonfire_ffmpeg_ioctx_set_direct(avio_ctx.as_ptr()) };
336 }
337 Ok(Self {
338 avio_ctx,
339 _ctx: ctx,
340 })
341 }
342
343 fn release(self) -> *mut AVIOContext {
344 let p = self.avio_ctx.as_ptr();
345 std::mem::forget(self);
346 p
347 }
348}
349
350impl<'a> Drop for IoContextWrapper<'a> {
351 fn drop(&mut self) {
352 let mut p = self.avio_ctx.as_ptr();
353 unsafe { avio_context_free(&mut p) };
354 }
355}
356
357impl<'a> InputFormatContext<'a> {
358 pub fn open(source: &CStr, dict: &mut Dictionary) -> Result<Self, Error> {
359 let mut ctx = ptr::null_mut();
360 Error::wrap(unsafe { avformat_open_input(&mut ctx, source.as_ptr(), ptr::null(), dict) })?;
361 let pkt = unsafe { moonfire_ffmpeg_packet_alloc() };
362 if pkt.is_null() {
363 unsafe { avformat_close_input(&mut ctx) };
364 return Err(Error::enomem());
365 }
366 unsafe { av_init_packet(pkt) };
367 Ok(InputFormatContext {
368 ctx,
369 _io_ctx: PhantomData,
370 pkt: RefCell::new(pkt),
371 })
372 }
373
374 pub fn with_io_context(
375 source: &CStr,
376 io_ctx: &'a mut dyn IoContext,
377 dict: &mut Dictionary,
378 ) -> Result<Self, Error> {
379 let wrapper = IoContextWrapper::new(io_ctx)?;
380 let mut ctx = unsafe { avformat_alloc_context() };
381 if ctx.is_null() {
382 return Err(Error::enomem());
383 }
384 unsafe { moonfire_ffmpeg_fctx_set_pb(ctx, wrapper.release()) };
386 Error::wrap(unsafe { avformat_open_input(&mut ctx, source.as_ptr(), ptr::null(), dict) })?;
387 let pkt = unsafe { moonfire_ffmpeg_packet_alloc() };
388 if pkt.is_null() {
389 unsafe { avformat_close_input(&mut ctx) };
390 return Err(Error::enomem());
391 }
392 unsafe { av_init_packet(pkt) };
393 Ok(InputFormatContext {
394 _io_ctx: PhantomData,
395 ctx,
396 pkt: RefCell::new(pkt),
397 })
398 }
399
400 pub fn find_stream_info(&mut self) -> Result<(), Error> {
401 Error::wrap(unsafe { avformat_find_stream_info(self.ctx, ptr::null_mut()) })?;
402 Ok(())
403 }
404
405 pub fn read_frame(&self) -> Result<Packet<'_>, Error> {
408 let pkt = self.pkt.borrow();
409 Error::wrap(unsafe { av_read_frame(self.ctx, *pkt) })?;
410 Ok(Packet(pkt))
411 }
412
413 pub fn streams(&self) -> Streams {
414 Streams(unsafe {
415 let s = moonfire_ffmpeg_fctx_streams(self.ctx);
416 std::slice::from_raw_parts(s.streams, s.len as usize)
417 })
418 }
419}
420
421unsafe impl<'a> Send for InputFormatContext<'a> {}
422
423impl<'a> Drop for InputFormatContext<'a> {
424 fn drop(&mut self) {
425 unsafe {
426 moonfire_ffmpeg_packet_free(*self.pkt.borrow());
427 avformat_close_input(&mut self.ctx);
428 }
429 }
430}
431
432#[repr(C)]
434struct StreamsLen {
435 streams: *const *const AVStream,
436 len: libc::size_t,
437}
438
439pub struct Streams<'owner>(&'owner [*const AVStream]);
440
441impl<'owner> Streams<'owner> {
442 pub fn get(&self, i: usize) -> InputStream<'owner> {
443 InputStream(unsafe { self.0[i].as_ref() }.unwrap())
444 }
445 pub fn len(&self) -> usize {
446 self.0.len()
447 }
448 pub fn is_empty(&self) -> bool {
449 self.len() == 0
450 }
451}
452
453pub struct InputStream<'o>(&'o AVStream);
454
455impl<'o> InputStream<'o> {
456 pub fn codecpar(&self) -> InputCodecParameters<'o> {
457 InputCodecParameters(unsafe { moonfire_ffmpeg_stream_codecpar(self.0).as_ref() }.unwrap())
458 }
459
460 pub fn time_base(&self) -> crate::avutil::Rational {
461 unsafe { moonfire_ffmpeg_stream_time_base(self.0) }
462 }
463
464 pub fn duration(&self) -> i64 {
465 unsafe { moonfire_ffmpeg_stream_duration(self.0) }
466 }
467}
468
469#[cfg(test)]
470mod test {
471 use cstr::cstr;
472
473 fn with_packets<F>(ctx: &mut super::InputFormatContext, mut f: F)
474 where
475 F: FnMut(crate::avcodec::Packet),
476 {
477 loop {
478 let pkt = match ctx.read_frame() {
479 Err(e) if e.is_eof() => break,
480 Err(e) => panic!("{}", e),
481 Ok(p) => p,
482 };
483 f(pkt);
484 }
485 }
486
487 #[test]
488 fn file() {
489 crate::Ffmpeg::new();
490 let mut dict = crate::avutil::Dictionary::new();
491 let mut ctx =
492 super::InputFormatContext::open(cstr!("src/testdata/clip.mp4"), &mut dict).unwrap();
493 let mut pts = Vec::new();
494 with_packets(&mut ctx, |pkt| pts.push(pkt.pts().unwrap()));
495 assert_eq!(pts, &[0, 29700, 59400, 90000, 119700, 149400]);
496 }
497
498 #[test]
500 fn slice() {
501 crate::Ffmpeg::new();
502 let mut dict = crate::avutil::Dictionary::new();
503 let mut io_ctx = super::SliceIoContext::new(include_bytes!("testdata/clip.mp4"));
504 let mut ctx =
505 super::InputFormatContext::with_io_context(cstr!(""), &mut io_ctx, &mut dict).unwrap();
506 let mut pts = Vec::new();
507 with_packets(&mut ctx, |pkt| pts.push(pkt.pts().unwrap()));
508 assert_eq!(pts, &[0, 29700, 59400, 90000, 119700, 149400]);
509 }
510}