| 1 | // Take a look at the license at the top of the repository in the LICENSE file. |
| 2 | |
| 3 | use std::{fmt, marker::PhantomData, ptr}; |
| 4 | |
| 5 | use glib::translate::*; |
| 6 | |
| 7 | use crate::{ffi, utils::HasStreamLock, video_info::VideoInfo}; |
| 8 | |
| 9 | pub trait VideoCodecStateContext<'a> { |
| 10 | #[doc (alias = "get_element" )] |
| 11 | fn element(&self) -> Option<&'a dyn HasStreamLock>; |
| 12 | #[doc (alias = "get_element_as_ptr" )] |
| 13 | fn element_as_ptr(&self) -> *const gst::ffi::GstElement; |
| 14 | } |
| 15 | |
| 16 | pub struct InNegotiation<'a> { |
| 17 | /* GstVideoCodecState API isn't safe so protect the state using the |
| 18 | * element (decoder or encoder) stream lock */ |
| 19 | element: &'a dyn HasStreamLock, |
| 20 | } |
| 21 | pub struct Readable {} |
| 22 | |
| 23 | impl<'a> VideoCodecStateContext<'a> for InNegotiation<'a> { |
| 24 | #[inline ] |
| 25 | fn element(&self) -> Option<&'a dyn HasStreamLock> { |
| 26 | Some(self.element) |
| 27 | } |
| 28 | |
| 29 | #[inline ] |
| 30 | fn element_as_ptr(&self) -> *const gst::ffi::GstElement { |
| 31 | self.element.element_as_ptr() |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | impl<'a> VideoCodecStateContext<'a> for Readable { |
| 36 | #[inline ] |
| 37 | fn element(&self) -> Option<&'a dyn HasStreamLock> { |
| 38 | None |
| 39 | } |
| 40 | |
| 41 | #[inline ] |
| 42 | fn element_as_ptr(&self) -> *const gst::ffi::GstElement { |
| 43 | ptr::null() |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | pub struct VideoCodecState<'a, T: VideoCodecStateContext<'a>> { |
| 48 | state: *mut ffi::GstVideoCodecState, |
| 49 | pub(crate) context: T, |
| 50 | phantom: PhantomData<&'a T>, |
| 51 | } |
| 52 | |
| 53 | impl<'a, T: VideoCodecStateContext<'a>> fmt::Debug for VideoCodecState<'a, T> { |
| 54 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 55 | f&mut DebugStruct<'_, '_>.debug_struct("VideoCodecState" ) |
| 56 | .field("info" , &self.info()) |
| 57 | .field("caps" , &self.caps()) |
| 58 | .field("codec_data" , &self.codec_data()) |
| 59 | .field(name:"allocation_caps" , &self.allocation_caps()) |
| 60 | .finish() |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | impl VideoCodecState<'_, Readable> { |
| 65 | // Take ownership of @state |
| 66 | #[inline ] |
| 67 | pub(crate) unsafe fn new(state: *mut ffi::GstVideoCodecState) -> Self { |
| 68 | skip_assert_initialized!(); |
| 69 | Self { |
| 70 | state, |
| 71 | context: Readable {}, |
| 72 | phantom: PhantomData, |
| 73 | } |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | impl<'a> VideoCodecState<'a, InNegotiation<'a>> { |
| 78 | // Take ownership of @state |
| 79 | #[inline ] |
| 80 | pub(crate) unsafe fn new<T: HasStreamLock>( |
| 81 | state: *mut ffi::GstVideoCodecState, |
| 82 | element: &'a T, |
| 83 | ) -> Self { |
| 84 | skip_assert_initialized!(); |
| 85 | let stream_lock: *mut GRecMutex = element.stream_lock(); |
| 86 | glib::ffi::g_rec_mutex_lock(rec_mutex:stream_lock); |
| 87 | Self { |
| 88 | state, |
| 89 | context: InNegotiation { element }, |
| 90 | phantom: PhantomData, |
| 91 | } |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | impl<'a, T: VideoCodecStateContext<'a>> VideoCodecState<'a, T> { |
| 96 | #[doc (alias = "get_info" )] |
| 97 | #[inline ] |
| 98 | pub fn info(&self) -> VideoInfo { |
| 99 | unsafe { |
| 100 | VideoInfo::from_glib_none(&((*self.as_mut_ptr()).info) as *const ffi::GstVideoInfo) |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | #[doc (alias = "get_caps" )] |
| 105 | #[inline ] |
| 106 | pub fn caps(&self) -> Option<&gst::CapsRef> { |
| 107 | unsafe { |
| 108 | let ptr = (*self.as_mut_ptr()).caps; |
| 109 | |
| 110 | if ptr.is_null() { |
| 111 | None |
| 112 | } else { |
| 113 | Some(gst::CapsRef::from_ptr(ptr)) |
| 114 | } |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | #[doc (alias = "get_caps" )] |
| 119 | #[inline ] |
| 120 | pub fn caps_owned(&self) -> Option<gst::Caps> { |
| 121 | unsafe { from_glib_none((*self.as_mut_ptr()).caps) } |
| 122 | } |
| 123 | |
| 124 | #[doc (alias = "get_codec_data" )] |
| 125 | #[inline ] |
| 126 | pub fn codec_data(&self) -> Option<&gst::BufferRef> { |
| 127 | unsafe { |
| 128 | let ptr = (*self.as_mut_ptr()).codec_data; |
| 129 | |
| 130 | if ptr.is_null() { |
| 131 | None |
| 132 | } else { |
| 133 | Some(gst::BufferRef::from_ptr(ptr)) |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | #[doc (alias = "get_codec_data" )] |
| 139 | #[inline ] |
| 140 | pub fn codec_data_owned(&self) -> Option<gst::Buffer> { |
| 141 | unsafe { from_glib_none((*self.as_mut_ptr()).codec_data) } |
| 142 | } |
| 143 | |
| 144 | #[doc (alias = "get_allocation_caps" )] |
| 145 | #[inline ] |
| 146 | pub fn allocation_caps(&self) -> Option<&gst::CapsRef> { |
| 147 | unsafe { |
| 148 | let ptr = (*self.as_mut_ptr()).allocation_caps; |
| 149 | |
| 150 | if ptr.is_null() { |
| 151 | None |
| 152 | } else { |
| 153 | Some(gst::CapsRef::from_ptr(ptr)) |
| 154 | } |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | #[doc (alias = "get_allocation_caps" )] |
| 159 | #[inline ] |
| 160 | pub fn allocation_caps_owned(&self) -> Option<gst::Caps> { |
| 161 | unsafe { from_glib_none((*self.as_mut_ptr()).allocation_caps) } |
| 162 | } |
| 163 | |
| 164 | #[doc (hidden)] |
| 165 | #[inline ] |
| 166 | pub fn as_mut_ptr(&self) -> *mut ffi::GstVideoCodecState { |
| 167 | self.state |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | impl<'a, T: VideoCodecStateContext<'a>> Drop for VideoCodecState<'a, T> { |
| 172 | #[inline ] |
| 173 | fn drop(&mut self) { |
| 174 | unsafe { |
| 175 | if let Some(element: &'a dyn HasStreamLock) = self.context.element() { |
| 176 | let stream_lock: *mut GRecMutex = element.stream_lock(); |
| 177 | glib::ffi::g_rec_mutex_unlock(rec_mutex:stream_lock); |
| 178 | } |
| 179 | ffi::gst_video_codec_state_unref(self.state); |
| 180 | } |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | impl<'a> VideoCodecState<'a, InNegotiation<'a>> { |
| 185 | #[inline ] |
| 186 | pub fn set_info(&mut self, info: VideoInfo) { |
| 187 | unsafe { |
| 188 | ptr::write(&mut (*self.as_mut_ptr()).info, *(info.to_glib_none().0)); |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | #[inline ] |
| 193 | pub fn set_caps(&mut self, caps: &gst::Caps) { |
| 194 | unsafe { |
| 195 | let prev = (*self.as_mut_ptr()).caps; |
| 196 | |
| 197 | if !prev.is_null() { |
| 198 | gst::ffi::gst_mini_object_unref(prev as *mut gst::ffi::GstMiniObject) |
| 199 | } |
| 200 | |
| 201 | ptr::write( |
| 202 | &mut (*self.as_mut_ptr()).caps, |
| 203 | gst::ffi::gst_mini_object_ref(caps.as_mut_ptr() as *mut _) as *mut _, |
| 204 | ); |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | #[inline ] |
| 209 | pub fn set_codec_data(&mut self, codec_data: &gst::Buffer) { |
| 210 | unsafe { |
| 211 | let prev = (*self.as_mut_ptr()).codec_data; |
| 212 | |
| 213 | if !prev.is_null() { |
| 214 | gst::ffi::gst_mini_object_unref(prev as *mut gst::ffi::GstMiniObject) |
| 215 | } |
| 216 | |
| 217 | ptr::write( |
| 218 | &mut (*self.as_mut_ptr()).codec_data, |
| 219 | gst::ffi::gst_mini_object_ref(codec_data.as_mut_ptr() as *mut _) as *mut _, |
| 220 | ); |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | #[inline ] |
| 225 | pub fn set_allocation_caps(&mut self, allocation_caps: &gst::Caps) { |
| 226 | unsafe { |
| 227 | let prev = (*self.as_mut_ptr()).allocation_caps; |
| 228 | |
| 229 | if !prev.is_null() { |
| 230 | gst::ffi::gst_mini_object_unref(prev as *mut gst::ffi::GstMiniObject) |
| 231 | } |
| 232 | |
| 233 | ptr::write( |
| 234 | &mut (*self.as_mut_ptr()).allocation_caps, |
| 235 | gst::ffi::gst_mini_object_ref(allocation_caps.as_mut_ptr() as *mut _) as *mut _, |
| 236 | ); |
| 237 | } |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | impl Clone for VideoCodecState<'_, Readable> { |
| 242 | #[inline ] |
| 243 | fn clone(&self) -> Self { |
| 244 | unsafe { |
| 245 | let state: *mut GstVideoCodecState = ffi::gst_video_codec_state_ref(self.state); |
| 246 | Self::new(state) |
| 247 | } |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | unsafe impl Send for VideoCodecState<'_, Readable> {} |
| 252 | unsafe impl Sync for VideoCodecState<'_, Readable> {} |
| 253 | |