playa-ffmpeg 8.0.3

Safe FFmpeg wrapper with vcpkg integration for simplified cross-platform builds (FFmpeg 8.0, Rust 2024)
Documentation
use super::Disposition;
use crate::{
    DictionaryRef, Discard, Rational,
    codec::{self, packet},
    ffi::*,
    format::context::common::Context,
};
use libc::c_int;

#[derive(Debug)]
pub struct Stream<'a> {
    context: &'a Context,
    index: usize,
}

impl<'a> Stream<'a> {
    pub unsafe fn wrap(context: &Context, index: usize) -> Stream<'_> {
        Stream { context, index }
    }

    pub unsafe fn as_ptr(&self) -> *const AVStream {
        unsafe { *(*self.context.as_ptr()).streams.add(self.index) }
    }
}

impl<'a> Stream<'a> {
    pub fn id(&self) -> i32 {
        unsafe { (*self.as_ptr()).id }
    }

    #[cfg(not(feature = "ffmpeg_5_0"))]
    pub fn codec(&self) -> codec::Context {
        unsafe { codec::Context::wrap((*self.as_ptr()).codec, Some(self.context.destructor())) }
    }

    pub fn parameters(&self) -> codec::Parameters {
        unsafe { codec::Parameters::wrap((*self.as_ptr()).codecpar, Some(self.context.destructor())) }
    }

    pub fn index(&self) -> usize {
        unsafe { (*self.as_ptr()).index as usize }
    }

    pub fn time_base(&self) -> Rational {
        unsafe { Rational::from((*self.as_ptr()).time_base) }
    }

    pub fn start_time(&self) -> i64 {
        unsafe { (*self.as_ptr()).start_time }
    }

    pub fn duration(&self) -> i64 {
        unsafe { (*self.as_ptr()).duration }
    }

    pub fn frames(&self) -> i64 {
        unsafe { (*self.as_ptr()).nb_frames }
    }

    pub fn disposition(&self) -> Disposition {
        unsafe { Disposition::from_bits_truncate((*self.as_ptr()).disposition) }
    }

    pub fn discard(&self) -> Discard {
        unsafe { Discard::from((*self.as_ptr()).discard) }
    }

    pub fn side_data(&self) -> SideDataIter<'_> {
        SideDataIter::new(self)
    }

    pub fn rate(&self) -> Rational {
        unsafe { Rational::from((*self.as_ptr()).r_frame_rate) }
    }

    pub fn avg_frame_rate(&self) -> Rational {
        unsafe { Rational::from((*self.as_ptr()).avg_frame_rate) }
    }

    pub fn metadata(&self) -> DictionaryRef<'_> {
        unsafe { DictionaryRef::wrap((*self.as_ptr()).metadata) }
    }
}

impl<'a> PartialEq for Stream<'a> {
    fn eq(&self, other: &Self) -> bool {
        unsafe { self.as_ptr() == other.as_ptr() }
    }
}

impl<'a> Eq for Stream<'a> {}

pub struct SideDataIter<'a> {
    stream: &'a Stream<'a>,
    current: c_int,
}

impl<'a> SideDataIter<'a> {
    pub fn new<'sd, 's: 'sd>(stream: &'s Stream) -> SideDataIter<'sd> {
        SideDataIter { stream, current: 0 }
    }
}

impl<'a> Iterator for SideDataIter<'a> {
    type Item = packet::SideData<'a>;

    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
        #[cfg(not(feature = "ffmpeg_8_0"))]
        unsafe {
            if self.current >= (*self.stream.as_ptr()).nb_side_data {
                return None;
            }

            self.current += 1;

            Some(packet::SideData::wrap((*self.stream.as_ptr()).side_data.offset((self.current - 1) as isize)))
        }
        #[cfg(feature = "ffmpeg_8_0")]
        unsafe {
            if self.current >= (*self.stream.parameters().as_ptr()).nb_coded_side_data {
                return None;
            }

            self.current += 1;

            Some(packet::SideData::wrap((*self.stream.parameters().as_ptr()).coded_side_data.offset((self.current - 1) as isize) as *mut _))
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        unsafe {
            #[cfg(not(feature = "ffmpeg_8_0"))]
            let length = (*self.stream.as_ptr()).nb_side_data as usize;
            #[cfg(feature = "ffmpeg_8_0")]
            let length = (*self.stream.parameters().as_ptr()).nb_coded_side_data as usize;

            (length - self.current as usize, Some(length - self.current as usize))
        }
    }
}

impl<'a> ExactSizeIterator for SideDataIter<'a> {}