本篇文章的视频复用器是不带编码的。程序输入两个文件,从其中一个文件挑出视频流,从另一个文件挑出音频流,然后合成为某种视频格式(如mp4/ts/flv等),输出成文件。
其中视频流所在的文件,可以是mp4/ts/flv等格式的视频文件,也可以是h264等纯视频编码文件。音频流所在文件可以是mp4/ts/flv等格式的视频文件,也可以是aac等纯音频编码的文件。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <libavutil/avassert.h>
#include <libavutil/channel_layout.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
static int in_videoindex = -1, in_audioindex = -1, out_videoindex = -1, out_audioindex = -1;
static AVFormatContext *ifmt_ctx_v = NULL, *ifmt_ctx_a = NULL, *ofmt_ctx = NULL;
static int frame_index = 0;
static void mux_without_encode()
{
const char *in_filename_v = "/Users/zhw/Desktop/resource/sintel_h264_aac.ts";
const char *in_filename_a = "/Users/zhw/Desktop/resource/sintel_h264_aac.ts";
const char *out_filename = "/Users/zhw/Desktop/result.mp4";
int ret;
AVOutputFormat *ofmt;
ret = avformat_open_input(&ifmt_ctx_v, in_filename_v, NULL, NULL);
if (ret < 0) {
printf("avformat_open_input %s fail\n", in_filename_v);
exit(1);
}
ret = avformat_find_stream_info(ifmt_ctx_v, NULL);
if (ret < 0) {
printf("avformat_find_stream_info %s fail\n", in_filename_v);
exit(1);
}
ret = avformat_open_input(&ifmt_ctx_a, in_filename_a, NULL, NULL);
if (ret < 0) {
printf("avformat_open_input %s fail\n", in_filename_a);
exit(1);
}
ret = avformat_find_stream_info(ifmt_ctx_a, NULL);
if (ret < 0) {
printf("avformat_find_stream_info %s fail\n", in_filename_a);
exit(1);
}
av_dump_format(ifmt_ctx_v, 0, in_filename_v, 0);
av_dump_format(ifmt_ctx_a, 0, in_filename_a, 0);
//查找视频流
for (int i = 0; i < ifmt_ctx_v->nb_streams; i++) {
if (ifmt_ctx_v->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
in_videoindex = i;
break;
}
}
//查找音频流
for (int i = 0; i < ifmt_ctx_a->nb_streams; i++) {
if (ifmt_ctx_a->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
in_audioindex = i;
break;
}
}
if (in_videoindex == -1 || in_audioindex == -1) {
printf("input video or audio stream not exist\n");
exit(1);
}
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
if (!ofmt_ctx) {
printf("Could not create output context\n");
exit(1);
}
ofmt = ofmt_ctx->oformat;
AVStream *in_stream_v = ifmt_ctx_v->streams[in_videoindex];
AVStream *out_stream_v = avformat_new_stream(ofmt_ctx, NULL);
if (!out_stream_v) {
printf("avformat_new_stream fail\n");
exit(1);
}
out_videoindex = out_stream_v->index;
avcodec_parameters_copy(out_stream_v->codecpar, in_stream_v->codecpar);
out_stream_v->codecpar->codec_tag = 0;
AVStream *in_stream_a = ifmt_ctx_a->streams[in_audioindex];
AVStream *out_stream_a = avformat_new_stream(ofmt_ctx, NULL);
if (!out_stream_a) {
printf("avformat_new_stream fail\n");
exit(1);
}
out_audioindex = out_stream_a->index;
avcodec_parameters_copy(out_stream_a->codecpar, in_stream_a->codecpar);
out_stream_a->codecpar->codec_tag = 0;
av_dump_format(ofmt_ctx, 1, out_filename, 1);
//打开输出文件
if (!(ofmt->flags & AVFMT_NOFILE)) {
if (avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE) < 0) {
printf( "Could not open output file '%s'", out_filename);
exit(1);
}
}
//写文件头
if ((ret = avformat_write_header(ofmt_ctx, NULL)) < 0) {
printf( "Error occurred when opening output file\n");
exit(1);
}
ret = 0;
while (ret >= 0) {
ret = mux();
}
//写文件尾
av_write_trailer(ofmt_ctx);
avformat_close_input(&ifmt_ctx_v);
avformat_close_input(&ifmt_ctx_a);
/* close output */
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
avio_close(ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
printf("finish \n");
}
static int mux()
{
int ret = 0;
static int64_t cur_pts_v=0,cur_pts_a=0;
AVStream *in_stream_v, *in_stream_a;
AVPacket pkt;
av_init_packet(&pkt);
in_stream_v = ifmt_ctx_v->streams[in_videoindex];
in_stream_a = ifmt_ctx_a->streams[in_audioindex];
//Get an AVPacket
if(av_compare_ts(cur_pts_v, in_stream_v->time_base, cur_pts_a, in_stream_a->time_base) <= 0){
while ((ret = av_read_frame(ifmt_ctx_v, &pkt)) >= 0) {
if (pkt.stream_index == in_videoindex) {
if (pkt.pts == AV_NOPTS_VALUE) {
AVRational time_base = in_stream_v->time_base;
//两帧之间的时间间隔
double calc_duration = 1 / av_q2d(in_stream_v->r_frame_rate);
//Parameters
pkt.pts = frame_index * calc_duration / av_q2d(time_base);
pkt.dts = pkt.pts;
pkt.duration = calc_duration / av_q2d(time_base);
frame_index++;
}
cur_pts_v = pkt.pts;
break;
}
av_packet_unref(&pkt);
}
av_packet_rescale_ts(&pkt, ifmt_ctx_v->streams[in_videoindex]->time_base, ofmt_ctx->streams[out_videoindex]->time_base);
pkt.stream_index = out_videoindex;
}else{
while ((ret = av_read_frame(ifmt_ctx_a, &pkt)) >= 0) {
if (pkt.stream_index == in_audioindex) {
if(pkt.pts==AV_NOPTS_VALUE){
AVRational time_base = in_stream_a->time_base;
//两帧之间的时间间隔
double calc_duration = 1 / av_q2d(in_stream_a->r_frame_rate);
//Parameters
pkt.pts = frame_index * calc_duration / av_q2d(time_base);
pkt.dts = pkt.pts;
pkt.duration = calc_duration / av_q2d(time_base);
frame_index++;
}
cur_pts_a = pkt.pts;
break;
}
av_packet_unref(&pkt);
}
av_packet_rescale_ts(&pkt, ifmt_ctx_a->streams[in_audioindex]->time_base, ofmt_ctx->streams[out_audioindex]->time_base);
pkt.stream_index = out_audioindex;
}
printf("Write Packet. size:%5d\tpts:%lld stream_index:%d\n", pkt.size, pkt.pts, pkt.stream_index);
if (pkt.size == 0) {
if (av_interleaved_write_frame(ofmt_ctx, NULL) < 0) {
printf( "Error muxing packet\n");
return -1;
}
}else {
//Write
if (av_interleaved_write_frame(ofmt_ctx, &pkt) < 0) {
printf( "Error muxing packet\n");
return -1;
}
}
return ret;
}