网站首页 > 编程文章 正文
前面完成了视频RTMP推流实践,本文介绍RTMP的音频推流,包括AAC,g711a,g711u三种场景音频推流。
基于前面的视频推流实践,我们新增了推流AAC,g711a,g711u的三个接口。分别为SendAAcData(),sendg711a_audio(),sendg711u_audio(),对外提供API调用。整体类定义如下:
class Wrapper_RtmpLib
{
public:
Wrapper_RtmpLib(char * url);
~Wrapper_RtmpLib();
int Open();
int SendVideoData(char * data,int dataLength, unsigned int timeStamp);
int SendAAcData(char * data,int dataLength, unsigned int timeStamp);
int sendg711a_audio(unsigned char *buf,int len,unsigned int timeStamp);
int sendg711u_audio(unsigned char *buf,int len,unsigned int timeStamp);
int IsConnect();
int Close();
private:
………由于篇幅所限,这里省略
};
1) AAC推流实践:
SendAAcData 接收上层传来的AAC帧数据,长度和时间戳。其中AAC帧数据包括ADTS头和RAW的AAC数据。该函数从上层接收一帧数据,将ADTS头组装一个packet 通过sendaac_headerconfig发送出去,然后再将raw的AAC数据打包成packet通过sendaac_rawaudio发送出去。
int Wrapper_RtmpLib::SendAAcData(char * data,int dataLength, unsigned int timeStamp)
{
int ret = -1;
uint8_t audioSpecificConfig[2] = { 0 };
AdtsKeyHeader tAdtsKeyHeader;
GetAdtsKeyConfig(data, &tAdtsKeyHeader);
GetAAcSpecificConfig(tAdtsKeyHeader.nProfile+1, tAdtsKeyHeader.nSfIndex, tAdtsKeyHeader.nChannelConfiguration, audioSpecificConfig);
printf("nProfile %d nSfIndex %d nChannelConfiguration %d \n", tAdtsKeyHeader.nProfile, tAdtsKeyHeader.nSfIndex, tAdtsKeyHeader.nChannelConfiguration);
ret = sendaac_headerconfig(audioSpecificConfig, 2, 0);
if(ret==-1)
{
printf("send key frame is failed\n");
}
else
{
printf("send key frame now len 7\n");
}
ret = sendaac_rawaudio((unsigned char *)data+7,dataLength-7,timeStamp);
if(ret==-1)
{
printf("send raw frame is failed\n");
}
else
{
printf("send raw frame now len %d \n", tAdtsKeyHeader.nAacFrameLength-7);
}
}
AAC 推流的Demo使用如下:
#include <cstdio>
#include"Wrapper_RtmpLib.hpp"
#include <unistd.h>
#include<string.h>
#include <signal.h>
#include<time.h>
#include <errno.h>
uint8_t tag0 = 0xff;
uint8_t tag1 = 0xf0;
void help(char *p){
printf("Use:");
printf("%s AudioFile RTMP_URL \n",p);
}
int main(int argc,char *argv[]){
if(argc<3)
{
help(argv[0]);
return 1;
}
uint8_t FrameBuffer[4096];
signal(SIGPIPE, SIG_IGN);
Wrapper_RtmpLib test(argv[2]);
if (test.Open() < 0)
{
printf("open is failed\n");
return 0;
}
if (test.IsConnect() < 0)
{
printf("connect is failed\n");
return 0;
}
else
printf("connect is ok\n");
//uint8_t adtsheader[7];
FILE *fd = fopen(argv[1], "rb+");
if (fd == NULL)
{
printf("fopen is failed,err %d\n", errno);
return 0;
}
void *pStart = NULL;
void *pEnd = NULL;
int count = 0;
unsigned int timestamp = 0;
while (1)
{
int ret = fread(FrameBuffer, 7, 1, fd);
if (ret != 1)
{
printf("fread header is failed,err %d\n", errno);
return 0;
}
if ((FrameBuffer[0] == tag0) && (FrameBuffer[1] & 0xf0 == tag1))
// if ((FrameBuffer[0] == '\xFF') && (FrameBuffer[1] & 0xf0 == '\xF0'))
{
//分析头数据,读裸露数据
bs_s bitbuffer_Robj;
bs_init(&bitbuffer_Robj, FrameBuffer, 7);
bs_skip(&bitbuffer_Robj, 30);//调过30bit,找到len字段
int len = bs_read(&bitbuffer_Robj, 13);
printf("raw_len = %d\n", len);
int ret = fread(FrameBuffer+7, len-7, 1, fd);
if (ret != 1)
{
printf("fread raw block is failed,err %d\n", errno);
return 0;
}
test.SendAAcData((char *)FrameBuffer, len, timestamp);
timestamp += 22;
usleep(1000*22);
}
}
printf("data is endof now");
getchar();
}
这里需要说明的是:因为AAC每一帧是1024个采样,所以一帧的时间间隔是:1026/48Khz=21.33ms,故设置时间戳间隔为22ms。(注意我的测试程序AAC采用频率为48K hz)
2)g711推流实践
G711a和g711u推流接口为sendg711a_audio()和sendg711u_audio()。这个2个接口实现原理是一样。就是直接将raw数据推送出去即可。但毕竟是两个不同格式,rtmp的数据包的tag头不一样。后面我们会讲到rtmp的音频数据tag头定义。
G711a推流Demo使用如下:
#include <cstdio>
#include"Wrapper_RtmpLib.hpp"
#include <unistd.h>
#include<string.h>
#include <signal.h>
#include<time.h>
#include <errno.h>
uint8_t tag0 = 0xff;
uint8_t tag1 = 0xf0;
void help(char *p){
printf("Use:");
printf("%s AudioFile RTMP_URL \n",p);
}
int main(int argc,char *argv[]){
if(argc<3)
{
help(argv[0]);
return 1;
}
uint8_t FrameBuffer[4096];
signal(SIGPIPE, SIG_IGN);
Wrapper_RtmpLib test(argv[2]);
if (test.Open() < 0)
{
printf("open is failed\n");
return 0;
}
if (test.IsConnect() < 0)
{
printf("connect is failed\n");
return 0;
}
else
printf("connect is ok\n");
//uint8_t adtsheader[7];
FILE *fd = fopen(argv[1], "rb+");
if (fd == NULL)
{
printf("fopen is failed,err %d\n", errno);
return 0;
}
int count = 0;
unsigned int timestamp = 0;
while (1)
{
int ret = fread(FrameBuffer, 640, 1, fd);
if (ret != 1)
{
printf("fread header is failed,err %d\n", errno);
return 0;
}
test.sendg711a_audio((unsigned char *)FrameBuffer, 640, timestamp);
printf("send g711a now 80\n");
timestamp += 80;
usleep(1000*40);
}
printf("data is endof now");
getchar();
}
这里注意:因为测试g711a是大华IPC抓下来的包。大华IPC的g711是80ms打一包(RTP时间戳增量为640,真实时间间隔为640/8000=80ms),所以时间戳增量为80ms.但考虑网络延时和处理开销,这里每个包只延时了40ms就发送下一个包(这个根据实际情况决定)。因为80ms打一个包,包的大小恰好为640个字节(80ms*8000hz*2/2=640,g711压缩率为2),所以每次拷贝640个字节(即一个RTP包的音频负载)。由于g711a和g711u算法类似,推流方式也类似,g711u这里不再赘述。
3)RTMP的音频tag头
在RTMP发送音频数据包,包必须包括tag头+音频数据。类似flv的tag+data的数据格式。但由于flv和RTMP格式是兼容的,所以tag头=RTMP的body头。
具体定义如下:
总结起来如下:
body第一个字节:
bit7 bit6 bit5 bit4 |bit3 bit2 |bit1 | bit0
format rate bit-depth soudtype
body第二个字节
bit7~bit0
if format=10
0:AAC config 1:aac raw
根据以上规范
AAC :
body第一个字节a|11|1|1=0xAF
body第二个字节
AAC config =0
AAC rawdata = 1
实测AAC的 body第一个字节低4bit影响不大。解码器主要还是根据发送的config数据来解码的。
g711a
body第一个字节7|01|1|1 =0x77
//双通道,16bit采样精度,8K采样频率
g711u:
body 第一个字节 8|01|1|1 =0x87
//双通道,16bit采样精度,8K采样频率
更多更详细资源请关注公众号:AV_Chat
猜你喜欢
- 2024-10-21 Qt/C++音视频开发61-多屏渲染/解码渲染到多个窗口/画面实时同步
- 2024-10-21 Sip网络广播号角,sip广播系统公共广播系统有源喇叭
- 2024-10-21 Qt/C++音视频开发63-设置视频旋转角度/支持0-90-180-270度旋转
- 2024-10-21 基于GB28181协议,实现WVP平台语音广播
- 2024-10-21 ROIP 无线集群网关介绍(ros网关ip设置)
- 2024-10-21 FLV封装原理(flip封装)
- 2024-10-21 GOIP移动电话网关简介(移动网关管理网址)
- 2024-10-21 ICR-1000 录音录像服务器性能参数
- 2024-10-21 Qt/C++检查是否含有B帧/转码推流/拉流显示/监控拉流推流
- 2024-10-21 Qt编写4K/8K大分辨率播放器(8K占用1%CPU)
你 发表评论:
欢迎- 最近发表
-
- 数据不丢失 从Windows 11的DEV版降级到正式版
- Win11学院:在Windows11 25905预览版中如何启用Dev Drive
- DEVC++的卸载(devcon卸载驱动)
- win11 dev 开发版 升级攻略完整版
- 最新Windows11+Windows10系统各种版本永久激活密钥以及下载链接
- 想学Python,却还记不住语法?神仙书籍 python背记手册双手奉上
- 如何用Python语言开发大型服务器程序
- 30天Python 入门到精通(python零基础入门到精通)
- 入门扫盲:9本自学Python PDF书籍,让你避免踩坑,轻松变大神!
- 学好Python需要看的4本书推荐(学python好用的书)
- 标签列表
-
- spire.doc (59)
- system.data.oracleclient (61)
- 按键小精灵源码提取 (66)
- pyqt5designer教程 (65)
- 联想刷bios工具 (66)
- c#源码 (64)
- graphics.h头文件 (62)
- mysqldump下载 (66)
- sqljdbc4.jar下载 (56)
- libmp3lame (60)
- maven3.3.9 (63)
- 二调符号库 (57)
- 苹果ios字体下载 (56)
- git.exe下载 (68)
- diskgenius_winpe (72)
- pythoncrc16 (57)
- solidworks宏文件下载 (59)
- qt帮助文档中文版 (73)
- satacontroller (66)
- hgcad (64)
- bootimg.exe (69)
- android-gif-drawable (62)
- axure9元件库免费下载 (57)
- libmysqlclient.so.18 (58)
- springbootdemo (64)
本文暂时没有评论,来添加一个吧(●'◡'●)