xref: /haiku/src/kits/media/BufferProducer.cpp (revision f0650dc98fed895fc134a359aab99c27de6a0c6a)
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 			TRACE("PRODUCER_SET_RUN_MODE_DELAY: fDelay now %Ld\n", fDelay);
146 			SetRunMode(fRunMode);
147 			return B_OK;
148 		}
149 
150 		case PRODUCER_FORMAT_SUGGESTION_REQUESTED:
151 		{
152 			const producer_format_suggestion_requested_request* request
153 				= static_cast<
154 					const producer_format_suggestion_requested_request*>(data);
155 			producer_format_suggestion_requested_reply reply;
156 			status_t status = FormatSuggestionRequested(request->type,
157 				request->quality, &reply.format);
158 			request->SendReply(status, &reply, sizeof(reply));
159 			return B_OK;
160 		}
161 
162 		case PRODUCER_FORMAT_PROPOSAL:
163 		{
164 			const producer_format_proposal_request* request
165 				= static_cast<const producer_format_proposal_request*>(data);
166 			producer_format_proposal_reply reply;
167 			reply.format = request->format;
168 			status_t status = FormatProposal(request->output, &reply.format);
169 			request->SendReply(status, &reply, sizeof(reply));
170 			return B_OK;
171 		}
172 
173 		case PRODUCER_PREPARE_TO_CONNECT:
174 		{
175 			const producer_prepare_to_connect_request* request
176 				= static_cast<const producer_prepare_to_connect_request*>(data);
177 			producer_prepare_to_connect_reply reply;
178 			reply.format = request->format;
179 			reply.out_source = request->source;
180 			memcpy(reply.name, request->name, B_MEDIA_NAME_LENGTH);
181 			status_t status = PrepareToConnect(request->source,
182 				request->destination, &reply.format, &reply.out_source,
183 				reply.name);
184 			request->SendReply(status, &reply, sizeof(reply));
185 			return B_OK;
186 		}
187 
188 		case PRODUCER_CONNECT:
189 		{
190 			const producer_connect_request* request
191 				= static_cast<const producer_connect_request*>(data);
192 			producer_connect_reply reply;
193 			memcpy(reply.name, request->name, B_MEDIA_NAME_LENGTH);
194 			Connect(request->error, request->source, request->destination,
195 				request->format, reply.name);
196 			request->SendReply(B_OK, &reply, sizeof(reply));
197 			return B_OK;
198 		}
199 
200 		case PRODUCER_DISCONNECT:
201 		{
202 			const producer_disconnect_request* request
203 				= static_cast<const producer_disconnect_request*>(data);
204 			producer_disconnect_reply reply;
205 			Disconnect(request->source, request->destination);
206 			request->SendReply(B_OK, &reply, sizeof(reply));
207 			return B_OK;
208 		}
209 
210 		case PRODUCER_GET_INITIAL_LATENCY:
211 		{
212 			const producer_get_initial_latency_request* request
213 				= static_cast<
214 					const producer_get_initial_latency_request*>(data);
215 			producer_get_initial_latency_reply reply;
216 			reply.initial_latency = fInitialLatency;
217 			reply.flags = fInitialFlags;
218 			request->SendReply(B_OK, &reply, sizeof(reply));
219 			return B_OK;
220 		}
221 
222 		case PRODUCER_SET_PLAY_RATE:
223 		{
224 			const producer_set_play_rate_request* request
225 				= static_cast<const producer_set_play_rate_request*>(data);
226 			producer_set_play_rate_reply reply;
227 			status_t status = SetPlayRate(request->numer, request->denom);
228 			request->SendReply(status, &reply, sizeof(reply));
229 			return B_OK;
230 		}
231 
232 		case PRODUCER_GET_LATENCY:
233 		{
234 			const producer_get_latency_request* request
235 				= static_cast<const producer_get_latency_request*>(data);
236 			producer_get_latency_reply reply;
237 			status_t status = GetLatency(&reply.latency);
238 			request->SendReply(status, &reply, sizeof(reply));
239 			return B_OK;
240 		}
241 
242 		case PRODUCER_GET_NEXT_OUTPUT:
243 		{
244 			const producer_get_next_output_request* request
245 				= static_cast<const producer_get_next_output_request*>(data);
246 			producer_get_next_output_reply reply;
247 			reply.cookie = request->cookie;
248 			status_t status = GetNextOutput(&reply.cookie, &reply.output);
249 			request->SendReply(status, &reply, sizeof(reply));
250 			return B_OK;
251 		}
252 
253 		case PRODUCER_DISPOSE_OUTPUT_COOKIE:
254 		{
255 			const producer_dispose_output_cookie_request*request
256 				= static_cast<
257 					const producer_dispose_output_cookie_request*>(data);
258 			producer_dispose_output_cookie_reply reply;
259 			DisposeOutputCookie(request->cookie);
260 			request->SendReply(B_OK, &reply, sizeof(reply));
261 			return B_OK;
262 		}
263 
264 		case PRODUCER_SET_BUFFER_GROUP:
265 		{
266 			const producer_set_buffer_group_command* command
267 				= static_cast<const producer_set_buffer_group_command*>(data);
268 			node_request_completed_command replycommand;
269 			BBufferGroup *group;
270 			group = command->buffer_count != 0
271 				? new BBufferGroup(command->buffer_count, command->buffers)
272 				: NULL;
273 
274 			if (group != NULL && group->InitCheck() != B_OK) {
275 				ERROR("BBufferProducer::HandleMessage PRODUCER_SET_BUFFER_GROUP"
276 					" group InitCheck() failed.\n");
277 				delete group;
278 				group = NULL;
279 			}
280 			status_t status = SetBufferGroup(command->source, group);
281 			if (command->destination == media_destination::null)
282 				return B_OK;
283 			replycommand.info.what
284 				= media_request_info::B_SET_OUTPUT_BUFFERS_FOR;
285 			replycommand.info.change_tag = command->change_tag;
286 			replycommand.info.status = status;
287 			replycommand.info.cookie = group;
288 			replycommand.info.user_data = command->user_data;
289 			replycommand.info.source = command->source;
290 			replycommand.info.destination = command->destination;
291 			SendToPort(command->destination.port, NODE_REQUEST_COMPLETED,
292 				&replycommand, sizeof(replycommand));
293 			return B_OK;
294 		}
295 
296 		case PRODUCER_FORMAT_CHANGE_REQUESTED:
297 		{
298 			const producer_format_change_requested_command* command
299 				= static_cast<
300 					const producer_format_change_requested_command*>(data);
301 			node_request_completed_command replycommand;
302 			replycommand.info.format = command->format;
303 			status_t status = FormatChangeRequested(command->source,
304 				command->destination, &replycommand.info.format, NULL);
305 			if (command->destination == media_destination::null)
306 				return B_OK;
307 			replycommand.info.what
308 				= media_request_info::B_REQUEST_FORMAT_CHANGE;
309 			replycommand.info.change_tag = command->change_tag;
310 			replycommand.info.status = status;
311 			//replycommand.info.cookie
312 			replycommand.info.user_data = command->user_data;
313 			replycommand.info.source = command->source;
314 			replycommand.info.destination = command->destination;
315 			SendToPort(command->destination.port, NODE_REQUEST_COMPLETED,
316 				&replycommand, sizeof(replycommand));
317 			return B_OK;
318 		}
319 
320 		case PRODUCER_VIDEO_CLIPPING_CHANGED:
321 		{
322 			const producer_video_clipping_changed_command* command
323 				= static_cast<
324 					const producer_video_clipping_changed_command*>(data);
325 			node_request_completed_command replycommand;
326 			status_t status = VideoClippingChanged(command->source,
327 				command->short_count, (int16 *)command->shorts,
328 				command->display, NULL);
329 			if (command->destination == media_destination::null)
330 				return B_OK;
331 			replycommand.info.what
332 				= media_request_info::B_SET_VIDEO_CLIPPING_FOR;
333 			replycommand.info.change_tag = command->change_tag;
334 			replycommand.info.status = status;
335 			//replycommand.info.cookie
336 			replycommand.info.user_data = command->user_data;
337 			replycommand.info.source = command->source;
338 			replycommand.info.destination = command->destination;
339 			replycommand.info.format.type = B_MEDIA_RAW_VIDEO;
340 			replycommand.info.format.u.raw_video.display = command->display;
341 			SendToPort(command->destination.port, NODE_REQUEST_COMPLETED,
342 				&replycommand, sizeof(replycommand));
343 			return B_OK;
344 		}
345 
346 		case PRODUCER_ADDITIONAL_BUFFER_REQUESTED:
347 		{
348 			const producer_additional_buffer_requested_command* command
349 				= static_cast<
350 					const producer_additional_buffer_requested_command*>(data);
351 			AdditionalBufferRequested(command->source, command->prev_buffer,
352 				command->prev_time, command->has_seek_tag
353 					? &command->prev_tag : NULL);
354 			return B_OK;
355 		}
356 
357 		case PRODUCER_LATENCY_CHANGED:
358 		{
359 			const producer_latency_changed_command* command
360 				= static_cast<const producer_latency_changed_command*>(data);
361 			LatencyChanged(command->source, command->destination,
362 				command->latency, command->flags);
363 			return B_OK;
364 		}
365 
366 		case PRODUCER_LATE_NOTICE_RECEIVED:
367 		{
368 			const producer_late_notice_received_command* command
369 				= static_cast<
370 					const producer_late_notice_received_command*>(data);
371 			LateNoticeReceived(command->source, command->how_much,
372 				command->performance_time);
373 			return B_OK;
374 		}
375 
376 		case PRODUCER_ENABLE_OUTPUT:
377 		{
378 			const producer_enable_output_command* command
379 				= static_cast<const producer_enable_output_command*>(data);
380 			node_request_completed_command replycommand;
381 			EnableOutput(command->source, command->enabled, NULL);
382 			if (command->destination == media_destination::null)
383 				return B_OK;
384 
385 			replycommand.info.what = media_request_info::B_SET_OUTPUT_ENABLED;
386 			replycommand.info.change_tag = command->change_tag;
387 			replycommand.info.status = B_OK;
388 			//replycommand.info.cookie
389 			replycommand.info.user_data = command->user_data;
390 			replycommand.info.source = command->source;
391 			replycommand.info.destination = command->destination;
392 			//replycommand.info.format
393 			SendToPort(command->destination.port, NODE_REQUEST_COMPLETED,
394 				&replycommand, sizeof(replycommand));
395 			return B_OK;
396 		}
397 	}
398 
399 	return B_ERROR;
400 }
401 
402 
403 void
404 BBufferProducer::AdditionalBufferRequested(const media_source& source,
405 	media_buffer_id previousBuffer, bigtime_t previousTime,
406 	const media_seek_tag* previousTag)
407 {
408 	CALLED();
409 	// may be implemented by derived classes
410 }
411 
412 
413 void
414 BBufferProducer::LatencyChanged(const media_source& source,
415 	const media_destination& destination, bigtime_t newLatency, uint32 flags)
416 {
417 	CALLED();
418 	// may be implemented by derived classes
419 }
420 
421 
422 status_t
423 BBufferProducer::SendBuffer(BBuffer* buffer, const media_source& source,
424 	const media_destination& destination)
425 {
426 	CALLED();
427 	if (destination == media_destination::null)
428 		return B_MEDIA_BAD_DESTINATION;
429 	if (source == media_source::null)
430 		return B_MEDIA_BAD_SOURCE;
431 	if (buffer == NULL)
432 		return B_BAD_VALUE;
433 
434 	consumer_buffer_received_command command;
435 	command.buffer = buffer->ID();
436 	command.header = *buffer->Header();
437 	command.header.buffer = command.buffer;
438 	command.header.source_port = source.port;
439 	command.header.source = source.id;
440 	command.header.destination = destination.id;
441 	command.header.owner = 0; // XXX fill with "buffer owner info area"
442 	command.header.start_time += fDelay;
443 		// time compensation as set by BMediaRoster::SetProducerRunModeDelay()
444 
445 	//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);
446 
447 	return SendToPort(destination.port, CONSUMER_BUFFER_RECEIVED, &command,
448 		sizeof(command));
449 }
450 
451 
452 status_t
453 BBufferProducer::SendDataStatus(int32 status,
454 	const media_destination& destination, bigtime_t atTime)
455 {
456 	CALLED();
457 	if (IS_INVALID_DESTINATION(destination))
458 		return B_MEDIA_BAD_DESTINATION;
459 
460 	consumer_producer_data_status_command command;
461 	command.for_whom = destination;
462 	command.status = status;
463 	command.at_performance_time = atTime;
464 
465 	return SendToPort(destination.port, CONSUMER_PRODUCER_DATA_STATUS, &command,
466 		sizeof(command));
467 }
468 
469 
470 status_t
471 BBufferProducer::ProposeFormatChange(media_format* format,
472 	const media_destination& destination)
473 {
474 	CALLED();
475 	if (IS_INVALID_DESTINATION(destination))
476 		return B_MEDIA_BAD_DESTINATION;
477 
478 	consumer_accept_format_request request;
479 	consumer_accept_format_reply reply;
480 
481 	request.dest = destination;
482 	request.format = *format;
483 	status_t status = QueryPort(destination.port, CONSUMER_ACCEPT_FORMAT,
484 		&request, sizeof(request), &reply, sizeof(reply));
485 	if (status != B_OK)
486 		return status;
487 
488 	*format = reply.format;
489 	return B_OK;
490 }
491 
492 
493 status_t
494 BBufferProducer::ChangeFormat(const media_source& source,
495 	const media_destination& destination, media_format* format)
496 {
497 	CALLED();
498 	if (IS_INVALID_SOURCE(source))
499 		return B_MEDIA_BAD_SOURCE;
500 	if (IS_INVALID_DESTINATION(destination))
501 		return B_MEDIA_BAD_DESTINATION;
502 
503 	consumer_format_changed_request request;
504 	consumer_format_changed_reply reply;
505 
506 	request.producer = source;
507 	request.consumer = destination;
508 	request.format = *format;
509 
510 	// we use a request/reply to make this synchronous
511 	return QueryPort(destination.port, CONSUMER_FORMAT_CHANGED, &request,
512 		sizeof(request), &reply, sizeof(reply));
513 }
514 
515 
516 status_t
517 BBufferProducer::FindLatencyFor(const media_destination& destination,
518 	bigtime_t* _latency, media_node_id* _timesource)
519 {
520 	CALLED();
521 	if (IS_INVALID_DESTINATION(destination))
522 		return B_MEDIA_BAD_DESTINATION;
523 
524 	consumer_get_latency_for_request request;
525 	consumer_get_latency_for_reply reply;
526 
527 	request.for_whom = destination;
528 
529 	status_t status = QueryPort(destination.port, CONSUMER_GET_LATENCY_FOR,
530 		&request, sizeof(request), &reply, sizeof(reply));
531 	if (status != B_OK)
532 		return status;
533 
534 	*_latency = reply.latency;
535 	*_timesource = reply.timesource;
536 	return B_OK;
537 }
538 
539 
540 status_t
541 BBufferProducer::FindSeekTag(const media_destination& destination,
542 	bigtime_t targetTime, media_seek_tag* _tag, bigtime_t* _tagged_time,
543 	uint32* _flags, uint32 flags)
544 {
545 	CALLED();
546 	if (IS_INVALID_DESTINATION(destination))
547 		return B_MEDIA_BAD_DESTINATION;
548 
549 	consumer_seek_tag_requested_request request;
550 	consumer_seek_tag_requested_reply reply;
551 
552 	request.destination = destination;
553 	request.target_time = targetTime;
554 	request.flags = flags;
555 
556 	status_t status = QueryPort(destination.port, CONSUMER_SEEK_TAG_REQUESTED,
557 		&request, sizeof(request), &reply, sizeof(reply));
558 	if (status != B_OK)
559 		return status;
560 
561 	*_tag = reply.seek_tag;
562 	*_tagged_time = reply.tagged_time;
563 	*_flags = reply.flags;
564 	return B_OK;
565 }
566 
567 
568 void
569 BBufferProducer::SetInitialLatency(bigtime_t initialLatency, uint32 flags)
570 {
571 	fInitialLatency = initialLatency;
572 	fInitialFlags = flags;
573 }
574 
575 
576 // #pragma mark - private BBufferProducer
577 
578 
579 /*
580 private unimplemented
581 BBufferProducer::BBufferProducer()
582 BBufferProducer::BBufferProducer(const BBufferProducer &clone)
583 BBufferProducer & BBufferProducer::operator=(const BBufferProducer &clone)
584 */
585 
586 status_t BBufferProducer::_Reserved_BufferProducer_0(void*) { return B_ERROR; }
587 status_t BBufferProducer::_Reserved_BufferProducer_1(void*) { return B_ERROR; }
588 status_t BBufferProducer::_Reserved_BufferProducer_2(void*) { return B_ERROR; }
589 status_t BBufferProducer::_Reserved_BufferProducer_3(void*) { return B_ERROR; }
590 status_t BBufferProducer::_Reserved_BufferProducer_4(void*) { return B_ERROR; }
591 status_t BBufferProducer::_Reserved_BufferProducer_5(void*) { return B_ERROR; }
592 status_t BBufferProducer::_Reserved_BufferProducer_6(void*) { return B_ERROR; }
593 status_t BBufferProducer::_Reserved_BufferProducer_7(void*) { return B_ERROR; }
594 status_t BBufferProducer::_Reserved_BufferProducer_8(void*) { return B_ERROR; }
595 status_t BBufferProducer::_Reserved_BufferProducer_9(void*) { return B_ERROR; }
596 status_t BBufferProducer::_Reserved_BufferProducer_10(void*) { return B_ERROR; }
597 status_t BBufferProducer::_Reserved_BufferProducer_11(void*) { return B_ERROR; }
598 status_t BBufferProducer::_Reserved_BufferProducer_12(void*) { return B_ERROR; }
599 status_t BBufferProducer::_Reserved_BufferProducer_13(void*) { return B_ERROR; }
600 status_t BBufferProducer::_Reserved_BufferProducer_14(void*) { return B_ERROR; }
601 status_t BBufferProducer::_Reserved_BufferProducer_15(void*) { return B_ERROR; }
602 
603 
604 //! Deprecated.
605 status_t
606 BBufferProducer::SendBuffer(BBuffer* buffer,
607 	const media_destination& destination)
608 {
609 	CALLED();
610 
611 	// Try to find the source - this is the best we can do
612 	media_output output;
613 	int32 cookie = 0;
614 	status_t status = GetNextOutput(&cookie, &output);
615 	if (status != B_OK)
616 		return status;
617 
618 	return SendBuffer(buffer, output.source, destination);
619 }
620 
621 
622 status_t
623 BBufferProducer::clip_shorts_to_region(const int16* data, int count,
624 	BRegion* output)
625 {
626 	UNIMPLEMENTED();
627 	return B_ERROR;
628 }
629 
630 
631 status_t
632 BBufferProducer::clip_region_to_shorts(const BRegion* input, int16* data,
633 	int maxCount, int* _count)
634 {
635 	UNIMPLEMENTED();
636 	return B_ERROR;
637 }
638