Initial merge of the BMediaClient branch

* The BMediaClient is an higher level API to the media_kit. It
corresponds to what the layout API was for the interface_kit.
The main idea is to allow the developer concentrate only on
higher level details and avoiding handle with the tricky parts
of the media_kit. At the same time the general purpose node that
is implemented inside would allow implementing the best techniques
around thus at the same time reducing code duplication and increasing
efficiency.
* BMediaClient is WIP, this is the initial merge of the branch.
The initial development stone was set long time ago and walked
through various design/implementation phases.
This commit is contained in:
Dario Casalinuovo 2016-11-11 13:39:33 +01:00
parent 956c2f5fb0
commit 9b410c41e8
7 changed files with 2213 additions and 2 deletions

View File

@ -0,0 +1,257 @@
/*
* Copyright 2015, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _MEDIA_CLIENT_H
#define _MEDIA_CLIENT_H
#include <ObjectList.h>
#include <Buffer.h>
#include <MediaAddOn.h>
#include <MediaConnection.h>
#include <MediaDefs.h>
#include <MediaNode.h>
#include "MediaClientNode.h"
namespace BPrivate { namespace media {
// The node can receive media data.
#define B_MEDIA_RECORDER 0x000000001
// The node can send media data to another node.
#define B_MEDIA_PLAYER 0x000000002
// The node specify a control GUI which can be used to configure it.
#define B_MEDIA_CONTROLLABLE 0x000000004
// TODO: Add file interface
// TODO: Offline mode is still missing
// BMediaClient is a general purpose class allowing to create any kind
// of media_node. It automatically manage the expected behavior under
// different run modes, and allow to specify the different capabilities
// which you may want. It work with a central loop allowing to handle,
// calling the right callbacks automatically events at a certain
// performance_time.
class BMediaClient {
public:
enum notification {
B_WILL_START = 1, // performance_time
B_WILL_STOP, // performance_time immediate
B_WILL_SEEK, // performance_time media_time
B_WILL_TIMEWARP, // real_time performance_time
B_FORMAT_SUGGESTION, // media_type type, int32 quality,
// media_format* format
};
typedef void (*notify_hook)(void* cookie,
notification what,
...);
// TODO: Should allow BControllable capabilities
BMediaClient(const char* name,
media_type type
= B_MEDIA_UNKNOWN_TYPE,
uint64
capabilities = B_MEDIA_PLAYER
& B_MEDIA_RECORDER);
virtual ~BMediaClient();
status_t InitCheck() const;
// Return the capabilities of this BMediaClient instance.
uint64 Capabilities() const;
media_type Type() const;
// To connect pass the BMediaConnection to this class or to another BMediaClient,
// also in another team the connection object will be valid.
// When those functions return, the BMediaConnection is added to the
// list and is visible to other nodes as not connected.
// This is supplied to support generic connections not related
// to a certain destination or source node, however for various ambiguity
// reasons we want the BMediaConnection to be declared as input or output.
// You can pass the object returned by this function to another
// BMediaClient::BeginConnection() and then Connect(), so that it
// will automatically connect to this node.
virtual BMediaConnection* BeginConnection(media_connection_kind kind);
// Those are used if you want to connect to a precise input/output of
// another node, the connection returned represents a remote input/output.
virtual BMediaConnection* BeginConnection(const media_input& input);
virtual BMediaConnection* BeginConnection(const media_output& output);
// This is used when we want to connect us with another BMediaClient, the
// returned connection is the one you will use to call the Connect method
// and will represents it by the point of view of this node. Don't try to
// use directly connections built by another BMediaClient, it will fail,
// always pass between this function to get a local connection from the point
// of view of *this* node and then call Connect().
virtual BMediaConnection* BeginConnection(BMediaConnection* connection);
// Bind internally two connections of the same BMediaClient, so that the
// input will be automatically forwarded to the output just after the
// ProcessFunc is called. The buffer is automatically recycled too.
// Beware that the binding operation is valid only for local connections
// which belong to this node, otherwise return B_ERROR.
virtual status_t Bind(BMediaConnection* input,
BMediaConnection* output);
virtual status_t Unbind(BMediaConnection* input,
BMediaConnection* output);
// If the user want a particular format for a connection it should
// use BMediaConnection::SetAcceptedFormat(), if it's not specified
// BMediaClient::Format() will be used, in case both aren't specified
// an error is returned. The connection should always belong to this
// node, in any other case it will return an error.
virtual status_t Connect(BMediaConnection* ourConnection,
BMediaConnection* theirConnection);
virtual status_t Connect(BMediaConnection* ourConnection,
const dormant_node_info& dormantInfo);
virtual status_t Connect(BMediaConnection* ourConnection,
const media_node& node);
// Disconnect any connection belonging to this object, to disconnect
// a single connection use BMediaConnection::Disconnect().
virtual status_t Disconnect();
int32 CountConnections() const;
int32 CountInputs() const;
int32 CountOutputs() const;
BMediaConnection* InputAt(int32 index) const;
BMediaConnection* OutputAt(int32 index) const;
BMediaConnection* FindConnection(
const media_destination& dest) const;
BMediaConnection* FindConnection(
const media_source& source) const;
bool IsRunning() const;
virtual status_t Start(bool force = false);
virtual status_t Stop(bool force = false);
status_t Seek(bigtime_t mediaTime,
bigtime_t performanceTime);
status_t Roll(bigtime_t start, bigtime_t stop,
bigtime_t seek);
status_t Preroll();
status_t SyncTo(bigtime_t performanceTime,
bigtime_t timeout = -1);
// It will be B_INCREASE_LATENCY by default
BMediaNode::run_mode RunMode() const;
status_t SetRunMode(BMediaNode::run_mode mode);
status_t SetTimeSource(media_node timesource);
// Specify a latency range to allow the node behave correctly.
// Ideally the minimum latency should be the algorithmic latency you expect
// from the node and will be used as starting point. The max latency is the
// maximum acceptable by you, over that point the node will adjust it's
// performance time to recover if a big delay happen.
void SetLatencyRange(bigtime_t min,
bigtime_t max);
void GetLatencyRange(bigtime_t* min,
bigtime_t* max) const;
// When in B_OFFLINE it returns OfflineTime().
bigtime_t OfflineTime() const;
// Return the current performance time handled by the object
// when run_mode != B_OFFLINE.
bigtime_t PerformanceTime() const;
// When a connection is not binded with another, it's your job to send
// the buffer to the connection you want. You might want
// to ovverride it so that you can track something, in this case
// be sure to call the base version.
// Automatically recycle the BBuffer.
virtual status_t SendBuffer(BMediaConnection* connection,
BBuffer* buffer);
// This is supplied to support using this class in a BMediaAddOn.
// Default version just return NULL.
virtual BMediaAddOn* AddOn(int32* id) const;
void SetNotificationHook(notify_hook notifyHook = NULL,
void* cookie = NULL);
protected:
virtual void BufferReceived(BMediaConnection* connection,
BBuffer* buffer);
// This is used when the user want to override the BeginConnection
// mechanism, for example to supply your BMediaConnection derived
// class. Take ownership of the object.
virtual void AddConnection(BMediaConnection* connection);
// Called from BMediaConnection
status_t DisconnectConnection(BMediaConnection* conn);
status_t ResetConnection(BMediaConnection* conn);
status_t ReleaseConnection(BMediaConnection* conn);
private:
void _Init();
void _Deinit();
status_t _TranslateConnection(BMediaConnection* dest,
BMediaConnection* source);
status_t _Connect(BMediaConnection* conn,
media_node node);
status_t _ConnectInput(BMediaConnection* output,
BMediaConnection* input);
status_t _ConnectOutput(BMediaConnection* input,
BMediaConnection* output);
status_t fInitErr;
uint64 fCapabilities;
bool fRunning;
BMediaClientNode* fNode;
bigtime_t fPerformanceTime;
bigtime_t fOfflineTime;
bigtime_t fMinLatency;
bigtime_t fMaxLatency;
notify_hook fNotifyHook;
void* fNotifyCookie;
BObjectList<BMediaConnection> fInputs;
BObjectList<BMediaConnection> fOutputs;
virtual void _ReservedMediaClient0();
virtual void _ReservedMediaClient1();
virtual void _ReservedMediaClient2();
virtual void _ReservedMediaClient3();
virtual void _ReservedMediaClient4();
virtual void _ReservedMediaClient5();
virtual void _ReservedMediaClient6();
virtual void _ReservedMediaClient7();
virtual void _ReservedMediaClient8();
virtual void _ReservedMediaClient9();
virtual void _ReservedMediaClient10();
uint32 fPadding[64];
friend class BMediaClientNode;
friend class BMediaConnection;
};
}
}
using namespace BPrivate::media;
#endif

View File

@ -0,0 +1,151 @@
/*
* Copyright 2015, Dario Casalinuovo. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _MEDIA_CLIENT_NODE_H
#define _MEDIA_CLIENT_NODE_H
#include <BufferConsumer.h>
#include <BufferProducer.h>
#include <Controllable.h>
#include <MediaDefs.h>
#include <MediaEventLooper.h>
namespace BPrivate { namespace media {
class BMediaClient;
class BMediaConnection;
class BMediaClientNode : public BBufferConsumer, public BBufferProducer,
public BMediaEventLooper {
public:
BMediaClientNode(const char* name,
BMediaClient* owner,
media_type type
= B_MEDIA_UNKNOWN_TYPE);
// Various useful stuff
status_t SendBuffer(BBuffer* buffer, BMediaConnection* conn);
protected:
virtual BMediaAddOn* AddOn(int32* id) const;
virtual void NodeRegistered();
virtual void SetRunMode(run_mode mode);
virtual void Start(bigtime_t performanceTime);
virtual void Stop(bigtime_t performanceTime,
bool immediate);
virtual void Seek(bigtime_t mediaTime,
bigtime_t performanceTime);
virtual void TimeWarp(bigtime_t realTime,
bigtime_t performanceTime);
virtual status_t HandleMessage(int32 message,
const void* data,
size_t size);
// BBufferConsumer
virtual status_t AcceptFormat(const media_destination& dest,
media_format* format);
virtual status_t GetNextInput(int32* cookie,
media_input* input);
virtual void DisposeInputCookie(int32 cookie);
virtual void BufferReceived(BBuffer* buffer);
virtual status_t GetLatencyFor(const media_destination& dest,
bigtime_t* latency,
media_node_id* timesource);
virtual status_t Connected(const media_source& source,
const media_destination& where,
const media_format& format,
media_input* outInput);
virtual void Disconnected(const media_source& source,
const media_destination& where);
virtual status_t FormatChanged(const media_source& source,
const media_destination& consumer,
int32 tag,
const media_format& format);
// BBufferProducer
virtual status_t FormatSuggestionRequested(media_type type,
int32 quality, media_format* format);
virtual status_t FormatProposal(const media_source& source,
media_format *format);
virtual status_t FormatChangeRequested(const media_source& source,
const media_destination& dest,
media_format *format,
int32* _deprecated_);
virtual void LateNoticeReceived(const media_source& source,
bigtime_t late, bigtime_t when);
virtual status_t GetNextOutput(int32 *cookie, media_output *output);
virtual status_t DisposeOutputCookie(int32 cookie);
virtual status_t SetBufferGroup(const media_source& source,
BBufferGroup *group);
virtual status_t PrepareToConnect(const media_source& source,
const media_destination& dest,
media_format *format,
media_source *out_source,
char *name);
virtual void Connect(status_t status,
const media_source& source,
const media_destination& dest,
const media_format &format,
char* name);
virtual void Disconnect(const media_source& source,
const media_destination& dest);
virtual void EnableOutput(const media_source& source,
bool enabled, int32* _deprecated_);
virtual status_t GetLatency(bigtime_t *outLatency);
virtual void LatencyChanged( const media_source& source,
const media_destination& dest,
bigtime_t latency, uint32 flags);
void ProducerDataStatus(const media_destination& dest,
int32 status, bigtime_t when);
protected:
virtual void HandleEvent(const media_timed_event *event,
bigtime_t late,
bool realTimeEvent=false);
virtual ~BMediaClientNode();
private:
void _HandleBuffer(BBuffer* buffer);
void _ProduceNewBuffer(const media_timed_event* event,
bigtime_t late);
void _HandleStart(bigtime_t performanceTime);
void _HandleStop(bigtime_t performanceTime,
bool immediate);
void _HandleSeek(bigtime_t mediaTime,
bigtime_t performanceTime);
void _HandleTimeWarp(bigtime_t realTime,
bigtime_t performanceTime);
BMediaClient* fOwner;
};
}
}
using namespace BPrivate::media;
#endif

View File

@ -0,0 +1,195 @@
/*
* Copyright 2015, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _MEDIA_CONNECTION_H
#define _MEDIA_CONNECTION_H
#include <BufferGroup.h>
#include <MediaDefs.h>
#include "MediaClientNode.h"
namespace BPrivate { namespace media {
enum media_connection_kind {
B_MEDIA_INPUT = 0,
B_MEDIA_OUTPUT = 1
};
class BMediaClient;
// The BMediaConnection class is the swiss knife of BMediaClient.
// It represents a connection between two nodes and allow to create complex
// nodes without dealing with the unneeded complexity. Two local connections,
// can be binded, this means that when you will receive a buffer A as input,
// the Process function will be called so that you can process the BBuffer,
// and once the function returns the output will be automatically forwarded
// to the connection B.
// If you don't bind a connection, you have to call yourself the
// BMediaClient::SendBuffer() method or call BMediaClient::Recycle to
// recycle the buffer when you don't want to do anything further.
class BMediaConnection {
public:
enum notification {
B_CONNECTED = 1,
B_DISCONNECTED,
B_PREPARE_TO_CONNECT, // media_format* format, media_source* source,
// char* name
B_CONNECT,
B_DISCONNECT,
B_FORMAT_PROPOSAL, // media_format* format
B_ASK_FORMAT_CHANGE,
B_FORMAT_CHANGED,
B_ASK_TIMER
};
// This function is called when it has come the moment to handle a buffer.
typedef void (*process_hook)(BMediaConnection* connection,
BBuffer* buffer);
// Used to notify or inquire the client about what to do when certain
// events happen.
typedef status_t (*notify_hook)(notification what,
BMediaConnection* connection,
...);
virtual ~BMediaConnection();
// TODO: while most of the objects for both kinds are common
// it would be worthwile to have a private implementation
// so that we can better model the differences and avoid
// problems.
bool IsOutput() const;
bool IsInput() const;
const media_destination& Destination() const;
const media_source& Source() const;
bool HasBinding() const;
BMediaConnection* Binding() const;
// This allow to specify a format that will be used while
// connecting to another node. See BMediaClient::SetFormat.
void SetAcceptedFormat(
const media_format& format);
const media_format& AcceptedFormat() const;
// Represents the buffer size used by the media_node
void SetBufferSize(size_t size);
size_t BufferSize() const;
// Represents the duration of one buffer
void SetBufferDuration(bigtime_t duration);
bigtime_t BufferDuration() const;
bool IsConnected() const;
void SetOutputEnabled(bool enabled);
bool IsOutputEnabled() const;
void SetCookie(void* cookie);
void* Cookie() const;
// Disconnect this connection.
status_t Disconnect();
// TODO: We really need a Reset()?
// When you reset a connection it can be reused as it was brand new.
status_t Reset();
// Once you are done with this connection you release it, it automatically
// remove the object from the BMediaClient and free all used resources.
// This will make the connection to disappear completely, so if you
// want to preserve it for future connections just Disconnect() and
// Reset() it.
status_t Release();
// Use this to set your callbacks.
void SetHooks(process_hook processHook = NULL,
notify_hook notifyHook = NULL,
void* cookie = NULL);
protected:
BMediaConnection(BMediaClient* owner,
media_connection_kind kind);
BMediaConnection(BMediaClient* owner,
const media_output& output);
BMediaConnection(BMediaClient* owner,
const media_input& input);
// TODO: All notifications should be done into private callbacks like this.
void ConnectedCallback(const media_source& source,
const media_format& format);
void DisconnectedCallback(const media_source& source);
void ConnectCallback(const media_destination& source);
void DisconnectCallback(const media_destination& source);
private:
void BuildMediaOutput(media_output* output) const;
void BuildMediaInput(media_input* output) const;
void _Init();
bool fConnected;
bool fOutputEnabled;
media_connection_kind fKind;
BMediaClient* fOwner;
media_node fOwnerNode;
team_id fOwnerTeam;
// A connection might be binded so that it will automatically
// forward or receive the data from/to a local BMediaConnection,
// see BMediaClient::Bind.
BMediaConnection* fBind;
process_hook fProcessHook;
notify_hook fNotifyHook;
void* fBufferCookie;
media_source fSource;
media_destination fDestination;
// This represents the node at other end of connection.
media_node fRemoteNode;
media_format fFormat;
size_t fBufferSize;
bigtime_t fBufferDuration;
BBufferGroup* fBufferGroup;
virtual void _ReservedMediaConnection0();
virtual void _ReservedMediaConnection1();
virtual void _ReservedMediaConnection2();
virtual void _ReservedMediaConnection3();
virtual void _ReservedMediaConnection4();
virtual void _ReservedMediaConnection5();
virtual void _ReservedMediaConnection6();
virtual void _ReservedMediaConnection7();
virtual void _ReservedMediaConnection8();
virtual void _ReservedMediaConnection9();
virtual void _ReservedMediaConnection10();
uint32 fPadding[64];
friend class BMediaClient;
friend class BMediaClientNode;
};
}
}
using namespace BPrivate::media;
#endif

View File

@ -23,9 +23,13 @@ for architectureObject in [ MultiArchSubDirSetup ] {
# Private Media Kit
!missing_symbols.cpp
Deprecated.cpp
MediaRecorder.cpp
MediaIO.cpp
AdapterIO.cpp
MediaIO.cpp
MediaClient.cpp
MediaConnection.cpp
MediaRecorder.cpp
# Public Media Kit
Buffer.cpp
@ -63,6 +67,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
DefaultMediaTheme.cpp
DormantNodeManager.cpp
FormatManager.cpp
MediaClientNode.cpp
MediaRecorderNode.cpp
Notifications.cpp
request_data.cpp

617
src/kits/media/MediaClient.cpp Executable file
View File

@ -0,0 +1,617 @@
/*
* Copyright 2015, Dario Casalinuovo. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include "MediaClient.h"
#include <MediaRoster.h>
#include <TimeSource.h>
#include "debug.h"
BMediaClient::BMediaClient(const char* name,
media_type type, uint64 capability)
:
fCapabilities(capability)
{
CALLED();
fNode = new BMediaClientNode(name, this, type);
_Init();
}
BMediaClient::~BMediaClient()
{
CALLED();
_Deinit();
}
status_t
BMediaClient::InitCheck() const
{
CALLED();
return fInitErr;
}
uint64
BMediaClient::Capabilities() const
{
CALLED();
return fCapabilities;
}
media_type
BMediaClient::Type() const
{
CALLED();
// Right now ConsumerType() and ProducerType() are the same.
return fNode->ConsumerType();
}
BMediaConnection*
BMediaClient::BeginConnection(media_connection_kind kind)
{
CALLED();
BMediaConnection* conn = new BMediaConnection(this, kind);
AddConnection(conn);
return conn;
}
BMediaConnection*
BMediaClient::BeginConnection(const media_input& input)
{
CALLED();
BMediaConnection* conn = new BMediaConnection(this, input);
AddConnection(conn);
return conn;
}
BMediaConnection*
BMediaClient::BeginConnection(const media_output& output)
{
CALLED();
BMediaConnection* conn = new BMediaConnection(this, output);
AddConnection(conn);
return conn;
}
BMediaConnection*
BMediaClient::BeginConnection(BMediaConnection* connection)
{
CALLED();
if (connection->fOwner == this)
return NULL;
BMediaConnection* ret = NULL;
if (_TranslateConnection(ret, connection) == B_OK) {
AddConnection(ret);
return ret;
}
return NULL;
}
status_t
BMediaClient::Bind(BMediaConnection* input, BMediaConnection* output)
{
CALLED();
if (input->fOwner != this || output->fOwner != this)
return B_ERROR;
else if (!input->IsInput() || !output->IsOutput())
return B_ERROR;
if (input == NULL
|| input == NULL)
return B_ERROR;
// TODO: Implement binding one input to more outputs.
if (input->fBind != NULL
|| output->fBind != NULL)
return B_ERROR;
input->fBind = output;
output->fBind = input;
return B_OK;
}
status_t
BMediaClient::Unbind(BMediaConnection* input, BMediaConnection* output)
{
CALLED();
if (input->fOwner != this || output->fOwner != this)
return B_ERROR;
else if (!input->IsInput() || !output->IsOutput())
return B_ERROR;
if (input == NULL
|| input == NULL)
return B_ERROR;
input->fBind = NULL;
output->fBind = NULL;
return B_OK;
}
status_t
BMediaClient::Connect(BMediaConnection* ourConnection,
BMediaConnection* theirConnection)
{
CALLED();
if (ourConnection->IsOutput() && theirConnection->IsInput())
return _ConnectInput(ourConnection, theirConnection);
else if (ourConnection->IsInput() && theirConnection->IsOutput())
return _ConnectOutput(ourConnection, theirConnection);
return B_ERROR;
}
status_t
BMediaClient::Connect(BMediaConnection* connection,
const dormant_node_info& dormantInfo)
{
CALLED();
media_node node;
status_t err = BMediaRoster::CurrentRoster()->InstantiateDormantNode(
dormantInfo, &node, B_FLAVOR_IS_GLOBAL);
if (err != B_OK)
return err;
return Connect(connection, node);
}
status_t
BMediaClient::Connect(BMediaConnection* connection,
const media_node& node)
{
CALLED();
// TODO: implement this
return B_ERROR;
}
status_t
BMediaClient::Disconnect()
{
CALLED();
for (int32 i = 0; i < CountInputs(); i++)
InputAt(i)->Disconnect();
for (int32 i = 0; i < CountOutputs(); i++)
OutputAt(i)->Disconnect();
return B_OK;
}
status_t
BMediaClient::DisconnectConnection(BMediaConnection* conn)
{
CALLED();
return B_OK;
}
status_t
BMediaClient::ResetConnection(BMediaConnection* conn)
{
CALLED();
return B_OK;
}
status_t
BMediaClient::ReleaseConnection(BMediaConnection* conn)
{
CALLED();
return B_OK;
}
int32
BMediaClient::CountConnections() const
{
CALLED();
return fOutputs.CountItems()+fInputs.CountItems();
}
int32
BMediaClient::CountInputs() const
{
CALLED();
return fInputs.CountItems();
}
int32
BMediaClient::CountOutputs() const
{
CALLED();
return fOutputs.CountItems();
}
BMediaConnection*
BMediaClient::InputAt(int32 index) const
{
CALLED();
return fInputs.ItemAt(index);
}
BMediaConnection*
BMediaClient::OutputAt(int32 index) const
{
CALLED();
return fOutputs.ItemAt(index);
}
BMediaConnection*
BMediaClient::FindConnection(const media_destination& dest) const
{
CALLED();
for (int32 i = 0; i < CountInputs(); i++) {
if (dest.id == InputAt(i)->Destination().id)
return InputAt(i);
}
return NULL;
}
BMediaConnection*
BMediaClient::FindConnection(const media_source& source) const
{
CALLED();
for (int32 i = 0; i < CountOutputs(); i++) {
if (source.id == OutputAt(i)->Source().id)
return OutputAt(i);
}
return NULL;
}
bool
BMediaClient::IsRunning() const
{
CALLED();
return fRunning;
}
status_t
BMediaClient::Start(bool force)
{
CALLED();
status_t err = B_OK;
for (int32 i = 0; i < CountOutputs(); i++) {
media_node remoteNode = OutputAt(i)->fRemoteNode;
if (remoteNode.kind & B_TIME_SOURCE)
err = BMediaRoster::CurrentRoster()->StartTimeSource(
remoteNode, BTimeSource::RealTime());
else
err = BMediaRoster::CurrentRoster()->StartNode(
remoteNode, fNode->TimeSource()->Now());
}
err = BMediaRoster::CurrentRoster()->StartNode(
fNode->Node(), fNode->TimeSource()->Now());
if (err == B_OK)
fRunning = true;
else
fRunning = false;
return err;
}
status_t
BMediaClient::Stop(bool force)
{
CALLED();
status_t err = BMediaRoster::CurrentRoster()->StopNode(
fNode->Node(), fNode->TimeSource()->Now());
if (err == B_OK)
fRunning = false;
else
fRunning = true;
return err;
}
status_t
BMediaClient::Seek(bigtime_t mediaTime,
bigtime_t performanceTime)
{
CALLED();
return BMediaRoster::CurrentRoster()->SeekNode(fNode->Node(),
mediaTime, performanceTime);
}
status_t
BMediaClient::Roll(bigtime_t start, bigtime_t stop, bigtime_t seek)
{
CALLED();
return BMediaRoster::CurrentRoster()->RollNode(fNode->Node(),
start, stop, seek);
}
status_t
BMediaClient::Preroll()
{
CALLED();
return BMediaRoster::CurrentRoster()->PrerollNode(fNode->Node());
}
status_t
BMediaClient::SyncTo(bigtime_t performanceTime, bigtime_t timeout)
{
CALLED();
return BMediaRoster::CurrentRoster()->SyncToNode(fNode->Node(),
performanceTime, timeout);
}
BMediaNode::run_mode
BMediaClient::RunMode() const
{
CALLED();
return fNode->RunMode();
}
status_t
BMediaClient::SetRunMode(BMediaNode::run_mode mode)
{
CALLED();
return BMediaRoster::CurrentRoster()->SetRunModeNode(fNode->Node(), mode);
}
status_t
BMediaClient::SetTimeSource(media_node timesource)
{
CALLED();
return BMediaRoster::CurrentRoster()->SetTimeSourceFor(fNode->Node().node,
timesource.node);
}
void
BMediaClient::GetLatencyRange(bigtime_t* min, bigtime_t* max) const
{
CALLED();
*min = fMinLatency;
*max = fMaxLatency;
}
void
BMediaClient::SetLatencyRange(bigtime_t min, bigtime_t max)
{
CALLED();
fMinLatency = min;
fMaxLatency = max;
}
bigtime_t
BMediaClient::OfflineTime() const
{
CALLED();
return fOfflineTime;
}
bigtime_t
BMediaClient::PerformanceTime() const
{
CALLED();
return fPerformanceTime;
}
status_t
BMediaClient::SendBuffer(BMediaConnection* connection, BBuffer* buffer)
{
CALLED();
return fNode->SendBuffer(buffer, connection);
}
void
BMediaClient::AddConnection(BMediaConnection* connection)
{
CALLED();
if (connection->IsInput())
fInputs.AddItem(connection);
else
fOutputs.AddItem(connection);
}
void
BMediaClient::BufferReceived(BMediaConnection* connection,
BBuffer* buffer)
{
CALLED();
if (connection->fProcessHook != NULL)
connection->fProcessHook(connection, buffer);
}
BMediaAddOn*
BMediaClient::AddOn(int32* id) const
{
CALLED();
return NULL;
}
void
BMediaClient::SetNotificationHook(notify_hook notifyHook, void* cookie)
{
CALLED();
fNotifyHook = notifyHook;
fNotifyCookie = cookie;
}
void
BMediaClient::_Init()
{
CALLED();
fNotifyHook = NULL;
fNotifyCookie = NULL;
BMediaRoster* roster = BMediaRoster::Roster(&fInitErr);
if (fInitErr == B_OK && roster != NULL)
fInitErr = roster->RegisterNode(fNode);
}
void
BMediaClient::_Deinit()
{
CALLED();
if (fRunning)
Stop();
Disconnect();
fNode->Release();
}
status_t
BMediaClient::_TranslateConnection(BMediaConnection* dest,
BMediaConnection* source)
{
CALLED();
return B_ERROR;
}
status_t
BMediaClient::_Connect(BMediaConnection* connection,
media_node node)
{
CALLED();
}
status_t
BMediaClient::_ConnectInput(BMediaConnection* output,
BMediaConnection* input)
{
CALLED();
return B_ERROR;
}
status_t
BMediaClient::_ConnectOutput(BMediaConnection* input,
BMediaConnection* output)
{
CALLED();
if (output->Source() == media_source::null)
return B_MEDIA_BAD_SOURCE;
media_input ourInput;
media_output theirOutput;
media_format format = input->AcceptedFormat();
input->BuildMediaInput(&ourInput);
output->BuildMediaOutput(&theirOutput);
// TODO manage the node problems
//fNode->ActivateInternalConnect(false);
return BMediaRoster::CurrentRoster()->Connect(theirOutput.source,
ourInput.destination, &format, &theirOutput, &ourInput,
BMediaRoster::B_CONNECT_MUTED);
}
void BMediaClient::_ReservedMediaClient0() {}
void BMediaClient::_ReservedMediaClient1() {}
void BMediaClient::_ReservedMediaClient2() {}
void BMediaClient::_ReservedMediaClient3() {}
void BMediaClient::_ReservedMediaClient4() {}
void BMediaClient::_ReservedMediaClient5() {}
void BMediaClient::_ReservedMediaClient6() {}
void BMediaClient::_ReservedMediaClient7() {}
void BMediaClient::_ReservedMediaClient8() {}
void BMediaClient::_ReservedMediaClient9() {}
void BMediaClient::_ReservedMediaClient10() {}

View File

@ -0,0 +1,657 @@
/*
* Copyright 2015, Dario Casalinuovo. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include "MediaClientNode.h"
#include <MediaClient.h>
#include <scheduler.h>
#include <TimeSource.h>
#include <string.h>
#include "debug.h"
#define B_NEW_BUFFER (BTimedEventQueue::B_USER_EVENT + 1)
BMediaClientNode::BMediaClientNode(const char* name,
BMediaClient* owner, media_type type)
:
BMediaNode(name),
BBufferConsumer(type),
BBufferProducer(type),
BMediaEventLooper(),
fOwner(owner)
{
CALLED();
// Configure the node to do the requested jobs
if (fOwner->Capabilities() & B_MEDIA_PLAYER)
AddNodeKind(B_BUFFER_PRODUCER);
if (fOwner->Capabilities() & B_MEDIA_RECORDER)
AddNodeKind(B_BUFFER_CONSUMER);
if (fOwner->Capabilities() & B_MEDIA_CONTROLLABLE)
AddNodeKind(B_CONTROLLABLE);
}
status_t
BMediaClientNode::SendBuffer(BBuffer* buffer, BMediaConnection* conn)
{
return BBufferProducer::SendBuffer(buffer, conn->Source(), conn->Destination());
}
BMediaAddOn*
BMediaClientNode::AddOn(int32* id) const
{
CALLED();
return fOwner->AddOn(id);
}
void
BMediaClientNode::NodeRegistered()
{
CALLED();
Run();
}
void
BMediaClientNode::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
BMediaClientNode::Start(bigtime_t performanceTime)
{
CALLED();
BMediaEventLooper::Start(performanceTime);
fOwner->fRunning = true;
}
void
BMediaClientNode::Stop(bigtime_t performanceTime, bool immediate)
{
CALLED();
BMediaEventLooper::Stop(performanceTime, immediate);
fOwner->fRunning = false;
}
void
BMediaClientNode::Seek(bigtime_t mediaTime, bigtime_t performanceTime)
{
CALLED();
BMediaEventLooper::Seek(mediaTime, performanceTime);
}
void
BMediaClientNode::TimeWarp(bigtime_t realTime, bigtime_t performanceTime)
{
CALLED();
BMediaEventLooper::TimeWarp(realTime, performanceTime);
}
status_t
BMediaClientNode::HandleMessage(int32 message,
const void* data, size_t size)
{
CALLED();
return B_ERROR;
}
status_t
BMediaClientNode::AcceptFormat(const media_destination& dest,
media_format* format)
{
CALLED();
BMediaConnection* conn = fOwner->FindConnection(dest);
if (conn == NULL)
return B_MEDIA_BAD_DESTINATION;
if (format_is_compatible(*format, conn->AcceptedFormat()))
return B_OK;
*format = conn->AcceptedFormat();
return B_MEDIA_BAD_FORMAT;
}
status_t
BMediaClientNode::GetNextInput(int32* cookie,
media_input* input)
{
CALLED();
if (fOwner->CountInputs() == 0)
return B_BAD_INDEX;
if (*cookie < 0 || *cookie > fOwner->CountInputs()) {
*cookie = -1;
input = NULL;
} else {
BMediaConnection* conn = fOwner->InputAt(*cookie);
if (conn != NULL) {
conn->BuildMediaInput(input);
*cookie += 1;
return B_OK;
}
}
return B_BAD_INDEX;
}
void
BMediaClientNode::DisposeInputCookie(int32 cookie)
{
CALLED();
}
void
BMediaClientNode::BufferReceived(BBuffer* buffer)
{
CALLED();
EventQueue()->AddEvent(media_timed_event(buffer->Header()->start_time,
BTimedEventQueue::B_HANDLE_BUFFER, buffer,
BTimedEventQueue::B_RECYCLE_BUFFER));
}
status_t
BMediaClientNode::GetLatencyFor(const media_destination& dest,
bigtime_t* latency, media_node_id* timesource)
{
CALLED();
BMediaConnection* conn = fOwner->FindConnection(dest);
if (conn == NULL)
return B_MEDIA_BAD_DESTINATION;
*latency = fOwner->fMaxLatency;
*timesource = TimeSource()->ID();
return B_OK;
}
status_t
BMediaClientNode::Connected(const media_source& source,
const media_destination& where, const media_format& format,
media_input* outInput)
{
CALLED();
BMediaConnection* conn = fOwner->FindConnection(where);
if (conn == NULL)
return B_MEDIA_BAD_DESTINATION;
conn->ConnectedCallback(source, format);
conn->BuildMediaInput(outInput);
return B_OK;
}
void
BMediaClientNode::Disconnected(const media_source& source,
const media_destination& where)
{
CALLED();
BMediaConnection* conn = fOwner->FindConnection(where);
if (conn == NULL)
return;
conn->DisconnectedCallback(source);
}
status_t
BMediaClientNode::FormatChanged(const media_source& source,
const media_destination& consumer,
int32 tag, const media_format& format)
{
CALLED();
BMediaConnection* conn = fOwner->FindConnection(consumer);
if (conn == NULL)
return B_MEDIA_BAD_DESTINATION;
if (!format_is_compatible(format, conn->AcceptedFormat()))
return B_MEDIA_BAD_FORMAT;
conn->SetAcceptedFormat(format);
return B_OK;
}
status_t
BMediaClientNode::FormatSuggestionRequested(media_type type,
int32 quality, media_format* format)
{
CALLED();
if (type != ConsumerType()
&& type != ProducerType()) {
return B_MEDIA_BAD_FORMAT;
}
if (fOwner->fNotifyHook != NULL) {
status_t result = B_OK;
(*fOwner->fNotifyHook)(fOwner->fNotifyCookie,
BMediaClient::B_FORMAT_SUGGESTION,
type, quality, format, &result);
return result;
} else {
// In that case we return just a very generic format.
media_format outFormat;
outFormat.type = fOwner->Type();
*format = outFormat;
return B_OK;
}
}
status_t
BMediaClientNode::FormatProposal(const media_source& source,
media_format* format)
{
CALLED();
BMediaConnection* conn = fOwner->FindConnection(source);
if (conn == NULL)
return B_MEDIA_BAD_DESTINATION;
if (fOwner->fNotifyHook != NULL) {
return (*conn->fNotifyHook)(BMediaConnection::B_FORMAT_PROPOSAL,
conn, format);
} else
*format = conn->AcceptedFormat();
return B_OK;
}
status_t
BMediaClientNode::FormatChangeRequested(const media_source& source,
const media_destination& dest, media_format* format,
int32* _deprecated_)
{
CALLED();
return B_ERROR;
}
void
BMediaClientNode::LateNoticeReceived(const media_source& source,
bigtime_t late, bigtime_t when)
{
CALLED();
}
status_t
BMediaClientNode::GetNextOutput(int32* cookie, media_output* output)
{
CALLED();
if (fOwner->CountOutputs() == 0)
return B_BAD_INDEX;
if (*cookie < 0 || *cookie > fOwner->CountOutputs()) {
*cookie = -1;
output = NULL;
} else {
BMediaConnection* conn = fOwner->OutputAt(*cookie);
if (conn != NULL) {
conn->BuildMediaOutput(output);
*cookie += 1;
return B_OK;
}
}
return B_BAD_INDEX;
}
status_t
BMediaClientNode::DisposeOutputCookie(int32 cookie)
{
CALLED();
return B_OK;
}
status_t
BMediaClientNode::SetBufferGroup(const media_source& source, BBufferGroup* group)
{
CALLED();
BMediaConnection* conn = fOwner->FindConnection(source);
if (conn == NULL)
return B_MEDIA_BAD_SOURCE;
if (group == conn->fBufferGroup)
return B_OK;
delete conn->fBufferGroup;
if (group != NULL) {
conn->fBufferGroup = group;
return B_OK;
}
bigtime_t latency = 0;
GetLatency(&latency);
int32 count = int32(latency / conn->BufferDuration() + 2);
if (count < 3)
count = 3;
conn->fBufferGroup = new BBufferGroup(conn->BufferSize(), count);
if (conn->fBufferGroup == NULL)
return B_NO_MEMORY;
return conn->fBufferGroup->InitCheck();
}
status_t
BMediaClientNode::PrepareToConnect(const media_source& source,
const media_destination& dest, media_format* format,
media_source* out_source, char *name)
{
CALLED();
BMediaConnection* conn = fOwner->FindConnection(source);
if (conn == NULL)
return B_MEDIA_BAD_SOURCE;
if (conn->fDestination != media_destination::null)
return B_MEDIA_ALREADY_CONNECTED;
if (format->type != fOwner->Type())
return B_MEDIA_BAD_FORMAT;
conn->fDestination = dest;
conn->SetAcceptedFormat(*format);
*out_source = conn->fSource;
strcpy(name, Name());
return B_OK;
}
void
BMediaClientNode::Connect(status_t status, const media_source& source,
const media_destination& dest, const media_format& format,
char* name)
{
CALLED();
BMediaConnection* conn = fOwner->FindConnection(source);
if (conn == NULL)
return;
// Reset the connection to reuse it
if (status != B_OK) {
conn->Reset();
return;
}
conn->ConnectCallback(dest);
strcpy(name, Name());
conn->SetAcceptedFormat(format);
bigtime_t latency;
media_node_id id;
FindLatencyFor(dest, &latency, &id);
bigtime_t duration = ((format.u.raw_audio.buffer_size * 1000000LL)
/ ((format.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK)
* format.u.raw_audio.channel_count))
/ (int32)format.u.raw_audio.frame_rate;
SetBufferDuration(duration);
SetEventLatency((3 * BufferDuration()) / 4);
// TODO: Allocate buffers, add correct latency estimate
}
void
BMediaClientNode::Disconnect(const media_source& source,
const media_destination& dest)
{
CALLED();
BMediaConnection* conn = fOwner->FindConnection(source);
if (conn == NULL)
return;
if (source == conn->fSource && dest == conn->fDestination)
conn->Reset();
conn->DisconnectCallback(dest);
}
void
BMediaClientNode::EnableOutput(const media_source& source,
bool enabled, int32* _deprecated_)
{
CALLED();
BMediaConnection* conn = fOwner->FindConnection(source);
if (conn != NULL) {
conn->fOutputEnabled = enabled;
return;
}
}
status_t
BMediaClientNode::GetLatency(bigtime_t* outLatency)
{
CALLED();
// TODO: finish latency handling
*outLatency = fOwner->fMinLatency;
return B_OK;
}
void
BMediaClientNode::LatencyChanged(const media_source& source,
const media_destination& dest, bigtime_t latency, uint32 flags)
{
CALLED();
}
void
BMediaClientNode::ProducerDataStatus(const media_destination& dest,
int32 status, bigtime_t when)
{
CALLED();
}
void
BMediaClientNode::HandleEvent(const media_timed_event* event,
bigtime_t late, bool realTimeEvent)
{
CALLED();
switch (event->type) {
// This event is used for inputs which consumes buffers
// or binded connections which also sent them to an output.
case BTimedEventQueue::B_HANDLE_BUFFER:
_HandleBuffer((BBuffer*)event->pointer);
break;
// This is used for connections which produce buffers only.
case B_NEW_BUFFER:
_ProduceNewBuffer(event, late);
break;
case BTimedEventQueue::B_START:
{
if (RunState() != B_STARTED)
_HandleStart(event->event_time);
break;
}
case BTimedEventQueue::B_STOP:
_HandleStop(event->event_time, true);
break;
case BTimedEventQueue::B_SEEK:
_HandleSeek(event->event_time, event->bigdata);
break;
case BTimedEventQueue::B_WARP:
_HandleTimeWarp(event->event_time, event->bigdata);
break;
}
}
BMediaClientNode::~BMediaClientNode()
{
CALLED();
Quit();
}
void
BMediaClientNode::_HandleBuffer(BBuffer* buffer)
{
CALLED();
media_destination dest;
dest.id = buffer->Header()->destination;
BMediaConnection* conn = fOwner->FindConnection(dest);
if (conn != NULL)
fOwner->BufferReceived(conn, buffer);
// TODO: this should be logged someway
}
void
BMediaClientNode::_ProduceNewBuffer(const media_timed_event* event,
bigtime_t late)
{
CALLED();
if (RunState() != BMediaEventLooper::B_STARTED)
return;
// We get the data through the event
// so that we know which connection
// event.pointer == connection
}
void
BMediaClientNode::_HandleStart(bigtime_t performanceTime)
{
CALLED();
if (fOwner->fNotifyHook != NULL) {
(*fOwner->fNotifyHook)(fOwner->fNotifyCookie,
BMediaClient::B_WILL_START,
performanceTime);
}
}
void
BMediaClientNode::_HandleStop(bigtime_t performanceTime,
bool immediate)
{
CALLED();
if (fOwner->fNotifyHook != NULL) {
(*fOwner->fNotifyHook)(fOwner->fNotifyCookie,
BMediaClient::B_WILL_STOP,
performanceTime, immediate);
}
EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
BTimedEventQueue::B_HANDLE_BUFFER);
}
void
BMediaClientNode::_HandleSeek(bigtime_t mediaTime,
bigtime_t performanceTime)
{
CALLED();
if (fOwner->fNotifyHook != NULL) {
(*fOwner->fNotifyHook)(fOwner->fNotifyCookie,
BMediaClient::B_WILL_SEEK,
performanceTime, mediaTime);
}
}
void
BMediaClientNode::_HandleTimeWarp(bigtime_t realTime,
bigtime_t performanceTime)
{
CALLED();
if (fOwner->fNotifyHook != NULL) {
(*fOwner->fNotifyHook)(fOwner->fNotifyCookie,
BMediaClient::B_WILL_TIMEWARP,
realTime, performanceTime);
}
}

View File

@ -0,0 +1,329 @@
/*
* Copyright 2015, Dario Casalinuovo. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <MediaConnection.h>
#include <MediaClient.h>
#include "debug.h"
bool
BMediaConnection::IsOutput() const
{
CALLED();
return fKind == B_MEDIA_OUTPUT;
}
bool
BMediaConnection::IsInput() const
{
CALLED();
return fKind == B_MEDIA_INPUT;
}
void
BMediaConnection::BuildMediaOutput(media_output* output) const
{
CALLED();
output->format = AcceptedFormat();
output->node = fOwner->fNode->Node();
output->source = fSource;
output->source.port = fOwner->fNode->ControlPort();
}
void
BMediaConnection::BuildMediaInput(media_input* input) const
{
CALLED();
input->format = AcceptedFormat();
input->node = fOwner->fNode->Node();
input->destination = fDestination;
input->destination.port = fOwner->fNode->ControlPort();
}
const media_destination&
BMediaConnection::Destination() const
{
CALLED();
return fDestination;
}
const media_source&
BMediaConnection::Source() const
{
CALLED();
return fSource;
}
bool
BMediaConnection::HasBinding() const
{
CALLED();
return fBind != NULL;
}
BMediaConnection*
BMediaConnection::Binding() const
{
CALLED();
return fBind;
}
void
BMediaConnection::SetAcceptedFormat(const media_format& format)
{
CALLED();
fFormat = format;
}
const media_format&
BMediaConnection::AcceptedFormat() const
{
CALLED();
return fFormat;
}
bool
BMediaConnection::IsConnected() const
{
CALLED();
return fConnected;
}
bool
BMediaConnection::IsOutputEnabled() const
{
CALLED();
return fOutputEnabled;
}
void*
BMediaConnection::Cookie() const
{
CALLED();
return fBufferCookie;
}
status_t
BMediaConnection::Disconnect()
{
CALLED();
return fOwner->DisconnectConnection(this);
}
status_t
BMediaConnection::Reset()
{
CALLED();
if (IsOutput())
fDestination = media_destination::null;
else
fSource = media_source::null;
delete fBufferGroup;
fBufferGroup = NULL;
return fOwner->ResetConnection(this);
}
status_t
BMediaConnection::Release()
{
CALLED();
return fOwner->ReleaseConnection(this);
}
void
BMediaConnection::SetHooks(process_hook processHook,
notify_hook notifyHook, void* cookie)
{
CALLED();
fProcessHook = processHook;
fNotifyHook = notifyHook;
fBufferCookie = cookie;
}
BMediaConnection::BMediaConnection(BMediaClient* owner,
media_connection_kind kind)
:
fKind(kind),
fOwner(owner),
fBind(NULL)
{
CALLED();
_Init();
}
BMediaConnection::BMediaConnection(BMediaClient* owner,
const media_output& output)
:
fKind(B_MEDIA_OUTPUT),
fOwner(owner),
fBind(NULL)
{
CALLED();
_Init();
}
BMediaConnection::BMediaConnection(BMediaClient* owner,
const media_input& input)
:
fKind(B_MEDIA_INPUT),
fOwner(owner),
fBind(NULL)
{
CALLED();
_Init();
}
BMediaConnection::~BMediaConnection()
{
CALLED();
}
void
BMediaConnection::SetBufferSize(size_t size)
{
CALLED();
fBufferSize = size;
}
size_t
BMediaConnection::BufferSize() const
{
CALLED();
return fBufferSize;
}
void
BMediaConnection::SetBufferDuration(bigtime_t duration)
{
CALLED();
fBufferDuration = duration;
}
bigtime_t
BMediaConnection::BufferDuration() const
{
CALLED();
return fBufferDuration;
}
void
BMediaConnection::ConnectedCallback(const media_source& source,
const media_format& format)
{
fSource = source;
SetAcceptedFormat(format);
if (fNotifyHook != NULL)
(*fNotifyHook)(B_CONNECTED, this);
fConnected = true;
}
void
BMediaConnection::DisconnectedCallback(const media_source& source)
{
fSource = media_source::null;
if (fNotifyHook != NULL)
(*fNotifyHook)(B_DISCONNECTED, this);
fConnected = false;
}
void
BMediaConnection::ConnectCallback(const media_destination& destination)
{
fDestination = destination;
}
void
BMediaConnection::DisconnectCallback(const media_destination& destination)
{
fDestination = destination;
}
void
BMediaConnection::_Init()
{
CALLED();
fBufferGroup = NULL;
fNotifyHook = NULL;
fProcessHook = NULL;
fSource = media_source::null;
fDestination = media_destination::null;
}
void BMediaConnection::_ReservedMediaConnection0() {}
void BMediaConnection::_ReservedMediaConnection1() {}
void BMediaConnection::_ReservedMediaConnection2() {}
void BMediaConnection::_ReservedMediaConnection3() {}
void BMediaConnection::_ReservedMediaConnection4() {}
void BMediaConnection::_ReservedMediaConnection5() {}
void BMediaConnection::_ReservedMediaConnection6() {}
void BMediaConnection::_ReservedMediaConnection7() {}
void BMediaConnection::_ReservedMediaConnection8() {}
void BMediaConnection::_ReservedMediaConnection9() {}
void BMediaConnection::_ReservedMediaConnection10() {}