一 为什么要提取h264帧?
因为我们经常需要从事实流中截取一些画面,用于变动的封面,安全,鉴黄等用处。
二 从nginx_rtmp中怎么提取一帧h264帧呢?
前面我们讲过如何提取sps和pps,这里只需要提取一个nalu,如果是I帧就手动把sps和pps添加到帧前面就可以了。
三 提取实现代码
可以参照前面sps和pps提取,
从ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)函数入手
(1)首先我们合并ngx_chain_t *in 数据。
ngx_str_t tmp_str = {0};
tmp_str.data = (u_char*)ngx_palloc(s->connection->pool,MAX_TAG_SIZE);
if(tmp_str.data == NULL)
{
return NGX_ERROR;
}
ngx_chain_t *tmp_in = NULL;
int in_len = 0;
int parse_offset = 0;
for(tmp_in = in;tmp_in;tmp_in = tmp_in->next)
{
memcpy(tmp_str.data + in_len,tmp_in->buf->pos,(int)(tmp_in->buf->last - tmp_in->buf->pos));
in_len += (int)(tmp_in->buf->last - tmp_in->buf->pos);
}
tmp_str.len = in_len;
这里内存申请,统一用nginx自带的内存池,这样方便管理,当连接结束的时候,nginx会自动释放这个内存池。
s->connection->pool。
但是由于我们这个一个tag占用内存太多,因此最好是用一次申请完就释放掉,或者最好直接保存到会话中,一次申请,永久使用。
(2) 接下看看合并后的数据格式,
17 01 00 00 50 00 00 8E 72 65 88 82
这是开头的几个字节的数据,从数据看就比较直观了。这个直接是rtmp video message的内容,去掉了messgae头的。也就是对应的flv文件的
video tag的body。17 1 表示keyframe,7表示AVC 。接下来的字节01是 AVCPacketType,然后是3个字节的CompositionTime,
然后是4个字节的nalu数据的长度。后面接着的是nalu数据体
(3) 实现代码
int nalu_type = tmp_str.data[9]&0x1f;
if(nalu_type != 5)
{
return NGX_ERROR;
}//先得到nalu type 这里我们只提取I帧,
parse_offset += 5;
int nalu_len = ntohl(*(unsigned int *)((char *)tmp_str.data + parse_offset));
//解析nalu数据长度
memcpy(nalu->data + nalu->len,s->mjpeg_info.sps.data,s->mjpeg_info.sps.len);
nalu->len += s->mjpeg_info.sps.len;//追加sps到帧前面
memcpy(nalu->data + nalu->len,s->mjpeg_info.pps.data,s->mjpeg_info.pps.len);
nalu->len += s->mjpeg_info.pps.len;//追加pps到帧前面
memcpy(nalu->data + nalu->len,nalu_head,4);
nalu->len += 4;//追加nalu 头到前面 0x00000001
memcpy(nalu->data + nalu->len,&tmp_str.data[9],tmp_str.len - 9);
nalu->len += (tmp_str.len - 9);//加上刚解析出来的nalu数据体
注意:sps和pps也都是一种nalu。
四 如何提取aac音频的这里就不详细描述了,与上面类似