1use ffav_sys::{AVMediaType::*, *};
32use std::convert::TryInto;
33use std::env;
34use std::ffi::CString;
35
36unsafe extern "C" fn log_packet(fmt_ctx: *const AVFormatContext, pkt: *const AVPacket, tag: &str) {
37 let pkt = &*pkt;
38 let fmt_ctx = &*fmt_ctx;
39 let streams: &[*mut AVStream] =
40 std::slice::from_raw_parts(fmt_ctx.streams, fmt_ctx.nb_streams as usize);
41 let stream = &*streams[pkt.stream_index as usize];
42 let time_base = &stream.time_base;
43
44 println!(
45 "{}: pts:{} pts_time:{} dts:{} dts_time:{} duration:{} duration_time:{} stream_index:{}",
46 tag,
47 av_ts2str(pkt.pts),
48 av_ts2timestr(pkt.pts, time_base),
49 av_ts2str(pkt.dts),
50 av_ts2timestr(pkt.dts, time_base),
51 av_ts2str(pkt.duration),
52 av_ts2timestr(pkt.duration, time_base),
53 pkt.stream_index,
54 );
55}
56
57fn main() {
58 unsafe {
59 let mut ofmt_ptr: *mut AVOutputFormat = std::ptr::null_mut();
60 let mut ifmt_ctx_ptr: *mut AVFormatContext = std::ptr::null_mut();
61 let mut ofmt_ctx_ptr: *mut AVFormatContext = std::ptr::null_mut();
62 let mut pkt: AVPacket = std::mem::zeroed();
63 let mut ret;
64
65 let args = env::args().collect::<Vec<_>>();
66 if args.len() < 3 {
67 println!(
68 "usage: {} input output\n\
69 API example program to remux a media file with libavformat and libavcodec.\n\
70 The output format is guessed according to the file extension.\n",
71 args[0]
72 );
73 std::process::exit(-1);
74 }
75
76 let in_filename = CString::new(args[1].clone()).unwrap();
77 let out_filename = CString::new(args[2].clone()).unwrap();
78
79 'outer: loop {
80 ret = avformat_open_input(
81 &mut ifmt_ctx_ptr,
82 in_filename.as_ptr(),
83 std::ptr::null_mut(),
84 std::ptr::null_mut(),
85 );
86 if ret < 0 {
87 println!("Could not open input file {:?}", in_filename);
88 break 'outer;
89 }
90
91 ret = avformat_find_stream_info(ifmt_ctx_ptr, std::ptr::null_mut());
92 if ret < 0 {
93 println!("Failed to retrieve input stream information");
94 break 'outer;
95 }
96
97 av_dump_format(ifmt_ctx_ptr, 0, in_filename.as_ptr(), 0);
98
99 avformat_alloc_output_context2(
100 &mut ofmt_ctx_ptr,
101 std::ptr::null_mut(),
102 std::ptr::null_mut(),
103 out_filename.as_ptr(),
104 );
105 if ofmt_ctx_ptr.is_null() {
106 println!("Could not create output context");
107 ret = AVERROR_UNKNOWN;
108 break 'outer;
109 }
110
111 let ifmt_ctx = &mut *ifmt_ctx_ptr;
112 let ofmt_ctx = &mut *ofmt_ctx_ptr;
113
114 let in_nb_streams = ifmt_ctx.nb_streams as usize;
115 let in_streams: &[*mut AVStream] =
116 std::slice::from_raw_parts(ifmt_ctx.streams, in_nb_streams);
117
118 let mut stream_index = 0;
119 let mut stream_mapping: Vec<i32> = Vec::with_capacity(in_nb_streams);
120 stream_mapping.resize(stream_mapping.capacity(), -1);
121
122 ofmt_ptr = ofmt_ctx.oformat;
123
124 for i in 0..in_nb_streams {
125 let in_stream = &mut *in_streams[i];
126 let in_codecpar_ptr: *mut AVCodecParameters = in_stream.codecpar;
127 let in_codecpar = &mut *in_codecpar_ptr;
128
129 if in_codecpar.codec_type != AVMEDIA_TYPE_AUDIO
130 && in_codecpar.codec_type != AVMEDIA_TYPE_VIDEO
131 && in_codecpar.codec_type != AVMEDIA_TYPE_SUBTITLE
132 {
133 stream_mapping[i] = -1;
134 continue;
135 }
136
137 stream_mapping[i] = stream_index;
138 stream_index += 1;
139
140 let out_stream_ptr = avformat_new_stream(ofmt_ctx_ptr, std::ptr::null_mut());
141 if out_stream_ptr.is_null() {
142 println!("Failed allocating output stream");
143 ret = AVERROR_UNKNOWN;
144 break 'outer;
145 }
146
147 let out_stream = &mut *out_stream_ptr;
148
149 ret = avcodec_parameters_copy(out_stream.codecpar, in_codecpar_ptr);
150 if ret < 0 {
151 println!("Failed to copy codec parameters");
152 break 'outer;
153 }
154 (*out_stream.codecpar).codec_tag = 0;
155 }
156
157 av_dump_format(ofmt_ctx_ptr, 0, out_filename.as_ptr(), 1);
158
159 if ((*ofmt_ptr).flags & AVFMT_NOFILE) != AVFMT_NOFILE {
160 ret = avio_open(&mut ofmt_ctx.pb, out_filename.as_ptr(), AVIO_FLAG_WRITE);
161 if ret < 0 {
162 println!("Could not open output file {:?}", out_filename);
163 break 'outer;
164 }
165 }
166
167 ret = avformat_write_header(ofmt_ctx_ptr, std::ptr::null_mut());
168 if ret < 0 {
169 println!("Error occurred when opening output file");
170 break 'outer;
171 }
172
173 let out_streams = std::slice::from_raw_parts(
174 ofmt_ctx.streams,
175 ofmt_ctx.nb_streams.try_into().unwrap(),
176 );
177
178 let mut cur_pts: [i64; 64] = [0; 64];
179
180 'inner: loop {
181 ret = av_read_frame(ifmt_ctx_ptr, &mut pkt);
182 if ret < 0 {
183 break 'inner;
184 }
185
186 let curr_stream_index = pkt.stream_index as usize;
187 let in_stream_ptr = in_streams[curr_stream_index];
188 if curr_stream_index >= stream_mapping.len()
189 || stream_mapping[curr_stream_index] < 0
190 {
191 av_packet_unref(&mut pkt);
192 continue;
193 }
194
195 pkt.stream_index = stream_mapping[curr_stream_index];
196 let out_stream_ptr = out_streams[curr_stream_index];
197
198 let in_stream = &mut *in_stream_ptr;
199 let out_stream = &mut *out_stream_ptr;
200
201 let orig_pts = pkt.pts;
202 let orig_duration = pkt.duration;
203
204 if orig_pts == AV_NOPTS_VALUE {
205 pkt.pts = cur_pts[curr_stream_index];
206 pkt.dts = pkt.pts;
207 }
208
209 log_packet(ifmt_ctx_ptr, &pkt, "in");
210
211 pkt.pts = av_rescale_q_rnd(
213 pkt.pts,
214 in_stream.time_base,
215 out_stream.time_base,
216 AVRounding::new().near_inf().pass_min_max(),
217 );
218 pkt.dts = av_rescale_q_rnd(
219 pkt.dts,
220 in_stream.time_base,
221 out_stream.time_base,
222 AVRounding::new().near_inf().pass_min_max(),
223 );
224 pkt.duration =
225 av_rescale_q(pkt.duration, in_stream.time_base, out_stream.time_base);
226 pkt.pos = -1;
227 log_packet(ofmt_ctx_ptr, &pkt, "out");
228
229 ret = av_interleaved_write_frame(ofmt_ctx_ptr, &mut pkt);
230 if ret < 0 {
231 println!("Error muxing packet");
232 break 'inner;
233 }
234
235 if orig_pts == AV_NOPTS_VALUE {
236 cur_pts[curr_stream_index] += orig_duration;
237 }
238
239 av_packet_unref(&mut pkt);
240 }
241
242 av_write_trailer(ofmt_ctx_ptr);
243
244 break 'outer;
245 }
246
247 avformat_close_input(&mut ifmt_ctx_ptr);
248
249 if !ofmt_ctx_ptr.is_null() && ((*ofmt_ptr).flags & AVFMT_NOFILE) != AVFMT_NOFILE {
251 avio_closep(&mut (*ofmt_ctx_ptr).pb);
252 }
253 avformat_free_context(ofmt_ctx_ptr);
254
255 if ret < 0 && ret != AVERROR_EOF {
256 println!("Error occurred: {:?}", av_err2str(ret));
257 std::process::exit(-2);
258 }
259 }
260}