remuxing/
remuxing.rs

1/*
2 * Copyright (c) 2013 Stefano Sabatini
3 * Copyright (c) 2020 Varphone Wong
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 * THE SOFTWARE.
22 */
23
24/**
25 * @file
26 * libavformat/libavcodec demuxing and muxing API example.
27 *
28 * Remux streams from one container format to another.
29 * @example remuxing.rs
30 */
31use 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                /* copy packet */
212                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        // close output
250        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}