#include "rtsp.h"
#include <AdapterIO.h>
#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:
virtual Boolean continuePlaying();
private:
BInputAdapter* fInputAdapter;
u_int8_t* fReceiveBuffer;
MediaSubsession& fSubsession;
char* fStreamId;
};
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";
client->session = MediaSession::createNew(env, sdpDescription);
delete[] sdpDescription;
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;
}
client->iter = new MediaSubsessionIterator(*client->session);
setupNextSubsession(rtspClient);
return;
} while (0);
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";
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";
rtspClient->sendSetupCommand(*client->subsession,
continueAfterSETUP, False, REQUEST_STREAMING_OVER_TCP);
}
return;
}
if (client->session->absStartTime() != NULL) {
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";
client->subsession->sink = AdapterSink::createNew(env, *client->subsession,
((HaikuRTSPClient*)rtspClient)->GetInputAdapter(), rtspClient->url());
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";
client->subsession->miscPtr = rtspClient;
client->subsession->sink
->startPlaying(*(client->subsession->readSource()),
subsessionAfterPlaying, client->subsession);
if (client->subsession->rtcpInstance() != NULL) {
client->subsession->rtcpInstance()->setByeHandler(
subsessionByeHandler,
client->subsession);
}
} while (0);
delete[] resultString;
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;
}
if (client->duration > 0) {
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) {
shutdownStream(rtspClient);
} else
client->NotifySucces();
}
void subsessionAfterPlaying(void* clientData)
{
MediaSubsession* subsession = (MediaSubsession*)clientData;
RTSPClient* rtspClient = (RTSPClient*)(subsession->miscPtr);
Medium::close(subsession->sink);
subsession->sink = NULL;
MediaSession& session = subsession->parentSession();
MediaSubsessionIterator iter(session);
while ((subsession = iter.next()) != NULL) {
if (subsession->sink != NULL)
return;
}
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";
subsessionAfterPlaying(subsession);
}
void streamTimerHandler(void* clientData)
{
HaikuRTSPClient* client = (HaikuRTSPClient*)clientData;
client->streamTimerTask = NULL;
shutdownStream(client);
}
void shutdownStream(RTSPClient* rtspClient, int exitCode)
{
UsageEnvironment& env = rtspClient->envir();
HaikuRTSPClient* client = (HaikuRTSPClient*) rtspClient;
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) {
subsession->rtcpInstance()->setByeHandler(NULL, NULL);
}
someSubsessionsWereActive = True;
}
}
if (someSubsessionsWereActive) {
rtspClient->sendTeardownCommand(*client->session, NULL);
}
}
env << *rtspClient << "Closing the stream.\n";
Medium::close(rtspClient);
client->NotifyError();
}
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 )
{
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;
}