ffmpeg 编码示例

ffmpeg 自身有h264的解码模块。编码却需要打包入其他编码库。具体效率分析参看: 主流开源编解码器Xvid,x264,ffmpeg 性能对比(转)


实现编码定义了四个函数。


一:帧添加结束后flush,将未输出的avpacket写入

int vflush_encoder(AVFormatContext *fmt_ctx, unsigned int stream_index) {
    int ret = 0;
    int got_frame;
    AVPacket enc_pkt;
    if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities
            & CODEC_CAP_DELAY))
        return 0;
    av_init_packet(&enc_pkt);
    while (1) {
        enc_pkt.data = NULL;
        enc_pkt.size = 0;
        ret = avcodec_encode_video2(fmt_ctx->streams[stream_index]->codec,
                &enc_pkt, NULL, &got_frame);
        if (ret < 0)
            break;
        if (!got_frame) {
            ret = 0;
            break;
        }
        ret = av_write_frame(fmt_ctx, &enc_pkt);
        av_free_packet(&enc_pkt);
        if (ret < 0)
            break;
    }

    return ret;
}



二 编码前初始化

ENCODE_KEY* encode_init(char* h264file)
{
    ENCODE_KEY* temp = (ENCODE_KEY*) malloc(sizeof(ENCODE_KEY));
    AVFormatContext* pFormatCtx;
    AVOutputFormat* fmt;
    AVStream* video_st;
    AVCodecContext* pCodecCtx;
    AVCodec* pCodec;
    AVFrame* pFrame;
    char* out_file = h264file;
    av_register_all();
    pFormatCtx = avformat_alloc_context();
    fmt = av_guess_format(NULL, param->h264file, NULL);
    pFormatCtx->oformat = fmt;

    if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0) {
        dmprint_string("Failed to open output file! ");
        return NULL;
    }

    video_st = avformat_new_stream(pFormatCtx, 0);
    video_st->time_base.num = 1;
    video_st->time_base.den = 30;

    if (video_st == NULL) {
        return NULL;
    }
    //Param that must set
    pCodecCtx = video_st->codec;
    pCodecCtx->codec_id =AV_CODEC_ID_H264;
    pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
    pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    pCodecCtx->width =640;
    pCodecCtx->height =640;
    pCodecCtx->time_base.num = 1;
    pCodecCtx->time_base.den = 30;
    //pCodecCtx->bit_rate = 1600000;
    pCodecCtx->gop_size = 150;
    pCodecCtx->keyint_min =30;
    pCodecCtx->thread_count =15;
    pCodecCtx->me_range = 16;
    pCodecCtx->max_qdiff = 4;
    pCodecCtx->qcompress = 0.6;

    pCodecCtx->max_b_frames = 0;
    pCodecCtx->b_frame_strategy = true;
    //量化因子
    pCodecCtx->qmin = 16;
    pCodecCtx->qmax =24;

    av_opt_set(pCodecCtx->priv_data, "preset", "ultrafast", 0);
    av_opt_set(pCodecCtx->priv_data, "tune", "zerolatency", 0);

  
    int ret;
    pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
    if (!pCodec) {
        puts("Can not find encoder!");
        return NULL;
    }
    if ((ret = avcodec_open2(pCodecCtx, pCodec, &p)) < 0) {
           printf("Failed to open encoder! ret = %d\n",ret);
        return NULL;
    }


    pFrame = av_frame_alloc();
    pFrame->width = param->width;
    pFrame->height = param->height;
    pFrame->format = param->pic_fmt;
   
    avformat_write_header(pFormatCtx, NULL);

    temp->pFormatCtx = pFormatCtx;
    temp->video_st = video_st;
    temp->pCodecCtx=pCodecCtx;
    temp->pFrame=pFrame;
    return temp;
}


ENCODE_KEY是封装的一个结构体,里面有编码所用到的结构体集合和部分参数。
内容如下;
typedef struct encode_key{
    AVFormatContext* pFormatCtx;
    AVCodecContext* pCodecCtx;
    AVStream* video_st;
    AVFrame* pFrame;
}ENCODE_KEY;    


为了实现同时支持编码多个视频,所以将这些封装为结构体传送指针。


三 编码一帧

int encode_frame(AVFrame* frame,ENCODE_KEY* temp)
{
    AVPacket encode_pkt;
    int got_picture  = 0;
    int encode_ret = 0;
    av_new_packet(&encode_pkt, avpicture_get_size(temp->pCodecCtx->pix_fmt, temp->pCodecCtx->width,
            temp->pCodecCtx->height));
    int ret = 0;

    ret = avcodec_encode_video2(temp->pCodecCtx, &encode_pkt, frame, &got_picture);

    if (ret < 0) {
        encode_ret = -1;
        goto encode_end;
    }
    if (got_picture == 1) {
        encode_pkt.stream_index = temp->video_st->index;
        ret = av_write_frame(temp->pFormatCtx, &encode_pkt);
    }
    else
    {
       //
    }
encode_end:
    av_free_packet(&encode_pkt);
    return encode_ret;

}




四  停止编码,释放资源

int encode_release(ENCODE_KEY* temp)
{
    int ret = vflush_encoder(temp->pFormatCtx, 0);
    if (ret < 0) {
        
        return -1;
    }


    //Write file trailer
    av_write_trailer(temp->pFormatCtx);

    //Clean
    if (temp->video_st) {
        avcodec_close(temp->video_st->codec);
        av_free(temp->pFrame);
    }
    avio_close(temp->pFormatCtx->pb);
    avformat_free_context(temp->pFormatCtx);
  
    return 0;
}


以下是一个基本的 ffmpeg 编码示例,可以将 FIFO 中的视频信息放入编码器: ```c #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> int main(int argc, char *argv[]) { AVFormatContext *format_context; AVCodecContext *codec_context; AVCodec *codec; AVPacket packet; AVFrame *frame; int stream_index; int ret; // 打开输入 FIFO ret = avformat_open_input(&format_context, "input.fifo", NULL, NULL); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Could not open input FIFO\n"); return ret; } // 查找输入流信息 ret = avformat_find_stream_info(format_context, NULL); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Could not find stream information\n"); return ret; } // 查找视频流 stream_index = av_find_best_stream(format_context, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0); if (stream_index < 0) { av_log(NULL, AV_LOG_ERROR, "Could not find video stream in input file\n"); return stream_index; } // 打开视频解码器 codec_context = avcodec_alloc_context3(codec); if (!codec_context) { av_log(NULL, AV_LOG_ERROR, "Could not allocate codec context\n"); return AVERROR(ENOMEM); } ret = avcodec_parameters_to_context(codec_context, format_context->streams[stream_index]->codecpar); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Could not copy codec parameters to codec context\n"); return ret; } ret = avcodec_open2(codec_context, codec, NULL); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Could not open codec\n"); return ret; } // 分配视频帧内存 frame = av_frame_alloc(); if (!frame) { av_log(NULL, AV_LOG_ERROR, "Could not allocate frame\n"); return AVERROR(ENOMEM); } // 初始化 AVPacket av_init_packet(&packet); packet.data = NULL; packet.size = 0; // 读取 FIFO 中的视频信息 while (av_read_frame(format_context, &packet) >= 0) { // 将 AVPacket 转换为 AVFrame ret = avcodec_send_packet(codec_context, &packet); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Error sending packet to codec\n"); break; } while (ret >= 0) { ret = avcodec_receive_frame(codec_context, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; else if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Error receiving frame from codec\n"); break; } // 在这里将 AVFrame 写入编码器 } av_packet_unref(&packet); } // 清理 av_frame_free(&frame); avcodec_free_context(&codec_context); avformat_close_input(&format_context); return 0; } ``` 在上面的代码中,`av_read_frame()` 函数从 FIFO 中读取一个 `AVPacket`,然后将其发送到编码器。编码器使用 `avcodec_receive_frame()` 函数将 `AVPacket` 转换为 `AVFrame`,然后在代码中的注释部分,可以将 `AVFrame` 写入编码器中。注意,这里仅仅是一个编码示例,实际上如何将 `AVFrame` 写入编码器中取决于你使用的编码器和编码设置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值