將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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章