http://www.chinavideo.org/archiver/?tid-14008.html
关于实时编码解码问题 请教 分享一个编码代码 H264
首先分享一个编码代码使用的是X264 core 122版本 [code]
H264EncWrapper::~H264EncWrapper()
{
Destroy();
}
int H264EncWrapper::Initialize(int iWidth, int iHeight, int iRateBit, int iFps)
{
m_param.i_fps_num = iFps;
m_param.i_fps_den = 1;
m_param.rc.i_bitrate = iRateBit;
m_param.rc.i_vbv_max_bitrate=128;
m_param.i_threads = X264_THREADS_AUTO;
m_param.i_width = iWidth;
m_param.i_height = iHeight;
m_param.i_bframe=0;
m_param.i_frame_reference=3;
m_param.rc.i_rc_method=X264_RC_ABR;//X264_RC_CQP ;
m_param.i_csp = X264_CSP_I420;
m_param.b_visualize=1;
m_param.b_cabac =0;
m_param.b_interlaced=0;
m_param.i_level_idc=21;
m_param.i_keyint_max=m_param.i_fps_num*1.5;
m_param.i_keyint_min=1;
m_param.pf_log = NULL; //x264log;
m_param.p_log_private = NULL;
m_param.i_log_level=X264_LOG_NONE;
//m_param.i_keyint_max = 8;
//m_param.i_keyint_min = 4;
/* 根据输入参数param初始化总结构 x264_t *h */
if( ( m_h = x264_encoder_open( &m_param) ) == NULL )
{
fprintf( stderr, "x264 [error]: x264_encoder_open failed\n" );
return -1;
}
int i_nal;
if(x264_encoder_headers( m_h, &m_headers, &i_nal )<0)
{
fprintf( stderr, "x264 [error]: x264_encoder_headers failed\n" );
return -1;
}
x264_picture_alloc( &m_pic, X264_CSP_I420, m_param.i_width, m_param.i_height );
m_pic.i_type = X264_TYPE_AUTO;
m_pic.i_qpplus1 = 0;
return 0;
}
int H264EncWrapper::Destroy()
{
x264_picture_clean( &m_pic );
x264_encoder_close(m_h);
return 0;
}
//编码
int H264EncWrapper::Encode(unsigned char* szYUVFrame, unsigned char * pOutputBuffer)
{
m_pic.img.i_plane = 3;
m_pic.img.plane[0] = szYUVFrame;
m_pic.img.plane[1] = szYUVFrame + m_param.i_width * m_param.i_height;
m_pic.img.plane[2] = (unsigned char *)m_pic.img.plane[1] + m_param.i_width * m_param.i_height / 4;
m_pic.img.plane[3] = 0;
m_pic.i_pts = (int64_t)m_iFrameNum * m_param.i_fps_den;
x264_picture_t pic_out;
x264_nal_t *nal;
int i_nal, i; // nal的个数
if( x264_encoder_encode( m_h, &nal, &i_nal, &m_pic, &pic_out ) < 0 )
{
fprintf( stderr, "x264 [error]: x264_encoder_encode failed\n" );
return -1;
}
if(i_nal==0)
{
//m_iFrameNum++;
return 0;
}
int result=0;
for( i = 0; i < i_nal; i++ )
{
memcpy(pOutputBuffer, nal[i].p_payload, nal[i].i_payload);
pOutputBuffer += nal[i].i_payload;
result += nal[i].i_payload;
}
m_iFrameNum++;
return result;
}
[/code]
编码出来可以用播放器播放 也可以用ffplay 播放 和 ffmpeg解码
请教一个问题 在我解码的时候 解码几针后出现 avcodec_decode_video2 函数崩溃了
[attach]1780[/attach]
而且出现了这个提示
[attach]1781[/attach]
但我通过软件查看数据流发现有PPS 和 SPS帧
[attach]1782[/attach]
我用的是最新的ffmpeg库
附上解码部分代码
主函数中:
[code] avcodec_register_all();
av_register_all();
m_pCodec=avcodec_find_decoder(CODEC_ID_H264);
if(m_pCodec==NULL)
{
printf("Error avcodec_find_decoder");
return -1;
}
m_pCodecContext=avcodec_alloc_context();
if(m_pCodecContext==NULL)
{
printf("Error avcodec_alloc_context");
return -1;
}
m_pCodecContext->width = 320;
m_pCodecContext->height = 240;
//m_pCodecContext->pix_fmt = PIX_FMT_YUV420P;
//m_pCodecContext->codec_id = CODEC_ID_H264;
m_pCodecContext->pix_fmt = PIX_FMT_YUV420P;
int iRet = avcodec_open(m_pCodecContext,m_pCodec);
if(iRet<0)
{
printf("Error avcodec_open");
return -1;
}
m_pFrame=avcodec_alloc_frame();
if(m_pFrame==NULL)
{
printf("Error avcodec_alloc_frame");
return -1;
}
m_pFormatCtx=avformat_alloc_context();
if (!m_pFormatCtx)//分配内存失败
{
printf("avformat_alloc_context error\n");
return -1;
}
m_parser = av_parser_init(CODEC_ID_H264);
if(!m_parser)
return -1; [/code]
解码函数:
[code]VCodecContext* m_pCodecContext;
AVFrame * m_pFrame;
AVFormatContext *m_pFormatCtx;
AVCodecParserContext * m_parser;
static int DecodeFrame(uint8_t *data , int size)
{
int got_picture=0;
AVPacket avp;
av_init_packet(&avp);
int right=0;
avp.data=data;
avp.size=size;
int iRet = avcodec_decode_video2(m_pCodecContext,m_pFrame,&got_picture,&avp);
if(iRet>=0)
{
if(got_picture)
{
right++;
}
}
return got_picture;
}
static void DecodeVideo(uint8_t * pInBuffer, int size)
{
int pos=0;
int64_t pts=AV_NOPTS_VALUE;
int64_t dts=AV_NOPTS_VALUE;
do
{
uint8_t *pout;
int pout_len;
int len= av_parser_parse2(m_parser,m_pCodecContext,&pout,&pout_len,
pInBuffer+pos,size-pos,pts,dts,AV_NOPTS_VALUE);
pos +=len;
if(pout_len >0 )
{
DecodeFrame(pout,pout_len);
}
} while (pos<size);
if(size<=0)
{
while(DecodeFrame(NULL,0));
}
}
[/code]
希望大家能帮帮忙 , 任何回复都十分感谢 !
louyily 发表于 2012-3-22 02:24 PM
你的数据来源 能不能保证 传入 解码器的 是一个个完整的 nal 单元,我之前做实时编码的时候 发现传入数据如果不是完整帧就有可能出错的,判断一下 nal的头吧
lovesxw 发表于 2012-3-22 02:36 PM
av_parser_parse2 函数不是校验完整吗? 如果完整的话 就会有返回 不完整就会等待输入 ?
louyily 发表于 2012-3-22 03:13 PM
我之前一直没有用 这个parser,是自己对nal 头做下了判断 因为解的数据是udp发过来的,会出现一些错误的包数据
lovesxw 发表于 2012-3-22 04:00 PM
[i=s] 本帖最后由 lovesxw 于 2012-3-22 04:26 PM 编辑 [/i]
我加了一个判断这回 不过还是不对
[code]
/*the return value is the head offset*/
static int findheader(unsigned char* databuf, int len)
{
char aa[320*240*3]={0};
CopyMemory(aa,databuf,len);
int offset = 0;
bool bFind=false;
for(; offset< len -4; offset++,databuf++)
{
if(databuf[0] == 0x00 && databuf[1] == 0x00 && databuf[2] == 0x00 && databuf[3] == 0x01 && databuf[4] == 0x67)
{
bFind=true;
return offset;
}
if(databuf[0] == 0x00 && databuf[1] == 0x00 && databuf[2] == 0x00 && databuf[3] == 0x01 && databuf[4] == 0x65)
{
bFind=true;
return offset;
}
if(databuf[0] == 0x00 && databuf[1] == 0x00 && databuf[2] == 0x00 && databuf[3] == 0x01 && databuf[4] == 0x68)
{
bFind=true;
return offset;
}
}
if(!bFind)
{
return -1;
}
return offset;
}
static int DecodeFrame(uint8_t *data , int size)
{
//防止数据错误
int offset = findheader(data,size);
if(offset<0)
{
printf("Error data \n");
return 0;
}
int got_picture=0;
AVPacket avp;
av_init_packet(&avp);
int right=0;
avp.data=data+offset;
avp.size=size-offset;
int iRet = avcodec_decode_video2(m_pCodecContext,m_pFrame,&got_picture,&avp);
if(iRet>=0)
{
if(got_picture)
{
right++;
}
}
return got_picture;
}
static void DecodeVideo(uint8_t * pInBuffer, int size)
{
int pos=0;
int64_t pts=AV_NOPTS_VALUE;
int64_t dts=AV_NOPTS_VALUE;
do
{
uint8_t *pout;
int pout_len;
int len= av_parser_parse2(m_parser,m_pCodecContext,&pout,&pout_len,
pInBuffer+pos,size-pos,pts,dts,AV_NOPTS_VALUE);
pos +=len;
if(pout_len >0 )
{
DecodeFrame(pout,pout_len);
}
} while (pos<size);
if(size<=0)
{
while(DecodeFrame(NULL,0));
}
}[/code]
这种判断可以吧?
louyily 发表于 2012-3-22 05:21 PM
会不会是av_parser_parse2的用法问题
看了下 我的解码流程 就是比你多了个av_free_packet(&packet);
:(等晚上 让版主来看看吧
lovesxw 发表于 2012-3-22 05:29 PM
o(︶︿︶)o 唉 不知道版主能不能帮忙。。。 这个函数是释放 好像和功能无关 愁啊
lovesxw 发表于 2012-3-22 09:00 PM
自己解决了 希望大家不要走弯路 原因是我自己编译的ffmpeg有问题 可以直接下载官方提供的 。
最终代码
[code]static int DecodeFrame(uint8_t *data , int size)
{
int got_picture=0;
AVPacket avp;
av_init_packet(&avp);
avp.data=data;
avp.size=size;
int iRet = avcodec_decode_video2(m_pCodecContext,m_pFrame,&got_picture,&avp);
if(iRet>=0)
{
if(got_picture)
{
FILE * fp;
if((fp=fopen("d:/testout.yuv","ab"))==NULL)
{
printf("cant open the file");
exit(0);
}
for(int i=0; i<m_pCodecContext->height; i++)
fwrite(m_pFrame->data[0] + i * m_pFrame->linesize[0], 1, m_pCodecContext->width, fp);
for(int i=0; i<m_pCodecContext->height/2; i++)
fwrite(m_pFrame->data[1] + i * m_pFrame->linesize[1], 1, m_pCodecContext->width/2, fp);
for(int i=0; i<m_pCodecContext->height/2; i++)
fwrite(m_pFrame->data[2] + i * m_pFrame->linesize[2], 1, m_pCodecContext->width/2, fp);
fclose(fp);
}
}
return got_picture;
}
static void DecodeVideo(uint8_t * pInBuffer, int size)
{
int pos=0;
int64_t pts=AV_NOPTS_VALUE;
int64_t dts=AV_NOPTS_VALUE;
do
{
uint8_t *pout;
int pout_len;
int len= av_parser_parse2(m_parser,m_pCodecContext,&pout,&pout_len,
pInBuffer+pos,size-pos,pts,dts,AV_NOPTS_VALUE);
pos +=len;
if(pout_len >0 )
{
DecodeFrame(pout,pout_len);
}
} while (pos<size);
if(size<=0)
{
while(DecodeFrame(NULL,0));
}
}
[/code]
保存为yuv图像 不需要检测header 以为我那个已经保证是完整的包了
lovesxw 发表于 2012-3-22 09:01 PM
对了 忘了free 我就不补充了
niulei20012001 发表于 2012-3-24 03:03 PM
[i=s] 本帖最后由 niulei20012001 于 2012-3-24 03:07 PM 编辑 [/i]
兄弟,我来给你补充完整[code]AVFrame * m_pFrame;
//AVFormatContext *m_pFormatCtx;
AVCodecParserContext * m_parser;
AVCodec *m_pCodec;
AVCodecContext *m_pCodecContext;
//初始化
bool Decode_init(unsigned int width,unsigned int height)
{
m_pCodec=avcodec_find_decoder(CODEC_ID_H264);
if(m_pCodec==NULL)
{
printf("Error avcodec_find_decoder");
return 0;
}
m_pCodecContext=avcodec_alloc_context();
if(m_pCodecContext==NULL)
{
printf("Error avcodec_alloc_context");
return 0;
}
m_pCodecContext->width = width;
m_pCodecContext->height = height;
//m_pCodecContext->codec_id = CODEC_ID_H264;
m_pCodecContext->pix_fmt = PIX_FMT_YUV420P;
int iRet = avcodec_open(m_pCodecContext,m_pCodec);
if(iRet<0)
{
printf("Error avcodec_open");
return 0;
}
m_pFrame=avcodec_alloc_frame();
if(m_pFrame==NULL)
{
printf("Error avcodec_alloc_frame");
return 0;
}
//下面的内容,示例中没有
//m_pFormatCtx=avformat_alloc_context();
//if (!m_pFormatCtx)//分配内存失败
//{
// printf("avformat_alloc_context error\n");
// return 0;
//}
m_parser = av_parser_init(CODEC_ID_H264);
if(!m_parser)
return 0;
return 1;
}
//释放
bool Decode_uninit()
{
av_free(m_pFrame);
avcodec_close(m_pCodecContext);
av_free(m_pCodecContext);
return 1;
}
static int DecodeFrame(uint8_t *data , int size,unsigned char *yuvOutBuffer)
{
int got_picture=0;
AVPacket avp;
av_init_packet(&avp);
avp.data=data;
avp.size=size;
int iRet = avcodec_decode_video2(m_pCodecContext,m_pFrame,&got_picture,&avp);
if(iRet>=0)
{
if(got_picture)
{
for(int i=0,nDataLen=0;i<3;i++)
{
int nShift=(i==0)?0:1;
PBYTE pYUVData=(PBYTE)m_pFrame->data[i];
for(int j=0;j<(m_pCodecContext->height>>nShift);j++)
{
memcpy(&yuvOutBuffer[nDataLen],pYUVData,(m_pCodecContext->width>>nShift));
pYUVData+=m_pFrame->linesize[i];
nDataLen+=(m_pCodecContext->width>>nShift);
}
}
/*FILE * fp;
if((fp=fopen("d:/testout.yuv","ab"))==NULL)
{
printf("cant open the file");
exit(0);
}
for(int i=0; i<m_pCodecContext->height; i++)
fwrite(m_pFrame->data[0] + i * m_pFrame->linesize[0], 1, m_pCodecContext->width, fp);
for(int i=0; i<m_pCodecContext->height/2; i++)
fwrite(m_pFrame->data[1] + i * m_pFrame->linesize[1], 1, m_pCodecContext->width/2, fp);
for(int i=0; i<m_pCodecContext->height/2; i++)
fwrite(m_pFrame->data[2] + i * m_pFrame->linesize[2], 1, m_pCodecContext->width/2, fp);
fclose(fp);*/
}
}
return got_picture;
}
//调用入口函数
void DecodeVideo(uint8_t * pInBuffer, int size,unsigned char *yuvOutBuffer)
{
int pos=0;
int64_t pts=AV_NOPTS_VALUE;
int64_t dts=AV_NOPTS_VALUE;
do
{
uint8_t *pout;
int pout_len;
int len= av_parser_parse2(m_parser,m_pCodecContext,&pout,&pout_len, pInBuffer+pos,size-pos,pts,dts,AV_NOPTS_VALUE);
pos +=len;
if(pout_len >0 )
{
DecodeFrame(pout,pout_len,yuvOutBuffer);
}
} while (pos<size);
if(size<=0)
{
while(DecodeFrame(NULL,0,yuvOutBuffer));
}
}[/code]
qibo15193 发表于 2012-4-21 04:07 PM
这个代码没问题么?
cbzhaojay 发表于 2012-5-26 03:26 PM
[b]回复 [url=http://bbs.chinavideo.org/redirect.php?goto=findpost&pid=55342&ptid=14008]10#[/url] [i]niulei20012001[/i] [/b]
你好呀,兄弟, DecodeVideo你这个函数入口 传入的 pInBuffer是一个frame数据还是?
我传入了数据格式如下:
h264head sps h264head pps h264head +后面为一帧的数据
解码不成功也 兄弟能帮忙解释下么?