Audio and video development nine: extract video data stream

It is basically the same as the process of extracting audio data. The main difference is the type of media stream to read and the time base to calculate DTS and PTS.

The general steps are:

  1. open multimedia files
  2. Find video stream from multimedia file
  3. Open the context of the destination file
  4. Create a new video stream for the destination file
  5. Set output video parameters
  6. Write multimedia file header to destination file
  7. Read audio/video data from the source multimedia file to the destination file
  8. Write the end of the multimedia file to the file
  9. Release the requested resources

The flow chart is as follows:

Implementation code:

Extract video data from an MP4 file and output it as an h264 video file.

#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");

}

Guess you like

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