1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
use crate::{ffi, shared::*}; use std::{ ffi::CStr, num::NonZeroI32, ops::Drop, ptr::{self, NonNull}, }; pub type AVSampleFormat = ffi::AVSampleFormat; /// Return the name of given sample_fmt, or `None` if sample_fmt is not /// recognized. /// /// ```rust /// # use rsmpeg::avutil::get_sample_fmt_name; /// # use rsmpeg::ffi::AVSampleFormat_AV_SAMPLE_FMT_FLT; /// # use std::ffi::CString; /// # fn main() { /// assert_eq!( /// CString::new("flt").ok().as_deref(), /// get_sample_fmt_name(AVSampleFormat_AV_SAMPLE_FMT_FLT) /// ); /// # } /// ``` pub fn get_sample_fmt_name(sample_fmt: AVSampleFormat) -> Option<&'static CStr> { unsafe { ffi::av_get_sample_fmt_name(sample_fmt) .upgrade() .map(|x| CStr::from_ptr(x.as_ptr())) } } /// Return a sample format corresponding to name, or None on error. /// /// ```rust /// # use rsmpeg::avutil::get_sample_fmt; /// # use rsmpeg::ffi::AVSampleFormat_AV_SAMPLE_FMT_FLT; /// # use std::ffi::CString; /// # fn main() { /// assert_eq!( /// Some(AVSampleFormat_AV_SAMPLE_FMT_FLT), /// get_sample_fmt(&CString::new("flt").unwrap()) /// ); /// # } /// ``` pub fn get_sample_fmt(name: &CStr) -> Option<AVSampleFormat> { let sample_fmt = unsafe { ffi::av_get_sample_fmt(name.as_ptr()) }; match sample_fmt { ffi::AVSampleFormat_AV_SAMPLE_FMT_NONE => None, _ => Some(sample_fmt), } } /// Get the packed alternative form of the given sample format, return `None` on /// error. /// /// i.e. [`AV_SAMPLE_FMT_S16P`](ffi::AVSampleFormat_AV_SAMPLE_FMT_S16P) => [`AV_SAMPLE_FMT_S16`](ffi::AVSampleFormat_AV_SAMPLE_FMT_S16) /// ```rust /// # use rsmpeg::avutil::get_packed_sample_fmt; /// # use rsmpeg::ffi::{AVSampleFormat_AV_SAMPLE_FMT_S16, AVSampleFormat_AV_SAMPLE_FMT_S16P}; /// # fn main() { /// assert_eq!( /// Some(AVSampleFormat_AV_SAMPLE_FMT_S16), /// get_packed_sample_fmt(AVSampleFormat_AV_SAMPLE_FMT_S16P) /// ); /// # } /// ``` pub fn get_packed_sample_fmt(sample_fmt: AVSampleFormat) -> Option<AVSampleFormat> { let sample_fmt = unsafe { ffi::av_get_packed_sample_fmt(sample_fmt) }; match sample_fmt { ffi::AVSampleFormat_AV_SAMPLE_FMT_NONE => None, _ => Some(sample_fmt), } } /// Get the planar alternative form of the given sample format. return `None` on /// error. /// /// i.e. [`AV_SAMPLE_FMT_S16`](ffi::AVSampleFormat_AV_SAMPLE_FMT_S16) => [`AV_SAMPLE_FMT_S16P`](ffi::AVSampleFormat_AV_SAMPLE_FMT_S16P) /// ```rust /// # use rsmpeg::avutil::get_planar_sample_fmt; /// # use rsmpeg::ffi::{AVSampleFormat_AV_SAMPLE_FMT_S16, AVSampleFormat_AV_SAMPLE_FMT_S16P}; /// # fn main() { /// assert_eq!( /// Some(AVSampleFormat_AV_SAMPLE_FMT_S16P), /// get_planar_sample_fmt(AVSampleFormat_AV_SAMPLE_FMT_S16) /// ); /// # } /// ``` pub fn get_planar_sample_fmt(sample_fmt: AVSampleFormat) -> Option<AVSampleFormat> { let sample_fmt = unsafe { ffi::av_get_planar_sample_fmt(sample_fmt) }; match sample_fmt { ffi::AVSampleFormat_AV_SAMPLE_FMT_NONE => None, _ => Some(sample_fmt), } } /// Return number of bytes per sample, return `None` when sample format is unknown. pub fn get_bytes_per_sample(sample_fmt: AVSampleFormat) -> Option<i32> { NonZeroI32::new(unsafe { ffi::av_get_bytes_per_sample(sample_fmt) }).map(NonZeroI32::get) } /// Check if the sample format is planar. /// /// Returns 1 if the sample format is planar, 0 if it is interleaved pub fn is_planar(sample_fmt: AVSampleFormat) -> bool { unsafe { ffi::av_sample_fmt_is_planar(sample_fmt) == 1 } } // The `nb_samples` of `AVSamples` is the capacity rather than length. wrap! { AVSamples: Vec<*mut u8>, linesize: i32 = 0, nb_channels: i32 = 0, nb_samples: i32 = 0, sample_fmt: AVSampleFormat = ffi::AVSampleFormat_AV_SAMPLE_FMT_NONE, align: i32 = 0, } impl AVSamples { /// Get the required (linesize, buffer_size) for the given audio parameters, /// returns `None` when parameters are invalid. /// /// ```txt /// nb_channels number of audio channels /// nb_samples number of samples per channel /// sample_fmt Audio sample formats /// align buffer size alignment (0 = default, 1 = no alignment) /// ``` pub fn get_buffer_size( nb_channels: i32, nb_samples: i32, sample_fmt: i32, align: i32, ) -> Option<(i32, i32)> { let mut linesize = 0; unsafe { ffi::av_samples_get_buffer_size( &mut linesize, nb_channels, nb_samples, sample_fmt, align, ) } .upgrade() .ok() .map(|buffer_size| (linesize, buffer_size)) } /// Allocate a data pointers array, samples buffer for nb_samples samples, /// and fill data pointers and linesize accordingly. /// /// ```txt /// nb_channels number of audio channels /// nb_samples number of samples per channel /// sample_fmt Audio sample formats /// align buffer size alignment (0 = default, 1 = no alignment) /// ``` pub fn new(nb_channels: i32, nb_samples: i32, sample_fmt: AVSampleFormat, align: i32) -> Self { // Implementation inspired by `av_samples_alloc_array_and_samples`. let nb_planes = if is_planar(sample_fmt) { nb_channels } else { 1 }; let mut audio_data = vec![ptr::null_mut(); nb_planes as usize]; let mut linesize = 0; // From the documentation, this function only error on no memory, so // unwrap. unsafe { ffi::av_samples_alloc( audio_data.as_mut_ptr(), &mut linesize, nb_channels, nb_samples, sample_fmt, align, ) } .upgrade() .unwrap(); // Leaks a Vec. let audio_data = Box::leak(Box::new(audio_data)); let mut samples = unsafe { AVSamples::from_raw(NonNull::new(audio_data).unwrap()) }; samples.linesize = linesize; samples.nb_channels = nb_channels; samples.nb_samples = nb_samples; samples.sample_fmt = sample_fmt; samples.align = align; samples } /// Fill an audio buffer with silence. /// `offset` offset in samples at which to start filling. /// `nb_samples` number of samples to fill. pub fn set_silence(&mut self, offset: i32, nb_samples: i32) { let x = unsafe { ffi::av_samples_set_silence( self.deref_mut().as_mut_ptr(), offset, nb_samples, self.nb_channels, self.sample_fmt, ) }; // From the ffmpeg implementation, `av_samples_set_silence` function // returns nothing but 0, so we can confidently throw the function // output. If this assert is triggered, please file an issue. debug_assert!(x == 0); } } impl Drop for AVSamples { fn drop(&mut self) { // Documentation states: // // The allocated samples buffer can be freed by using av_freep(&audio_data[0]) // Allocated data will be initialized to silence. // // Which means all the elements in this array shares the same buffer // (check the implementation of av_samples_fill_arrays). So we first // free the audio_data[0]. then free the audio_data array(since it's // allocated by `av_samples_alloc_array_and_samples`). unsafe { ffi::av_free(self[0].cast()) } // Recover the leaked vec, and drop it. let _x = unsafe { Box::from_raw(self.as_mut_ptr()) }; } }