简单来说,RTSP协议是一种基于命令形式的协议,对于服务端S和客户端C,两者的命令交互过程为:
由于网上已有详细介绍,不再重复,可转至
live555 RTSP客户端源码分析学习。
数据流的接收都在DummySink::afterGettingFrame函数里,
void DummySink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
struct timeval presentationTime, unsigned /*durationInMicroseconds*/)
这个函数自带的第一个参数就是当前的数据流的大小,数据流指针为DummySink的成员变量u_int8_t* fReceiveBuffer,这个变量作为参数在continuePlaying()函数中被调用,进行了数据的填充,填充完毕后,调用回调函数,也就是上面的afterGettingFrame来进行通知,那么我们只需要在这个函数中处理fReceiveBuffer即可。
void DummySink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
struct timeval presentationTime, unsigned /*durationInMicroseconds*/) {
// We've just received a frame of data. (Optionally) print out information about it:
#ifdef DEBUG_PRINT_EACH_RECEIVED_FRAME
if (fStreamId != NULL) envir() << "Stream \"" << fStreamId << "\"; ";
envir() << fSubsession.mediumName() << "/" << fSubsession.codecName() << ":\tReceived " << frameSize << " bytes";
if (numTruncatedBytes > 0) envir() << " (with " << numTruncatedBytes << " bytes truncated)";
char uSecsStr[6+1]; // used to output the 'microseconds' part of the presentation time
sprintf(uSecsStr, "%06u", (unsigned)presentationTime.tv_usec);
envir() << ".\tPresentation time: " << (unsigned)presentationTime.tv_sec << "." << uSecsStr;
if (fSubsession.rtpSource() != NULL && !fSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) {
envir() << "!"; // mark the debugging output to indicate that this presentation time is not RTCP-synchronized
}
envir() << "\n";
#endif
//当时做的是用的公司内部的解码器,暂时找不到合适的解码器,就直接把rtsp流写到文件中
//如果是使用解码器,那么需要创建一个解码线程,在里面处理解码,然后把解码数据显示到窗口上
static bool bFirstFrame = true;
if(!strcmp(fSubsession.mediumName(), "video"))
{
unsigned char chStart[4] = {0x00, 0x00, 0x00, 0x01};
if(bFirstFrame)
{
unsigned int numSPropRecords;
SPropRecord *sps = parseSPropParameterSets(fSubsession.fmtp_spropparametersets(), numSPropRecords);
FILE *fp = fopen("test.264", "a+b");
if(fp)
{
fwrite(chStart, 4, 1, fp);
fwrite(sps[0].sPropBytes, sps[0].sPropLength, 1, fp);
fwrite(chStart, 4, 1, fp);
fwrite(sps[1].sPropBytes, sps[1].sPropLength, 1, fp);
fclose(fp);
}
delete [] sps;
bFirstFrame = false;
}
char *pBuf = (char *)fReceiveBuffer;
FILE *fp = fopen("test.264", "a+b");
if(fp)
{
fwrite(chStart, 4, 1, fp);
fwrite(fReceiveBuffer, frameSize, 1, fp);
fclose(fp);
}
}
// Then continue, to request the next frame of data:
continuePlaying();
}
此处简单处理,我们直接将RTSP流转换为H264格式的视频文件,写到本地,可以直接使用VLC等播放器进行播放。当然,如果是要显示到自己的窗口上,那么需要用到解码器,将rtsp流转为RGB或者YUV数据,然后绘制到窗口上,实时显示的性能主要受到解码器的影响。(这种方式可以直接参考调用VLC库实现,几行代码就能达到一样的效果,但是性能方面,拉流速度慢于live555,所以大部分公司都是使用自己的解码库,或者在其他开源库上封装的解码库,来实现这一部分流程)
注意:整个RTSP的播放都是在非主线程上进行处理的,因为需要让当前线程进入循环等待状态来与服务端保持一个通信,接收来自服务端的数据,然后,如果需要解码,则另外创建一个线程,解码完成后的数据,通过SendMessage抛送到UI线程的窗口上去绘制。
附上代码连接:https://download.csdn.net/download/bajianxiaofendui/10714639