Hi,

i wrote my own Subclass of FramedSource and OnDemandServerMediaSubsession to stream a h264 video encoded by libav(ffmpeg). I used these classes the same way testOnDemandRTSPServer.cpp does (as you can see in my main). When i try to connect via vlc to the rtsp server my framed source gets created and after that destroyed directly (deliverFrame0() and doGetNextFrame() are not being called).

I dont know what im doing wrong so here is my code:

imLiveStreamSource.cpp // derivec from framedsource
###################################################

#include "imLiveStreamSource.h"
#include <GroupsockHelper.hh> // for gettimeofday()

EventTriggerId imLiveStreamSource::eventTriggerId = 0;
unsigned imLiveStreamSource::mReferenceCount = 0;

imLiveStreamSource* imLiveStreamSource::createNew(UsageEnvironment& env, imLiveStreamParameters params)
{
    return new imLiveStreamSource(env, params);
}

imLiveStreamSource::imLiveStreamSource(UsageEnvironment& env, imLiveStreamParameters param)
    : FramedSource(env),
    mReady(true),
    mParameters(param),
    mEncodedVideoFrame(NULL),
    mEncodedVideoFrameSize(0),
//    mIOService(new boost::asio::io_service()),
//    mWork(new boost::asio::io_service::work(*mIOService)),
//    mTimer(*mIOService),
    mEncodingEnabled(true),
    mNextEncodedVideoFrameWanted(false)
{
    if(mReferenceCount == 0)
    {

        av_register_all();
        mOutputFormat = av_guess_format(NULL, "test.h264", NULL);

        if(!mOutputFormat)
        {
std::cout << "Cannot guess output format! Using mpeg!" << std::endl;
            mOutputFormat = av_guess_format("mpeg", NULL, NULL);
        }
        if(!mOutputFormat)
        {
std::cout << "Could not find suitable output format." << std::endl;
            mReady = false;
        }

        mContext = avformat_alloc_context();
        if(!mContext)
        {
            std::cout << "Cannot allocate avformat memory." << std::endl;
            mReady = false;
        }
        mContext->oformat = mOutputFormat;

        mVideoStream = NULL;
        mOutputFormat->audio_codec = CODEC_ID_NONE;
mVideoStream = addVideoStream(mContext, mOutputFormat->video_codec);

        if(mVideoStream)
            openVideo(mContext, mVideoStream);

        for (int x = 0; x < NUMBER_OF_THREADS; x++)
        {
//mWorkerThreads.create_thread(boost::bind(&imLiveStreamSource::workerThread, this));
        }

//mTimer.expires_from_now(boost::posix_time::seconds((int)(1/mParameters.mFrameRate))); //mTimer.async_wait(boost::bind(&imLiveStreamSource::encodingThread, this, _1));
    }
    ++mReferenceCount;

    // TODO: local init stuff


    if(eventTriggerId == 0)
    {
eventTriggerId = envir().taskScheduler().createEventTrigger(deliverFrame0);
    }
}

imLiveStreamSource::~imLiveStreamSource()
{
// Any instance-specific 'destruction' (i.e., resetting) of the device would be done here:
  //%%% TO BE WRITTEN %%%

    --mReferenceCount;
    if(mReferenceCount == 0)
    {
        //! Free video encoding stuff
        if(mVideoStream)
            closeVideo(mContext, mVideoStream);
        for(int i = 0; i < mContext->nb_streams; i++)
        {
            av_freep(&mContext->streams[i]->codec);
            av_freep(&mContext->streams[i]);
        }
        av_free(mContext);

        //! Video streaming stuff
        envir().taskScheduler().deleteEventTrigger(eventTriggerId);
        eventTriggerId = 0;
    }
}

void imLiveStreamSource::doGetNextFrame()
{
// This function is called (by our 'downstream' object) when it asks for new data.

// Note: If, for some reason, the source device stops being readable (e.g., it gets closed), then you do the following:
  if(!mReady)
  {
    handleClosure(this);
    return;
  }

// If a new frame of data is immediately available to be delivered, then do this now:
  if (mNextEncodedVideoFrame) {
      write_video_frame(mContext, mVideoStream);
    deliverFrame();
  }
  else
      mNextEncodedVideoFrameWanted = true;

// No new data is immediately available to be delivered. We don't do anything more here. // Instead, our event trigger must be called (e.g., from a separate thread) when new data becomes available.
}

void imLiveStreamSource::deliverFrame0(void* clientData)
{
  ((imLiveStreamSource*)clientData)->deliverFrame();
}

void imLiveStreamSource::deliverFrame()
{

if (!isCurrentlyAwaitingData()) return; // we're not ready for the data yet

  u_int8_t* newFrameDataStart = mEncodedVideoFrame;
  unsigned int newFrameSize = (int)(mEncodedVideoFrameSize);

  // Deliver the data here:
  if (newFrameSize > fMaxSize) {
    fFrameSize = fMaxSize;
    fNumTruncatedBytes = newFrameSize - fMaxSize;
  } else {
    fFrameSize = newFrameSize;
  }
gettimeofday(&fPresentationTime, NULL); // If you have a more accurate time - e.g., from an encoder - then use that instead. // If the device is *not* a 'live source' (e.g., it comes instead from a file or buffer), then set "fDurationInMicroseconds" here.
  memmove(fTo, newFrameDataStart, fFrameSize);
  mNextEncodedVideoFrame = false;

  // After delivering the data, inform the reader that it is now available:
  FramedSource::afterGetting(this);
}

######################################################

imLiveStreamMediaSubsession.cpp //derived from OnDemandServerMediaSubsession

######################################################

imLiveStreamMediaSubsession::imLiveStreamMediaSubsession(UsageEnvironment& env, char const* fileName, Boolean reuseFirstSource)
  : OnDemandServerMediaSubsession(env, reuseFirstSource)
{
}

imLiveStreamMediaSubsession::~imLiveStreamMediaSubsession()
{
}

imLiveStreamMediaSubsession* imLiveStreamMediaSubsession::createNew(UsageEnvironment& env, char const* fileName, Boolean reuseFirstSource)
{
  return new imLiveStreamMediaSubsession(env, fileName, reuseFirstSource);
}

FramedSource* imLiveStreamMediaSubsession::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate)
{
  estBitrate = 400; // kbps, estimate ??

  imLiveStreamParameters param;
  param.mBitRate = 400000;
  param.mCodec = "x264";
  param.mFrameRate = 24;
  param.mHeight = 480;
  param.mWidth = 800;
  // Create a framer for the Video Elementary Stream:
  return imLiveStreamSource::createNew(envir(), param);
}

RTPSink* imLiveStreamMediaSubsession::createNewRTPSink(Groupsock* rtpGroupsock,
                                  unsigned char rtpPayloadTypeIfDynamic,
                                  FramedSource* /*inputSource*/)
{
return H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
}


######################################################

main.cpp

######################################################

int main(int argc, char** argv) {
  // Begin by setting up our usage environment:
  TaskScheduler* scheduler = BasicTaskScheduler::createNew();
  env = BasicUsageEnvironment::createNew(*scheduler);

  UserAuthenticationDatabase* authDB = NULL;

  // Create the RTSP server:
  RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB);
  if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
    exit(1);
  }

  char const* descriptionString
    = "Session streamed by \"INGAme\"";

  // A H.264 video elementary stream:
  {
    char const* streamName = "h264ESVideoTest";
    char const* inputFileName = "test.264";

ServerMediaSession* sms = ServerMediaSession::createNew(*env, streamName, streamName, descriptionString); sms->addSubsession(imLiveStreamMediaSubsession::createNew(*env, inputFileName, reuseFirstSource));
    rtspServer->addServerMediaSession(sms);

    announceStream(rtspServer, sms, streamName, inputFileName);
  }

  // Also, attempt to create a HTTP server for RTSP-over-HTTP tunneling.
// Try first with the default HTTP port (80), and then with the alternative HTTP
  // port numbers (8000 and 8080).

if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) { *env << "\n(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling.)\n";
  } else {
    *env << "\n(RTSP-over-HTTP tunneling is not available.)\n";
  }

  env->taskScheduler().doEventLoop(); // does not return

  return 0; // only to prevent compiler warning
}

static void announceStream(RTSPServer* rtspServer, ServerMediaSession* sms,
               char const* streamName, char const* inputFileName) {
  char* url = rtspServer->rtspURL(sms);
  UsageEnvironment& env = rtspServer->envir();
  env << "\n\"" << streamName << "\" stream, from the file \""
<< inputFileName << "\"\n";
  env << "Play this stream using the URL \"" << url << "\"\n";
  delete[] url;
}

#########################################

Thank you for your time reading this!

Christian


_______________________________________________
live-devel mailing list
live-devel@lists.live555.com
http://lists.live555.com/mailman/listinfo/live-devel

Reply via email to