root/src/kits/media/MediaRecorderNode.cpp
/*
 * Copyright 2014-2016, Dario Casalinuovo
 * Copyright 1999, Be Incorporated
 * All Rights Reserved.
 * This file may be used under the terms of the Be Sample Code License.
 */


#include "MediaRecorderNode.h"

#include <Buffer.h>
#include <scheduler.h>
#include <MediaRoster.h>
#include <MediaRosterEx.h>
#include <TimedEventQueue.h>
#include <TimeSource.h>

#include "MediaDebug.h"


BMediaRecorderNode::BMediaRecorderNode(const char* name,
        BMediaRecorder* recorder, media_type type)
        :
        BMediaNode(name),
        BMediaEventLooper(),
        BBufferConsumer(type),
        fRecorder(recorder),
        fConnectMode(true)
{
        CALLED();

        fInput.node = Node();
        fInput.destination.id = 1;
        fInput.destination.port = ControlPort();

        fName.SetTo(name);

        BString str(name);
        str << " Input";
        strcpy(fInput.name, str.String());
}


BMediaRecorderNode::~BMediaRecorderNode()
{
        CALLED();
}


BMediaAddOn*
BMediaRecorderNode::AddOn(int32* id) const
{
        CALLED();

        if (id)
                *id = -1;

        return NULL;
}


void
BMediaRecorderNode::NodeRegistered()
{
        CALLED();
        Run();
}


void
BMediaRecorderNode::SetRunMode(run_mode mode)
{
        CALLED();

        int32 priority;

        if (mode == BMediaNode::B_OFFLINE)
                priority = B_OFFLINE_PROCESSING;
        else {
                switch(ConsumerType()) {
                        case B_MEDIA_RAW_AUDIO:
                        case B_MEDIA_ENCODED_AUDIO:
                                priority = B_AUDIO_RECORDING;
                                break;

                        case B_MEDIA_RAW_VIDEO:
                        case B_MEDIA_ENCODED_VIDEO:
                                priority = B_VIDEO_RECORDING;
                                break;

                        default:
                                priority = B_DEFAULT_MEDIA_PRIORITY;
                }
        }

        SetPriority(suggest_thread_priority(priority));

        BMediaNode::SetRunMode(mode);
}


void
BMediaRecorderNode::SetAcceptedFormat(const media_format& format)
{
        CALLED();

        fInput.format = format;
        fOKFormat = format;
}


const media_format&
BMediaRecorderNode::AcceptedFormat() const
{
        CALLED();

        return fInput.format;
}


void
BMediaRecorderNode::GetInput(media_input* outInput)
{
        CALLED();

        fInput.node = Node();
        *outInput = fInput;
}


void
BMediaRecorderNode::SetDataEnabled(bool enabled)
{
        CALLED();

        int32 tag;

        SetOutputEnabled(fInput.source,
                fInput.destination, enabled, NULL, &tag);
}


void
BMediaRecorderNode::ActivateInternalConnect(bool connectMode)
{
        fConnectMode = connectMode;
}


void
BMediaRecorderNode::HandleEvent(const media_timed_event* event,
        bigtime_t lateness, bool realTimeEvent)
{
        CALLED();

        // we ignore them all!
}


void
BMediaRecorderNode::Start(bigtime_t performanceTime)
{
        CALLED();

        if (fRecorder->fNotifyHook)
                (*fRecorder->fNotifyHook)(fRecorder->fBufferCookie,
                        BMediaRecorder::B_WILL_START, performanceTime);

        fRecorder->fRunning = true;
}


void
BMediaRecorderNode::Stop(bigtime_t performanceTime, bool immediate)
{
        CALLED();

        if (fRecorder->fNotifyHook)
                (*fRecorder->fNotifyHook)(fRecorder->fBufferCookie,
                        BMediaRecorder::B_WILL_STOP, performanceTime, immediate);

        fRecorder->fRunning = false;
}


void
BMediaRecorderNode::Seek(bigtime_t mediaTime, bigtime_t performanceTime)
{
        CALLED();

        if (fRecorder->fNotifyHook)
                (*fRecorder->fNotifyHook)(fRecorder->fBufferCookie,
                        BMediaRecorder::B_WILL_SEEK, performanceTime, mediaTime);
}


void
BMediaRecorderNode::TimeWarp(bigtime_t realTime, bigtime_t performanceTime)
{
        CALLED();

        // Since buffers will come pre-time-stamped, we only need to look
        // at them, so we can ignore the time warp as a consumer.
        if (fRecorder->fNotifyHook)
                (*fRecorder->fNotifyHook)(fRecorder->fBufferCookie,
                        BMediaRecorder::B_WILL_TIMEWARP, realTime, performanceTime);
}


status_t
BMediaRecorderNode::HandleMessage(int32 message,
        const void* data, size_t size)
{
        CALLED();

        if (BBufferConsumer::HandleMessage(message, data, size) < 0
                && BMediaEventLooper::HandleMessage(message, data, size) < 0
                && BMediaNode::HandleMessage(message, data, size) < 0) {
                HandleBadMessage(message, data, size);
                return B_ERROR;
        }
        return B_OK;
}


status_t
BMediaRecorderNode::AcceptFormat(const media_destination& dest,
        media_format* format)
{
        CALLED();

        if (format_is_compatible(*format, fOKFormat))
                return B_OK;

        *format = fOKFormat;

        return B_MEDIA_BAD_FORMAT;
}


status_t
BMediaRecorderNode::GetNextInput(int32* cookie, media_input* outInput)
{
        CALLED();

        if (*cookie == 0) {
                *cookie = -1;
                *outInput = fInput;
                return B_OK;
        }

        return B_BAD_INDEX;
}


void
BMediaRecorderNode::DisposeInputCookie(int32 cookie)
{
        CALLED();
}


void
BMediaRecorderNode::BufferReceived(BBuffer* buffer)
{
        CALLED();

        fRecorder->BufferReceived(buffer->Data(), buffer->SizeUsed(),
                *buffer->Header());

        buffer->Recycle();
}


void
BMediaRecorderNode::ProducerDataStatus(
        const media_destination& forWhom, int32 status,
        bigtime_t performanceTime)
{
        CALLED();
}


status_t
BMediaRecorderNode::GetLatencyFor(const media_destination& forWhom,
        bigtime_t* outLatency, media_node_id* outTimesource)
{
        CALLED();

        *outLatency = 0;
        *outTimesource = TimeSource()->ID();

        return B_OK;
}


status_t
BMediaRecorderNode::Connected(const media_source &producer,
        const media_destination &where, const media_format &withFormat,
        media_input* outInput)
{
        CALLED();

        fInput.source = producer;
        fInput.format = withFormat;
        *outInput = fInput;

        if (fConnectMode == true) {
                // This is a workaround needed for us to get the node
                // so that our owner class can do it's operations.
                media_node node;
                BMediaRosterEx* roster = MediaRosterEx(BMediaRoster::CurrentRoster());
                if (roster->GetNodeFor(roster->NodeIDFor(producer.port), &node) != B_OK)
                        return B_MEDIA_BAD_NODE;

                fRecorder->fOutputNode = node;
                fRecorder->fReleaseOutputNode = true;
        }
        fRecorder->SetUpConnection(producer);
        fRecorder->fConnected = true;

        return B_OK;
}


void
BMediaRecorderNode::Disconnected(const media_source& producer,
        const media_destination& where)
{
        CALLED();

        fInput.source = media_source::null;
        // Reset the connection mode
        fConnectMode = true;
        fRecorder->fConnected = false;
        fInput.format = fOKFormat;
}


status_t
BMediaRecorderNode::FormatChanged(const media_source& producer,
        const media_destination& consumer, int32 tag,
        const media_format& format)
{
        CALLED();

        if (!format_is_compatible(format, fOKFormat))
                return B_MEDIA_BAD_FORMAT;

        fInput.format = format;

        return B_OK;
}