From 4c7eafb74b56eca40630bba0580d660874dc6268 Mon Sep 17 00:00:00 2001 From: Dario Casalinuovo Date: Thu, 2 Jun 2016 18:58:48 +0200 Subject: [PATCH] Include testing version rtsp_streamer * Rewritten from scratch to use live555 and BAdapterIO. --- src/add-ons/media/plugins/Jamfile | 1 + .../media/plugins/rtsp_streamer/Jamfile | 30 ++ .../plugins/rtsp_streamer/RTSPMediaIO.cpp | 121 +++++ .../media/plugins/rtsp_streamer/RTSPMediaIO.h | 67 +++ .../rtsp_streamer/RTSPStreamerPlugin.cpp | 40 ++ .../rtsp_streamer/RTSPStreamerPlugin.h | 20 + .../media/plugins/rtsp_streamer/rtsp.cpp | 447 ++++++++++++++++++ .../media/plugins/rtsp_streamer/rtsp.h | 34 ++ 8 files changed, 760 insertions(+) create mode 100644 src/add-ons/media/plugins/rtsp_streamer/Jamfile create mode 100644 src/add-ons/media/plugins/rtsp_streamer/RTSPMediaIO.cpp create mode 100644 src/add-ons/media/plugins/rtsp_streamer/RTSPMediaIO.h create mode 100644 src/add-ons/media/plugins/rtsp_streamer/RTSPStreamerPlugin.cpp create mode 100644 src/add-ons/media/plugins/rtsp_streamer/RTSPStreamerPlugin.h create mode 100644 src/add-ons/media/plugins/rtsp_streamer/rtsp.cpp create mode 100644 src/add-ons/media/plugins/rtsp_streamer/rtsp.h diff --git a/src/add-ons/media/plugins/Jamfile b/src/add-ons/media/plugins/Jamfile index c8ead7fe8b..4c11e7fe20 100644 --- a/src/add-ons/media/plugins/Jamfile +++ b/src/add-ons/media/plugins/Jamfile @@ -14,6 +14,7 @@ SubInclude HAIKU_TOP src add-ons media plugins mov_reader ; SubInclude HAIKU_TOP src add-ons media plugins mp4_reader ; SubInclude HAIKU_TOP src add-ons media plugins musepack ; SubInclude HAIKU_TOP src add-ons media plugins raw_decoder ; +SubInclude HAIKU_TOP src add-ons media plugins rtsp_streamer ; SubInclude HAIKU_TOP src add-ons media plugins wav_reader ; # The following add-ons are GPL licensed, and can only be used with diff --git a/src/add-ons/media/plugins/rtsp_streamer/Jamfile b/src/add-ons/media/plugins/rtsp_streamer/Jamfile new file mode 100644 index 0000000000..54dad62ad7 --- /dev/null +++ b/src/add-ons/media/plugins/rtsp_streamer/Jamfile @@ -0,0 +1,30 @@ +SubDir HAIKU_TOP src add-ons media plugins rtsp_streamer ; + +UsePrivateHeaders media shared ; + +# TODO: Investigate this +SubDirC++Flags -w ; + +local architectureObject ; +for architectureObject in [ MultiArchSubDirSetup ] { + on $(architectureObject) { + + UseBuildFeatureHeaders live555 ; + + Includes [ FGristFiles + rtsp.cpp + RTSPStreamerPlugin.cpp + RTSPMediaIO.cpp ] + : [ BuildFeatureAttribute live555 : headers ] ; + + Addon [ MultiArchDefaultGristFiles rtsp_streamer ] : + RTSPStreamerPlugin.cpp + RTSPMediaIO.cpp + rtsp.cpp + : + [ BuildFeatureAttribute live555 : libraries ] + be media network bnetapi shared + [ TargetLibstdc++ ] [ TargetLibsupc++ ] + ; + } +} diff --git a/src/add-ons/media/plugins/rtsp_streamer/RTSPMediaIO.cpp b/src/add-ons/media/plugins/rtsp_streamer/RTSPMediaIO.cpp new file mode 100644 index 0000000000..dcfa0fc8a1 --- /dev/null +++ b/src/add-ons/media/plugins/rtsp_streamer/RTSPMediaIO.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2016, Dario Casalinuovo. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "RTSPMediaIO.h" + + +#define LIVE555_VERBOSITY 1 + + +RTSPMediaIO::RTSPMediaIO(BUrl* ourUrl) + : + BAdapterIO( + B_MEDIA_STREAMING | B_MEDIA_MUTABLE_SIZE | B_MEDIA_SEEK_BACKWARD, + B_INFINITE_TIMEOUT), + fUrl(ourUrl), + fInitErr(B_OK), + fScheduler(NULL), + fEnv(NULL), + loopWatchVariable(0) +{ + fScheduler = BasicTaskScheduler::createNew(); + fEnv = BasicUsageEnvironment::createNew(*fScheduler); + + HaikuRTSPClient* client = new HaikuRTSPClient(*fEnv, fUrl->UrlString(), + 0, BuildInputAdapter()); + if (client == NULL) { + fInitErr = B_ERROR; + return; + } + + client->sendDescribeCommand(continueAfterDESCRIBE); + + fEnv->taskScheduler().doEventLoop(&loopWatchVariable); + + fInitErr = client->WaitForInit(5000000); +} + + +RTSPMediaIO::~RTSPMediaIO() +{ +} + + +status_t +RTSPMediaIO::InitCheck() const +{ + return fInitErr; +} + + +ssize_t +RTSPMediaIO::WriteAt(off_t position, const void* buffer, size_t size) +{ + return B_NOT_SUPPORTED; +} + + +HaikuRTSPClient::HaikuRTSPClient(UsageEnvironment& env, char const* rtspURL, + portNumBits tunnelOverHTTPPortNum, BInputAdapter* inputAdapter) + : + RTSPClient(env, rtspURL, LIVE555_VERBOSITY, "Haiku RTSP Streamer", + tunnelOverHTTPPortNum, -1), + iter(NULL), + session(NULL), + subsession(NULL), + streamTimerTask(NULL), + duration(0.0f), + fInputAdapter(inputAdapter) +{ + fInitPort = create_port(1, "RTSP Client wait port"); +} + + +status_t +HaikuRTSPClient::WaitForInit(bigtime_t timeout) +{ + status_t status = B_ERROR; + read_port_etc(fInitPort, NULL, &status, + sizeof(status), B_RELATIVE_TIMEOUT, timeout); + + close_port(fInitPort); + delete_port(fInitPort); + return status; +} + + +void +HaikuRTSPClient::NotifyError() +{ + status_t status = B_ERROR; + write_port(fInitPort, NULL, &status, sizeof(status)); +} + + +void +HaikuRTSPClient::NotifySucces() +{ + status_t status = B_OK; + write_port(fInitPort, NULL, &status, sizeof(status)); +} + + +HaikuRTSPClient::~HaikuRTSPClient() +{ + delete iter; + if (session != NULL) { + UsageEnvironment& env = session->envir(); + env.taskScheduler().unscheduleDelayedTask(streamTimerTask); + Medium::close(session); + } +} + + +BInputAdapter* +HaikuRTSPClient::GetInputAdapter() const +{ + return fInputAdapter; +} diff --git a/src/add-ons/media/plugins/rtsp_streamer/RTSPMediaIO.h b/src/add-ons/media/plugins/rtsp_streamer/RTSPMediaIO.h new file mode 100644 index 0000000000..630c64cf05 --- /dev/null +++ b/src/add-ons/media/plugins/rtsp_streamer/RTSPMediaIO.h @@ -0,0 +1,67 @@ +/* + * Copyright 2016, Dario Casalinuovo. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _RTSP_MEDIA_IO_H +#define _RTSP_MEDIA_IO_H + + +#include +#include + +#include "rtsp.h" + + +class RTSPMediaIO : public BAdapterIO +{ +public: + RTSPMediaIO(BUrl* ourUrl); + virtual ~RTSPMediaIO(); + + status_t InitCheck() const; + + virtual ssize_t WriteAt(off_t position, const void* buffer, + size_t size); +private: + BUrl* fUrl; + + TaskScheduler* fScheduler; + UsageEnvironment* fEnv; + char loopWatchVariable; + + status_t fInitErr; +}; + + +class HaikuRTSPClient : public RTSPClient +{ +public: + HaikuRTSPClient(UsageEnvironment& env, + char const* rtspURL, + portNumBits tunnelOverHTTPPortNum, + BInputAdapter* fInputAdapter); + + BInputAdapter* GetInputAdapter() const; + + status_t WaitForInit(bigtime_t timeout); + + void NotifyError(); + void NotifySucces(); + +protected: + virtual ~HaikuRTSPClient(); + +public: + + MediaSubsessionIterator* iter; + MediaSession* session; + MediaSubsession* subsession; + TaskToken streamTimerTask; + double duration; + +private: + BInputAdapter* fInputAdapter; + port_id fInitPort; +}; + +#endif diff --git a/src/add-ons/media/plugins/rtsp_streamer/RTSPStreamerPlugin.cpp b/src/add-ons/media/plugins/rtsp_streamer/RTSPStreamerPlugin.cpp new file mode 100644 index 0000000000..001d804ec2 --- /dev/null +++ b/src/add-ons/media/plugins/rtsp_streamer/RTSPStreamerPlugin.cpp @@ -0,0 +1,40 @@ + +#include "RTSPStreamerPlugin.h" + +#include "RTSPMediaIO.h" + + +RTSPStreamer::RTSPStreamer() +{ +} + + +RTSPStreamer::~RTSPStreamer() +{ +} + + +status_t +RTSPStreamer::Sniff(BUrl* url, BDataIO** source) +{ + RTSPMediaIO* ret = new RTSPMediaIO(url); + if (ret->InitCheck() == B_OK) { + *source = ret; + return B_OK; + } + delete ret; + return B_ERROR; +} + + +Streamer* +RTSPStreamerPlugin::NewStreamer() +{ + return new RTSPStreamer(); +} + + +MediaPlugin *instantiate_plugin() +{ + return new RTSPStreamerPlugin(); +} diff --git a/src/add-ons/media/plugins/rtsp_streamer/RTSPStreamerPlugin.h b/src/add-ons/media/plugins/rtsp_streamer/RTSPStreamerPlugin.h new file mode 100644 index 0000000000..eb743be59a --- /dev/null +++ b/src/add-ons/media/plugins/rtsp_streamer/RTSPStreamerPlugin.h @@ -0,0 +1,20 @@ +#ifndef _RTSP_STREAMER_PLUGIN_H +#define _RTSP_STREAMER_PLUGIN_H + +#include "StreamerPlugin.h" + +class RTSPStreamer : public Streamer { +public: + RTSPStreamer(); + virtual ~RTSPStreamer(); + + virtual status_t Sniff(BUrl* url, BDataIO** source); +}; + + +class RTSPStreamerPlugin : public StreamerPlugin { +public: + virtual Streamer* NewStreamer(); +}; + +#endif // _RTSP_STREAMER_PLUGIN_H diff --git a/src/add-ons/media/plugins/rtsp_streamer/rtsp.cpp b/src/add-ons/media/plugins/rtsp_streamer/rtsp.cpp new file mode 100644 index 0000000000..afe935e30b --- /dev/null +++ b/src/add-ons/media/plugins/rtsp_streamer/rtsp.cpp @@ -0,0 +1,447 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) +This library is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved +// Copyright (c) 2016, Dario Casalinuovo. All rights reserved. + + +#include "rtsp.h" + +#include + +#include "RTSPMediaIO.h" + + +#define REQUEST_STREAMING_OVER_TCP False +#define RECEIVE_BUFFER_SIZE 100000 + + +UsageEnvironment& operator<<(UsageEnvironment& env, + const RTSPClient& rtspClient) +{ + return env << "[URL:\"" << rtspClient.url() << "\"]: "; +} + + +UsageEnvironment& operator<<(UsageEnvironment& env, + const MediaSubsession& subsession) +{ + return env << subsession.mediumName() << "/" << subsession.codecName(); +} + + +class AdapterSink : public MediaSink +{ +public: + static AdapterSink* createNew(UsageEnvironment& env, + MediaSubsession& subsession, + BInputAdapter* inputAdapter, + char const* streamId = NULL); + +private: + AdapterSink(UsageEnvironment& env, + MediaSubsession& subsession, + char const* streamId, + BInputAdapter* inputAdapter); + + virtual ~AdapterSink(); + + static void afterGettingFrame(void* clientData, + unsigned frameSize, + unsigned numTruncatedBytes, + struct timeval presentationTime, + unsigned durationInMicroseconds); + + void afterGettingFrame(unsigned frameSize, + unsigned numTruncatedBytes, + struct timeval presentationTime, + unsigned durationInMicroseconds); + +private: + // redefined virtual functions: + virtual Boolean continuePlaying(); + +private: + BInputAdapter* fInputAdapter; + u_int8_t* fReceiveBuffer; + MediaSubsession& fSubsession; + char* fStreamId; +}; + +// Implementation of the RTSP 'response handlers': + +void continueAfterDESCRIBE(RTSPClient* rtspClient, + int resultCode, char* resultString) +{ + UsageEnvironment& env = rtspClient->envir(); + HaikuRTSPClient* client = (HaikuRTSPClient*) rtspClient; + do { + if (resultCode != 0) { + env << *rtspClient << "Failed to get a SDP description: " + << resultString << "\n"; + delete[] resultString; + + break; + } + + char* const sdpDescription = resultString; + env << *rtspClient << "Got a SDP description:\n" + << sdpDescription << "\n"; + + // Create a media session object from this SDP description: + client->session = MediaSession::createNew(env, sdpDescription); + delete[] sdpDescription; // because we don't need it anymore + if (client->session == NULL) { + env << *rtspClient + << "Failed to create a MediaSession object " + "from the SDP description: " + << env.getResultMsg() << "\n"; + + break; + } else if (!client->session->hasSubsessions()) { + env << *rtspClient << "This session has no media subsessions" + " (i.e., no \"m=\" lines)\n"; + + break; + } + + // Then, create and set up our data source objects for the session. + // We do this by iterating over the session's 'subsessions', + // calling "MediaSubsession::initiate()", + // and then sending a RTSP "SETUP" command, on each one. + // (Each 'subsession' will have its own data source.) + client->iter = new MediaSubsessionIterator(*client->session); + setupNextSubsession(rtspClient); + return; + } while (0); + + client->NotifyError(); + // An unrecoverable error occurred with this stream. + shutdownStream(rtspClient); +} + + +void setupNextSubsession(RTSPClient* rtspClient) +{ + UsageEnvironment& env = rtspClient->envir(); + HaikuRTSPClient* client = (HaikuRTSPClient*) rtspClient; + + client->subsession = client->iter->next(); + if (client->subsession != NULL) { + if (!client->subsession->initiate()) { + + env << *rtspClient << "Failed to initiate the \"" + << *client->subsession << "\" subsession: " + << env.getResultMsg() << "\n"; + + // give up on this subsession; go to the next one + setupNextSubsession(rtspClient); + } + else { + env << *rtspClient << "Initiated the \"" + << *client->subsession << "\" subsession ("; + + if (client->subsession->rtcpIsMuxed()) { + env << "client port " << client->subsession->clientPortNum(); + } else { + env << "client ports " << client->subsession->clientPortNum() + << "-" << client->subsession->clientPortNum() + 1; + } + env << ")\n"; + + // Continue setting up this subsession, + // by sending a RTSP "SETUP" command: + rtspClient->sendSetupCommand(*client->subsession, + continueAfterSETUP, False, REQUEST_STREAMING_OVER_TCP); + } + return; + } + + // We've finished setting up all of the subsessions. + // Now, send a RTSP "PLAY" command to start the streaming: + if (client->session->absStartTime() != NULL) { + // Special case: The stream is indexed by 'absolute' time, + // so send an appropriate "PLAY" command: + rtspClient->sendPlayCommand(*client->session, continueAfterPLAY, + client->session->absStartTime(), client->session->absEndTime()); + } + else { + client->duration = client->session->playEndTime() + - client->session->playStartTime(); + rtspClient->sendPlayCommand(*client->session, continueAfterPLAY); + } +} + + +void continueAfterSETUP(RTSPClient* rtspClient, + int resultCode, char* resultString) +{ + do { + UsageEnvironment& env = rtspClient->envir(); + HaikuRTSPClient* client = (HaikuRTSPClient*) rtspClient; + + if (resultCode != 0) { + env << *rtspClient << "Failed to set up the \"" + << *client->subsession << "\" subsession: " + << resultString << "\n"; + break; + } + + env << *rtspClient << "Set up the \"" + << *client->subsession << "\" subsession ("; + if (client->subsession->rtcpIsMuxed()) { + env << "client port " << client->subsession->clientPortNum(); + } + else { + env << "client ports " << client->subsession->clientPortNum() + << "-" << client->subsession->clientPortNum() + 1; + } + env << ")\n"; + + // Having successfully setup the subsession, create a data sink for it, + // and call "startPlaying()" on it. + // (This will prepare the data sink to receive data; the actual + // flow of data from the client won't start happening until later, + // after we've sent a RTSP "PLAY" command.) + + client->subsession->sink = AdapterSink::createNew(env, *client->subsession, + ((HaikuRTSPClient*)rtspClient)->GetInputAdapter(), rtspClient->url()); + // perhaps use your own custom "MediaSink" subclass instead + if (client->subsession->sink == NULL) { + env << *rtspClient << "Failed to create a data sink for the \"" + << *client->subsession << "\" subsession: " + << env.getResultMsg() << "\n"; + break; + } + + env << *rtspClient << "Created a data sink for the \"" + << *client->subsession << "\" subsession\n"; + // a hack to let subsession handler functions + // get the "RTSPClient" from the subsession + client->subsession->miscPtr = rtspClient; + client->subsession->sink + ->startPlaying(*(client->subsession->readSource()), + subsessionAfterPlaying, client->subsession); + // Also set a handler to be called if a RTCP "BYE" + // arrives for this subsession: + if (client->subsession->rtcpInstance() != NULL) { + client->subsession->rtcpInstance()->setByeHandler( + subsessionByeHandler, + client->subsession); + } + } while (0); + delete[] resultString; + + // Set up the next subsession, if any: + setupNextSubsession(rtspClient); +} + + +void continueAfterPLAY(RTSPClient* rtspClient, + int resultCode, char* resultString) +{ + Boolean success = False; + UsageEnvironment& env = rtspClient->envir(); + HaikuRTSPClient* client = (HaikuRTSPClient*) rtspClient; + + do { + if (resultCode != 0) { + env << *rtspClient << "Failed to start playing session: " + << resultString << "\n"; + break; + } + + // Set a timer to be handled at the end of the stream's + // expected duration (if the stream does not already signal its end + // using a RTCP "BYE"). This is optional. If, instead, you want + // to keep the stream active - e.g., so you can later + // 'seek' back within it and do another RTSP "PLAY" + // - then you can omit this code. + // (Alternatively, if you don't want to receive the entire stream, + // you could set this timer for some shorter value.) + if (client->duration > 0) { + // number of seconds extra to delay, + // after the stream's expected duration. (This is optional.) + unsigned const delaySlop = 2; + client->duration += delaySlop; + unsigned uSecsToDelay = (unsigned)(client->duration * 1000000); + client->streamTimerTask + = env.taskScheduler().scheduleDelayedTask(uSecsToDelay, + (TaskFunc*)streamTimerHandler, rtspClient); + } + + env << *rtspClient << "Started playing session"; + if (client->duration > 0) { + env << " (for up to " << client->duration << " seconds)"; + } + env << "...\n"; + + success = True; + } while (0); + delete[] resultString; + + if (!success) { + client->NotifyError(); + // An unrecoverable error occurred with this stream. + shutdownStream(rtspClient); + } else + client->NotifySucces(); +} + +// Implementation of the other event handlers: + +void subsessionAfterPlaying(void* clientData) +{ + MediaSubsession* subsession = (MediaSubsession*)clientData; + RTSPClient* rtspClient = (RTSPClient*)(subsession->miscPtr); + + // Begin by closing this subsession's stream: + Medium::close(subsession->sink); + subsession->sink = NULL; + + // Next, check whether *all* subsessions' streams have now been closed: + MediaSession& session = subsession->parentSession(); + MediaSubsessionIterator iter(session); + while ((subsession = iter.next()) != NULL) { + if (subsession->sink != NULL) + return; // this subsession is still active + } + + // All subsessions' streams have now been closed, so shutdown the client: + shutdownStream(rtspClient); +} + + +void subsessionByeHandler(void* clientData) +{ + MediaSubsession* subsession = (MediaSubsession*)clientData; + RTSPClient* rtspClient = (RTSPClient*)subsession->miscPtr; + UsageEnvironment& env = rtspClient->envir(); + + env << *rtspClient << "Received RTCP \"BYE\" on \"" + << *subsession << "\" subsession\n"; + + // Now act as if the subsession had closed: + subsessionAfterPlaying(subsession); +} + + +void streamTimerHandler(void* clientData) +{ + HaikuRTSPClient* client = (HaikuRTSPClient*)clientData; + + client->streamTimerTask = NULL; + + // Shut down the stream: + shutdownStream(client); +} + + +void shutdownStream(RTSPClient* rtspClient, int exitCode) +{ + UsageEnvironment& env = rtspClient->envir(); + HaikuRTSPClient* client = (HaikuRTSPClient*) rtspClient; + + // First, check whether any subsessions have still to be closed: + if (client->session != NULL) { + Boolean someSubsessionsWereActive = False; + MediaSubsessionIterator iter(*client->session); + MediaSubsession* subsession; + + while ((subsession = iter.next()) != NULL) { + if (subsession->sink != NULL) { + Medium::close(subsession->sink); + subsession->sink = NULL; + + if (subsession->rtcpInstance() != NULL) { + // in case the server sends a RTCP "BYE" + // while handling "TEARDOWN" + subsession->rtcpInstance()->setByeHandler(NULL, NULL); + } + + someSubsessionsWereActive = True; + } + } + + if (someSubsessionsWereActive) { + // Send a RTSP "TEARDOWN" command, + // to tell the server to shutdown the stream. + // Don't bother handling the response to the "TEARDOWN". + rtspClient->sendTeardownCommand(*client->session, NULL); + } + } + + env << *rtspClient << "Closing the stream.\n"; + Medium::close(rtspClient); + // Note that this will also cause this stream's + // "StreamClientState" structure to get reclaimed. +} + + +AdapterSink* AdapterSink::createNew(UsageEnvironment& env, + MediaSubsession& subsession, BInputAdapter* inputAdapter, + char const* streamId) +{ + return new AdapterSink(env, subsession, streamId, inputAdapter); +} + + +AdapterSink::AdapterSink(UsageEnvironment& env, MediaSubsession& subsession, + char const* streamId, BInputAdapter* inputAdapter) + : + MediaSink(env), + fSubsession(subsession), + fInputAdapter(inputAdapter) +{ + fStreamId = strDup(streamId); + fReceiveBuffer = new u_int8_t[RECEIVE_BUFFER_SIZE]; +} + + +AdapterSink::~AdapterSink() +{ + delete[] fReceiveBuffer; + delete[] fStreamId; +} + + +void AdapterSink::afterGettingFrame(void* clientData, unsigned frameSize, + unsigned numTruncatedBytes, struct timeval presentationTime, + unsigned durationInMicroseconds) +{ + AdapterSink* sink = (AdapterSink*)clientData; + sink->afterGettingFrame(frameSize, numTruncatedBytes, + presentationTime, durationInMicroseconds); +} + + +void +AdapterSink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, + struct timeval presentationTime, unsigned /*durationInMicroseconds*/) +{ + fInputAdapter->Write(fReceiveBuffer, frameSize); + continuePlaying(); +} + + +Boolean +AdapterSink::continuePlaying() +{ + if (fSource == NULL) + return False; + + fSource->getNextFrame(fReceiveBuffer, RECEIVE_BUFFER_SIZE, + afterGettingFrame, this, + onSourceClosure, this); + return True; +} diff --git a/src/add-ons/media/plugins/rtsp_streamer/rtsp.h b/src/add-ons/media/plugins/rtsp_streamer/rtsp.h new file mode 100644 index 0000000000..a8d2404045 --- /dev/null +++ b/src/add-ons/media/plugins/rtsp_streamer/rtsp.h @@ -0,0 +1,34 @@ +/********** +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the +Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. (See .) +This library is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**********/ +// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved +// Copyright (c) 2016, Dario Casalinuovo. All rights reserved. +#ifndef _RTSP_H +#define _RTSP_H + +#include "liveMedia.hh" +#include "BasicUsageEnvironment.hh" + + +void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString); +void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, char* resultString); +void continueAfterPLAY(RTSPClient* rtspClient, int resultCode, char* resultString); + +void subsessionAfterPlaying(void* clientData); +void subsessionByeHandler(void* clientData); +void streamTimerHandler(void* clientData); +void setupNextSubsession(RTSPClient* rtspClient); +void shutdownStream(RTSPClient* rtspClient, int exitCode = 1); + + +#endif