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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM