1/*
2 * Copyright 2015, Dario Casalinuovo. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "MediaClient.h"
7
8#include <MediaConnection.h>
9
10#include <MediaRoster.h>
11#include <TimeSource.h>
12
13#include "MediaClientNode.h"
14
15#include "MediaDebug.h"
16
17
18namespace BPrivate { namespace media {
19
20
21class ConnReleaser {
22public:
23	ConnReleaser(BMediaConnection* conn)
24		:
25		fConn(conn) {}
26
27	virtual ~ConnReleaser()
28	{
29		fConn->Release();
30	}
31
32	bool operator== (const ConnReleaser &c1)
33	{
34		return c1.fConn == this->fConn;
35	}
36
37protected:
38	BMediaConnection* Obj() const
39	{
40		return fConn;
41	}
42
43private:
44	BMediaConnection* fConn;
45};
46
47
48class InputReleaser : public ConnReleaser {
49public:
50	InputReleaser(BMediaInput* input)
51		:
52		ConnReleaser(input) {}
53
54	BMediaInput* Obj() const
55	{
56		return dynamic_cast<BMediaInput*>(ConnReleaser::Obj());
57	}
58};
59
60
61class OutputReleaser : public ConnReleaser {
62public:
63	OutputReleaser(BMediaOutput* output)
64		:
65		ConnReleaser(output) {}
66
67	BMediaOutput* Obj() const
68	{
69		return dynamic_cast<BMediaOutput*>(ConnReleaser::Obj());
70	}
71};
72
73
74}
75}
76
77
78BMediaClient::BMediaClient(const char* name,
79	media_type type, media_client_kinds kinds)
80	:
81	fLastID(-1)
82{
83	CALLED();
84
85	fNode = new BMediaClientNode(name, this, type);
86	_Init();
87
88	fClient.node = fNode->Node();
89	fClient.kinds = kinds;
90}
91
92
93BMediaClient::~BMediaClient()
94{
95	CALLED();
96
97	_Deinit();
98}
99
100
101const media_client&
102BMediaClient::Client() const
103{
104	return fClient;
105}
106
107
108status_t
109BMediaClient::InitCheck() const
110{
111	CALLED();
112
113	return fInitErr;
114}
115
116
117media_client_kinds
118BMediaClient::Kinds() const
119{
120	CALLED();
121
122	return fClient.Kinds();
123}
124
125
126media_type
127BMediaClient::MediaType() const
128{
129	CALLED();
130
131	// Right now ConsumerType() and ProducerType() are the same.
132	return fNode->ConsumerType();
133}
134
135
136status_t
137BMediaClient::RegisterInput(BMediaInput* input)
138{
139	input->_ConnectionRegistered(this, ++fLastID);
140	_AddInput(input);
141	return B_OK;
142}
143
144
145status_t
146BMediaClient::RegisterOutput(BMediaOutput* output)
147{
148	output->_ConnectionRegistered(this, ++fLastID);
149	_AddOutput(output);
150	return B_OK;
151}
152
153
154status_t
155BMediaClient::Bind(BMediaInput* input, BMediaOutput* output)
156{
157	CALLED();
158
159	if (input == NULL
160		|| output == NULL)
161		return B_ERROR;
162
163	if (input->fOwner != this || output->fOwner != this)
164		return B_ERROR;
165
166	// TODO: Implement binding one input to more outputs.
167	if (input->fBind != NULL
168		|| output->fBind != NULL)
169		return B_ERROR;
170
171	input->fBind = output;
172	output->fBind = input;
173	return B_OK;
174}
175
176
177status_t
178BMediaClient::Unbind(BMediaInput* input, BMediaOutput* output)
179{
180	CALLED();
181
182	if (input == NULL || output == NULL)
183		return B_ERROR;
184
185	if (input->fOwner != this || output->fOwner != this)
186		return B_ERROR;
187
188	input->fBind = NULL;
189	output->fBind = NULL;
190	return B_OK;
191}
192
193
194status_t
195BMediaClient::Connect(BMediaConnection* ourConnection,
196	BMediaConnection* theirConnection)
197{
198	CALLED();
199
200	return Connect(ourConnection, theirConnection->Connection());
201}
202
203
204status_t
205BMediaClient::Connect(BMediaConnection* ourConnection,
206	const media_connection& theirConnection)
207{
208	CALLED();
209
210	BMediaOutput* output = dynamic_cast<BMediaOutput*>(ourConnection);
211	if (output != NULL && theirConnection.IsInput())
212		return _ConnectInput(output, theirConnection);
213
214	BMediaInput* input = dynamic_cast<BMediaInput*>(ourConnection);
215	if (input != NULL && theirConnection.IsOutput())
216		return _ConnectOutput(input, theirConnection);
217
218	return B_ERROR;
219}
220
221
222status_t
223BMediaClient::Connect(BMediaConnection* connection,
224	const media_client& client)
225{
226	UNIMPLEMENTED();
227
228	return B_ERROR;
229}
230
231
232status_t
233BMediaClient::Disconnect()
234{
235	CALLED();
236
237	for (int32 i = 0; i < CountInputs(); i++)
238		InputAt(i)->Disconnect();
239
240	for (int32 i = 0; i < CountOutputs(); i++)
241		OutputAt(i)->Disconnect();
242
243	return B_OK;
244}
245
246
247int32
248BMediaClient::CountInputs() const
249{
250	CALLED();
251
252	return fInputs.CountItems();
253}
254
255
256int32
257BMediaClient::CountOutputs() const
258{
259	CALLED();
260
261	return fOutputs.CountItems();
262}
263
264
265BMediaInput*
266BMediaClient::InputAt(int32 index) const
267{
268	CALLED();
269
270	return fInputs.ItemAt(index)->Obj();
271}
272
273
274BMediaOutput*
275BMediaClient::OutputAt(int32 index) const
276{
277	CALLED();
278
279	return fOutputs.ItemAt(index)->Obj();
280}
281
282
283BMediaInput*
284BMediaClient::FindInput(const media_connection& input) const
285{
286	CALLED();
287
288	if (!input.IsInput())
289		return NULL;
290
291	return _FindInput(input.destination);
292}
293
294
295BMediaOutput*
296BMediaClient::FindOutput(const media_connection& output) const
297{
298	CALLED();
299
300	if (!output.IsOutput())
301		return NULL;
302
303	return _FindOutput(output.source);
304}
305
306
307bool
308BMediaClient::IsStarted() const
309{
310	CALLED();
311
312	return fRunning;
313}
314
315
316void
317BMediaClient::ClientRegistered()
318{
319	CALLED();
320}
321
322
323status_t
324BMediaClient::Start()
325{
326	CALLED();
327
328	status_t err = B_OK;
329	for (int32 i = 0; i < CountOutputs(); i++) {
330		media_node remoteNode = OutputAt(i)->Connection().remote_node;
331		if (remoteNode.kind & B_TIME_SOURCE)
332			err = BMediaRoster::CurrentRoster()->StartTimeSource(
333				remoteNode, BTimeSource::RealTime());
334		else
335			err = BMediaRoster::CurrentRoster()->StartNode(
336				remoteNode, fNode->TimeSource()->Now());
337	}
338
339	return BMediaRoster::CurrentRoster()->StartNode(
340		fNode->Node(), fNode->TimeSource()->Now());
341}
342
343
344status_t
345BMediaClient::Stop()
346{
347	CALLED();
348
349	return BMediaRoster::CurrentRoster()->StopNode(
350		fNode->Node(), fNode->TimeSource()->Now());
351}
352
353
354status_t
355BMediaClient::Seek(bigtime_t mediaTime,
356	bigtime_t performanceTime)
357{
358	CALLED();
359
360	return BMediaRoster::CurrentRoster()->SeekNode(fNode->Node(),
361		mediaTime, performanceTime);
362}
363
364
365status_t
366BMediaClient::Roll(bigtime_t start, bigtime_t stop, bigtime_t seek)
367{
368	CALLED();
369
370	return BMediaRoster::CurrentRoster()->RollNode(fNode->Node(),
371		start, stop, seek);
372}
373
374
375bigtime_t
376BMediaClient::CurrentTime() const
377{
378	CALLED();
379
380	return fCurrentTime;
381}
382
383
384BMediaAddOn*
385BMediaClient::AddOn(int32* id) const
386{
387	CALLED();
388
389	return NULL;
390}
391
392
393void
394BMediaClient::HandleStart(bigtime_t performanceTime)
395{
396	fRunning = true;
397}
398
399
400void
401BMediaClient::HandleStop(bigtime_t performanceTime)
402{
403	fRunning = false;
404}
405
406
407void
408BMediaClient::HandleSeek(bigtime_t mediaTime, bigtime_t performanceTime)
409{
410}
411
412
413status_t
414BMediaClient::FormatSuggestion(media_type type, int32 quality,
415	media_format* format)
416{
417	return B_ERROR;
418}
419
420
421void
422BMediaClient::_Init()
423{
424	CALLED();
425
426	BMediaRoster* roster = BMediaRoster::Roster(&fInitErr);
427	if (fInitErr == B_OK && roster != NULL)
428		fInitErr = roster->RegisterNode(fNode);
429}
430
431
432void
433BMediaClient::_Deinit()
434{
435	CALLED();
436
437	if (IsStarted())
438		Stop();
439
440	Disconnect();
441
442	// This will release the connections too.
443	fInputs.MakeEmpty(true);
444	fOutputs.MakeEmpty(true);
445
446	fNode->Release();
447}
448
449
450void
451BMediaClient::_AddInput(BMediaInput* input)
452{
453	CALLED();
454
455	fInputs.AddItem(new InputReleaser(input));
456}
457
458
459void
460BMediaClient::_AddOutput(BMediaOutput* output)
461{
462	CALLED();
463
464	fOutputs.AddItem(new OutputReleaser(output));
465}
466
467
468BMediaInput*
469BMediaClient::_FindInput(const media_destination& dest) const
470{
471	CALLED();
472
473	for (int32 i = 0; i < CountInputs(); i++) {
474		if (dest.id == InputAt(i)->_Destination().id)
475			return InputAt(i);
476	}
477	return NULL;
478}
479
480
481BMediaOutput*
482BMediaClient::_FindOutput(const media_source& source) const
483{
484	CALLED();
485
486	for (int32 i = 0; i < CountOutputs(); i++) {
487		if (source.id == OutputAt(i)->_Source().id)
488			return OutputAt(i);
489	}
490	return NULL;
491}
492
493
494status_t
495BMediaClient::_ConnectInput(BMediaOutput* output,
496	const media_connection& input)
497{
498	CALLED();
499
500	if (input.destination == media_destination::null)
501		return B_MEDIA_BAD_DESTINATION;
502
503	media_output ourOutput = output->Connection()._BuildMediaOutput();
504	media_input theirInput = input._BuildMediaInput();
505	media_format format;
506
507	// NOTE: We want to set this data in the callbacks if possible.
508	// The correct format should have been set in BMediaConnection::Connected.
509	// TODO: Perhaps add some check assert?
510
511	status_t ret = BMediaRoster::CurrentRoster()->Connect(ourOutput.source,
512		theirInput.destination, &format, &ourOutput, &theirInput,
513		BMediaRoster::B_CONNECT_MUTED);
514
515#if 0
516	if (ret == B_OK)
517		output->fConnection.format = format;
518#endif
519
520	return ret;
521}
522
523
524status_t
525BMediaClient::_ConnectOutput(BMediaInput* input,
526	const media_connection& output)
527{
528	CALLED();
529
530	if (output.source == media_source::null)
531		return B_MEDIA_BAD_SOURCE;
532
533	media_input ourInput = input->Connection()._BuildMediaInput();
534	media_output theirOutput = output._BuildMediaOutput();
535	media_format format;
536
537	// NOTE: We want to set this data in the callbacks if possible.
538	// The correct format should have been set in BMediaConnection::Connected.
539	// TODO: Perhaps add some check assert?
540
541	status_t ret = BMediaRoster::CurrentRoster()->Connect(theirOutput.source,
542		ourInput.destination, &format, &theirOutput, &ourInput,
543		BMediaRoster::B_CONNECT_MUTED);
544
545#if 0
546	if (ret == B_OK)
547		input->fConnection.format = format;
548#endif
549
550	return ret;
551}
552
553
554status_t
555BMediaClient::_DisconnectConnection(BMediaConnection* conn)
556{
557	CALLED();
558
559	if (conn->Client() != this)
560		return B_ERROR;
561
562	const media_connection& handle = conn->Connection();
563	if (handle.IsInput()) {
564		return BMediaRoster::CurrentRoster()->Disconnect(
565			handle.remote_node.node, handle.source,
566			handle._Node().node, handle.destination);
567	} else {
568		return BMediaRoster::CurrentRoster()->Disconnect(
569			handle._Node().node, handle.source,
570			handle.remote_node.node, handle.destination);
571	}
572
573	return B_ERROR;
574}
575
576
577status_t
578BMediaClient::_ReleaseConnection(BMediaConnection* conn)
579{
580	if (conn->Client() != this)
581		return B_ERROR;
582
583	if (conn->Connection().IsInput()) {
584		InputReleaser obj(dynamic_cast<BMediaInput*>(conn));
585		fInputs.RemoveItem(&obj, false);
586		return B_OK;
587	} else {
588		OutputReleaser obj(dynamic_cast<BMediaOutput*>(conn));
589		fOutputs.RemoveItem(&obj, false);
590		return B_OK;
591	}
592
593	return B_ERROR;
594}
595
596
597void BMediaClient::_ReservedMediaClient0() {}
598void BMediaClient::_ReservedMediaClient1() {}
599void BMediaClient::_ReservedMediaClient2() {}
600void BMediaClient::_ReservedMediaClient3() {}
601void BMediaClient::_ReservedMediaClient4() {}
602void BMediaClient::_ReservedMediaClient5() {}
603void BMediaClient::_ReservedMediaClient6() {}
604void BMediaClient::_ReservedMediaClient7() {}
605void BMediaClient::_ReservedMediaClient8() {}
606void BMediaClient::_ReservedMediaClient9() {}
607void BMediaClient::_ReservedMediaClient10() {}
608