diff --git a/cashu.png b/cashu.png
new file mode 100644
index 0000000..f545ca0
Binary files /dev/null and b/cashu.png differ
diff --git a/examples/cashu.svg b/examples/cashu.svg
deleted file mode 100644
index 3230c9b..0000000
--- a/examples/cashu.svg
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
diff --git a/examples/main.rs b/examples/main.rs
index b0f6c64..2d12060 100644
--- a/examples/main.rs
+++ b/examples/main.rs
@@ -1,6 +1,6 @@
-use ffmpeg_rs_raw::{Decoder, Demuxer};
+use ffmpeg_rs_raw::{Decoder, Demuxer, DemuxerInfo};
+use ffmpeg_sys_the_third::AVHWDeviceType::{AV_HWDEVICE_TYPE_CUDA, AV_HWDEVICE_TYPE_VDPAU};
use ffmpeg_sys_the_third::{av_frame_free, av_packet_free};
-use std::collections::HashMap;
use std::env::args;
use std::fs::File;
use std::io::{Cursor, Read};
@@ -20,20 +20,6 @@ fn main() {
let fd = read_as_file(path.clone());
scan_input(fd);
-
- let mut opt = HashMap::new();
- opt.insert("analyzeduration".to_string(), "999".to_string());
- let svg = include_bytes!("./cashu.svg");
- let mut dx = Demuxer::new_custom_io(svg.as_slice(), Some("cashu.svg".to_string()));
- dx.set_opt(opt.clone()).unwrap();
- unsafe {
- let mut decoder = Decoder::new();
- let info = dx.probe_input().expect("probe failed");
- for chan in &info.channels {
- decoder.setup_decoder(chan, None).expect("setup failed");
- }
- loop_decoder(dx, decoder);
- }
}
fn read_as_custom_io(path: PathBuf) -> Demuxer {
@@ -51,12 +37,20 @@ fn scan_input(mut demuxer: Demuxer) {
unsafe {
let info = demuxer.probe_input().expect("demuxer failed");
println!("{}", info);
- decode_input(demuxer);
+ decode_input(demuxer, info);
}
}
-unsafe fn decode_input(demuxer: Demuxer) {
- let decoder = Decoder::new();
+unsafe fn decode_input(demuxer: Demuxer, info: DemuxerInfo) {
+ let mut decoder = Decoder::new();
+ decoder.enable_hw_decoder(AV_HWDEVICE_TYPE_VDPAU);
+ decoder.enable_hw_decoder(AV_HWDEVICE_TYPE_CUDA);
+ for ref stream in info.channels {
+ decoder
+ .setup_decoder(stream, None)
+ .expect("decoder setup failed");
+ }
+ println!("{}", decoder);
loop_decoder(demuxer, decoder);
}
diff --git a/src/decode.rs b/src/decode.rs
index 2035265..e411d59 100644
--- a/src/decode.rs
+++ b/src/decode.rs
@@ -1,21 +1,25 @@
-use crate::{options_to_dict, StreamInfoChannel};
-use std::collections::HashMap;
+use crate::{options_to_dict, return_ffmpeg_error, StreamInfoChannel};
+use std::collections::{HashMap, HashSet};
use std::ffi::CStr;
+use std::fmt::{Display, Formatter};
use std::ptr;
use anyhow::Error;
use ffmpeg_sys_the_third::AVPictureType::AV_PICTURE_TYPE_NONE;
use ffmpeg_sys_the_third::{
- av_buffer_alloc, av_frame_alloc, avcodec_alloc_context3, avcodec_find_decoder,
- avcodec_free_context, avcodec_get_name, avcodec_open2, avcodec_parameters_to_context,
- avcodec_receive_frame, avcodec_send_packet, AVCodec, AVCodecContext, AVFrame, AVMediaType,
- AVPacket, AVStream, AVERROR, AVERROR_EOF,
+ av_buffer_alloc, 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,
+ AVMediaType, AVPacket, AVStream, AVERROR, AVERROR_EOF, AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX,
};
use libc::memcpy;
pub struct DecoderCodecContext {
pub context: *mut AVCodecContext,
pub codec: *const AVCodec,
+ pub hw_config: *const AVCodecHWConfig,
}
impl DecoderCodecContext {
@@ -39,17 +43,61 @@ impl Drop for DecoderCodecContext {
}
}
+impl Display for DecoderCodecContext {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ unsafe {
+ let codec_name = CStr::from_ptr(avcodec_get_name((*self.codec).id))
+ .to_str()
+ .unwrap();
+ write!(
+ f,
+ "DecoderCodecContext: codec={}, hw={}",
+ codec_name,
+ if self.hw_config.is_null() {
+ "no"
+ } else {
+ CStr::from_ptr(av_hwdevice_get_type_name((*self.hw_config).device_type))
+ .to_str()
+ .unwrap()
+ }
+ )
+ }
+ }
+}
+
unsafe impl Send for DecoderCodecContext {}
unsafe impl Sync for DecoderCodecContext {}
pub struct Decoder {
codecs: HashMap,
+ /// List of [AVHWDeviceType] which are enabled
+ hw_decoder_types: Option>,
}
+impl Display for Decoder {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ for (idx, codec) in &self.codecs {
+ writeln!(f, "{}: {}", idx, codec)?;
+ }
+ Ok(())
+ }
+}
impl Decoder {
pub fn new() -> Self {
Self {
codecs: HashMap::new(),
+ hw_decoder_types: None,
+ }
+ }
+
+ /// Enable hardware decoding with [hw_type]
+ pub fn enable_hw_decoder(&mut self, hw_type: AVHWDeviceType) {
+ if let Some(ref mut t) = self.hw_decoder_types {
+ t.insert(hw_type);
+ } else {
+ let mut hwt = HashSet::new();
+ hwt.insert(hw_type);
+ self.hw_decoder_types = Some(hwt);
}
}
@@ -91,8 +139,38 @@ impl Decoder {
if context.is_null() {
anyhow::bail!("Failed to alloc context")
}
- if avcodec_parameters_to_context(context, (*stream).codecpar) != 0 {
- anyhow::bail!("Failed to copy codec parameters to context")
+
+ let mut ret = avcodec_parameters_to_context(context, (*stream).codecpar);
+ return_ffmpeg_error!(ret, "Failed to copy codec parameters to context");
+
+ // try use HW decoder
+ let mut hw_config = ptr::null();
+ if let Some(ref hw_types) = self.hw_decoder_types {
+ let mut hw_buf_ref = ptr::null_mut();
+ let mut i = 0;
+ loop {
+ hw_config = avcodec_get_hw_config(codec, i);
+ i += 1;
+ if hw_config.is_null() {
+ break;
+ }
+ if !hw_types.contains(&(*hw_config).device_type) {
+ continue;
+ }
+ let hw_flag = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX as libc::c_int;
+ if (*hw_config).methods & hw_flag == hw_flag {
+ ret = av_hwdevice_ctx_create(
+ &mut hw_buf_ref,
+ (*hw_config).device_type,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ 0,
+ );
+ return_ffmpeg_error!(ret, "Failed to create HW ctx");
+ (*context).hw_device_ctx = av_buffer_ref(hw_buf_ref);
+ break;
+ }
+ }
}
let mut dict = if let Some(options) = options {
options_to_dict(options)?
@@ -100,10 +178,14 @@ impl Decoder {
ptr::null_mut()
};
- if avcodec_open2(context, codec, &mut dict) < 0 {
- anyhow::bail!("Failed to open codec")
- }
- Ok(e.insert(DecoderCodecContext { context, codec }))
+ ret = avcodec_open2(context, codec, &mut dict);
+ return_ffmpeg_error!(ret, "Failed to open codec");
+
+ Ok(e.insert(DecoderCodecContext {
+ context,
+ codec,
+ hw_config,
+ }))
} else {
anyhow::bail!("Decoder already setup");
}
@@ -146,7 +228,7 @@ impl Decoder {
let mut pkgs = Vec::new();
while ret >= 0 {
- let frame = av_frame_alloc();
+ let mut frame = av_frame_alloc();
ret = avcodec_receive_frame(ctx.context, frame);
if ret < 0 {
if ret == AVERROR_EOF || ret == AVERROR(libc::EAGAIN) {
@@ -155,6 +237,17 @@ 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));
}
diff --git a/src/lib.rs b/src/lib.rs
index 755a798..13cf859 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -17,7 +17,12 @@ mod stream_info;
macro_rules! return_ffmpeg_error {
($x:expr) => {
if $x < 0 {
- return Err(Error::msg(get_ffmpeg_error_msg($x)));
+ anyhow::bail!(crate::get_ffmpeg_error_msg($x))
+ }
+ };
+ ($x:expr,$msg:expr) => {
+ if $x < 0 {
+ anyhow::bail!(format!("{}: {}", $msg, crate::get_ffmpeg_error_msg($x)))
}
};
}