本人使用Qt5.14.2编译Live555
pro文件添加子模块
include (UsageEnvironment/UsageEnvironment.pri)
include (groupsock/groupsock.pri)
include (BasicUsageEnvironment/BasicUsageEnvironment.pri)
include (liveMedia/liveMedia.pri)
include (openssl_x64/openssl_x64.pri)
将live555目录下的UsageEnvironment,groupsock, BasicUsageEnvironment, liveMedia的文件夹下的C++文件拷贝到Qt工程目录下,
并编写各自模块的pri文件。
如:UsageEnvironment.pri
INCLUDEPATH += $$PWD/include
SOURCES += \
$$PWD/HashTable.cpp \
$$PWD/UsageEnvironment.cpp \
$$PWD/strDup.cpp
HEADERS += \
$$PWD/include/Boolean.hh \
$$PWD/include/HashTable.hh \
$$PWD/include/UsageEnvironment.hh \
$$PWD/include/UsageEnvironment_version.hh \
$$PWD/include/strDup.hh
live555需要添加依赖库openssl_x64。
INCLUDEPATH += $$PWD/include
LIBS += -L$$PWD/lib -llibcrypto -llibssl
openssl.files += $$PWD/bin/libssl-1_1-x64.dll
openssl.files += $$PWD/bin/libcrypto-1_1-x64.dll
openssl.path = $$DESTDIR
INSTALLS += openssl
FramedSource* H264VideoServerMediaSubsession::createNewStreamSource(unsigned /*clientSessionId*/
, unsigned& estBitrate) {
estBitrate = 500; // kbps, estimate
if (!m_RingBuff || !m_buff) {
return NULL;
}
ByteStreamRingBuffSource* fileSource = ByteStreamRingBuffSource
::createNew(envir(), m_buff, m_buffSize, false);
fileSource->setRingBuffer(m_RingBuff);
if (fileSource == NULL) return NULL;
return H264VideoStreamFramer::createNew(envir(), fileSource, True);
}
设置环形队列,方便数据读写
void H264VideoServerMediaSubsession::setRingBuffer(RingBuffer *buff)
{
m_RingBuff = buff;
}
class RingBuffer
{
public:
RingBuffer();
~RingBuffer();
void pushData(QByteArray &data); // 写入数据到队列中
void pushData(char *data, int size);
bool getData(uchar *data, int size); // 读取数据
int size();
private:
QList<QByteArray> m_dataList;
};
ByteStreamRingBuffSource*
ByteStreamRingBuffSource::createNew(UsageEnvironment& env,
u_int8_t* buffer, u_int64_t bufferSize,
Boolean deleteBufferOnClose,
unsigned preferredFrameSize,
unsigned playTimePerFrame) {
if (buffer == NULL) return NULL;
return new ByteStreamRingBuffSource(env, buffer, bufferSize, deleteBufferOnClose, preferredFrameSize, playTimePerFrame);
}
void ByteStreamRingBuffSource::setRingBuffer(RingBuffer *buff)
{
fRingbuff = buff;
}
ByteStreamRingBuffSource::ByteStreamRingBuffSource(UsageEnvironment& env,
u_int8_t* buffer, u_int64_t bufferSize,
Boolean deleteBufferOnClose,
unsigned preferredFrameSize,
unsigned playTimePerFrame)
: FramedSource(env), fBuffer(buffer), fBufferSize(bufferSize), fCurIndex(0), fDeleteBufferOnClose(deleteBufferOnClose),
fPreferredFrameSize(preferredFrameSize), fPlayTimePerFrame(playTimePerFrame), fLastPlayTime(0),
fLimitNumBytesToStream(False), fNumBytesToStream(0) {
}
需要实现doGetNextFrame()
void ByteStreamRingBuffSource::doGetNextFrame() {
if (fCurIndex >= fBufferSize || (fLimitNumBytesToStream && fNumBytesToStream == 0)) {
//handleClosure();
// return;
// fCurIndex = 0;
}
// Try to read as many bytes as will fit in the buffer provided (or "fPreferredFrameSize" if less)
fFrameSize = fMaxSize;
if (fLimitNumBytesToStream && fNumBytesToStream < (u_int64_t)fFrameSize) {
fFrameSize = (unsigned)fNumBytesToStream;
}
if (fPreferredFrameSize > 0 && fPreferredFrameSize < fFrameSize) {
fFrameSize = fPreferredFrameSize;
}
if (fFrameSize > fBufferSize) {
fFrameSize = (unsigned)fBufferSize;
}
if (fRingbuff->getData(fBuffer, fFrameSize)) {
memmove(fTo, fBuffer, fFrameSize);
}
fNumBytesToStream -= fFrameSize;
// Set the 'presentation time':
if (fPlayTimePerFrame > 0 && fPreferredFrameSize > 0) {
if (fPresentationTime.tv_sec == 0 && fPresentationTime.tv_usec == 0) {
// This is the first frame, so use the current time:
gettimeofday(&fPresentationTime, NULL);
} else {
// Increment by the play time of the previous data:
unsigned uSeconds = fPresentationTime.tv_usec + fLastPlayTime;
fPresentationTime.tv_sec += uSeconds/1000000;
fPresentationTime.tv_usec = uSeconds%1000000;
}
// Remember the play time of this data:
fLastPlayTime = (fPlayTimePerFrame*fFrameSize)/fPreferredFrameSize;
fDurationInMicroseconds = fLastPlayTime;
} else {
// We don't know a specific play time duration for this data,
// so just record the current time as being the 'presentation time':
gettimeofday(&fPresentationTime, NULL);
}
// Inform the downstream object that it has data:
FramedSource::afterGetting(this);
}
好了,搭建RTSP视频服务器,所需的类已经完成。
开启服务器,只需仿造DynamicRTSPServer.cpp中的代码 testOnDemandRTSPServer() 方法即可
void ZSYLiveServer::startServer()
{
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);
UserAuthenticationDatabase* authDB = NULL;
#ifdef ACCESS_CONTROL
// To implement client access control to the RTSP server, do the following:
authDB = new UserAuthenticationDatabase;
authDB->addUserRecord("username1", "password1"); // replace these with real strings
// Repeat the above with each , that you wish to allow
// access to the server.
#endif
// Create the RTSP server:
#ifdef SERVER_USE_TLS
// Serve RTSPS: RTSP over a TLS connection:
RTSPServer* rtspServer = RTSPServer::createNew(*env, 322, authDB);
#else
// Serve regular RTSP (over a TCP connection):
RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB);
#endif
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
char const* descriptionString
= "Session streamed by \"testOnDemandRTSPServer\"";
char const* streamName = "h264ESVideoTest";
char const* inputFileName = "test.264";
QFile f("test.264");
RingBuffer ringBuff;
QByteArray data;
if (f.open(QFile::ReadOnly)) {
while (!f.atEnd()) {
data = f.read(150000);
ringBuff.pushData(data); // 添加数据到环形队列
}
f.close();
}
//qWarning() << "SDJKFSJDFJ:" << ringBuff.size();
H264VideoServerMediaSubsession *session = H264VideoServerMediaSubsession
::createNew(*env, true);
session->allcBuffer(1500000);
session->setRingBuffer(&ringBuff);
//char const* streamName = "h264ESVideoTest";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
// To convert 16-bit PCM data to 8-bit u-law, prior to streaming,
// change the following to True:
sms->addSubsession(session);
char const* aFileName = "test.wav";
sms->addSubsession(WAVAudioFileServerMediaSubsession
::createNew(*env, aFileName, True, False));
rtspServer->addServerMediaSession(sms);
announceURL(rtspServer, sms);
#ifdef SERVER_USE_TLS
// (Attempt to) use the default HTTPS port (443) instead:
char const* httpProtocolStr = "HTTPS";
if (rtspServer->setUpTunnelingOverHTTP(443)) {
#else
char const* httpProtocolStr = "HTTP";
if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) {
#endif
*env << "\n(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-" << httpProtocolStr << " tunneling.)\n";
} else {
*env << "\n(RTSP-over-" << httpProtocolStr << " tunneling is not available.)\n";
}
env->taskScheduler().doEventLoop(); // does not return
qWarning() << "wait....";
//announceStream(rtspServer, sms, streamName, inputFileName);
}
注意:,如果环形队列在不同线程中读写,会造成线程冲突,所以要加锁。