流媒体学习笔记3(live555的source-sink)
来源:互联网 发布:dd linux iso 编辑:程序博客网 时间:2024/04/30 14:21
上次提到source-sink,也就是SDP消息的组装过程,在OnDemandServerMediaSubsession::sdpLines()中创建了临时的FileSource和RTPSink,先看下这两个createNew
FramedSource* H264VideoFileServerMediaSubsession::createNewStreamSource(unsigned /*clientSessionId*/, unsigned& estBitrate) { estBitrate = 500; // kbps, estimate // Create the video source: ByteStreamFileSource* fileSource = ByteStreamFileSource::createNew(envir(), fFileName); if (fileSource == NULL) return NULL; fFileSize = fileSource->fileSize(); // Create a framer for the Video Elementary Stream: return H264VideoStreamFramer::createNew(envir(), fileSource);}RTPSink* H264VideoFileServerMediaSubsession::createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* /*inputSource*/) { return H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);}
先看source部分,可以看到,实际文件资源定义的部分是ByteStreamFileSource,它的倒继承关系为:
ByteStreamFileSource::FramedFileSource::FramedSource::MediaSource
而返回的却是H264VideoStreamFramer,我们看下它的倒继承关系为:
H264VideoStreamFramer::MPEGVideoStreamFramer::FramedFilter::FramedSource::MediaSource
其实ByteStreamFileSource确实是最初始打开文件获得Byte流的地方,但是单单知道一个Byte-Source还不够,一个最终的处理h264文件,分析格式的Source才是完整的。重点来看下分析h264格式。在H264VideoStreamFramer 中看到的都是一些SPS(序列参数集Sequence Parameter Set)和 PPS(图像参数集Picture Parameter Set)的 get,set基本操作,具体的解析部分在哪呢?哈,H264VideoStreamFramer的构造函数中有这么一句new H264VideoStreamParser(this, inputSource, includeStartCodeInOutput),没错,Parser就是一个解析器。看下它的继承关系:
H264VieoStreamParser::MPEGVideoStreamParser::StreamParser
先看StreamParser,大多都是一些byte操作,有一个成员FramedSource* fInputSource,很明显fInputSource的赋值从H264VideoStreamFramer::createNew(envir(), fileSource)一直传递过来,所以它是一个ByteStreamFileSource的实例。文件读取部分是通过get?Byte(),getBits()来读取fInputSource。
再看下MPEGVideoStreamParser,其中有一个纯虚函数parse(),很明显它是H264VideoStreamParser做最终的分析的接口。看成员函数saveByte(),save4Bytes()的内容:
void saveByte(u_int8_t byte) { if (fTo >= fLimit) { // there's no space left ++fNumTruncatedBytes; return; } *fTo++ = byte; }
可以在H264VideoStreamParser::parse()中看到通过调用get?Byte(),getBits()将值传入saveByte(),save4Bytes(),实际上就是获得Byte-Source保存到MPEGVideoStreamParser的成员 fTo 中。而MPEGVideoStreamFramer的doGetNextFrame正是用来调用整个parse()的过程
void MPEGVideoStreamFramer::doGetNextFrame() { fParser->registerReadInterest(fTo, fMaxSize); continueReadProcessing();}void MPEGVideoStreamFramer::continueReadProcessing(void* clientData, unsigned char* /*ptr*/, unsigned /*size*/, struct timeval /*presentationTime*/) { MPEGVideoStreamFramer* framer = (MPEGVideoStreamFramer*)clientData; framer->continueReadProcessing();}void MPEGVideoStreamFramer::continueReadProcessing() { unsigned acquiredFrameSize = fParser->parse(); if (acquiredFrameSize > 0) { // We were able to acquire a frame from the input. // It has already been copied to the reader's space. fFrameSize = acquiredFrameSize; fNumTruncatedBytes = fParser->numTruncatedBytes(); // "fPresentationTime" should have already been computed. // Compute "fDurationInMicroseconds" now: fDurationInMicroseconds = (fFrameRate == 0.0 || ((int)fPictureCount) < 0) ? 0 : (unsigned)((fPictureCount*1000000)/fFrameRate);#ifdef DEBUG fprintf(stderr, "%d bytes @%u.%06d, fDurationInMicroseconds: %d ((%d*1000000)/%f)\n", acquiredFrameSize, fPresentationTime.tv_sec, fPresentationTime.tv_usec, fDurationInMicroseconds, fPictureCount, fFrameRate);#endif fPictureCount = 0; // Call our own 'after getting' function. Because we're not a 'leaf' // source, we can call this directly, without risking infinite recursion. afterGetting(this); } else { // We were unable to parse a complete frame from the input, because: // - we had to read more data from the source stream, or // - the source stream has ended. }}
也就是说文件数据从ByteStreamFileSource读入,在H264VideoStreamFramer处理,计算出帧的持续时间后,调用FrameSource的afterGetting(),afterGetting()回调了FrameSource::getNextFrame()中初始化的一个函数指针,而调用getNextFrame()是在Sink中完成的,也就是说afterGetting()最终会调用RTPSink的函数。
好的,这里source部分的分析先停下,我们去寻找SubSession是怎么来获得SDP信息的,我们回到最初OnDemandServerMediaSubsession::sdpLines()中的setSDPLinesFromRTPSink(dummyRTPSink, inputSource, estBitrate)。观察这个函数可以知道核心获得sink得到的AuxSDPLine,再经过包装成SDPLine。核心在这:
char const* rangeLine = rangeSDPLine(); char const* auxSDPLine = getAuxSDPLine(rtpSink, inputSource);
H264VideoFileServerMediaSubsession 有重载函数getAuxSDPLine,所以这里调用的应该是下面的
char const* H264VideoFileServerMediaSubsession::getAuxSDPLine(RTPSink* rtpSink, FramedSource* inputSource) { if (fAuxSDPLine != NULL) return fAuxSDPLine; // it's already been set up (for a previous client) if (fDummyRTPSink == NULL) { // we're not already setting it up for another, concurrent stream // Note: For H264 video files, the 'config' information ("profile-level-id" and "sprop-parameter-sets") isn't known // until we start reading the file. This means that "rtpSink"s "auxSDPLine()" will be NULL initially, // and we need to start reading data from our file until this changes. fDummyRTPSink = rtpSink; // Start reading the file: fDummyRTPSink->startPlaying(*inputSource, afterPlayingDummy, this); // Check whether the sink's 'auxSDPLine()' is ready: checkForAuxSDPLine(this); } envir().taskScheduler().doEventLoop(&fDoneFlag); return fAuxSDPLine;}
注释的意思是开始的时候不知道H264视频文件的配置信息,直到播放一段后才可以获得SDP。继续追踪startPlaying()到continuePlaying()来到
Boolean H264VideoRTPSink::continuePlaying() { // First, check whether we have a 'fragmenter' class set up yet. // If not, create it now: if (fOurFragmenter == NULL) { fOurFragmenter = new H264FUAFragmenter(envir(), fSource, OutPacketBuffer::maxSize, ourMaxPacketSize() - 12/*RTP hdr size*/); } else { fOurFragmenter->reassignInputSource(fSource); } fSource = fOurFragmenter; // Then call the parent class's implementation: return MultiFramedRTPSink::continuePlaying();}
这里将fSource又进行了一次包装,认真查看H264FUAFragmenter知道,他是处理Nal Unit的。继续跟踪continuPlaying()->buildAndSendPacket()->packFrame()->fSource,终于到了我们最想看到的部分:
fSource->getNextFrame(fOutBuf->curPtr(), fOutBuf->totalBytesAvailable(), afterGettingFrame, this, ourHandleClosure, this);
这是一次获取一个完整帧进行转发。getNextFrame()里面调用的是dogetNextFrame(),H264FUAFragmenter的dogetNextFrame的实现:1.没数据时调用fInputSource->getNextFrame(),fInputSource是H264VideoStreamFramer,H264VideoStreamFramer的getNextFrame()会调用H264VideoStreamParser的parser(),parser()又调用ByteStreamFileSource获取数据,然后分析,parser()完成后又回调到dogetNextFrame。2.这时候有数据了,则进行数据分析。
总结下:文件数据从ByteStreamFileSource读入,经H264VideoStreamFramer处理传给H264FUAFragmenter.ByteStreamFileSource返回给H264VideoStreamFramer一段数据,H264VideoStreamFramer返回一个H264FUAFragmenter一个Nal unit .
总结source-sink:一步一步的调用XXXFramer做文件读取和包装,真正的source其实是ByteStreamFileSource,数据最后会移交到MultiFramedRTPSink做RTP包装。
个人总结:学习阶段,表示看源码看到晕死,连博文也看的晕死。上面鬼话只供参考
- 流媒体学习笔记3(live555的source-sink)
- live555杂谈系列(一)---source,sink简介
- Flink学习笔记 --- DataSet Source and Sink
- 流媒体Live555学习(一)
- 流媒体Live555学习(二)
- 流媒体Live555学习(三)
- 流媒体Live555学习(一)
- 流媒体Live555学习(二)
- 流媒体Live555学习(三)
- 流媒体学习笔记1(live555+vs2010编译)
- 流媒体学习笔记2(live555中的Session)
- Flume NG 学习笔记(十) Transaction、Sink、Source和Channel开发
- Flume学习笔记 --- Flume内置source,channel, sink介绍
- Source和Sink:可以把source理解为发送端的流,sink理解为接受端。MediaSink是各种类型的Sink的基类,MediaSource是各种类型Source的基类,各种类型的流媒体格式
- live555学习笔记3
- Flume笔记二之source,channel,sink
- Flume学习进阶(一):source、channel、sink、processor、interceptor等组件列表说明及包含的类型介绍
- live555学习笔记(二)
- GetLastInputInfo
- FTP报错 553 Could not create file
- java实用技巧
- 数据结构与程序设计 练习4.1 E1
- RHEL 6.0 为局域网提供上网代理,并做相应的限制。
- 流媒体学习笔记3(live555的source-sink)
- 作业 Exercise 4.2 E2(a)和Exercise4.3E2
- 回调函数
- netty与protobuf与node.js
- 安卓开发36:layout对齐属性总结
- SOAP,WebService
- eclipse+axis2+webservice开发实例
- Java之异常
- 开发笔记 那些年追过的图片(一): image的读取