最近在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
#include
#include
#include
#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(pkt)) {
SrsFMLEStartPacket* unpublish = dynamic_cast(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(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