音视频开发九:抽取视频数据流

和抽取音频数据流程基本一致。主要区别在于读取媒体流的类型和时间基计算DTS和PTS。

一般步骤为:

  1. 打开多媒体文件
  2. 从多媒体文件中找到视频流
  3. 打开目的文件的上下文
  4. 为目的文件,创建一个新的视频流
  5. 设置输出视频参数
  6. 写多媒体文件头到目的文件
  7. 从源多媒体文件中读取音/视频数据到目的文件中
  8. 写多媒体文件尾到文件中
  9. 将申请的资源释放掉

流程图如下:

实现代码:

从一个MP4文件中,抽取视频数据输出为h264视频文件。

#include <stdio.h>
#include <libavutil/log.h>
#include <libavutil/avutil.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avio.h>
#include <libavutil/opt.h>
#include <SDL.h>
#include <stdbool.h>

void getVideo() {

    int ret = -1;
    int idx = -1;

    //1. 处理一些参数;
    char* src;
    char* dst;

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

    const AVOutputFormat* outFmt = NULL;
    AVStream* outStream = NULL;
    AVStream* inStream = NULL;

    AVPacket pkt;

    av_log_set_level(AV_LOG_DEBUG);

    src = "F:\\test_data\\crop_jiuzhe_summer.mp4";
    /*
    * 通常情况下,.h264 文件都是压缩后的裸数据文件,并不包含视频封装格式信息,因此也被称为裸流文件。
      裸流文件中只包含编码后的码流数据,不包含任何音视频格式信息,如帧率、分辨率、采样率、声道数、颜色空间等信息。
    */
    dst = "F:\\test_data\\crop_jiuzhe_summer.h264";
    //dst = "F:\\test_data\\crop_jiuzhe_summer.flv";


    //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. 从多媒体文件中找到视频流
    idx = av_find_best_stream(pFmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if (idx < 0) {
        av_log(pFmtCtx, AV_LOG_ERROR, "Does not include audio stream!\n");
        goto _ERROR;
    }

    //4. 打开目的文件的上下文
    oFmtCtx = avformat_alloc_context();
    if (!oFmtCtx) {
        av_log(NULL, AV_LOG_ERROR, "NO Memory!\n");
        goto _ERROR;
    }
    outFmt = av_guess_format(NULL, dst, NULL);
    oFmtCtx->oformat = outFmt;

    //5. 为目的文件,创建一个新的视频流
    outStream = avformat_new_stream(oFmtCtx, NULL);
    //6. 设置输出视频参数
    inStream = pFmtCtx->streams[idx];
    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) {
        if (pkt.stream_index == idx) {
            // 时间基计算,dts音频和视频不同, 对于音频来说没有B帧 音频的pts=dts 视频有B帧,dts和pts 值不同,单独计算。
            pkt.pts = av_rescale_q_rnd(pkt.pts, inStream->time_base, outStream->time_base, (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
            pkt.dts = av_rescale_q_rnd(pkt.dts, inStream->time_base, outStream->time_base, (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
            pkt.duration = av_rescale_q(pkt.duration, inStream->time_base, outStream->time_base);
            pkt.stream_index = 0;
            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;
    }
    printf("hello, world!\n");

}

猜你喜欢

转载自blog.csdn.net/qq_38056514/article/details/130190770