xref: /haiku/src/kits/media/experimental/MediaClientNode.cpp (revision 1a3518cf757c2da8006753f83962da5935bbc82b)
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 
21 BMediaClientNode::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 
42 status_t
43 BMediaClientNode::SendBuffer(BBuffer* buffer, BMediaConnection* conn)
44 {
45 	return BBufferProducer::SendBuffer(buffer, conn->_Source(), conn->_Destination());
46 }
47 
48 
49 BMediaAddOn*
50 BMediaClientNode::AddOn(int32* id) const
51 {
52 	CALLED();
53 
54 	return fOwner->AddOn(id);
55 }
56 
57 
58 void
59 BMediaClientNode::NodeRegistered()
60 {
61 	CALLED();
62 
63 	fOwner->ClientRegistered();
64 
65 	Run();
66 }
67 
68 
69 void
70 BMediaClientNode::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 
99 void
100 BMediaClientNode::Start(bigtime_t performanceTime)
101 {
102 	CALLED();
103 
104 	BMediaEventLooper::Start(performanceTime);
105 }
106 
107 
108 void
109 BMediaClientNode::Stop(bigtime_t performanceTime, bool immediate)
110 {
111 	CALLED();
112 
113 	BMediaEventLooper::Stop(performanceTime, immediate);
114 }
115 
116 
117 void
118 BMediaClientNode::Seek(bigtime_t mediaTime, bigtime_t performanceTime)
119 {
120 	CALLED();
121 
122 	BMediaEventLooper::Seek(mediaTime, performanceTime);
123 }
124 
125 
126 void
127 BMediaClientNode::TimeWarp(bigtime_t realTime, bigtime_t performanceTime)
128 {
129 	CALLED();
130 
131 	BMediaEventLooper::TimeWarp(realTime, performanceTime);
132 }
133 
134 
135 status_t
136 BMediaClientNode::HandleMessage(int32 message,
137 	const void* data, size_t size)
138 {
139 	CALLED();
140 
141 	return B_ERROR;
142 }
143 
144 
145 status_t
146 BMediaClientNode::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 
159 status_t
160 BMediaClientNode::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 
183 void
184 BMediaClientNode::DisposeInputCookie(int32 cookie)
185 {
186 	CALLED();
187 }
188 
189 
190 void
191 BMediaClientNode::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 
201 status_t
202 BMediaClientNode::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 
217 status_t
218 BMediaClientNode::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 
246 void
247 BMediaClientNode::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 
269 status_t
270 BMediaClientNode::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 
279 status_t
280 BMediaClientNode::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 
303 status_t
304 BMediaClientNode::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 
317 status_t
318 BMediaClientNode::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 
328 void
329 BMediaClientNode::LateNoticeReceived(const media_source& source,
330 	bigtime_t late, bigtime_t when)
331 {
332 	CALLED();
333 
334 }
335 
336 
337 status_t
338 BMediaClientNode::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 
360 status_t
361 BMediaClientNode::DisposeOutputCookie(int32 cookie)
362 {
363 	CALLED();
364 
365 	return B_OK;
366 }
367 
368 
369 status_t
370 BMediaClientNode::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 
396 status_t
397 BMediaClientNode::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 
428 void
429 BMediaClientNode::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 
467 void
468 BMediaClientNode::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 
493 void
494 BMediaClientNode::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 
505 status_t
506 BMediaClientNode::GetLatency(bigtime_t* outLatency)
507 {
508 	CALLED();
509 
510 	return BBufferProducer::GetLatency(outLatency);
511 }
512 
513 
514 void
515 BMediaClientNode::LatencyChanged(const media_source& source,
516 	const media_destination& dest, bigtime_t latency, uint32 flags)
517 {
518 	CALLED();
519 }
520 
521 
522 void
523 BMediaClientNode::ProducerDataStatus(const media_destination& dest,
524 	int32 status, bigtime_t when)
525 {
526 	CALLED();
527 }
528 
529 
530 void
531 BMediaClientNode::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 
579 BMediaClientNode::~BMediaClientNode()
580 {
581 	CALLED();
582 
583 	Quit();
584 }
585 
586 
587 void
588 BMediaClientNode::_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 
607 void
608 BMediaClientNode::_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 
628 void
629 BMediaClientNode::_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 
673 BBuffer*
674 BMediaClientNode::_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