一 简介
本文章主要是实现一个rtsp服务程序.该rtsp服务程序参考EsayRtspServer开源库,该rtsp服务程序使用到的库有: EasyRTSPServer,SimpleStreamClient,FFmpeg.本服务支持rtsp客户端从本服务拉取rtsp媒体流。拉流地址示例:rtsp://192.168.22.53:8554/rtsp地址.
二 依赖库
1.EasyRTSPServer库 --------- 开源库;
基于live555实现的可并发多路的rtsp服务组件;
2.SimpleStreamClient库 ------ 自定义库,参考链接 :拉流库
基于ffmpeg实现的拉取媒体流数据包动态库;
3. FFmpeg库 ------------ 开源库;
开源媒体流框架
三 相关文件
QRtspServer.pro
QT +=core widgets
CONFIG += c++11 console
CONFIG -= app_bundle
QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO
QMAKE_LFLAGS_RELEASE = $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp \
rtspserver.cpp
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
HEADERS += \
mainwindow.h \
rtspserver.h
INCLUDEPATH += $$PWD/ffmpeg/include
LIBS += -L$$PWD/ffmpeg/lib \
-lavcodec \
-lavdevice \
-lavformat \
-lavutil \
-lswscale \
-lswresample
unix:!macx: LIBS += -L$$PWD/EasyRtspServer/ -lEasyRTSPServer
INCLUDEPATH += $$PWD/EasyRtspServer
DEPENDPATH += $$PWD/EasyRtspServer
unix:!macx: LIBS += -L$$PWD/SimpleStreamClient/lib/ -lSimpleStreamClient
INCLUDEPATH += $$PWD/SimpleStreamClient/include
DEPENDPATH += $$PWD/SimpleStreamClient/include
rtspserver.h
cpp
#ifndef RTSPSERVER_H
#define RTSPSERVER_H
#include <QThread>
#include <QDebug>
#include "EasyRtspServerAPI.h"
#include "SimpleStreamClientAPI.h"
typedef struct __AV_FRAME_PTS_T
{
long long pts;
long long dts;
float dtsDecimal;
}AV_FRAME_PTS_T;
typedef struct __RTSP_CHANNEL_T
{
char url[256];
char resourcename[256];
char username[36];
char password[36];
EASY_RTSPSERVER_MEDIA_INFO_T mediaInfo; //源媒体信息
SIMPLE_MEDIA_INFO_T s_mediaInfo;
Simple_Handle rtspClientHandle;//rtsp客户端句柄
EASY_CHANNEL_HANDLE channelHandle;
int status;
}RTSP_CHANNEL_T;
class RtspServer:public QObject
{
Q_OBJECT
public:
explicit RtspServer(QObject *parent = nullptr);
~RtspServer();
bool startSrv(int port = 8554);
void stopSrv();
private:
private:
};
#endif // RTSPSERVER_H
rtspserver.cpp
cpp
#include "rtspserver.h"
#include <QDateTime>
#include "SimpleStreamClientAPI.h"
//#define EASYSTREALCLIENT
//输入的pbuf必须包含start code(00 00 00 01)
int GetH265VPSandSPSandPPS(char *pbuf, int bufsize, char *_vps, int *_vpslen, char *_sps, int *_spslen, char *_pps, int *_ppslen)
{
char vps[512]={0}, sps[512] = {0}, pps[128] = {0};
int vpslen=0, spslen=0, ppslen=0, i=0, iStartPos=0, ret=-1;
int iFoundVPS=0, iFoundSPS=0, iFoundPPS=0, iFoundSEI=0;
if (NULL == pbuf || bufsize<4) return -1;
for (i=0; i<bufsize; i++)
{
if ( (unsigned char)pbuf[i] == 0x00 && (unsigned char)pbuf[i+1] == 0x00 &&
(unsigned char)pbuf[i+2] == 0x00 && (unsigned char)pbuf[i+3] == 0x01 )
{
//printf("0x%X\n", (unsigned char)pbuf[i+4]);
switch ((unsigned char)pbuf[i+4])
{
case 0x40: //VPS
{
iFoundVPS = 1;
iStartPos = i+4;
}
break;
case 0x42: //SPS
{
if (iFoundVPS == 0x01 && i>4)
{
vpslen = i-4-iStartPos+4;
if (vpslen>256) return -1; //vps长度超出范围
memset(vps, 0x00, sizeof(vps));
memcpy(vps, pbuf+iStartPos, vpslen);
}
iStartPos = i+4;
iFoundSPS = 1;
}
break;
case 0x44: //PPS
{
if (iFoundSPS == 0x01 && i>4)
{
spslen = i-4-iStartPos+4;
if (spslen>256) return -1;
memset(sps, 0x0, sizeof(sps));
memcpy(sps, pbuf+iStartPos, spslen);
}
iStartPos = i+4;
iFoundPPS = 1;
}
break;
case 0x4E: //SEI
case 0x50:
{
if (iFoundPPS == 0x01 && i>4)
{
ppslen = i-4-iStartPos+4;
if (ppslen>256) return -1;
memset(pps, 0x0, sizeof(pps));
memcpy(pps, pbuf+iStartPos, ppslen);
}
iStartPos = i+4;
iFoundSEI = 1;
}
break;
default:
{
iStartPos = i+4;
}
break;
}
}
if (iFoundSEI == 0x01) break;
}
if (iFoundVPS == 0x01)
{
if (vpslen < 1)
{
if (bufsize < sizeof(vps))
{
vpslen = bufsize-4;
memset(vps, 0x00, sizeof(vps));
memcpy(vps, pbuf+4, vpslen);
}
}
if (vpslen > 0)
{
if (NULL != _vps) memcpy(_vps, vps, vpslen);
if (NULL != _vpslen) *_vpslen = vpslen;
}
ret = 0;
}
if (iFoundSPS == 0x01)
{
if (spslen < 1)
{
if (bufsize < sizeof(sps))
{
spslen = bufsize-4;
memset(sps, 0x00, sizeof(sps));
memcpy(sps, pbuf+4, spslen);
}
}
if (spslen > 0)
{
if (NULL != _sps) memcpy(_sps, sps, spslen);
if (NULL != _spslen) *_spslen = spslen;
}
ret = 0;
}
if (iFoundPPS == 0x01)
{
if (ppslen < 1)
{
if (bufsize < sizeof(pps))
{
ppslen = bufsize-4;
memset(pps, 0x00, sizeof(pps));
memcpy(pps, pbuf+4, ppslen); //pps
}
}
if (ppslen > 0)
{
if (NULL != _pps) memcpy(_pps, pps, ppslen);
if (NULL != _ppslen) *_ppslen = ppslen;
}
ret = 0;
}
return ret;
}
int GetH264SPSandPPS(char* pbuf, int bufsize, char* _sps, int* _spslen, char* _pps, int* _ppslen, char* _sei, int* _seilen)
{
char sps[512] = { 0 }, pps[128] = { 0 }, sei[128] = { 0 };
int spslen = 0, ppslen = 0, seilen = 0, i = 0, iStartPos = 0, ret = -1;
int iFoundSPS = 0, iFoundPPS = 0, iFoundSEI = 0, iFoundIDR = 0;
if (NULL == pbuf || bufsize < 4) return -1;
int startCodeOffset = -1;
int offsetLen = 0;
for (i = 0; i < bufsize; i++)
{
if ((unsigned char)pbuf[i] == 0x00 && (unsigned char)pbuf[i + 1] == 0x00 &&
(unsigned char)pbuf[i + 2] == 0x00 && (unsigned char)pbuf[i + 3] == 0x01)
{
startCodeOffset = 4;
}
else if ((unsigned char)pbuf[i] == 0x00 && (unsigned char)pbuf[i + 1] == 0x00 &&
(unsigned char)pbuf[i + 2] == 0x01)
{
startCodeOffset = 3;
}
if (startCodeOffset >= 0)
{
unsigned char naltype = ((unsigned char)pbuf[i + startCodeOffset] & 0x1F);
if (naltype == 7) //sps
{
iFoundSPS = 1;
iStartPos = i + startCodeOffset;
offsetLen += startCodeOffset;
i += 1;
}
else if (naltype == 8) //pps
{
//copy sps
if (iFoundSPS == 0x01 && i > 4)
{
spslen = i - startCodeOffset;
if (spslen > 256) return -1; //sps3¤?è3?3?·??§
memset(sps, 0x00, sizeof(sps));
memcpy(sps, pbuf + startCodeOffset, spslen);
}
iFoundPPS = 1;
offsetLen += startCodeOffset;
i += 1;
}
else if (naltype == 6) //sei
{
if (iFoundPPS == 0x01 && i > 4)
{
ppslen = i - spslen - offsetLen;
if (ppslen > 0 && ppslen < sizeof(pps))
{
memset(pps, 0x00, sizeof(pps));
memcpy(pps, pbuf + spslen + offsetLen, ppslen); //pps
}
}
iFoundSEI = 1;
offsetLen += startCodeOffset;
i += 1;
}
else if (naltype == 5) //idr
{
iFoundIDR = 1;
if (iFoundPPS == 1 && iFoundSEI == 0)
{
ppslen = i - spslen - offsetLen;
if (ppslen > 0 && ppslen < sizeof(pps))
{
memset(pps, 0x00, sizeof(pps));
memcpy(pps, pbuf + spslen + offsetLen, ppslen); //pps
}
}
else if (iFoundSEI==1)
{
seilen = i - spslen - offsetLen - ppslen;
if (seilen > 0 && seilen < sizeof(sei))
{
memset(sei, 0x00, sizeof(sei));
memcpy(sei, pbuf + spslen + offsetLen + ppslen, seilen); //sei
}
else
{
seilen = 0;
}
}
break;
}
startCodeOffset = -1;
}
else
{
}
}
if (iFoundSPS == 0x01)
{
if (spslen < 1)
{
if (bufsize < sizeof(sps))
{
spslen = bufsize - 4;
memset(sps, 0x00, sizeof(sps));
memcpy(sps, pbuf + 4, spslen);
}
}
if (spslen > 0)
{
if (NULL != _sps) memcpy(_sps, sps, spslen);
if (NULL != _spslen) *_spslen = spslen;
}
ret = 0;
}
if (iFoundPPS == 0x01)
{
if (ppslen < 1)
{
if (bufsize < sizeof(pps))
{
ppslen = bufsize - 4;
memset(pps, 0x00, sizeof(pps));
memcpy(pps, pbuf + 4, ppslen); //pps
}
}
if (ppslen > 0)
{
if (NULL != _pps) memcpy(_pps, pps, ppslen);
if (NULL != _ppslen) *_ppslen = ppslen;
}
ret = 0;
}
if (iFoundSEI == 0x01)
{
if (seilen > 0)
{
if (NULL != _sei) memcpy(_sei, sei, seilen);
if (NULL != _seilen) *_seilen = seilen;
}
ret = 0;
}
return ret;
}
int CALLBACK __SimpleStreamClientCallBack(void *_channelPtr, int _frameType, void *pBuf, SIMPLE_FRAME_INFO* frameInfo)
{
RTSP_CHANNEL_T* pChannel = (RTSP_CHANNEL_T*)_channelPtr;
if (_frameType == EASY_SDK_VIDEO_FRAME_FLAG){
if (pChannel->mediaInfo.spsLength < 1 && frameInfo->type == 1)
{
#if 1
if (frameInfo->codec == EASY_SDK_VIDEO_CODEC_H264)
{
GetH264SPSandPPS((char*)pBuf, frameInfo->length,
(char*)pChannel->mediaInfo.sps, (int*)&pChannel->mediaInfo.spsLength,
(char*)pChannel->mediaInfo.pps, (int*)&pChannel->mediaInfo.ppsLength,
(char*)pChannel->mediaInfo.sei, (int*)&pChannel->mediaInfo.seiLength);
qDebug() << "I帧 H264 spslen:" << pChannel->mediaInfo.spsLength << " ppslen:" << pChannel->mediaInfo.ppsLength;
pChannel->mediaInfo.videoCodec = EASY_SDK_VIDEO_CODEC_H264;
}
else if (frameInfo->codec == EASY_SDK_VIDEO_CODEC_H265)
{
GetH265VPSandSPSandPPS((char*)pBuf, frameInfo->length, (char*)pChannel->mediaInfo.vps, (int*)&pChannel->mediaInfo.vpsLength, (char*)pChannel->mediaInfo.sps, (int*)&pChannel->mediaInfo.spsLength, (char*)pChannel->mediaInfo.pps, (int*)&pChannel->mediaInfo.ppsLength);
pChannel->mediaInfo.videoCodec = EASY_SDK_VIDEO_CODEC_H265;
}
qDebug("__EasyStreamClientCallBack Get Media Info: video:%u fps:%u audio:%u channel:%u sampleRate:%u spslen: %d ppslen:%d\n",
pChannel->mediaInfo.videoCodec, pChannel->mediaInfo.videoFps,
pChannel->mediaInfo.audioCodec, pChannel->mediaInfo.audioChannel, pChannel->mediaInfo.audioSampleRate,
pChannel->mediaInfo.spsLength, pChannel->mediaInfo.ppsLength);
#if 0
if (pChannel->mediaInfo.spsLength > 0)
{
pChannel->mediaInfo.videoCodec = frameInfo->codec;
pChannel->mediaInfo.audioCodec = EASY_SDK_AUDIO_CODEC_G711U;
pChannel->mediaInfo.audioSampleRate = 8000;
pChannel->mediaInfo.audioChannel = 1;
pChannel->mediaInfo.audioBitsPerSample = 16;
}
#endif
#endif
}
EASY_AV_Frame avFrameInfo;
bool bSendSpsAndPps = false;
#if 1
if (frameInfo->type == 0x01 && pChannel->mediaInfo.spsLength > 0 && pChannel->mediaInfo.ppsLength > 0)
{
bSendSpsAndPps = true;
if (pChannel->mediaInfo.vpsLength > 0)
{
//VPS
memset(&avFrameInfo, 0x00, sizeof(EASY_AV_Frame));
avFrameInfo.u32AVFrameFlag = _frameType;
avFrameInfo.u32AVFrameLen = pChannel->mediaInfo.vpsLength;
avFrameInfo.u32VFrameType = frameInfo->type;
avFrameInfo.pBuffer = (unsigned char*)(pChannel->mediaInfo.vps);
EasyRtspServer_PushFrame(pChannel->channelHandle, &avFrameInfo);
}
#if 1
//SPS
memset(&avFrameInfo, 0x00, sizeof(EASY_AV_Frame));
avFrameInfo.u32AVFrameFlag = _frameType;
avFrameInfo.u32AVFrameLen = pChannel->mediaInfo.spsLength;
avFrameInfo.u32VFrameType = frameInfo->type;
avFrameInfo.pBuffer = (unsigned char*)(pChannel->mediaInfo.sps);
EasyRtspServer_PushFrame(pChannel->channelHandle, &avFrameInfo);
//PPS
memset(&avFrameInfo, 0x00, sizeof(EASY_AV_Frame));
avFrameInfo.u32AVFrameFlag = _frameType;
avFrameInfo.u32AVFrameLen = pChannel->mediaInfo.ppsLength;
avFrameInfo.u32VFrameType = frameInfo->type;
avFrameInfo.pBuffer = (unsigned char*)(pChannel->mediaInfo.pps);
EasyRtspServer_PushFrame(pChannel->channelHandle, &avFrameInfo);
#endif
}
#endif
memset(&avFrameInfo, 0x00, sizeof(EASY_AV_Frame));
avFrameInfo.u32AVFrameFlag = _frameType;
//rtsp流
avFrameInfo.u32AVFrameLen = frameInfo->length - 4;
avFrameInfo.u32VFrameType = frameInfo->type;
avFrameInfo.pBuffer = (unsigned char*)(pChannel->mediaInfo.sps);
if (bSendSpsAndPps)
{
if (pChannel->mediaInfo.videoCodec == EASY_SDK_VIDEO_CODEC_H264)
{
#if 1
unsigned char* frameData = (unsigned char*)pBuf;
for (unsigned int idx = 0; idx < frameInfo->length - 1; idx++)
{
if (((unsigned char)(frameData)[idx] == 0x00) &&
((unsigned char)frameData[idx + 1] == 0x01) &&
(((unsigned char)frameData[idx + 2] & 0x1f) == 0x5)) //I帧
{
avFrameInfo.u32AVFrameLen = frameInfo->length - idx - 2;
avFrameInfo.pBuffer = (unsigned char*)(frameData + idx + 2);
break;
}
}
#else
int trim_length = 4 + pChannel->mediaInfo.spsLength;
trim_length += 4 + pChannel->mediaInfo.ppsLength;
if (pChannel->mediaInfo.seiLength > 0) trim_length += 4 + pChannel->mediaInfo.seiLength;
trim_length += 4;
avFrameInfo.u32AVFrameLen = frameInfo->length ;
avFrameInfo.pBuffer = (unsigned char*)((char*)pBuf);
#endif
#if 0
qDebug("h264 I frame:%d\n",avFrameInfo.u32AVFrameLen);
QByteArray arr((char*)avFrameInfo.pBuffer,50);
QString hex = arr.toHex(' ');
qDebug().noquote() <<"前五十个字节:"<< hex;
#endif
}
else if (pChannel->mediaInfo.videoCodec == EASY_SDK_VIDEO_CODEC_H265)
{
#if 0
unsigned char* frameData = (unsigned char*)pBuf;
for (unsigned int idx = 0; idx < frameInfo->length - 1; idx++)
{
if (((unsigned char)(frameData)[idx] == 0x00) &&
((unsigned char)frameData[idx + 1] == 0x01) &&
((((unsigned char)frameData[idx + 2] & 0x7f) == 19||
((unsigned char)frameData[idx + 2] & 0x7f) == 20))) //I帧
{
avFrameInfo.u32AVFrameLen = frameInfo->length - idx - 2;
avFrameInfo.pBuffer = (unsigned char*)(frameData + idx + 2);
break;
}
}
#else
int trim_length = 4 + pChannel->mediaInfo.vpsLength;
trim_length += 4 + pChannel->mediaInfo.spsLength;
trim_length += 4 + pChannel->mediaInfo.ppsLength;
if (pChannel->mediaInfo.seiLength > 0)
trim_length += 4 + pChannel->mediaInfo.seiLength;
avFrameInfo.u32AVFrameLen = frameInfo->length-trim_length;
avFrameInfo.pBuffer = (unsigned char*)((char*)pBuf+trim_length);
#endif
qDebug("h265 I frame:%d\n",avFrameInfo.u32AVFrameLen);
QByteArray arr((char*)avFrameInfo.pBuffer,50);
QString hex = arr.toHex(' ');
qDebug().noquote() <<"前十个字节:"<< hex;
qDebug("\n");
}
}
else
{
avFrameInfo.pBuffer = (unsigned char*)((char*)pBuf + 4);
#if 0
qDebug("h264 P frame:%d\n",avFrameInfo.u32AVFrameLen);
QByteArray arr((char*)avFrameInfo.pBuffer,50);
QString hex = arr.toHex(' ');
qDebug().noquote() <<"前五十个字节:"<< hex;
#endif
}
EasyRtspServer_PushFrame(pChannel->channelHandle, &avFrameInfo);
}else if(_frameType == EASY_SDK_AUDIO_FRAME_FLAG){
EASY_AV_Frame avFrameInfo;
memset(&avFrameInfo, 0x00, sizeof(EASY_AV_Frame));
avFrameInfo.u32AVFrameFlag = _frameType;
avFrameInfo.u32AVFrameLen = frameInfo->length;
avFrameInfo.u32VFrameType = frameInfo->type;
avFrameInfo.pBuffer = (unsigned char*)pBuf;
EasyRtspServer_PushFrame(pChannel->channelHandle, &avFrameInfo);
}else if(_frameType == EASY_SDK_EVENT_FRAME_FLAG){
}else if(_frameType == EASY_SDK_MEDIA_INFO_FLAG){
EASY_MEDIA_INFO_T* mediainfo = (EASY_MEDIA_INFO_T*)pBuf;
if (pBuf != NULL)
{
memcpy(mediainfo, pBuf, sizeof(EASY_MEDIA_INFO_T));
#if 0
printf("Source;[%s] Get MediaInfo: video:%u fps:%u audio:%u channel:%u sampleRate:%u spslen: %d ppslen:%d\n",
pChannel->url,
mediainfo->u32VideoCodec, mediainfo->u32VideoFps,
mediainfo->u32AudioCodec, mediainfo->u32AudioChannel, mediainfo->u32AudioSamplerate,
mediainfo->u32SpsLength, mediainfo->u32PpsLength);
#endif
}
if (NULL != mediainfo && NULL != pChannel)
{
pChannel->mediaInfo.audioChannel = mediainfo->u32AudioChannel;
pChannel->mediaInfo.audioSampleRate = mediainfo->u32AudioSamplerate;
pChannel->mediaInfo.audioBitsPerSample = mediainfo->u32AudioBitsPerSample;
pChannel->mediaInfo.vpsLength = mediainfo->u32VpsLength;
if (pChannel->mediaInfo.vpsLength > 0) memcpy(pChannel->mediaInfo.vps, mediainfo->u8Vps, mediainfo->u32VpsLength);
pChannel->mediaInfo.spsLength = mediainfo->u32SpsLength;
if (pChannel->mediaInfo.spsLength > 0) memcpy(pChannel->mediaInfo.sps, mediainfo->u8Sps, mediainfo->u32SpsLength);
pChannel->mediaInfo.ppsLength = mediainfo->u32PpsLength;
if (pChannel->mediaInfo.ppsLength > 0) memcpy(pChannel->mediaInfo.pps, mediainfo->u8Pps, mediainfo->u32PpsLength);
//最后再设置编码类型
pChannel->mediaInfo.audioCodec = mediainfo->u32AudioCodec;
pChannel->mediaInfo.videoCodec = mediainfo->u32VideoCodec;
}
}
return 0;
}
int CALLBACK __EasyRtspServer_Callback(EASY_RTSPSERVER_STATE_T serverStatus, const char *resourceName,
EASY_CHANNEL_HANDLE *channelHandle,
EASY_RTSPSERVER_MEDIA_INFO_T *mediaInfo,
EASY_PLAY_CONTROL_INFO_T *playCtrlInfo,
void *userPtr, void *channelPtr)
{
int ret = -1;
qDebug("EasyStreamingServer New Request: %s status[%d]\n", resourceName, serverStatus );
if (EASY_CHANNEL_OPEN_STREAM == serverStatus)
{
EASY_CHANNEL_HANDLE pChannelHandle = NULL;
qDebug("EasyStreamingServer New Request: %s\n", resourceName);
char szURL[260] = { 0 };
#if 0
char filename[MAX_PATH] = { 0 };
sprintf(filename, "%s.txt", resourceName);
FILE* f = fopen(filename, "rb");
if (NULL == f)
{
return 0;
}
fgets(szURL, sizeof(szURL), f);
fclose(f);
#else
strcpy(szURL, resourceName);
#endif
int len = (int)strlen(szURL);
for (int i = len - 1; i > 0; i--)
{
if (((unsigned char)szURL[i] == '\r') ||
((unsigned char)szURL[i] == '\n'))
{
szURL[i] = '\0';
}
}
#if 0
if ((int)strlen(szURL) < 3) return 0;
#endif
RTSP_CHANNEL_T *pRtspChannel = new RTSP_CHANNEL_T;
if (NULL == pRtspChannel) return -1;
memset(pRtspChannel, 0x00, sizeof(RTSP_CHANNEL_T));
strcpy(pRtspChannel->url, szURL);// resourceName);
#ifdef EASYSTREALCLIENT
EasyStreamClient_Init(&pRtspChannel->rtspClientHandle, 2);
if (!pRtspChannel->rtspClientHandle)
{
printf("EasyStreamClient_Init Initial fail.\n");
return 0;
}
EasyStreamClient_SetAudioEnable(pRtspChannel->rtspClientHandle, 1);
EasyStreamClient_SetCallback(pRtspChannel->rtspClientHandle, __EasyStreamClientCallBack);
unsigned int mediaType = EASY_SDK_VIDEO_FRAME_FLAG | EASY_SDK_AUDIO_FRAME_FLAG;
EasyStreamClient_OpenStream(pRtspChannel->rtspClientHandle, pRtspChannel->url, EASY_RTP_OVER_TCP, (void*)pRtspChannel, 1000, 1, 1);
#else
SimpleStreamClient_Init(&pRtspChannel->rtspClientHandle, 0);
if (!pRtspChannel->rtspClientHandle)
{
printf("EasyStreamClient_Init Initial fail.\n");
return 0;
}
SimpleStreamClient_SetCallback(pRtspChannel->rtspClientHandle,__SimpleStreamClientCallBack);
SimpleStreamClient_OpenStream(pRtspChannel->rtspClientHandle,pRtspChannel->url,Simple_RTP_OVER_TCP,(void*)pRtspChannel,1000,1,1);
#endif
for (int i=0; i<10*2; i++) //等待5秒
{
if (pRtspChannel->s_mediaInfo.u32VideoCodec>0) break;// && pRtspChannel->mediaInfo.audioCodec) break;
QThread::msleep(100);
}
//设置媒体信息
mediaInfo->videoCodec = pRtspChannel->s_mediaInfo.u32VideoCodec;
mediaInfo->audioCodec = pRtspChannel->s_mediaInfo.u32AudioCodec;
mediaInfo->audioSampleRate = pRtspChannel->s_mediaInfo.u32AudioSamplerate;
mediaInfo->audioChannel = pRtspChannel->s_mediaInfo.u32AudioChannel;
mediaInfo->audioBitsPerSample = pRtspChannel->s_mediaInfo.u32AudioBitsPerSample == 0 ? 16 : pRtspChannel->s_mediaInfo.u32AudioBitsPerSample;
qDebug("Get Source:[%s] videoCodec:%d,audioCodec:%d,audioSampleRate=%d,pRtspChannel=%x\n",
pRtspChannel->url,
pRtspChannel->s_mediaInfo.u32VideoCodec,
pRtspChannel->s_mediaInfo.u32AudioCodec,
pRtspChannel->s_mediaInfo.u32AudioSamplerate,
pRtspChannel);
#if 1
//此处可指定sps & pps, 也可以不指定
mediaInfo->vpsLength = pRtspChannel->s_mediaInfo.u32VpsLength;
memcpy(mediaInfo->vps, pRtspChannel->s_mediaInfo.u8Vps, mediaInfo->vpsLength);
mediaInfo->spsLength = pRtspChannel->s_mediaInfo.u32SpsLength;
memcpy(mediaInfo->sps, pRtspChannel->s_mediaInfo.u8Sps, mediaInfo->spsLength);
mediaInfo->ppsLength = pRtspChannel->s_mediaInfo.u32PpsLength;
memcpy(mediaInfo->pps, pRtspChannel->s_mediaInfo.u8Pps, mediaInfo->ppsLength);
#endif
mediaInfo->videoFps = 25;
if (pRtspChannel->s_mediaInfo.u32VideoCodec > 0)
{
qDebug("create channel: %s\n", resourceName);
//创建通道
EasyRtspServer_CreateChannel(resourceName, &pChannelHandle, (void *)pRtspChannel);
*channelHandle = pChannelHandle;
pRtspChannel->channelHandle = pChannelHandle;
ret = 0;
}
else //关闭源
{
qDebug("Get Source[%s][%s] sdp fail.\n", resourceName, pRtspChannel->url);
#ifdef EASYSTREALCLIENT
EasyStreamClient_Deinit(pRtspChannel->rtspClientHandle);
#else
SimpleStreamClient_Deinit(pRtspChannel->rtspClientHandle);
#endif
delete pRtspChannel;
pRtspChannel = NULL;
}
}
if (EASY_CHANNEL_START_STREAM == serverStatus)
{
RTSP_CHANNEL_T *pRtspChannel = (RTSP_CHANNEL_T *)channelPtr;
pRtspChannel->status = 0x01; //开始发送流
}
if (EASY_CHANNEL_STOP_STREAM == serverStatus)
{
RTSP_CHANNEL_T *pRtspChannel = (RTSP_CHANNEL_T *)channelPtr;
pRtspChannel->status = 0x00; //停止发送流
}
else if (EASY_CHANNEL_CLOSE_STREAM == serverStatus)
{
qDebug("#######################Close Stream: %s\n", resourceName);
RTSP_CHANNEL_T *pRtspChannel = (RTSP_CHANNEL_T *)channelPtr;
if (NULL != pRtspChannel)
{
if (NULL != pRtspChannel->rtspClientHandle)
{
#ifdef EASYSTREALCLIENT
EasyStreamClient_Deinit(pRtspChannel->rtspClientHandle);
#else
SimpleStreamClient_Deinit(pRtspChannel->rtspClientHandle);
#endif
}
qDebug("Close Source OK: %s %s\n", resourceName, pRtspChannel->url);
EasyRtspServer_DeleteChannel(&pRtspChannel->channelHandle);
delete pRtspChannel;
pRtspChannel = NULL;
}
ret = 0;
}
else if (EASY_CHANNEL_PLAY_CONTROL == serverStatus)
{
//以下为播放控制, 用于录像回放时使用
#if 0
printf("RTSP_CHANNEL_PLAY_CONTROL: %s\n", resourceName);
RTSP_CHANNEL_T *pRtspChannel = (RTSP_CHANNEL_T *)channelPtr;
if (NULL != pRtspChannel)
{
char szMediaType[16] = {0};
if (EASY_SDK_VIDEO_FRAME_FLAG == playCtrlInfo->mediaType) strcpy(szMediaType, "Video");
else if (EASY_SDK_AUDIO_FRAME_FLAG == playCtrlInfo->mediaType) strcpy(szMediaType, "Audio");
else if (EASY_SDK_EVENT_FRAME_FLAG == playCtrlInfo->mediaType) strcpy(szMediaType, "Metadata");
if (EASY_PLAY_CTRL_CMD_GET_DURATION == playCtrlInfo->ctrlCommand)
printf("GetDuration... %s\n", szMediaType);
else if (EASY_PLAY_CTRL_CMD_PAUSE == playCtrlInfo->ctrlCommand)
printf("Pause...%s\n", szMediaType);
else if (EASY_PLAY_CTRL_CMD_RESUME == playCtrlInfo->ctrlCommand)
printf("Resume...%s\n", szMediaType);
else if (EASY_PLAY_CTRL_CMD_SCALE == playCtrlInfo->ctrlCommand)
printf("Scale[%s]: %.2f\n", szMediaType, playCtrlInfo->scale);
else if (EASY_PLAY_CTRL_CMD_SEEK_STREAM == playCtrlInfo->ctrlCommand)
printf("Seek[%s]: %s - %s \n", szMediaType, playCtrlInfo->startTime, playCtrlInfo->endTime);
}
#endif
}
return ret;
}
RtspServer::RtspServer(QObject *parent)
{
}
RtspServer::~RtspServer()
{
stopSrv();
}
bool RtspServer::startSrv(int port)
{
// 启动RTSP服务
if (EasyRtspServer_Startup(port,nullptr,EASY_AUTHENTICATION_TYPE_NONE
,"admin","12345",__EasyRtspServer_Callback,this) < 0) {
qDebug() << "RTSP服务启动失败";
printf("startup fail.\n");
EasyRtspServer_Shutdown();
return false;
}
qDebug() << "RTSP服务启动成功...";
qDebug() << "rtsp://127.0.0.1:" << port << "/ch[1-7]";
qDebug() << "rtsp://127.0.0.1:" <<port << "/[rtsp地址]";
return true;
}
void RtspServer::stopSrv()
{
EasyRtspServer_Shutdown();
}
main.cpp
cpp
#include <QApplication>
#include "rtspserver.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
RtspServer *_rtspServer = new RtspServer;
_rtspServer->startSrv();
return a.exec();
}
四 关键代码
SimpleStreamClient库回调函数
主要实现的功能包括:
处理视频流,并通过接口发送数据包给rtsp服务;
处理音频流,并通过接口发送数据包给rtsp服务;
处理事件帧信息;(未使用)
处理媒体信息.(未使用)
int CALLBACK __SimpleStreamClientCallBack(void *_channelPtr, int _frameType, void *pBuf, SIMPLE_FRAME_INFO* frameInfo)
{
RTSP_CHANNEL_T* pChannel = (RTSP_CHANNEL_T*)_channelPtr;
if (_frameType == EASY_SDK_VIDEO_FRAME_FLAG){
if (pChannel->mediaInfo.spsLength < 1 && frameInfo->type == 1)
{
#if 1
if (frameInfo->codec == EASY_SDK_VIDEO_CODEC_H264)
{
GetH264SPSandPPS((char*)pBuf, frameInfo->length,
(char*)pChannel->mediaInfo.sps, (int*)&pChannel->mediaInfo.spsLength,
(char*)pChannel->mediaInfo.pps, (int*)&pChannel->mediaInfo.ppsLength,
(char*)pChannel->mediaInfo.sei, (int*)&pChannel->mediaInfo.seiLength);
qDebug() << "I帧 H264 spslen:" << pChannel->mediaInfo.spsLength << " ppslen:" << pChannel->mediaInfo.ppsLength;
pChannel->mediaInfo.videoCodec = EASY_SDK_VIDEO_CODEC_H264;
}
else if (frameInfo->codec == EASY_SDK_VIDEO_CODEC_H265)
{
GetH265VPSandSPSandPPS((char*)pBuf, frameInfo->length, (char*)pChannel->mediaInfo.vps, (int*)&pChannel->mediaInfo.vpsLength, (char*)pChannel->mediaInfo.sps, (int*)&pChannel->mediaInfo.spsLength, (char*)pChannel->mediaInfo.pps, (int*)&pChannel->mediaInfo.ppsLength);
pChannel->mediaInfo.videoCodec = EASY_SDK_VIDEO_CODEC_H265;
}
qDebug("__EasyStreamClientCallBack Get Media Info: video:%u fps:%u audio:%u channel:%u sampleRate:%u spslen: %d ppslen:%d\n",
pChannel->mediaInfo.videoCodec, pChannel->mediaInfo.videoFps,
pChannel->mediaInfo.audioCodec, pChannel->mediaInfo.audioChannel, pChannel->mediaInfo.audioSampleRate,
pChannel->mediaInfo.spsLength, pChannel->mediaInfo.ppsLength);
#if 0
if (pChannel->mediaInfo.spsLength > 0)
{
pChannel->mediaInfo.videoCodec = frameInfo->codec;
pChannel->mediaInfo.audioCodec = EASY_SDK_AUDIO_CODEC_G711U;
pChannel->mediaInfo.audioSampleRate = 8000;
pChannel->mediaInfo.audioChannel = 1;
pChannel->mediaInfo.audioBitsPerSample = 16;
}
#endif
#endif
}
EASY_AV_Frame avFrameInfo;
bool bSendSpsAndPps = false;
#if 1
if (frameInfo->type == 0x01 && pChannel->mediaInfo.spsLength > 0 && pChannel->mediaInfo.ppsLength > 0)
{
bSendSpsAndPps = true;
if (pChannel->mediaInfo.vpsLength > 0)
{
//VPS
memset(&avFrameInfo, 0x00, sizeof(EASY_AV_Frame));
avFrameInfo.u32AVFrameFlag = _frameType;
avFrameInfo.u32AVFrameLen = pChannel->mediaInfo.vpsLength;
avFrameInfo.u32VFrameType = frameInfo->type;
avFrameInfo.pBuffer = (unsigned char*)(pChannel->mediaInfo.vps);
EasyRtspServer_PushFrame(pChannel->channelHandle, &avFrameInfo);
}
#if 1
//SPS
memset(&avFrameInfo, 0x00, sizeof(EASY_AV_Frame));
avFrameInfo.u32AVFrameFlag = _frameType;
avFrameInfo.u32AVFrameLen = pChannel->mediaInfo.spsLength;
avFrameInfo.u32VFrameType = frameInfo->type;
avFrameInfo.pBuffer = (unsigned char*)(pChannel->mediaInfo.sps);
EasyRtspServer_PushFrame(pChannel->channelHandle, &avFrameInfo);
//PPS
memset(&avFrameInfo, 0x00, sizeof(EASY_AV_Frame));
avFrameInfo.u32AVFrameFlag = _frameType;
avFrameInfo.u32AVFrameLen = pChannel->mediaInfo.ppsLength;
avFrameInfo.u32VFrameType = frameInfo->type;
avFrameInfo.pBuffer = (unsigned char*)(pChannel->mediaInfo.pps);
EasyRtspServer_PushFrame(pChannel->channelHandle, &avFrameInfo);
#endif
}
#endif
memset(&avFrameInfo, 0x00, sizeof(EASY_AV_Frame));
avFrameInfo.u32AVFrameFlag = _frameType;
//rtsp流
avFrameInfo.u32AVFrameLen = frameInfo->length - 4;
avFrameInfo.u32VFrameType = frameInfo->type;
avFrameInfo.pBuffer = (unsigned char*)(pChannel->mediaInfo.sps);
if (bSendSpsAndPps)
{
if (pChannel->mediaInfo.videoCodec == EASY_SDK_VIDEO_CODEC_H264)
{
#if 1
unsigned char* frameData = (unsigned char*)pBuf;
for (unsigned int idx = 0; idx < frameInfo->length - 1; idx++)
{
if (((unsigned char)(frameData)idx == 0x00) &&
((unsigned char)frameDataidx + 1 == 0x01) &&
(((unsigned char)frameDataidx + 2 & 0x1f) == 0x5)) //I帧
{
avFrameInfo.u32AVFrameLen = frameInfo->length - idx - 2;
avFrameInfo.pBuffer = (unsigned char*)(frameData + idx + 2);
break;
}
}
#else
int trim_length = 4 + pChannel->mediaInfo.spsLength;
trim_length += 4 + pChannel->mediaInfo.ppsLength;
if (pChannel->mediaInfo.seiLength > 0) trim_length += 4 + pChannel->mediaInfo.seiLength;
trim_length += 4;
avFrameInfo.u32AVFrameLen = frameInfo->length ;
avFrameInfo.pBuffer = (unsigned char*)((char*)pBuf);
#endif
#if 0
qDebug("h264 I frame:%d\n",avFrameInfo.u32AVFrameLen);
QByteArray arr((char*)avFrameInfo.pBuffer,50);
QString hex = arr.toHex(' ');
qDebug().noquote() <<"前五十个字节:"<< hex;
#endif
}
else if (pChannel->mediaInfo.videoCodec == EASY_SDK_VIDEO_CODEC_H265)
{
#if 0
unsigned char* frameData = (unsigned char*)pBuf;
for (unsigned int idx = 0; idx < frameInfo->length - 1; idx++)
{
if (((unsigned char)(frameData)idx == 0x00) &&
((unsigned char)frameDataidx + 1 == 0x01) &&
((((unsigned char)frameDataidx + 2 & 0x7f) == 19||
((unsigned char)frameDataidx + 2 & 0x7f) == 20))) //I帧
{
avFrameInfo.u32AVFrameLen = frameInfo->length - idx - 2;
avFrameInfo.pBuffer = (unsigned char*)(frameData + idx + 2);
break;
}
}
#else
int trim_length = 4 + pChannel->mediaInfo.vpsLength;
trim_length += 4 + pChannel->mediaInfo.spsLength;
trim_length += 4 + pChannel->mediaInfo.ppsLength;
if (pChannel->mediaInfo.seiLength > 0)
trim_length += 4 + pChannel->mediaInfo.seiLength;
avFrameInfo.u32AVFrameLen = frameInfo->length-trim_length;
avFrameInfo.pBuffer = (unsigned char*)((char*)pBuf+trim_length);
#endif
qDebug("h265 I frame:%d\n",avFrameInfo.u32AVFrameLen);
QByteArray arr((char*)avFrameInfo.pBuffer,50);
QString hex = arr.toHex(' ');
qDebug().noquote() <<"前十个字节:"<< hex;
qDebug("\n");
}
}
else
{
avFrameInfo.pBuffer = (unsigned char*)((char*)pBuf + 4);
#if 0
qDebug("h264 P frame:%d\n",avFrameInfo.u32AVFrameLen);
QByteArray arr((char*)avFrameInfo.pBuffer,50);
QString hex = arr.toHex(' ');
qDebug().noquote() <<"前五十个字节:"<< hex;
#endif
}
EasyRtspServer_PushFrame(pChannel->channelHandle, &avFrameInfo);
}else if(_frameType == EASY_SDK_AUDIO_FRAME_FLAG){
EASY_AV_Frame avFrameInfo;
memset(&avFrameInfo, 0x00, sizeof(EASY_AV_Frame));
avFrameInfo.u32AVFrameFlag = _frameType;
avFrameInfo.u32AVFrameLen = frameInfo->length;
avFrameInfo.u32VFrameType = frameInfo->type;
avFrameInfo.pBuffer = (unsigned char*)pBuf;
EasyRtspServer_PushFrame(pChannel->channelHandle, &avFrameInfo);
}else if(_frameType == EASY_SDK_EVENT_FRAME_FLAG){
}else if(_frameType == EASY_SDK_MEDIA_INFO_FLAG){
EASY_MEDIA_INFO_T* mediainfo = (EASY_MEDIA_INFO_T*)pBuf;
if (pBuf != NULL)
{
memcpy(mediainfo, pBuf, sizeof(EASY_MEDIA_INFO_T));
#if 0
printf("Source;%s Get MediaInfo: video:%u fps:%u audio:%u channel:%u sampleRate:%u spslen: %d ppslen:%d\n",
pChannel->url,
mediainfo->u32VideoCodec, mediainfo->u32VideoFps,
mediainfo->u32AudioCodec, mediainfo->u32AudioChannel, mediainfo->u32AudioSamplerate,
mediainfo->u32SpsLength, mediainfo->u32PpsLength);
#endif
}
if (NULL != mediainfo && NULL != pChannel)
{
pChannel->mediaInfo.audioChannel = mediainfo->u32AudioChannel;
pChannel->mediaInfo.audioSampleRate = mediainfo->u32AudioSamplerate;
pChannel->mediaInfo.audioBitsPerSample = mediainfo->u32AudioBitsPerSample;
pChannel->mediaInfo.vpsLength = mediainfo->u32VpsLength;
if (pChannel->mediaInfo.vpsLength > 0) memcpy(pChannel->mediaInfo.vps, mediainfo->u8Vps, mediainfo->u32VpsLength);
pChannel->mediaInfo.spsLength = mediainfo->u32SpsLength;
if (pChannel->mediaInfo.spsLength > 0) memcpy(pChannel->mediaInfo.sps, mediainfo->u8Sps, mediainfo->u32SpsLength);
pChannel->mediaInfo.ppsLength = mediainfo->u32PpsLength;
if (pChannel->mediaInfo.ppsLength > 0) memcpy(pChannel->mediaInfo.pps, mediainfo->u8Pps, mediainfo->u32PpsLength);
//最后再设置编码类型
pChannel->mediaInfo.audioCodec = mediainfo->u32AudioCodec;
pChannel->mediaInfo.videoCodec = mediainfo->u32VideoCodec;
}
}
return 0;
}
EasyRtspServer库回调函数
主要实现的功能包括:
处理打开媒体流;
处理开始发送流;
处理停止发送流;
处理关闭流;
处理流控制.
int CALLBACK __EasyRtspServer_Callback(EASY_RTSPSERVER_STATE_T serverStatus, const char *resourceName,
EASY_CHANNEL_HANDLE *channelHandle,
EASY_RTSPSERVER_MEDIA_INFO_T *mediaInfo,
EASY_PLAY_CONTROL_INFO_T *playCtrlInfo,
void *userPtr, void *channelPtr)
{
int ret = -1;
qDebug("EasyStreamingServer New Request: %s status%d\n", resourceName, serverStatus );
if (EASY_CHANNEL_OPEN_STREAM == serverStatus)
{
EASY_CHANNEL_HANDLE pChannelHandle = NULL;
qDebug("EasyStreamingServer New Request: %s\n", resourceName);
char szURL260 = { 0 };
#if 0
char filenameMAX_PATH = { 0 };
sprintf(filename, "%s.txt", resourceName);
FILE* f = fopen(filename, "rb");
if (NULL == f)
{
return 0;
}
fgets(szURL, sizeof(szURL), f);
fclose(f);
#else
strcpy(szURL, resourceName);
#endif
int len = (int)strlen(szURL);
for (int i = len - 1; i > 0; i--)
{
if (((unsigned char)szURLi == '\r') ||
((unsigned char)szURLi == '\n'))
{
szURLi = '\0';
}
}
#if 0
if ((int)strlen(szURL) < 3) return 0;
#endif
RTSP_CHANNEL_T *pRtspChannel = new RTSP_CHANNEL_T;
if (NULL == pRtspChannel) return -1;
memset(pRtspChannel, 0x00, sizeof(RTSP_CHANNEL_T));
strcpy(pRtspChannel->url, szURL);// resourceName);
#ifdef EASYSTREALCLIENT
EasyStreamClient_Init(&pRtspChannel->rtspClientHandle, 2);
if (!pRtspChannel->rtspClientHandle)
{
printf("EasyStreamClient_Init Initial fail.\n");
return 0;
}
EasyStreamClient_SetAudioEnable(pRtspChannel->rtspClientHandle, 1);
EasyStreamClient_SetCallback(pRtspChannel->rtspClientHandle, __EasyStreamClientCallBack);
unsigned int mediaType = EASY_SDK_VIDEO_FRAME_FLAG | EASY_SDK_AUDIO_FRAME_FLAG;
EasyStreamClient_OpenStream(pRtspChannel->rtspClientHandle, pRtspChannel->url, EASY_RTP_OVER_TCP, (void*)pRtspChannel, 1000, 1, 1);
#else
SimpleStreamClient_Init(&pRtspChannel->rtspClientHandle, 0);
if (!pRtspChannel->rtspClientHandle)
{
printf("EasyStreamClient_Init Initial fail.\n");
return 0;
}
SimpleStreamClient_SetCallback(pRtspChannel->rtspClientHandle,__SimpleStreamClientCallBack);
SimpleStreamClient_OpenStream(pRtspChannel->rtspClientHandle,pRtspChannel->url,Simple_RTP_OVER_TCP,(void*)pRtspChannel,1000,1,1);
#endif
for (int i=0; i<10*2; i++) //等待5秒
{
if (pRtspChannel->s_mediaInfo.u32VideoCodec>0) break;// && pRtspChannel->mediaInfo.audioCodec) break;
QThread::msleep(100);
}
//设置媒体信息
mediaInfo->videoCodec = pRtspChannel->s_mediaInfo.u32VideoCodec;
mediaInfo->audioCodec = pRtspChannel->s_mediaInfo.u32AudioCodec;
mediaInfo->audioSampleRate = pRtspChannel->s_mediaInfo.u32AudioSamplerate;
mediaInfo->audioChannel = pRtspChannel->s_mediaInfo.u32AudioChannel;
mediaInfo->audioBitsPerSample = pRtspChannel->s_mediaInfo.u32AudioBitsPerSample == 0 ? 16 : pRtspChannel->s_mediaInfo.u32AudioBitsPerSample;
qDebug("Get Source:%s videoCodec:%d,audioCodec:%d,audioSampleRate=%d,pRtspChannel=%x\n",
pRtspChannel->url,
pRtspChannel->s_mediaInfo.u32VideoCodec,
pRtspChannel->s_mediaInfo.u32AudioCodec,
pRtspChannel->s_mediaInfo.u32AudioSamplerate,
pRtspChannel);
#if 1
//此处可指定sps & pps, 也可以不指定
mediaInfo->vpsLength = pRtspChannel->s_mediaInfo.u32VpsLength;
memcpy(mediaInfo->vps, pRtspChannel->s_mediaInfo.u8Vps, mediaInfo->vpsLength);
mediaInfo->spsLength = pRtspChannel->s_mediaInfo.u32SpsLength;
memcpy(mediaInfo->sps, pRtspChannel->s_mediaInfo.u8Sps, mediaInfo->spsLength);
mediaInfo->ppsLength = pRtspChannel->s_mediaInfo.u32PpsLength;
memcpy(mediaInfo->pps, pRtspChannel->s_mediaInfo.u8Pps, mediaInfo->ppsLength);
#endif
mediaInfo->videoFps = 25;
if (pRtspChannel->s_mediaInfo.u32VideoCodec > 0)
{
qDebug("create channel: %s\n", resourceName);
//创建通道
EasyRtspServer_CreateChannel(resourceName, &pChannelHandle, (void *)pRtspChannel);
*channelHandle = pChannelHandle;
pRtspChannel->channelHandle = pChannelHandle;
ret = 0;
}
else //关闭源
{
qDebug("Get Source%s%s sdp fail.\n", resourceName, pRtspChannel->url);
#ifdef EASYSTREALCLIENT
EasyStreamClient_Deinit(pRtspChannel->rtspClientHandle);
#else
SimpleStreamClient_Deinit(pRtspChannel->rtspClientHandle);
#endif
delete pRtspChannel;
pRtspChannel = NULL;
}
}
if (EASY_CHANNEL_START_STREAM == serverStatus)
{
RTSP_CHANNEL_T *pRtspChannel = (RTSP_CHANNEL_T *)channelPtr;
pRtspChannel->status = 0x01; //开始发送流
}
if (EASY_CHANNEL_STOP_STREAM == serverStatus)
{
RTSP_CHANNEL_T *pRtspChannel = (RTSP_CHANNEL_T *)channelPtr;
pRtspChannel->status = 0x00; //停止发送流
}
else if (EASY_CHANNEL_CLOSE_STREAM == serverStatus)
{
qDebug("#######################Close Stream: %s\n", resourceName);
RTSP_CHANNEL_T *pRtspChannel = (RTSP_CHANNEL_T *)channelPtr;
if (NULL != pRtspChannel)
{
if (NULL != pRtspChannel->rtspClientHandle)
{
#ifdef EASYSTREALCLIENT
EasyStreamClient_Deinit(pRtspChannel->rtspClientHandle);
#else
SimpleStreamClient_Deinit(pRtspChannel->rtspClientHandle);
#endif
}
qDebug("Close Source OK: %s %s\n", resourceName, pRtspChannel->url);
EasyRtspServer_DeleteChannel(&pRtspChannel->channelHandle);
delete pRtspChannel;
pRtspChannel = NULL;
}
ret = 0;
}
else if (EASY_CHANNEL_PLAY_CONTROL == serverStatus)
{
//以下为播放控制, 用于录像回放时使用
#if 0
printf("RTSP_CHANNEL_PLAY_CONTROL: %s\n", resourceName);
RTSP_CHANNEL_T *pRtspChannel = (RTSP_CHANNEL_T *)channelPtr;
if (NULL != pRtspChannel)
{
char szMediaType16 = {0};
if (EASY_SDK_VIDEO_FRAME_FLAG == playCtrlInfo->mediaType) strcpy(szMediaType, "Video");
else if (EASY_SDK_AUDIO_FRAME_FLAG == playCtrlInfo->mediaType) strcpy(szMediaType, "Audio");
else if (EASY_SDK_EVENT_FRAME_FLAG == playCtrlInfo->mediaType) strcpy(szMediaType, "Metadata");
if (EASY_PLAY_CTRL_CMD_GET_DURATION == playCtrlInfo->ctrlCommand)
printf("GetDuration... %s\n", szMediaType);
else if (EASY_PLAY_CTRL_CMD_PAUSE == playCtrlInfo->ctrlCommand)
printf("Pause...%s\n", szMediaType);
else if (EASY_PLAY_CTRL_CMD_RESUME == playCtrlInfo->ctrlCommand)
printf("Resume...%s\n", szMediaType);
else if (EASY_PLAY_CTRL_CMD_SCALE == playCtrlInfo->ctrlCommand)
printf("Scale%s: %.2f\n", szMediaType, playCtrlInfo->scale);
else if (EASY_PLAY_CTRL_CMD_SEEK_STREAM == playCtrlInfo->ctrlCommand)
printf("Seek%s: %s - %s \n", szMediaType, playCtrlInfo->startTime, playCtrlInfo->endTime);
}
#endif
}
return ret;
}
五 运行日志
RTSP服务启动成功...
rtsp://127.0.0.1: 8554 /ch1-7
rtsp://127.0.0.1: 8554 /rtsp地址
EasyStreamingServer New Request: ch1 status1
EasyStreamingServer New Request: ch1
Get Source:ch1 videoCodec:28,audioCodec:0,audioSampleRate=0,pRtspChannel=9c000d20
create channel: ch1
EasyStreamingServer New Request: ch1 status6
EasyStreamingServer New Request: ch1 status6
EasyStreamingServer New Request: ch1 status2
EasyStreamingServer New Request: ch1 status6
EasyStreamingServer New Request: ch1 status6
EasyStreamingServer New Request: ch1 status4
EasyStreamingServer New Request: ch1 status5
#######################Close Stream: ch1
Close Source OK: ch1 ch1