From 9496612afa737dcd6b68073edbc3000fe111fcad Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 15 Dec 2025 10:25:00 +0000 Subject: [PATCH] Refactor: Introduce FFmpegPacket RAII wrapper Co-authored-by: ijamespine --- crates/ffmpeg/src/audio_decoder.rs | 42 ++++++++++-------------------- crates/ffmpeg/src/lib.rs | 1 + crates/ffmpeg/src/packet.rs | 37 ++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 28 deletions(-) create mode 100644 crates/ffmpeg/src/packet.rs diff --git a/crates/ffmpeg/src/audio_decoder.rs b/crates/ffmpeg/src/audio_decoder.rs index 090f1e05a..cba0bde0c 100644 --- a/crates/ffmpeg/src/audio_decoder.rs +++ b/crates/ffmpeg/src/audio_decoder.rs @@ -4,15 +4,14 @@ use crate::{ codec_ctx::FFmpegCodecContext, error::{Error, FFmpegError}, format_ctx::FFmpegFormatContext, + packet::FFmpegPacket, utils::from_path, + video_frame::FFmpegFrame, }; use std::{path::Path, slice}; -use ffmpeg_sys_next::{ - av_frame_alloc, av_frame_free, av_packet_alloc, av_packet_free, av_packet_unref, av_read_frame, - avcodec_find_decoder, AVFrame, AVMediaType, AVSampleFormat, -}; +use ffmpeg_sys_next::{av_read_frame, avcodec_find_decoder, AVFrame, AVMediaType, AVSampleFormat}; /// Extract audio samples from a media file as 16kHz mono f32 PCM pub fn extract_audio_samples(filename: impl AsRef) -> Result, Error> { @@ -46,57 +45,44 @@ pub fn extract_audio_samples(filename: impl AsRef) -> Result, Err codec_ctx.parameters_to_context(codecpar)?; codec_ctx.open2(decoder)?; - // Allocate packet and frame - let packet = av_packet_alloc(); - if packet.is_null() { - return Err(FFmpegError::NullError.into()); - } - - let frame = av_frame_alloc(); - if frame.is_null() { - av_packet_free(&packet as *const _ as *mut _); - return Err(FFmpegError::FrameAllocation.into()); - } + // Allocate packet and frame using RAII wrappers for automatic cleanup + let mut packet = FFmpegPacket::new()?; + let mut frame = FFmpegFrame::new()?; let mut samples = Vec::new(); // Read and decode packets - while av_read_frame(format_ctx.as_mut(), packet) >= 0 { + while av_read_frame(format_ctx.as_mut(), packet.as_ptr()) >= 0 { let pkt = packet.as_ref().ok_or(FFmpegError::NullError)?; if pkt.stream_index == audio_stream_index { // Send packet to decoder - if codec_ctx.send_packet(packet).is_err() { - av_packet_unref(packet); + if codec_ctx.send_packet(packet.as_ptr()).is_err() { + packet.unref(); continue; } // Receive decoded frames loop { - match codec_ctx.receive_frame(frame) { + match codec_ctx.receive_frame(frame.as_mut()) { Ok(true) => { - let frame_ref = frame.as_ref().ok_or(FFmpegError::NullError)?; // Extract samples from this frame - let frame_samples = extract_and_convert_frame(frame_ref)?; + let frame_samples = extract_and_convert_frame(frame.as_ref())?; samples.extend_from_slice(&frame_samples); } Ok(false) | Err(FFmpegError::Again) => break, Err(e) => { - av_packet_unref(packet); - av_frame_free(&frame as *const _ as *mut _); - av_packet_free(&packet as *const _ as *mut _); + // RAII wrappers handle cleanup automatically via Drop return Err(e.into()); } } } } - av_packet_unref(packet); + packet.unref(); } - // Cleanup - av_frame_free(&frame as *const _ as *mut _); - av_packet_free(&packet as *const _ as *mut _); + // RAII wrappers handle cleanup automatically when they go out of scope // Now resample to 16kHz mono if needed let codec_ref = codec_ctx.as_ref(); diff --git a/crates/ffmpeg/src/lib.rs b/crates/ffmpeg/src/lib.rs index 3bee2b32d..df232cae8 100644 --- a/crates/ffmpeg/src/lib.rs +++ b/crates/ffmpeg/src/lib.rs @@ -41,6 +41,7 @@ mod filter_graph; mod format_ctx; mod frame_decoder; pub mod model; +mod packet; mod thumbnailer; mod utils; mod video_frame; diff --git a/crates/ffmpeg/src/packet.rs b/crates/ffmpeg/src/packet.rs new file mode 100644 index 000000000..9f1779009 --- /dev/null +++ b/crates/ffmpeg/src/packet.rs @@ -0,0 +1,37 @@ +use crate::error::FFmpegError; +use ffmpeg_sys_next::{av_packet_alloc, av_packet_free, av_packet_unref, AVPacket}; + +pub struct FFmpegPacket(*mut AVPacket); + +impl FFmpegPacket { + pub(crate) fn new() -> Result { + let ptr = unsafe { av_packet_alloc() }; + if ptr.is_null() { + return Err(FFmpegError::NullError); + } + Ok(Self(ptr)) + } + + pub(crate) fn as_ptr(&self) -> *mut AVPacket { + self.0 + } + + pub(crate) fn as_ref(&self) -> Option<&AVPacket> { + unsafe { self.0.as_ref() } + } + + pub(crate) fn unref(&mut self) { + if !self.0.is_null() { + unsafe { av_packet_unref(self.0) }; + } + } +} + +impl Drop for FFmpegPacket { + fn drop(&mut self) { + if !self.0.is_null() { + unsafe { av_packet_free(&mut self.0) }; + self.0 = std::ptr::null_mut(); + } + } +}