use ffav_sys::{AVMediaType::*, *};
use std::convert::TryInto;
use std::env;
use std::ffi::CString;
unsafe extern "C" fn log_packet(fmt_ctx: *const AVFormatContext, pkt: *const AVPacket, tag: &str) {
let pkt = &*pkt;
let fmt_ctx = &*fmt_ctx;
let streams: &[*mut AVStream] =
std::slice::from_raw_parts(fmt_ctx.streams, fmt_ctx.nb_streams as usize);
let stream = &*streams[pkt.stream_index as usize];
let time_base = &stream.time_base;
println!(
"{}: pts:{} pts_time:{} dts:{} dts_time:{} duration:{} duration_time:{} stream_index:{}",
tag,
av_ts2str(pkt.pts),
av_ts2timestr(pkt.pts, time_base),
av_ts2str(pkt.dts),
av_ts2timestr(pkt.dts, time_base),
av_ts2str(pkt.duration),
av_ts2timestr(pkt.duration, time_base),
pkt.stream_index,
);
}
fn main() {
unsafe {
let mut ofmt_ptr: *mut AVOutputFormat = std::ptr::null_mut();
let mut ifmt_ctx_ptr: *mut AVFormatContext = std::ptr::null_mut();
let mut ofmt_ctx_ptr: *mut AVFormatContext = std::ptr::null_mut();
let mut pkt: AVPacket = std::mem::zeroed();
let mut ret;
let args = env::args().collect::<Vec<_>>();
if args.len() < 3 {
println!(
"usage: {} input output\n\
API example program to remux a media file with libavformat and libavcodec.\n\
The output format is guessed according to the file extension.\n",
args[0]
);
std::process::exit(-1);
}
let in_filename = CString::new(args[1].clone()).unwrap();
let out_filename = CString::new(args[2].clone()).unwrap();
'outer: loop {
ret = avformat_open_input(
&mut ifmt_ctx_ptr,
in_filename.as_ptr(),
std::ptr::null_mut(),
std::ptr::null_mut(),
);
if ret < 0 {
println!("Could not open input file {:?}", in_filename);
break 'outer;
}
ret = avformat_find_stream_info(ifmt_ctx_ptr, std::ptr::null_mut());
if ret < 0 {
println!("Failed to retrieve input stream information");
break 'outer;
}
av_dump_format(ifmt_ctx_ptr, 0, in_filename.as_ptr(), 0);
avformat_alloc_output_context2(
&mut ofmt_ctx_ptr,
std::ptr::null_mut(),
std::ptr::null_mut(),
out_filename.as_ptr(),
);
if ofmt_ctx_ptr.is_null() {
println!("Could not create output context");
ret = AVERROR_UNKNOWN;
break 'outer;
}
let ifmt_ctx = &mut *ifmt_ctx_ptr;
let ofmt_ctx = &mut *ofmt_ctx_ptr;
let in_nb_streams = ifmt_ctx.nb_streams as usize;
let in_streams: &[*mut AVStream] =
std::slice::from_raw_parts(ifmt_ctx.streams, in_nb_streams);
let mut stream_index = 0;
let mut stream_mapping: Vec<i32> = Vec::with_capacity(in_nb_streams);
stream_mapping.resize(stream_mapping.capacity(), -1);
ofmt_ptr = ofmt_ctx.oformat;
for i in 0..in_nb_streams {
let in_stream = &mut *in_streams[i];
let in_codecpar_ptr: *mut AVCodecParameters = in_stream.codecpar;
let in_codecpar = &mut *in_codecpar_ptr;
if in_codecpar.codec_type != AVMEDIA_TYPE_AUDIO
&& in_codecpar.codec_type != AVMEDIA_TYPE_VIDEO
&& in_codecpar.codec_type != AVMEDIA_TYPE_SUBTITLE
{
stream_mapping[i] = -1;
continue;
}
stream_mapping[i] = stream_index;
stream_index += 1;
let out_stream_ptr = avformat_new_stream(ofmt_ctx_ptr, std::ptr::null_mut());
if out_stream_ptr.is_null() {
println!("Failed allocating output stream");
ret = AVERROR_UNKNOWN;
break 'outer;
}
let out_stream = &mut *out_stream_ptr;
ret = avcodec_parameters_copy(out_stream.codecpar, in_codecpar_ptr);
if ret < 0 {
println!("Failed to copy codec parameters");
break 'outer;
}
(*out_stream.codecpar).codec_tag = 0;
}
av_dump_format(ofmt_ctx_ptr, 0, out_filename.as_ptr(), 1);
if ((*ofmt_ptr).flags & AVFMT_NOFILE) != AVFMT_NOFILE {
ret = avio_open(&mut ofmt_ctx.pb, out_filename.as_ptr(), AVIO_FLAG_WRITE);
if ret < 0 {
println!("Could not open output file {:?}", out_filename);
break 'outer;
}
}
ret = avformat_write_header(ofmt_ctx_ptr, std::ptr::null_mut());
if ret < 0 {
println!("Error occurred when opening output file");
break 'outer;
}
let out_streams = std::slice::from_raw_parts(
ofmt_ctx.streams,
ofmt_ctx.nb_streams.try_into().unwrap(),
);
let mut cur_pts: [i64; 64] = [0; 64];
'inner: loop {
ret = av_read_frame(ifmt_ctx_ptr, &mut pkt);
if ret < 0 {
break 'inner;
}
let curr_stream_index = pkt.stream_index as usize;
let in_stream_ptr = in_streams[curr_stream_index];
if curr_stream_index >= stream_mapping.len()
|| stream_mapping[curr_stream_index] < 0
{
av_packet_unref(&mut pkt);
continue;
}
pkt.stream_index = stream_mapping[curr_stream_index];
let out_stream_ptr = out_streams[curr_stream_index];
let in_stream = &mut *in_stream_ptr;
let out_stream = &mut *out_stream_ptr;
let orig_pts = pkt.pts;
let orig_duration = pkt.duration;
if orig_pts == AV_NOPTS_VALUE {
pkt.pts = cur_pts[curr_stream_index];
pkt.dts = pkt.pts;
}
log_packet(ifmt_ctx_ptr, &pkt, "in");
pkt.pts = av_rescale_q_rnd(
pkt.pts,
in_stream.time_base,
out_stream.time_base,
AVRounding::new().near_inf().pass_min_max(),
);
pkt.dts = av_rescale_q_rnd(
pkt.dts,
in_stream.time_base,
out_stream.time_base,
AVRounding::new().near_inf().pass_min_max(),
);
pkt.duration =
av_rescale_q(pkt.duration, in_stream.time_base, out_stream.time_base);
pkt.pos = -1;
log_packet(ofmt_ctx_ptr, &pkt, "out");
ret = av_interleaved_write_frame(ofmt_ctx_ptr, &mut pkt);
if ret < 0 {
println!("Error muxing packet");
break 'inner;
}
if orig_pts == AV_NOPTS_VALUE {
cur_pts[curr_stream_index] += orig_duration;
}
av_packet_unref(&mut pkt);
}
av_write_trailer(ofmt_ctx_ptr);
break 'outer;
}
avformat_close_input(&mut ifmt_ctx_ptr);
if !ofmt_ctx_ptr.is_null() && ((*ofmt_ptr).flags & AVFMT_NOFILE) != AVFMT_NOFILE {
avio_closep(&mut (*ofmt_ctx_ptr).pb);
}
avformat_free_context(ofmt_ctx_ptr);
if ret < 0 && ret != AVERROR_EOF {
println!("Error occurred: {:?}", av_err2str(ret));
std::process::exit(-2);
}
}
}