title: Live555: X264 Stream Live source based on "testOnDemandRTSPServer"
body: <p>I am trying to create a rtsp Server that streams the OpenGL output of my program. I had a look at <a href="https://stackoverflow.com/questions/13863673/how-to-write-a-live555-framedsource-to-allow-me-to-stream-h-264-live">How to write a Live555 FramedSource to allow me to stream H.264 live</a>, but I need the stream to be unicast. So I had a look at testOnDemandRTSPServer. Using the same Code fails. To my understanding I need to provide memory in which I store my h264 frames so the OnDemandServer can read them on Demand.</p>
  
  <p>H264VideoStreamServerMediaSubsession.cpp</p>
  
  <pre><code>H264VideoStreamServerMediaSubsession*
  H264VideoStreamServerMediaSubsession::createNew(UsageEnvironment&amp; env,
                            Boolean reuseFirstSource) {
    return new H264VideoStreamServerMediaSubsession(env, reuseFirstSource);
  }
  
  H264VideoStreamServerMediaSubsession::H264VideoStreamServerMediaSubsession(UsageEnvironment&amp; env, Boolean reuseFirstSource)
    : OnDemandServerMediaSubsession(env, reuseFirstSource), fAuxSDPLine(NULL), fDoneFlag(0), fDummyRTPSink(NULL) {
  }
  
  H264VideoStreamServerMediaSubsession::~H264VideoStreamServerMediaSubsession() {
    delete[] fAuxSDPLine;
  }
  
  static void afterPlayingDummy(void* clientData) {
    H264VideoStreamServerMediaSubsession* subsess = (H264VideoStreamServerMediaSubsession*)clientData;
    subsess-&gt;afterPlayingDummy1();
  }
  
  void H264VideoStreamServerMediaSubsession::afterPlayingDummy1() {
    // Unschedule any pending 'checking' task:
    envir().taskScheduler().unscheduleDelayedTask(nextTask());
    // Signal the event loop that we're done:
    setDoneFlag();
  }
  
  static void checkForAuxSDPLine(void* clientData) {
    H264VideoStreamServerMediaSubsession* subsess = (H264VideoStreamServerMediaSubsession*)clientData;
    subsess-&gt;checkForAuxSDPLine1();
  }
  
  void H264VideoStreamServerMediaSubsession::checkForAuxSDPLine1() {
    char const* dasl;
  
    if (fAuxSDPLine != NULL) {
      // Signal the event loop that we're done:
      setDoneFlag();
    } else if (fDummyRTPSink != NULL &amp;&amp; (dasl = fDummyRTPSink-&gt;auxSDPLine()) != NULL) {
      fAuxSDPLine = strDup(dasl);
      fDummyRTPSink = NULL;
  
      // Signal the event loop that we're done:
      setDoneFlag();
    } else {
      // try again after a brief delay:
      int uSecsToDelay = 100000; // 100 ms
      nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecsToDelay,
                    (TaskFunc*)checkForAuxSDPLine, this);
    }
  }
  
  char const* H264VideoStreamServerMediaSubsession::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-&gt;startPlaying(*inputSource, afterPlayingDummy, this);
  
      // Check whether the sink's 'auxSDPLine()' is ready:
      checkForAuxSDPLine(this);
    }
  
    envir().taskScheduler().doEventLoop(&amp;fDoneFlag);
  
    return fAuxSDPLine;
  }
  
  FramedSource* H264VideoStreamServerMediaSubsession::createNewStreamSource(unsigned /*clientSessionId*/, unsigned&amp; estBitrate) {
    estBitrate = 500; // kb
    megamol::remotecontrol::View3D_MRC *parent = (megamol::remotecontrol::View3D_MRC*)this-&gt;parent;
    return H264VideoStreamFramer::createNew(envir(), parent-&gt;h264FramedSource);
  }
  
  RTPSink* H264VideoStreamServerMediaSubsession::createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* /*inputSource*/) {
    return H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
  }
  </code></pre>
  
  <p>FramedSource.cpp</p>
  
  <pre><code>H264FramedSource* H264FramedSource::createNew(UsageEnvironment&amp; env,
                                            unsigned preferredFrameSize,
                                            unsigned playTimePerFrame)
  {
      return new H264FramedSource(env, preferredFrameSize, playTimePerFrame);
  }
  
  H264FramedSource::H264FramedSource(UsageEnvironment&amp; env,
                                 unsigned preferredFrameSize,
                                 unsigned playTimePerFrame)
      : FramedSource(env),
      fPreferredFrameSize(fMaxSize),
      fPlayTimePerFrame(playTimePerFrame),
      fLastPlayTime(0),
      fCurIndex(0)
  {
  
      x264_param_default_preset(&amp;param, "veryfast", "zerolatency");
      param.i_threads = 1;
      param.i_width = 1024;
      param.i_height = 768;
      param.i_fps_num = 30;
      param.i_fps_den = 1;
      // Intra refres:
      param.i_keyint_max = 60;
      param.b_intra_refresh = 1;
      //Rate control:
      param.rc.i_rc_method = X264_RC_CRF;
      param.rc.f_rf_constant = 25;
      param.rc.f_rf_constant_max = 35;
      param.i_sps_id = 7;
      //For streaming:
      param.b_repeat_headers = 1;
      param.b_annexb = 1;
      x264_param_apply_profile(&amp;param, "baseline");
  
      param.i_log_level = X264_LOG_ERROR;
  
      encoder = x264_encoder_open(&amp;param);
      pic_in.i_type            = X264_TYPE_AUTO;
      pic_in.i_qpplus1         = 0;
      pic_in.img.i_csp         = X264_CSP_I420;
      pic_in.img.i_plane       = 3;
  
  
      x264_picture_alloc(&amp;pic_in, X264_CSP_I420, 1024, 768);
  
      convertCtx = sws_getContext(1024, 768, PIX_FMT_RGBA, 1024, 768, PIX_FMT_YUV420P, SWS_FAST_BILINEAR, NULL, NULL, NULL);
      eventTriggerId = envir().taskScheduler().createEventTrigger(deliverFrame0);
  }
  
  H264FramedSource::~H264FramedSource()
  {
      envir().taskScheduler().deleteEventTrigger(eventTriggerId);
      eventTriggerId = 0;
  }
  
  void H264FramedSource::AddToBuffer(uint8_t* buf, int surfaceSizeInBytes)
  {
      uint8_t* surfaceData = (new uint8_t[surfaceSizeInBytes]);
  
      memcpy(surfaceData, buf, surfaceSizeInBytes);
  
      int srcstride = 1024*4;
      sws_scale(convertCtx, &amp;surfaceData, &amp;srcstride,0, 768, pic_in.img.plane, pic_in.img.i_stride);
      x264_nal_t* nals = NULL;
      int i_nals = 0;
      int frame_size = -1;
  
  
      frame_size = x264_encoder_encode(encoder, &amp;nals, &amp;i_nals, &amp;pic_in, &amp;pic_out);
  
      static bool finished = false;
  
      if (frame_size &gt;= 0)
      {
      static bool alreadydone = false;
      if(!alreadydone)
      {
  
          x264_encoder_headers(encoder, &amp;nals, &amp;i_nals);
          alreadydone = true;
      }
      for(int i = 0; i &lt; i_nals; ++i)
      {
          m_queue.push(nals[i]);
      }
      }
      delete [] surfaceData;
      surfaceData = nullptr;
  
      envir().taskScheduler().triggerEvent(eventTriggerId, this);
  }
  
  void H264FramedSource::doGetNextFrame()
  {
      deliverFrame();
  }
  
  void H264FramedSource::deliverFrame0(void* clientData)
  {
      ((H264FramedSource*)clientData)-&gt;deliverFrame();
  }
  
  void H264FramedSource::deliverFrame()
  {
      x264_nal_t nalToDeliver;
  
      if (fPlayTimePerFrame &gt; 0 &amp;&amp; fPreferredFrameSize &gt; 0) {
      if (fPresentationTime.tv_sec == 0 &amp;&amp; fPresentationTime.tv_usec == 0) {
          // This is the first frame, so use the current time:
          gettimeofday(&amp;fPresentationTime, NULL);
      } else {
          // Increment by the play time of the previous data:
          unsigned uSeconds   = fPresentationTime.tv_usec + fLastPlayTime;
          fPresentationTime.tv_sec += uSeconds/1000000;
          fPresentationTime.tv_usec = uSeconds%1000000;
      }
  
      // Remember the play time of this data:
      fLastPlayTime = (fPlayTimePerFrame*fFrameSize)/fPreferredFrameSize;
      fDurationInMicroseconds = fLastPlayTime;
      } else {
      // We don't know a specific play time duration for this data,
      // so just record the current time as being the 'presentation time':
      gettimeofday(&amp;fPresentationTime, NULL);
      }
  
      if(!m_queue.empty())
      {
      m_queue.wait_and_pop(nalToDeliver);
  
      uint8_t* newFrameDataStart = (uint8_t*)0xD15EA5E;
  
      newFrameDataStart = (uint8_t*)(nalToDeliver.p_payload);
      unsigned newFrameSize = nalToDeliver.i_payload;
  
      // Deliver the data here:
      if (newFrameSize &gt; fMaxSize) {
          fFrameSize = fMaxSize;
          fNumTruncatedBytes = newFrameSize - fMaxSize;
      }
      else {
          fFrameSize = newFrameSize;
      }
  
      memcpy(fTo, nalToDeliver.p_payload, nalToDeliver.i_payload);
  
      FramedSource::afterGetting(this);
      }
  }
  </code></pre>
  
  <p>Relevant part of the RTSP-Server Therad</p>
  
  <pre><code>  RTSPServer* rtspServer = RTSPServer::createNew(*(parent-&gt;env), 8554, NULL);
    if (rtspServer == NULL) {
      *(parent-&gt;env) &lt;&lt; "Failed to create RTSP server: " &lt;&lt; (parent-&gt;env)-&gt;getResultMsg() &lt;&lt; "\n";
      exit(1);
    }
    char const* streamName = "Stream";
    parent-&gt;h264FramedSource = H264FramedSource::createNew(*(parent-&gt;env), 0, 0);
    H264VideoStreamServerMediaSubsession *h264VideoStreamServerMediaSubsession = H264VideoStreamServerMediaSubsession::createNew(*(parent-&gt;env), true);
    h264VideoStreamServerMediaSubsession-&gt;parent = parent;
    sms-&gt;addSubsession(h264VideoStreamServerMediaSubsession);
    rtspServer-&gt;addServerMediaSession(sms);
  
    parent-&gt;env-&gt;taskScheduler().doEventLoop(); // does not return
  </code></pre>
  
  <p>Once a connection exists the render loop calls</p>
  
  <pre><code>h264FramedSource-&gt;AddToBuffer(videoData, 1024*768*4);
  </code></pre>