xref: /haiku/src/kits/media/BufferProducer.cpp (revision 04a0e9c7b68cbe3a43d38e2bca8e860fd80936fb)
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 			status_t status = SetBufferGroup(command->source, group);
274 			if (command->destination == media_destination::null)
275 				return B_OK;
276 			replycommand.info.what
277 				= media_request_info::B_SET_OUTPUT_BUFFERS_FOR;
278 			replycommand.info.change_tag = command->change_tag;
279 			replycommand.info.status = status;
280 			replycommand.info.cookie = group;
281 			replycommand.info.user_data = command->user_data;
282 			replycommand.info.source = command->source;
283 			replycommand.info.destination = command->destination;
284 			SendToPort(command->destination.port, NODE_REQUEST_COMPLETED,
285 				&replycommand, sizeof(replycommand));
286 			return B_OK;
287 		}
288 
289 		case PRODUCER_FORMAT_CHANGE_REQUESTED:
290 		{
291 			const producer_format_change_requested_command* command
292 				= static_cast<
293 					const producer_format_change_requested_command*>(data);
294 			node_request_completed_command replycommand;
295 			replycommand.info.format = command->format;
296 			status_t status = FormatChangeRequested(command->source,
297 				command->destination, &replycommand.info.format, NULL);
298 			if (command->destination == media_destination::null)
299 				return B_OK;
300 			replycommand.info.what
301 				= media_request_info::B_REQUEST_FORMAT_CHANGE;
302 			replycommand.info.change_tag = command->change_tag;
303 			replycommand.info.status = status;
304 			//replycommand.info.cookie
305 			replycommand.info.user_data = command->user_data;
306 			replycommand.info.source = command->source;
307 			replycommand.info.destination = command->destination;
308 			SendToPort(command->destination.port, NODE_REQUEST_COMPLETED,
309 				&replycommand, sizeof(replycommand));
310 			return B_OK;
311 		}
312 
313 		case PRODUCER_VIDEO_CLIPPING_CHANGED:
314 		{
315 			const producer_video_clipping_changed_command* command
316 				= static_cast<
317 					const producer_video_clipping_changed_command*>(data);
318 			node_request_completed_command replycommand;
319 			status_t status = VideoClippingChanged(command->source,
320 				command->short_count, (int16 *)command->shorts,
321 				command->display, NULL);
322 			if (command->destination == media_destination::null)
323 				return B_OK;
324 			replycommand.info.what
325 				= media_request_info::B_SET_VIDEO_CLIPPING_FOR;
326 			replycommand.info.change_tag = command->change_tag;
327 			replycommand.info.status = status;
328 			//replycommand.info.cookie
329 			replycommand.info.user_data = command->user_data;
330 			replycommand.info.source = command->source;
331 			replycommand.info.destination = command->destination;
332 			replycommand.info.format.type = B_MEDIA_RAW_VIDEO;
333 			replycommand.info.format.u.raw_video.display = command->display;
334 			SendToPort(command->destination.port, NODE_REQUEST_COMPLETED,
335 				&replycommand, sizeof(replycommand));
336 			return B_OK;
337 		}
338 
339 		case PRODUCER_ADDITIONAL_BUFFER_REQUESTED:
340 		{
341 			const producer_additional_buffer_requested_command* command
342 				= static_cast<
343 					const producer_additional_buffer_requested_command*>(data);
344 			AdditionalBufferRequested(command->source, command->prev_buffer,
345 				command->prev_time, command->has_seek_tag
346 					? &command->prev_tag : NULL);
347 			return B_OK;
348 		}
349 
350 		case PRODUCER_LATENCY_CHANGED:
351 		{
352 			const producer_latency_changed_command* command
353 				= static_cast<const producer_latency_changed_command*>(data);
354 			LatencyChanged(command->source, command->destination,
355 				command->latency, command->flags);
356 			return B_OK;
357 		}
358 
359 		case PRODUCER_LATE_NOTICE_RECEIVED:
360 		{
361 			const producer_late_notice_received_command* command
362 				= static_cast<
363 					const producer_late_notice_received_command*>(data);
364 			LateNoticeReceived(command->source, command->how_much,
365 				command->performance_time);
366 			return B_OK;
367 		}
368 
369 		case PRODUCER_ENABLE_OUTPUT:
370 		{
371 			const producer_enable_output_command* command
372 				= static_cast<const producer_enable_output_command*>(data);
373 			node_request_completed_command replycommand;
374 			EnableOutput(command->source, command->enabled, NULL);
375 			if (command->destination == media_destination::null)
376 				return B_OK;
377 
378 			replycommand.info.what = media_request_info::B_SET_OUTPUT_ENABLED;
379 			replycommand.info.change_tag = command->change_tag;
380 			replycommand.info.status = B_OK;
381 			//replycommand.info.cookie
382 			replycommand.info.user_data = command->user_data;
383 			replycommand.info.source = command->source;
384 			replycommand.info.destination = command->destination;
385 			//replycommand.info.format
386 			SendToPort(command->destination.port, NODE_REQUEST_COMPLETED,
387 				&replycommand, sizeof(replycommand));
388 			return B_OK;
389 		}
390 	}
391 
392 	return B_ERROR;
393 }
394 
395 
396 void
397 BBufferProducer::AdditionalBufferRequested(const media_source& source,
398 	media_buffer_id previousBuffer, bigtime_t previousTime,
399 	const media_seek_tag* previousTag)
400 {
401 	CALLED();
402 	// may be implemented by derived classes
403 }
404 
405 
406 void
407 BBufferProducer::LatencyChanged(const media_source& source,
408 	const media_destination& destination, bigtime_t newLatency, uint32 flags)
409 {
410 	CALLED();
411 	// may be implemented by derived classes
412 }
413 
414 
415 status_t
416 BBufferProducer::SendBuffer(BBuffer* buffer, const media_source& source,
417 	const media_destination& destination)
418 {
419 	CALLED();
420 	if (destination == media_destination::null)
421 		return B_MEDIA_BAD_DESTINATION;
422 	if (source == media_source::null)
423 		return B_MEDIA_BAD_SOURCE;
424 	if (buffer == NULL)
425 		return B_BAD_VALUE;
426 
427 	consumer_buffer_received_command command;
428 	command.buffer = buffer->ID();
429 	command.header = *buffer->Header();
430 	command.header.buffer = command.buffer;
431 	command.header.source_port = source.port;
432 	command.header.source = source.id;
433 	command.header.destination = destination.id;
434 	command.header.owner = 0; // XXX fill with "buffer owner info area"
435 	command.header.start_time += fDelay;
436 		// time compensation as set by BMediaRoster::SetProducerRunModeDelay()
437 
438 	//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);
439 
440 	return SendToPort(destination.port, CONSUMER_BUFFER_RECEIVED, &command,
441 		sizeof(command));
442 }
443 
444 
445 status_t
446 BBufferProducer::SendDataStatus(int32 status,
447 	const media_destination& destination, bigtime_t atTime)
448 {
449 	CALLED();
450 	if (IS_INVALID_DESTINATION(destination))
451 		return B_MEDIA_BAD_DESTINATION;
452 
453 	consumer_producer_data_status_command command;
454 	command.for_whom = destination;
455 	command.status = status;
456 	command.at_performance_time = atTime;
457 
458 	return SendToPort(destination.port, CONSUMER_PRODUCER_DATA_STATUS, &command,
459 		sizeof(command));
460 }
461 
462 
463 status_t
464 BBufferProducer::ProposeFormatChange(media_format* format,
465 	const media_destination& destination)
466 {
467 	CALLED();
468 	if (IS_INVALID_DESTINATION(destination))
469 		return B_MEDIA_BAD_DESTINATION;
470 
471 	consumer_accept_format_request request;
472 	consumer_accept_format_reply reply;
473 
474 	request.dest = destination;
475 	request.format = *format;
476 	status_t status = QueryPort(destination.port, CONSUMER_ACCEPT_FORMAT,
477 		&request, sizeof(request), &reply, sizeof(reply));
478 	if (status != B_OK)
479 		return status;
480 
481 	*format = reply.format;
482 	return B_OK;
483 }
484 
485 
486 status_t
487 BBufferProducer::ChangeFormat(const media_source& source,
488 	const media_destination& destination, media_format* format)
489 {
490 	CALLED();
491 	if (IS_INVALID_SOURCE(source))
492 		return B_MEDIA_BAD_SOURCE;
493 	if (IS_INVALID_DESTINATION(destination))
494 		return B_MEDIA_BAD_DESTINATION;
495 
496 	consumer_format_changed_request request;
497 	consumer_format_changed_reply reply;
498 
499 	request.producer = source;
500 	request.consumer = destination;
501 	request.format = *format;
502 
503 	// we use a request/reply to make this synchronous
504 	return QueryPort(destination.port, CONSUMER_FORMAT_CHANGED, &request,
505 		sizeof(request), &reply, sizeof(reply));
506 }
507 
508 
509 status_t
510 BBufferProducer::FindLatencyFor(const media_destination& destination,
511 	bigtime_t* _latency, media_node_id* _timesource)
512 {
513 	CALLED();
514 	if (IS_INVALID_DESTINATION(destination))
515 		return B_MEDIA_BAD_DESTINATION;
516 
517 	consumer_get_latency_for_request request;
518 	consumer_get_latency_for_reply reply;
519 
520 	request.for_whom = destination;
521 
522 	status_t status = QueryPort(destination.port, CONSUMER_GET_LATENCY_FOR,
523 		&request, sizeof(request), &reply, sizeof(reply));
524 	if (status != B_OK)
525 		return status;
526 
527 	*_latency = reply.latency;
528 	*_timesource = reply.timesource;
529 	return B_OK;
530 }
531 
532 
533 status_t
534 BBufferProducer::FindSeekTag(const media_destination& destination,
535 	bigtime_t targetTime, media_seek_tag* _tag, bigtime_t* _tagged_time,
536 	uint32* _flags, uint32 flags)
537 {
538 	CALLED();
539 	if (IS_INVALID_DESTINATION(destination))
540 		return B_MEDIA_BAD_DESTINATION;
541 
542 	consumer_seek_tag_requested_request request;
543 	consumer_seek_tag_requested_reply reply;
544 
545 	request.destination = destination;
546 	request.target_time = targetTime;
547 	request.flags = flags;
548 
549 	status_t status = QueryPort(destination.port, CONSUMER_SEEK_TAG_REQUESTED,
550 		&request, sizeof(request), &reply, sizeof(reply));
551 	if (status != B_OK)
552 		return status;
553 
554 	*_tag = reply.seek_tag;
555 	*_tagged_time = reply.tagged_time;
556 	*_flags = reply.flags;
557 	return B_OK;
558 }
559 
560 
561 void
562 BBufferProducer::SetInitialLatency(bigtime_t initialLatency, uint32 flags)
563 {
564 	fInitialLatency = initialLatency;
565 	fInitialFlags = flags;
566 }
567 
568 
569 // #pragma mark - private BBufferProducer
570 
571 
572 /*
573 private unimplemented
574 BBufferProducer::BBufferProducer()
575 BBufferProducer::BBufferProducer(const BBufferProducer &clone)
576 BBufferProducer & BBufferProducer::operator=(const BBufferProducer &clone)
577 */
578 
579 status_t BBufferProducer::_Reserved_BufferProducer_0(void*) { return B_ERROR; }
580 status_t BBufferProducer::_Reserved_BufferProducer_1(void*) { return B_ERROR; }
581 status_t BBufferProducer::_Reserved_BufferProducer_2(void*) { return B_ERROR; }
582 status_t BBufferProducer::_Reserved_BufferProducer_3(void*) { return B_ERROR; }
583 status_t BBufferProducer::_Reserved_BufferProducer_4(void*) { return B_ERROR; }
584 status_t BBufferProducer::_Reserved_BufferProducer_5(void*) { return B_ERROR; }
585 status_t BBufferProducer::_Reserved_BufferProducer_6(void*) { return B_ERROR; }
586 status_t BBufferProducer::_Reserved_BufferProducer_7(void*) { return B_ERROR; }
587 status_t BBufferProducer::_Reserved_BufferProducer_8(void*) { return B_ERROR; }
588 status_t BBufferProducer::_Reserved_BufferProducer_9(void*) { return B_ERROR; }
589 status_t BBufferProducer::_Reserved_BufferProducer_10(void*) { return B_ERROR; }
590 status_t BBufferProducer::_Reserved_BufferProducer_11(void*) { return B_ERROR; }
591 status_t BBufferProducer::_Reserved_BufferProducer_12(void*) { return B_ERROR; }
592 status_t BBufferProducer::_Reserved_BufferProducer_13(void*) { return B_ERROR; }
593 status_t BBufferProducer::_Reserved_BufferProducer_14(void*) { return B_ERROR; }
594 status_t BBufferProducer::_Reserved_BufferProducer_15(void*) { return B_ERROR; }
595 
596 
597 //! Deprecated.
598 status_t
599 BBufferProducer::SendBuffer(BBuffer* buffer,
600 	const media_destination& destination)
601 {
602 	CALLED();
603 
604 	// Try to find the source - this is the best we can do
605 	media_output output;
606 	int32 cookie = 0;
607 	status_t status = GetNextOutput(&cookie, &output);
608 	if (status != B_OK)
609 		return status;
610 
611 	return SendBuffer(buffer, output.source, destination);
612 }
613 
614 
615 status_t
616 BBufferProducer::clip_shorts_to_region(const int16* data, int count,
617 	BRegion* output)
618 {
619 	UNIMPLEMENTED();
620 	return B_ERROR;
621 }
622 
623 
624 status_t
625 BBufferProducer::clip_region_to_shorts(const BRegion* input, int16* data,
626 	int maxCount, int* _count)
627 {
628 	UNIMPLEMENTED();
629 	return B_ERROR;
630 }
631