feat: setup filters (wip)
This commit is contained in:
@ -7,13 +7,14 @@ use std::ptr;
|
||||
use anyhow::Error;
|
||||
use ffmpeg_sys_the_third::AVPictureType::AV_PICTURE_TYPE_NONE;
|
||||
use ffmpeg_sys_the_third::{
|
||||
av_buffer_ref, av_frame_alloc, av_frame_copy_props, av_frame_free, av_hwdevice_ctx_create,
|
||||
av_hwdevice_get_type_name, av_hwframe_transfer_data, avcodec_alloc_context3,
|
||||
avcodec_find_decoder, avcodec_free_context, avcodec_get_hw_config, avcodec_get_name,
|
||||
avcodec_open2, avcodec_parameters_to_context, avcodec_receive_frame, avcodec_send_packet,
|
||||
AVCodec, AVCodecContext, AVCodecHWConfig, AVFrame, AVHWDeviceType, AVPacket, AVStream, AVERROR,
|
||||
AVERROR_EOF, AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX,
|
||||
av_buffer_ref, av_frame_alloc, av_hwdevice_ctx_create, av_hwdevice_get_type_name,
|
||||
av_hwdevice_iterate_types, avcodec_alloc_context3, avcodec_find_decoder, avcodec_free_context,
|
||||
avcodec_get_hw_config, avcodec_get_name, avcodec_open2, avcodec_parameters_to_context,
|
||||
avcodec_receive_frame, avcodec_send_packet, AVCodec, AVCodecContext, AVCodecHWConfig, AVFrame,
|
||||
AVHWDeviceType, AVPacket, AVStream, AVERROR, AVERROR_EOF,
|
||||
AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX,
|
||||
};
|
||||
use log::debug;
|
||||
|
||||
pub struct DecoderCodecContext {
|
||||
pub context: *mut AVCodecContext,
|
||||
@ -100,6 +101,22 @@ impl Decoder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable hardware decoding
|
||||
pub fn enable_hw_decoder_any(&mut self) {
|
||||
let mut res = HashSet::new();
|
||||
let mut hwt = AVHWDeviceType::AV_HWDEVICE_TYPE_NONE;
|
||||
unsafe {
|
||||
loop {
|
||||
hwt = av_hwdevice_iterate_types(hwt);
|
||||
if hwt == AVHWDeviceType::AV_HWDEVICE_TYPE_NONE {
|
||||
break;
|
||||
}
|
||||
res.insert(hwt);
|
||||
}
|
||||
}
|
||||
self.hw_decoder_types = Some(res);
|
||||
}
|
||||
|
||||
/// Set up a decoder for a given channel
|
||||
pub fn setup_decoder(
|
||||
&mut self,
|
||||
@ -142,6 +159,7 @@ impl Decoder {
|
||||
let mut ret = avcodec_parameters_to_context(context, (*stream).codecpar);
|
||||
return_ffmpeg_error!(ret, "Failed to copy codec parameters to context");
|
||||
|
||||
let codec_name = CStr::from_ptr(avcodec_get_name((*codec).id)).to_str()?;
|
||||
// try use HW decoder
|
||||
let mut hw_config = ptr::null();
|
||||
if let Some(ref hw_types) = self.hw_decoder_types {
|
||||
@ -153,7 +171,11 @@ impl Decoder {
|
||||
if hw_config.is_null() {
|
||||
break;
|
||||
}
|
||||
let hw_name =
|
||||
CStr::from_ptr(av_hwdevice_get_type_name((*hw_config).device_type))
|
||||
.to_str()?;
|
||||
if !hw_types.contains(&(*hw_config).device_type) {
|
||||
debug!("skipping hwaccel={}_{}", codec_name, hw_name);
|
||||
continue;
|
||||
}
|
||||
let hw_flag = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX as libc::c_int;
|
||||
@ -167,6 +189,7 @@ impl Decoder {
|
||||
);
|
||||
return_ffmpeg_error!(ret, "Failed to create HW ctx");
|
||||
(*context).hw_device_ctx = av_buffer_ref(hw_buf_ref);
|
||||
debug!("using hwaccel={}_{}", codec_name, hw_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -180,6 +203,7 @@ impl Decoder {
|
||||
ret = avcodec_open2(context, codec, &mut dict);
|
||||
return_ffmpeg_error!(ret, "Failed to open codec");
|
||||
|
||||
debug!("opened decoder={}", codec_name);
|
||||
Ok(e.insert(DecoderCodecContext {
|
||||
context,
|
||||
codec,
|
||||
@ -219,17 +243,6 @@ impl Decoder {
|
||||
return Err(Error::msg(format!("Failed to decode {}", ret)));
|
||||
}
|
||||
|
||||
// copy frame from GPU
|
||||
if !ctx.hw_config.is_null() {
|
||||
let sw_frame = av_frame_alloc();
|
||||
ret = av_hwframe_transfer_data(sw_frame, frame, 0);
|
||||
return_ffmpeg_error!(ret, "Failed to transfer data from GPU");
|
||||
|
||||
av_frame_copy_props(sw_frame, frame);
|
||||
av_frame_free(&mut frame);
|
||||
frame = sw_frame;
|
||||
}
|
||||
|
||||
(*frame).pict_type = AV_PICTURE_TYPE_NONE; // encoder prints warnings
|
||||
pkgs.push((frame, stream));
|
||||
}
|
||||
|
110
src/filter.rs
Normal file
110
src/filter.rs
Normal file
@ -0,0 +1,110 @@
|
||||
use crate::{cstr, return_ffmpeg_error, set_opts};
|
||||
use anyhow::Error;
|
||||
use ffmpeg_sys_the_third::{
|
||||
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_parse2, avfilter_graph_parse_ptr, avfilter_inout_alloc, AVFilterContext,
|
||||
AVFilterGraph, AVFrame,
|
||||
};
|
||||
use log::debug;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CStr;
|
||||
use std::ptr;
|
||||
|
||||
pub struct Filter {
|
||||
graph: *mut AVFilterGraph,
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
graph: unsafe { avfilter_graph_alloc() },
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse filter from string using [avfilter_graph_parse2]
|
||||
///
|
||||
/// https://ffmpeg.org/ffmpeg-filters.html
|
||||
pub unsafe fn parse(graph: &str) -> Result<Self, Error> {
|
||||
let mut ctx = avfilter_graph_alloc();
|
||||
let inputs = avfilter_inout_alloc();
|
||||
let outputs = avfilter_inout_alloc();
|
||||
let src = avfilter_get_by_name(cstr!("buffer"));
|
||||
let dst = avfilter_get_by_name(cstr!("buffersink"));
|
||||
let mut src_ctx = ptr::null_mut();
|
||||
let mut dst_ctx = ptr::null_mut();
|
||||
let ret = avfilter_graph_create_filter(
|
||||
&mut src_ctx,
|
||||
src,
|
||||
cstr!("in"),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ctx,
|
||||
);
|
||||
return_ffmpeg_error!(ret, "Failed to parse graph");
|
||||
|
||||
let ret = avfilter_graph_create_filter(
|
||||
&mut dst_ctx,
|
||||
dst,
|
||||
cstr!("out"),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ctx,
|
||||
);
|
||||
return_ffmpeg_error!(ret, "Failed to parse graph");
|
||||
|
||||
(*outputs).name = av_strdup((*dst).name);
|
||||
(*outputs).filter_ctx = dst_ctx;
|
||||
(*outputs).pad_idx = 0;
|
||||
(*outputs).next = ptr::null_mut();
|
||||
|
||||
(*inputs).name = av_strdup((*src).name);
|
||||
(*inputs).filter_ctx = src_ctx;
|
||||
(*inputs).pad_idx = 0;
|
||||
(*inputs).next = ptr::null_mut();
|
||||
|
||||
let ret = avfilter_graph_parse(ctx, cstr!(graph), inputs, outputs, ptr::null_mut());
|
||||
return_ffmpeg_error!(ret, "Failed to parse graph");
|
||||
let mut ret = Self { graph: ctx };
|
||||
ret.build()?;
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub fn add_filter(
|
||||
&mut self,
|
||||
name: &str,
|
||||
options: Option<HashMap<String, String>>,
|
||||
) -> Result<*mut AVFilterContext, Error> {
|
||||
if self.graph.is_null() {
|
||||
anyhow::bail!("Filter graph is null.");
|
||||
}
|
||||
unsafe {
|
||||
let filter = avfilter_get_by_name(cstr!(name));
|
||||
if filter.is_null() {
|
||||
anyhow::bail!("Filter {} not found", name);
|
||||
}
|
||||
let flt = avfilter_graph_alloc_filter(self.graph, filter, ptr::null_mut());
|
||||
if flt.is_null() {
|
||||
anyhow::bail!("Filter {} not found", name);
|
||||
}
|
||||
if let Some(opt) = options {
|
||||
set_opts(flt as *mut libc::c_void, opt)?;
|
||||
}
|
||||
Ok(flt)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn build(&mut self) -> Result<(), Error> {
|
||||
let mut d = avfilter_graph_dump(self.graph, ptr::null_mut());
|
||||
debug!("{}", CStr::from_ptr(d).to_string_lossy());
|
||||
av_free(d as *mut _);
|
||||
|
||||
let ret = avfilter_graph_config(self.graph, ptr::null_mut());
|
||||
return_ffmpeg_error!(ret, "Failed to build filter");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe fn process_frame(&mut self, frame: *mut AVFrame) -> Result<*mut AVFrame, Error> {
|
||||
todo!();
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ use std::ptr;
|
||||
|
||||
mod decode;
|
||||
mod demux;
|
||||
mod filter;
|
||||
mod resample;
|
||||
mod scale;
|
||||
mod stream_info;
|
||||
@ -27,6 +28,13 @@ macro_rules! return_ffmpeg_error {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cstr {
|
||||
($str:expr) => {
|
||||
format!("{}\0", $str).as_ptr() as *const libc::c_char
|
||||
};
|
||||
}
|
||||
|
||||
fn get_ffmpeg_error_msg(ret: libc::c_int) -> String {
|
||||
unsafe {
|
||||
const BUF_SIZE: usize = 512;
|
||||
@ -112,6 +120,7 @@ fn set_opts(ctx: *mut libc::c_void, options: HashMap<String, String>) -> Result<
|
||||
pub use decode::*;
|
||||
pub use demux::*;
|
||||
pub use ffmpeg_sys_the_third;
|
||||
pub use filter::*;
|
||||
pub use resample::*;
|
||||
pub use scale::*;
|
||||
pub use stream_info::*;
|
||||
|
@ -68,7 +68,7 @@ impl Display for DemuxerInfo {
|
||||
|
||||
write!(
|
||||
f,
|
||||
"Demuxer Info: duration={}, bitrate={}k",
|
||||
"Demuxer Info: duration={}, bitrate={}",
|
||||
format_time(self.duration),
|
||||
bitrate_str
|
||||
)?;
|
||||
|
Reference in New Issue
Block a user