/*
 * Copyright 2015, Hamish Morrison <hamishm53@gmail.com>
 * 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 <MediaRecorder.h>
 
#include <MediaAddOn.h>
#include <MediaRoster.h>
#include <TimeSource.h>
 
#include "MediaDebug.h"
#include "MediaRecorderNode.h"
 
 
BMediaRecorder::BMediaRecorder(const char* name, media_type type)
	:
	fInitErr(B_OK),
	fConnected(false),
	fRunning(false),
	fReleaseOutputNode(false),
	fRecordHook(NULL),
	fNotifyHook(NULL),
	fNode(NULL),
	fBufferCookie(NULL)
{
	CALLED();
 
	BMediaRoster::Roster(&fInitErr);
 
	if (fInitErr == B_OK) {
		fNode = new(std::nothrow) BMediaRecorderNode(name, this, type);
		if (fNode == NULL)
			fInitErr = B_NO_MEMORY;
 
		fInitErr = BMediaRoster::CurrentRoster()->RegisterNode(fNode);
	}
}
 
 
BMediaRecorder::~BMediaRecorder()
{
	CALLED();
 
	if (fNode != NULL) {
		Stop();
		Disconnect();
		fNode->Release();
	}
}
 
 
status_t
BMediaRecorder::InitCheck() const
{
	CALLED();
 
	return fInitErr;
}
 
 
void
BMediaRecorder::SetAcceptedFormat(const media_format& format)
{
	CALLED();
 
	fNode->SetAcceptedFormat(format);
}
 
 
const media_format&
BMediaRecorder::AcceptedFormat() const
{
	CALLED();
 
	return fNode->AcceptedFormat();
}
 
 
status_t
BMediaRecorder::SetHooks(ProcessFunc recordFunc, NotifyFunc notifyFunc,
	void* cookie)
{
	CALLED();
 
	fRecordHook = recordFunc;
	fNotifyHook = notifyFunc;
	fBufferCookie = cookie;
 
	return B_OK;
}
 
 
void
BMediaRecorder::BufferReceived(void* buffer, size_t size,
	const media_header& header)
{
	CALLED();
 
	if (fRecordHook) {
		(*fRecordHook)(fBufferCookie, header.start_time,
			buffer, size, Format());
	}
}
 
 
status_t
BMediaRecorder::Connect(const media_format& format)
{
	CALLED();
 
	if (fInitErr != B_OK)
		return fInitErr;
 
	if (fConnected)
		return B_MEDIA_ALREADY_CONNECTED;
 
	status_t err = B_OK;
	media_node node;
 
	switch (format.type) {
		// switch on format for default
		case B_MEDIA_RAW_AUDIO:
			err = BMediaRoster::Roster()->GetAudioMixer(&node);
			break;
		case B_MEDIA_RAW_VIDEO:
		case B_MEDIA_ENCODED_VIDEO:
			err = BMediaRoster::Roster()->GetVideoInput(&node);
			break;
		// give up?
		default:
			return B_MEDIA_BAD_FORMAT;
	}
 
	if (err != B_OK)
		return err;
 
	fReleaseOutputNode = true;
 
	err = _Connect(node, NULL, format);
 
	if (err != B_OK) {
		BMediaRoster::Roster()->ReleaseNode(node);
		fReleaseOutputNode = false;
	}
 
	return err;
}
 
 
status_t
BMediaRecorder::Connect(const dormant_node_info& dormantNode,
	const media_format& format)
{
	CALLED();
 
	if (fInitErr != B_OK)
		return fInitErr;
 
	if (fConnected)
		return B_MEDIA_ALREADY_CONNECTED;
 
	media_node node;
	status_t err = BMediaRoster::Roster()->InstantiateDormantNode(dormantNode,
		&node, B_FLAVOR_IS_GLOBAL);
 
	if (err != B_OK)
		return err;
 
	fReleaseOutputNode = true;
 
	err = _Connect(node, NULL, format);
 
	if (err != B_OK) {
		BMediaRoster::Roster()->ReleaseNode(node);
		fReleaseOutputNode = false;
	}
 
	return err;
}
 
 
status_t
BMediaRecorder::Connect(const media_node& node,
	const media_output* output, const media_format* format)
{
	CALLED();
 
	if (fInitErr != B_OK)
		return fInitErr;
 
	if (fConnected)
		return B_MEDIA_ALREADY_CONNECTED;
 
	if (format == NULL && output != NULL)
		format = &output->format;
 
	return _Connect(node, output, *format);
}
 
 
status_t
BMediaRecorder::Disconnect()
{
	CALLED();
 
	status_t err = B_OK;
 
	if (fInitErr != B_OK)
		return fInitErr;
 
	if (!fConnected)
		return B_MEDIA_NOT_CONNECTED;
 
	if (!fNode)
		return B_ERROR;
 
	if (fRunning)
		err = Stop();
 
	if (err != B_OK)
		return err;
 
	media_input ourInput;
	fNode->GetInput(&ourInput);
 
	// do the disconnect
	err = BMediaRoster::CurrentRoster()->Disconnect(
		fOutputNode.node, fOutputSource,
			fNode->Node().node, ourInput.destination);
 
	if (fReleaseOutputNode) {
		BMediaRoster::Roster()->ReleaseNode(fOutputNode);
		fReleaseOutputNode = false;
	}
 
	fConnected = false;
	fRunning = false;
 
	return err;
}
 
 
status_t
BMediaRecorder::Start(bool force)
{
	CALLED();
 
	if (fInitErr != B_OK)
		return fInitErr;
 
	if (!fConnected)
		return B_MEDIA_NOT_CONNECTED;
 
	if (fRunning && !force)
		return EALREADY;
 
	if (!fNode)
		return B_ERROR;
 
	// start node here
	status_t err = B_OK;
 
	if ((fOutputNode.kind & B_TIME_SOURCE) != 0)
		err = BMediaRoster::CurrentRoster()->StartTimeSource(
			fOutputNode, BTimeSource::RealTime());
	else
		err = BMediaRoster::CurrentRoster()->StartNode(
			fOutputNode, fNode->TimeSource()->Now());
 
	// then un-mute it
	if (err == B_OK) {
		fNode->SetDataEnabled(true);
		fRunning = true;
	} else
		fRunning = false;
 
	return err;
}
 
 
status_t
BMediaRecorder::Stop(bool force)
{
	CALLED();
 
	if (fInitErr != B_OK)
		return fInitErr;
 
	if (!fRunning && !force)
		return EALREADY;
 
	if (!fNode)
		return B_ERROR;
 
	// should have the Node mute the output here
	fNode->SetDataEnabled(false);
 
	fRunning = false;
 
	return BMediaRoster::CurrentRoster()->StopNode(fNode->Node(), 0);
}
 
 
bool
BMediaRecorder::IsRunning() const
{
	CALLED();
 
	return fRunning;
}
 
 
bool
BMediaRecorder::IsConnected() const
{
	CALLED();
 
	return fConnected;
}
 
 
const media_source&
BMediaRecorder::MediaSource() const
{
	CALLED();
 
	return fOutputSource;
}
 
 
const media_input&
BMediaRecorder::MediaInput() const
{
	CALLED();
 
	media_input* input = NULL;
	fNode->GetInput(input);
	return *input;
}
 
 
const media_format&
BMediaRecorder::Format() const
{
	CALLED();
 
	return fNode->AcceptedFormat();
}
 
 
status_t
BMediaRecorder::SetUpConnection(media_source outputSource)
{
	fOutputSource = outputSource;
 
	// Perform the connection
	media_node timeSource;
	if ((fOutputNode.kind & B_TIME_SOURCE) != 0)
		timeSource = fOutputNode;
	else
		BMediaRoster::Roster()->GetTimeSource(&timeSource);
 
	// Set time source
	return BMediaRoster::Roster()->SetTimeSourceFor(fNode->Node().node,
		timeSource.node);
}
 
 
status_t
BMediaRecorder::_Connect(const media_node& node,
	const media_output* output, const media_format& format)
{
	CALLED();
 
	status_t err = B_OK;
	media_format ourFormat = format;
	media_output ourOutput;
 
	if (fNode == NULL)
		return B_ERROR;
 
	fNode->SetAcceptedFormat(ourFormat);
 
	fOutputNode = node;
 
	// Figure out the output provided
	if (output != NULL) {
		ourOutput = *output;
	} else if (err == B_OK) {
		media_output outputs[10];
		int32 count = 10;
 
		err = BMediaRoster::Roster()->GetFreeOutputsFor(fOutputNode,
			outputs, count, &count, ourFormat.type);
 
		if (err != B_OK)
			return err;
 
		for (int i = 0; i < count; i++) {
			if (format_is_compatible(outputs[i].format, ourFormat)) {
				ourOutput = outputs[i];
				ourFormat = outputs[i].format;
				break;
			}
		}
	}
 
	if (ourOutput.source == media_source::null)
		return B_MEDIA_BAD_SOURCE;
 
	// Find our Node's free input
	media_input ourInput;
	fNode->GetInput(&ourInput);
 
	// Acknowledge the node that we already know
	// who is our producer node.
	fNode->ActivateInternalConnect(false);
 
	return BMediaRoster::CurrentRoster()->Connect(ourOutput.source,
		ourInput.destination, &ourFormat, &ourOutput, &ourInput,
		BMediaRoster::B_CONNECT_MUTED);
}
 
 
void BMediaRecorder::_ReservedMediaRecorder0() { }
void BMediaRecorder::_ReservedMediaRecorder1() { }
void BMediaRecorder::_ReservedMediaRecorder2() { }
void BMediaRecorder::_ReservedMediaRecorder3() { }
void BMediaRecorder::_ReservedMediaRecorder4() { }
void BMediaRecorder::_ReservedMediaRecorder5() { }
void BMediaRecorder::_ReservedMediaRecorder6() { }
void BMediaRecorder::_ReservedMediaRecorder7() { }
 

V522 Dereferencing of the null pointer 'input' might take place.