use failure::{Backtrace, Context, Fail};
use ffmpeg_sys::*;
use std::convert::TryInto;
use std::ffi::{CStr, CString, OsString};
use std::fmt;
use std::path::{Path, PathBuf};
use std::ptr::null_mut;
use crate::define_error;
fn av_err2str(errnum: libc::c_int) -> String {
let mut err_buffer: [libc::c_char; 256] = [0; 256];
unsafe {
av_make_error_string(err_buffer.as_mut_ptr() as *mut i8, err_buffer.len(), errnum);
CStr::from_ptr(&err_buffer as *const libc::c_char)
.to_string_lossy()
.to_string()
}
}
define_error!(DecoderError, DecoderErrorKind);
#[derive(Debug, Fail)]
pub(crate) enum DecoderErrorKind {}
fn format_cmd(cmd_path: &PathBuf, args: &[OsString]) -> String {
let args_string: String = args
.iter()
.map(|x| format!("{}", x.to_string_lossy()))
.collect::<Vec<String>>()
.join(" ");
format!("{} {}", cmd_path.display(), args_string)
}
impl fmt::Display for DecoderErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unimplemented!()
}
}
pub struct VideoDecoderFFmpegLibrary {}
impl VideoDecoderFFmpegLibrary {
pub(crate) fn decode<T>(
file_path: impl AsRef<Path>,
mut receiver: impl super::AudioReceiver<Output = T>,
mut progress_handler: impl super::ProgressHandler,
) -> Result<T, DecoderError> {
unsafe {
let mut format_context: *mut AVFormatContext = avformat_alloc_context();
let file_path_: String = file_path.as_ref().to_string_lossy().into_owned();
let result: libc::c_int;
result = avformat_open_input(
&mut format_context as *mut *mut AVFormatContext,
file_path_.as_bytes().as_ptr() as *const i8,
null_mut(),
null_mut(),
);
if result < 0 {
panic!(
"Failed to open media file '{}': {}",
file_path.as_ref().display(),
av_err2str(result)
);
}
avformat_find_stream_info(format_context, null_mut());
let streams: &[*mut AVStream] =
std::slice::from_raw_parts((*format_context).streams, (*format_context).nb_streams as usize);
let mut audio_stream_opt: Option<*mut AVStream> = None;
for &stream in streams {
let local_codec_parameters: *mut AVCodecParameters = (*stream).codecpar;
if (*local_codec_parameters).codec_type == AVMediaType::AVMEDIA_TYPE_AUDIO {
if let Some(saved_audio_stream) = audio_stream_opt {
if (*(*saved_audio_stream).codecpar).channels > (*local_codec_parameters).channels {
audio_stream_opt = Some(stream);
}
} else {
audio_stream_opt = Some(stream);
}
}
}
if audio_stream_opt.is_none() {
panic!("no audio stream found");
}
let audio_stream = audio_stream_opt.unwrap();
let local_codec_parameters: *mut AVCodecParameters = (*audio_stream).codecpar;
let local_codec: *mut AVCodec = avcodec_find_decoder((*local_codec_parameters).codec_id);
let codec_context: *mut AVCodecContext = avcodec_alloc_context3(local_codec as *const AVCodec);
avcodec_parameters_to_context(codec_context, local_codec_parameters);
avcodec_open2(codec_context, local_codec, null_mut());
let _av_opt_set_int = |swr: *mut SwrContext, name: &str, val: i64, search_flag: libc::c_int| {
av_opt_set_int(
swr as *mut libc::c_void,
CString::new(name).unwrap().into_raw(),
val,
search_flag,
)
};
let _av_opt_set_int = |swr: *mut SwrContext, name: &str, val: i64, search_flag: libc::c_int| {
av_opt_set_int(
swr as *mut libc::c_void,
CString::new(name).unwrap().into_raw(),
val,
search_flag,
)
};
let _av_opt_set_sample_fmt =
|obj: *mut SwrContext, name: &str, fmt: AVSampleFormat, search_flags: libc::c_int| -> libc::c_int {
av_opt_set_sample_fmt(
obj as *mut libc::c_void,
CString::new(name).unwrap().into_raw(),
fmt,
search_flags,
)
};
let in_channel_layout = (*codec_context).channel_layout.try_into().unwrap();
let in_channel_count: i64 = (*codec_context).channels.try_into().unwrap();
let in_sample_rate: i64 = (*codec_context).sample_rate.try_into().unwrap();
let in_sample_format = (*codec_context).sample_fmt;
let out_channel_count = 1;
let out_channel_layout = AV_CH_LAYOUT_MONO.try_into().unwrap();
let out_sample_rate = 8000;
let out_sample_format = AVSampleFormat::AV_SAMPLE_FMT_S16P;
let swr: *mut SwrContext = swr_alloc();
_av_opt_set_int(swr, "in_channel_count", in_channel_count, 0);
_av_opt_set_int(swr, "in_channel_layout", in_channel_layout, 0);
_av_opt_set_int(swr, "in_sample_rate", in_sample_rate, 0);
_av_opt_set_sample_fmt(swr, "in_sample_fmt", in_sample_format, 0);
_av_opt_set_int(swr, "out_channel_count", out_channel_count, 0);
_av_opt_set_int(swr, "out_channel_layout", out_channel_layout, 0);
_av_opt_set_int(swr, "out_sample_rate", out_sample_rate, 0);
_av_opt_set_sample_fmt(swr, "out_sample_fmt", out_sample_format, 0);
swr_init(swr);
if swr_is_initialized(swr) == 0 {
unimplemented!();
}
let src_nb_samples = 1024; let mut max_out_samples: i32 =
av_rescale_rnd(src_nb_samples, out_sample_rate, in_sample_rate, AVRounding::AV_ROUND_UP) as i32;
let mut buffer: *mut i16 = null_mut();
av_samples_alloc(
&mut buffer as *mut *mut i16 as *mut *mut u8,
null_mut(),
out_channel_count as i32,
max_out_samples,
out_sample_format,
0,
);
let packet: *mut AVPacket = av_packet_alloc();
let frame: *mut AVFrame = av_frame_alloc();
progress_handler.init((*audio_stream).nb_frames);
while av_read_frame(format_context, packet) >= 0 {
if (*packet).stream_index != (*audio_stream).index {
continue;
}
progress_handler.inc();
let mut response = avcodec_send_packet(codec_context, packet);
if response < 0 {
panic!("{}", av_err2str(response));
}
loop {
response = avcodec_receive_frame(codec_context, frame);
if response == AVERROR(EAGAIN) || response == AVERROR_EOF {
break;
} else if response < 0 {
panic!("Error: {}", av_err2str(response));
}
let out_sample_count = swr_get_out_samples(swr, (*frame).nb_samples);
if out_sample_count > max_out_samples {
max_out_samples = out_sample_count;
av_freep(&mut buffer as *mut *mut i16 as *mut libc::c_void);
av_samples_alloc(
&mut buffer as *mut *mut i16 as *mut *mut u8,
null_mut(),
out_channel_count as i32,
max_out_samples,
out_sample_format,
0,
);
}
let frame_count = swr_convert(
swr,
&mut buffer as *mut *mut i16 as *mut *mut u8,
out_sample_count,
(*frame).data.as_mut_ptr() as *mut *const u8,
(*frame).nb_samples,
);
let out_slice = std::slice::from_raw_parts_mut(buffer, frame_count as usize);
receiver.push_samples(out_slice);
}
av_packet_unref(packet);
}
av_freep(&mut buffer as *mut *mut i16 as *mut libc::c_void);
avformat_free_context(format_context);
}
progress_handler.finish();
Ok(receiver.finish())
}
}