将OBS录制数据通过RTMP协议引入到自己的程序中

最近在window是平台下,做了一功能实现通过OBS采集音视频,并通过RTMP协议将其编码压缩后的数据接入到自己的程序中来,因OBS软件自带有很强大的游戏录制和桌面录制的功能,以及输入、输出音频设备数据的采集并混音的功能,目前斗鱼游戏直播也是使用的此软件作为录制工具。

OBS软件由于使用了window sdk进行音频的采集,所以只支持window vista版本以上平台,故XP系统用户是使用不了此软件的,下载地址:https://obsproject.com/download

OBS目前只支持RTMP协议进行广播,所以需要在自己的程序中搭建一RTMP server,让OBS接进来,然后通过研读OBS源码,对接收到的RTMP音视频数据进行反封装,最后组装成原始的音视频裸码流,如H.264、MP3、AAC。

下面详细介绍下主要流程:

1、如何搭建rtmp server

首先用户可以下载我上传的librtmp库http://down.51cto.com/data/1904438

2、接收到视频的处理

由于OBS在编码后在第一帧会把pps、sps、spi等解码关键信息协议开始后单独发送一次,所以在接收端必须保存该数据内容,并加在以后的每一个关键帧头部,否则不能正常的解码。以后每接受到一个关键帧(通过判断其第一个字节是否是0x17),在其头部加上该信息,然后在所有数据帧前面加上3个字节0x00 0x00 0x01 ,大致流程如上,具体请参考代码。

3、接收到音频的处理

目前OBS支持MP3和AAC两种音频编码格式,针对MP3编码,接收端只需要将数据包从第二个字节开始全部存储即可通过MP3解码器进行正常的解码了。针对AAC编码,目前还没有处理。

相关代码如下,已经去掉了一些敏感代码,仅供参考。

//头文件
#include <rtmp/srs_protocol_rtmp.hpp>
#include <rtmp/srs_protocol_rtmp_stack.hpp>
#include <libs/srs_librtmp.hpp>
#include <process.h>

#define InitSockets()	{	WORD version;				WSADATA wsaData;				version = MAKEWORD(1,1);		WSAStartup(version, &wsaData);	}

#define	CleanupSockets()	WSACleanup()

class RtmpServer : public CThread
{
public:
	RtmpServer( );
	~RtmpServer();

	int StartRtmpServer( );
	void StopRtmpServer( );
	int fmle_publish(SrsRtmpServer* rtmp, SrsRequest* req);

	virtual int32_t terminate();

	virtual void execute ( );

private:
	RtmpCaptureDataCallback* callback_;
	short			m_port;
	SimpleSocketStream *	m_pSocket;

};
定义文件
#include "rtmpsrv.h"
#include "rtmp_input_device.h"

RtmpServer::RtmpServer( )
{
	callback_	= NULL;
	m_port		= 1935;
	m_pSocket	= NULL;

}

RtmpServer::~RtmpServer()
{
	return;
}

int RtmpServer::fmle_publish(SrsRtmpServer* rtmp, SrsRequest* req )
{
	if (NULL == rtmp || NULL == req )
	{
		return -1;
	}
	int ret = ERROR_SUCCESS;

	bool hasHead = false, bVFirst = true, bAFirst = true;
	char head[128] = {0};
	char commonHead[4] = {0x00, 0x00, 0x00, 0x01};
	string video_data, audio_data;
	int headLen = 0;
	uint64_t audio_last_recv_time(0), video_last_recv_time(0);

	while ( false ==  get_terminated() )
	{
		Sleep(5);

		SrsMessage* msg = NULL;
		if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) 
		{
			break;
		}

		SrsAutoFree(SrsMessage, msg);

		if (msg->header.is_amf0_command() || msg->header.is_amf3_command()) 
		{
			SrsPacket* pkt = NULL;
			if ((ret = rtmp->decode_message(msg, &pkt)) != ERROR_SUCCESS) {
				break;
			}

			SrsAutoFree(SrsPacket, pkt);

			if (dynamic_cast<SrsFMLEStartPacket*>(pkt)) {
				SrsFMLEStartPacket* unpublish = dynamic_cast<SrsFMLEStartPacket*>(pkt);
				if ((ret = rtmp->fmle_unpublish(10, unpublish->transaction_id)) != ERROR_SUCCESS) {
					break;
				}
				break;
			}
			continue;
		}

		if (msg->header.is_audio()) 
		{
			audio_data.clear();
			if (bAFirst)
			{
				audio_data.append((char *)msg->payload + 1, msg->size - 1);
				bAFirst = false;
			}else
				audio_data.append((char *)msg->payload, msg->size -1);
		}else if (msg->header.is_video()) 
		{
			if (!hasHead)
			{
				memcpy(head, commonHead, 4);
				int8_t *skip = msg->payload;
				int8_t *skip2 = msg->payload;
				while(*(skip++) != 0x67);
				while(*(skip2++) != 0x68);
				int diff = skip2 - skip;
				if (diff <= 0)
				{
					continue;
				}

				memcpy(head + 4, skip - 1, diff - 4);	//copy sps
				memcpy(head + 4 + diff - 4, commonHead, 4);
				memcpy(head + 4 + diff - 4 + 4, skip2 - 1, 4); //copy pps
				hasHead = true;
				headLen = 4 + diff - 4 + 4 + 4;
			}else
			{
				video_data.clear();
				if (bVFirst)
				{
					video_last_recv_time = msg->header.timestamp;
					video_data.append(head, 128);
					video_data.append((const char *)msg->payload+9, msg->size - 9);
					bVFirst = false;
				} else
				{
					if (msg->header.timestamp > video_last_recv_time)
					{
						video_last_recv_time = msg->header.timestamp;
					}
					if (msg->payload[0] == 0x17)	//I frame
					{
						video_data.append(head, headLen);
					}
					video_data.append(commonHead + 1, 3);
					video_data.append((const char *)msg->payload+9, msg->size - 9);
				}
			}
		}
	}
	return ret;
}

void RtmpServer::execute( )
{
	if (NULL == m_pSocket)
	{
		return ;
	}
	m_pSocket->listen("0.0.0.0", m_port, 10);

	SimpleSocketStream* server = new SimpleSocketStream;
	while ( false ==  get_terminated() )
	{
		if (NULL == server)
		{
			SimpleSocketStream* server = new SimpleSocketStream;
		}
		if (NULL != m_pSocket && 0 == m_pSocket->accept(*server))
		{
			AUDIO_INFO("RtmpServer thread accept obs connected!");

			SrsRtmpServer* rtmp = new SrsRtmpServer(server);
			SrsRequest* req = new SrsRequest();

			int ret = ERROR_SUCCESS;

			if ((ret = rtmp->handshake()) != ERROR_SUCCESS) {
				break;
			}

			SrsMessage* msg = NULL;
			SrsConnectAppPacket* pkt = NULL;
			if ((ret = srs_rtmp_expect_message<SrsConnectAppPacket>(rtmp->get_protocol(), &msg, &pkt)) != ERROR_SUCCESS) {
				break;
			}
			if ((ret = rtmp->response_connect_app(req, 0)) != ERROR_SUCCESS) {
				break;
			}
			while ( false ==  get_terminated()  ) {
				SrsRtmpConnType type;
				if ((ret = rtmp->identify_client(10, type, req->stream, req->duration)) != ERROR_SUCCESS){
					terminate();
					// RtmpServer thread peer shutdown
					break;
				}
				assert(type == SrsRtmpConnFMLEPublish);
				req->strip();
				rtmp->start_fmle_publish(10);
				int ret = fmle_publish( rtmp, req );
				if ((ret != ERROR_CONTROL_REPUBLISH) && (ret != ERROR_CONTROL_RTMP_CLOSE)) //OBS主动停止串流
				{
					break;
				}
			}
			if (NULL != rtmp)
			{
				delete rtmp;
				rtmp = NULL;
			}
			if(NULL != req)
			{
				delete req;
				req = NULL;
			}
			if (NULL != server)
			{
				server->close_socket();
			}
		}else
		{
			Sleep(5);
		}
	} 
	if (NULL != server)
	{
		delete server;
		server = NULL;
	}
}

int RtmpServer::StartRtmpServer( )
{
	InitSockets();
	m_port = 1935;

	if (NULL != m_pSocket)
	{
		m_pSocket->close_socket();
		delete m_pSocket;
		m_pSocket = NULL;
	}
	m_pSocket = new SimpleSocketStream;
	m_pSocket->create_socket();

	return start();
}

void RtmpServer::StopRtmpServer( )
{
	terminate();

	if (NULL != m_pSocket)
	{
		m_pSocket->close_socket();
		delete m_pSocket;
		m_pSocket = NULL;
	}
	CleanupSockets();
};

int32_t RtmpServer::terminate()
{
	terminated_ = true;
	if(thr_handle_ != NULL)
	{
		if(WAIT_TIMEOUT == WaitForSingleObject(thr_handle_, 100))
		{
			TerminateThread(thr_handle_, -1);
		}
		thr_handle_ = NULL;
	}
	return 0;
}

#endif

最后在OBS广播设定 伺服器那里填入rtmp://ip:port

时间: 2024-12-04 17:51:09

将OBS录制数据通过RTMP协议引入到自己的程序中的相关文章

RTMP协议中文翻译(首发)

翻译:阿宝 更新:2016-09-11 来源:彩色世界(https://blog.hz601.org/2016/07/03/real-time-messaging-protocol/index.html) Adobe公司的实时消息传输协议 摘要 此备忘录描述了 Adobe公司的实时消息传输协议(RTMP),此协议从属于应用层,被设计用来在适合的传输协议(如TCP)上复用和打包多媒体传输流(如音频.视频和互动内容). 目录 1.简介 1.1.术语 2.贡献者 3.定义 4.字节序,对齐,和时间格式

(转)RTMP协议从入门到放弃

转载自:  http://blog.csdn.net/shangmingyang/article/details/50837852 RTMP协议是Real Time Message Protocol(实时信息传输协议)的缩写,它是由Adobe公司提出的一种应用层的协议,用来解决多媒体数据传输流的多路复用(Multiplexing)和分包(packetizing)的问题.随着VR技术的发展,视频直播等领域逐渐活跃起来,RTMP作为业内广泛使用的协议也重新被相关开发者重视起来.正好最近在从事这方面的

rtmplib rtmp协议过程分析

转自:http://chenzhenianqing.cn/articles/1009.html 写的很好,收藏如下,向作者致敬! 没事碰到了librtmp库,这个库是ffmpeg的依赖库,用来接收,发布RTMP协议格式的数据. 代码在这里:git clone git://git.ffmpeg.org/rtmpdump 先看一段通过librtmp.so库下载RTMP源发布的数据的例子,从rtmpdump中抽取出来.使用的大体流程如下: RTMP_Init主要就初始化了一下RTMP*rtmp变量的成

nginx搭建rtmp协议流媒体服务器总结

最近在 ubuntu12.04+wdlinux(centos)上搭建了一个rtmp服务器,感觉还挺麻烦的,所以记录下. 大部分都是参考网络上的资料. 前提: 在linux下某个目录中新建一个nginx目录. 然后进入该目录去下载搭建环境所需要的一些资源包. 此处在 /root/  目录下新建一个nginx目录即: /root/softsource/ 注意:依赖包和工具包需要下载,请在良好的网络环境下安装,否则在网速不好的情况下容易下漏掉,造成后面安装失败 ====================

调试libRTMP代码来分析RTMP协议

RTMP是Real Time Messaging Protocol(实时消息传输协议)的首字母缩写.该协议基于TCP,是一个协议族,常用在视频直播领域.RTMP协议的默认端口是1935. 学习一个协议最好的方法就是调试其通信过程,期间还可以使用wireshark抓包分析.本人在libRTMP的基础上添加了推流部分,并且使得整个过程变得可调试,学习其协议就变得简单多了.配置好的VS2010可调试的libRTMP工程:https://github.com/jiayayao/librtmp.该工程可以

HTTP协议/RTSP协议/RTMP协议的区别

RTSP. RTMP.HTTP的共同点.区别 共同点: 1:RTSP RTMP HTTP都是在应用应用层. 2: 理论上RTSP RTMPHTTP都可以做直播和点播,但一般做直播用RTSP RTMP,做点播用HTTP.做视频会议的时候原来用SIP协议,现在基本上被RTMP协议取代了. 区别: 1:HTTP: 即超文本传送协议(ftp即文件传输协议). HTTP:(Real Time Streaming Protocol),实时流传输协议. HTTP全称Routing Table Maintena

nginx搭建支持http和rtmp协议的流媒体服务器之一

实验目的: 让Nginx支持flv和mp4格式文件,支持RTMP协议的直播和点播: 同时打开RTMP的HLS功能 ?资料: HTTP Live Streaming(缩写是 HLS)是一个由苹果公司提出的基于HTTP的流媒体网络传输协议. HLS只请求基本的HTTP报文,与实时传输协议(RTP)不同,HLS可以穿过任何允许HTTP数据通过的防火墙或者代理服务器. 它也很容易使用内容分发网络来传输媒体流. 使用ffmpeg来完成对flv.mp4.mp3等格式的转化(点播实验暂时不测试) 一.准备工作

(转载)RTMP 协议学习总结

RTMP协议是一个互联网TCP/IP五层体系结构中应用层的协议.RTMP协议中基本的数据单元称为消息(Message).当RTMP协议在互联网中传输数据的时候,消息会被拆分成更小的单元,称为消息块(Chunk). 1 消息 消息是RTMP协议中基本的数据单元.不同种类的消息包含不同的Message Type ID,代表不同的功能.RTMP协议中一共规定了十多种消息类型,分别发挥着不同的作用.例如,Message Type ID在1-7的消息用于协议控制,这些消息一般是RTMP协议自身管理要使用的

C++实现RTMP协议发送H.264编码及AAC编码的音视频,摄像头直播

C++实现RTMP协议发送H.264编码及AAC编码的音视频 RTMP(Real Time Messaging Protocol)是专门用来传输音视频数据的流媒体协议,最初由Macromedia 公司创建,后来归Adobe公司所有,是一种私有协议,主要用来联系Flash Player和RtmpServer,如FMS, Red5, crtmpserver等.RTMP协议可用于实现直播.点播应用,通过FMLE(Flash Media Live Encoder)推送音视频数据至RtmpServer,可