1 // Copyright 2022-2024 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 //! This module implements a lightweight and safe decoder interface over `libavcodec`. It is 6 //! designed to concentrate all calls to unsafe methods in one place, while providing the same 7 //! low-level access as the libavcodec functions do. 8 9 use std::ffi::CStr; 10 use std::fmt::Debug; 11 use std::fmt::Display; 12 use std::marker::PhantomData; 13 use std::mem::ManuallyDrop; 14 use std::ops::Deref; 15 16 use libc::c_char; 17 use libc::c_int; 18 use libc::c_void; 19 use thiserror::Error as ThisError; 20 21 use super::*; 22 use crate::ffmpeg::ffi::AVPictureType; 23 24 /// An error returned by a low-level libavcodec function. 25 #[derive(Debug, ThisError)] 26 pub struct AvError(pub libc::c_int); 27 28 impl AvError { result(ret: c_int) -> Result<(), Self>29 pub fn result(ret: c_int) -> Result<(), Self> { 30 if ret >= 0 { 31 Ok(()) 32 } else { 33 Err(AvError(ret)) 34 } 35 } 36 } 37 38 impl Display for AvError { fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 40 let mut buffer = [0u8; 255]; 41 let ret = 42 // SAFETY: 43 // Safe because we are passing valid bounds for the buffer. 44 unsafe { ffi::av_strerror(self.0, buffer.as_mut_ptr() as *mut c_char, buffer.len()) }; 45 match ret { 46 ret if ret >= 0 => { 47 let end_of_string = buffer.iter().position(|i| *i == 0).unwrap_or(buffer.len()); 48 let error_string = std::string::String::from_utf8_lossy(&buffer[..end_of_string]); 49 f.write_str(&error_string) 50 } 51 _ => f.write_fmt(format_args!("Unknown avcodec error {}", self.0)), 52 } 53 } 54 } 55 56 /// Lightweight abstraction over libavcodec's `AVCodec` struct, allowing the query the capabilities 57 /// of supported codecs and opening a session to work with them. 58 /// 59 /// `AVCodec` instances in libavcodec are all static, hence we can safely use a static reference 60 /// lifetime here. 61 #[derive(Copy, Clone)] 62 pub struct AvCodec(&'static ffi::AVCodec); 63 64 /// SAFETY: `AVCodec` is static and thus safe to share. 65 unsafe impl Send for AvCodec {} 66 67 #[derive(Debug, ThisError)] 68 pub enum AvCodecOpenError { 69 #[error("failed to allocate AVContext object")] 70 ContextAllocation, 71 #[error("failed to open AVContext object")] 72 ContextOpen, 73 #[error("ContextBuilder variant does not match codec type")] 74 UnexpectedCodecType, 75 } 76 77 /// Dimensions of a frame, used in AvCodecContext and AvFrame. 78 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 79 pub struct Dimensions { 80 pub width: u32, 81 pub height: u32, 82 } 83 84 impl AvCodec { 85 /// Returns whether the codec is a decoder. is_decoder(&self) -> bool86 pub fn is_decoder(&self) -> bool { 87 // SAFETY: 88 // Safe because `av_codec_is_decoder` is called on a valid static `AVCodec` reference. 89 (unsafe { ffi::av_codec_is_decoder(self.0) } != 0) 90 } 91 92 /// Returns whether the codec is an encoder. is_encoder(&self) -> bool93 pub fn is_encoder(&self) -> bool { 94 // SAFETY: 95 // Safe because `av_codec_is_encoder` is called on a valid static `AVCodec` reference. 96 (unsafe { ffi::av_codec_is_encoder(self.0) } != 0) 97 } 98 99 /// Returns the name of the codec. name(&self) -> &'static str100 pub fn name(&self) -> &'static str { 101 const INVALID_CODEC_STR: &str = "invalid codec"; 102 103 // SAFETY: 104 // Safe because `CStr::from_ptr` is called on a valid zero-terminated C string. 105 unsafe { CStr::from_ptr(self.0.name).to_str() }.unwrap_or(INVALID_CODEC_STR) 106 } 107 108 /// Returns the capabilities of the codec, as a mask of AV_CODEC_CAP_* bits. capabilities(&self) -> u32109 pub fn capabilities(&self) -> u32 { 110 self.0.capabilities as u32 111 } 112 113 /// Returns an iterator over the profiles supported by this codec. profile_iter(&self) -> AvProfileIterator114 pub fn profile_iter(&self) -> AvProfileIterator { 115 AvProfileIterator(self.0.profiles) 116 } 117 118 /// Returns an iterator over the pixel formats supported by this codec. 119 /// 120 /// For a decoder, the returned array will likely be empty. This means that ffmpeg's native 121 /// pixel format (YUV420) will be used. pixel_format_iter(&self) -> AvPixelFormatIterator122 pub fn pixel_format_iter(&self) -> AvPixelFormatIterator { 123 AvPixelFormatIterator(self.0.pix_fmts) 124 } 125 126 /// Get a builder for a encoder [`AvCodecContext`] using this codec. build_encoder(&self) -> Result<EncoderContextBuilder, AvCodecOpenError>127 pub fn build_encoder(&self) -> Result<EncoderContextBuilder, AvCodecOpenError> { 128 if !self.is_encoder() { 129 return Err(AvCodecOpenError::UnexpectedCodecType); 130 } 131 132 Ok(EncoderContextBuilder { 133 codec: self.0, 134 context: self.alloc_context()?, 135 }) 136 } 137 138 /// Get a builder for a decoder [`AvCodecContext`] using this codec. build_decoder(&self) -> Result<DecoderContextBuilder, AvCodecOpenError>139 pub fn build_decoder(&self) -> Result<DecoderContextBuilder, AvCodecOpenError> { 140 if !self.is_decoder() { 141 return Err(AvCodecOpenError::UnexpectedCodecType); 142 } 143 144 Ok(DecoderContextBuilder { 145 codec: self.0, 146 context: self.alloc_context()?, 147 }) 148 } 149 150 /// Internal helper for `build_decoder` to allocate an [`AvCodecContext`]. This needs to be 151 /// paired with a later call to [`AvCodecContext::init`]. alloc_context(&self) -> Result<AvCodecContext, AvCodecOpenError>152 fn alloc_context(&self) -> Result<AvCodecContext, AvCodecOpenError> { 153 // TODO(b:315859322): add safety doc string 154 #[allow(clippy::undocumented_unsafe_blocks)] 155 let context = unsafe { ffi::avcodec_alloc_context3(self.0).as_mut() } 156 .ok_or(AvCodecOpenError::ContextAllocation)?; 157 158 Ok(AvCodecContext(context)) 159 } 160 } 161 162 /// A builder to create a [`AvCodecContext`] suitable for decoding. 163 // This struct wraps an AvCodecContext directly, but the only way it can be taken out is to call 164 // `build()`, which finalizes the context and prevent further modification to the callback, etc. 165 pub struct DecoderContextBuilder { 166 codec: *const ffi::AVCodec, 167 context: AvCodecContext, 168 } 169 170 impl DecoderContextBuilder { 171 /// Set a custom callback that provides output buffers. 172 /// 173 /// `get_buffer2` is a function that decides which buffer is used to render a frame (see 174 /// libavcodec's documentation for `get_buffer2` for more details). If provided, this function 175 /// must be thread-safe. 176 /// `opaque` is a pointer that will be passed as first argument to `get_buffer2` when it is 177 /// called. set_get_buffer_2( &mut self, get_buffer2: unsafe extern "C" fn(*mut ffi::AVCodecContext, *mut ffi::AVFrame, i32) -> i32, opaque: *mut libc::c_void, )178 pub fn set_get_buffer_2( 179 &mut self, 180 get_buffer2: unsafe extern "C" fn(*mut ffi::AVCodecContext, *mut ffi::AVFrame, i32) -> i32, 181 opaque: *mut libc::c_void, 182 ) { 183 // SAFETY: 184 // Safe because self.context.0 is a pointer to a live AVCodecContext allocation. 185 let context = unsafe { &mut *(self.context.0) }; 186 context.get_buffer2 = Some(get_buffer2); 187 context.opaque = opaque; 188 } 189 190 /// Set the initial format for the context. 191 /// 192 /// This ensures the context has a valid resolution and pixel format right after its creation. set_initial_format(&self, coded_format: (u32, u32), pix_fmt: AvPixelFormat)193 pub fn set_initial_format(&self, coded_format: (u32, u32), pix_fmt: AvPixelFormat) { 194 // SAFETY: 195 // Safe because self.context.0 is a pointer to a live AVCodecContext allocation. 196 let context = unsafe { &mut *(self.context.0) }; 197 context.width = coded_format.0 as i32; 198 context.height = coded_format.1 as i32; 199 context.coded_width = coded_format.0 as i32; 200 context.coded_height = coded_format.1 as i32; 201 context.pix_fmt = pix_fmt.0; 202 } 203 204 /// Build a decoder AvCodecContext from the configured options. build(mut self) -> Result<AvCodecContext, AvCodecOpenError>205 pub fn build(mut self) -> Result<AvCodecContext, AvCodecOpenError> { 206 self.context.init(self.codec)?; 207 Ok(self.context) 208 } 209 } 210 211 /// A builder to create a [`AvCodecContext`] suitable for encoding. 212 // This struct wraps an AvCodecContext directly, but the only way it can be taken out is to call 213 // `build()`, which finalizes the context and prevent further modification to the callback, etc. 214 pub struct EncoderContextBuilder { 215 codec: *const ffi::AVCodec, 216 context: AvCodecContext, 217 } 218 219 impl EncoderContextBuilder { 220 /// Set the width of input frames for this encoding context. set_dimensions(&mut self, dimensions: Dimensions)221 pub fn set_dimensions(&mut self, dimensions: Dimensions) { 222 // TODO(b:315859322): add safety doc string 223 #[allow(clippy::undocumented_unsafe_blocks)] 224 let context = unsafe { &mut *(self.context.0) }; 225 context.width = dimensions.width as _; 226 context.height = dimensions.height as _; 227 } 228 229 /// Set the time base for this encoding context. set_time_base(&mut self, time_base: ffi::AVRational)230 pub fn set_time_base(&mut self, time_base: ffi::AVRational) { 231 // TODO(b:315859322): add safety doc string 232 #[allow(clippy::undocumented_unsafe_blocks)] 233 let context = unsafe { &mut *(self.context.0) }; 234 context.time_base = time_base; 235 } 236 237 /// Set the input pixel format for this encoding context. set_pix_fmt(&mut self, fmt: AvPixelFormat)238 pub fn set_pix_fmt(&mut self, fmt: AvPixelFormat) { 239 // TODO(b:315859322): add safety doc string 240 #[allow(clippy::undocumented_unsafe_blocks)] 241 let context = unsafe { &mut *(self.context.0) }; 242 context.pix_fmt = fmt.pix_fmt(); 243 } 244 245 /// Build a encoder AvCodecContext from the configured options. build(mut self) -> Result<AvCodecContext, AvCodecOpenError>246 pub fn build(mut self) -> Result<AvCodecContext, AvCodecOpenError> { 247 self.context.init(self.codec)?; 248 Ok(self.context) 249 } 250 } 251 252 impl Default for AvCodecIterator { default() -> Self253 fn default() -> Self { 254 Self::new() 255 } 256 } 257 258 /// Lightweight abstraction over libavcodec's `av_codec_iterate` function that can be used to 259 /// enumerate all the supported codecs. 260 pub struct AvCodecIterator(*mut libc::c_void); 261 262 impl AvCodecIterator { new() -> Self263 pub fn new() -> Self { 264 Self(std::ptr::null_mut()) 265 } 266 } 267 268 impl Iterator for AvCodecIterator { 269 type Item = AvCodec; 270 next(&mut self) -> Option<Self::Item>271 fn next(&mut self) -> Option<Self::Item> { 272 // SAFETY: 273 // Safe because our pointer was initialized to `NULL` and we only use it with 274 // `av_codec_iterate`, which will update it to a valid value. 275 unsafe { ffi::av_codec_iterate(&mut self.0 as *mut *mut libc::c_void).as_ref() } 276 .map(AvCodec) 277 } 278 } 279 280 /// Simple wrapper over `AVProfile` that provides helpful methods. 281 pub struct AvProfile(&'static ffi::AVProfile); 282 283 impl AvProfile { 284 /// Return the profile id, which can be matched against FF_PROFILE_*. profile(&self) -> u32285 pub fn profile(&self) -> u32 { 286 self.0.profile as u32 287 } 288 289 /// Return the name of this profile. name(&self) -> &'static str290 pub fn name(&self) -> &'static str { 291 const INVALID_PROFILE_STR: &str = "invalid profile"; 292 293 // SAFETY: 294 // Safe because `CStr::from_ptr` is called on a valid zero-terminated C string. 295 unsafe { CStr::from_ptr(self.0.name).to_str() }.unwrap_or(INVALID_PROFILE_STR) 296 } 297 } 298 299 impl Display for AvProfile { fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result300 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 301 f.write_str(self.name()) 302 } 303 } 304 305 impl Debug for AvProfile { fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result306 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 307 Display::fmt(self, f) 308 } 309 } 310 311 /// Lightweight abstraction over the array of supported profiles for a given codec. 312 pub struct AvProfileIterator(*const ffi::AVProfile); 313 314 impl Iterator for AvProfileIterator { 315 type Item = AvProfile; 316 next(&mut self) -> Option<Self::Item>317 fn next(&mut self) -> Option<Self::Item> { 318 // SAFETY: 319 // Safe because the contract of `new` stipulates we have received a valid `AVCodec` 320 // reference, thus the `profiles` pointer must either be NULL or point to a valid array 321 // or `VAProfile`s. 322 match unsafe { self.0.as_ref() } { 323 None => None, 324 Some(profile) => { 325 match profile.profile { 326 ffi::FF_PROFILE_UNKNOWN => None, 327 _ => { 328 // SAFETY: 329 // Safe because we have been initialized to a static, valid profiles array 330 // which is terminated by FF_PROFILE_UNKNOWN. 331 self.0 = unsafe { self.0.offset(1) }; 332 Some(AvProfile(profile)) 333 } 334 } 335 } 336 } 337 } 338 } 339 340 #[derive(Clone, Copy)] 341 /// Simple wrapper over `AVPixelFormat` that provides helpful methods. 342 pub struct AvPixelFormat(pub ffi::AVPixelFormat); 343 344 pub const AV_PIXEL_FORMAT_YUV420P: AvPixelFormat = 345 AvPixelFormat(ffi::AVPixelFormat_AV_PIX_FMT_YUV420P); 346 pub const AV_PIXEL_FORMAT_NV12: AvPixelFormat = AvPixelFormat(ffi::AVPixelFormat_AV_PIX_FMT_NV12); 347 348 impl From<AvPixelFormat> for ffi::AVPixelFormat { from(val: AvPixelFormat) -> Self349 fn from(val: AvPixelFormat) -> Self { 350 val.0 351 } 352 } 353 354 impl AvPixelFormat { 355 /// Return the name of this pixel format. name(&self) -> &'static str356 pub fn name(&self) -> &'static str { 357 const INVALID_FORMAT_STR: &str = "invalid pixel format"; 358 359 // SAFETY: 360 // Safe because `av_get_pix_fmt_name` returns either NULL or a valid C string. 361 let pix_fmt_name = unsafe { ffi::av_get_pix_fmt_name(self.0) }; 362 // SAFETY: 363 // Safe because `pix_fmt_name` is a valid pointer to a C string. 364 match unsafe { 365 pix_fmt_name 366 .as_ref() 367 .and_then(|s| CStr::from_ptr(s).to_str().ok()) 368 } { 369 None => INVALID_FORMAT_STR, 370 Some(string) => string, 371 } 372 } 373 374 /// Return the avcodec profile id, which can be matched against AV_PIX_FMT_*. 375 /// 376 /// Note that this is **not** the same as a fourcc. pix_fmt(&self) -> ffi::AVPixelFormat377 pub fn pix_fmt(&self) -> ffi::AVPixelFormat { 378 self.0 379 } 380 381 /// Return the fourcc of the pixel format, or a series of zeros if its fourcc is unknown. fourcc(&self) -> [u8; 4]382 pub fn fourcc(&self) -> [u8; 4] { 383 // SAFETY: 384 // Safe because `avcodec_pix_fmt_to_codec_tag` does not take any pointer as input and 385 // handles any value passed as argument. 386 unsafe { ffi::avcodec_pix_fmt_to_codec_tag(self.0) }.to_le_bytes() 387 } 388 389 /// Given the width and plane index, returns the line size (data pointer increment per row) in 390 /// bytes. line_size(&self, width: u32, plane: usize) -> usize391 pub fn line_size(&self, width: u32, plane: usize) -> usize { 392 // This should not fail if the format is correct. 393 av_image_line_size(*self, width, plane).unwrap_or_default() 394 } 395 396 /// Given an iterator of line sizes and height, return the size required for each plane's buffer 397 /// in bytes. plane_sizes<I: IntoIterator<Item = u32>>( &self, linesizes: I, height: u32, ) -> Vec<usize>398 pub fn plane_sizes<I: IntoIterator<Item = u32>>( 399 &self, 400 linesizes: I, 401 height: u32, 402 ) -> Vec<usize> { 403 av_image_plane_sizes(*self, linesizes, height).unwrap_or_default() 404 } 405 } 406 407 #[derive(Debug)] 408 pub struct FromAVPixelFormatError(()); 409 410 impl TryFrom<ffi::AVPixelFormat> for AvPixelFormat { 411 type Error = FromAVPixelFormatError; 412 try_from(value: ffi::AVPixelFormat) -> Result<Self, Self::Error>413 fn try_from(value: ffi::AVPixelFormat) -> Result<Self, Self::Error> { 414 if value > ffi::AVPixelFormat_AV_PIX_FMT_NONE && value < ffi::AVPixelFormat_AV_PIX_FMT_NB { 415 Ok(AvPixelFormat(value)) 416 } else { 417 Err(FromAVPixelFormatError(())) 418 } 419 } 420 } 421 422 impl Display for AvPixelFormat { fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result423 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 424 f.write_str(self.name()) 425 } 426 } 427 428 impl Debug for AvPixelFormat { fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result429 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 430 let fourcc = self.fourcc(); 431 f.write_fmt(format_args!( 432 "{}{}{}{}", 433 fourcc[0] as char, fourcc[1] as char, fourcc[2] as char, fourcc[3] as char 434 )) 435 } 436 } 437 438 /// Lightweight abstraction over the array of supported pixel formats for a given codec. 439 pub struct AvPixelFormatIterator(*const ffi::AVPixelFormat); 440 441 impl Iterator for AvPixelFormatIterator { 442 type Item = AvPixelFormat; 443 next(&mut self) -> Option<Self::Item>444 fn next(&mut self) -> Option<Self::Item> { 445 // SAFETY: 446 // Safe because the contract of `AvCodec::new` and `AvCodec::pixel_format_iter` guarantees 447 // that we have been built from a valid `AVCodec` reference, which `pix_fmts` pointer 448 // must either be NULL or point to a valid array or `VAPixelFormat`s. 449 match unsafe { self.0.as_ref() } { 450 None => None, 451 Some(&pixfmt) => { 452 match pixfmt { 453 // Array of pixel formats is terminated by AV_PIX_FMT_NONE. 454 ffi::AVPixelFormat_AV_PIX_FMT_NONE => None, 455 _ => { 456 // SAFETY: 457 // Safe because we have been initialized to a static, valid profiles array 458 // which is terminated by AV_PIX_FMT_NONE. 459 self.0 = unsafe { self.0.offset(1) }; 460 Some(AvPixelFormat(pixfmt)) 461 } 462 } 463 } 464 } 465 } 466 } 467 468 /// A codec context from which decoding can be performed. 469 pub struct AvCodecContext(*mut ffi::AVCodecContext); 470 471 impl Drop for AvCodecContext { drop(&mut self)472 fn drop(&mut self) { 473 // SAFETY: 474 // Safe because our context member is properly allocated and owned by us. 475 // Note: `avcodec_open2` might not have been called in case we're wrapped by a 476 // `DecoderContextBuilder` but avcodec_free_context works on both opened and closed 477 // contexts. 478 unsafe { ffi::avcodec_free_context(&mut self.0) }; 479 } 480 } 481 482 impl AsRef<ffi::AVCodecContext> for AvCodecContext { as_ref(&self) -> &ffi::AVCodecContext483 fn as_ref(&self) -> &ffi::AVCodecContext { 484 // SAFETY: 485 // Safe because our context member is properly initialized and fully owned by us. 486 unsafe { &*self.0 } 487 } 488 } 489 490 pub enum TryReceiveResult { 491 Received, 492 TryAgain, 493 FlushCompleted, 494 } 495 496 impl AvCodecContext { 497 /// Internal helper for [`DecoderContextBuilder`] to initialize the context. init(&mut self, codec: *const ffi::AVCodec) -> Result<(), AvCodecOpenError>498 fn init(&mut self, codec: *const ffi::AVCodec) -> Result<(), AvCodecOpenError> { 499 // SAFETY: 500 // Safe because `codec` is a valid static AVCodec reference, and `self.0` is a valid 501 // AVCodecContext allocation. 502 if unsafe { ffi::avcodec_open2(self.0, codec, std::ptr::null_mut()) } < 0 { 503 return Err(AvCodecOpenError::ContextOpen); 504 } 505 506 Ok(()) 507 } 508 509 /// Send a packet to be decoded by the codec. 510 /// 511 /// Returns `true` if the packet has been accepted and will be decoded, `false` if the codec can 512 /// not accept frames at the moment - in this case `try_receive_frame` must be called before 513 /// the packet can be submitted again. 514 /// 515 /// Error codes are the same as those returned by `avcodec_send_packet` with the exception of 516 /// EAGAIN which is converted into `Ok(false)` as it is not actually an error. try_send_packet(&mut self, packet: &AvPacket) -> Result<bool, AvError>517 pub fn try_send_packet(&mut self, packet: &AvPacket) -> Result<bool, AvError> { 518 // SAFETY: 519 // Safe because the context is valid through the life of this object, and `packet`'s 520 // lifetime properties ensures its memory area is readable. 521 match unsafe { ffi::avcodec_send_packet(self.0, &packet.packet) } { 522 AVERROR_EAGAIN => Ok(false), 523 ret if ret >= 0 => Ok(true), 524 err => Err(AvError(err)), 525 } 526 } 527 528 /// Attempt to write a decoded frame in `frame` if the codec has enough data to do so. 529 /// 530 /// Returns `Received` if `frame` has been filled with the next decoded frame, `TryAgain` if 531 /// no frame could be returned at that time (in which case `try_send_packet` should be called to 532 /// submit more input to decode), or `FlushCompleted` to signal that a previous flush triggered 533 /// by calling the `flush` method has completed. 534 /// 535 /// Error codes are the same as those returned by `avcodec_receive_frame` with the exception of 536 /// EAGAIN and EOF which are handled as `TryAgain` and `FlushCompleted` respectively. try_receive_frame(&mut self, frame: &mut AvFrame) -> Result<TryReceiveResult, AvError>537 pub fn try_receive_frame(&mut self, frame: &mut AvFrame) -> Result<TryReceiveResult, AvError> { 538 // SAFETY: 539 // Safe because the context is valid through the life of this object, and `avframe` is 540 // guaranteed to contain a properly initialized frame. 541 match unsafe { ffi::avcodec_receive_frame(self.0, frame.0) } { 542 AVERROR_EAGAIN => Ok(TryReceiveResult::TryAgain), 543 AVERROR_EOF => Ok(TryReceiveResult::FlushCompleted), 544 ret if ret >= 0 => Ok(TryReceiveResult::Received), 545 err => Err(AvError(err)), 546 } 547 } 548 549 /// Send a frame to be encoded by the codec. 550 /// 551 /// Returns `true` if the frame has been accepted and will be encoded, `false` if the codec can 552 /// not accept input at the moment - in this case `try_receive_frame` must be called before 553 /// the frame can be submitted again. 554 /// 555 /// Error codes are the same as those returned by `avcodec_send_frame` with the exception of 556 /// EAGAIN which is converted into `Ok(false)` as it is not actually an error. try_send_frame(&mut self, frame: &AvFrame) -> Result<bool, AvError>557 pub fn try_send_frame(&mut self, frame: &AvFrame) -> Result<bool, AvError> { 558 // TODO(b:315859322): add safety doc string 559 #[allow(clippy::undocumented_unsafe_blocks)] 560 match unsafe { ffi::avcodec_send_frame(self.0, frame.0 as *const _) } { 561 AVERROR_EAGAIN => Ok(false), 562 ret if ret >= 0 => Ok(true), 563 err => Err(AvError(err)), 564 } 565 } 566 567 /// Attempt to write an encoded frame in `packet` if the codec has enough data to do so. 568 /// 569 /// Returns `Received` if `packet` has been filled with encoded data, `TryAgain` if 570 /// no packet could be returned at that time (in which case `try_send_frame` should be called to 571 /// submit more input to decode), or `FlushCompleted` to signal that a previous flush triggered 572 /// by calling the `flush` method has completed. 573 /// 574 /// Error codes are the same as those returned by `avcodec_receive_packet` with the exception of 575 /// EAGAIN and EOF which are handled as `TryAgain` and `FlushCompleted` respectively. try_receive_packet( &mut self, packet: &mut AvPacket, ) -> Result<TryReceiveResult, AvError>576 pub fn try_receive_packet( 577 &mut self, 578 packet: &mut AvPacket, 579 ) -> Result<TryReceiveResult, AvError> { 580 // SAFETY: 581 // Safe because the context is valid through the life of this object, and `avframe` is 582 // guaranteed to contain a properly initialized frame. 583 match unsafe { ffi::avcodec_receive_packet(self.0, &mut packet.packet) } { 584 AVERROR_EAGAIN => Ok(TryReceiveResult::TryAgain), 585 AVERROR_EOF => Ok(TryReceiveResult::FlushCompleted), 586 ret if ret >= 0 => Ok(TryReceiveResult::Received), 587 err => Err(AvError(err)), 588 } 589 } 590 591 /// Reset the internal codec state/flush internal buffers. 592 /// Should be called e.g. when seeking or switching to a different stream. reset(&mut self)593 pub fn reset(&mut self) { 594 // SAFETY: 595 // Safe because the context is valid through the life of this object. 596 unsafe { ffi::avcodec_flush_buffers(self.0) } 597 } 598 599 /// Ask the context to start flushing, i.e. to process all pending input packets and produce 600 /// frames for them. 601 /// 602 /// The flush process is complete when `try_receive_frame` returns `FlushCompleted`, flush_decoder(&mut self) -> Result<(), AvError>603 pub fn flush_decoder(&mut self) -> Result<(), AvError> { 604 // SAFETY: 605 // Safe because the context is valid through the life of this object. 606 AvError::result(unsafe { ffi::avcodec_send_packet(self.0, std::ptr::null()) }) 607 } 608 609 /// Ask the context to start flushing, i.e. to process all pending input frames and produce 610 /// packets for them. 611 /// 612 /// The flush process is complete when `try_receive_packet` returns `FlushCompleted`, flush_encoder(&mut self) -> Result<(), AvError>613 pub fn flush_encoder(&mut self) -> Result<(), AvError> { 614 // SAFETY: 615 // Safe because the context is valid through the life of this object. 616 AvError::result(unsafe { ffi::avcodec_send_frame(self.0, std::ptr::null()) }) 617 } 618 619 /// Set the time base for this context. set_time_base(&mut self, time_base: AVRational)620 pub fn set_time_base(&mut self, time_base: AVRational) { 621 // TODO(b:315859322): add safety doc string 622 #[allow(clippy::undocumented_unsafe_blocks)] 623 let context = unsafe { &mut *(self.0) }; 624 context.time_base = time_base; 625 } 626 627 /// Set the bit rate for this context. set_bit_rate(&mut self, bit_rate: u64)628 pub fn set_bit_rate(&mut self, bit_rate: u64) { 629 // TODO(b:315859322): add safety doc string 630 #[allow(clippy::undocumented_unsafe_blocks)] 631 let context = unsafe { &mut *(self.0) }; 632 context.bit_rate = bit_rate as _; 633 } 634 635 /// Set the max bit rate (rc_max_rate) for this context. set_max_bit_rate(&mut self, bit_rate: u64)636 pub fn set_max_bit_rate(&mut self, bit_rate: u64) { 637 // TODO(b:315859322): add safety doc string 638 #[allow(clippy::undocumented_unsafe_blocks)] 639 let context = unsafe { &mut *(self.0) }; 640 context.rc_max_rate = bit_rate as _; 641 } 642 } 643 644 /// Trait for types that can be used as data provider for a `AVBuffer`. 645 /// 646 /// `AVBuffer` is an owned buffer type, so all the type needs to do is being able to provide a 647 /// stable pointer to its own data as well as its length. Implementors need to be sendable across 648 /// threads because avcodec is allowed to use threads in its codec implementations. 649 pub trait AvBufferSource: Send { as_ptr(&self) -> *const u8650 fn as_ptr(&self) -> *const u8; as_mut_ptr(&mut self) -> *mut u8651 fn as_mut_ptr(&mut self) -> *mut u8; 652 len(&self) -> usize653 fn len(&self) -> usize; 654 is_empty(&self) -> bool655 fn is_empty(&self) -> bool { 656 self.len() == 0 657 } 658 } 659 660 impl AvBufferSource for Vec<u8> { as_ptr(&self) -> *const u8661 fn as_ptr(&self) -> *const u8 { 662 self.as_ptr() 663 } 664 as_mut_ptr(&mut self) -> *mut u8665 fn as_mut_ptr(&mut self) -> *mut u8 { 666 self.as_mut_ptr() 667 } 668 len(&self) -> usize669 fn len(&self) -> usize { 670 self.len() 671 } 672 } 673 674 /// Wrapper around `AVBuffer` and `AVBufferRef`. 675 /// 676 /// libavcodec can manage its own memory for input and output data. Doing so implies a transparent 677 /// copy of user-provided data (packets or frames) from and to this memory, which is wasteful. 678 /// 679 /// This copy can be avoided by explicitly providing our own buffers to libavcodec using 680 /// `AVBufferRef`. Doing so means that the lifetime of these buffers becomes managed by avcodec. 681 /// This struct helps make this process safe by taking full ownership of an `AvBufferSource` and 682 /// dropping it when libavcodec is done with it. 683 pub struct AvBuffer(*mut ffi::AVBufferRef); 684 685 impl AvBuffer { 686 /// Create a new `AvBuffer` from an `AvBufferSource`. 687 /// 688 /// Ownership of `source` is transferred to libavcodec, which will drop it when the number of 689 /// references to this buffer reaches zero. 690 /// 691 /// Returns `None` if the buffer could not be created due to an error in libavcodec. new<D: AvBufferSource + 'static>(source: D) -> Option<Self>692 pub fn new<D: AvBufferSource + 'static>(source: D) -> Option<Self> { 693 // Move storage to the heap so we find it at the same place in `avbuffer_free` 694 let mut storage = Box::new(source); 695 696 extern "C" fn avbuffer_free<D>(opaque: *mut c_void, _data: *mut u8) { 697 // SAFETY: 698 // Safe because `opaque` has been created from `Box::into_raw`. `storage` will be 699 // dropped immediately which will release any resources held by the storage. 700 let _ = unsafe { Box::from_raw(opaque as *mut D) }; 701 } 702 703 // SAFETY: 704 // Safe because storage points to valid data throughout the lifetime of AVBuffer and we are 705 // checking the return value against NULL, which signals an error. 706 Some(Self(unsafe { 707 ffi::av_buffer_create( 708 storage.as_mut_ptr(), 709 storage.len(), 710 Some(avbuffer_free::<D>), 711 Box::into_raw(storage) as *mut c_void, 712 0, 713 ) 714 .as_mut()? 715 })) 716 } 717 718 /// Return a slice to the data contained in this buffer. as_mut_slice(&mut self) -> &mut [u8]719 pub fn as_mut_slice(&mut self) -> &mut [u8] { 720 // SAFETY: 721 // Safe because the data has been initialized from valid storage in the constructor. 722 unsafe { std::slice::from_raw_parts_mut((*self.0).data, (*self.0).size) } 723 } 724 725 /// Consumes the `AVBuffer`, returning a `AVBufferRef` that can be used in `AVFrame`, `AVPacket` 726 /// and others. 727 /// 728 /// After calling, the caller is responsible for unref-ing the returned AVBufferRef, either 729 /// directly or through one of the automatic management facilities in `AVFrame`, `AVPacket` or 730 /// others. into_raw(self) -> *mut ffi::AVBufferRef731 pub fn into_raw(self) -> *mut ffi::AVBufferRef { 732 ManuallyDrop::new(self).0 733 } 734 } 735 736 impl Drop for AvBuffer { drop(&mut self)737 fn drop(&mut self) { 738 // SAFETY: 739 // Safe because `self.0` is a valid pointer to an AVBufferRef. 740 unsafe { ffi::av_buffer_unref(&mut self.0) }; 741 } 742 } 743 744 /// An encoded input packet that can be submitted to `AvCodecContext::try_send_packet`. 745 pub struct AvPacket<'a> { 746 packet: ffi::AVPacket, 747 _buffer_data: PhantomData<&'a ()>, 748 } 749 750 impl<'a> Drop for AvPacket<'a> { drop(&mut self)751 fn drop(&mut self) { 752 // SAFETY: 753 // Safe because `self.packet` is a valid `AVPacket` instance. 754 unsafe { 755 ffi::av_packet_unref(&mut self.packet); 756 } 757 } 758 } 759 760 impl<'a> AsRef<ffi::AVPacket> for AvPacket<'a> { as_ref(&self) -> &ffi::AVPacket761 fn as_ref(&self) -> &ffi::AVPacket { 762 &self.packet 763 } 764 } 765 766 impl<'a> AvPacket<'a> { 767 /// Create an empty AvPacket without buffers. 768 /// 769 /// This packet should be only used with an encoder; in which case the encoder will 770 /// automatically allocate a buffer of appropriate size and store it inside this `AvPacket`. empty() -> Self771 pub fn empty() -> Self { 772 Self { 773 packet: ffi::AVPacket { 774 pts: AV_NOPTS_VALUE as i64, 775 dts: AV_NOPTS_VALUE as i64, 776 pos: -1, 777 // SAFETY: 778 // Safe because all the other elements of this struct can be zeroed. 779 ..unsafe { std::mem::zeroed() } 780 }, 781 _buffer_data: PhantomData, 782 } 783 } 784 785 /// Create a new AvPacket that borrows the `input_data`. 786 /// 787 /// The returned `AvPacket` will hold a reference to `input_data`, meaning that libavcodec might 788 /// perform a copy from/to it. new<T: AvBufferSource>(pts: i64, input_data: &'a mut T) -> Self789 pub fn new<T: AvBufferSource>(pts: i64, input_data: &'a mut T) -> Self { 790 Self { 791 packet: ffi::AVPacket { 792 buf: std::ptr::null_mut(), 793 pts, 794 dts: AV_NOPTS_VALUE as i64, 795 data: input_data.as_mut_ptr(), 796 size: input_data.len() as c_int, 797 side_data: std::ptr::null_mut(), 798 pos: -1, 799 // SAFETY: 800 // Safe because all the other elements of this struct can be zeroed. 801 ..unsafe { std::mem::zeroed() } 802 }, 803 _buffer_data: PhantomData, 804 } 805 } 806 807 /// Create a new AvPacket that owns the `av_buffer`. 808 /// 809 /// The returned `AvPacket` will have a `'static` lifetime and will keep `input_data` alive for 810 /// as long as libavcodec needs it. new_owned(pts: i64, mut av_buffer: AvBuffer) -> Self811 pub fn new_owned(pts: i64, mut av_buffer: AvBuffer) -> Self { 812 let data_slice = av_buffer.as_mut_slice(); 813 let data = data_slice.as_mut_ptr(); 814 let size = data_slice.len() as i32; 815 816 Self { 817 packet: ffi::AVPacket { 818 buf: av_buffer.into_raw(), 819 pts, 820 dts: AV_NOPTS_VALUE as i64, 821 data, 822 size, 823 side_data: std::ptr::null_mut(), 824 pos: -1, 825 // SAFETY: 826 // Safe because all the other elements of this struct can be zeroed. 827 ..unsafe { std::mem::zeroed() } 828 }, 829 _buffer_data: PhantomData, 830 } 831 } 832 } 833 834 /// An owned AVFrame, i.e. one decoded frame from libavcodec that can be converted into a 835 /// destination buffer. 836 pub struct AvFrame(*mut ffi::AVFrame); 837 838 /// A builder for AVFrame that allows specifying buffers and image metadata. 839 pub struct AvFrameBuilder(AvFrame); 840 841 /// A descriptor describing a subslice of `buffers` in [`AvFrameBuilder::build_owned`] that 842 /// represents a plane's image data. 843 #[derive(Debug, Clone)] 844 pub struct PlaneDescriptor { 845 /// The index within `buffers`. 846 pub buffer_index: usize, 847 /// The offset from the start of `buffers[buffer_index]`. 848 pub offset: usize, 849 /// The increment of data pointer in bytes per row of the plane. 850 pub stride: usize, 851 } 852 853 #[derive(Debug, ThisError)] 854 pub enum AvFrameError { 855 #[error("failed to allocate AVFrame object")] 856 FrameAllocationFailed, 857 #[error("dimension is negative or too large")] 858 DimensionOverflow, 859 #[error("a row does not fit in the specified stride")] 860 InvalidStride, 861 #[error("buffer index out of range")] 862 BufferOutOfRange, 863 #[error("specified dimensions overflow the buffer size")] 864 BufferTooSmall, 865 #[error("plane reference to buffer alias each other")] 866 BufferAlias, 867 #[error("error while calling libavcodec")] 868 AvError(#[from] AvError), 869 } 870 871 impl AvFrame { 872 /// Create a new AvFrame. The frame's parameters and backing memory will be assigned when it is 873 /// decoded into. new() -> Result<Self, AvFrameError>874 pub fn new() -> Result<Self, AvFrameError> { 875 Ok(Self( 876 // SAFETY: 877 // Safe because `av_frame_alloc` does not take any input. 878 unsafe { ffi::av_frame_alloc().as_mut() }.ok_or(AvFrameError::FrameAllocationFailed)?, 879 )) 880 } 881 882 /// Create a new AvFrame builder that allows setting the frame's parameters and backing memory 883 /// through its methods. builder() -> Result<AvFrameBuilder, AvFrameError>884 pub fn builder() -> Result<AvFrameBuilder, AvFrameError> { 885 AvFrame::new().map(AvFrameBuilder) 886 } 887 888 /// Return the frame's width and height. dimensions(&self) -> Dimensions889 pub fn dimensions(&self) -> Dimensions { 890 Dimensions { 891 width: self.as_ref().width as _, 892 height: self.as_ref().height as _, 893 } 894 } 895 896 /// Return the frame's pixel format. format(&self) -> AvPixelFormat897 pub fn format(&self) -> AvPixelFormat { 898 AvPixelFormat(self.as_ref().format) 899 } 900 901 /// Set the picture type (I-frame, P-frame etc.) on this frame. set_pict_type(&mut self, ty: AVPictureType)902 pub fn set_pict_type(&mut self, ty: AVPictureType) { 903 // SAFETY: 904 // Safe because self.0 is a valid AVFrame reference. 905 unsafe { 906 (*self.0).pict_type = ty; 907 } 908 } 909 910 /// Set the presentation timestamp (PTS) of this frame. set_pts(&mut self, ts: i64)911 pub fn set_pts(&mut self, ts: i64) { 912 // SAFETY: 913 // Safe because self.0 is a valid AVFrame reference. 914 unsafe { 915 (*self.0).pts = ts; 916 } 917 } 918 919 /// Query if this AvFrame is writable, i.e. it is refcounted and the refcounts are 1. is_writable(&self) -> bool920 pub fn is_writable(&self) -> bool { 921 // SAFETY: 922 // Safe because self.0 is a valid AVFrame reference. 923 unsafe { ffi::av_frame_is_writable(self.0) != 0 } 924 } 925 926 /// If the frame is not writable already (see [`is_writable`]), make a copy of its buffer to 927 /// make it writable. 928 /// 929 /// [`is_writable`]: AvFrame::is_writable make_writable(&mut self) -> Result<(), AvFrameError>930 pub fn make_writable(&mut self) -> Result<(), AvFrameError> { 931 // SAFETY: 932 // Safe because self.0 is a valid AVFrame reference. 933 AvError::result(unsafe { ffi::av_frame_make_writable(self.0) }).map_err(Into::into) 934 } 935 } 936 937 impl AvFrameBuilder { 938 /// Set the frame's width and height. 939 /// 940 /// The dimensions must not be greater than `i32::MAX`. set_dimensions(&mut self, dimensions: Dimensions) -> Result<(), AvFrameError>941 pub fn set_dimensions(&mut self, dimensions: Dimensions) -> Result<(), AvFrameError> { 942 // SAFETY: 943 // Safe because self.0 is a valid AVFrame instance and width and height are in range. 944 unsafe { 945 (*self.0 .0).width = dimensions 946 .width 947 .try_into() 948 .map_err(|_| AvFrameError::DimensionOverflow)?; 949 (*self.0 .0).height = dimensions 950 .height 951 .try_into() 952 .map_err(|_| AvFrameError::DimensionOverflow)?; 953 } 954 Ok(()) 955 } 956 957 /// Set the frame's format. set_format(&mut self, format: AvPixelFormat) -> Result<(), AvFrameError>958 pub fn set_format(&mut self, format: AvPixelFormat) -> Result<(), AvFrameError> { 959 // SAFETY: 960 // Safe because self.0 is a valid AVFrame instance and format is a valid pixel format. 961 unsafe { 962 (*self.0 .0).format = format.pix_fmt(); 963 } 964 Ok(()) 965 } 966 967 /// Build an AvFrame from iterators of [`AvBuffer`]s and subslice of buffers describing the 968 /// planes. 969 /// 970 /// The frame will own the `buffers`. 971 /// 972 /// This function checks that: 973 /// - Each plane fits inside the bounds of the associated buffer. 974 /// - Different planes do not overlap each other's buffer slice. In this check, all planes are 975 /// assumed to be potentially mutable, regardless of whether the AvFrame is actually used for 976 /// read or write access. Aliasing reference to the same buffer will be rejected, since it can 977 /// potentially allow routines to overwrite each 978 // other's result. 979 /// An exception to this is when the same buffer is passed multiple times in `buffers`. In 980 /// this case, each buffer is treated as a different buffer. Since clones have to be made to 981 /// be passed multiple times in `buffers`, the frame will not be considered [writable]. Hence 982 /// aliasing is safe in this case, but the caller is required to explicit opt-in to this 983 /// read-only handling by passing clones of the buffer into `buffers` and have a different 984 /// buffer index for each plane combination that could overlap in their range. 985 /// 986 /// [writable]: AvFrame::is_writable build_owned< BI: IntoIterator<Item = AvBuffer>, PI: IntoIterator<Item = PlaneDescriptor>, >( self, buffers: BI, planes: PI, ) -> Result<AvFrame, AvFrameError>987 pub fn build_owned< 988 BI: IntoIterator<Item = AvBuffer>, 989 PI: IntoIterator<Item = PlaneDescriptor>, 990 >( 991 self, 992 buffers: BI, 993 planes: PI, 994 ) -> Result<AvFrame, AvFrameError> { 995 let mut buffers: Vec<_> = buffers.into_iter().collect(); 996 let planes: Vec<_> = planes.into_iter().collect(); 997 let format = self.0.format(); 998 let plane_sizes = format.plane_sizes( 999 planes.iter().map(|x| x.stride as u32), 1000 self.0.dimensions().height, 1001 ); 1002 let mut ranges = vec![]; 1003 1004 for ( 1005 plane, 1006 PlaneDescriptor { 1007 buffer_index, 1008 offset, 1009 stride, 1010 }, 1011 ) in planes.into_iter().enumerate() 1012 { 1013 if buffer_index > buffers.len() { 1014 return Err(AvFrameError::BufferOutOfRange); 1015 } 1016 let end = offset + plane_sizes[plane]; 1017 if end > buffers[buffer_index].as_mut_slice().len() { 1018 return Err(AvFrameError::BufferTooSmall); 1019 } 1020 if stride < format.line_size(self.0.dimensions().width, plane) { 1021 return Err(AvFrameError::InvalidStride); 1022 } 1023 // TODO(b:315859322): add safety doc string 1024 #[allow(clippy::undocumented_unsafe_blocks)] 1025 unsafe { 1026 (*self.0 .0).data[plane] = 1027 buffers[buffer_index].as_mut_slice()[offset..].as_mut_ptr(); 1028 (*self.0 .0).linesize[plane] = stride as c_int; 1029 } 1030 ranges.push((buffer_index, offset, end)); 1031 } 1032 1033 // Check for range overlaps. 1034 // See function documentation for the exact rule and reasoning. 1035 ranges.sort_unstable(); 1036 for pair in ranges.windows(2) { 1037 // (buffer_index, start, end) 1038 let (b0, _s0, e0) = pair[0]; 1039 let (b1, s1, _e1) = pair[1]; 1040 1041 if b0 != b1 { 1042 continue; 1043 } 1044 // Note that s0 <= s1 always holds, so we only need to check 1045 // that the start of the second range is before the end of the first range. 1046 if s1 < e0 { 1047 return Err(AvFrameError::BufferAlias); 1048 } 1049 } 1050 1051 for (i, buf) in buffers.into_iter().enumerate() { 1052 // SAFETY: 1053 // Safe because self.0 is a valid AVFrame instance and buffers contains valid AvBuffers. 1054 unsafe { 1055 (*self.0 .0).buf[i] = buf.into_raw(); 1056 } 1057 } 1058 Ok(self.0) 1059 } 1060 } 1061 1062 impl AsRef<ffi::AVFrame> for AvFrame { as_ref(&self) -> &ffi::AVFrame1063 fn as_ref(&self) -> &ffi::AVFrame { 1064 // SAFETY: 1065 // Safe because the AVFrame has been properly initialized during construction. 1066 unsafe { &*self.0 } 1067 } 1068 } 1069 1070 impl Deref for AvFrame { 1071 type Target = ffi::AVFrame; 1072 deref(&self) -> &Self::Target1073 fn deref(&self) -> &Self::Target { 1074 // SAFETY: 1075 // Safe because the AVFrame has been properly initialized during construction. 1076 unsafe { self.0.as_ref().unwrap() } 1077 } 1078 } 1079 1080 impl Drop for AvFrame { drop(&mut self)1081 fn drop(&mut self) { 1082 // SAFETY: 1083 // Safe because the AVFrame is valid through the life of this object and fully owned by us. 1084 unsafe { ffi::av_frame_free(&mut self.0) }; 1085 } 1086 } 1087 1088 #[cfg(test)] 1089 mod tests { 1090 use std::sync::atomic::AtomicBool; 1091 use std::sync::atomic::Ordering; 1092 use std::sync::Arc; 1093 1094 use super::*; 1095 1096 #[test] test_averror()1097 fn test_averror() { 1098 // Just test that the error is wrapper properly. The bindings test module already checks 1099 // that the error bindings correspond to the right ffmpeg errors. 1100 let averror = AvError(AVERROR_EOF); 1101 let msg = format!("{}", averror); 1102 assert_eq!(msg, "End of file"); 1103 1104 let averror = AvError(0); 1105 let msg = format!("{}", averror); 1106 assert_eq!(msg, "Success"); 1107 1108 let averror = AvError(10); 1109 let msg = format!("{}", averror); 1110 assert_eq!(msg, "Unknown avcodec error 10"); 1111 } 1112 1113 // Test that the AVPacket wrapper frees the owned AVBuffer on drop. 1114 #[test] test_avpacket_drop()1115 fn test_avpacket_drop() { 1116 struct DropTestBufferSource { 1117 dropped: Arc<AtomicBool>, 1118 } 1119 impl Drop for DropTestBufferSource { 1120 fn drop(&mut self) { 1121 self.dropped.store(true, Ordering::SeqCst); 1122 } 1123 } 1124 impl AvBufferSource for DropTestBufferSource { 1125 fn as_ptr(&self) -> *const u8 { 1126 self as *const _ as *const u8 1127 } 1128 1129 fn as_mut_ptr(&mut self) -> *mut u8 { 1130 self as *mut _ as *mut u8 1131 } 1132 1133 fn len(&self) -> usize { 1134 0 1135 } 1136 } 1137 1138 let dropped = Arc::new(AtomicBool::new(false)); 1139 1140 let pkt = AvPacket::new_owned( 1141 0, 1142 AvBuffer::new(DropTestBufferSource { 1143 dropped: dropped.clone(), 1144 }) 1145 .unwrap(), 1146 ); 1147 assert!(!dropped.load(Ordering::SeqCst)); 1148 drop(pkt); 1149 assert!(dropped.load(Ordering::SeqCst)); 1150 } 1151 } 1152