首页 > 代码库 > 将OBS录制数据通过RTMP协议引入到自己的程序中

将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

本文出自 “大师兄” 博客,请务必保留此出处http://9662841.blog.51cto.com/9652841/1583498

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