From ba53d2eeb990303f2ef9b3a0e52b5516193c10ed Mon Sep 17 00:00:00 2001 From: kieran Date: Tue, 5 Nov 2024 13:57:13 +0000 Subject: [PATCH] fix: display channel info --- src/decode.rs | 30 ++++---------------- src/lib.rs | 23 +++++++++++++++ src/scale.rs | 1 - src/stream_info.rs | 71 +++++++++++++++++++++++++++++++++++++--------- 4 files changed, 87 insertions(+), 38 deletions(-) diff --git a/src/decode.rs b/src/decode.rs index e411d59..367f31a 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -7,14 +7,13 @@ 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_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, + 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, }; -use libc::memcpy; pub struct DecoderCodecContext { pub context: *mut AVCodecContext, @@ -204,23 +203,6 @@ impl Decoder { ); if let Some(ctx) = self.codecs.get_mut(&stream_index) { - // subtitles don't need decoding, create a frame from the pkt data - if (*ctx.codec).type_ == AVMediaType::AVMEDIA_TYPE_SUBTITLE { - let frame = av_frame_alloc(); - (*frame).pts = (*pkt).pts; - (*frame).pkt_dts = (*pkt).dts; - (*frame).duration = (*pkt).duration; - (*frame).buf[0] = av_buffer_alloc((*pkt).size as usize); - (*frame).data[0] = (*(*frame).buf[0]).data; - (*frame).linesize[0] = (*pkt).size; - memcpy( - (*frame).data[0] as *mut libc::c_void, - (*pkt).data as *const libc::c_void, - (*pkt).size as usize, - ); - return Ok(vec![(frame, stream)]); - } - let mut ret = avcodec_send_packet(ctx.context, pkt); if ret < 0 { return Err(Error::msg(format!("Failed to decode packet {}", ret))); diff --git a/src/lib.rs b/src/lib.rs index 13cf859..3c55f15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,6 +50,29 @@ unsafe fn options_to_dict(options: HashMap) -> Result<*mut AVDic Ok(dict) } +/// Format seconds value into human-readable string +pub fn format_time(secs: f32) -> String { + const MIN: f32 = 60.0; + const HR: f32 = MIN * 60.0; + + if secs >= HR { + format!( + "{:0>2.0}h {:0>2.0}m {:0>2.0}s", + (secs / HR).floor(), + ((secs % HR) / MIN).floor(), + (secs % MIN).floor() + ) + } else if secs >= MIN { + format!( + "{:0>2.0}m {:0>2.0}s", + (secs / MIN).floor(), + (secs % MIN).floor() + ) + } else { + format!("{:0>2.2}s", secs) + } +} + fn list_opts(ctx: *mut libc::c_void) -> Result, Error> { let mut opt_ptr: *const AVOption = ptr::null_mut(); diff --git a/src/scale.rs b/src/scale.rs index 79741d2..8e3defe 100644 --- a/src/scale.rs +++ b/src/scale.rs @@ -1,4 +1,3 @@ -use crate::get_ffmpeg_error_msg; use std::mem::transmute; use std::ptr; diff --git a/src/stream_info.rs b/src/stream_info.rs index 478ec7c..6e09f00 100644 --- a/src/stream_info.rs +++ b/src/stream_info.rs @@ -1,4 +1,7 @@ -use ffmpeg_sys_the_third::{avcodec_get_name, AVMediaType, AVStream}; +use crate::format_time; +use ffmpeg_sys_the_third::{ + av_get_pix_fmt_name, av_get_sample_fmt_name, avcodec_get_name, AVMediaType, AVStream, +}; use std::ffi::CStr; use std::fmt::{Display, Formatter}; use std::intrinsics::transmute; @@ -55,9 +58,22 @@ impl DemuxerInfo { impl Display for DemuxerInfo { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "Demuxer Info:")?; + let bitrate_str = if self.bitrate > 1_000_000 { + format!("{}M", self.bitrate / 1_000_000) + } else if self.bitrate > 1_000 { + format!("{}k", self.bitrate / 1_000) + } else { + self.bitrate.to_string() + }; + + write!( + f, + "Demuxer Info: duration={}, bitrate={}k", + format_time(self.duration), + bitrate_str + )?; for c in &self.channels { - write!(f, "\n{}", c)?; + write!(f, "\n {}", c)?; } Ok(()) } @@ -115,15 +131,44 @@ impl StreamInfoChannel { impl Display for StreamInfoChannel { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let codec_name = unsafe { CStr::from_ptr(avcodec_get_name(transmute(self.codec as i32))) }; - write!( - f, - "{} #{}: codec={},size={}x{},fps={}", - self.channel_type, - self.index, - codec_name.to_str().unwrap(), - self.width, - self.height, - self.fps - ) + match self.channel_type { + StreamChannelType::Video => write!( + f, + "{} #{}: codec={},size={}x{},fps={:.3},pix_fmt={}", + self.channel_type, + self.index, + codec_name.to_str().unwrap(), + self.width, + self.height, + self.fps, + unsafe { + CStr::from_ptr(av_get_pix_fmt_name(transmute(self.format as libc::c_int))) + } + .to_str() + .unwrap(), + ), + StreamChannelType::Audio => write!( + f, + "{} #{}: codec={},format={},sample_rate={}", + self.channel_type, + self.index, + codec_name.to_str().unwrap(), + unsafe { + CStr::from_ptr(av_get_sample_fmt_name(transmute( + self.format as libc::c_int, + ))) + } + .to_str() + .unwrap(), + self.sample_rate, + ), + StreamChannelType::Subtitle => write!( + f, + "{} #{}: codec={}", + self.channel_type, + self.index, + codec_name.to_str().unwrap() + ), + } } }