使用live555制作rtsp客户端,捕获h264等解码
Posted qianbo_insist
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用live555制作rtsp客户端,捕获h264等解码相关的知识,希望对你有一定的参考价值。
1、live555
live555是一个优秀的rtsp lib,坚持了很多年,细看代码,是精炼的,值得研读。
2、rtsp
rtsp协议包含了rtp,rtcp,sdp等协议,需要把这几个协议都有一定的了解,才能更好地理解代码。
3、show me the code
以下是客户端代码,不作过多解释,值得注意的地方:
1 需要设定接收缓冲区大小,对于高分辨率的视频来说,系统socket缓冲区需要设大一些
2 需要知道服务端支持tcp方式还是udp方式
3 断线需要重连
4 实际上,live555帮我们做了保活连接
/* ---------------------------------------------------------------------------
**
2021-02-13
email 418511899@qq.com
**
** -------------------------------------------------------------------------*/
#pragma once
#include "BasicUsageEnvironment.hh"
#include "liveMedia.hh"
#include <string>
#include <iostream>
#ifdef WIN32
#pragma warning (disable: 4512)
#pragma warning (disable: 4100)
#pragma warning (disable: 4091)
#include <stdint.h>
typedef int ssize_t;
#endif
#define RTSP_CALLBACK(uri, resultCode, resultString) \\
static void continueAfter ## uri(RTSPClient* rtspClient, int resultCode, char* resultString) static_cast<c_rtsp::RTSPClientConnection*>(rtspClient)->continueAfter ## uri(resultCode, resultString); \\
void continueAfter ## uri (int resultCode, char* resultString); \\
/**/
#define TASK_CALLBACK(class,task) \\
TaskToken m_ ## task ## Task; \\
static void Task ## task(void* rtspClient) static_cast<class*>(rtspClient)->Task ## task(); \\
void Task ## task (); \\
/**/
#if LIVEMEDIA_LIBRARY_VERSION_INT > 1371168000
#define RTSPClientConstrutor(env, url, verbosity, appname, httpTunnelPort) RTSPClient(env, url, verbosity, appname, httpTunnelPort ,-1)
#else
#define RTSPClientConstrutor(env, url, verbosity, appname, httpTunnelPort) RTSPClient(env, url, verbosity, appname, httpTunnelPort)
#endif
class Environment : public BasicUsageEnvironment
public:
Environment(char * stop) : BasicUsageEnvironment(*BasicTaskScheduler::createNew()), m_stop(stop)
m_stop = stop;
~Environment()
TaskScheduler* scheduler = &this->taskScheduler();
delete scheduler;
void mainloop()
this->taskScheduler().doEventLoop(m_stop);
void stop()
*m_stop = 1;
protected:
char* m_stop;
;
typedef void (*callback_onData)(void * puser, uint8_t*, ssize_t len);
class c_rtsp
public:
class Callback
public:
virtual bool onNewSession(const char* id, const char* media,
const char* codec, const char* sdp) return true;
virtual bool onData(const char* id, unsigned char* buffer,
ssize_t size, struct timeval presentationTime)
if (v_callback != NULL)
//回调函数,传回名称和数据,数据大小+头部的大小
v_callback(v_user, buffer, size);
return true;
return false;
virtual ssize_t onNewBuffer(unsigned char* buffer, ssize_t size)
return 0;
virtual void onError(c_rtsp&, const char*message)
std::cout << v_name << ":Error:" << message << std::endl;
virtual void onConnectionTimeout(c_rtsp& connection)
std::cout << v_name << ":Connection timeout -> retry" << std::endl;
v_callback(v_user, NULL, -1);
connection.start();
virtual void onDataTimeout(c_rtsp& connection)
std::cout << v_name << ":Data timeout -> retry" << std::endl;
v_callback(v_user, NULL, -1);
connection.start();
//增加的头部长度
int v_header = 0;
std::string v_name= "empty" ;
void RegisterCallBack(void * puser, int headlen, callback_onData cb)
v_user = puser;
v_header = headlen;
v_callback = cb;
callback_onData v_callback = NULL;
void * v_user = NULL;
;
protected:
class SessionSink: public MediaSink
public:
static SessionSink* createNew(UsageEnvironment& env, Callback* callback) return new SessionSink(env, callback);
private:
SessionSink(UsageEnvironment& env, Callback* callback);
virtual ~SessionSink();
void allocate(ssize_t bufferSize);
static void afterGettingFrame(void* clientData, unsigned frameSize,
unsigned numTruncatedBytes,
struct timeval presentationTime,
unsigned durationInMicroseconds)
static_cast<SessionSink*>(clientData)->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime, durationInMicroseconds);
void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime, unsigned durationInMicroseconds);
virtual Boolean continuePlaying();
private:
u_int8_t* m_buffer;
ssize_t m_bufferSize;
Callback* m_callback;
;
class RTSPClientConnection : public RTSPClient
public:
RTSPClientConnection(c_rtsp& connection, Environment& env, Callback* callback, const char* rtspURL, int timeout, bool rtpovertcp, int verbosityLevel);
virtual ~RTSPClientConnection();
protected:
void sendNextCommand();
RTSP_CALLBACK(DESCRIBE,resultCode,resultString);
RTSP_CALLBACK(SETUP,resultCode,resultString);
RTSP_CALLBACK(PLAY,resultCode,resultString);
TASK_CALLBACK(c_rtsp::RTSPClientConnection,ConnectionTimeout);
TASK_CALLBACK(c_rtsp::RTSPClientConnection,DataArrivalTimeout);
protected:
c_rtsp& m_connection;
int m_timeout;
bool m_rtpovertcp;
MediaSession* m_session;
MediaSubsession* m_subSession;
MediaSubsessionIterator* m_subSessionIter;
Callback * m_callback;
unsigned int m_nbPacket;
;
public:
c_rtsp(const char* rtspURL,
int header = 4,
int timeout = 5,
bool rtpovertcp = false,
int verbosityLevel = 0);
virtual ~c_rtsp();
void Register_Callcack(void * puser,const char * name, int headlen, callback_onData cb)
if (name != NULL)
m_callback.v_name = name;
else
m_callback.v_name = "empty";
m_callback.RegisterCallBack(puser, headlen, cb);
void start(unsigned int delay = 0);
void stop();
protected:
TASK_CALLBACK(c_rtsp,startCallback);
protected:
Environment m_env;
Callback m_callback;
std::string m_url;
int m_timeout = 2;
bool m_rtpovertcp = false;
int m_verbosity = 0;
RTSPClientConnection* m_rtspClient;
;
以上为头文件定义,其中比较Register_Callcack ,注册回调函数,将来在自己的文件里面处理收到的包。下面给出cpp
#include "common.h"
#include "c_rtsp.h"
c_rtsp::SessionSink::SessionSink(UsageEnvironment& env, Callback* callback)
: MediaSink(env)
, m_buffer(NULL)
, m_bufferSize(0)
, m_callback(callback)
//, m_markerSize(0)
allocate(512*1024);
c_rtsp::SessionSink::~SessionSink()
delete [] m_buffer;
void c_rtsp::SessionSink::allocate(ssize_t bufferSize)
m_bufferSize = bufferSize;
m_buffer = new u_int8_t[m_bufferSize];
if (m_callback)
m_callback->onNewBuffer(m_buffer, m_bufferSize);
void c_rtsp::SessionSink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime, unsigned durationInMicroseconds)
if (numTruncatedBytes != 0)
delete [] m_buffer;
envir() << "buffer too small " << (int)m_bufferSize << " allocate bigger one\\n";
allocate(m_bufferSize*2);
else if (m_callback)
if (!m_callback->onData(this->name(), m_buffer, frameSize+m_callback->v_header, presentationTime))
envir() << "NOTIFY failed\\n";
this->continuePlaying();
Boolean c_rtsp::SessionSink::continuePlaying()
Boolean ret = False;
if (source() != NULL)
int header = m_callback->v_header;
source()->getNextFrame(m_buffer+header, m_bufferSize-header,
afterGettingFrame, this,
onSourceClosure, this);
ret = True;
return ret;
c_rtsp::c_rtsp(const char* rtspURL, int header,
int timeout, bool rtpovertcp, int verbosityLevel)
: m_startCallbackTask(NULL)
, m_env(0)
, m_url(rtspURL)
, m_timeout(timeout)
, m_rtpovertcp(rtpovertcp)
, m_verbosity(verbosityLevel)
, m_rtspClient(NULL)
void c_rtsp::start(unsigned int delay)
m_startCallbackTask = m_env.taskScheduler().scheduleDelayedTask(delay*1000000, TaskstartCallback, this);
//start();
m_env.mainloop();
void c_rtsp::stop()
m_env.stop();
void c_rtsp::TaskstartCallback()
if (m_rtspClient)
Medium::close(m_rtspClient);
m_rtspClient = new RTSPClientConnection(*this, m_env, &m_callback, m_url.c_str(), m_timeout, m_rtpovertcp, m_verbosity);
c_rtsp::~c_rtsp()
m_env.taskScheduler().unscheduleDelayedTask(m_startCallbackTask);
Medium::close(m_rtspClient);
c_rtsp::RTSPClientConnection::RTSPClientConnection(c_rtsp& connection, Environment& env, Callback* callback, const char* rtspURL, int timeout, bool rtpovertcp, int verbosityLevel)
: RTSPClientConstrutor(env, rtspURL, verbosityLevel, NULL, 0)
, m_connection(connection)
, m_timeout(timeout)
, m_rtpovertcp(rtpovertcp)
, m_session(NULL)
, m_subSessionIter(NULL)
, m_callback(callback)
, m_nbPacket(0)
// start tasks
m_ConnectionTimeoutTask = envir().taskScheduler().scheduleDelayedTask(m_timeout*1000000, TaskConnectionTimeout, this);
// initiate connection process
this->sendNextCommand();
c_rtsp::RTSPClientConnection::~RTSPClientConnection()
envir().taskScheduler().unscheduleDelayedTask(m_ConnectionTimeoutTask);
envir().taskScheduler().unscheduleDelayedTask(m_DataArrivalTimeoutTask);
delete m_subSessionIter;
// free subsession
if (m_session != NULL)
MediaSubsessionIterator iter(*m_session);
MediaSubsession* subsession;
while ((subsession = iter.next()) != NULL)
if (subsession->sink)
envir() << "Close session: " << subsession->mediumName() << "/" << subsession->codecName() << "\\n";
Medium::close(subsession->sink);
subsession->sink = NULL;
Medium::close(m_session);
static unsigned getBufferSize(UsageEnvironment& env, int bufOptName,
int socket)
unsigned curSize;
socklen_t sizeSize = sizeof curSize;
if (getsockopt(socket, SOL_SOCKET, bufOptName,
(char*)&curSize, &sizeSize) < 0)
env<<"getBufferSize() error: ";
return 0;
return curSize;
static unsigned setBufferTo(UsageEnvironment& env, int bufOptName,
int socket, unsigned requestedSize)
socklen_t sizeSize = sizeof requestedSize;
setsockopt(socket, SOL_SOCKET, bufOptName, (char*)&requestedSize, sizeSize);
// Get and return the actual, resulting buffer size:
return getBufferSize(env, bufOptName, socket);
void c_rtsp::RTSPClientConnection::sendNextCommand()
if (m_subSessionIter == NULL)
// no SDP, send DESCRIBE
this->sendDescribeCommand(continueAfterDESCRIBE);
else
m_subSession = m_subSessionIter->next();
if (m_subSession != NULL)
// still subsession to SETUP
if (!m_subSession->initiate())
envir() << "Failed to initiate " << m_subSession->mediumName() << "/" << m_subSession->codecName() << " subsession: " << envir().getResultMsg() << "\\n";
this->sendNextCommand();
else
#define RECV_BUFFER 1024*1024
Environment &env = m_connection.m_env;
int socketNum = m_subSession->rtpSource()->RTPgs()->socketNum();
unsigned curBufferSize = setBufferTo(env, SO_RCVBUF, socketNum, 1024 * 1024);
if (curBufferSize < RECV_BUFFER)
env << "socket Buff is small than set,check system set! \\r\\n";
if (fVerbosityLevel > 1)
envir() << "Initiated " << m_subSession->mediumName() << "/" << m_subSession以上是关于使用live555制作rtsp客户端,捕获h264等解码的主要内容,如果未能解决你的问题,请参考以下文章
使用 Live555 从连接到 H264 编码器的 IP 摄像机流式传输实时视频
调用Live555接收RTSP直播流,转换为Http Live Streaming(iOS直播)协议
在 ffplay 中获得绿屏:使用 Live555 通过 RTP 流将桌面(DirectX 表面)流式传输为 H264 视频