14c7eafb7SDario Casalinuovo/**********
24c7eafb7SDario CasalinuovoThis library is free software; you can redistribute it and/or modify it under
34c7eafb7SDario Casalinuovothe terms of the GNU Lesser General Public License as published by the
44c7eafb7SDario CasalinuovoFree Software Foundation; either version 2.1 of the License, or (at your
54c7eafb7SDario Casalinuovooption) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
64c7eafb7SDario CasalinuovoThis library is distributed in the hope that it will be useful, but WITHOUT
74c7eafb7SDario CasalinuovoANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
84c7eafb7SDario CasalinuovoFOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
94c7eafb7SDario Casalinuovomore details.
104c7eafb7SDario CasalinuovoYou should have received a copy of the GNU Lesser General Public License
114c7eafb7SDario Casalinuovoalong with this library; if not, write to the Free Software Foundation, Inc.,
124c7eafb7SDario Casalinuovo51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
134c7eafb7SDario Casalinuovo**********/
144c7eafb7SDario Casalinuovo// Copyright (c) 1996-2016, Live Networks, Inc.  All rights reserved
154c7eafb7SDario Casalinuovo// Copyright (c) 2016, Dario Casalinuovo. All rights reserved.
164c7eafb7SDario Casalinuovo
174c7eafb7SDario Casalinuovo
184c7eafb7SDario Casalinuovo#include "rtsp.h"
194c7eafb7SDario Casalinuovo
204c7eafb7SDario Casalinuovo#include <AdapterIO.h>
214c7eafb7SDario Casalinuovo
224c7eafb7SDario Casalinuovo#include "RTSPMediaIO.h"
234c7eafb7SDario Casalinuovo
244c7eafb7SDario Casalinuovo
254c7eafb7SDario Casalinuovo#define REQUEST_STREAMING_OVER_TCP False
264c7eafb7SDario Casalinuovo#define RECEIVE_BUFFER_SIZE 100000
274c7eafb7SDario Casalinuovo
284c7eafb7SDario Casalinuovo
294c7eafb7SDario CasalinuovoUsageEnvironment& operator<<(UsageEnvironment& env,
304c7eafb7SDario Casalinuovo	const RTSPClient& rtspClient)
314c7eafb7SDario Casalinuovo{
324c7eafb7SDario Casalinuovo	return env << "[URL:\"" << rtspClient.url() << "\"]: ";
334c7eafb7SDario Casalinuovo}
344c7eafb7SDario Casalinuovo
354c7eafb7SDario Casalinuovo
364c7eafb7SDario CasalinuovoUsageEnvironment& operator<<(UsageEnvironment& env,
374c7eafb7SDario Casalinuovo	const MediaSubsession& subsession)
384c7eafb7SDario Casalinuovo{
394c7eafb7SDario Casalinuovo	return env << subsession.mediumName() << "/" << subsession.codecName();
404c7eafb7SDario Casalinuovo}
414c7eafb7SDario Casalinuovo
424c7eafb7SDario Casalinuovo
434c7eafb7SDario Casalinuovoclass AdapterSink : public MediaSink
444c7eafb7SDario Casalinuovo{
454c7eafb7SDario Casalinuovopublic:
46d004f813SDario Casalinuovo			static	 			AdapterSink* createNew(UsageEnvironment& env,
47d004f813SDario Casalinuovo									MediaSubsession& subsession,
48d004f813SDario Casalinuovo									BInputAdapter* inputAdapter,
49d004f813SDario Casalinuovo									char const* streamId = NULL);
504c7eafb7SDario Casalinuovo
514c7eafb7SDario Casalinuovoprivate:
52d004f813SDario Casalinuovo								AdapterSink(UsageEnvironment& env,
53d004f813SDario Casalinuovo									MediaSubsession& subsession,
54d004f813SDario Casalinuovo									char const* streamId,
55d004f813SDario Casalinuovo									BInputAdapter* inputAdapter);
564c7eafb7SDario Casalinuovo
57d004f813SDario Casalinuovo	virtual 					~AdapterSink();
584c7eafb7SDario Casalinuovo
59d004f813SDario Casalinuovo			static void			afterGettingFrame(void* clientData,
60d004f813SDario Casalinuovo									unsigned frameSize,
61d004f813SDario Casalinuovo									unsigned numTruncatedBytes,
62d004f813SDario Casalinuovo									struct timeval presentationTime,
63d004f813SDario Casalinuovo									unsigned durationInMicroseconds);
644c7eafb7SDario Casalinuovo
65d004f813SDario Casalinuovo			void				afterGettingFrame(unsigned frameSize,
66d004f813SDario Casalinuovo									unsigned numTruncatedBytes,
67d004f813SDario Casalinuovo									struct timeval presentationTime,
68d004f813SDario Casalinuovo									unsigned durationInMicroseconds);
694c7eafb7SDario Casalinuovo
704c7eafb7SDario Casalinuovoprivate:
714c7eafb7SDario Casalinuovo	// redefined virtual functions:
72d004f813SDario Casalinuovo	virtual Boolean				continuePlaying();
734c7eafb7SDario Casalinuovo
744c7eafb7SDario Casalinuovoprivate:
75d004f813SDario Casalinuovo			BInputAdapter*		fInputAdapter;
76d004f813SDario Casalinuovo			u_int8_t*			fReceiveBuffer;
77d004f813SDario Casalinuovo			MediaSubsession&	fSubsession;
78d004f813SDario Casalinuovo			char*				fStreamId;
794c7eafb7SDario Casalinuovo};
804c7eafb7SDario Casalinuovo
814c7eafb7SDario Casalinuovo// Implementation of the RTSP 'response handlers':
824c7eafb7SDario Casalinuovo
834c7eafb7SDario Casalinuovovoid continueAfterDESCRIBE(RTSPClient* rtspClient,
844c7eafb7SDario Casalinuovo	int resultCode, char* resultString)
854c7eafb7SDario Casalinuovo{
864c7eafb7SDario Casalinuovo	UsageEnvironment& env = rtspClient->envir();
874c7eafb7SDario Casalinuovo	HaikuRTSPClient* client = (HaikuRTSPClient*) rtspClient;
884c7eafb7SDario Casalinuovo	do {
894c7eafb7SDario Casalinuovo		if (resultCode != 0) {
904c7eafb7SDario Casalinuovo			env << *rtspClient << "Failed to get a SDP description: "
914c7eafb7SDario Casalinuovo				<< resultString << "\n";
924c7eafb7SDario Casalinuovo			delete[] resultString;
934c7eafb7SDario Casalinuovo
944c7eafb7SDario Casalinuovo			break;
954c7eafb7SDario Casalinuovo		}
964c7eafb7SDario Casalinuovo
974c7eafb7SDario Casalinuovo		char* const sdpDescription = resultString;
984c7eafb7SDario Casalinuovo		env << *rtspClient << "Got a SDP description:\n"
994c7eafb7SDario Casalinuovo			<< sdpDescription << "\n";
1004c7eafb7SDario Casalinuovo
1014c7eafb7SDario Casalinuovo		// Create a media session object from this SDP description:
1024c7eafb7SDario Casalinuovo		client->session = MediaSession::createNew(env, sdpDescription);
1034c7eafb7SDario Casalinuovo		delete[] sdpDescription; // because we don't need it anymore
1044c7eafb7SDario Casalinuovo		if (client->session == NULL) {
1054c7eafb7SDario Casalinuovo			env << *rtspClient
1064c7eafb7SDario Casalinuovo				<< "Failed to create a MediaSession object "
1074c7eafb7SDario Casalinuovo					"from the SDP description: "
1084c7eafb7SDario Casalinuovo				<< env.getResultMsg() << "\n";
1094c7eafb7SDario Casalinuovo
1104c7eafb7SDario Casalinuovo			break;
1114c7eafb7SDario Casalinuovo		} else if (!client->session->hasSubsessions()) {
1124c7eafb7SDario Casalinuovo			env << *rtspClient << "This session has no media subsessions"
1134c7eafb7SDario Casalinuovo				" (i.e., no \"m=\" lines)\n";
1144c7eafb7SDario Casalinuovo
1154c7eafb7SDario Casalinuovo			break;
1164c7eafb7SDario Casalinuovo		}
1174c7eafb7SDario Casalinuovo
1184c7eafb7SDario Casalinuovo		// Then, create and set up our data source objects for the session.
1194c7eafb7SDario Casalinuovo		// We do this by iterating over the session's 'subsessions',
1204c7eafb7SDario Casalinuovo		// calling "MediaSubsession::initiate()",
1214c7eafb7SDario Casalinuovo		// and then sending a RTSP "SETUP" command, on each one.
1224c7eafb7SDario Casalinuovo		// (Each 'subsession' will have its own data source.)
1234c7eafb7SDario Casalinuovo		client->iter = new MediaSubsessionIterator(*client->session);
1244c7eafb7SDario Casalinuovo		setupNextSubsession(rtspClient);
1254c7eafb7SDario Casalinuovo		return;
1264c7eafb7SDario Casalinuovo	} while (0);
1274c7eafb7SDario Casalinuovo
1284c7eafb7SDario Casalinuovo	// An unrecoverable error occurred with this stream.
1294c7eafb7SDario Casalinuovo	shutdownStream(rtspClient);
1304c7eafb7SDario Casalinuovo}
1314c7eafb7SDario Casalinuovo
1324c7eafb7SDario Casalinuovo
1334c7eafb7SDario Casalinuovovoid setupNextSubsession(RTSPClient* rtspClient)
1344c7eafb7SDario Casalinuovo{
1354c7eafb7SDario Casalinuovo	UsageEnvironment& env = rtspClient->envir();
1364c7eafb7SDario Casalinuovo	HaikuRTSPClient* client = (HaikuRTSPClient*) rtspClient;
1374c7eafb7SDario Casalinuovo
1384c7eafb7SDario Casalinuovo	client->subsession = client->iter->next();
1394c7eafb7SDario Casalinuovo	if (client->subsession != NULL) {
1404c7eafb7SDario Casalinuovo		if (!client->subsession->initiate()) {
1414c7eafb7SDario Casalinuovo
1424c7eafb7SDario Casalinuovo			env << *rtspClient << "Failed to initiate the \""
1434c7eafb7SDario Casalinuovo				<< *client->subsession << "\" subsession: "
1444c7eafb7SDario Casalinuovo				<< env.getResultMsg() << "\n";
1454c7eafb7SDario Casalinuovo
1464c7eafb7SDario Casalinuovo			// give up on this subsession; go to the next one
1474c7eafb7SDario Casalinuovo			setupNextSubsession(rtspClient);
1484c7eafb7SDario Casalinuovo		}
1494c7eafb7SDario Casalinuovo		else {
1504c7eafb7SDario Casalinuovo			env << *rtspClient << "Initiated the \""
1514c7eafb7SDario Casalinuovo				<< *client->subsession << "\" subsession (";
1524c7eafb7SDario Casalinuovo
1534c7eafb7SDario Casalinuovo			if (client->subsession->rtcpIsMuxed()) {
1544c7eafb7SDario Casalinuovo				env << "client port " << client->subsession->clientPortNum();
1554c7eafb7SDario Casalinuovo			} else {
1564c7eafb7SDario Casalinuovo				env << "client ports " << client->subsession->clientPortNum()
1574c7eafb7SDario Casalinuovo					<< "-" << client->subsession->clientPortNum() + 1;
1584c7eafb7SDario Casalinuovo			}
1594c7eafb7SDario Casalinuovo			env << ")\n";
1604c7eafb7SDario Casalinuovo
1614c7eafb7SDario Casalinuovo			// Continue setting up this subsession,
1624c7eafb7SDario Casalinuovo			// by sending a RTSP "SETUP" command:
1634c7eafb7SDario Casalinuovo			rtspClient->sendSetupCommand(*client->subsession,
1644c7eafb7SDario Casalinuovo				continueAfterSETUP, False, REQUEST_STREAMING_OVER_TCP);
1654c7eafb7SDario Casalinuovo		}
1664c7eafb7SDario Casalinuovo		return;
1674c7eafb7SDario Casalinuovo	}
1684c7eafb7SDario Casalinuovo
1694c7eafb7SDario Casalinuovo	// We've finished setting up all of the subsessions.
1704c7eafb7SDario Casalinuovo	// Now, send a RTSP "PLAY" command to start the streaming:
1714c7eafb7SDario Casalinuovo	if (client->session->absStartTime() != NULL) {
1724c7eafb7SDario Casalinuovo		// Special case: The stream is indexed by 'absolute' time,
1734c7eafb7SDario Casalinuovo		// so send an appropriate "PLAY" command:
1744c7eafb7SDario Casalinuovo		rtspClient->sendPlayCommand(*client->session, continueAfterPLAY,
1754c7eafb7SDario Casalinuovo			client->session->absStartTime(), client->session->absEndTime());
176d004f813SDario Casalinuovo	} else {
1774c7eafb7SDario Casalinuovo		client->duration = client->session->playEndTime()
1784c7eafb7SDario Casalinuovo			- client->session->playStartTime();
1794c7eafb7SDario Casalinuovo		rtspClient->sendPlayCommand(*client->session, continueAfterPLAY);
1804c7eafb7SDario Casalinuovo	}
1814c7eafb7SDario Casalinuovo}
1824c7eafb7SDario Casalinuovo
1834c7eafb7SDario Casalinuovo
1844c7eafb7SDario Casalinuovovoid continueAfterSETUP(RTSPClient* rtspClient,
1854c7eafb7SDario Casalinuovo	int resultCode, char* resultString)
1864c7eafb7SDario Casalinuovo{
1874c7eafb7SDario Casalinuovo	do {
1884c7eafb7SDario Casalinuovo		UsageEnvironment& env = rtspClient->envir();
1894c7eafb7SDario Casalinuovo		HaikuRTSPClient* client = (HaikuRTSPClient*) rtspClient;
1904c7eafb7SDario Casalinuovo
1914c7eafb7SDario Casalinuovo		if (resultCode != 0) {
1924c7eafb7SDario Casalinuovo			env << *rtspClient << "Failed to set up the \""
1934c7eafb7SDario Casalinuovo				<< *client->subsession << "\" subsession: "
1944c7eafb7SDario Casalinuovo				<< resultString << "\n";
1954c7eafb7SDario Casalinuovo			break;
1964c7eafb7SDario Casalinuovo		}
1974c7eafb7SDario Casalinuovo
1984c7eafb7SDario Casalinuovo		env << *rtspClient << "Set up the \""
1994c7eafb7SDario Casalinuovo			<< *client->subsession << "\" subsession (";
2004c7eafb7SDario Casalinuovo		if (client->subsession->rtcpIsMuxed()) {
2014c7eafb7SDario Casalinuovo			env << "client port " << client->subsession->clientPortNum();
202d004f813SDario Casalinuovo		} else {
2034c7eafb7SDario Casalinuovo			env << "client ports " << client->subsession->clientPortNum()
2044c7eafb7SDario Casalinuovo				<< "-" << client->subsession->clientPortNum() + 1;
2054c7eafb7SDario Casalinuovo		}
2064c7eafb7SDario Casalinuovo		env << ")\n";
2074c7eafb7SDario Casalinuovo
2084c7eafb7SDario Casalinuovo		// Having successfully setup the subsession, create a data sink for it,
2094c7eafb7SDario Casalinuovo		// and call "startPlaying()" on it.
2104c7eafb7SDario Casalinuovo		// (This will prepare the data sink to receive data; the actual
2114c7eafb7SDario Casalinuovo		// flow of data from the client won't start happening until later,
2124c7eafb7SDario Casalinuovo		// after we've sent a RTSP "PLAY" command.)
2134c7eafb7SDario Casalinuovo
2144c7eafb7SDario Casalinuovo		client->subsession->sink = AdapterSink::createNew(env, *client->subsession,
2154c7eafb7SDario Casalinuovo			((HaikuRTSPClient*)rtspClient)->GetInputAdapter(), rtspClient->url());
2164c7eafb7SDario Casalinuovo		// perhaps use your own custom "MediaSink" subclass instead
2174c7eafb7SDario Casalinuovo		if (client->subsession->sink == NULL) {
2184c7eafb7SDario Casalinuovo			env << *rtspClient << "Failed to create a data sink for the \""
2194c7eafb7SDario Casalinuovo				<< *client->subsession << "\" subsession: "
2204c7eafb7SDario Casalinuovo				<< env.getResultMsg() << "\n";
2214c7eafb7SDario Casalinuovo			break;
2224c7eafb7SDario Casalinuovo		}
2234c7eafb7SDario Casalinuovo
2244c7eafb7SDario Casalinuovo		env << *rtspClient << "Created a data sink for the \""
2254c7eafb7SDario Casalinuovo			<< *client->subsession << "\" subsession\n";
2264c7eafb7SDario Casalinuovo		// a hack to let subsession handler functions
2274c7eafb7SDario Casalinuovo		// get the "RTSPClient" from the subsession
2284c7eafb7SDario Casalinuovo		client->subsession->miscPtr = rtspClient;
2294c7eafb7SDario Casalinuovo		client->subsession->sink
2304c7eafb7SDario Casalinuovo				->startPlaying(*(client->subsession->readSource()),
2314c7eafb7SDario Casalinuovo					subsessionAfterPlaying, client->subsession);
2324c7eafb7SDario Casalinuovo		// Also set a handler to be called if a RTCP "BYE"
2334c7eafb7SDario Casalinuovo		// arrives for this subsession:
2344c7eafb7SDario Casalinuovo		if (client->subsession->rtcpInstance() != NULL) {
2354c7eafb7SDario Casalinuovo			client->subsession->rtcpInstance()->setByeHandler(
2364c7eafb7SDario Casalinuovo				subsessionByeHandler,
2374c7eafb7SDario Casalinuovo				client->subsession);
2384c7eafb7SDario Casalinuovo		}
2394c7eafb7SDario Casalinuovo	} while (0);
2404c7eafb7SDario Casalinuovo	delete[] resultString;
2414c7eafb7SDario Casalinuovo
2424c7eafb7SDario Casalinuovo	// Set up the next subsession, if any:
2434c7eafb7SDario Casalinuovo	setupNextSubsession(rtspClient);
2444c7eafb7SDario Casalinuovo}
2454c7eafb7SDario Casalinuovo
2464c7eafb7SDario Casalinuovo
2474c7eafb7SDario Casalinuovovoid continueAfterPLAY(RTSPClient* rtspClient,
2484c7eafb7SDario Casalinuovo	int resultCode, char* resultString)
2494c7eafb7SDario Casalinuovo{
2504c7eafb7SDario Casalinuovo	Boolean success = False;
2514c7eafb7SDario Casalinuovo	UsageEnvironment& env = rtspClient->envir();
2524c7eafb7SDario Casalinuovo	HaikuRTSPClient* client = (HaikuRTSPClient*) rtspClient;
2534c7eafb7SDario Casalinuovo
2544c7eafb7SDario Casalinuovo	do {
2554c7eafb7SDario Casalinuovo		if (resultCode != 0) {
2564c7eafb7SDario Casalinuovo			env << *rtspClient << "Failed to start playing session: "
2574c7eafb7SDario Casalinuovo				<< resultString << "\n";
2584c7eafb7SDario Casalinuovo			break;
2594c7eafb7SDario Casalinuovo		}
2604c7eafb7SDario Casalinuovo
2614c7eafb7SDario Casalinuovo		// Set a timer to be handled at the end of the stream's
2624c7eafb7SDario Casalinuovo		// expected duration (if the stream does not already signal its end
2634c7eafb7SDario Casalinuovo		// using a RTCP "BYE").  This is optional.  If, instead, you want
2644c7eafb7SDario Casalinuovo		// to keep the stream active - e.g., so you can later
2654c7eafb7SDario Casalinuovo		// 'seek' back within it and do another RTSP "PLAY"
2664c7eafb7SDario Casalinuovo		// - then you can omit this code.
2674c7eafb7SDario Casalinuovo		// (Alternatively, if you don't want to receive the entire stream,
2684c7eafb7SDario Casalinuovo		// you could set this timer for some shorter value.)
2694c7eafb7SDario Casalinuovo		if (client->duration > 0) {
2704c7eafb7SDario Casalinuovo			// number of seconds extra to delay,
2714c7eafb7SDario Casalinuovo			// after the stream's expected duration.  (This is optional.)
2724c7eafb7SDario Casalinuovo			unsigned const delaySlop = 2;
2734c7eafb7SDario Casalinuovo			client->duration += delaySlop;
2744c7eafb7SDario Casalinuovo			unsigned uSecsToDelay = (unsigned)(client->duration * 1000000);
2754c7eafb7SDario Casalinuovo			client->streamTimerTask
2764c7eafb7SDario Casalinuovo				= env.taskScheduler().scheduleDelayedTask(uSecsToDelay,
2774c7eafb7SDario Casalinuovo					(TaskFunc*)streamTimerHandler, rtspClient);
2784c7eafb7SDario Casalinuovo		}
2794c7eafb7SDario Casalinuovo
2804c7eafb7SDario Casalinuovo		env << *rtspClient << "Started playing session";
2814c7eafb7SDario Casalinuovo		if (client->duration > 0) {
2824c7eafb7SDario Casalinuovo			env << " (for up to " << client->duration << " seconds)";
2834c7eafb7SDario Casalinuovo		}
2844c7eafb7SDario Casalinuovo		env << "...\n";
2854c7eafb7SDario Casalinuovo
2864c7eafb7SDario Casalinuovo		success = True;
2874c7eafb7SDario Casalinuovo	} while (0);
2884c7eafb7SDario Casalinuovo	delete[] resultString;
2894c7eafb7SDario Casalinuovo
2904c7eafb7SDario Casalinuovo	if (!success) {
2914c7eafb7SDario Casalinuovo		// An unrecoverable error occurred with this stream.
2924c7eafb7SDario Casalinuovo		shutdownStream(rtspClient);
2934c7eafb7SDario Casalinuovo	} else
2944c7eafb7SDario Casalinuovo		client->NotifySucces();
2954c7eafb7SDario Casalinuovo}
2964c7eafb7SDario Casalinuovo
2974c7eafb7SDario Casalinuovo// Implementation of the other event handlers:
2984c7eafb7SDario Casalinuovo
2994c7eafb7SDario Casalinuovovoid subsessionAfterPlaying(void* clientData)
3004c7eafb7SDario Casalinuovo{
3014c7eafb7SDario Casalinuovo	MediaSubsession* subsession = (MediaSubsession*)clientData;
3024c7eafb7SDario Casalinuovo	RTSPClient* rtspClient = (RTSPClient*)(subsession->miscPtr);
3034c7eafb7SDario Casalinuovo
3044c7eafb7SDario Casalinuovo	// Begin by closing this subsession's stream:
3054c7eafb7SDario Casalinuovo	Medium::close(subsession->sink);
3064c7eafb7SDario Casalinuovo	subsession->sink = NULL;
3074c7eafb7SDario Casalinuovo
3084c7eafb7SDario Casalinuovo	// Next, check whether *all* subsessions' streams have now been closed:
3094c7eafb7SDario Casalinuovo	MediaSession& session = subsession->parentSession();
3104c7eafb7SDario Casalinuovo	MediaSubsessionIterator iter(session);
3114c7eafb7SDario Casalinuovo	while ((subsession = iter.next()) != NULL) {
3124c7eafb7SDario Casalinuovo		if (subsession->sink != NULL)
3134c7eafb7SDario Casalinuovo			return; // this subsession is still active
3144c7eafb7SDario Casalinuovo	}
3154c7eafb7SDario Casalinuovo
3164c7eafb7SDario Casalinuovo	// All subsessions' streams have now been closed, so shutdown the client:
3174c7eafb7SDario Casalinuovo	shutdownStream(rtspClient);
3184c7eafb7SDario Casalinuovo}
3194c7eafb7SDario Casalinuovo
3204c7eafb7SDario Casalinuovo
3214c7eafb7SDario Casalinuovovoid subsessionByeHandler(void* clientData)
3224c7eafb7SDario Casalinuovo{
3234c7eafb7SDario Casalinuovo	MediaSubsession* subsession = (MediaSubsession*)clientData;
3244c7eafb7SDario Casalinuovo	RTSPClient* rtspClient = (RTSPClient*)subsession->miscPtr;
3254c7eafb7SDario Casalinuovo	UsageEnvironment& env = rtspClient->envir();
3264c7eafb7SDario Casalinuovo
3274c7eafb7SDario Casalinuovo	env << *rtspClient << "Received RTCP \"BYE\" on \""
3284c7eafb7SDario Casalinuovo		<< *subsession << "\" subsession\n";
3294c7eafb7SDario Casalinuovo
3304c7eafb7SDario Casalinuovo	// Now act as if the subsession had closed:
3314c7eafb7SDario Casalinuovo	subsessionAfterPlaying(subsession);
3324c7eafb7SDario Casalinuovo}
3334c7eafb7SDario Casalinuovo
3344c7eafb7SDario Casalinuovo
3354c7eafb7SDario Casalinuovovoid streamTimerHandler(void* clientData)
3364c7eafb7SDario Casalinuovo{
3374c7eafb7SDario Casalinuovo	HaikuRTSPClient* client = (HaikuRTSPClient*)clientData;
3384c7eafb7SDario Casalinuovo
3394c7eafb7SDario Casalinuovo	client->streamTimerTask = NULL;
3404c7eafb7SDario Casalinuovo
3414c7eafb7SDario Casalinuovo	// Shut down the stream:
3424c7eafb7SDario Casalinuovo	shutdownStream(client);
3434c7eafb7SDario Casalinuovo}
3444c7eafb7SDario Casalinuovo
3454c7eafb7SDario Casalinuovo
3464c7eafb7SDario Casalinuovovoid shutdownStream(RTSPClient* rtspClient, int exitCode)
3474c7eafb7SDario Casalinuovo{
3484c7eafb7SDario Casalinuovo	UsageEnvironment& env = rtspClient->envir();
3494c7eafb7SDario Casalinuovo	HaikuRTSPClient* client = (HaikuRTSPClient*) rtspClient;
3504c7eafb7SDario Casalinuovo
3514c7eafb7SDario Casalinuovo	// First, check whether any subsessions have still to be closed:
3524c7eafb7SDario Casalinuovo	if (client->session != NULL) {
3534c7eafb7SDario Casalinuovo		Boolean someSubsessionsWereActive = False;
3544c7eafb7SDario Casalinuovo		MediaSubsessionIterator iter(*client->session);
3554c7eafb7SDario Casalinuovo		MediaSubsession* subsession;
3564c7eafb7SDario Casalinuovo
3574c7eafb7SDario Casalinuovo		while ((subsession = iter.next()) != NULL) {
3584c7eafb7SDario Casalinuovo			if (subsession->sink != NULL) {
3594c7eafb7SDario Casalinuovo				Medium::close(subsession->sink);
3604c7eafb7SDario Casalinuovo				subsession->sink = NULL;
3614c7eafb7SDario Casalinuovo
3624c7eafb7SDario Casalinuovo				if (subsession->rtcpInstance() != NULL) {
3634c7eafb7SDario Casalinuovo					// in case the server sends a RTCP "BYE"
3644c7eafb7SDario Casalinuovo					// while handling "TEARDOWN"
3654c7eafb7SDario Casalinuovo					subsession->rtcpInstance()->setByeHandler(NULL, NULL);
3664c7eafb7SDario Casalinuovo				}
3674c7eafb7SDario Casalinuovo
3684c7eafb7SDario Casalinuovo				someSubsessionsWereActive = True;
3694c7eafb7SDario Casalinuovo			}
3704c7eafb7SDario Casalinuovo		}
3714c7eafb7SDario Casalinuovo
3724c7eafb7SDario Casalinuovo		if (someSubsessionsWereActive) {
3734c7eafb7SDario Casalinuovo			// Send a RTSP "TEARDOWN" command,
3744c7eafb7SDario Casalinuovo			// to tell the server to shutdown the stream.
3754c7eafb7SDario Casalinuovo			// Don't bother handling the response to the "TEARDOWN".
3764c7eafb7SDario Casalinuovo			rtspClient->sendTeardownCommand(*client->session, NULL);
3774c7eafb7SDario Casalinuovo		}
3784c7eafb7SDario Casalinuovo	}
3794c7eafb7SDario Casalinuovo
3804c7eafb7SDario Casalinuovo	env << *rtspClient << "Closing the stream.\n";
3814c7eafb7SDario Casalinuovo	Medium::close(rtspClient);
3824c7eafb7SDario Casalinuovo	// Note that this will also cause this stream's
3834c7eafb7SDario Casalinuovo	// "StreamClientState" structure to get reclaimed.
3840957c821SDario Casalinuovo	client->NotifyError();
3854c7eafb7SDario Casalinuovo}
3864c7eafb7SDario Casalinuovo
3874c7eafb7SDario Casalinuovo
3884c7eafb7SDario CasalinuovoAdapterSink* AdapterSink::createNew(UsageEnvironment& env,
3894c7eafb7SDario Casalinuovo	MediaSubsession& subsession, BInputAdapter* inputAdapter,
3904c7eafb7SDario Casalinuovo	char const* streamId)
3914c7eafb7SDario Casalinuovo{
3924c7eafb7SDario Casalinuovo	return new AdapterSink(env, subsession, streamId, inputAdapter);
3934c7eafb7SDario Casalinuovo}
3944c7eafb7SDario Casalinuovo
3954c7eafb7SDario Casalinuovo
3964c7eafb7SDario CasalinuovoAdapterSink::AdapterSink(UsageEnvironment& env, MediaSubsession& subsession,
3974c7eafb7SDario Casalinuovo	char const* streamId, BInputAdapter* inputAdapter)
3984c7eafb7SDario Casalinuovo	:
3994c7eafb7SDario Casalinuovo	MediaSink(env),
4004c7eafb7SDario Casalinuovo	fSubsession(subsession),
4014c7eafb7SDario Casalinuovo	fInputAdapter(inputAdapter)
4024c7eafb7SDario Casalinuovo{
4034c7eafb7SDario Casalinuovo	fStreamId = strDup(streamId);
4044c7eafb7SDario Casalinuovo	fReceiveBuffer = new u_int8_t[RECEIVE_BUFFER_SIZE];
4054c7eafb7SDario Casalinuovo}
4064c7eafb7SDario Casalinuovo
4074c7eafb7SDario Casalinuovo
4084c7eafb7SDario CasalinuovoAdapterSink::~AdapterSink()
4094c7eafb7SDario Casalinuovo{
4104c7eafb7SDario Casalinuovo	delete[] fReceiveBuffer;
4114c7eafb7SDario Casalinuovo	delete[] fStreamId;
4124c7eafb7SDario Casalinuovo}
4134c7eafb7SDario Casalinuovo
4144c7eafb7SDario Casalinuovo
4154c7eafb7SDario Casalinuovovoid AdapterSink::afterGettingFrame(void* clientData, unsigned frameSize,
4164c7eafb7SDario Casalinuovo	unsigned numTruncatedBytes, struct timeval presentationTime,
4174c7eafb7SDario Casalinuovo	unsigned durationInMicroseconds)
4184c7eafb7SDario Casalinuovo{
4194c7eafb7SDario Casalinuovo	AdapterSink* sink = (AdapterSink*)clientData;
4204c7eafb7SDario Casalinuovo	sink->afterGettingFrame(frameSize, numTruncatedBytes,
4214c7eafb7SDario Casalinuovo		presentationTime, durationInMicroseconds);
4224c7eafb7SDario Casalinuovo}
4234c7eafb7SDario Casalinuovo
4244c7eafb7SDario Casalinuovo
4254c7eafb7SDario Casalinuovovoid
4264c7eafb7SDario CasalinuovoAdapterSink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
4274c7eafb7SDario Casalinuovo	struct timeval presentationTime, unsigned /*durationInMicroseconds*/)
4284c7eafb7SDario Casalinuovo{
4294c7eafb7SDario Casalinuovo	fInputAdapter->Write(fReceiveBuffer, frameSize);
4304c7eafb7SDario Casalinuovo	continuePlaying();
4314c7eafb7SDario Casalinuovo}
4324c7eafb7SDario Casalinuovo
4334c7eafb7SDario Casalinuovo
4344c7eafb7SDario CasalinuovoBoolean
4354c7eafb7SDario CasalinuovoAdapterSink::continuePlaying()
4364c7eafb7SDario Casalinuovo{
4374c7eafb7SDario Casalinuovo	if (fSource == NULL)
4384c7eafb7SDario Casalinuovo		return False;
4394c7eafb7SDario Casalinuovo
4404c7eafb7SDario Casalinuovo	fSource->getNextFrame(fReceiveBuffer, RECEIVE_BUFFER_SIZE,
4414c7eafb7SDario Casalinuovo		afterGettingFrame, this,
4424c7eafb7SDario Casalinuovo		onSourceClosure, this);
4434c7eafb7SDario Casalinuovo	return True;
4444c7eafb7SDario Casalinuovo}
445