本文主要分析mp4封装的h265/h264,copy转hls,红色为自己添加的注释。
动态添加此filter
for (i = 0; i < nb_output_streams; i++) {
ost = output_streams[i];
if(ost->st->codec->codec_type == AVMEDIA_TYPE_VIDEO && ost->st->codec->codec_id == AV_CODEC_ID_H264)
{
AVBitStreamFilterContext * avFilter = av_bitstream_filter_init("h264_mp4toannexb");
ost->bitstream_filters = avFilter;
}else if(ost->st->codec->codec_type == AVMEDIA_TYPE_VIDEO && ost->st->codec->codec_id == AV_CODEC_ID_HEVC)
{
AVBitStreamFilterContext * avFilter = av_bitstream_filter_init("hevc_mp4toannexb");
ost->bitstream_filters = avFilter;
}
}
hevc->hls
如果不加 -bsf:v hevc_mp4toannex,就会报如下错误:
原因如下:
check_hevc_startcode会发现没有00000001字段。
相关代码有:hevc_mp4toannexb_bsf.c
前缀(vps,sps,pps)例子如下:
typedef structHEVCBSFContext {
uint8_t length_size;
int extradata_parsed;
}HEVCBSFContext;
static inthevc_extradata_to_annexb(AVBSFContext *ctx)
{
GetByteContext gb;
int length_size, num_arrays, i, j;
int ret = 0;
uint8_t *new_extradata = NULL;
size_t new_extradata_size = 0;
bytestream2_init(&gb,ctx->par_in->extradata, ctx->par_in->extradata_size);
bytestream2_skip(&gb, 21);
//跳过21个字节
length_size =(bytestream2_get_byte(&gb) & 3) + 1;
//第22个字节(0x03)后两位加上1。
num_arrays = bytestream2_get_byte(&gb);
//第23个字节(0x03)
for (i = 0; i < num_arrays; i++) {
int type =bytestream2_get_byte(&gb) & 0x3f;
//对应0x20,0x21,0x22
int cnt = bytestream2_get_be16(&gb);
//对应0x00 0x01, 0x00 0x01, 0x00 0x01,
if (!(type == NAL_VPS || type ==NAL_SPS || type == NAL_PPS ||
type == NAL_SEI_PREFIX || type ==NAL_SEI_SUFFIX || type == 0)) {
av_log(ctx, AV_LOG_ERROR,"Invalid NAL unit type in extradata: %d\n",
type);
ret = AVERROR_INVALIDDATA;
goto fail;
}
for (j = 0; j < cnt; j++) {
int nalu_len =bytestream2_get_be16(&gb);
//对应0x00 0x17(0x40~0x59),0x00 0x27(0x42~0x40),0x00 0x07(0x44~0x20)
if (4 +AV_INPUT_BUFFER_PADDING_SIZE + nalu_len > SIZE_MAX - new_extradata_size) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
ret =av_reallocp(&new_extradata, new_extradata_size + nalu_len + 4 +AV_INPUT_BUFFER_PADDING_SIZE);
if (ret < 0)
goto fail;
AV_WB32(new_extradata +new_extradata_size, 1);
// addthe startcode,也就是00 00 00 01
bytestream2_get_buffer(&gb,new_extradata + new_extradata_size + 4, nalu_len);
new_extradata_size += 4 + nalu_len;
memset(new_extradata + new_extradata_size,0, AV_INPUT_BUFFER_PADDING_SIZE);
}
}
av_freep(&ctx->par_out->extradata);
ctx->par_out->extradata = new_extradata;
ctx->par_out->extradata_size =new_extradata_size;
//得到新的字符串,长度。
if (!new_extradata_size)
av_log(ctx, AV_LOG_WARNING, "Noparameter sets in the extradata\n");
return length_size;
fail:
av_freep(&new_extradata);
return ret;
}
static inthevc_mp4toannexb_init(AVBSFContext *ctx)
{
...
}
//对于每一帧,都会进入hevc_mp4toannexb_filter函数,如下:
static inthevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
{
HEVCBSFContext *s = ctx->priv_data;
AVPacket *in;
GetByteContext gb;
int got_irap = 0;
int i, ret = 0;
ret = ff_bsf_get_packet(ctx, &in);
if (ret < 0)
return ret;
if (!s->extradata_parsed) {
av_packet_move_ref(out, in);
av_packet_free(&in);
return 0;
}
bytestream2_init(&gb, in->data,in->size);
while (bytestream2_get_bytes_left(&gb)){
uint32_t nalu_size = 0;
int nalu_type;
int is_irap, add_extradata, extra_size,prev_size;
for (i = 0; i < s->length_size;i++)
nalu_size = (nalu_size << 8)| bytestream2_get_byte(&gb);
//前4位表示数据大小。
nalu_type =(bytestream2_peek_byte(&gb) >> 1) & 0x3f;
//对应0x26
/* prepend extradata to IRAP frames */
is_irap = nalu_type >= 16 &&nalu_type <= 23;
//帧类型在16到23之间,都会被强插入vps等前缀。
add_extradata = is_irap &&!got_irap;
extra_size = add_extradata *ctx->par_out->extradata_size;
got_irap |= is_irap;
if (SIZE_MAX - nalu_size < 4 ||
SIZE_MAX - 4 - nalu_size ret = AVERROR_INVALIDDATA; goto fail; } prev_size = out->size; ret = av_grow_packet(out, 4 + nalu_size+ extra_size); if (ret < 0) goto fail; if (add_extradata) memcpy(out->data + prev_size,ctx->par_out->extradata, extra_size); //插入vps等前缀。 AV_WB32(out->data + prev_size +extra_size, 1); // add thestartcode,也就是00 00 00 01 bytestream2_get_buffer(&gb,out->data + prev_size + 4 + extra_size, nalu_size); //拷贝帧数据。 } ret = av_packet_copy_props(out, in); if (ret < 0) goto fail; fail: if (ret < 0) av_packet_unref(out); av_packet_free(&in); return ret; } ... h264->hls 解析mp4的块,得到sps,pps总数据,在h264_mp4toannexb_filter中去除头,得到sps,pps(只做一次),然后在每个关键帧前插sps,pps数据。