1/*
2 * Copyright 2015, Dario Casalinuovo. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "MediaClientNode.h"
7
8#include <MediaClient.h>
9#include <MediaConnection.h>
10#include <MediaRoster.h>
11#include <scheduler.h>
12#include <TimeSource.h>
13
14#include <string.h>
15
16#include "MediaDebug.h"
17
18#define B_NEW_BUFFER (BTimedEventQueue::B_USER_EVENT + 1)
19
20
21BMediaClientNode::BMediaClientNode(const char* name,
22	BMediaClient* owner, media_type type)
23	:
24	BMediaNode(name),
25	BBufferConsumer(type),
26	BBufferProducer(type),
27	BMediaEventLooper(),
28	fOwner(owner)
29{
30	CALLED();
31
32	// Configure the node to do the requested jobs
33	if (fOwner->Kinds() & B_MEDIA_PLAYER)
34		AddNodeKind(B_BUFFER_PRODUCER);
35	if (fOwner->Kinds() & B_MEDIA_RECORDER)
36		AddNodeKind(B_BUFFER_CONSUMER);
37	if (fOwner->Kinds() & B_MEDIA_CONTROLLABLE)
38		AddNodeKind(B_CONTROLLABLE);
39}
40
41
42status_t
43BMediaClientNode::SendBuffer(BBuffer* buffer, BMediaConnection* conn)
44{
45	return BBufferProducer::SendBuffer(buffer, conn->_Source(), conn->_Destination());
46}
47
48
49BMediaAddOn*
50BMediaClientNode::AddOn(int32* id) const
51{
52	CALLED();
53
54	return fOwner->AddOn(id);
55}
56
57
58void
59BMediaClientNode::NodeRegistered()
60{
61	CALLED();
62
63	fOwner->ClientRegistered();
64
65	Run();
66}
67
68
69void
70BMediaClientNode::SetRunMode(run_mode mode)
71{
72	CALLED();
73
74	int32 priority;
75	if (mode == BMediaNode::B_OFFLINE)
76		priority = B_OFFLINE_PROCESSING;
77	else {
78		switch(ConsumerType()) {
79			case B_MEDIA_RAW_AUDIO:
80			case B_MEDIA_ENCODED_AUDIO:
81				priority = B_AUDIO_RECORDING;
82				break;
83
84			case B_MEDIA_RAW_VIDEO:
85			case B_MEDIA_ENCODED_VIDEO:
86				priority = B_VIDEO_RECORDING;
87				break;
88
89			default:
90				priority = B_DEFAULT_MEDIA_PRIORITY;
91		}
92	}
93
94	SetPriority(suggest_thread_priority(priority));
95	BMediaNode::SetRunMode(mode);
96}
97
98
99void
100BMediaClientNode::Start(bigtime_t performanceTime)
101{
102	CALLED();
103
104	BMediaEventLooper::Start(performanceTime);
105}
106
107
108void
109BMediaClientNode::Stop(bigtime_t performanceTime, bool immediate)
110{
111	CALLED();
112
113	BMediaEventLooper::Stop(performanceTime, immediate);
114}
115
116
117void
118BMediaClientNode::Seek(bigtime_t mediaTime, bigtime_t performanceTime)
119{
120	CALLED();
121
122	BMediaEventLooper::Seek(mediaTime, performanceTime);
123}
124
125
126void
127BMediaClientNode::TimeWarp(bigtime_t realTime, bigtime_t performanceTime)
128{
129	CALLED();
130
131	BMediaEventLooper::TimeWarp(realTime, performanceTime);
132}
133
134
135status_t
136BMediaClientNode::HandleMessage(int32 message,
137	const void* data, size_t size)
138{
139	CALLED();
140
141	return B_ERROR;
142}
143
144
145status_t
146BMediaClientNode::AcceptFormat(const media_destination& dest,
147	media_format* format)
148{
149	CALLED();
150
151	BMediaInput* conn = fOwner->_FindInput(dest);
152	if (conn == NULL)
153		return B_MEDIA_BAD_DESTINATION;
154
155	return conn->AcceptFormat(format);
156}
157
158
159status_t
160BMediaClientNode::GetNextInput(int32* cookie,
161	media_input* input)
162{
163	CALLED();
164
165	if (fOwner->CountInputs() == 0)
166		return B_BAD_INDEX;
167
168	if (*cookie < 0 || *cookie >= fOwner->CountInputs()) {
169		*cookie = -1;
170		input = NULL;
171	} else {
172		BMediaInput* conn = fOwner->InputAt(*cookie);
173		if (conn != NULL) {
174			*input = conn->fConnection._BuildMediaInput();
175			*cookie += 1;
176			return B_OK;
177		}
178	}
179	return B_BAD_INDEX;
180}
181
182
183void
184BMediaClientNode::DisposeInputCookie(int32 cookie)
185{
186	CALLED();
187}
188
189
190void
191BMediaClientNode::BufferReceived(BBuffer* buffer)
192{
193	CALLED();
194
195	EventQueue()->AddEvent(media_timed_event(buffer->Header()->start_time,
196		BTimedEventQueue::B_HANDLE_BUFFER, buffer,
197		BTimedEventQueue::B_RECYCLE_BUFFER));
198}
199
200
201status_t
202BMediaClientNode::GetLatencyFor(const media_destination& dest,
203	bigtime_t* latency, media_node_id* timesource)
204{
205	CALLED();
206
207	BMediaInput* conn = fOwner->_FindInput(dest);
208	if (conn == NULL)
209		return B_MEDIA_BAD_DESTINATION;
210
211	//*latency = conn->fLatency;
212	*timesource = TimeSource()->ID();
213	return B_OK;
214}
215
216
217status_t
218BMediaClientNode::Connected(const media_source& source,
219	const media_destination& dest, const media_format& format,
220	media_input* outInput)
221{
222	CALLED();
223
224	BMediaInput* conn = fOwner->_FindInput(dest);
225	if (conn == NULL)
226		return B_MEDIA_BAD_DESTINATION;
227
228	conn->fConnection.source = source;
229	conn->fConnection.format = format;
230
231	// Retrieve the node without using GetNodeFor that's pretty inefficient.
232	// Unfortunately we don't have an alternative which doesn't require us
233	// to release the cloned node.
234	// However, our node will not have flags set. Keep in mind this.
235	conn->fConnection.remote_node.node
236		= BMediaRoster::CurrentRoster()->NodeIDFor(source.port);
237	conn->fConnection.remote_node.port = source.port;
238
239	conn->Connected(format);
240
241	*outInput = conn->fConnection._BuildMediaInput();
242	return B_OK;
243}
244
245
246void
247BMediaClientNode::Disconnected(const media_source& source,
248	const media_destination& dest)
249{
250	CALLED();
251
252	BMediaInput* conn = fOwner->_FindInput(dest);
253	if (conn == NULL)
254		return;
255
256	if (conn->_Source() == source) {
257		// Cleanup the connection
258		conn->fConnection.source = media_source::null;
259		conn->fConnection.format = media_format();
260
261		conn->fConnection.remote_node.node = -1;
262		conn->fConnection.remote_node.port = -1;
263
264		conn->Disconnected();
265	}
266}
267
268
269status_t
270BMediaClientNode::FormatChanged(const media_source& source,
271	const media_destination& dest,
272	int32 tag, const media_format& format)
273{
274	CALLED();
275	return B_ERROR;
276}
277
278
279status_t
280BMediaClientNode::FormatSuggestionRequested(media_type type,
281	int32 quality, media_format* format)
282{
283	CALLED();
284
285	if (type != ConsumerType()
286			&& type != ProducerType()) {
287		return B_MEDIA_BAD_FORMAT;
288	}
289
290	status_t ret = fOwner->FormatSuggestion(type, quality, format);
291	if (ret != B_OK) {
292		// In that case we return just a very generic format.
293		media_format outFormat;
294		outFormat.type = fOwner->MediaType();
295		*format = outFormat;
296		return B_OK;
297	}
298
299	return ret;
300}
301
302
303status_t
304BMediaClientNode::FormatProposal(const media_source& source,
305	media_format* format)
306{
307	CALLED();
308
309	BMediaOutput* conn = fOwner->_FindOutput(source);
310	if (conn == NULL)
311		return B_MEDIA_BAD_DESTINATION;
312
313	return conn->FormatProposal(format);
314}
315
316
317status_t
318BMediaClientNode::FormatChangeRequested(const media_source& source,
319	const media_destination& dest, media_format* format,
320	int32* _deprecated_)
321{
322	CALLED();
323
324	return B_ERROR;
325}
326
327
328void
329BMediaClientNode::LateNoticeReceived(const media_source& source,
330	bigtime_t late, bigtime_t when)
331{
332	CALLED();
333
334}
335
336
337status_t
338BMediaClientNode::GetNextOutput(int32* cookie, media_output* output)
339{
340	CALLED();
341
342	if (fOwner->CountOutputs() == 0)
343		return B_BAD_INDEX;
344
345	if (*cookie < 0 || *cookie >= fOwner->CountOutputs()) {
346		*cookie = -1;
347		output = NULL;
348	} else {
349		BMediaOutput* conn = fOwner->OutputAt(*cookie);
350		if (conn != NULL) {
351			*output = conn->fConnection._BuildMediaOutput();
352			*cookie += 1;
353			return B_OK;
354		}
355	}
356	return B_BAD_INDEX;
357}
358
359
360status_t
361BMediaClientNode::DisposeOutputCookie(int32 cookie)
362{
363	CALLED();
364
365	return B_OK;
366}
367
368
369status_t
370BMediaClientNode::SetBufferGroup(const media_source& source, BBufferGroup* group)
371{
372	CALLED();
373
374	BMediaOutput* conn = fOwner->_FindOutput(source);
375	if (conn == NULL)
376		return B_MEDIA_BAD_SOURCE;
377
378	if (group == conn->fBufferGroup)
379		return B_OK;
380
381	delete conn->fBufferGroup;
382
383	if (group != NULL) {
384		conn->fBufferGroup = group;
385		return B_OK;
386	}
387
388	conn->fBufferGroup = new BBufferGroup(conn->BufferSize(), 3);
389	if (conn->fBufferGroup == NULL)
390		return B_NO_MEMORY;
391
392	return conn->fBufferGroup->InitCheck();
393}
394
395
396status_t
397BMediaClientNode::PrepareToConnect(const media_source& source,
398	const media_destination& dest, media_format* format,
399	media_source* out_source, char *name)
400{
401	CALLED();
402
403	BMediaOutput* conn = fOwner->_FindOutput(source);
404	if (conn == NULL)
405		return B_MEDIA_BAD_SOURCE;
406
407	if (conn->_Destination() != media_destination::null)
408		return B_MEDIA_ALREADY_CONNECTED;
409
410	if (fOwner->MediaType() != B_MEDIA_UNKNOWN_TYPE
411			&& format->type != fOwner->MediaType()) {
412		return B_MEDIA_BAD_FORMAT;
413	}
414
415	conn->fConnection.destination = dest;
416
417	status_t err = conn->PrepareToConnect(format);
418	if (err != B_OK)
419		return err;
420
421	*out_source = conn->_Source();
422	strcpy(name, conn->Name());
423
424	return B_OK;
425}
426
427
428void
429BMediaClientNode::Connect(status_t status, const media_source& source,
430	const media_destination& dest, const media_format& format,
431	char* name)
432{
433	CALLED();
434
435	BMediaOutput* conn = fOwner->_FindOutput(source);
436	if (conn == NULL)
437		return;
438
439	// Connection failed, return.
440	if (status != B_OK)
441		return;
442
443	conn->fConnection.destination = dest;
444	conn->fConnection.format = format;
445
446	// Retrieve the node without using GetNodeFor that's pretty inefficient.
447	// Unfortunately we don't have an alternative which doesn't require us
448	// to release the cloned node.
449	// However, our node will not have flags set. Keep in mind this.
450	conn->fConnection.remote_node.node
451		= BMediaRoster::CurrentRoster()->NodeIDFor(dest.port);
452	conn->fConnection.remote_node.port = dest.port;
453
454	strcpy(name, conn->Name());
455
456	// TODO: add correct latency estimate
457	SetEventLatency(1000);
458
459	conn->fBufferGroup = new BBufferGroup(conn->BufferSize(), 3);
460	if (conn->fBufferGroup == NULL)
461		TRACE("Can't allocate the buffer group\n");
462
463	conn->Connected(format);
464}
465
466
467void
468BMediaClientNode::Disconnect(const media_source& source,
469	const media_destination& dest)
470{
471	CALLED();
472
473	BMediaOutput* conn = fOwner->_FindOutput(source);
474	if (conn == NULL)
475		return;
476
477	if (conn->_Destination() == dest) {
478		// Cleanup the connection
479		delete conn->fBufferGroup;
480		conn->fBufferGroup = NULL;
481
482		conn->fConnection.destination = media_destination::null;
483		conn->fConnection.format = media_format();
484
485		conn->fConnection.remote_node.node = -1;
486		conn->fConnection.remote_node.port = -1;
487
488		conn->Disconnected();
489	}
490}
491
492
493void
494BMediaClientNode::EnableOutput(const media_source& source,
495	bool enabled, int32* _deprecated_)
496{
497	CALLED();
498
499	BMediaOutput* conn = fOwner->_FindOutput(source);
500	if (conn != NULL)
501		conn->_SetEnabled(enabled);
502}
503
504
505status_t
506BMediaClientNode::GetLatency(bigtime_t* outLatency)
507{
508	CALLED();
509
510	return BBufferProducer::GetLatency(outLatency);
511}
512
513
514void
515BMediaClientNode::LatencyChanged(const media_source& source,
516	const media_destination& dest, bigtime_t latency, uint32 flags)
517{
518	CALLED();
519}
520
521
522void
523BMediaClientNode::ProducerDataStatus(const media_destination& dest,
524	int32 status, bigtime_t when)
525{
526	CALLED();
527}
528
529
530void
531BMediaClientNode::HandleEvent(const media_timed_event* event,
532	bigtime_t late, bool realTimeEvent)
533{
534	CALLED();
535
536	switch (event->type) {
537		// This event is used for inputs which consumes buffers
538		// or binded connections which also send them to an output.
539		case BTimedEventQueue::B_HANDLE_BUFFER:
540			_HandleBuffer((BBuffer*)event->pointer);
541			break;
542
543		// This is used for connections which produce buffers only.
544		case B_NEW_BUFFER:
545			_ProduceNewBuffer(event, late);
546			break;
547
548		case BTimedEventQueue::B_START:
549		{
550			if (RunState() != B_STARTED)
551				fOwner->HandleStart(event->event_time);
552
553			fStartTime = event->event_time;
554
555			_ScheduleConnections(event->event_time);
556			break;
557		}
558
559		case BTimedEventQueue::B_STOP:
560		{
561			fOwner->HandleStop(event->event_time);
562
563			EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
564				BTimedEventQueue::B_HANDLE_BUFFER);
565			break;
566		}
567
568		case BTimedEventQueue::B_SEEK:
569			fOwner->HandleSeek(event->event_time, event->bigdata);
570			break;
571
572		case BTimedEventQueue::B_WARP:
573			// NOTE: We have no need to handle it
574			break;
575	}
576}
577
578
579BMediaClientNode::~BMediaClientNode()
580{
581	CALLED();
582
583	Quit();
584}
585
586
587void
588BMediaClientNode::_ScheduleConnections(bigtime_t eventTime)
589{
590	for (int32 i = 0; i < fOwner->CountOutputs(); i++) {
591		BMediaOutput* output = fOwner->OutputAt(i);
592
593		if (output->HasBinding())
594			continue;
595
596		media_timed_event firstBufferEvent(eventTime,
597			B_NEW_BUFFER);
598
599		output->fFramesSent = 0;
600
601		firstBufferEvent.pointer = (void*) output;
602		EventQueue()->AddEvent(firstBufferEvent);
603	}
604}
605
606
607void
608BMediaClientNode::_HandleBuffer(BBuffer* buffer)
609{
610	CALLED();
611
612	media_destination dest;
613	dest.id = buffer->Header()->destination;
614	BMediaInput* conn = fOwner->_FindInput(dest);
615
616	if (conn != NULL)
617		conn->HandleBuffer(buffer);
618
619	// TODO: Investigate system level latency logging
620
621	if (conn->HasBinding()) {
622		BMediaOutput* output = dynamic_cast<BMediaOutput*>(conn->Binding());
623		output->SendBuffer(buffer);
624	}
625}
626
627
628void
629BMediaClientNode::_ProduceNewBuffer(const media_timed_event* event,
630	bigtime_t late)
631{
632	CALLED();
633
634	if (RunState() != BMediaEventLooper::B_STARTED)
635		return;
636
637	// The connection is get through the event
638	BMediaOutput* output
639		= dynamic_cast<BMediaOutput*>((BMediaConnection*)event->pointer);
640	if (output == NULL)
641		return;
642
643	if (output->_IsEnabled()) {
644		BBuffer* buffer = _GetNextBuffer(output, event->event_time);
645
646		if (buffer != NULL) {
647			if (output->SendBuffer(buffer) != B_OK) {
648				TRACE("BMediaClientNode: Failed to send buffer\n");
649				// The output failed, let's recycle the buffer
650				buffer->Recycle();
651			}
652		}
653	}
654
655	bigtime_t time = 0;
656	media_format format = output->fConnection.format;
657	if (format.IsAudio()) {
658		size_t nFrames = format.u.raw_audio.buffer_size
659			/ ((format.u.raw_audio.format
660				& media_raw_audio_format::B_AUDIO_SIZE_MASK)
661			* format.u.raw_audio.channel_count);
662		output->fFramesSent += nFrames;
663
664		time = fStartTime + bigtime_t((1000000LL * output->fFramesSent)
665			/ (int32)format.u.raw_audio.frame_rate);
666	}
667
668	media_timed_event nextEvent(time, B_NEW_BUFFER);
669	EventQueue()->AddEvent(nextEvent);
670}
671
672
673BBuffer*
674BMediaClientNode::_GetNextBuffer(BMediaOutput* output, bigtime_t eventTime)
675{
676	CALLED();
677
678	BBuffer* buffer
679		= output->fBufferGroup->RequestBuffer(output->BufferSize(), 0);
680	if (buffer == NULL) {
681		TRACE("MediaClientNode:::_GetNextBuffer: Failed to get the buffer\n");
682		return NULL;
683	}
684
685	media_header* header = buffer->Header();
686	header->type = output->fConnection.format.type;
687	header->size_used = output->BufferSize();
688	header->time_source = TimeSource()->ID();
689	header->start_time = eventTime;
690
691	return buffer;
692}
693