live555转发DVB实时流(TS流)数据

 最近在做一个关于流媒体的项目,其需求简单描述就是把DVB上播放的电视内容,通过流媒体服务器发送到局域网上,使用户可以在手机或者PC端观看电视节目。

    虽然从网上知道Live555并不是最好的流媒体解决方案,但是想快速出Demo的话,Live555还算是一个不错的选择。之前介绍了Live555的移植,这里就不再赘述。

    闲话没有,直接捞干的。

    一、Live555需要修改的内容

    Live555需要修改的内容主要是两个方面:

    1)Source

    2)MediaSubsession

    因为从DVB上读取到的是TS格式的数据流,所以我们参考TS文件的代码。参考的两个类及其在Live555中的继承关系分别是:

    1)ByteStreamFileSource


    2)MPEG2TransportFileServerMediaSubsession


二、新创建的类及继承关系

1)ByteStreamLiveSource


2)MPEG2TransportLiveServerMediaSubsession


三、这两个类的实现

1)定义一个数据类型,用于存放从DVB中读取到的数据

    文件名称:LiveTSType.hh

    说明:因为我们读取的实时TS流数据,所以需要定时的为数据增加PAT表和PMT表,以使客户端随时能够获取TS流中的音视频格式信息。

[cpp]  view plain  copy
  1. #ifndef __LIVE_TS_BUFFER_TYPE_HH  
  2. #define __LIVE_TS_BUFFER_TYPE_HH  
  3.   
  4. #ifdef __cplusplus  
  5. extern "C"{  
  6. #endif  
  7.   
  8. #define TRANSPORT_SYNC_BYTE 0x47        // TS流同步字节  
  9. #define REC_BUF_MAX_LEN 256*1024+2*188  // 每个buffer的有效数据长度为256K字节,多出的2*188字节存放PAT表和PMT表  
  10.   
  11. typedef struct s_buffer   
  12. {  
  13.     unsigned char   buf_writing;        // buffer是否处于被写状态,buffer在写入状态不稳定,需要等待写完成才能读取  
  14.     unsigned int    buf_len;            // 写入数据的实际长度  
  15.     unsigned int    buf_read_counter;   // 记录已经读取的字节数  
  16.     unsigned char   buf_data[REC_BUF_MAX_LEN];  // 存放数据的buffer  
  17. } t_TS_Buffer;  
  18.   
  19. #ifdef __cplusplus  
  20. }  
  21. #endif  
  22.   
  23.   
  24. #endif  

2)ByteStreamLiveSource类

    文件名称:ByteStreamLiveSource.hh

[cpp]  view plain  copy
  1. #ifndef _BYTE_STREAM_LIVE_SOURCE_HH  
  2. #define _BYTE_STREAM_LIVE_SOURCE_HH  
  3.   
  4. #ifndef _FRAMED_SOURCE_HH  
  5. #include "FramedSource.hh"  
  6. #endif  
  7.   
  8. #ifndef __LIVE_TS_BUFFER_TYPE_HH  
  9. #include "LiveTSType.hh"  
  10. #endif  
  11.   
  12.   
  13. class ByteStreamLiveSource: public FramedSource   
  14. {  
  15. public:  
  16.     static ByteStreamLiveSource* createNew(UsageEnvironment& env,  
  17.                          t_TS_Buffer *p_tsbuf,  
  18.                          unsigned preferredFrameSize = 0,  
  19.                          unsigned playTimePerFrame = 0);  
  20.   
  21.   
  22.     //void seekToByteAbsolute(u_int64_t byteNumber, u_int64_t numBytesToStream = 0);  
  23.     // if "numBytesToStream" is >0, then we limit the stream to that number of bytes, before treating it as EOF  
  24.     //void seekToByteRelative(int64_t offset, u_int64_t numBytesToStream = 0);  
  25.     //void seekToEnd(); // to force EOF handling on the next read  
  26.   
  27. protected:  
  28.     ByteStreamLiveSource(UsageEnvironment& env,  
  29.                t_TS_Buffer *p_tsbuf,  
  30.                unsigned preferredFrameSize,  
  31.                unsigned playTimePerFrame);  
  32.     // called only by createNew()  
  33.   
  34.     virtual ~ByteStreamLiveSource();  
  35.   
  36.     //static void fileReadableHandler(ByteStreamFileSource* source, int mask);  
  37.     void doReadFromBuffer();  
  38.   
  39. private:  
  40.     // redefined virtual functions:  
  41.     virtual void doGetNextFrame();  
  42.     virtual void doStopGettingFrames();  
  43.   
  44. private:  
  45.     unsigned fPreferredFrameSize;  
  46.     unsigned fPlayTimePerFrame;  
  47.     //Boolean fFidIsSeekable;  
  48.     unsigned fLastPlayTime;  
  49.     Boolean fHaveStartedReading;  
  50.     Boolean fLimitNumBytesToStream;  
  51.     u_int64_t fNumBytesToStream; // used iff "fLimitNumBytesToStream" is True  
  52.   
  53.     // Added by zhaoyulei, for live ts buffer  
  54.     t_TS_Buffer *fPTsBuf;       // TS流buffer数据指针  
  55.     t_TS_Buffer fLocalBuf;      // 本地buffer  
  56. };  
  57.   
  58. #endif  


    文件名称:ByteStreamLiveSource.cpp

    说明:需要注意的是异常处理,比如读到的数据第一个字节不是0x47的情形。代码中的注释比较详细,不再细说。

[cpp]  view plain  copy
  1. #include "ByteStreamLiveSource.hh"  
  2. #include "GroupsockHelper.hh"  
  3.   
  4. // ByteStreamLiveSource //  
  5.   
  6. #include "LiveTSType.hh"  
  7.   
  8. ByteStreamLiveSource*  
  9. ByteStreamLiveSource::createNew(UsageEnvironment& env,   
  10.                 t_TS_Buffer *p_tsbuf,  
  11.                 unsigned preferredFrameSize,  
  12.                 unsigned playTimePerFrame)  
  13. {  
  14.     printf("=== zyl ===, %s, %d\n",__FILE__, __LINE__);  
  15.   
  16.     if (p_tsbuf == NULL)   
  17.         return NULL;  
  18.   
  19.     ByteStreamLiveSource* newSource  
  20.         = new ByteStreamLiveSource(env, p_tsbuf, preferredFrameSize, playTimePerFrame);  
  21.     //newSource->fPTsBuf = p_tsbuf;  
  22.     // 初始化  
  23.     newSource->fLocalBuf.buf_writing = 0;  
  24.     newSource->fLocalBuf.buf_len = 0;  
  25.     newSource->fLocalBuf.buf_read_counter = 0;  
  26.     memset(newSource->fLocalBuf.buf_data, 0, REC_BUF_MAX_LEN);  
  27.   
  28.     return newSource;  
  29. }  
  30.   
  31. ByteStreamLiveSource::ByteStreamLiveSource(UsageEnvironment& env,   
  32.                         t_TS_Buffer *p_tsbuf,  
  33.                         unsigned preferredFrameSize,  
  34.                         unsigned playTimePerFrame)  
  35.   : FramedSource(env), fPTsBuf(p_tsbuf),fPreferredFrameSize(preferredFrameSize),  
  36.     fPlayTimePerFrame(playTimePerFrame), fLastPlayTime(0),  
  37.     fHaveStartedReading(False), fLimitNumBytesToStream(False), fNumBytesToStream(0)   
  38. {  
  39.   
  40. }  
  41.   
  42. ByteStreamLiveSource::~ByteStreamLiveSource()   
  43. {  
  44.   
  45. }  
  46.   
  47. void ByteStreamLiveSource::doGetNextFrame()   
  48. {  
  49.     if (fLimitNumBytesToStream && fNumBytesToStream == 0)   
  50.     {  
  51.         handleClosure();  
  52.         return;  
  53.     }  
  54.   
  55.     doReadFromBuffer();  
  56. }  
  57.   
  58. void ByteStreamLiveSource::doStopGettingFrames()   
  59. {  
  60.     envir().taskScheduler().unscheduleDelayedTask(nextTask());  
  61. }  
  62.   
  63. void ByteStreamLiveSource::doReadFromBuffer()   
  64. {  
  65.     //printf("=== zyl ===, %s, %d\n",__FILE__, __LINE__);  
  66.     //初始化计数器  
  67.     unsigned int readLen = 0;   
  68.     unsigned int syncBytePosition = 0;  
  69.   
  70.     // 没用,之前确定最多读取字节数的变量  
  71.     fMaxSize = fPreferredFrameSize;  
  72.   
  73.     //printf("=== zyl ===, fLocalBuf.buf_read_counter = %d, fLocalBuf.buf_len = %d\n",  
  74.     //            fLocalBuf.buf_read_counter, fLocalBuf.buf_len);  
  75.     //初始化Frame Size   
  76.     fFrameSize = 0;  
  77.   
  78.     //如果剩余的字节数不够,先读取剩余的字节数  
  79.     if((fLocalBuf.buf_len - fLocalBuf.buf_read_counter) < fPreferredFrameSize)  
  80.     {  
  81.         // fMaxSize = fLocalBuf.buf_len - fLocalBuf.buf_read_counter;  
  82.         // 确定要读取的字节数  
  83.         readLen = fLocalBuf.buf_len - fLocalBuf.buf_read_counter;  
  84.   
  85.         // 读取这些字节  
  86.         memcpy(fTo, (fLocalBuf.buf_data + fLocalBuf.buf_read_counter), readLen);  
  87.         //fMaxSize += fLocalBuf.buf_len - fLocalBuf.buf_read_counter;  
  88.   
  89.         // 已经读取字节数统计值  
  90.         fLocalBuf.buf_read_counter += readLen;  
  91.   
  92.         // 当前Frame Size  
  93.         fFrameSize += readLen;  
  94.     }  
  95.                   
  96.     // 如果已经读完一个buffer  
  97.     if(fLocalBuf.buf_read_counter == fLocalBuf.buf_len)  
  98.     {  
  99.         while(fPTsBuf->buf_writing !=0 )  
  100.         {  
  101.             printf("=== zyl === waiting for buf_writing\n");  
  102.         };  
  103.         #if 0  
  104.         for(i = 0; i < 188; i++)  
  105.         {  
  106.             printf("%02x, ", fPTsBuf->buf_data[i]);  
  107.             if ((i+1)%16 == 0)  
  108.                 printf("\n");  
  109.         }  
  110.         printf("\n");  
  111.         #endif  
  112.         memcpy(fLocalBuf.buf_data, fPTsBuf->buf_data, fPTsBuf->buf_len);  
  113.         fLocalBuf.buf_read_counter = 0;  
  114.         fLocalBuf.buf_len = fPTsBuf->buf_len;  
  115.     }  
  116.   
  117.     // 如果已经读取的字节数不够  
  118.     if(fFrameSize < fPreferredFrameSize)  
  119.     {  
  120.         // 还需要读取这些字节的数据  
  121.         readLen = fPreferredFrameSize - fFrameSize;  
  122.   
  123.         // 读取这些字节,当然,起始地址需要改变一下  
  124.         memcpy(fTo+fFrameSize, (fLocalBuf.buf_data + fLocalBuf.buf_read_counter), readLen);  
  125.           
  126.         // 已经读取字节数统计值  
  127.         fLocalBuf.buf_read_counter += readLen;  
  128.           
  129.         // 当前Frame Size  
  130.         fFrameSize += readLen;  
  131.     }  
  132.   
  133.     // 如果读到的buffer第一个字节不是0x47  
  134.       
  135.     while(TRANSPORT_SYNC_BYTE != fTo[syncBytePosition])  
  136.     {  
  137.         syncBytePosition++;  
  138.     }  
  139.     if(0 != syncBytePosition)  
  140.     {  
  141.         printf("=== zyl === syncBytePosition !=0\n");  
  142.         memmove(fTo, &fTo[syncBytePosition], fFrameSize - syncBytePosition);  
  143.         fFrameSize -= syncBytePosition;  
  144.           
  145.         // 如果已经读取的字节数不够  
  146.         if(fFrameSize < fPreferredFrameSize)  
  147.         {  
  148.             // 还需要读取这些字节的数据  
  149.             readLen = fPreferredFrameSize - fFrameSize;  
  150.   
  151.             // 读取这些字节,当然,起始地址需要改变一下  
  152.             memcpy(fTo+fFrameSize, (fLocalBuf.buf_data + fLocalBuf.buf_read_counter), readLen);  
  153.               
  154.             // 已经读取字节数统计值  
  155.             fLocalBuf.buf_read_counter += readLen;  
  156.               
  157.             // 当前Frame Size  
  158.             fFrameSize += readLen;  
  159.         }  
  160.     }  
  161.       
  162.   
  163.     //printf("=== zyl === ,fLocalBuf.buf_read_counter = %d, fLocalBuf.buf_len = %d\n",  
  164.     //            fLocalBuf.buf_read_counter, fLocalBuf.buf_len);  
  165.   
  166.     if (fFrameSize == 0)   
  167.     {  
  168.         handleClosure();  
  169.         return;  
  170.     }  
  171.     //fNumBytesToStream -= fFrameSize;  
  172.   
  173.     // Set the 'presentation time':  
  174.     if (fPlayTimePerFrame > 0 && fPreferredFrameSize > 0)   
  175.     {  
  176.         if (fPresentationTime.tv_sec == 0 && fPresentationTime.tv_usec == 0)   
  177.         {  
  178.             // This is the first frame, so use the current time:  
  179.             gettimeofday(&fPresentationTime, NULL);  
  180.         }   
  181.         else  
  182.         {  
  183.             // Increment by the play time of the previous data:  
  184.             unsigned uSeconds   = fPresentationTime.tv_usec + fLastPlayTime;  
  185.             fPresentationTime.tv_sec += uSeconds/1000000;  
  186.             fPresentationTime.tv_usec = uSeconds%1000000;  
  187.         }  
  188.   
  189.         // Remember the play time of this data:  
  190.         fLastPlayTime = (fPlayTimePerFrame*fFrameSize)/fPreferredFrameSize;  
  191.         fDurationInMicroseconds = fLastPlayTime;  
  192.     }   
  193.     else   
  194.     {  
  195.         // We don't know a specific play time duration for this data,  
  196.         // so just record the current time as being the 'presentation time':  
  197.         gettimeofday(&fPresentationTime, NULL);  
  198.     }  
  199.   
  200.     // Inform the reader that he has data:  
  201.     // Because the file read was done from the event loop, we can call the  
  202.     // 'after getting' function directly, without risk of infinite recursion:  
  203.     FramedSource::afterGetting(this);  
  204. }  

3)MPEG2TransportLiveServerMediaSubsession类

    文件名称:MPEG2TransportLiveServerMediaSubsession.hh

    说明:参照MPEG2TransportFileServerMediaSubsession类,把不需要的代码给删除掉

[cpp]  view plain  copy
  1. #ifndef _MPEG2_TRANSPORT_LIVE_SERVER_MEDIA_SUBSESSION_HH  
  2. #define _MPEG2_TRANSPORT_LIVE_SERVER_MEDIA_SUBSESSION_HH  
  3.   
  4. #ifndef _ON_DEMAND_SERVER_MEDIA_SUBSESSION_HH  
  5. #include "OnDemandServerMediaSubsession.hh"  
  6. #endif  
  7. #ifndef _MPEG2_TRANSPORT_STREAM_FRAMER_HH  
  8. #include "MPEG2TransportStreamFramer.hh"  
  9. #endif  
  10. #ifndef _BYTE_STREAM_LIVE_SOURCE_HH  
  11. #include "ByteStreamLiveSource.hh"  
  12. #endif  
  13. #ifndef _MPEG2_TRANSPORT_STREAM_TRICK_MODE_FILTER_HH  
  14. #include "MPEG2TransportStreamTrickModeFilter.hh"  
  15. #endif  
  16. #ifndef _MPEG2_TRANSPORT_STREAM_FROM_ES_SOURCE_HH  
  17. #include "MPEG2TransportStreamFromESSource.hh"  
  18. #endif  
  19.   
  20. #ifndef __LIVE_TS_BUFFER_TYPE_HH  
  21. #include "LiveTSType.hh"  
  22. #endif  
  23.   
  24.   
  25. //class ClientTrickPlayState; // forward  
  26.   
  27. class MPEG2TransportLiveServerMediaSubsession: public OnDemandServerMediaSubsession  
  28. {  
  29. public:  
  30.     static MPEG2TransportLiveServerMediaSubsession* createNew(UsageEnvironment& env,  
  31.                 Boolean reuseFirstSource,  
  32.                 t_TS_Buffer *p_tsbuf);  
  33.   
  34. protected:  
  35.     MPEG2TransportLiveServerMediaSubsession(UsageEnvironment& env,  
  36.                 Boolean reuseFirstSource,  
  37.                 t_TS_Buffer *p_tsbuf);  
  38.                 // called only by createNew();  
  39.     virtual ~MPEG2TransportLiveServerMediaSubsession();  
  40.   
  41.     //virtual ClientTrickPlayState* newClientTrickPlayState();  
  42.   
  43. private// redefined virtual functions  
  44.   // Note that because - to implement 'trick play' operations - we're operating on  
  45.   // more than just the input source, we reimplement some functions that are  
  46.   // already implemented in "OnDemandServerMediaSubsession", rather than  
  47.   // reimplementing "seekStreamSource()" and "setStreamSourceScale()":  
  48.     virtual void startStream(unsigned clientSessionId, void* streamToken,  
  49.                 TaskFunc* rtcpRRHandler,  
  50.                 void* rtcpRRHandlerClientData,  
  51.                 unsigned short& rtpSeqNum,  
  52.                 unsigned& rtpTimestamp,  
  53.                 ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,  
  54.                 void* serverRequestAlternativeByteHandlerClientData);  
  55.     virtual void pauseStream(unsigned clientSessionId, void* streamToken);  
  56.     virtual void seekStream(unsigned clientSessionId, void* streamToken, double& seekNPT, double streamDuration, u_int64_t& numBytes);  
  57.     virtual void setStreamScale(unsigned clientSessionId, void* streamToken, float scale);  
  58.     virtual void deleteStream(unsigned clientSessionId, void*& streamToken);  
  59.   
  60.     // The virtual functions thare are usually implemented by "ServerMediaSubsession"s:  
  61.     virtual FramedSource* createNewStreamSource(unsigned clientSessionId,  
  62.                 unsigned& estBitrate);  
  63.     virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock,  
  64.                 unsigned char rtpPayloadTypeIfDynamic,  
  65.                 FramedSource* inputSource);  
  66.   
  67.     virtual void testScaleFactor(float& scale);  
  68.     virtual float duration() const;  
  69.   
  70. private:  
  71.     //ClientTrickPlayState* lookupClient(unsigned clientSessionId);  
  72.   
  73. private:  
  74. //    MPEG2TransportStreamIndexFile* fIndexFile;  
  75.     float fDuration;  
  76.     HashTable* fClientSessionHashTable; // indexed by client session id  
  77.   
  78.     // The live ts buffer, added by zhaoyulei, 2015.06.01  
  79.     t_TS_Buffer *fPTsBuf;  // buffer指针  
  80.     
  81. };  

    文件名称:MPEG2TransportLiveServerMediaSubsession.cpp

    说明:参照MPEG2TransportFileServerMediaSubsession类,把不需要的代码给删除掉

[cpp]  view plain  copy
  1. #include "MPEG2TransportLiveServerMediaSubsession.hh"  
  2. #include "SimpleRTPSink.hh"  
  3. #include "LiveTSType.hh"  
  4. #include <zyl_debug.h>  
  5.   
  6. MPEG2TransportLiveServerMediaSubsession*  
  7. MPEG2TransportLiveServerMediaSubsession::createNew(  
  8.                 UsageEnvironment&   env,  
  9.                 Boolean             reuseFirstSource,  
  10.                 t_TS_Buffer*        p_tsbuf)  
  11. {  
  12.     // printf("=== zyl ===, %s, %d\n",__FILE__, __LINE__);  
  13.   
  14.     MPEG2TransportLiveServerMediaSubsession* newpSubsession =  
  15.     new MPEG2TransportLiveServerMediaSubsession(env,  
  16.                              reuseFirstSource, p_tsbuf);  
  17.     //newpSubsession->fPTsBuf = p_tsbuf;   
  18.     return newpSubsession;  
  19. }  
  20.   
  21.   
  22. MPEG2TransportLiveServerMediaSubsession  
  23. ::MPEG2TransportLiveServerMediaSubsession(  
  24.                 UsageEnvironment&               env,  
  25.                 Boolean                         reuseFirstSource,  
  26.                 t_TS_Buffer*                    p_tsbuf)  
  27. : OnDemandServerMediaSubsession(env, reuseFirstSource),fPTsBuf(p_tsbuf),  
  28. fDuration(0.0), fClientSessionHashTable(NULL)   
  29. {  
  30.   
  31. }  
  32.   
  33. MPEG2TransportLiveServerMediaSubsession  
  34. ::~MPEG2TransportLiveServerMediaSubsession()   
  35. {  
  36. }  
  37.   
  38. #define TRANSPORT_PACKET_SIZE 188  
  39. #define TRANSPORT_PACKETS_PER_NETWORK_PACKET 7  
  40. // The product of these two numbers must be enough to fit within a network packet  
  41.   
  42. void MPEG2TransportLiveServerMediaSubsession  
  43. ::startStream(unsigned clientSessionId, void* streamToken, TaskFunc* rtcpRRHandler,  
  44.           void* rtcpRRHandlerClientData, unsigned short& rtpSeqNum,  
  45.           unsigned& rtpTimestamp,  
  46.           ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,  
  47.           void* serverRequestAlternativeByteHandlerClientData)   
  48. {  
  49.     // Call the original, default version of this routine:  
  50.     OnDemandServerMediaSubsession::startStream(clientSessionId, streamToken,  
  51.                          rtcpRRHandler, rtcpRRHandlerClientData,  
  52.                          rtpSeqNum, rtpTimestamp,  
  53.                          serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData);  
  54. }  
  55.   
  56. void MPEG2TransportLiveServerMediaSubsession  
  57. ::pauseStream(unsigned clientSessionId, void* streamToken)   
  58. {  
  59.     // Call the original, default version of this routine:  
  60.     OnDemandServerMediaSubsession::pauseStream(clientSessionId, streamToken);  
  61. }  
  62.   
  63. void MPEG2TransportLiveServerMediaSubsession  
  64. ::seekStream(unsigned clientSessionId, void* streamToken, double& seekNPT, double streamDuration, u_int64_t& numBytes)   
  65. {  
  66.     // Begin by calling the original, default version of this routine:  
  67.     OnDemandServerMediaSubsession::seekStream(clientSessionId, streamToken, seekNPT, streamDuration, numBytes);  
  68.   
  69. }  
  70.   
  71. void MPEG2TransportLiveServerMediaSubsession  
  72. ::setStreamScale(unsigned clientSessionId, void* streamToken, float scale)   
  73. {  
  74.   
  75.     // Call the original, default version of this routine:  
  76.     OnDemandServerMediaSubsession::setStreamScale(clientSessionId, streamToken, scale);  
  77. }  
  78.   
  79. void MPEG2TransportLiveServerMediaSubsession  
  80. ::deleteStream(unsigned clientSessionId, void*& streamToken)   
  81. {  
  82.   
  83.     // Call the original, default version of this routine:  
  84.     OnDemandServerMediaSubsession::deleteStream(clientSessionId, streamToken);  
  85. }  
  86.   
  87. FramedSource* MPEG2TransportLiveServerMediaSubsession  
  88. ::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate)   
  89. {  
  90.     // Create the video source:  
  91.     unsigned const inputDataChunkSize  
  92.         = TRANSPORT_PACKETS_PER_NETWORK_PACKET*TRANSPORT_PACKET_SIZE;  
  93.   
  94.     // 使用自己定义的source  
  95.     ByteStreamLiveSource* liveSource  
  96.         = ByteStreamLiveSource::createNew(envir(), fPTsBuf,inputDataChunkSize);  
  97.   
  98.     estBitrate = 5000; // kbps, estimate  
  99.       
  100.   
  101.     // Create a framer for the Transport Stream:  
  102.     MPEG2TransportStreamFramer* framer  
  103.         = MPEG2TransportStreamFramer::createNew(envir(), liveSource);  
  104.   
  105.     return framer;  
  106. }  
  107.   
  108. RTPSink* MPEG2TransportLiveServerMediaSubsession  
  109. ::createNewRTPSink(Groupsock* rtpGroupsock,  
  110.            unsigned char /*rtpPayloadTypeIfDynamic*/,  
  111.            FramedSource* /*inputSource*/)   
  112. {  
  113.     return SimpleRTPSink::createNew(envir(), rtpGroupsock,  
  114.                   33, 90000, "video""MP2T",  
  115.                   1, True, False /*no 'M' bit*/);  
  116. }  
  117.   
  118. void MPEG2TransportLiveServerMediaSubsession::testScaleFactor(float& scale)   
  119. {  
  120.         scale = 1.0f;  
  121. }  
  122.   
  123. float MPEG2TransportLiveServerMediaSubsession::duration() const   
  124. {  
  125.     return fDuration;  
  126. }  

    这样我们需要的两个类就创建好了,最主要的工作算完成了

四、修改DynamicRTSPServer类,增加我们的媒体类型

1)修改DynamicRTSPServer.hh,重定义一个createNew成员函数,增加一个私有的t_TS_Buffer * 型成员

[cpp]  view plain  copy
  1. #ifndef _DYNAMIC_RTSP_SERVER_HH  
  2. #define _DYNAMIC_RTSP_SERVER_HH  
  3.   
  4. #ifndef _RTSP_SERVER_SUPPORTING_HTTP_STREAMING_HH  
  5. #include "RTSPServerSupportingHTTPStreaming.hh"  
  6. #endif  
  7.   
  8. #ifndef __LIVE_TS_BUFFER_TYPE_HH  
  9. #include "LiveTSType.hh"  
  10. #endif  
  11.   
  12. class DynamicRTSPServer: public RTSPServerSupportingHTTPStreaming {  
  13. public:  
  14.   static DynamicRTSPServer* createNew(UsageEnvironment& env, Port ourPort,  
  15.                       UserAuthenticationDatabase* authDatabase,  
  16.                       unsigned reclamationTestSeconds = 65);  
  17.                         
  18.     // 新的createNew,可以传递t_TS_Buffer指针  
  19.     static DynamicRTSPServer* createNew(UsageEnvironment& env, Port ourPort,  
  20.                       UserAuthenticationDatabase* authDatabase,  
  21.                       t_TS_Buffer *p_tsbuf,  
  22.                       unsigned reclamationTestSeconds = 65);  
  23.   
  24. protected:  
  25.   DynamicRTSPServer(UsageEnvironment& env, int ourSocket, Port ourPort,  
  26.             UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds);  
  27.   // called only by createNew();  
  28.   virtual ~DynamicRTSPServer();  
  29.   
  30. protected// redefined virtual functions  
  31.   virtual ServerMediaSession*  
  32.   lookupServerMediaSession(char const* streamName, Boolean isFirstLookupInSession);  
  33.   
  34. private:  
  35.     // The live ts buffer, added by zhaoyulei, 2015.06.01  
  36.     // t_TS_Buffer指针  
  37.     t_TS_Buffer *fPTsBuf;  
  38. };  
  39.   
  40. #endif  

3、修改DynamicRTSPServer.cpp,

[cpp]  view plain  copy
  1. #include "DynamicRTSPServer.hh"  
  2. #include <liveMedia.hh>  
  3. #include <string.h>  
  4.   
  5. DynamicRTSPServer*  
  6. DynamicRTSPServer::createNew(UsageEnvironment& env, Port ourPort,  
  7.                  UserAuthenticationDatabase* authDatabase,  
  8.                  unsigned reclamationTestSeconds) {  
  9.     int ourSocket = setUpOurSocket(env, ourPort);  
  10.     if (ourSocket == -1) return NULL;  
  11.   
  12.     return new DynamicRTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds);  
  13. }  
  14.   
  15. // 新的createNew函数  
  16. DynamicRTSPServer*  
  17. DynamicRTSPServer::createNew(UsageEnvironment& env, Port ourPort,  
  18.                  UserAuthenticationDatabase* authDatabase,  
  19.                  t_TS_Buffer *p_tsbuf,  
  20.                  unsigned reclamationTestSeconds)   
  21. {  
  22.     int ourSocket = setUpOurSocket(env, ourPort);  
  23.     if (ourSocket == -1) return NULL;  
  24.   
  25.     DynamicRTSPServer* newDynamicRTSPServer =  
  26.             new DynamicRTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds);  
  27.     newDynamicRTSPServer->fPTsBuf = p_tsbuf;  
  28.     return newDynamicRTSPServer;  
  29. }  
  30.   
  31. DynamicRTSPServer::DynamicRTSPServer(UsageEnvironment& env, int ourSocket,  
  32.                      Port ourPort,  
  33.                      UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds)  
  34.   : RTSPServerSupportingHTTPStreaming(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds),  
  35.   fPTsBuf(NULL)  
  36. {  
  37. }  
  38.   
  39. DynamicRTSPServer::~DynamicRTSPServer() {  
  40. }  
  41.   
  42. static ServerMediaSession* createNewSMS(UsageEnvironment& env,  
  43.                     char const* fileName, FILE* fid, t_TS_Buffer *p_tsbuf); // forward  
  44.   
  45. ServerMediaSession* DynamicRTSPServer  
  46. ::lookupServerMediaSession(char const* streamName, Boolean isFirstLookupInSession)   
  47. {  
  48.     // First, check whether the specified "streamName" exists as a local file:  
  49.       
  50.     FILE* fid = fopen(streamName, "rb");  
  51.     Boolean fileExists = fid != NULL;  
  52.     // 处理新的数据类型  
  53.     if (strcmp(streamName, "live") == 0)  
  54.     {  
  55.         fileExists = 1;  
  56.     }  
  57.       
  58.     // Next, check whether we already have a "ServerMediaSession" for this file:  
  59.     ServerMediaSession* sms = RTSPServer::lookupServerMediaSession(streamName);  
  60.     Boolean smsExists = sms != NULL;  
  61.   
  62.   // Handle the four possibilities for "fileExists" and "smsExists":  
  63.   if (!fileExists) {  
  64.     if (smsExists) {  
  65.       // "sms" was created for a file that no longer exists. Remove it:  
  66.       removeServerMediaSession(sms);  
  67.       sms = NULL;  
  68.     }  
  69.   
  70.     return NULL;  
  71.   } else {  
  72.     if (smsExists && isFirstLookupInSession) {   
  73.       // Remove the existing "ServerMediaSession" and create a new one, in case the underlying  
  74.       // file has changed in some way:  
  75.       removeServerMediaSession(sms);   
  76.       sms = NULL;  
  77.     }   
  78.         // 处理新的数据类型  
  79.         if (sms == NULL)   
  80.         {  
  81.             sms = createNewSMS(envir(), streamName, fid, fPTsBuf);   
  82.             addServerMediaSession(sms);  
  83.         }  
  84.         if(fileExists && (strcmp(streamName, "live") != 0))  
  85.         {  
  86.             fclose(fid);  
  87.         }  
  88.         return sms;  
  89.     }  
  90. }  
  91.   
  92. // Special code for handling Matroska files:  
  93. struct MatroskaDemuxCreationState {  
  94.   MatroskaFileServerDemux* demux;  
  95.   char watchVariable;  
  96. };  
  97. static void onMatroskaDemuxCreation(MatroskaFileServerDemux* newDemux, void* clientData) {  
  98.   MatroskaDemuxCreationState* creationState = (MatroskaDemuxCreationState*)clientData;  
  99.   creationState->demux = newDemux;  
  100.   creationState->watchVariable = 1;  
  101. }  
  102. // END Special code for handling Matroska files:  
  103.   
  104. // Special code for handling Ogg files:  
  105. struct OggDemuxCreationState {  
  106.   OggFileServerDemux* demux;  
  107.   char watchVariable;  
  108. };  
  109. static void onOggDemuxCreation(OggFileServerDemux* newDemux, void* clientData) {  
  110.   OggDemuxCreationState* creationState = (OggDemuxCreationState*)clientData;  
  111.   creationState->demux = newDemux;  
  112.   creationState->watchVariable = 1;  
  113. }  
  114. // END Special code for handling Ogg files:  
  115.   
  116. #define NEW_SMS(description) do {\  
  117. char const* descStr = description\  
  118.     ", streamed by the LIVE555 Media Server";\  
  119. sms = ServerMediaSession::createNew(env, fileName, fileName, descStr);\  
  120. while(0)  
  121.   
  122. static ServerMediaSession* createNewSMS(UsageEnvironment& env,  
  123.                     char const* fileName, FILE/*fid*/, t_TS_Buffer *p_tsbuf) {  
  124.   // Use the file name extension to determine the type of "ServerMediaSession":  
  125.     char const* extension = strrchr(fileName, '.');  
  126.     if (strcmp(fileName, "live") == 0)  
  127.     {  
  128.         extension = "live";  
  129.     }  
  130.   
  131.   if (extension == NULL) return NULL;  
  132.   
  133.   ServerMediaSession* sms = NULL;  
  134.   Boolean const reuseSource = False;  
  135.   if (strcmp(extension, ".aac") == 0) {  
  136.     // Assumed to be an AAC Audio (ADTS format) file:  
  137.     NEW_SMS("AAC Audio");  
  138.     sms->addSubsession(ADTSAudioFileServerMediaSubsession::createNew(env, fileName, reuseSource));  
  139.   } else if (strcmp(extension, ".amr") == 0) {  
  140.     // Assumed to be an AMR Audio file:  
  141.     NEW_SMS("AMR Audio");  
  142.     sms->addSubsession(AMRAudioFileServerMediaSubsession::createNew(env, fileName, reuseSource));  
  143.   } else if (strcmp(extension, ".ac3") == 0) {  
  144.     // Assumed to be an AC-3 Audio file:  
  145.     NEW_SMS("AC-3 Audio");  
  146.     sms->addSubsession(AC3AudioFileServerMediaSubsession::createNew(env, fileName, reuseSource));  
  147.   } else if (strcmp(extension, ".m4e") == 0) {  
  148.     // Assumed to be a MPEG-4 Video Elementary Stream file:  
  149.     NEW_SMS("MPEG-4 Video");  
  150.     sms->addSubsession(MPEG4VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));  
  151.   } else if (strcmp(extension, ".264") == 0) {  
  152.     // Assumed to be a H.264 Video Elementary Stream file:  
  153.     NEW_SMS("H.264 Video");  
  154.     OutPacketBuffer::maxSize = 100000; // allow for some possibly large H.264 frames  
  155.     sms->addSubsession(H264VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));  
  156.   } else if (strcmp(extension, ".265") == 0) {  
  157.     // Assumed to be a H.265 Video Elementary Stream file:  
  158.     NEW_SMS("H.265 Video");  
  159.     OutPacketBuffer::maxSize = 100000; // allow for some possibly large H.265 frames  
  160.     sms->addSubsession(H265VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));  
  161.   } else if (strcmp(extension, ".mp3") == 0) {  
  162.     // Assumed to be a MPEG-1 or 2 Audio file:  
  163.     NEW_SMS("MPEG-1 or 2 Audio");  
  164.     // To stream using 'ADUs' rather than raw MP3 frames, uncomment the following:  
  165. //#define STREAM_USING_ADUS 1  
  166.     // To also reorder ADUs before streaming, uncomment the following:  
  167. //#define INTERLEAVE_ADUS 1  
  168.     // (For more information about ADUs and interleaving,  
  169.     //  see <http://www.live555.com/rtp-mp3/>)  
  170.     Boolean useADUs = False;  
  171.     Interleaving* interleaving = NULL;  
  172. #ifdef STREAM_USING_ADUS  
  173.     useADUs = True;  
  174. #ifdef INTERLEAVE_ADUS  
  175.     unsigned char interleaveCycle[] = {0,2,1,3}; // or choose your own...  
  176.     unsigned const interleaveCycleSize  
  177.       = (sizeof interleaveCycle)/(sizeof (unsigned char));  
  178.     interleaving = new Interleaving(interleaveCycleSize, interleaveCycle);  
  179. #endif  
  180. #endif  
  181.     sms->addSubsession(MP3AudioFileServerMediaSubsession::createNew(env, fileName, reuseSource, useADUs, interleaving));  
  182.   } else if (strcmp(extension, ".mpg") == 0) {  
  183.     // Assumed to be a MPEG-1 or 2 Program Stream (audio+video) file:  
  184.     NEW_SMS("MPEG-1 or 2 Program Stream");  
  185.     MPEG1or2FileServerDemux* demux  
  186.       = MPEG1or2FileServerDemux::createNew(env, fileName, reuseSource);  
  187.     sms->addSubsession(demux->newVideoServerMediaSubsession());  
  188.     sms->addSubsession(demux->newAudioServerMediaSubsession());  
  189.   } else if (strcmp(extension, ".vob") == 0) {  
  190.     // Assumed to be a VOB (MPEG-2 Program Stream, with AC-3 audio) file:  
  191.     NEW_SMS("VOB (MPEG-2 video with AC-3 audio)");  
  192.     MPEG1or2FileServerDemux* demux  
  193.       = MPEG1or2FileServerDemux::createNew(env, fileName, reuseSource);  
  194.     sms->addSubsession(demux->newVideoServerMediaSubsession());  
  195.     sms->addSubsession(demux->newAC3AudioServerMediaSubsession());  
  196.   } else if (strcmp(extension, ".ts") == 0) {  
  197.     // Assumed to be a MPEG Transport Stream file:  
  198.     // Use an index file name that's the same as the TS file name, except with ".tsx":  
  199.     unsigned indexFileNameLen = strlen(fileName) + 2; // allow for trailing "x\0"  
  200.     char* indexFileName = new char[indexFileNameLen];  
  201.     sprintf(indexFileName, "%sx", fileName);  
  202.     NEW_SMS("MPEG Transport Stream");  
  203.     sms->addSubsession(MPEG2TransportFileServerMediaSubsession::createNew(env, fileName, indexFileName, reuseSource));  
  204.     delete[] indexFileName;  
  205.   } else if (strcmp(extension, ".wav") == 0) {  
  206.     // Assumed to be a WAV Audio file:  
  207.     NEW_SMS("WAV Audio Stream");  
  208.     // To convert 16-bit PCM data to 8-bit u-law, prior to streaming,  
  209.     // change the following to True:  
  210.     Boolean convertToULaw = False;  
  211.     sms->addSubsession(WAVAudioFileServerMediaSubsession::createNew(env, fileName, reuseSource, convertToULaw));  
  212.   } else if (strcmp(extension, ".dv") == 0) {  
  213.     // Assumed to be a DV Video file  
  214.     // First, make sure that the RTPSinks' buffers will be large enough to handle the huge size of DV frames (as big as 288000).  
  215.     OutPacketBuffer::maxSize = 300000;  
  216.   
  217.     NEW_SMS("DV Video");  
  218.     sms->addSubsession(DVVideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));  
  219.   } else if (strcmp(extension, ".mkv") == 0 || strcmp(extension, ".webm") == 0) {  
  220.     // Assumed to be a Matroska file (note that WebM ('.webm') files are also Matroska files)  
  221.     OutPacketBuffer::maxSize = 100000; // allow for some possibly large VP8 or VP9 frames  
  222.     NEW_SMS("Matroska video+audio+(optional)subtitles");  
  223.   
  224.     // Create a Matroska file server demultiplexor for the specified file.  
  225.     // (We enter the event loop to wait for this to complete.)  
  226.     MatroskaDemuxCreationState creationState;  
  227.     creationState.watchVariable = 0;  
  228.     MatroskaFileServerDemux::createNew(env, fileName, onMatroskaDemuxCreation, &creationState);  
  229.     env.taskScheduler().doEventLoop(&creationState.watchVariable);  
  230.   
  231.     ServerMediaSubsession* smss;  
  232.     while ((smss = creationState.demux->newServerMediaSubsession()) != NULL) {  
  233.       sms->addSubsession(smss);  
  234.     }  
  235.   } else if (strcmp(extension, ".ogg") == 0 || strcmp(extension, ".ogv") == 0 || strcmp(extension, ".opus") == 0) {  
  236.     // Assumed to be an Ogg file  
  237.     NEW_SMS("Ogg video and/or audio");  
  238.   
  239.     // Create a Ogg file server demultiplexor for the specified file.  
  240.     // (We enter the event loop to wait for this to complete.)  
  241.     OggDemuxCreationState creationState;  
  242.     creationState.watchVariable = 0;  
  243.     OggFileServerDemux::createNew(env, fileName, onOggDemuxCreation, &creationState);  
  244.     env.taskScheduler().doEventLoop(&creationState.watchVariable);  
  245.   
  246.     ServerMediaSubsession* smss;  
  247.     while ((smss = creationState.demux->newServerMediaSubsession()) != NULL) {  
  248.       sms->addSubsession(smss);  
  249.     }  
  250.   }  
  251.     // 增加新的媒体类型"live",此类型不是根据文件名的后缀识别,而是全名  
  252.     else if (strcmp(extension, "live") == 0)  
  253.     {  
  254.         if (p_tsbuf != NULL)  
  255.         {  
  256.             NEW_SMS("MPEG2 Live Transport Stream");  
  257.             sms->addSubsession(MPEG2TransportLiveServerMediaSubsession::createNew(env,reuseSource, p_tsbuf));  
  258.         }  
  259.     }  
  260.   
  261.   return sms;  
  262. }  

四、主调函数的实现

1)在live555MediaServer.cpp的main函数基础上进行修改。

    增加一个头文件:live555MediaServer.h

[plain]  view plain  copy
  1. #ifndef __TCB_LIVE555_MEDIA_SERVER_HH_  
  2. #define __TCB_LIVE555_MEDIA_SERVER_HH_  
  3.   
  4.   
  5. #include "LiveTSType.hh"  
  6.   
  7.   
  8. #ifdef __cplusplus  
  9. extern "C"  
  10. {  
  11. #endif  
  12.   
  13. int live555ms_play(t_TS_Buffer *p_tsbuf);  
  14.   
  15. #ifdef __cplusplus  
  16. }  
  17. #endif  
  18.   
  19. #endif  
    

    修改live555MediaServer.cpp

[plain]  view plain  copy
  1. #include <BasicUsageEnvironment.hh>  
  2. #include "DynamicRTSPServer.hh"  
  3. #include "version.hh"  
  4. #include <liveMedia.hh>  
  5.   
  6. #include "live555MediaServer.h"  
  7. #include "LiveTSType.hh"  
  8. #include <zyl_debug.h>  
  9.   
  10. #include <pthread.h>  
  11.   
  12. #define ZYL_DEBUG_LEVEL 5  
  13.   
  14. static int live555mediaserver(t_TS_Buffer *p_tsbuf)  
  15. {  
  16.     // 设置使用环境  
  17.     // 创建调度器  
  18.     TaskScheduler* scheduler = BasicTaskScheduler::createNew();  
  19.     // 创建交互环境  
  20.     UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);  
  21.   
  22.     // 用户数据库指针  
  23.     UserAuthenticationDatabase* authDB = NULL;  
  24. #ifdef ACCESS_CONTROL  
  25.     // 创建用户数据库  
  26.     authDB = new UserAuthenticationDatabase;  
  27.     // 添加用户  
  28.     authDB->addUserRecord("username1", "password1");  
  29. #endif  
  30.   
  31.     // 创建RTSP服务器,首先使用554端口号,如果不成功,则尝试使用8554  
  32.     RTSPServer* rtspServer;  
  33.     portNumBits rtspServerPortNum = 554;  
  34.     rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB, p_tsbuf);  
  35.     if (rtspServer == NULL)   
  36.     {  
  37.         rtspServerPortNum = 8554;  
  38.         rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB, p_tsbuf);  
  39.     }  
  40.     if (rtspServer == NULL)   
  41.     {  
  42.         *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";  
  43.         exit(1);  
  44.     }  
  45.   
  46.     *env << "TCB LIVE555 Media Server\n";  
  47.     *env << "\tversion " << MEDIA_SERVER_VERSION_STRING  
  48.        << " (LIVE555 Streaming Media library version "  
  49.        << LIVEMEDIA_LIBRARY_VERSION_STRING << ").\n";  
  50.   
  51.     char* urlPrefix = rtspServer->rtspURLPrefix();  
  52.     *env << "Play streams from this server using the URL\n\t"  
  53.        << urlPrefix << "<filename>\nwhere <filename> is a file present in the current directory.\n";  
  54.     *env << "Each file's type is inferred from its name suffix:\n";  
  55.     *env << "\t\".264\" => a H.264 Video Elementary Stream file\n";  
  56.     *env << "\t\".265\" => a H.265 Video Elementary Stream file\n";  
  57.     *env << "\t\".aac\" => an AAC Audio (ADTS format) file\n";  
  58.     *env << "\t\".ac3\" => an AC-3 Audio file\n";  
  59.     *env << "\t\".amr\" => an AMR Audio file\n";  
  60.     *env << "\t\".dv\" => a DV Video file\n";  
  61.     *env << "\t\".m4e\" => a MPEG-4 Video Elementary Stream file\n";  
  62.     *env << "\t\".mkv\" => a Matroska audio+video+(optional)subtitles file\n";  
  63.     *env << "\t\".mp3\" => a MPEG-1 or 2 Audio file\n";  
  64.     *env << "\t\".mpg\" => a MPEG-1 or 2 Program Stream (audio+video) file\n";  
  65.     *env << "\t\".ogg\" or \".ogv\" or \".opus\" => an Ogg audio and/or video file\n";  
  66.     *env << "\t\".ts\" => a MPEG Transport Stream file\n";  
  67.     *env << "\t\t(a \".tsx\" index file - if present - provides server 'trick play' support)\n";  
  68.     *env << "\t\".vob\" => a VOB (MPEG-2 video with AC-3 audio) file\n";  
  69.     *env << "\t\".wav\" => a WAV Audio file\n";  
  70.     *env << "\t\"live\" => a live ts profram\n";  
  71.     *env << "\t\".webm\" => a WebM audio(Vorbis)+video(VP8) file\n";  
  72.     *env << "See http://www.live555.com/mediaServer/ for additional documentation.\n";  
  73.   
  74.     // Also, attempt to create a HTTP server for RTSP-over-HTTP tunneling.  
  75.     // Try first with the default HTTP port (80), and then with the alternative HTTP  
  76.     // port numbers (8000 and 8080).  
  77.   
  78.     if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080))   
  79.     {  
  80.         *env << "(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling, or for HTTP live streaming (for indexed Transport Stream files only).)\n";  
  81.     }   
  82.     else   
  83.     {  
  84.         *env << "(RTSP-over-HTTP tunneling is not available.)\n";  
  85.     }  
  86.   
  87.     env->taskScheduler().doEventLoop(); // does not return  
  88.   
  89.     return 0; // only to prevent compiler warning  
  90. }  
  91. // live555主函数  
  92. int live555ms_play(t_TS_Buffer *p_tsbuf)  
  93. {  
  94.     printf("=== zyl ===, %s, %d\n",__FILE__, __LINE__);  
  95.     //return live_ts_rtsp_server(p_tsbuf);  
  96.     return live555mediaserver(p_tsbuf);  
  97. }  

五、函数调用

    以上是对整个live555部分的修改,在调用的时候,使用

[plain]  view plain  copy
  1. live555ms_play(t_TS_Buffer *p_tsbuf)  

传递存放数据的指针,当然,需要为Live555启动一个线程,因为这是一个不会返回的函数。

### 回答1: live555是一个基于C++的开源媒体服务器,是由美国的Live Networks公司开发并维护。它的主要功能是在网络上实现实时传输,支持RTSP、RTP/RTCP和SIP等协议。 在使用live555推送实时之前,我们需要先设置好服务器地址、端口号以及媒体文件等参数。然后,我们通过live555中的MediaSession类,创建一个媒体会话,将需要传输的媒体加入到会话中。媒体可以是音频或视频,也可以是音视频混合的多媒体。 接着,我们使用live555提供的MediaSubsession类,将加入媒体会话的媒体进行分割。分割后的媒体将按照RTSP或RTP协议进行传输,可以通过RTSP或RTP服务器进行接收。其中,RTSP用于控制媒体的播放、暂停、停止等操作;而RTP则是在网络传输中实现实时数据传输和同步的协议。 总的来说,通过使用live555的媒体会话和媒体分割功能,我们可以轻松地实现实时的推送和传输,为视频监控、视频会议、实时视频直播等应用提供了非常可靠和高效的技术支持。 ### 回答2: Live555是一个开源的C++多媒体框架,可用于实现实时的推送。Live555提供了一套完整的库和工具,能够支持常见的视频和音频协议,例如RTSP,RTP,RTCP等。 实时推送的过程可以概括为以下几个步骤: 1. 创建一个`RTSPServer`实例,用于接收客户端的连接请求。 2. 为需要推送的媒体资源创建一个`MediaSession`实例,并将其添加到`RTSPServer`中。 3. 创建一个`RTSPClientConnection`实例,用于处理客户端的连接和请求。 4. 在`RTSPSession`中添加需要推送的媒体资源,并为其创建一个`RTPSink`实例,用于将媒体数据发送到客户端。 5. 创建一个`MediaSource`实例,用于从媒体源(例如摄像头或音频设备)中获取实时数据。 6. 将`MediaSource`连接到`RTPSink`,并启动数据传输。 7. 开始监听客户端的连接请求,并响应相应的RTSP请求。 8. 当有客户端连接成功后,将媒体数据通过RTP协议发送给客户端。 9. 如果有多个客户端连接,可以使用多线程或多进程来处理并发连接。 通过以上步骤,Live555可以实现将实时从媒体源推送到客户端。实时推送广泛应用于视频直播、视频会议等领域,能够实现高效的实时数据传输和播放。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值