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