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