root/src/add-ons/media/plugins/rtsp_streamer/RTSPMediaIO.cpp
/*
 * 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),
        fClient(NULL),
        fScheduler(NULL),
        fLoopWatchVariable(0),
        fLoopThread(-1)
{
        fScheduler = BasicTaskScheduler::createNew();
        fEnv = BasicUsageEnvironment::createNew(*fScheduler);
}


RTSPMediaIO::~RTSPMediaIO()
{
        fClient->Close();

        ShutdownLoop();

        status_t status;
        if (fLoopThread != -1)
                wait_for_thread(fLoopThread, &status);
}


ssize_t
RTSPMediaIO::WriteAt(off_t position, const void* buffer, size_t size)
{
        return B_NOT_SUPPORTED;
}


status_t
RTSPMediaIO::SetSize(off_t size)
{
        return B_NOT_SUPPORTED;
}


status_t
RTSPMediaIO::Open()
{
        fClient = new HaikuRTSPClient(*fEnv, fUrl.UrlString(),
                0, this);
        if (fClient == NULL)
                return B_ERROR;

        fClient->sendDescribeCommand(continueAfterDESCRIBE);

        fLoopThread = spawn_thread(_LoopThread, "two minutes hate thread",
                B_NORMAL_PRIORITY, this);

        if (fLoopThread <= 0 || resume_thread(fLoopThread) != B_OK)
                return B_ERROR;

        return fClient->WaitForInit(5000000);
}


int32
RTSPMediaIO::_LoopThread(void* data)
{
        static_cast<RTSPMediaIO *>(data)->LoopThread();
        return 0;
}


void
RTSPMediaIO::LoopThread()
{
        fEnv->taskScheduler().doEventLoop(&fLoopWatchVariable);
        fLoopThread = -1;
}


void
RTSPMediaIO::ShutdownLoop()
{
        fLoopWatchVariable = 1;
}


HaikuRTSPClient::HaikuRTSPClient(UsageEnvironment& env, char const* rtspURL,
                portNumBits tunnelOverHTTPPortNum, RTSPMediaIO* interface)
        :
        RTSPClient(env, rtspURL, LIVE555_VERBOSITY, "Haiku RTSP Streamer",
                tunnelOverHTTPPortNum, -1),
        iter(NULL),
        session(NULL),
        subsession(NULL),
        streamTimerTask(NULL),
        duration(0.0f),
        fInterface(interface),
        fInitPort(-1)
{
        fInitPort = create_port(1, "RTSP Client wait port");
}


HaikuRTSPClient::~HaikuRTSPClient()
{
}


void
HaikuRTSPClient::Close()
{
        delete iter;
        if (session != NULL) {
                UsageEnvironment& env = session->envir();
                env.taskScheduler().unscheduleDelayedTask(streamTimerTask);
                Medium::close(session);
        }
}


status_t
HaikuRTSPClient::WaitForInit(bigtime_t timeout)
{
        status_t status = B_ERROR;
        if (read_port_etc(fInitPort, NULL, &status,
                        sizeof(status), B_RELATIVE_TIMEOUT, timeout) < 0) {
                return B_ERROR;
        }

        close_port(fInitPort);
        delete_port(fInitPort);
        fInitPort = -1;
        return status;
}


void
HaikuRTSPClient::NotifyError()
{
        fInterface->ShutdownLoop();
        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));
}


BInputAdapter*
HaikuRTSPClient::GetInputAdapter() const
{
        return fInterface->BuildInputAdapter();
}