21.FFmpeg学习笔记 - 无编码的视频复用器(mux)

本篇文章的视频复用器是不带编码的。程序输入两个文件,从其中一个文件挑出视频流,从另一个文件挑出音频流,然后合成为某种视频格式(如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;

  
}

猜你喜欢

转载自blog.csdn.net/whoyouare888/article/details/95052458