天天看點

ffmpeg進行h264編碼

首先需要穿件編碼的上下文以及指定編碼器代碼如下

//視訊編碼上下文
- (AVCodecContext *)get_avcodecContext_fv{
    AVCodecContext *c_ctx = NULL;
    AVCodec *codec = NULL;
    codec = avcodec_find_encoder_by_name("libx264");
//    AV_CODEC_ID_H264  avcodec_find_encoder(AV_CODEC_ID_H264);
    c_ctx = avcodec_alloc_context3(codec);
//    c_ctx->flags = AV_CODEC_FLAG_GLOBAL_HEADER;
    c_ctx->profile = FF_PROFILE_H264_HIGH_444;
    c_ctx->level    = 50;
    c_ctx->bit_rate = self.config.bit_rate;
    c_ctx->width    = self.config.width;
    c_ctx->height    = self.config.height;
    c_ctx->time_base = (AVRational){1,self.config.time_base_m};
    c_ctx->framerate = (AVRational){25,1};
    c_ctx->gop_size = 10;
    c_ctx->max_b_frames = 1;
    c_ctx->thread_count = 8;
    c_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
        if (codec->id == AV_CODEC_ID_H264)
    av_opt_set(c_ctx->priv_data, "preset", "slow", 0);
    output_en_codec_v = codec;
    output_en_ctx_v = c_ctx;
    c_ctx->thread_count = 8;
    int is_open = avcodec_open2(c_ctx, codec, NULL);
    if (is_open < 0) {
        fprintf(stderr, "encode_ctx open faild");
        return NULL;
    }
    if (!c_ctx) {
        fprintf(stderr, "Failed to open c_ctx");
        return NULL;
    }
    return c_ctx;
}
           

ffmpeg可以進行軟編碼也可以進行寫死 寫死在編譯ffmpeg庫的時候需要進行特殊的配置在以後的部落格中我會單獨在寫一個ffmpeg 實作寫死的方式 本文隻講述 ffmpeg如何進行軟編碼 

配置編碼使用AVFrame 

AVFrame* get_avframe_video(AVCodecContext *output_en_ctx_v){
    AVFrame *av_frame = NULL;
    av_frame = av_frame_alloc();
    if (!av_frame) {
        fprintf(stderr,"av_frame_alloc failed");
        goto __ERROR;
    }
    av_frame->width     = output_en_ctx_v->width;
    av_frame->height    = output_en_ctx_v->height;
    av_frame->format = output_en_ctx_v->pix_fmt;//AV_PIX_FMT_YUV420P;
    av_frame_get_buffer(av_frame, 32);
    if (!av_frame->data[0]) {
        printf("Error, Failed to alloc buf in frame!\n");
        return NULL;
    }
    return av_frame;
__ERROR:
    if (!av_frame) {
        av_frame_free(&av_frame);
    }
    return NULL;
}
           

進行編碼ffmpeg在16年還是17年以後推薦使用的api為

int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame);

int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);

- (BOOL)encode_video:(AVCodecContext *)output_ctx and_packet:(AVPacket *)output_avpacket and_frame:(AVFrame *)output_avframe with_h264file:(FILE*)h264_file{
    BOOL is_encode_success = NO;
    int ret = -1;
    ret = avcodec_send_frame(output_ctx, output_avframe);
    while (ret >= 0) {
        ret = avcodec_receive_packet(output_ctx, output_avpacket);
        if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
            return is_encode_success;
        }else if( ret < 0){
            printf("Error, encoding audio frame\n");
            exit(-1);
        }
        
        if (h264_file) {
            fwrite(output_avpacket->data, 1, output_avpacket->size, h264_file);
        }else{
            [self separation_avpacket_with_avpacket:output_avpacket];
        }
        is_encode_success = YES;
        av_packet_unref(output_avpacket);
    }
    return is_encode_success;
}
           

由ffmpeg 編碼後的avpacket 可以使用ffmpeg 進行推流 也可以使用rtmp 進行推流 如果使用rtmp進行推流 則需要自己建構rtmppacket 

avpacket如何rtmppacket的建構在下個部落格中進行講解

這裡編碼出的avpacket 可以直接寫入檔案生成.h264 檔案 需要注意的是 在編碼時需要指定時間基 時間基的指定可以有多種方式

1.每次自增1

2.采取目前時間戳同步來進行指派

這兩種方式都可以 正常的播放 

如果不指定時間戳,寫入的h264檔案在播放時會特别快 再有就是 如果寫入flv檔案 需要注意的問題是 在停止采集時需要向編碼器發送空資料,将編碼器緩沖區的資料取出