基于Live555实现简单RTSP服务器

编译Live555

本人使用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

实现RTSP服务器

  1. 实现H264VideoServerMediaSubsession,继承于OnDemandServerMediaSubsession,提供RTSP会话Session
    主要实现createNewStreamSource()方法,
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;
};
  1. 实现ByteStreamRingBuffSource,继承于FramedSource,提供RTSP视频源
 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);
}

注意:,如果环形队列在不同线程中读写,会造成线程冲突,所以要加锁。

你可能感兴趣的:(服务器,c++,qt)