439 lines
7.4 KiB
C++
439 lines
7.4 KiB
C++
/*
|
|
* 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;
|
|
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() { }
|
|
|