前段时间在WINCE6搞好一阵时间利用ffmpeg分离mp4出来的视频帧送到硬件解码去,但发现不少问题,我用的是三星的S5PC100处理器,想利用三星提供的MFC的API来进行硬解码,但是根本无法解码,根据调试输出的信息,少了信息头数据,上网查找了好久后,发现mp4/mkv/mov/flv封装的h.264,因为为了减少存储,减少了一些头信息,不是标准的ES流,而从av_read_frame出来的码流一般都是完整的一帧数据的
经过研究的ffmpeg的原代码发现,一般视频帧的信息头数据都会保存在AVCodecContext结构中的extradata ,extradata_size的变量中, 如果是MPEG4编码的话,直接把extradata加在经过av_read_frame分离的视频数据帧前面就可以硬解码了,若是H264的话,我是运用如下函数得到标准ES流的.
#define EINVAL 22
#define ENOMEM 12
#define AV_RB16(x) ((((const uint8_t*)(x))[0] << 8) | ((const uint8_t*)(x))[1])
#define AV_RB32(x) ((((const uint8_t*)(x))[0] << 24) | /
(((const uint8_t*)(x))[1] << 16) | /
(((const uint8_t*)(x))[2] << 8) | /
((const uint8_t*)(x))[3])
#define AV_RB32(x) ((((const uint8_t*)(x))[0] << 24) | /
(((const uint8_t*)(x))[1] << 16) | /
(((const uint8_t*)(x))[2] << 8) | /
((const uint8_t*)(x))[3])
#define AV_WB32(p, d) do { /
((uint8_t*)(p))[3] = (d); /
((uint8_t*)(p))[2] = (d)>>8; /
((uint8_t*)(p))[1] = (d)>>16; /
((uint8_t*)(p))[0] = (d)>>24; } while(0)
typedef struct H264BSFContext {
uint8_t length_size;
uint8_t first_idr;
uint8_t *sps_pps_data;
uint32_t size;
} H264BSFContext;
static void alloc_and_copy(uint8_t **poutbuf, int *poutbuf_size,
const uint8_t *sps_pps, uint32_t sps_pps_size,
const uint8_t *in, uint32_t in_size)
{
uint32_t offset = *poutbuf_size;
uint8_t nal_header_size = 4;
*poutbuf_size += sps_pps_size+in_size+nal_header_size;
*poutbuf = (unsigned char *)av_realloc(*poutbuf, *poutbuf_size);
if (*poutbuf==NULL)
return ;
if (sps_pps)
memcpy(*poutbuf+offset, sps_pps, sps_pps_size);
memcpy(*poutbuf+sps_pps_size+nal_header_size+offset, in, in_size);
//if (!offset)
AV_WB32(*poutbuf+offset+sps_pps_size, 1);
//else
//{
// (*poutbuf+offset+sps_pps_size)[0] = (*poutbuf+offset+sps_pps_size)[1] = 0;
// (*poutbuf+offset+sps_pps_size)[2] = 1;
//}
}
static int h264_mp4toannexb_filter(H264BSFContext *ctx, AVCodecContext *avctx, const char *args,
uint8_t **poutbuf, int *poutbuf_size,
const uint8_t *buf, int buf_size,
int keyframe)
{
uint8_t unit_type;
uint32_t nal_size, cumul_size = 0;
/* nothing to filter */
if (!avctx->extradata || avctx->extradata_size < 6)
{
*poutbuf = (uint8_t*) buf;
*poutbuf_size = buf_size;
return 0;
}
/* retrieve sps and pps NAL units from extradata */
if (!ctx->sps_pps_data)
{
uint16_t unit_size;
uint32_t total_size = 0;
uint8_t *out = NULL, unit_nb, sps_done = 0;
const uint8_t *extradata = avctx->extradata+4;
static const uint8_t nalu_header[4] = {0, 0, 0, 1};
/* retrieve length coded size */
ctx->length_size = (*extradata++ & 0x3) + 1;
if (ctx->length_size == 3)
return AVERROR(EINVAL);
/* retrieve sps and pps unit(s) */
unit_nb = *extradata++ & 0x1f; /* number of sps unit(s) */
if (!unit_nb)
{
unit_nb = *extradata++; /* number of pps unit(s) */
sps_done++;
}
while (unit_nb--)
{
unit_size = AV_RB16(extradata);
total_size += unit_size+4;
if (extradata+2+unit_size > avctx->extradata+avctx->extradata_size)
{
av_free(out);
return AVERROR(EINVAL);
}
out = (unsigned char *)av_realloc(out, total_size);
if (!out)
return AVERROR(ENOMEM);
memcpy(out+total_size-unit_size-4, nalu_header, 4);
memcpy(out+total_size-unit_size, extradata+2, unit_size);
extradata += 2+unit_size;
if (!unit_nb && !sps_done++)
unit_nb = *extradata++; /* number of pps unit(s) */
}
ctx->sps_pps_data = out;
ctx->size = total_size;
ctx->first_idr = 1;
//char str[512]={0};
// memcpy(str, out, total_size);
}
*poutbuf_size = 0;
*poutbuf = NULL;
do
{
if (ctx->length_size == 1)
nal_size = buf[0];
else if (ctx->length_size == 2)
nal_size = AV_RB16(buf);
else
{
nal_size = AV_RB32(buf);
}
buf += ctx->length_size;
unit_type = *buf & 0x1f;
/* prepend only to the first type 5 NAL unit of an IDR picture */
if ( unit_type != 6 )
{
alloc_and_copy(poutbuf, poutbuf_size, ctx->sps_pps_data, ctx->size, buf, nal_size);
ctx->first_idr = 0;
}
//else
//{
// alloc_and_copy(poutbuf, poutbuf_size, NULL, 0, buf, nal_size);
// if (!ctx->first_idr && unit_type == 1)
// ctx->first_idr = 1;
//}
buf += nal_size;
cumul_size += nal_size + ctx->length_size;
} while (cumul_size < buf_size);
return 1;
}
通过调用 h264_mp4toannexb_filter函数后就可以得到标准的ES流了,先定义一个H264BSFContext变量,然后传给h264_mp4toannexb_filter函数,
如:
H264BSFContext *ctx=(H264BSFContext *) malloc(sizeof(H264BSFContext));
memset(ctx, 0, sizeof(H264BSFContext));
while(av_read_frame(pFormatCtx, &packet) >= 0)
{
if(packet.stream_index==videoStream)
{
unsigned char *lp=NULL; //输入的指针
int len;
h264_mp4toannexb_filter(ctx, pCodecCtx, NULL, &lp, &len, packet.data, packet.size, 1);
memcpy((LPBYTE)virInBuf, pCodecCtx->extradata, pCodecCtx->extradata_size);
}
}
硬件就可能解码了,这个我是在S5PC100测试成功的,如果是S3C6410应该是类似的,如果是avi文件很容易了,通过av_read_frame直接把它送硬件解码器进行解码就行了