Audio and video development ten: multimedia format conversion

Conversion principle

The main thing is to decapsulate multimedia files and change to a new encapsulation format. Compared with extracting audio stream data or video stream data separately before, the format conversion is to write all the required streams after decapsulation into the new encapsulation format. .

logical flow

Implementation code

Convert an mp4 multimedia file to mov format.

#include <stdio.h>
#include <libavutil/log.h>
#include <libavutil/avutil.h>
#include <libavformat/avformat.h>

/*转封装格式*/
int remux() {
    
    

    int ret = -1;
    int idx = -1;
    int stream_idx = 0;
    int i = 0;
    char* src;
    char* dst;

   

    int* stream_map = NULL;

    AVFormatContext* pFmtCtx = NULL;
    AVFormatContext* oFmtCtx = NULL;

    const AVOutputFormat* outFmt = NULL;

    AVPacket pkt;

    av_log_set_level(AV_LOG_DEBUG);
    


    src = "F:\\test_data\\crop_jiuzhe_summer.mp4";
    dst = "F:\\test_data\\crop_jiuzhe_summer.mov";

    //2. 打开多媒体文件
    if ((ret = avformat_open_input(&pFmtCtx, src, NULL, NULL)) < 0) {
    
    
        av_log(NULL, AV_LOG_ERROR, "%s\n", av_err2str(ret));
        exit(-1);
    }

    //3. 现在是抽取所有的流,所以省去了抽取某一个流的步骤

    //4. 打开目的文件的上下文,同时指定格式
    avformat_alloc_output_context2(&oFmtCtx, NULL, NULL, dst);
    if (!oFmtCtx) {
    
    
        av_log(NULL, AV_LOG_ERROR, "NO MEMORY!\n");
        goto _ERROR;
    }

    // ffmpeg 分配空间方法
    stream_map = av_calloc(pFmtCtx->nb_streams, sizeof(int));
    if (!stream_map) {
    
    
        av_log(NULL, AV_LOG_ERROR, "NO MEMORY!\n");
        goto _ERROR;
    }

    // 对输入文件所有的流进行遍历操作
    // 注意,在输入文件中,可能不只包含音频、视频流和字幕流,
    // 还有可能meta数据的流,这部分的流是对我们来说是没有意义的。转封装的话,meta信息会改变。
    for (i = 0; i < pFmtCtx->nb_streams; i++) {
    
    
        AVStream* outStream = NULL;
        AVStream* inStream = pFmtCtx->streams[i];
        AVCodecParameters* inCodecPar = inStream->codecpar;
        // 如果不是音频 视频或者字幕流 就把改流标记位-1
        if (inCodecPar->codec_type != AVMEDIA_TYPE_AUDIO &&
            inCodecPar->codec_type != AVMEDIA_TYPE_VIDEO &&
            inCodecPar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
    
    
            stream_map[i] = -1;
            continue;
        }
        stream_map[i] = stream_idx++;

        //5. 为目的文件,创建一个新的多媒体流 每次创建都会在输出上下文中添加一项
        outStream = avformat_new_stream(oFmtCtx, NULL);
        if (!outStream) {
    
    
            av_log(oFmtCtx, AV_LOG_ERROR, "NO MEMORY!\n");
            goto _ERROR;
        }

        avcodec_parameters_copy(outStream->codecpar, inStream->codecpar);
        outStream->codecpar->codec_tag = 0;
    }

    //绑定
    ret = avio_open2(&oFmtCtx->pb, dst, AVIO_FLAG_WRITE, NULL, NULL);
    if (ret < 0) {
    
    
        av_log(oFmtCtx, AV_LOG_ERROR, "%s", av_err2str(ret));
        goto _ERROR;
    }

    //7. 写多媒体文件头到目的文件
    ret = avformat_write_header(oFmtCtx, NULL);
    if (ret < 0) {
    
    
        av_log(oFmtCtx, AV_LOG_ERROR, "%s", av_err2str(ret));
        goto _ERROR;
    }
    //8. 从源多媒体文件中读取音频/视频/字幕数据到目的文件中
    while (av_read_frame(pFmtCtx, &pkt) >= 0) {
    
    
        AVStream* inStream, * outStream;
        int tmepIndex = pkt.stream_index;

        // av_read_frame 读取这个数据帧,存储在pkt包中
        // pkt.stream_index 表示 这个包所在的媒体流的索引
        inStream = pFmtCtx->streams[tmepIndex];

        // stream_map[pkt.stream_index] < 0 表示该流不是音视频流和字幕流
        if (stream_map[tmepIndex] < 0) {
    
    
            av_packet_unref(&pkt);
            continue;
        }

        // stream_map[pkt.stream_index] 返回的一个ID值 ,表示输出文件所在媒体流所在的索引ID
        pkt.stream_index = stream_map[tmepIndex];

        outStream = oFmtCtx->streams[tmepIndex];

        // 更好用的修改时间基的用法。替代了之前抽取音/视频 对pts和dts时间戳修改的步骤
        av_packet_rescale_ts(&pkt, inStream->time_base, outStream->time_base);
        pkt.pos = -1;
        av_interleaved_write_frame(oFmtCtx, &pkt);
        av_packet_unref(&pkt);

    }
    //9. 写多媒体文件尾到文件中
    av_write_trailer(oFmtCtx);

    //10. 将申请的资源释放掉
_ERROR:
    if (pFmtCtx) {
    
    
        avformat_close_input(&pFmtCtx);
        pFmtCtx = NULL;
    }
    if (oFmtCtx->pb) {
    
    
        avio_close(oFmtCtx->pb);
    }

    if (oFmtCtx) {
    
    
        avformat_free_context(oFmtCtx);
        oFmtCtx = NULL;
    }

    if (stream_map) {
    
    
        av_free(stream_map);
    }
    printf("hello, world!\n");
    return 0;
}

Guess you like

Origin blog.csdn.net/qq_38056514/article/details/130190778