Valkka  1.6.1
OpenSource Video Management
Live streaming

Some notes on receiving/sending live streams

Receiving streams

Sending streams

From the point of view of API users, sending frames happens simply by connecting a source to LiveFifo (see the examples). Behind the scene, frames are sent over the net, by calling LiveFifo::writeCopy - i.e. using the unified approach in Valkka library to handle frames; they are handled in the same way, whether they are passed to the decoder or sent to the screen by using OpenGLFrameFifo (see Library architecture).

Sending frames happens roughly as follows:

SDP Streams

Streams sent directly to UDP ports, as defined in an SDP file ("SDP" streams)

  • SDPOutbound has a set of Stream instances in Outbound::streams. There is a Stream instance per media substream.
  • Stream instances encapsulate the usual live555 stuff per substream: RTPSink, RTCPInstance, Groupsock, FramedSource, etc.
  • Each Stream has also a reference to LiveFifo and Stream::buffer_source which is a BufferSource instance.
  • BufferSource is a subclass of live555 FramedSource that is used for sending frames.
  • BufferSource has it's own internal fifo BufferSource::internal_fifo. This fifo is used by BufferSource::doGetNextFrame, as it should in live555, i.e. if there are no frames left in the fifo, then BufferSource::doGetNextFrame exits immediately, otherwise it calls FramedSource::afterGetting and if necessary, re-schedules itself
  • To summarize, the call chain looks like this: LiveThread::handleFrame => Outbound::handleFrame => Stream::handleFrame (transfers the frame from LiveFifo to BufferSource::internal_fifo) => BufferSource::handleFrame => FramedSource::afterGetting
  • The Live555 filterchain looks like this:
    buffer_source  =new BufferSource(env, fifo, 0, 0, 4); // nalstamp offset: 4
    terminal       =H264VideoStreamDiscreteFramer::createNew(env, buffer_source);
    
    sink           = H264VideoRTPSink::createNew(env,rtpGroupsock, 96);
    
    sink->startPlaying(*(terminal), this->afterPlaying, this);
    
  • Frames flow like this: BufferSource => H264VideoStreamDiscreteFramer => H264VideoRTPSink

    This diagram, where "{}" means enclosing object will help you to understand this:

    BufferSource (.., fifo) {
       - Live555 FramedSource class with method "doGetNextFrame"
       - Recycles frames back to fifo (in doGetNextFrame)
    }
    
    Stream {
       RTPSink,
       RTCPInstance,
       Groupsock,
       BufferSource *buffer_source,
       FrameFifo &fifo,
       
       methods:
           startPlaying
               - issues startPlaying on the live555 
                 event loop (for the internal live555 
                 filterchain defined in the Stream subclasses)
           afterPlaying
               - a live555 callback
    }
    
    H264 : public Stream {
       - Instantiates buffer_source = new BufferSource (.., fifo)
       - Creates the live555 internal filterchain
    }
    

RTSP Server

Some misc. observations / code walkthrough:

  • Inheritance: H264ServerMediaSubsession => ValkkaServerMediaSubsession => OnDemandServerMediaSubsession
  • H264ServerMediaSubsession is placed into LiveThread::server (which is RTSPServer instance)
  • H264ServerMediaSubsession::createNewStreamSource is called from OnDemandServerMediaSubsession::sdpLines when a DESCRIBE request is done on the RTSPServer
  • OnDemandServerMediaSubsession::closeStreamSource calls Medium::close on inputsource it obtained from H264ServerMediaSubsession::createNewStreamSource, when TEARDOWN is issued on the RTSPServer.
  • .. this basically closes our BufferSource instance (that's using a fifo), so we need to check if BufferSource has been destructed
  • .. this is done using a reference variable that's given to BufferSource (ValkkaServerMediaSubsession::source_alive). That variable is checked before calling BufferSource::handleFrame.
  • There is a tricky inner event loop that generates sps/pps info into the sdp string at H264ServerMediaSubsession::getAuxSDPLine .. (as this has been hacked from H264VideoFileServerMediaSubsession). That can get stuck sometimes (!)
  • Media sessions can be removed from the server with RTSPServer::removeServerMediaSession(media_session)

To get this into one's head, let's take a look at this diagram. "{}" means enclosing object:

RTSPServer {

   H264ServerMediaSubsession {
       - member "fifo" is a reference to FrameFifo
   
       - method : createNewStreamSource
           - creates buffer_source = BufferSource (.., fifo)
           - creates H264VideoStreamDiscreteFramer(buffer_source)
           => returns to RTPServer: H264VideoStreamDiscreteFramer(buffer_source)
           
       - inherited method sdpLines
       - inherited method closeStreamSource
   }
}