FFmpeg从入门到精通:SEI那些事(2)
SEI例子
从video.js <https://github.com/videojs/video.js>的示例中下载oceans.mp4 <http://vjs.zencdn.net/v/oceans.mp4>并提取出H.264码流如下:
bitstream from oceans.mp4
NAL header
起始码(暗红底色)"0x00000001"分割出来的比特流即是NAL unit,起始码紧跟的第一个字节(墨绿底色)是NAL header。上图“NAL header”一共出现了四个数值:
·"0x06",此时NRI为"00B",NAL unit type为SEI类型。
·“0x67”,此时NRI为“11B”,NAL unit type为SPS类型。
·“0x68”,此时NRI为“11B”,NAL unit type为PPS类型。
·“0x65”,此时NRI为“11B”,NAL unit type为IDR图像。
SEI payload type
"0x06"后一个字节为“0x05”(淡黄底色)是SEI payload type,即表征SEI payload分析遵循user_data_unregistered()语法。
SEI payload size
“0x05”后一个字节为“0x2F”(淡蓝底色)是SEI payload size,此时整个payload是47个字节。
SEI payload uuid
"0x2F"随后的16个字节即为uuid,此时uuid为
dc45e9bd-e6d9-48b7-962c-d820d923eeef
SEI payload content
由于payload size是47个字节,除去16字节的uuid,剩下31个字节的content。由于content是字符串,所以有结束符"0x00",有效的30个字符内容是:
Zencoder Video Encoding System
rbsp trailing bits
47个payload字节后的"0x80"(灰底色)即是rbsp trailing bits,在user_data_unregistered()里面都是按字节写入的,所以此时的NAL unit结尾写入的字节一定是0x80。
SEI的生成
生成SEI的方式很多,大致可以有:
1.对已有码流做filter,插入SEI NAL
2.视频编码时生成SEI
3.容器层写入时插入SEI
以下代码示例来自于FFmpeg origin/master 分支。
bsf
BitStream Filter(码流过滤)的缩写为bsf,它的作用是,在不做码流解码的前提下,对已经编码后的比特流做特定的修改、调整。
bsf h264_metadata的调用
使用ffmpeg工具时,可以使用比特流过滤器。基本的filter调用格式如下:
ffmpeg -i INPUT -c:v copy -bsf:v filter1[=opt1=str1:opt2=str2][,filter2] OUTPUT
从上文提到的mp4文件中提取出h.264码流oceans.h264,可以使用 h264_metadata比特流过滤器添加SEI。下面示例命令添加了类型为未注册的用户数据的SEI,其中uuid为"086f3693-b7b3-4f2c-9653-21492feee5b8",payload内容为"hello":
./ffmpeg -I oceans.h264 -c:v copy -bsf:v h264_metadata=sei_user_data='086f3693-b7b3-4f2c-9653-21492feee5b8+hello' oceans.sei.h264
其中oceans.h264已经有一个SEI和28个SPS。输出的oceans.sei.h264码流中,共有28个SEI,其中第一个与输入保持一致,剩下27个为新插入的SEI。
bsf h264_metadata的代码分析
具体代码位于:libavcodec/h264_metadata_bsf.c中。
// 函数int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out)
if (ctx->sei_user_data && (has_sps || !ctx->sei_first_au)) {
H264RawSEI *sei;
H264RawSEIPayload *payload;
H264RawSEIUserDataUnregistered *udu;
int sei_pos, sei_new;
ctx->sei_first_au = 1;
for (i = 0; i < au->nb_units; i++) {
if (au->units[i].type == H264_NAL_SEI ||
au->units[i].type == H264_NAL_SLICE ||
au->units[i].type == H264_NAL_IDR_SLICE)
break;
}
sei_pos = i;
if (sei_pos < au->nb_units &&
au->units[sei_pos].type == H264_NAL_SEI) {
sei_new = 0;
sei = au->units[sei_pos].content;
} else {
sei_new = 1;
sei = &ctx->sei_nal;
memset(sei, 0, sizeof(*sei));
}
}
以上代码是h264_metadata添加SEI的判断逻辑,当指定了sei_user_data时,满足以下条件之一即可以处理:
·读取的access units是第一个au;
·当前au包含sps;
满足插入SEI逻辑后,具体处理过程中:
·如果发现第一个NAL已经是SEI,则该au不做插入SEI处理;
·如果au包含了IDR帧或者非IDR未分区的帧,则在其前面插入SEI信息。
基于以上代码,oceans.sei.h264码流中新插入27个新的SEI 符合处理逻辑。
相关报道:
- 金山云披露2017年财报数据 AI企业级商用带动高速增长2018-03-21
- FFmpeg代码导读系列:HEVC在RTMP中的扩展2018-02-01
- 金山云宣布融2亿美元 D轮资金累计达7.2亿美元2018-01-29
- 金山云全链路HTTPS安全方案开启用户体验新篇章2018-01-24
- 雷军旗下金山云融资3亿美元 全面降价布局多垂直领域2017-12-12
联系电话:13811959286
IT频道纠错邮箱:erjuner@163.com
责任编辑:新闻中心
本类最新
要闻推荐
今日视点
热点专题
新闻图片
- 新闻排行
- 评测排行