ffmpeg——提取h264

总体思路

       从mp4文件中取出需要的video流,从流中取包,写入新的h264文件。

/*
 *    对于音频:
 *    音频每一帧都有帧首部,包含码率等信息。
 *
 *     对于视频:
 *    start code: 用于确定一帧的开始
 *    SPS/PPS:    确定分辨率,由于每帧的分辨率可能不同,所以每关键帧数据都应该有SPS和PPS
 *    codec->extradata: 用于获取SPS/PPS
 *
 *     所以提取流数据时,除了多媒体数据本身,还应该拷贝帧头数据
 *     ffmpeg 为了方便编程,统一了api
 *
 *     avformat_alloc_output_context2/ avformat_free_context
 *     avformat_new_stream
 *     avcodec_parameters_copy
 *     
 *     文件首尾,帧首都可能添加信息,ffmpeg为了方便编程,统一了写输出文件的api
 *     avformat_write_header
 *     av_write_frame/ avinterleaved_write_frame
 *     av_write_trailer
 *
 */


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

#define PrintError(errnum) do{                            \
    char errstr[512];                            \
    av_strerror(errnum, errstr, sizeof(errstr));                \
    av_log(NULL, AV_LOG_ERROR, "failed to open %s : %s\n", in, errstr);    \
}while(0)

int main(int argc, char **argv)
{
    AVFormatContext        *ifmt_ctx = NULL, *ofmt_ctx  = NULL;
    AVOutputFormat        *output_fmt = NULL;
    const char         *in, *out;
    int             stream_index, errnum;
    AVStream        *o_stream = NULL, *i_stream = NULL;
    AVIOContext        *pb = NULL;
    AVPacket        pkt;

    //    av_register_all();
    av_log_set_level(AV_LOG_INFO);

    if (argc != 3) {
        av_log(NULL, AV_LOG_ERROR, "usage : a.out <in> <out>\n");
        goto __failed__;
    }

    in = argv[1];
    out = argv[2];

    /********打开多媒体文件*******/
    if ((errnum = avformat_open_input(&ifmt_ctx, in, NULL, NULL)) < 0) {
        PrintError(errnum);
        goto __failed__;
    }
    /*********找到需要的流***********/
    if (0 > (stream_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO /*AVMEDIA_TYPE_VIDEO*/, -1, -1, NULL, 0))) {

        av_log(NULL, AV_LOG_ERROR, "find not best stream\n");
        goto __failed__;
    }
    i_stream = ifmt_ctx->streams[stream_index];

    /**********构造输出多媒体文件并设置默认参数**************/
#if 1
    if (0 > (errnum = avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out))) {
        PrintError(errnum);
        goto __failed__;
    }
#else
    ofmt_ctx = avformat_alloc_context();
    output_fmt = av_guess_format(NULL, out, NULL);
    if (!output_fmt) {
        av_log(NULL, AV_LOG_DEBUG, "根据目标生成输出容器失败!\n");
        exit(1);
    }
    ofmt_ctx->oformat = output_fmt;
#endif

    /*********在ofmt_ctx上构造流********/
    if (!(o_stream = avformat_new_stream(ofmt_ctx, NULL))) {
        av_log(NULL, AV_LOG_ERROR, "failed to new stream\n");
        goto __failed__;
    }
    /**********初始化输出流的参数*********************/
    if (0 > avcodec_parameters_copy(o_stream->codecpar, i_stream->codecpar)) {
        av_log(NULL, AV_LOG_ERROR, "failed to copy stream codecpar\n");
        goto __failed__;
    }
    o_stream->codecpar->codec_tag = 0;
    /***********以只写方式打开输出文件***********/
    pb = ofmt_ctx->pb;
    if (0 > avio_open(&ofmt_ctx->pb, out, AVIO_FLAG_WRITE)) {
        av_log(NULL, AV_LOG_ERROR, "failed to open avio\n");
        goto __failed__;
    }

    /***********初始化pkt通用域**********/
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;

    /***********用默认值写头信息*************/
    if (avformat_write_header(ofmt_ctx, NULL) < 0) {
        av_log(NULL, AV_LOG_DEBUG, "写入头部信息失败!\n");
        exit(1);
    }


    /**********写数据*********/
    while(av_read_frame(ifmt_ctx, &pkt) >= 0) {
        if (pkt.stream_index == stream_index) {        // 需要保证包是需要的流
            pkt.dts = av_rescale_q_rnd(pkt.dts, i_stream->time_base, o_stream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);        // 四舍五入方式,重新度量显示时间戳
            pkt.pts = av_rescale_q_rnd(pkt.pts, i_stream->time_base, o_stream->time_base,  AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);        // 重四舍五入方式,新度量解码时间戳
            pkt.duration = av_rescale_q(pkt.duration, i_stream->time_base, o_stream->time_base) ;    // 重新度量周期
            pkt.pos = -1;        // 将该包的已读取位置置-1
            pkt.stream_index = 0;
            av_interleaved_write_frame(ofmt_ctx, &pkt);
        }

        av_packet_unref(&pkt);    // 将pkt.buf 引用计数减一
    }

    /*******写尾*******/
    av_write_trailer(ofmt_ctx);

    av_log(NULL, AV_LOG_DEBUG, "44\n");

__failed__:
    if (ifmt_ctx)
        avformat_close_input(&ifmt_ctx);
    if (ofmt_ctx)
        avformat_free_context(ofmt_ctx);
    if (pb)
        avio_close(pb);

    return 0;
}

猜你喜欢

转载自www.cnblogs.com/yangxinrui/p/11979639.html