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