基于RTMP推送实时AAC+H264流(二)
来源:互联网 发布:linux下安装数据库 编辑:程序博客网 时间:2024/05/07 17:31
编码
图像采用H264编码,声音采用AAC编码,用的是x264和faac这两个库
x264
流程:配置编码器、获取元数据、编码
配置编码器:首先是使用预先设定好的配置ultrafast
和zerolatency
,这两个用来控制编码速度和质量,也就是代替我们修改了一些参数,baseline
同理,也是预配置,然后是设置高宽,帧率等参数,接下来是几个重要的点: i_keyint_max
,代表最大关键帧间隔,默认值是250,也就是如果帧率为25的话,每10秒发送一次关键帧,间隔太过长了,比如视频推流经过1秒后,我打开播放器从服务器获取媒体流,这时候由于没有关键帧,无法显示出画面,也就是还要等9秒才能看到画面,所以这里设置成了和fps一样,也就是最多一秒要发一次关键帧 i_rc_method
,代表码率控制方式,X264_RC_ABR
为平均码率 b_repeat_headers
,代表是否要在关键帧前加入sps和pps,这两个帧在解码的时候需要用到,所以一般要在每个关键帧前加入,防止半途播放的用户无法解码,然而这里关闭了这个选项,是由于返回的数据格式不方便封装成RTMP包,而且sps和pps一般是不变的,所以,采取的策略是推流前获取这两个帧,然后保存下封装好的RTMP包,在每次发送数据时判断如果需要发送的是关键帧就先发送sps和pps b_annexb
,代表是否用附录B的格式打包NAL单元,简单来说就是在NAL单元前加上00 00 00 01
或00 00 01
用以分割各个单元,相对应的是RTP格式,这种格式在NAL单元前加上四个表示单元长度的字节,考虑到RTMP的封装格式与RTP格式一样,所以这里关闭这个选项
x264_param_t param;x264_picture_t picture;x264_t *handle;x264_param_default_preset(¶m, "ultrafast", "zerolatency");x264_param_apply_profile(¶m, "baseline");param.i_log_level = X264_LOG_NONE;param.i_csp = X264_CSP_I420;param.i_width = width;param.i_height = height;param.i_fps_den = 1;param.i_fps_num = fps;param.i_keyint_max = fps;param.rc.i_rc_method = X264_RC_ABR;param.rc.i_bitrate = bitrate;param.b_repeat_headers = 0;param.b_annexb = 0;x264_picture_alloc(&picture, param.i_csp, param.i_width, param.i_height);handle = x264_encoder_open(¶m);
获取元数据:也就是之前提到的sps和pps,这里264_encoder_headers
返回的应该有三个NAL单元,除了sps和pps,还有一个sei,代表的是增强信息,好像是辅助解码的一些额外参数信息,没有也不影响正常解码,所以这里忽略了
x264_nal_t *nal;int temp, size; x264_encoder_headers(handle, &nal, &temp); size = nal[0].i_payload + nal[1].i_payload;process(size, nal->p_payload);
编码:把I420格式的亮度与色度分别拷贝到picture
内,这里i_pts
控制显示顺序,由于一帧会被分为多个slice
,所以x264_encoder_encode
生成一组x264_nal_t
,这里第三个参数代表的是生成的x264_nal_t
数量,也就是说nal[1]
代表第二个NAL单元,需要注意的是,这些x264_nal_t
的p_payload
成员内存上是连续的,且函数的返回值表示的是所有p_payload
的总字节数,这里我们用到的就是p_payload
与其总字节数
const int luma = height * width;const int chroma = luma / 4;int temp, size;x264_picture_t out;x264_nal_t *nal;memcpy(picture.img.plane[0], frame, luma);memcpy(picture.img.plane[1], frame + luma, chorma);memcpy(picture.img.plane[2], frame + luma + chorma, chorma);picture.i_pts = pts++;size = x264_encoder_encode(handle, &nal, &temp, &picture, &out);process(size, nal->p_payload);
faac
流程:配置编码器、获取元数据、编码
配置编码器:设置采样率和通道数,打开编码器,获取到最大可接受的样本数量和输出的字节大小,以此来决定缓冲区的大小,接下来是几个参数,有些参数不懂什么意思 inputFormat
,代表输入格式,这里选的是FAAC_INPUT_16BIT
,也就是一个样本的大小为16位,与之前采集时设置的相同 outputFormat
,代表输出格式,值为1表示adts格式 aacObjectType
,代表AAC规格,LOW
也就是Low Complexity,由于少了预测和增益控制,编码复杂度较低,因此编码效率更高 useLfe
,代表使用低频增强,具体作用不知道
unsigned long maxSample, bufLength;char *buf;encoder = faacEncOpen(sampleRate, channals, &maxSample, &bufLength);faacEncConfigurationPtr conf = faacEncGetCurrentConfiguration(encoder);conf->inputFormat = FAAC_INPUT_16BIT;conf->outputFormat = 1;conf->aacObjectType = LOW;conf->allowMidside = 0;conf->useLfe = 0;conf->bitRate = bitrate;conf->bandWidth = 0.5 * bitrate;faacEncSetConfiguration(encoder, conf); buf = new char[bufLength];
获取元数据:结果应该只有2个字节,不调用这个函数,自己按照规范构造也可以
unsigned char *buf;unsigned long size;faacEncGetDecoderSpecificInfo(encoder, &buf, &size);process(size, buf);
编码:需要注意,一开始编码的几个帧都不返回数据,所以封装的时候需要额外判断一下,还有就是这里的sample
如果和maxSample
不一致的话,编码出来的声音会很奇怪
int size = faacEncEncode(encoder, (int*)(data), sample, buf, bufLength);process(size, buf);
封装
所有的消息块都需要预留一个RTMP_MAX_HEADER_SIZE
的大小来存放块头,且元数据与普通数据需要分开处理,时间戳在发送时再设置
H264
元数据:封装时要求sps与pps的大小用2个字节来表示,而编码得到的是用4个字节来表示,所以需要拷贝时需要注意下偏移,这里包括后面的m_nChannel = 0x04
代表声音和视频通道
char *buf = new char[RTMP_MAX_HEADER_SIZE + 8 + length];char *body = buf + RTMP_MAX_HEADER_SIZE;RTMPPacket packet;packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;packet.m_packetType = RTMP_PACKET_TYPE_VIDEO;packet.m_nChannel = 0x04;packet.m_hasAbsTimestamp = 0;packet.m_nBodySize = 8 + length;packet.m_body = body;*(body++) = 0x17; // 1-keyframe, 7-AVC*(body++) = 0x00;*(body++) = 0x00;*(body++) = 0x00;*(body++) = 0x00;// AVCDecoderConfigurationRecord*(body++) = 0x01; // configurationVersion*(body++) = data[5]; // AVCProfileIndication*(body++) = data[6]; // profile_compatibility*(body++) = data[7]; // AVCLevelIndication*(body++) = 0xff; // 111111(reserved) + lengthSizeMinusOneint len = (data[2] << 8) | data[3];*(body++) = 0xe1; // 111(reserved) + numOfSequenceParameterSetsmemcpy(body, data + 2, len + 2);body += (len + 2);*(body++) = 0x01; // numOfPictureParameterSetsmemcpy(body, data + len + 6, length - len - 6);
普通数据:第一个字节根据是否为关键帧而取不同的值
char *buf = new char[RTMP_MAX_HEADER_SIZE + length + 5];char *body = buf + RTMP_MAX_HEADER_SIZE;RTMPPacket packet;packet.m_headerType = RTMP_PACKET_SIZE_LARGE;packet.m_packetType = RTMP_PACKET_TYPE_VIDEO;packet.m_nChannel = 0x04;packet.m_hasAbsTimestamp = 0;packet.m_nBodySize = length + 5;packet.m_body = body;*(body++) = (data[4] & 0x1f) == 0x05 ? 0x17 : 0x27;*(body++) = 0x01;*(body++) = 0x00;*(body++) = 0x00;*(body++) = 0x00;memcpy(body, data, length);
AAC
元数据:0xAF
代表AAC数据
char *buf = new char[RTMP_MAX_HEADER_SIZE + length + 2];char *body = buf + RTMP_MAX_HEADER_SIZE;RTMPPacket packet;packet.m_headerType = RTMP_PACKET_SIZE_LARGE;packet.m_packetType = RTMP_PACKET_TYPE_AUDIO;packet.m_nChannel = 0x04;packet.m_hasAbsTimestamp = 0;packet.m_nBodySize = length + 2;packet.m_body = body;*(body++) = 0xAF;*(body++) = 0x00;memcpy(body, data, length);
普通数据:AAC数据的前7个字节为帧分隔符,不需要封装在内
char *buf = new char[RTMP_MAX_HEADER_SIZE + length - 5];char *body = buf + RTMP_MAX_HEADER_SIZE;RTMPPacket packet;packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;packet.m_packetType = RTMP_PACKET_TYPE_AUDIO;packet.m_nChannel = 0x04;packet.m_hasAbsTimestamp = 0;packet.m_nBodySize = length - 5;packet.m_body = body;*(body++) = 0xAF;*(body++) = 0x01;memcpy(body, data + 7, length - 7);
- 基于RTMP推送实时AAC+H264流(二)
- 基于RTMP推送实时AAC+H264流(一)
- 基于RTMP推送实时AAC+H264流(三)
- 基于RTMP推送实时AAC+H264流(一)
- 基于RTMP推送实时AAC+H264流(三)
- rtmp推送h264 aac
- rtmp推送h264及aac
- h264视频流,aac音频流(g711a转码)推送至rtmp服务器
- rtmp 推送h264 + aac 的数据
- rtmp 推送h264 + aac 的数据
- rtmp 推送h264 + aac 的数据
- rtmp 推送h264 + aac 的数据
- rtmp 推送h264 + aac 的数据
- rtmp 推送h264 + aac 的数据
- H264,aac rtmp
- h264+aac=>rtmp
- 【基于libRTMP的流媒体直播之 AAC、H264 推送】
- 基于libRTMP的流媒体直播之 AAC、H264 推送
- Android基础知识梳理之序
- Spring源码分析----AOP概念(Advice,Pointcut,Advisor)和AOP的设计与实现
- 用phpcms如何将静态页面制作成企业网站(中)
- RCNN系列学习笔记(3):Fast R-CNN
- BZOJ 3757 苹果树
- 基于RTMP推送实时AAC+H264流(二)
- fghhfh
- 火柴棍等式
- hrbust 1304 13哥的机器人
- iOS轮播的封装(直接使用)
- Android 6.0 主要特性
- 树莓派学习--底层GPIO开发
- B1042. 字符统计(20)
- bzoj1019: [SHOI2008]汉诺塔