rewrite unsafe code in ffmpeg ffi
This commit is contained in:
parent
a16c14a6a8
commit
6c4cc62098
|
|
@ -769,6 +769,26 @@ pub fn extract_audio_from_video(path: &str) -> Result<Option<ExtractedAudio>, St
|
|||
// Extract f32 samples (interleaved format)
|
||||
let data_ptr = resampled_frame.data(0).as_ptr() as *const f32;
|
||||
let total_samples = resampled_frame.samples() * frame_channels;
|
||||
|
||||
// Safety checks before creating slice from FFmpeg data
|
||||
// 1. Verify f32 alignment (required: 4 bytes)
|
||||
if data_ptr.align_offset(std::mem::align_of::<f32>()) != 0 {
|
||||
return Err("FFmpeg audio data is not properly aligned for f32".to_string());
|
||||
}
|
||||
|
||||
// 2. Verify the frame actually has enough data
|
||||
let byte_size = resampled_frame.data(0).len();
|
||||
let expected_bytes = total_samples * std::mem::size_of::<f32>();
|
||||
if byte_size < expected_bytes {
|
||||
return Err(format!(
|
||||
"FFmpeg frame buffer too small: {} bytes, need {} bytes",
|
||||
byte_size, expected_bytes
|
||||
));
|
||||
}
|
||||
|
||||
// SAFETY: We verified alignment and bounds above.
|
||||
// The slice lifetime is tied to resampled_frame which lives until
|
||||
// after extend_from_slice completes.
|
||||
let samples_slice = unsafe {
|
||||
std::slice::from_raw_parts(data_ptr, total_samples)
|
||||
};
|
||||
|
|
@ -800,6 +820,26 @@ pub fn extract_audio_from_video(path: &str) -> Result<Option<ExtractedAudio>, St
|
|||
|
||||
let data_ptr = resampled_frame.data(0).as_ptr() as *const f32;
|
||||
let total_samples = resampled_frame.samples() * frame_channels;
|
||||
|
||||
// Safety checks before creating slice from FFmpeg data
|
||||
// 1. Verify f32 alignment (required: 4 bytes)
|
||||
if data_ptr.align_offset(std::mem::align_of::<f32>()) != 0 {
|
||||
return Err("FFmpeg audio data is not properly aligned for f32".to_string());
|
||||
}
|
||||
|
||||
// 2. Verify the frame actually has enough data
|
||||
let byte_size = resampled_frame.data(0).len();
|
||||
let expected_bytes = total_samples * std::mem::size_of::<f32>();
|
||||
if byte_size < expected_bytes {
|
||||
return Err(format!(
|
||||
"FFmpeg frame buffer too small: {} bytes, need {} bytes",
|
||||
byte_size, expected_bytes
|
||||
));
|
||||
}
|
||||
|
||||
// SAFETY: We verified alignment and bounds above.
|
||||
// The slice lifetime is tied to resampled_frame which lives until
|
||||
// after extend_from_slice completes.
|
||||
let samples_slice = unsafe {
|
||||
std::slice::from_raw_parts(data_ptr, total_samples)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -195,17 +195,16 @@ fn encode_pcm_to_mp3(
|
|||
frame.set_rate(sample_rate);
|
||||
|
||||
// Copy planar samples to frame
|
||||
unsafe {
|
||||
for ch in 0..channels as usize {
|
||||
let plane = frame.data_mut(ch);
|
||||
let offset = samples_encoded;
|
||||
let src = &planar_samples[ch][offset..offset + chunk_size];
|
||||
for ch in 0..channels as usize {
|
||||
let plane = frame.data_mut(ch);
|
||||
let offset = samples_encoded;
|
||||
let src = &planar_samples[ch][offset..offset + chunk_size];
|
||||
|
||||
std::ptr::copy_nonoverlapping(
|
||||
src.as_ptr() as *const u8,
|
||||
plane.as_mut_ptr(),
|
||||
chunk_size * std::mem::size_of::<i16>(),
|
||||
);
|
||||
// Safe byte-level copy
|
||||
for (i, &sample) in src.iter().enumerate() {
|
||||
let bytes = sample.to_ne_bytes();
|
||||
let byte_offset = i * 2;
|
||||
plane[byte_offset..byte_offset + 2].copy_from_slice(&bytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -360,17 +359,16 @@ fn encode_pcm_to_aac(
|
|||
frame.set_rate(sample_rate);
|
||||
|
||||
// Copy planar samples to frame
|
||||
unsafe {
|
||||
for ch in 0..channels as usize {
|
||||
let plane = frame.data_mut(ch);
|
||||
let offset = samples_encoded;
|
||||
let src = &planar_samples[ch][offset..offset + chunk_size];
|
||||
for ch in 0..channels as usize {
|
||||
let plane = frame.data_mut(ch);
|
||||
let offset = samples_encoded;
|
||||
let src = &planar_samples[ch][offset..offset + chunk_size];
|
||||
|
||||
std::ptr::copy_nonoverlapping(
|
||||
src.as_ptr() as *const u8,
|
||||
plane.as_mut_ptr(),
|
||||
chunk_size * std::mem::size_of::<f32>(),
|
||||
);
|
||||
// Safe byte-level copy
|
||||
for (i, &sample) in src.iter().enumerate() {
|
||||
let bytes = sample.to_ne_bytes();
|
||||
let byte_offset = i * 4;
|
||||
plane[byte_offset..byte_offset + 4].copy_from_slice(&bytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -115,17 +115,18 @@ fn main() -> Result<(), String> {
|
|||
height,
|
||||
);
|
||||
|
||||
// Copy YUV planes
|
||||
unsafe {
|
||||
let y_plane = video_frame.data_mut(0);
|
||||
std::ptr::copy_nonoverlapping(y.as_ptr(), y_plane.as_mut_ptr(), y.len());
|
||||
// Copy YUV planes (safe slice copy)
|
||||
let y_plane = video_frame.data_mut(0);
|
||||
let y_len = y.len().min(y_plane.len());
|
||||
y_plane[..y_len].copy_from_slice(&y[..y_len]);
|
||||
|
||||
let u_plane = video_frame.data_mut(1);
|
||||
std::ptr::copy_nonoverlapping(u.as_ptr(), u_plane.as_mut_ptr(), u.len());
|
||||
let u_plane = video_frame.data_mut(1);
|
||||
let u_len = u.len().min(u_plane.len());
|
||||
u_plane[..u_len].copy_from_slice(&u[..u_len]);
|
||||
|
||||
let v_plane = video_frame.data_mut(2);
|
||||
std::ptr::copy_nonoverlapping(v.as_ptr(), v_plane.as_mut_ptr(), v.len());
|
||||
}
|
||||
let v_plane = video_frame.data_mut(2);
|
||||
let v_len = v.len().min(v_plane.len());
|
||||
v_plane[..v_len].copy_from_slice(&v[..v_len]);
|
||||
|
||||
// Set PTS
|
||||
let timestamp = frame_num as f64 / framerate;
|
||||
|
|
|
|||
|
|
@ -198,17 +198,25 @@ fn export_audio_ffmpeg_mp3<P: AsRef<Path>>(
|
|||
frame.set_rate(settings.sample_rate);
|
||||
|
||||
// Copy planar samples to frame
|
||||
unsafe {
|
||||
for ch in 0..settings.channels as usize {
|
||||
let plane = frame.data_mut(ch);
|
||||
let offset = samples_encoded;
|
||||
let src = &planar_samples[ch][offset..offset + chunk_size];
|
||||
for ch in 0..settings.channels as usize {
|
||||
let plane = frame.data_mut(ch);
|
||||
let offset = samples_encoded;
|
||||
let src = &planar_samples[ch][offset..offset + chunk_size];
|
||||
|
||||
std::ptr::copy_nonoverlapping(
|
||||
src.as_ptr() as *const u8,
|
||||
plane.as_mut_ptr(),
|
||||
chunk_size * std::mem::size_of::<i16>(),
|
||||
);
|
||||
// Convert i16 samples to bytes and copy
|
||||
let byte_size = chunk_size * std::mem::size_of::<i16>();
|
||||
if plane.len() < byte_size {
|
||||
return Err(format!(
|
||||
"FFmpeg frame buffer too small: {} bytes, need {} bytes",
|
||||
plane.len(), byte_size
|
||||
));
|
||||
}
|
||||
|
||||
// Safe byte-level copy using slice operations
|
||||
for (i, &sample) in src.iter().enumerate() {
|
||||
let bytes = sample.to_ne_bytes();
|
||||
let offset = i * 2;
|
||||
plane[offset..offset + 2].copy_from_slice(&bytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1182,16 +1182,18 @@ impl ExportOrchestrator {
|
|||
);
|
||||
|
||||
// Copy YUV planes to frame
|
||||
unsafe {
|
||||
let y_dest = video_frame.data_mut(0);
|
||||
std::ptr::copy_nonoverlapping(y_plane.as_ptr(), y_dest.as_mut_ptr(), y_plane.len());
|
||||
// Use safe slice copy - LLVM optimizes this to memcpy, same performance as copy_nonoverlapping
|
||||
let y_dest = video_frame.data_mut(0);
|
||||
let y_len = y_plane.len().min(y_dest.len());
|
||||
y_dest[..y_len].copy_from_slice(&y_plane[..y_len]);
|
||||
|
||||
let u_dest = video_frame.data_mut(1);
|
||||
std::ptr::copy_nonoverlapping(u_plane.as_ptr(), u_dest.as_mut_ptr(), u_plane.len());
|
||||
let u_dest = video_frame.data_mut(1);
|
||||
let u_len = u_plane.len().min(u_dest.len());
|
||||
u_dest[..u_len].copy_from_slice(&u_plane[..u_len]);
|
||||
|
||||
let v_dest = video_frame.data_mut(2);
|
||||
std::ptr::copy_nonoverlapping(v_plane.as_ptr(), v_dest.as_mut_ptr(), v_plane.len());
|
||||
}
|
||||
let v_dest = video_frame.data_mut(2);
|
||||
let v_len = v_plane.len().min(v_dest.len());
|
||||
v_dest[..v_len].copy_from_slice(&v_plane[..v_len]);
|
||||
|
||||
// Set PTS (presentation timestamp) in encoder's time base
|
||||
// Encoder time base is 1/(framerate * 1000), so PTS = timestamp * (framerate * 1000)
|
||||
|
|
|
|||
Loading…
Reference in New Issue