xref: /haiku/src/kits/media/BufferProducer.cpp (revision 71452e98334eaac603bf542d159e24788a46bebb)
1 /*
2  * Copyright 2010-2012, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2002-2003, Marcus Overhagen, <Marcus@Overhagen.de>.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include <Buffer.h>
9 #include <BufferConsumer.h>
10 #include <BufferGroup.h>
11 #include <BufferProducer.h>
12 
13 #include "debug.h"
14 #include "DataExchange.h"
15 #include "MediaMisc.h"
16 
17 
18 // #pragma mark - protected BBufferProducer
19 
20 
21 BBufferProducer::~BBufferProducer()
22 {
23 	CALLED();
24 }
25 
26 
27 // #pragma mark - public BBufferProducer
28 
29 
30 /*static*/ status_t
31 BBufferProducer::ClipDataToRegion(int32 format, int32 size, const void* data,
32 	BRegion* region)
33 {
34 	CALLED();
35 
36 	if (format != B_CLIP_SHORT_RUNS)
37 		return B_MEDIA_BAD_CLIP_FORMAT;
38 
39 	return clip_shorts_to_region((const int16*)data, size / sizeof(int16),
40 		region);
41 }
42 
43 
44 media_type
45 BBufferProducer::ProducerType()
46 {
47 	CALLED();
48 	return fProducerType;
49 }
50 
51 
52 // #pragma mark - protected BBufferProducer
53 
54 
55 BBufferProducer::BBufferProducer(media_type producer_type)
56 	:
57 	BMediaNode("called by BBufferProducer"),
58 	fProducerType(producer_type),
59 	fInitialLatency(0),
60 	fInitialFlags(0),
61 	fDelay(0)
62 {
63 	CALLED();
64 
65 	AddNodeKind(B_BUFFER_PRODUCER);
66 }
67 
68 
69 status_t
70 BBufferProducer::VideoClippingChanged(const media_source& source,
71 	int16 numShorts, int16* clipData, const media_video_display_info& display,
72 	int32* /*_deprecated_*/)
73 {
74 	CALLED();
75 	// may be implemented by derived classes
76 	return B_ERROR;
77 }
78 
79 
80 status_t
81 BBufferProducer::GetLatency(bigtime_t* _latency)
82 {
83 	CALLED();
84 	// The default implementation of GetLatency() finds the maximum
85 	// latency of your currently-available outputs by iterating over
86 	// them, and returns that value in outLatency
87 
88 	int32 cookie;
89 	bigtime_t latency;
90 	media_output output;
91 	media_node_id unused;
92 
93 	*_latency = 0;
94 	cookie = 0;
95 	while (GetNextOutput(&cookie, &output) == B_OK) {
96 		if (output.destination == media_destination::null)
97 			continue;
98 
99 		if (output.node.node == fNodeID) {
100 			// avoid port writes (deadlock) if loopback connection
101 			if (fConsumerThis == NULL)
102 				fConsumerThis = dynamic_cast<BBufferConsumer*>(this);
103 			if (fConsumerThis == NULL)
104 				continue;
105 
106 			latency = 0;
107 			if (fConsumerThis->GetLatencyFor(output.destination, &latency,
108 					&unused) == B_OK && latency > *_latency) {
109 				*_latency = latency;
110 			}
111 		} else if (FindLatencyFor(output.destination, &latency, &unused)
112 				== B_OK &&  latency > *_latency) {
113 			*_latency = latency;
114 		}
115 	}
116 	printf("BBufferProducer::GetLatency: node %" B_PRId32 ", name \"%s\" has "
117 		"max latency %" B_PRId64 "\n", fNodeID, fName, *_latency);
118 	return B_OK;
119 }
120 
121 
122 status_t
123 BBufferProducer::SetPlayRate(int32 numer, int32 denom)
124 {
125 	CALLED();
126 	// may be implemented by derived classes
127 	return B_ERROR;
128 }
129 
130 
131 status_t
132 BBufferProducer::HandleMessage(int32 message, const void* data, size_t size)
133 {
134 	PRINT(4, "BBufferProducer::HandleMessage %#lx, node %ld\n", message,
135 		fNodeID);
136 
137 	switch (message) {
138 		case PRODUCER_SET_RUN_MODE_DELAY:
139 		{
140 			const producer_set_run_mode_delay_command* command
141 				= static_cast<const producer_set_run_mode_delay_command*>(data);
142 			// when changing this, also change NODE_SET_RUN_MODE
143 			fDelay = command->delay;
144 			fRunMode = command->mode;
145 
146 			TRACE("PRODUCER_SET_RUN_MODE_DELAY: fDelay now %" B_PRId64 "\n",
147 				fDelay);
148 
149 			SetRunMode(fRunMode);
150 			return B_OK;
151 		}
152 
153 		case PRODUCER_FORMAT_SUGGESTION_REQUESTED:
154 		{
155 			const producer_format_suggestion_requested_request* request
156 				= static_cast<
157 					const producer_format_suggestion_requested_request*>(data);
158 			producer_format_suggestion_requested_reply reply;
159 			status_t status = FormatSuggestionRequested(request->type,
160 				request->quality, &reply.format);
161 			request->SendReply(status, &reply, sizeof(reply));
162 			return B_OK;
163 		}
164 
165 		case PRODUCER_FORMAT_PROPOSAL:
166 		{
167 			const producer_format_proposal_request* request
168 				= static_cast<const producer_format_proposal_request*>(data);
169 			producer_format_proposal_reply reply;
170 			reply.format = request->format;
171 			status_t status = FormatProposal(request->output, &reply.format);
172 			request->SendReply(status, &reply, sizeof(reply));
173 			return B_OK;
174 		}
175 
176 		case PRODUCER_PREPARE_TO_CONNECT:
177 		{
178 			const producer_prepare_to_connect_request* request
179 				= static_cast<const producer_prepare_to_connect_request*>(data);
180 			producer_prepare_to_connect_reply reply;
181 			reply.format = request->format;
182 			reply.out_source = request->source;
183 			memcpy(reply.name, request->name, B_MEDIA_NAME_LENGTH);
184 			status_t status = PrepareToConnect(request->source,
185 				request->destination, &reply.format, &reply.out_source,
186 				reply.name);
187 			request->SendReply(status, &reply, sizeof(reply));
188 			return B_OK;
189 		}
190 
191 		case PRODUCER_CONNECT:
192 		{
193 			const producer_connect_request* request
194 				= static_cast<const producer_connect_request*>(data);
195 			producer_connect_reply reply;
196 			memcpy(reply.name, request->name, B_MEDIA_NAME_LENGTH);
197 			Connect(request->error, request->source, request->destination,
198 				request->format, reply.name);
199 			request->SendReply(B_OK, &reply, sizeof(reply));
200 			return B_OK;
201 		}
202 
203 		case PRODUCER_DISCONNECT:
204 		{
205 			const producer_disconnect_request* request
206 				= static_cast<const producer_disconnect_request*>(data);
207 			producer_disconnect_reply reply;
208 			Disconnect(request->source, request->destination);
209 			request->SendReply(B_OK, &reply, sizeof(reply));
210 			return B_OK;
211 		}
212 
213 		case PRODUCER_GET_INITIAL_LATENCY:
214 		{
215 			const producer_get_initial_latency_request* request
216 				= static_cast<
217 					const producer_get_initial_latency_request*>(data);
218 			producer_get_initial_latency_reply reply;
219 			reply.initial_latency = fInitialLatency;
220 			reply.flags = fInitialFlags;
221 			request->SendReply(B_OK, &reply, sizeof(reply));
222 			return B_OK;
223 		}
224 
225 		case PRODUCER_SET_PLAY_RATE:
226 		{
227 			const producer_set_play_rate_request* request
228 				= static_cast<const producer_set_play_rate_request*>(data);
229 			producer_set_play_rate_reply reply;
230 			status_t status = SetPlayRate(request->numer, request->denom);
231 			request->SendReply(status, &reply, sizeof(reply));
232 			return B_OK;
233 		}
234 
235 		case PRODUCER_GET_LATENCY:
236 		{
237 			const producer_get_latency_request* request
238 				= static_cast<const producer_get_latency_request*>(data);
239 			producer_get_latency_reply reply;
240 			status_t status = GetLatency(&reply.latency);
241 			request->SendReply(status, &reply, sizeof(reply));
242 			return B_OK;
243 		}
244 
245 		case PRODUCER_GET_NEXT_OUTPUT:
246 		{
247 			const producer_get_next_output_request* request
248 				= static_cast<const producer_get_next_output_request*>(data);
249 			producer_get_next_output_reply reply;
250 			reply.cookie = request->cookie;
251 			status_t status = GetNextOutput(&reply.cookie, &reply.output);
252 			request->SendReply(status, &reply, sizeof(reply));
253 			return B_OK;
254 		}
255 
256 		case PRODUCER_DISPOSE_OUTPUT_COOKIE:
257 		{
258 			const producer_dispose_output_cookie_request*request
259 				= static_cast<
260 					const producer_dispose_output_cookie_request*>(data);
261 			producer_dispose_output_cookie_reply reply;
262 			DisposeOutputCookie(request->cookie);
263 			request->SendReply(B_OK, &reply, sizeof(reply));
264 			return B_OK;
265 		}
266 
267 		case PRODUCER_SET_BUFFER_GROUP:
268 		{
269 			const producer_set_buffer_group_command* command
270 				= static_cast<const producer_set_buffer_group_command*>(data);
271 			node_request_completed_command replycommand;
272 			BBufferGroup *group;
273 			group = command->buffer_count != 0
274 				? new BBufferGroup(command->buffer_count, command->buffers)
275 				: NULL;
276 
277 			if (group != NULL && group->InitCheck() != B_OK) {
278 				ERROR("BBufferProducer::HandleMessage PRODUCER_SET_BUFFER_GROUP"
279 					" group InitCheck() failed.\n");
280 				delete group;
281 				group = NULL;
282 			}
283 			status_t status = SetBufferGroup(command->source, group);
284 			if (command->destination == media_destination::null)
285 				return B_OK;
286 			replycommand.info.what
287 				= media_request_info::B_SET_OUTPUT_BUFFERS_FOR;
288 			replycommand.info.change_tag = command->change_tag;
289 			replycommand.info.status = status;
290 			replycommand.info.cookie = group;
291 			replycommand.info.user_data = command->user_data;
292 			replycommand.info.source = command->source;
293 			replycommand.info.destination = command->destination;
294 			SendToPort(command->destination.port, NODE_REQUEST_COMPLETED,
295 				&replycommand, sizeof(replycommand));
296 			return B_OK;
297 		}
298 
299 		case PRODUCER_FORMAT_CHANGE_REQUESTED:
300 		{
301 			const producer_format_change_requested_command* command
302 				= static_cast<
303 					const producer_format_change_requested_command*>(data);
304 			node_request_completed_command replycommand;
305 			replycommand.info.format = command->format;
306 			status_t status = FormatChangeRequested(command->source,
307 				command->destination, &replycommand.info.format, NULL);
308 			if (command->destination == media_destination::null)
309 				return B_OK;
310 			replycommand.info.what
311 				= media_request_info::B_REQUEST_FORMAT_CHANGE;
312 			replycommand.info.change_tag = command->change_tag;
313 			replycommand.info.status = status;
314 			//replycommand.info.cookie
315 			replycommand.info.user_data = command->user_data;
316 			replycommand.info.source = command->source;
317 			replycommand.info.destination = command->destination;
318 			SendToPort(command->destination.port, NODE_REQUEST_COMPLETED,
319 				&replycommand, sizeof(replycommand));
320 			return B_OK;
321 		}
322 
323 		case PRODUCER_VIDEO_CLIPPING_CHANGED:
324 		{
325 			const producer_video_clipping_changed_command* command
326 				= static_cast<
327 					const producer_video_clipping_changed_command*>(data);
328 			node_request_completed_command replycommand;
329 			status_t status = VideoClippingChanged(command->source,
330 				command->short_count, (int16 *)command->shorts,
331 				command->display, NULL);
332 			if (command->destination == media_destination::null)
333 				return B_OK;
334 			replycommand.info.what
335 				= media_request_info::B_SET_VIDEO_CLIPPING_FOR;
336 			replycommand.info.change_tag = command->change_tag;
337 			replycommand.info.status = status;
338 			//replycommand.info.cookie
339 			replycommand.info.user_data = command->user_data;
340 			replycommand.info.source = command->source;
341 			replycommand.info.destination = command->destination;
342 			replycommand.info.format.type = B_MEDIA_RAW_VIDEO;
343 			replycommand.info.format.u.raw_video.display = command->display;
344 			SendToPort(command->destination.port, NODE_REQUEST_COMPLETED,
345 				&replycommand, sizeof(replycommand));
346 			return B_OK;
347 		}
348 
349 		case PRODUCER_ADDITIONAL_BUFFER_REQUESTED:
350 		{
351 			const producer_additional_buffer_requested_command* command
352 				= static_cast<
353 					const producer_additional_buffer_requested_command*>(data);
354 			AdditionalBufferRequested(command->source, command->prev_buffer,
355 				command->prev_time, command->has_seek_tag
356 					? &command->prev_tag : NULL);
357 			return B_OK;
358 		}
359 
360 		case PRODUCER_LATENCY_CHANGED:
361 		{
362 			const producer_latency_changed_command* command
363 				= static_cast<const producer_latency_changed_command*>(data);
364 			LatencyChanged(command->source, command->destination,
365 				command->latency, command->flags);
366 			return B_OK;
367 		}
368 
369 		case PRODUCER_LATE_NOTICE_RECEIVED:
370 		{
371 			const producer_late_notice_received_command* command
372 				= static_cast<
373 					const producer_late_notice_received_command*>(data);
374 			LateNoticeReceived(command->source, command->how_much,
375 				command->performance_time);
376 			return B_OK;
377 		}
378 
379 		case PRODUCER_ENABLE_OUTPUT:
380 		{
381 			const producer_enable_output_command* command
382 				= static_cast<const producer_enable_output_command*>(data);
383 			node_request_completed_command replycommand;
384 			EnableOutput(command->source, command->enabled, NULL);
385 			if (command->destination == media_destination::null)
386 				return B_OK;
387 
388 			replycommand.info.what = media_request_info::B_SET_OUTPUT_ENABLED;
389 			replycommand.info.change_tag = command->change_tag;
390 			replycommand.info.status = B_OK;
391 			//replycommand.info.cookie
392 			replycommand.info.user_data = command->user_data;
393 			replycommand.info.source = command->source;
394 			replycommand.info.destination = command->destination;
395 			//replycommand.info.format
396 			SendToPort(command->destination.port, NODE_REQUEST_COMPLETED,
397 				&replycommand, sizeof(replycommand));
398 			return B_OK;
399 		}
400 	}
401 
402 	return B_ERROR;
403 }
404 
405 
406 void
407 BBufferProducer::AdditionalBufferRequested(const media_source& source,
408 	media_buffer_id previousBuffer, bigtime_t previousTime,
409 	const media_seek_tag* previousTag)
410 {
411 	CALLED();
412 	// may be implemented by derived classes
413 }
414 
415 
416 void
417 BBufferProducer::LatencyChanged(const media_source& source,
418 	const media_destination& destination, bigtime_t newLatency, uint32 flags)
419 {
420 	CALLED();
421 	// may be implemented by derived classes
422 }
423 
424 
425 status_t
426 BBufferProducer::SendBuffer(BBuffer* buffer, const media_source& source,
427 	const media_destination& destination)
428 {
429 	CALLED();
430 	if (destination == media_destination::null)
431 		return B_MEDIA_BAD_DESTINATION;
432 	if (source == media_source::null)
433 		return B_MEDIA_BAD_SOURCE;
434 	if (buffer == NULL)
435 		return B_BAD_VALUE;
436 
437 	consumer_buffer_received_command command;
438 	command.buffer = buffer->ID();
439 	command.header = *buffer->Header();
440 	command.header.buffer = command.buffer;
441 	command.header.source_port = source.port;
442 	command.header.source = source.id;
443 	command.header.destination = destination.id;
444 	command.header.owner = 0; // XXX fill with "buffer owner info area"
445 	command.header.start_time += fDelay;
446 		// time compensation as set by BMediaRoster::SetProducerRunModeDelay()
447 
448 	//printf("BBufferProducer::SendBuffer     node %2ld, buffer %2ld, start_time %12Ld with lateness %6Ld\n", ID(), buffer->Header()->buffer, command.header.start_time, TimeSource()->Now() - command.header.start_time);
449 
450 	return SendToPort(destination.port, CONSUMER_BUFFER_RECEIVED, &command,
451 		sizeof(command));
452 }
453 
454 
455 status_t
456 BBufferProducer::SendDataStatus(int32 status,
457 	const media_destination& destination, bigtime_t atTime)
458 {
459 	CALLED();
460 	if (IS_INVALID_DESTINATION(destination))
461 		return B_MEDIA_BAD_DESTINATION;
462 
463 	consumer_producer_data_status_command command;
464 	command.for_whom = destination;
465 	command.status = status;
466 	command.at_performance_time = atTime;
467 
468 	return SendToPort(destination.port, CONSUMER_PRODUCER_DATA_STATUS, &command,
469 		sizeof(command));
470 }
471 
472 
473 status_t
474 BBufferProducer::ProposeFormatChange(media_format* format,
475 	const media_destination& destination)
476 {
477 	CALLED();
478 	if (IS_INVALID_DESTINATION(destination))
479 		return B_MEDIA_BAD_DESTINATION;
480 
481 	consumer_accept_format_request request;
482 	consumer_accept_format_reply reply;
483 
484 	request.dest = destination;
485 	request.format = *format;
486 	status_t status = QueryPort(destination.port, CONSUMER_ACCEPT_FORMAT,
487 		&request, sizeof(request), &reply, sizeof(reply));
488 	if (status != B_OK)
489 		return status;
490 
491 	*format = reply.format;
492 	return B_OK;
493 }
494 
495 
496 status_t
497 BBufferProducer::ChangeFormat(const media_source& source,
498 	const media_destination& destination, media_format* format)
499 {
500 	CALLED();
501 	if (IS_INVALID_SOURCE(source))
502 		return B_MEDIA_BAD_SOURCE;
503 	if (IS_INVALID_DESTINATION(destination))
504 		return B_MEDIA_BAD_DESTINATION;
505 
506 	consumer_format_changed_request request;
507 	consumer_format_changed_reply reply;
508 
509 	request.producer = source;
510 	request.consumer = destination;
511 	request.format = *format;
512 
513 	// we use a request/reply to make this synchronous
514 	return QueryPort(destination.port, CONSUMER_FORMAT_CHANGED, &request,
515 		sizeof(request), &reply, sizeof(reply));
516 }
517 
518 
519 status_t
520 BBufferProducer::FindLatencyFor(const media_destination& destination,
521 	bigtime_t* _latency, media_node_id* _timesource)
522 {
523 	CALLED();
524 	if (IS_INVALID_DESTINATION(destination))
525 		return B_MEDIA_BAD_DESTINATION;
526 
527 	consumer_get_latency_for_request request;
528 	consumer_get_latency_for_reply reply;
529 
530 	request.for_whom = destination;
531 
532 	status_t status = QueryPort(destination.port, CONSUMER_GET_LATENCY_FOR,
533 		&request, sizeof(request), &reply, sizeof(reply));
534 	if (status != B_OK)
535 		return status;
536 
537 	*_latency = reply.latency;
538 	*_timesource = reply.timesource;
539 	return B_OK;
540 }
541 
542 
543 status_t
544 BBufferProducer::FindSeekTag(const media_destination& destination,
545 	bigtime_t targetTime, media_seek_tag* _tag, bigtime_t* _tagged_time,
546 	uint32* _flags, uint32 flags)
547 {
548 	CALLED();
549 	if (IS_INVALID_DESTINATION(destination))
550 		return B_MEDIA_BAD_DESTINATION;
551 
552 	consumer_seek_tag_requested_request request;
553 	consumer_seek_tag_requested_reply reply;
554 
555 	request.destination = destination;
556 	request.target_time = targetTime;
557 	request.flags = flags;
558 
559 	status_t status = QueryPort(destination.port, CONSUMER_SEEK_TAG_REQUESTED,
560 		&request, sizeof(request), &reply, sizeof(reply));
561 	if (status != B_OK)
562 		return status;
563 
564 	*_tag = reply.seek_tag;
565 	*_tagged_time = reply.tagged_time;
566 	*_flags = reply.flags;
567 	return B_OK;
568 }
569 
570 
571 void
572 BBufferProducer::SetInitialLatency(bigtime_t initialLatency, uint32 flags)
573 {
574 	fInitialLatency = initialLatency;
575 	fInitialFlags = flags;
576 }
577 
578 
579 // #pragma mark - private BBufferProducer
580 
581 
582 /*
583 private unimplemented
584 BBufferProducer::BBufferProducer()
585 BBufferProducer::BBufferProducer(const BBufferProducer &clone)
586 BBufferProducer & BBufferProducer::operator=(const BBufferProducer &clone)
587 */
588 
589 status_t BBufferProducer::_Reserved_BufferProducer_0(void*) { return B_ERROR; }
590 status_t BBufferProducer::_Reserved_BufferProducer_1(void*) { return B_ERROR; }
591 status_t BBufferProducer::_Reserved_BufferProducer_2(void*) { return B_ERROR; }
592 status_t BBufferProducer::_Reserved_BufferProducer_3(void*) { return B_ERROR; }
593 status_t BBufferProducer::_Reserved_BufferProducer_4(void*) { return B_ERROR; }
594 status_t BBufferProducer::_Reserved_BufferProducer_5(void*) { return B_ERROR; }
595 status_t BBufferProducer::_Reserved_BufferProducer_6(void*) { return B_ERROR; }
596 status_t BBufferProducer::_Reserved_BufferProducer_7(void*) { return B_ERROR; }
597 status_t BBufferProducer::_Reserved_BufferProducer_8(void*) { return B_ERROR; }
598 status_t BBufferProducer::_Reserved_BufferProducer_9(void*) { return B_ERROR; }
599 status_t BBufferProducer::_Reserved_BufferProducer_10(void*) { return B_ERROR; }
600 status_t BBufferProducer::_Reserved_BufferProducer_11(void*) { return B_ERROR; }
601 status_t BBufferProducer::_Reserved_BufferProducer_12(void*) { return B_ERROR; }
602 status_t BBufferProducer::_Reserved_BufferProducer_13(void*) { return B_ERROR; }
603 status_t BBufferProducer::_Reserved_BufferProducer_14(void*) { return B_ERROR; }
604 status_t BBufferProducer::_Reserved_BufferProducer_15(void*) { return B_ERROR; }
605 
606 
607 //! Deprecated.
608 status_t
609 BBufferProducer::SendBuffer(BBuffer* buffer,
610 	const media_destination& destination)
611 {
612 	CALLED();
613 
614 	// Try to find the source - this is the best we can do
615 	media_output output;
616 	int32 cookie = 0;
617 	status_t status = GetNextOutput(&cookie, &output);
618 	if (status != B_OK)
619 		return status;
620 
621 	return SendBuffer(buffer, output.source, destination);
622 }
623 
624 
625 status_t
626 BBufferProducer::clip_shorts_to_region(const int16* data, int count,
627 	BRegion* output)
628 {
629 	UNIMPLEMENTED();
630 	return B_ERROR;
631 }
632 
633 
634 status_t
635 BBufferProducer::clip_region_to_shorts(const BRegion* input, int16* data,
636 	int maxCount, int* _count)
637 {
638 	UNIMPLEMENTED();
639 	return B_ERROR;
640 }
641