由于工作需要,要将本公司的h264编码库,封装到ffmpeg中,替换正式的x264编码库,
1、使用公司svn上的2.6.2的版本,总是libpostproc出错,后来想从官网上下载,但在http://ffmpeg.org/download.html是没有2.6.2的版本的,它是从2.5.11跳到2.6.9,
要在http://ffmpeg.org/releases/可以找到各种版本的原码。
2、ffmpeg中默认是没有x264的,要单独下载,可以从x264的官网上下载,也可以ffmpeg提供的链接上下载,同时https://trac.ffmpeg.org/有讲,ffmpeg和x264的编译的介绍,还有别的库,方法相同的,https://trac.ffmpeg.org/wiki/CompilationGuide/Quick/libx264
http://www.videolan.org/developers/x264.html可下载x264
3、编译x264
./configure --prefix=dstdir --enable-static --enable-shared
make clean && make && make install
4、由于公司的264编码有命名空间,直接放到ffmpeg中是不行的,写了一个包装库,也叫libx264.so
5、编译前,ffmpeg的configure会检查libx264.so的,如果没有会出错,所以运行configure时,x264的路径要能找到真正的libx264.so,
运行make && make install时,在将包装库替换正式的libx264.so
6、由于公司的编码库与正式的x264的函数名是相同的,不要将2个库同时链接。
7、编译ffmpeg前,修改ffmpeg的libavcodec/libx264.c,修改相应的X264_init,X264_frame,X264_close函数。
同时libx264中不能用bool, false,true,要用int要代替。
8、编码后pkt的时间戳,使用frme中的时间戳。
9、本来X264_init是要将到avctx->extradata的,但用自己的库要到X264_frame才能根据编码的流得到,
这就有个问题,如果这个数据不写,用vlc是可以看到视频,但用fms自带的播放器和平台播放器看不到视频,用flv分析工具可以看到第一个视频tag的数据只有5个字节。
然后用gdb查得分析的32字节的内容,写下固定的内容,用rtmp可以看到视频,但用hls,只能看到一种分辨率下的视频。
10、延时写头
10.1、 修改avformat.h的AVOutputFormat结构体,增加变量int n_need_late_write_head;//是否加延时写头, 0:不要,1:要
10.2、修改avcodec.h的AVCodecContext结构体,增加变量int n_need_late_write_head;//是否加延时写头, 0:不要,1:要
10.3、在X264_init中设置avctx->n_need_late_write_head = 1;
10.4、在avformat_write_header中增加
s->oformat->n_need_late_write_head = 0;
for(int i = 0; i < s->nb_streams; i++)
{
if(s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
if(s->streams[i]->codec->n_need_late_write_head == 1)
{
s->oformat->n_need_late_write_head = 1;
break;
}
}
}
if( 0 == s->oformat->n_need_late_write_head)//增加的控制变量
{。。。。写头}
10.5、在X264_frame调用
if(1 == g_need_AnalysisH264header)
{
if(AnalysisH264header(ctx, NULL, NULL, 0, g_pBufferOut, noutsize) != 0)
{
return -1;
}
}
分析出一次头就不要再分析了,就是得到extradata数据
10.6、write_frame中增加以下部分,就是写文件时,先写头(本来是前面要写的)
if(s->oformat != 0 && s->oformat->write_header && s->oformat->n_need_late_write_head == 1)
{
//一定要分析出头部才写头
if(ost->enc_ctx->extradata_size <= 10)//暂时写为10吧,正常是32
{
return;
}
for(int i = 0; i < s->nb_streams; i++)
{
if(s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
//将ost->enc_ctx的extradata 拷贝到s对应的流下
s->streams[i]->codec->extradata = malloc(ost->enc_ctx->extradata_size);
if(s->streams[i]->codec->extradata == 0)
{
av_log(NULL, AV_LOG_ERROR, "Failed malloc extradata\n");
return;
}
s->streams[i]->codec->extradata_size = ost->enc_ctx->extradata_size;
memcpy(s->streams[i]->codec->extradata, ost->enc_ctx->extradata, ost->enc_ctx->extradata_size);
}
}
//copy from avformat_write_header
int retw = s->oformat->write_header(s);
if (retw >= 0 && s->pb && s->pb->error < 0)
retw = s->pb->error;
if (retw < 0)
return retw;
if (s->flush_packets && s->pb && s->pb->error >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS)
avio_flush(s->pb);
s->oformat->n_need_late_write_head = 0;//下一次就不要写了
}
注意分配得到的数据在ost->enc_ctx中,要复制到s的视频流的codec中