live555源码分析----mpg文件的处理(续)

 前一篇文章对mpg文件处理的分析中,有个一个比较严重的错误,因为有些重要的细节没有注意到。mpg文件是音视频交错排列的,如果需要读取的是视频数据,但当前文件位置却是音频数据该怎么办?前面分析时说,将把遇到的音频数据保存到缓存中,直到读取到视频数据,live555中并非如此处理的。


先简单的说明一下mpg处理过程涉及的几个类的作用
MPEG1or2Demux, 对应一个session,完成文件的解复用操作。其读取文件的过程是通过ByteStreamFileSource实现的
MPEG1or2DemuxedElementaryStream,作为source,对应一个流,一个或多个实例对应一个MPEG1or2Demux
MPEG1or2DemuxedServerMediaSubsession,作为subsession
MPEG1or2FileServerDemux, 一个服务类,整个程序只有一个实例,MPEG1or2Demux、MPEG1or2DemuxedElementaryStream、MPEG1or2DemuxedServerMediaSubsession三个类将通过本类进行联系。完成的工作如下:
1)创建subsession实例(MPEG1or2DemuxedServerMediaSubsession);
2)为每一个session创建MPEG1or2Demux实例,用于解复用。
3)创建source实例(MPEG1or2DemuxedElementaryStream), subsession中


再来看一个重要的结构体OutputDescriptor,它被定义在MPEG1or2Demux类中,文件中的每一个流对应一个OutputDescriptor实例

  1. // A descriptor for each possible stream id tag:  
  2. typedef struct OutputDescriptor {  
  3.   // input parameters  
  4.   unsigned char* to; unsigned maxSize;  
  5.   FramedSource::afterGettingFunc* fAfterGettingFunc;  
  6.   void* afterGettingClientData;  
  7.   FramedSource::onCloseFunc* fOnCloseFunc;  
  8.   void* onCloseClientData;  
  9.   
  10.   
  11.   // output parameters  
  12.   unsigned frameSize; struct timeval presentationTime;  
  13.   class SavedData; // forward  
  14.   SavedData* savedDataHead;  
  15.   SavedData* savedDataTail;  
  16.   unsigned savedDataTotalSize;  
  17.   
  18.   
  19.   // status parameters  
  20.   Boolean isPotentiallyReadable;  
  21.   Boolean isCurrentlyActive;  
  22.   Boolean isCurrentlyAwaitingData;  
  23. } OutputDescriptor_t;  
  24. OutputDescriptor_t fOutput[256];  


  
需要注意以下3个成员:
isPotentiallyReadable,对应的流存在,即存在对应流的MPEG1or2DemuxedElementaryStream实例
isCurrentlyActive,对应的流是活动的,当从第一次从这个流中读取数据时就置为True, 停止读取后置为False
isCurrentlyAwaitingData,当前正需要从对应的流中读取数据。从文件中读取数据并分析后,通过此标志判断读取到的数据是否是当前需要的流数据,如果不是则需要数据存在到缓存中


我们来看MPEG1or2Demux::getNextFrame函数

  1. void MPEG1or2Demux::getNextFrame(u_int8_t streamIdTag,  
  2.                  unsigned char* to, unsigned maxSize,  
  3.                  FramedSource::afterGettingFunc* afterGettingFunc,  
  4.                  void* afterGettingClientData,  
  5.                  FramedSource::onCloseFunc* onCloseFunc,  
  6.                  void* onCloseClientData) {  
  7.   // First, check whether we have saved data for this stream id:  
  8.   //从缓存中读取数据  
  9.   if (useSavedData(streamIdTag, to, maxSize,  
  10.            afterGettingFunc, afterGettingClientData)) {  
  11.     return;  
  12.   }  
  13.   
  14.   
  15.   // Then save the parameters of the specified stream id:  
  16.   // 将信息保存到流对应的OutputDescriptor_t实例中  
  17.   registerReadInterest(streamIdTag, to, maxSize,  
  18.                afterGettingFunc, afterGettingClientData,  
  19.                onCloseFunc, onCloseClientData);  
  20.   
  21.   
  22.   // Next, if we're the only currently pending read, continue looking for data:  
  23.   // 进一步处理,将从文件中读取数据  
  24.   if (fNumPendingReads == 1 || fHaveUndeliveredData) {  
  25.     fHaveUndeliveredData = 0;  
  26.     continueReadProcessing();  
  27.   } // otherwise the continued read processing has already been taken care of  
  28. }  



useSavedData函数从缓存中读取数据,没什么好说的。简单看一下registerReadInterest函数

  1. void MPEG1or2Demux::registerReadInterest(u_int8_t streamIdTag,  
  2.                      unsigned char* to, unsigned maxSize,  
  3.                      FramedSource::afterGettingFunc* afterGettingFunc,  
  4.                      void* afterGettingClientData,  
  5.                      FramedSource::onCloseFunc* onCloseFunc,  
  6.                      void* onCloseClientData) {  
  7.   struct OutputDescriptor& out = fOutput[streamIdTag];  
  8.   
  9.   
  10.   // Make sure this stream is not already being read:  
  11.   if (out.isCurrentlyAwaitingData) {  
  12.     envir() << "MPEG1or2Demux::registerReadInterest(): attempt to read stream id "  
  13.         << (void*)streamIdTag << " more than once!\n";  
  14.     envir().internalError();  
  15.   }  
  16.   
  17.   
  18.   out.to = to; out.maxSize = maxSize;  
  19.   out.fAfterGettingFunc = afterGettingFunc;  
  20.   out.afterGettingClientData = afterGettingClientData;  
  21.   out.fOnCloseFunc = onCloseFunc;  
  22.   out.onCloseClientData = onCloseClientData;  
  23.   out.isCurrentlyActive = True;  //当前流处理活动中  
  24.   out.isCurrentlyAwaitingData = True;  //当前流需要读取数据  
  25.   // out.frameSize and out.presentationTime will be set when a frame's read  
  26.   
  27.   
  28.   ++fNumPendingReads;  //等待读取数据的流计数  
  29. }  




registerReadInterest函数中,最后三条语句应注意。isCurrentlyAwaitingData为True表示对应流需要读取数据,fNumPendingReads表示等读取数据的流计数。


现在再来看MPEG1or2Demux::getNextFrame函数中后面的if条件语句
  1. if (fNumPendingReads == 1 || fHaveUndeliveredData) {  
  2.   fHaveUndeliveredData = 0;  
  3.   continueReadProcessing();  
  4. }  


   条件一fNumPendingReads == 1,fNumPendingReads表示未读取到数据的流的个数。为什么同时读取数据的流会超过一个呢?对于mpg文件来说,需要读取音频时当前文件位置可能正好是一个视频包,这时无法读取到所需要的数据,就会递增fNumPendingReads变量。条件二fHaveUndeliveredData,表示前面的处理过程中,某一个流未能如愿读取到需要的数据。


   上面的两个条件,我觉得有点问题,fNumPendingReads = 0是不可能的, fNumPendingReads>1时,表示前面的处理过程中,某一个流未能如愿读取到需要的数据,这时fHaveUndeliveredData就应该为True,这样的话if语句是不是永远为真呢?这个疑问有待确定。


继续来看MPEG1or2Demux::continueReadProcessing函数的实现

  1. void MPEG1or2Demux::continueReadProcessing() {  
  2.   while (fNumPendingReads > 0) {  
  3.     unsigned char acquiredStreamIdTag = fParser->parse();  
  4.   
  5.   
  6.     if (acquiredStreamIdTag != 0) {  
  7.       // We were able to acquire a frame from the input.  
  8.       struct OutputDescriptor& newOut = fOutput[acquiredStreamIdTag];  
  9.       newOut.isCurrentlyAwaitingData = False;  
  10.         // indicates that we can be read again  
  11.         // (This needs to be set before the 'after getting' call below,  
  12.         //  in case it tries to read another frame)  
  13.   
  14.   
  15.       // Call our own 'after getting' function.  Because we're not a 'leaf'  
  16.       // source, we can call this directly, without risking infinite recursion.  
  17.       if (newOut.fAfterGettingFunc != NULL) {  
  18.     (*newOut.fAfterGettingFunc)(newOut.afterGettingClientData,  
  19.                     newOut.frameSize, 0 /* numTruncatedBytes */,  
  20.                     newOut.presentationTime,  
  21.                     0 /* durationInMicroseconds ?????#####*/);  
  22.       --fNumPendingReads;  
  23.       }  
  24.     } else {  
  25.       // We were unable to parse a complete frame from the input, because:  
  26.       // - we had to read more data from the source stream, or  
  27.       // - we found a frame for a stream that was being read, but whose  
  28.       //   reader is not ready to get the frame right now, or  
  29.       // - the source stream has ended.  
  30.       break;  
  31.     }  
  32.   }  
  33. }  




    这个函数中有一个while循环,fParser->parse()返回当前读取到的数据的流标号,第一次循环过程的返回值必定是当前所需要的流数据,第二次循环返回值则是先前未读取到的流数据,当所有需要读取数据的流均已经读取数据后,fNumPendingReads值递减为0,循环结束。


再来看MPEGProgramStreamParser::parse函数
  1. unsigned char MPEGProgramStreamParser::parse() {  
  2.   unsigned char acquiredStreamTagId = 0;  
  3.   
  4.   
  5.   try {  
  6.     do {  
  7.       switch (fCurrentParseState) {  
  8.       case PARSING_PACK_HEADER: {  
  9.     parsePackHeader();  
  10.     break;  
  11.       }  
  12.       case PARSING_SYSTEM_HEADER: {  
  13.     parseSystemHeader();  
  14.     break;  
  15.       }  
  16.       case PARSING_PES_PACKET: {  
  17.     acquiredStreamTagId = parsePESPacket();  
  18.     break;  
  19.       }  
  20.       }  
  21.     } while(acquiredStreamTagId == 0);  
  22.   
  23.   
  24.     return acquiredStreamTagId;  
  25.   } catch (int /*e*/) {  
  26. #ifdef DEBUG  
  27.     fprintf(stderr, "MPEGProgramStreamParser::parse() EXCEPTION (This is normal behavior - *not* an error)\n");  
  28.     fflush(stderr);  
  29. #endif  
  30.     return 0;  // the parsing got interrupted  
  31.   }  
  32. }  




上面的函数中,最有catch语句,所以前面的函数调用必定会抛出异常。parsePESPacket函数返回流标识作为循环条件,所以这个函数最重要
  1. unsigned char MPEGProgramStreamParser::parsePESPacket() {  
  2.   
  3.   
  4.     ...  
  5.   
  6.   
  7.     // Check whether our using source is interested in this stream type.  
  8.     // If so, deliver the frame to him:  
  9.     MPEG1or2Demux::OutputDescriptor_t& out = fUsingDemux->fOutput[stream_id];  
  10.     if (out.isCurrentlyAwaitingData) {      //当前流正在等待数据  
  11.       unsigned numBytesToCopy;  
  12.       if (PES_packet_length > out.maxSize) {  
  13.     fUsingDemux->envir() << "MPEGProgramStreamParser::parsePESPacket() error: PES_packet_length ("  
  14.                   << PES_packet_length  
  15.                   << ") exceeds max frame size asked for ("  
  16.                   << out.maxSize << ")\n";  
  17.     numBytesToCopy = out.maxSize;  
  18.       } else {  
  19.     numBytesToCopy = PES_packet_length;  
  20.       }  
  21.   
  22.   
  23.       getBytes(out.to, numBytesToCopy);  
  24.       out.frameSize = numBytesToCopy;  
  25.   
  26.   
  27.       // set out.presentationTime later #####  
  28.       acquiredStreamIdTag = stream_id;  
  29.       PES_packet_length -= numBytesToCopy;  
  30.     } else if (out.isCurrentlyActive) {     //当前流是活动的  
  31.       // Someone has been reading this stream, but isn't right now.  
  32.       // We can't deliver this frame until he asks for it, so punt for now.  
  33.       // The next time he asks for a frame, he'll get it.  
  34.   
  35.   
  36.       restoreSavedParserState(); // so we read from the beginning next time  
  37.       fUsingDemux->fHaveUndeliveredData = True;     //有未投递的数据  
  38.       throw READER_NOT_READY;  //直接抛出异常,  
  39.     } else if (out.isPotentiallyReadable &&  //当前流需要读取数据  
  40.            out.savedDataTotalSize + PES_packet_length < 1000000 /*limit*/) {      
  41.       // Someone is interested in this stream, but hasn't begun reading it yet.  
  42.       // Save this data, so that the reader will get it when he later asks for it.  
  43.       //  
  44.       // 从文件中读取的数据,当前并不需要,所以就把它保存到存在中  
  45.       //  
  46.       unsigned char* buf = new unsigned char[PES_packet_length];  
  47.       getBytes(buf, PES_packet_length);  
  48.       MPEG1or2Demux::OutputDescriptor::SavedData* savedData  
  49.     = new MPEG1or2Demux::OutputDescriptor::SavedData(buf, PES_packet_length);  
  50.       if (out.savedDataHead == NULL) {  
  51.     out.savedDataHead = out.savedDataTail = savedData;  
  52.       } else {  
  53.     out.savedDataTail->next = savedData;  
  54.     out.savedDataTail = savedData;  
  55.       }  
  56.       out.savedDataTotalSize += PES_packet_length;  
  57.       PES_packet_length = 0;  
  58.     }  
  59.     skipBytes(PES_packet_length);  
  60.   }  
  61.   
  62.   
  63.   // Check for another PES Packet next:  
  64.   setParseState(PARSING_PES_PACKET);  
  65.   
  66.   
  67.  return acquiredStreamIdTag;  
  68. }  

我来看上面有中文注释的if结构,
第一个条件out.isCurrentlyAwaitingData为True时,表明读取到的数据正是需要读取的。这应该是正常的情况
第二个条件out.isCurrentlyActive为True时,这个条件是在表明对应的流是活动的,但当前并没有要求读取数据。这个条件只要流已经开始播放,在其停止之前都为True。这个条件处理中,将对应流的fHaveUndeliveredData标记为True,最后抛出了异常,这个异常在MPEGProgramStreamParser::parse中处理的
第三个条件out.isPotentiallyReadable为True时,保存数据到缓存中。isPotentiallyReadable是在MPEG1or2DemuxedElementaryStream类构造函数中就置为True的。


根据上面的分析,当流正在播放时,无论如何都不会将数据保存到缓冲中,只有在流的播放停止的情况下才会进行。


从上面的分析中,我们还可以发现其它一些东西,这里指明一下。当未能读取到所以的流数据时,就不会调用after函数进行处理,但是after函数中完成了一个重要的功能,每一个after调用最后都会将下一次数据处理的任务注册到任务管理器中。那不调用after函数流的处理是不是会中断呢?当然不会。例如,当前处理过程没能读取到需要的视频数据,但在下一次处理音频数据时,会把视频数据也读取了,并调用视频处理的after函数,这就是MPEG1or2Demux::continueReadProcessing() 函数中循环的作用。


一个需要进一步思考的问题:
1).按上述方式,当前需要处理的数据会推迟(直到其它流读取完数据),这样是不是会导致延时的时间比正常情况下要久,从而使得发送变慢呢?
2).out.isCurrentlyActive为True时,保存数据到缓存中,这样是不是更好些呢?
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值