ffmpeg 自身有h264的解码模块。编码却需要打包入其他编码库。具体效率分析参看:
主流开源编解码器Xvid,x264,ffmpeg 性能对比(转)
实现编码定义了四个函数。
一:帧添加结束后flush,将未输出的avpacket写入
调用flush原因分析:
ffmpeg /x264视频流编解码末尾丢帧问题分析和解决
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;
}