feat: scale tests

This commit is contained in:
kieran 2024-11-06 14:12:45 +00:00
parent cf3ce4348e
commit f8a085af09
No known key found for this signature in database
GPG Key ID: DE71CEB3925BE941
7 changed files with 110 additions and 70 deletions

View File

@ -1,8 +1,5 @@
use ffmpeg_rs_raw::{Decoder, Demuxer, DemuxerInfo, Filter, Scaler}; use ffmpeg_rs_raw::{Decoder, Demuxer, DemuxerInfo, Filter};
use ffmpeg_sys_the_third::AVHWDeviceType::{ use ffmpeg_sys_the_third::AVHWDeviceType::AV_HWDEVICE_TYPE_CUDA;
AV_HWDEVICE_TYPE_CUDA, AV_HWDEVICE_TYPE_D3D12VA, AV_HWDEVICE_TYPE_MEDIACODEC,
AV_HWDEVICE_TYPE_OPENCL, AV_HWDEVICE_TYPE_VDPAU, AV_HWDEVICE_TYPE_VULKAN,
};
use ffmpeg_sys_the_third::{av_frame_free, av_packet_free, AVMediaType}; use ffmpeg_sys_the_third::{av_frame_free, av_packet_free, AVMediaType};
use log::{error, info}; use log::{error, info};
use std::env::args; use std::env::args;

View File

@ -1,4 +1,4 @@
use crate::{options_to_dict, return_ffmpeg_error, StreamInfoChannel}; use crate::{options_to_dict, return_ffmpeg_error, rstr, StreamInfoChannel};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::ffi::CStr; use std::ffi::CStr;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -46,9 +46,7 @@ impl Drop for DecoderCodecContext {
impl Display for DecoderCodecContext { impl Display for DecoderCodecContext {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
unsafe { unsafe {
let codec_name = CStr::from_ptr(avcodec_get_name((*self.codec).id)) let codec_name = rstr!(avcodec_get_name((*self.codec).id));
.to_str()
.unwrap();
write!( write!(
f, f,
"DecoderCodecContext: codec={}, hw={}", "DecoderCodecContext: codec={}, hw={}",
@ -56,9 +54,7 @@ impl Display for DecoderCodecContext {
if self.hw_config.is_null() { if self.hw_config.is_null() {
"no" "no"
} else { } else {
CStr::from_ptr(av_hwdevice_get_type_name((*self.hw_config).device_type)) rstr!(av_hwdevice_get_type_name((*self.hw_config).device_type))
.to_str()
.unwrap()
} }
) )
} }
@ -148,7 +144,7 @@ impl Decoder {
if codec.is_null() { if codec.is_null() {
anyhow::bail!( anyhow::bail!(
"Failed to find codec: {}", "Failed to find codec: {}",
CStr::from_ptr(avcodec_get_name((*codec_par).codec_id)).to_str()? rstr!(avcodec_get_name((*codec_par).codec_id))
) )
} }
let context = avcodec_alloc_context3(codec); let context = avcodec_alloc_context3(codec);
@ -159,7 +155,7 @@ impl Decoder {
let mut ret = avcodec_parameters_to_context(context, (*stream).codecpar); let mut ret = avcodec_parameters_to_context(context, (*stream).codecpar);
return_ffmpeg_error!(ret, "Failed to copy codec parameters to context"); return_ffmpeg_error!(ret, "Failed to copy codec parameters to context");
let codec_name = CStr::from_ptr(avcodec_get_name((*codec).id)).to_str()?; let codec_name = rstr!(avcodec_get_name((*codec).id));
// try use HW decoder // try use HW decoder
let mut hw_config = ptr::null(); let mut hw_config = ptr::null();
if let Some(ref hw_types) = self.hw_decoder_types { if let Some(ref hw_types) = self.hw_decoder_types {
@ -171,9 +167,7 @@ impl Decoder {
if hw_config.is_null() { if hw_config.is_null() {
break; break;
} }
let hw_name = let hw_name = rstr!(av_hwdevice_get_type_name((*hw_config).device_type));
CStr::from_ptr(av_hwdevice_get_type_name((*hw_config).device_type))
.to_str()?;
if !hw_types.contains(&(*hw_config).device_type) { if !hw_types.contains(&(*hw_config).device_type) {
debug!("skipping hwaccel={}_{}", codec_name, hw_name); debug!("skipping hwaccel={}_{}", codec_name, hw_name);
continue; continue;
@ -234,7 +228,7 @@ impl Decoder {
let mut pkgs = Vec::new(); let mut pkgs = Vec::new();
while ret >= 0 { while ret >= 0 {
let mut frame = av_frame_alloc(); let frame = av_frame_alloc();
ret = avcodec_receive_frame(ctx.context, frame); ret = avcodec_receive_frame(ctx.context, frame);
if ret < 0 { if ret < 0 {
if ret == AVERROR_EOF || ret == AVERROR(libc::EAGAIN) { if ret == AVERROR_EOF || ret == AVERROR(libc::EAGAIN) {

View File

@ -1,4 +1,4 @@
use crate::return_ffmpeg_error; use crate::{cstr, return_ffmpeg_error};
use crate::{get_ffmpeg_error_msg, DemuxerInfo, StreamChannelType, StreamInfoChannel}; use crate::{get_ffmpeg_error_msg, DemuxerInfo, StreamChannelType, StreamInfoChannel};
use anyhow::Error; use anyhow::Error;
use ffmpeg_sys_the_third::*; use ffmpeg_sys_the_third::*;
@ -68,7 +68,7 @@ impl Demuxer {
match &mut self.input { match &mut self.input {
DemuxerInput::Url(input) => avformat_open_input( DemuxerInput::Url(input) => avformat_open_input(
&mut self.ctx, &mut self.ctx,
format!("{}\0", input).as_ptr() as *const libc::c_char, cstr!(input),
ptr::null_mut(), ptr::null_mut(),
ptr::null_mut(), ptr::null_mut(),
), ),
@ -89,7 +89,7 @@ impl Demuxer {
avformat_open_input( avformat_open_input(
&mut self.ctx, &mut self.ctx,
if let Some(url) = url { if let Some(url) = url {
format!("{}\0", url).as_ptr() as *const libc::c_char cstr!(url)
} else { } else {
ptr::null_mut() ptr::null_mut()
}, },

View File

@ -1,10 +1,9 @@
use crate::{cstr, return_ffmpeg_error, set_opts}; use crate::{cstr, return_ffmpeg_error, rstr, set_opts};
use anyhow::Error; use anyhow::Error;
use ffmpeg_sys_the_third::{ use ffmpeg_sys_the_third::{
av_free, av_strdup, avfilter_get_by_name, avfilter_graph_alloc, avfilter_graph_alloc_filter, av_free, av_strdup, avfilter_get_by_name, avfilter_graph_alloc, avfilter_graph_alloc_filter,
avfilter_graph_config, avfilter_graph_create_filter, avfilter_graph_dump, avfilter_graph_parse, avfilter_graph_config, avfilter_graph_create_filter, avfilter_graph_dump, avfilter_graph_parse,
avfilter_graph_parse2, avfilter_graph_parse_ptr, avfilter_inout_alloc, AVFilterContext, avfilter_inout_alloc, AVFilterContext, AVFilterGraph, AVFrame,
AVFilterGraph, AVFrame,
}; };
use log::debug; use log::debug;
use std::collections::HashMap; use std::collections::HashMap;
@ -26,7 +25,7 @@ impl Filter {
/// ///
/// https://ffmpeg.org/ffmpeg-filters.html /// https://ffmpeg.org/ffmpeg-filters.html
pub unsafe fn parse(graph: &str) -> Result<Self, Error> { pub unsafe fn parse(graph: &str) -> Result<Self, Error> {
let mut ctx = avfilter_graph_alloc(); let ctx = avfilter_graph_alloc();
let inputs = avfilter_inout_alloc(); let inputs = avfilter_inout_alloc();
let outputs = avfilter_inout_alloc(); let outputs = avfilter_inout_alloc();
let src = avfilter_get_by_name(cstr!("buffer")); let src = avfilter_get_by_name(cstr!("buffer"));
@ -95,9 +94,8 @@ impl Filter {
} }
pub unsafe fn build(&mut self) -> Result<(), Error> { pub unsafe fn build(&mut self) -> Result<(), Error> {
let mut d = avfilter_graph_dump(self.graph, ptr::null_mut()); let d = rstr!(avfilter_graph_dump(self.graph, ptr::null_mut()));
debug!("{}", CStr::from_ptr(d).to_string_lossy()); debug!("{}", d);
av_free(d as *mut _);
let ret = avfilter_graph_config(self.graph, ptr::null_mut()); let ret = avfilter_graph_config(self.graph, ptr::null_mut());
return_ffmpeg_error!(ret, "Failed to build filter"); return_ffmpeg_error!(ret, "Failed to build filter");

View File

@ -36,24 +36,26 @@ macro_rules! cstr {
}; };
} }
#[macro_export]
macro_rules! rstr {
($str:expr) => {
core::ffi::CStr::from_ptr($str).to_str().unwrap()
};
}
fn get_ffmpeg_error_msg(ret: libc::c_int) -> String { fn get_ffmpeg_error_msg(ret: libc::c_int) -> String {
unsafe { unsafe {
const BUF_SIZE: usize = 512; const BUF_SIZE: usize = 512;
let mut buf: [libc::c_char; BUF_SIZE] = [0; BUF_SIZE]; let mut buf: [libc::c_char; BUF_SIZE] = [0; BUF_SIZE];
av_make_error_string(buf.as_mut_ptr(), BUF_SIZE, ret); av_make_error_string(buf.as_mut_ptr(), BUF_SIZE, ret);
String::from(CStr::from_ptr(buf.as_ptr()).to_str().unwrap()) rstr!(buf.as_ptr()).to_string()
} }
} }
unsafe fn options_to_dict(options: HashMap<String, String>) -> Result<*mut AVDictionary, Error> { unsafe fn options_to_dict(options: HashMap<String, String>) -> Result<*mut AVDictionary, Error> {
let mut dict = ptr::null_mut(); let mut dict = ptr::null_mut();
for (key, value) in options { for (key, value) in options {
let ret = av_dict_set( let ret = av_dict_set(&mut dict, cstr!(key), cstr!(value), 0);
&mut dict,
format!("{}\0", key).as_ptr() as *const libc::c_char,
format!("{}\0", value).as_ptr() as *const libc::c_char,
0,
);
return_ffmpeg_error!(ret); return_ffmpeg_error!(ret);
} }
Ok(dict) Ok(dict)
@ -93,11 +95,7 @@ fn list_opts(ctx: *mut libc::c_void) -> Result<Vec<String>, Error> {
break; break;
} }
ret.push( ret.push(rstr!((*opt_ptr).name).to_string());
CStr::from_ptr((*opt_ptr).name)
.to_string_lossy()
.to_string(),
);
} }
} }
Ok(ret) Ok(ret)
@ -106,12 +104,7 @@ fn list_opts(ctx: *mut libc::c_void) -> Result<Vec<String>, Error> {
fn set_opts(ctx: *mut libc::c_void, options: HashMap<String, String>) -> Result<(), Error> { fn set_opts(ctx: *mut libc::c_void, options: HashMap<String, String>) -> Result<(), Error> {
unsafe { unsafe {
for (key, value) in options { for (key, value) in options {
let ret = av_opt_set( let ret = av_opt_set(ctx, cstr!(key), cstr!(value), AV_OPT_SEARCH_CHILDREN);
ctx,
format!("{}\0", key).as_ptr() as *const libc::c_char,
format!("{}\0", value).as_ptr() as *const libc::c_char,
AV_OPT_SEARCH_CHILDREN,
);
return_ffmpeg_error!(ret); return_ffmpeg_error!(ret);
} }
} }

View File

@ -1,12 +1,13 @@
use std::mem::transmute; use std::mem::transmute;
use std::ptr; use std::ptr;
use crate::return_ffmpeg_error; use crate::{return_ffmpeg_error, rstr};
use anyhow::{bail, Error}; use anyhow::{bail, Error};
use ffmpeg_sys_the_third::{ use ffmpeg_sys_the_third::{
av_frame_alloc, av_frame_copy_props, sws_freeContext, sws_getContext, sws_init_context, av_frame_alloc, av_frame_copy_props, av_get_pix_fmt_name, sws_freeContext, sws_getContext,
sws_scale_frame, AVFrame, AVPixelFormat, SwsContext, SWS_BILINEAR, sws_scale_frame, AVFrame, AVPixelFormat, SwsContext, SWS_BILINEAR,
}; };
use log::trace;
pub struct Scaler { pub struct Scaler {
width: u16, width: u16,
@ -17,8 +18,6 @@ pub struct Scaler {
unsafe impl Send for Scaler {} unsafe impl Send for Scaler {}
unsafe impl Sync for Scaler {}
impl Drop for Scaler { impl Drop for Scaler {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
@ -28,12 +27,18 @@ impl Drop for Scaler {
} }
} }
impl Default for Scaler {
fn default() -> Self {
Self::new()
}
}
impl Scaler { impl Scaler {
pub fn new(width: u16, height: u16, format: AVPixelFormat) -> Self { pub fn new() -> Self {
Self { Self {
width, width: 0,
height, height: 0,
format, format: AVPixelFormat::AV_PIX_FMT_YUV420P,
ctx: ptr::null_mut(), ctx: ptr::null_mut(),
} }
} }
@ -75,6 +80,16 @@ impl Scaler {
bail!("Failed to create scalar context"); bail!("Failed to create scalar context");
} }
trace!(
"scale setup: {}x{}@{} => {}x{}@{}",
(*frame).width,
(*frame).height,
rstr!(av_get_pix_fmt_name(transmute((*frame).format))),
width,
height,
rstr!(av_get_pix_fmt_name(format))
);
self.width = width; self.width = width;
self.height = height; self.height = height;
self.format = format; self.format = format;
@ -104,3 +119,54 @@ impl Scaler {
Ok(dst_frame) Ok(dst_frame)
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use ffmpeg_sys_the_third::{
av_frame_alloc, av_frame_free, av_frame_get_buffer, AVFrame, AVPixelFormat,
};
unsafe fn blank_frame() -> *mut AVFrame {
let frame = av_frame_alloc();
(*frame).width = 512;
(*frame).height = 512;
(*frame).format = AVPixelFormat::AV_PIX_FMT_RGB24 as libc::c_int;
av_frame_get_buffer(frame, 0);
frame
}
#[test]
fn scale_rgb24_yuv420() {
unsafe {
let mut frame = blank_frame();
let mut scaler = Scaler::new();
// downscale
let mut out_frame = scaler
.process_frame(frame, 128, 128, AVPixelFormat::AV_PIX_FMT_YUV420P)
.expect("Failed to process frame");
assert_eq!((*out_frame).width, 128);
assert_eq!((*out_frame).height, 128);
assert_eq!(
(*out_frame).format,
transmute(AVPixelFormat::AV_PIX_FMT_YUV420P)
);
av_frame_free(&mut out_frame);
// upscale
let mut out_frame = scaler
.process_frame(frame, 1024, 1024, AVPixelFormat::AV_PIX_FMT_YUV420P)
.expect("Failed to process frame");
assert_eq!((*out_frame).width, 1024);
assert_eq!((*out_frame).height, 1024);
assert_eq!(
(*out_frame).format,
transmute(AVPixelFormat::AV_PIX_FMT_YUV420P)
);
av_frame_free(&mut out_frame);
av_frame_free(&mut frame);
}
}
}

View File

@ -1,4 +1,4 @@
use crate::format_time; use crate::{format_time, rstr};
use ffmpeg_sys_the_third::{ use ffmpeg_sys_the_third::{
av_get_pix_fmt_name, av_get_sample_fmt_name, avcodec_get_name, AVMediaType, AVStream, av_get_pix_fmt_name, av_get_sample_fmt_name, avcodec_get_name, AVMediaType, AVStream,
}; };
@ -130,44 +130,36 @@ impl StreamInfoChannel {
impl Display for StreamInfoChannel { impl Display for StreamInfoChannel {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let codec_name = unsafe { CStr::from_ptr(avcodec_get_name(transmute(self.codec as i32))) }; let codec_name = unsafe { rstr!(avcodec_get_name(transmute(self.codec as i32))) };
match self.channel_type { match self.channel_type {
StreamChannelType::Video => write!( StreamChannelType::Video => write!(
f, f,
"{} #{}: codec={},size={}x{},fps={:.3},pix_fmt={}", "{} #{}: codec={},size={}x{},fps={:.3},pix_fmt={}",
self.channel_type, self.channel_type,
self.index, self.index,
codec_name.to_str().unwrap(), codec_name,
self.width, self.width,
self.height, self.height,
self.fps, self.fps,
unsafe { unsafe { rstr!(av_get_pix_fmt_name(transmute(self.format as libc::c_int))) },
CStr::from_ptr(av_get_pix_fmt_name(transmute(self.format as libc::c_int)))
}
.to_str()
.unwrap(),
), ),
StreamChannelType::Audio => write!( StreamChannelType::Audio => write!(
f, f,
"{} #{}: codec={},format={},sample_rate={}", "{} #{}: codec={},format={},sample_rate={}",
self.channel_type, self.channel_type,
self.index, self.index,
codec_name.to_str().unwrap(), codec_name,
unsafe { unsafe {
CStr::from_ptr(av_get_sample_fmt_name(transmute( rstr!(av_get_sample_fmt_name(transmute(
self.format as libc::c_int, self.format as libc::c_int,
))) )))
} },
.to_str()
.unwrap(),
self.sample_rate, self.sample_rate,
), ),
StreamChannelType::Subtitle => write!( StreamChannelType::Subtitle => write!(
f, f,
"{} #{}: codec={}", "{} #{}: codec={}",
self.channel_type, self.channel_type, self.index, codec_name
self.index,
codec_name.to_str().unwrap()
), ),
} }
} }