使用librtmp推h264、aac实时流
文章目录
- 前言
- 一、推视频流
- 1.sps、pps
- 2.视频帧
- 二、推音频流
- 1.音频帧
- 三、完整推流
- 1.实时流
- 总结
前言
librtmp可以用于推rtmp流,有时候我们需要将采集的摄像头或桌面的视频数据以及麦克风的音频数据推流出去,这时候就需要使用librtmp的推流功能了,其推流流程比较简单,只是一些细节需要注意即可。
一、推视频流
1.sps、pps
在推送idr前需要发送一个sps、pps数据包,代码如下:
/// <summary>
/// 推送sps、pps,在每个idr前需要推送这些数据
/// </summary>
/// <param name="rtmp">rtmp对象</param>
/// <param name="sps">sps数据</param>
/// <param name="spsLen">sps数据长度</param>
/// <param name="pps">pps数据</param>
/// <param name="ppsLen">pps数据长度</param>
void PushSPSPPS(RTMP* rtmp, const unsigned char* sps, int spsLen, const unsigned char* pps, int ppsLen) {int bodySize = spsLen + ppsLen + 16;RTMPPacket rtmpPacket;RTMPPacket_Alloc(&rtmpPacket, bodySize);RTMPPacket_Reset(&rtmpPacket);char* body = rtmpPacket.m_body;int i = 0;//frame type(4bit)和CodecId(4bit)合成一个字节(byte)//frame type 关键帧1 非关键帧2//CodecId 7表示avcbody[i++] = 0x17;//fixed 4bytebody[i++] = 0x00;body[i++] = 0x00;body[i++] = 0x00;body[i++] = 0x00;//configurationVersion: 版本 1bytebody[i++] = 0x01;//AVCProfileIndication:Profile 1byte sps[1]body[i++] = sps[1];//compatibility: 兼容性 1byte sps[2]body[i++] = sps[2];//AVCLevelIndication: ProfileLevel 1byte sps[3]body[i++] = sps[3];//lengthSizeMinusOne: 包长数据所使用的字节数 1bytebody[i++] = 0xff;//sps个数 1bytebody[i++] = 0xe1;//sps长度 2bytebody[i++] = (spsLen >> 8) & 0xff;body[i++] = spsLen & 0xff;//sps data 内容memcpy(&body[i], sps, spsLen);i += spsLen;//pps个数 1bytebody[i++] = 0x01;//pps长度 2bytebody[i++] = (ppsLen >> 8) & 0xff;body[i++] = ppsLen & 0xff;//pps data 内容memcpy(&body[i], pps, ppsLen);rtmpPacket.m_packetType = RTMP_PACKET_TYPE_VIDEO;rtmpPacket.m_nBodySize = bodySize;rtmpPacket.m_nTimeStamp = 0;rtmpPacket.m_hasAbsTimestamp = 1;rtmpPacket.m_nChannel = 0x04;//音频或者视频rtmpPacket.m_headerType = RTMP_PACKET_SIZE_MEDIUM;rtmpPacket.m_nInfoField2 = rtmp->m_stream_id;int nRet = RTMP_SendPacket(rtmp, &rtmpPacket, TRUE);RTMPPacket_Free(&rtmpPacket);
}
2.视频帧
推送视频需要区分是否为idr,然后需要设置时间戳,一般使用绝对时间戳较容易实现同步。
/// <summary>
/// 推h264数据
/// </summary>
/// <param name="rtmp">rtmp对象</param>
/// <param name="data">h264数据</param>
/// <param name="dataLen">h264数据长度</param>
/// <param name="isIdr">是否为gop首帧</param>
/// <param name="timestamp">时间戳,单位毫秒,绝对时间戳</param>
void PushH264Data(RTMP* rtmp, const unsigned char* data, int dataLen, bool isIdr, int timestamp) {int bodySize = dataLen + 9;RTMPPacket rtmpPacket;RTMPPacket_Alloc(&rtmpPacket, bodySize);RTMPPacket_Reset(&rtmpPacket);char* body = rtmpPacket.m_body;int i = 0;//frame type(4bit)和CodecId(4bit)合成一个字节(byte)//frame type 关键帧1 非关键帧2//CodecId 7表示avcif (isIdr) {body[i++] = 0x17;}else {body[i++] = 0x27;}//fixed 4byte 0x01表示NALU单元body[i++] = 0x01;body[i++] = 0x00;body[i++] = 0x00;body[i++] = 0x00;//dataLen 4bytebody[i++] = (dataLen >> 24) & 0xff;body[i++] = (dataLen >> 16) & 0xff;body[i++] = (dataLen >> 8) & 0xff;body[i++] = dataLen & 0xff;//datamemcpy(&body[i], data, dataLen);rtmpPacket.m_packetType = RTMP_PACKET_TYPE_VIDEO;rtmpPacket.m_nBodySize = bodySize;//持续播放时间rtmpPacket.m_nTimeStamp = timestamp;//使用绝对时间戳rtmpPacket.m_hasAbsTimestamp = 1;rtmpPacket.m_nChannel = 0x04;//音频或者视频rtmpPacket.m_headerType = RTMP_PACKET_SIZE_LARGE;rtmpPacket.m_nInfoField2 = rtmp->m_stream_id;int nRet = RTMP_SendPacket(rtmp, &rtmpPacket, TRUE);RTMPPacket_Free(&rtmpPacket);
}
二、推音频流
1.音频帧
推音频帧,需要设置采样率以及声道,时间戳采用绝对时间戳。
/// <summary>
/// 推流aac数据
/// </summary>
/// <param name="rtmp">rtmp对象</param>
/// <param name="samplerateIndex">采样率下标,0 = 5.5 kHz,1 = 11 kHz,2 = 22 kHz,3 = 44 kHz</param>
/// <param name="isStereo">是否立体声</param>
/// <param name="data">aac数据</param>
/// <param name="dataLen">aac数据长度</param>
/// <param name="timestamp">时间戳,单位毫秒,绝对时间戳</param>
void PushAacData(RTMP* rtmp, int samplerateIndex, bool isStereo, const unsigned char* data, int dataLen, int timestamp) {int bodySize = dataLen + 2;RTMPPacket rtmpPacket;RTMPPacket_Alloc(&rtmpPacket, bodySize);RTMPPacket_Reset(&rtmpPacket);char* body = rtmpPacket.m_body;//前四位表示音频数据格式 10(十进制)表示AAC,16进制就是A//第5-6位的数值表示采样率,0 = 5.5 kHz,1 = 11 kHz,2 = 22 kHz,3(11) = 44 kHz。//第7位表示采样精度,0 = 8bits,1 = 16bits。//第8位表示音频类型,0 = mono,1 = stereobody[0] = 0xA2 | ((samplerateIndex << 2) & 0x0c) | (isStereo & 0x1);//0x00 aac头信息, 0x01 aac 原始数据//这里都用0x01都可以body[1] = 0x01;//datamemcpy(&body[2], data, dataLen);rtmpPacket.m_packetType = RTMP_PACKET_TYPE_AUDIO;rtmpPacket.m_nBodySize = bodySize;//持续播放时间rtmpPacket.m_nTimeStamp = timestamp;//使用绝对时间戳rtmpPacket.m_hasAbsTimestamp = 1;rtmpPacket.m_nChannel = 0x04;//音频或者视频rtmpPacket.m_headerType = RTMP_PACKET_SIZE_LARGE;rtmpPacket.m_nInfoField2 = rtmp->m_stream_id;int nRet = RTMP_SendPacket(rtmp, &rtmpPacket, TRUE);RTMPPacket_Free(&rtmpPacket);
}
三、完整推流
1.实时流
同时推流时需要注意:视频音频都需要在同一个线程推流。推送视频dr帧之前先推送sps、pps。音频一般采用固定时间戳,通过下标计数计算会比较准确。其中NaluParse
对象参考《C++ 读取h264中的nalu》。
#ifdef _WIN32
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#endif // _WIN32
#include<exception>
#include "rtmp.h"
#include"NaluParse.h"
int main(int argc, char* argv[])
{#ifdef _WIN32WSADATA wsaData;int nRet;if ((nRet = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0) {return nRet;}
#endif // _Win32RTMP* rtmp = NULL;int64_t audioCount = 0;try {rtmp = RTMP_Alloc();RTMP_Init(rtmp);if (!RTMP_SetupURL(rtmp, (char*)"rtmp://127.0.0.1/live/123") ){throw std::exception("Setuping url failed!");}RTMP_EnableWrite(rtmp);if (!RTMP_Connect(rtmp, NULL)){throw std::exception("Connecting failed!");}if (!RTMP_ConnectStream(rtmp, 0)){throw std::exception("Connecting stream failed!");}while (1){//TODO:在实时流队列中取得h264和aac帧,videoFrame、audioFrame。略//解析h264获取nalu,这里示例的数据打包格式是Annex-B。auto nalus = AC::NaluParse::GetNalusFromFrame(videoFrame.Data, videoFrame.DataLength);AC::Nalu* sps = nullptr, * pps = nullptr;bool isIdr = true;//遍历h264帧内的nalufor (auto& i : nalus){switch (i.GetNaluType()){case 01:case 02:case 03:case 04:isIdr = false;case 05:{//推视频帧,视频使用采集时的时间戳,单位毫秒PushH264Data(rtmp, i.GetData(), i.GetDataLength(), isIdr, videoFrame.Timestamp);}break;case 7:{sps = &i;}break;case 8:{pps = &i;if (sps){//推sps和ppsPushSPSPPS(rtmp, sps->GetData(), sps->GetDataLength(), pps->GetData(), pps->GetDataLength());}}break;}}//推音频帧。这里音频是44100,双声道。采用固定间隔时间戳: audioCount++ * 1024 * 1000 / sampleRate。PushAacData(rtmp, 3, true, audioFrame.Data, audioFrame.DataLength, audioCount++ * 1024 * 1000 / 44100);}}catch (const std::exception& e) {printf("%s\n", e.what());}if (rtmp){RTMP_Close(rtmp);RTMP_Free(rtmp);}return 0;
}
总结
以上就是今天要讲的内容,使用librtmp推流其实是比较简单的,而且只需要初始化之后,直接推音视频数据即可,设置metada都不是必须的,唯一需要注意的就是在单线程中使用其方法。
使用librtmp推h264、aac实时流相关推荐
- NDK学习笔记:RtmpPusher之利用rtmpdump推h264/aac码流
NDK学习笔记:RtmpPusher之利用rtmpdump推h264/aac码流 本篇将是 RtmpPusher 的最后一篇.在之前的3篇文章里,我们已经把原生的视频YUV格式编码成h264,把音频的 ...
- hls直播实现源码V2(h264,aac流输入,ffmpeg实现,MFC,VC环境,ts,m3u8)
周星驰来也! 像很多80,90后年轻人(老男人吧)一样,周星驰一直就是哥喜爱的电影演员之一,不仅喜欢星爷的人物形象,更是喜欢这种乐观向上的精神!(哥16年的QQ一直就是周星驰这个昵称). 转入主题吧: ...
- 使用librtmp进行H264与AAC直播
http://my.oschina.net/jerikc/blog/501948#OSC_h3_13 使用librtmp进行H264与AAC直播 发表于5个月前(2015-09-06 23:19) ...
- rtsp实时流通过rtmp推送到服务端
rtsp实时流通过rtmp推送到服务端 很多朋友都会问到rtsp如何通过rtmp协议推送到服务端,正好前段时间开发了这个功能写在这里,和大家分享下. 首先我想说的是:ffmpeg可以实现这个功能.ff ...
- Nginx-rtmp 直播媒体实时流实现
0. 前言 这段时间在搭建一个IPCamera项目服务器.视频点对点通话,客户端会查看设备端的音视频实时流.为了省流量,是通过P2P进行穿透.但是由于NAT设备的原因和IPV4的枯竭.有些设备是无法进 ...
- 1小时学会:最简单的iOS直播推流(七)h264/aac 硬编码
最简单的iOS 推流代码,视频捕获,软编码(faac,x264),硬编码(aac,h264),美颜,flv编码,rtmp协议,陆续更新代码解析,你想学的知识这里都有,愿意懂直播技术的同学快来看!! 源 ...
- 人肉解析系列(一)————FLV-java你所关心的,我这里都有。附源码。非FFmpeg相关,纯java人肉解析。手写FLV。H264 AAC转FLV
第一次写博客,不知道有没有什么潜规则.总之呢,是好是坏都已经在落笔的那一刻开始了. 说起直播,各位都不陌生,毕竟国内这几年直播,短视频等视频行业大火,让所有程序员对直播都能如数家珍,随口便能讲出几种协 ...
- 1小时学会:最简单的iOS直播推流(八)h264/aac 软编码
最简单的iOS 推流代码,视频捕获,软编码(faac,x264),硬编码(aac,h264),美颜,flv编码,rtmp协议,陆续更新代码解析,你想学的知识这里都有,愿意懂直播技术的同学快来看!! 源 ...
- 基于Spark机器学习和实时流计算的智能推荐系统
概要: 随着电子商务的高速发展和普及应用,个性化推荐的推荐系统已成为一个重要研究领域. 个性化推荐算法是推荐系统中最核心的技术,在很大程度上决定了电子商务推荐系统性能的优劣,决定着是否能够推荐用户真正 ...
- power bi 实时_Power BI中的实时流
power bi 实时 The concept of the IOT (Internet of Things) is that every object that you might think of ...
最新文章
- 中国国际消费电子博览会拥抱转型,全新面貌拭目以待!
- linux 查看锁机时间,3分钟短文|Linux 登陆痕迹查看,last 锁定所有可疑对象
- 5G 信令流程 — 5GC 的业务请求(Service Request)
- finalshell Linux 传输文件 xftp
- Java 文件和byte数组转换
- 深度学习:Neural Network Layers Understanding
- 基于ROS的人脸识别
- Mybatis Plugin(拦截器)的开发
- cmake 生成vc 项目文件
- 苹果降价潮一波接一波 两款廉价iPad“箭在弦上”
- WinCE应用程序产生Data Abort 错误分析
- js判断变量类型是否为字符串,不符合条件则赋值为‘无’
- Leetcode 863.二叉树中所有距离为K的结点
- 机器学习初探(手写数字识别)matlab读取数据集
- 你想要的宏基因组-微生物组知识全在这(2021.3)
- ARCore之路-平面检测
- 立体匹配(Stereo Matching)
- 工业物联网创新方案亮相2018云栖大会
- sublime text 编译时提示[WinError 2] 系统找不到指定的文件。
- APP性能测试关注点详细介绍