Skip to main content

ffmpeg_the_third/codec/
context.rs

1use std::ptr;
2
3use super::decoder::Decoder;
4use super::encoder::Encoder;
5use super::{threading, Compliance, Debug, Flags, Id};
6use crate::ffi::*;
7use crate::media;
8use crate::option;
9use crate::{AsMutPtr, AsPtr};
10use crate::{Codec, Error};
11use libc::c_int;
12
13#[cfg(not(feature = "ffmpeg_5_0"))]
14type OwnerHolder = std::sync::Arc<dyn std::any::Any>;
15#[cfg(feature = "ffmpeg_5_0")]
16type OwnerHolder = ();
17
18pub struct Context {
19    ptr: *mut AVCodecContext,
20    owner: Option<OwnerHolder>,
21}
22
23unsafe impl Send for Context {}
24
25impl Context {
26    pub unsafe fn wrap(ptr: *mut AVCodecContext, owner: Option<OwnerHolder>) -> Self {
27        Context { ptr, owner }
28    }
29
30    pub unsafe fn as_ptr(&self) -> *const AVCodecContext {
31        self.ptr as *const _
32    }
33
34    pub unsafe fn as_mut_ptr(&mut self) -> *mut AVCodecContext {
35        self.ptr
36    }
37}
38
39impl Context {
40    pub fn new() -> Self {
41        unsafe {
42            Context {
43                ptr: avcodec_alloc_context3(ptr::null()),
44                owner: None,
45            }
46        }
47    }
48
49    pub fn new_with_codec(codec: Codec) -> Self {
50        unsafe {
51            Context {
52                ptr: avcodec_alloc_context3(codec.as_ptr()),
53                owner: None,
54            }
55        }
56    }
57
58    pub fn from_parameters<P: AsPtr<AVCodecParameters>>(parameters: P) -> Result<Self, Error> {
59        let mut context = Self::new();
60
61        unsafe {
62            match avcodec_parameters_to_context(context.as_mut_ptr(), parameters.as_ptr()) {
63                e if e < 0 => Err(Error::from(e)),
64                _ => Ok(context),
65            }
66        }
67    }
68
69    pub fn decoder(self) -> Decoder {
70        Decoder(self)
71    }
72
73    pub fn encoder(self) -> Encoder {
74        Encoder(self)
75    }
76
77    pub fn codec(&self) -> Option<Codec> {
78        unsafe { Codec::from_raw((*self.as_ptr()).codec) }
79    }
80
81    pub fn medium(&self) -> media::Type {
82        unsafe { media::Type::from((*self.as_ptr()).codec_type) }
83    }
84
85    pub fn set_flags(&mut self, value: Flags) {
86        unsafe {
87            (*self.as_mut_ptr()).flags = value.bits() as c_int;
88        }
89    }
90
91    pub fn id(&self) -> Id {
92        unsafe { Id::from((*self.as_ptr()).codec_id) }
93    }
94
95    pub fn compliance(&mut self, value: Compliance) {
96        unsafe {
97            (*self.as_mut_ptr()).strict_std_compliance = value.into();
98        }
99    }
100
101    pub fn debug(&mut self, value: Debug) {
102        unsafe {
103            (*self.as_mut_ptr()).debug = value.bits();
104        }
105    }
106
107    pub fn set_threading(&mut self, config: threading::Config) {
108        unsafe {
109            (*self.as_mut_ptr()).thread_type = config.kind.into();
110            (*self.as_mut_ptr()).thread_count = config.count as c_int;
111            #[cfg(not(feature = "ffmpeg_6_0"))]
112            {
113                (*self.as_mut_ptr()).thread_safe_callbacks = i32::from(config.safe);
114            }
115        }
116    }
117
118    pub fn threading(&self) -> threading::Config {
119        unsafe {
120            threading::Config {
121                kind: threading::Type::from((*self.as_ptr()).active_thread_type),
122                count: (*self.as_ptr()).thread_count as usize,
123                #[cfg(not(feature = "ffmpeg_6_0"))]
124                safe: (*self.as_ptr()).thread_safe_callbacks != 0,
125            }
126        }
127    }
128
129    pub fn set_parameters<P: AsPtr<AVCodecParameters>>(
130        &mut self,
131        parameters: P,
132    ) -> Result<(), Error> {
133        unsafe {
134            match avcodec_parameters_to_context(self.as_mut_ptr(), parameters.as_ptr()) {
135                e if e < 0 => Err(Error::from(e)),
136                _ => Ok(()),
137            }
138        }
139    }
140}
141
142impl Default for Context {
143    fn default() -> Self {
144        Self::new()
145    }
146}
147
148impl Drop for Context {
149    fn drop(&mut self) {
150        unsafe {
151            if self.owner.is_none() {
152                avcodec_free_context(&mut self.ptr);
153            }
154        }
155    }
156}
157
158#[cfg(not(feature = "ffmpeg_5_0"))]
159impl Clone for Context {
160    fn clone(&self) -> Self {
161        let mut ctx = Context::new();
162        ctx.clone_from(self);
163
164        ctx
165    }
166
167    fn clone_from(&mut self, source: &Self) {
168        unsafe {
169            // Removed in ffmpeg >= 5.0.
170            avcodec_copy_context(self.as_mut_ptr(), source.as_ptr());
171        }
172    }
173}
174
175/// `AVCodecContext` in `Context` is the target of `option` operations.
176impl AsPtr<AVCodecContext> for Context {
177    fn as_ptr(&self) -> *const AVCodecContext {
178        self.ptr as *const _
179    }
180}
181
182impl AsMutPtr<AVCodecContext> for Context {
183    fn as_mut_ptr(&mut self) -> *mut AVCodecContext {
184        self.ptr as *mut _
185    }
186}
187
188impl option::Settable<AVCodecContext> for Context {}
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193    use crate::codec::{decoder, Id};
194
195    // This test exercises opening a decoder and dropping it.
196    // It should not segfault when the `Context` and `Opened` wrappers are dropped.
197    #[test]
198    fn open_and_drop_pgs_decoder_does_not_segfault() {
199        // Find the PGS subtitle decoder and open it using a fresh context.
200        let pgs = decoder::find(Id::HDMV_PGS_SUBTITLE)
201            .expect("PGS decoder must be available in linked FFmpeg");
202
203        let ctx = Context::new();
204        let _opened = ctx.decoder().open_as(pgs).expect("can open PGS decoder");
205
206        // Drop occurs at end of scope; success is lack of crash.
207    }
208}