xref: /haiku/src/kits/media/BufferConsumer.cpp (revision 97901ec593ec4dd50ac115c1c35a6d72f6e489a5)
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 "BufferCache.h"
31 #include <BufferConsumer.h>
32 
33 #include <stdlib.h>
34 #include <string.h>
35 
36 #include <BufferProducer.h>
37 #include <BufferGroup.h>
38 #include <Buffer.h>
39 #include <TimeSource.h>
40 
41 #include <debug.h>
42 #include <MediaMisc.h>
43 #include <DataExchange.h>
44 
45 
46 
47 BBufferConsumer::~BBufferConsumer()
48 {
49 	CALLED();
50 	delete fBufferCache;
51 	delete fDeleteBufferGroup;
52 }
53 
54 
55 // #pragma mark - public BBufferConsumer
56 
57 
58 media_type
59 BBufferConsumer::ConsumerType()
60 {
61 	CALLED();
62 	return fConsumerType;
63 }
64 
65 
66 /*static*/ status_t
67 BBufferConsumer::RegionToClipData(const BRegion* region, int32* _format,
68 	int32 *_size, void* data)
69 {
70 	CALLED();
71 
72 	int count = *_size / sizeof(int16);
73 	status_t status = BBufferProducer::clip_region_to_shorts(region,
74 		static_cast<int16 *>(data), count, &count);
75 
76 	*_size = count * sizeof(int16);
77 	*_format = BBufferProducer::B_CLIP_SHORT_RUNS;
78 
79 	return status;
80 }
81 
82 
83 // #pragma mark - protected BBufferConsumer
84 
85 
86 BBufferConsumer::BBufferConsumer(media_type consumerType)
87 	:
88 	BMediaNode("called by BBufferConsumer"),
89 	fConsumerType(consumerType),
90 	fBufferCache(new BPrivate::BufferCache),
91 	fDeleteBufferGroup(0)
92 {
93 	CALLED();
94 
95 	AddNodeKind(B_BUFFER_CONSUMER);
96 }
97 
98 
99 /*static*/ void
100 BBufferConsumer::NotifyLateProducer(const media_source& whatSource,
101 	bigtime_t howMuch, bigtime_t performanceTime)
102 {
103 	CALLED();
104 	if (IS_INVALID_SOURCE(whatSource))
105 		return;
106 
107 	producer_late_notice_received_command command;
108 	command.source = whatSource;
109 	command.how_much = howMuch;
110 	command.performance_time = performanceTime;
111 
112 	SendToPort(whatSource.port, PRODUCER_LATE_NOTICE_RECEIVED, &command,
113 		sizeof(command));
114 }
115 
116 
117 status_t
118 BBufferConsumer::SetVideoClippingFor(const media_source& output,
119 	const media_destination& destination, const int16* shorts, int32 shortCount,
120 	const media_video_display_info& display, void* userData, int32* _changeTag,
121 	void *_reserved_)
122 {
123 	CALLED();
124 	if (IS_INVALID_SOURCE(output))
125 		return B_MEDIA_BAD_SOURCE;
126 	if (IS_INVALID_DESTINATION(destination))
127 		return B_MEDIA_BAD_DESTINATION;
128 	if (shortCount > int(B_MEDIA_MESSAGE_SIZE
129 			- sizeof(producer_video_clipping_changed_command)) / 2) {
130 		debugger("BBufferConsumer::SetVideoClippingFor short_count too large "
131 			"(8000 limit)\n");
132 	}
133 
134 	producer_video_clipping_changed_command* command;
135 	size_t size = sizeof(producer_video_clipping_changed_command)
136 		+ shortCount * sizeof(short);
137 	command
138 		= static_cast<producer_video_clipping_changed_command*>(malloc(size));
139 	if (command == NULL)
140 		return B_NO_MEMORY;
141 
142 	command->source = output;
143 	command->destination = destination;
144 	command->display = display;
145 	command->user_data = userData;
146 	command->change_tag = NewChangeTag();
147 	command->short_count = shortCount;
148 	memcpy(command->shorts, shorts, shortCount * sizeof(short));
149 	if (_changeTag != NULL)
150 		*_changeTag = command->change_tag;
151 
152 	status_t status = SendToPort(output.port, PRODUCER_VIDEO_CLIPPING_CHANGED,
153 		command, size);
154 
155 	free(command);
156 	return status;
157 }
158 
159 
160 status_t
161 BBufferConsumer::SetOutputEnabled(const media_source &source,
162 								  const media_destination &destination,
163 								  bool enabled,
164 								  void *user_data,
165 								  int32 *change_tag,
166 								  void *_reserved_)
167 {
168 	CALLED();
169 	if (IS_INVALID_SOURCE(source))
170 		return B_MEDIA_BAD_SOURCE;
171 	if (IS_INVALID_DESTINATION(destination))
172 		return B_MEDIA_BAD_DESTINATION;
173 
174 	producer_enable_output_command command;
175 
176 	command.source = source;
177 	command.destination = destination;
178 	command.enabled = enabled;
179 	command.user_data = user_data;
180 	command.change_tag = NewChangeTag();
181 	if (change_tag != NULL)
182 		*change_tag = command.change_tag;
183 
184 	return SendToPort(source.port, PRODUCER_ENABLE_OUTPUT, &command, sizeof(command));
185 }
186 
187 
188 status_t
189 BBufferConsumer::RequestFormatChange(const media_source &source,
190 									 const media_destination &destination,
191 									 const media_format &to_format,
192 									 void *user_data,
193 									 int32 *change_tag,
194 									 void *_reserved_)
195 {
196 	CALLED();
197 	if (IS_INVALID_SOURCE(source))
198 		return B_MEDIA_BAD_SOURCE;
199 	if (IS_INVALID_DESTINATION(destination))
200 		return B_MEDIA_BAD_DESTINATION;
201 
202 	producer_format_change_requested_command command;
203 
204 	command.source = source;
205 	command.destination = destination;
206 	command.format = to_format;
207 	command.user_data = user_data;
208 	command.change_tag = NewChangeTag();
209 	if (change_tag != NULL)
210 		*change_tag = command.change_tag;
211 
212 	return SendToPort(source.port, PRODUCER_FORMAT_CHANGE_REQUESTED, &command, sizeof(command));
213 }
214 
215 
216 status_t
217 BBufferConsumer::RequestAdditionalBuffer(const media_source &source,
218 										 BBuffer *prev_buffer,
219 										 void *_reserved)
220 {
221 	CALLED();
222 	if (IS_INVALID_SOURCE(source))
223 		return B_MEDIA_BAD_SOURCE;
224 
225 	producer_additional_buffer_requested_command command;
226 
227 	command.source = source;
228 	command.prev_buffer = prev_buffer->ID();
229 	command.prev_time = 0;
230 	command.has_seek_tag = false;
231 	//command.prev_tag =
232 
233 	return SendToPort(source.port, PRODUCER_ADDITIONAL_BUFFER_REQUESTED, &command, sizeof(command));
234 }
235 
236 
237 status_t
238 BBufferConsumer::RequestAdditionalBuffer(const media_source& source,
239 	bigtime_t startTime, void *_reserved)
240 {
241 	CALLED();
242 	if (IS_INVALID_SOURCE(source))
243 		return B_MEDIA_BAD_SOURCE;
244 
245 	producer_additional_buffer_requested_command command;
246 
247 	command.source = source;
248 	command.prev_buffer = 0;
249 	command.prev_time = startTime;
250 	command.has_seek_tag = false;
251 
252 	return SendToPort(source.port, PRODUCER_ADDITIONAL_BUFFER_REQUESTED,
253 		&command, sizeof(command));
254 }
255 
256 
257 status_t
258 BBufferConsumer::SetOutputBuffersFor(const media_source &source,
259 	const media_destination &destination, BBufferGroup *group, void *user_data,
260 	int32 *change_tag, bool will_reclaim, void *_reserved_)
261 {
262 	CALLED();
263 
264 	if (IS_INVALID_SOURCE(source))
265 		return B_MEDIA_BAD_SOURCE;
266 	if (IS_INVALID_DESTINATION(destination))
267 		return B_MEDIA_BAD_DESTINATION;
268 
269 	producer_set_buffer_group_command *command;
270 	BBuffer **buffers;
271 	int32 buffer_count;
272 	size_t size;
273 	status_t rv;
274 
275 	if (group == 0) {
276 		buffer_count = 0;
277 	} else {
278 		if (B_OK != group->CountBuffers(&buffer_count))
279 			return B_ERROR;
280 	}
281 
282 	if (buffer_count != 0) {
283 		buffers = new BBuffer * [buffer_count];
284 		if (B_OK != group->GetBufferList(buffer_count, buffers)) {
285 			delete [] buffers;
286 			return B_ERROR;
287 		}
288 	} else {
289 		buffers = NULL;
290 	}
291 
292 	size = sizeof(producer_set_buffer_group_command) + buffer_count * sizeof(media_buffer_id);
293 	command = static_cast<producer_set_buffer_group_command *>(malloc(size));
294 	command->source = source;
295 	command->destination = destination;
296 	command->user_data = user_data;
297 	command->change_tag = NewChangeTag();
298 	command->buffer_count = buffer_count;
299 	for (int32 i = 0; i < buffer_count; i++)
300 		command->buffers[i] = buffers[i]->ID();
301 
302 	delete [] buffers;
303 
304 	if (change_tag != NULL)
305 		*change_tag = command->change_tag;
306 
307 	rv = SendToPort(source.port, PRODUCER_SET_BUFFER_GROUP, command, size);
308 	free(command);
309 
310 	if (rv == B_OK) {
311 		// XXX will leak memory if port write failed
312 		delete fDeleteBufferGroup;
313 		fDeleteBufferGroup = will_reclaim ? NULL : group;
314 	}
315 	return rv;
316 }
317 
318 
319 status_t
320 BBufferConsumer::SendLatencyChange(const media_source& source,
321 	const media_destination& destination, bigtime_t newLatency, uint32 flags)
322 {
323 	CALLED();
324 	if (IS_INVALID_SOURCE(source))
325 		return B_MEDIA_BAD_SOURCE;
326 	if (IS_INVALID_DESTINATION(destination))
327 		return B_MEDIA_BAD_DESTINATION;
328 
329 	producer_latency_changed_command command;
330 
331 	command.source = source;
332 	command.destination = destination;
333 	command.latency = newLatency;
334 	command.flags = flags;
335 
336 	TRACE("###### BBufferConsumer::SendLatencyChange: latency from %ld/%ld to "
337 		"%ld/%ld changed to %Ld\n", source.port, source.id, destination.port,
338 		destination.id, newLatency);
339 
340 	return SendToPort(source.port, PRODUCER_LATENCY_CHANGED, &command,
341 		sizeof(command));
342 }
343 
344 
345 status_t
346 BBufferConsumer::HandleMessage(int32 message, const void* data, size_t size)
347 {
348 	PRINT(4, "BBufferConsumer::HandleMessage %#lx, node %ld\n", message, ID());
349 	status_t rv;
350 	switch (message) {
351 		case CONSUMER_ACCEPT_FORMAT:
352 		{
353 			const consumer_accept_format_request* request
354 				= static_cast<const consumer_accept_format_request*>(data);
355 
356 			consumer_accept_format_reply reply;
357 			reply.format = request->format;
358 			status_t status = AcceptFormat(request->dest, &reply.format);
359 			request->SendReply(status, &reply, sizeof(reply));
360 			return B_OK;
361 		}
362 
363 		case CONSUMER_GET_NEXT_INPUT:
364 		{
365 			const consumer_get_next_input_request *request = static_cast<const consumer_get_next_input_request *>(data);
366 			consumer_get_next_input_reply reply;
367 			reply.cookie = request->cookie;
368 			rv = GetNextInput(&reply.cookie, &reply.input);
369 			request->SendReply(rv, &reply, sizeof(reply));
370 			return B_OK;
371 		}
372 
373 		case CONSUMER_DISPOSE_INPUT_COOKIE:
374 		{
375 			const consumer_dispose_input_cookie_request *request = static_cast<const consumer_dispose_input_cookie_request *>(data);
376 			consumer_dispose_input_cookie_reply reply;
377 			DisposeInputCookie(request->cookie);
378 			request->SendReply(B_OK, &reply, sizeof(reply));
379 			return B_OK;
380 		}
381 
382 		case CONSUMER_BUFFER_RECEIVED:
383 		{
384 			const consumer_buffer_received_command* command
385 				= static_cast<const consumer_buffer_received_command*>(data);
386 
387 			BBuffer* buffer = fBufferCache->GetBuffer(command->buffer);
388 			buffer->SetHeader(&command->header);
389 
390 			PRINT(4, "calling BBufferConsumer::BufferReceived buffer %ld at "
391 				"perf %Ld and TimeSource()->Now() is %Ld\n",
392 				buffer->Header()->buffer, buffer->Header()->start_time,
393 				TimeSource()->Now());
394 
395 			BufferReceived(buffer);
396 			return B_OK;
397 		}
398 
399 		case CONSUMER_PRODUCER_DATA_STATUS:
400 		{
401 			const consumer_producer_data_status_command *command = static_cast<const consumer_producer_data_status_command *>(data);
402 			ProducerDataStatus(command->for_whom, command->status, command->at_performance_time);
403 			return B_OK;
404 		}
405 
406 		case CONSUMER_GET_LATENCY_FOR:
407 		{
408 			const consumer_get_latency_for_request *request = static_cast<const consumer_get_latency_for_request *>(data);
409 			consumer_get_latency_for_reply reply;
410 			rv = GetLatencyFor(request->for_whom, &reply.latency, &reply.timesource);
411 			request->SendReply(rv, &reply, sizeof(reply));
412 			return B_OK;
413 		}
414 
415 		case CONSUMER_CONNECTED:
416 		{
417 			const consumer_connected_request *request = static_cast<const consumer_connected_request *>(data);
418 			consumer_connected_reply reply;
419 			reply.input = request->input;
420 			rv = Connected(request->input.source, request->input.destination, request->input.format, &reply.input);
421 			request->SendReply(rv, &reply, sizeof(reply));
422 			return B_OK;
423 		}
424 
425 		case CONSUMER_DISCONNECTED:
426 		{
427 			const consumer_disconnected_request *request = static_cast<const consumer_disconnected_request *>(data);
428 			consumer_disconnected_reply reply;
429 			Disconnected(request->source, request->destination);
430 			request->SendReply(B_OK, &reply, sizeof(reply));
431 			return B_OK;
432 		}
433 
434 		case CONSUMER_FORMAT_CHANGED:
435 		{
436 			const consumer_format_changed_request *request = static_cast<const consumer_format_changed_request *>(data);
437 			consumer_format_changed_reply reply;
438 			rv = FormatChanged(request->producer, request->consumer, request->change_tag, request->format);
439 			request->SendReply(rv, &reply, sizeof(reply));
440 
441 			// XXX is this RequestCompleted() correct?
442 			node_request_completed_command completedcommand;
443 			completedcommand.info.what = media_request_info::B_FORMAT_CHANGED;
444 			completedcommand.info.change_tag = request->change_tag;
445 			completedcommand.info.status = reply.result;
446 			//completedcommand.info.cookie
447 			completedcommand.info.user_data = 0;
448 			completedcommand.info.source = request->producer;
449 			completedcommand.info.destination = request->consumer;
450 			completedcommand.info.format = request->format;
451 			SendToPort(request->consumer.port, NODE_REQUEST_COMPLETED, &completedcommand, sizeof(completedcommand));
452 			return B_OK;
453 		}
454 
455 		case CONSUMER_SEEK_TAG_REQUESTED:
456 		{
457 			const consumer_seek_tag_requested_request *request = static_cast<const consumer_seek_tag_requested_request *>(data);
458 			consumer_seek_tag_requested_reply reply;
459 			rv = SeekTagRequested(request->destination, request->target_time, request->flags, &reply.seek_tag, &reply.tagged_time, &reply.flags);
460 			request->SendReply(rv, &reply, sizeof(reply));
461 			return B_OK;
462 		}
463 	}
464 	return B_ERROR;
465 }
466 
467 status_t
468 BBufferConsumer::SeekTagRequested(const media_destination &destination,
469 								  bigtime_t in_target_time,
470 								  uint32 in_flags,
471 								  media_seek_tag *out_seek_tag,
472 								  bigtime_t *out_tagged_time,
473 								  uint32 *out_flags)
474 {
475 	CALLED();
476 	// may be implemented by derived classes
477 	return B_ERROR;
478 }
479 
480 
481 // #pragma mark - private BBufferConsumer
482 
483 
484 /*
485 not implemented:
486 BBufferConsumer::BBufferConsumer()
487 BBufferConsumer::BBufferConsumer(const BBufferConsumer &clone)
488 BBufferConsumer & BBufferConsumer::operator=(const BBufferConsumer &clone)
489 */
490 
491 
492 /*!	Deprecated function for BeOS R4.
493 */
494 /* static */ status_t
495 BBufferConsumer::SetVideoClippingFor(const media_source &output,
496 									 const int16 *shorts,
497 									 int32 short_count,
498 									 const media_video_display_info &display,
499 									 int32 *change_tag)
500 {
501 	CALLED();
502 	if (IS_INVALID_SOURCE(output))
503 		return B_MEDIA_BAD_SOURCE;
504 	if (short_count > int(B_MEDIA_MESSAGE_SIZE - sizeof(producer_video_clipping_changed_command)) / 2)
505 		debugger("BBufferConsumer::SetVideoClippingFor short_count too large (8000 limit)\n");
506 
507 	producer_video_clipping_changed_command *command;
508 	size_t size;
509 	status_t rv;
510 
511 	size = sizeof(producer_video_clipping_changed_command) + short_count * sizeof(short);
512 	command = static_cast<producer_video_clipping_changed_command *>(malloc(size));
513 	command->source = output;
514 	command->destination = media_destination::null;
515 	command->display = display;
516 	command->user_data = 0;
517 	command->change_tag = NewChangeTag();
518 	command->short_count = short_count;
519 	memcpy(command->shorts, shorts, short_count * sizeof(short));
520 	if (change_tag != NULL)
521 		*change_tag = command->change_tag;
522 
523 	rv = SendToPort(output.port, PRODUCER_VIDEO_CLIPPING_CHANGED, command, size);
524 	free(command);
525 	return rv;
526 }
527 
528 
529 /*!	Deprecated function for BeOS R4.
530 */
531 /*static*/ status_t
532 BBufferConsumer::RequestFormatChange(const media_source& source,
533 	const media_destination& destination, media_format* format,
534 	int32* _changeTag)
535 {
536 	CALLED();
537 	if (IS_INVALID_SOURCE(source))
538 		return B_MEDIA_BAD_SOURCE;
539 	if (IS_INVALID_DESTINATION(destination))
540 		return B_MEDIA_BAD_DESTINATION;
541 
542 	producer_format_change_requested_command command;
543 
544 	command.source = source;
545 	command.destination = destination;
546 	command.format = *format;
547 	command.user_data = 0;
548 	command.change_tag = NewChangeTag();
549 	if (_changeTag != NULL)
550 		*_changeTag = command.change_tag;
551 
552 	return SendToPort(source.port, PRODUCER_FORMAT_CHANGE_REQUESTED, &command,
553 		sizeof(command));
554 }
555 
556 
557 /*!	Deprecated function for BeOS R4.
558 */
559 /*static*/ status_t
560 BBufferConsumer::SetOutputEnabled(const media_source& source, bool enabled,
561 	int32* _changeTag)
562 {
563 	CALLED();
564 	if (IS_INVALID_SOURCE(source))
565 		return B_MEDIA_BAD_SOURCE;
566 
567 	producer_enable_output_command command;
568 
569 	command.source = source;
570 	command.destination = media_destination::null;
571 	command.enabled = enabled;
572 	command.user_data = 0;
573 	command.change_tag = NewChangeTag();
574 	if (_changeTag != NULL)
575 		*_changeTag = command.change_tag;
576 
577 	return SendToPort(source.port, PRODUCER_ENABLE_OUTPUT, &command,
578 		sizeof(command));
579 }
580 
581 
582 status_t BBufferConsumer::_Reserved_BufferConsumer_0(void*) { return B_ERROR; }
583 status_t BBufferConsumer::_Reserved_BufferConsumer_1(void*) { return B_ERROR; }
584 status_t BBufferConsumer::_Reserved_BufferConsumer_2(void*) { return B_ERROR; }
585 status_t BBufferConsumer::_Reserved_BufferConsumer_3(void*) { return B_ERROR; }
586 status_t BBufferConsumer::_Reserved_BufferConsumer_4(void*) { return B_ERROR; }
587 status_t BBufferConsumer::_Reserved_BufferConsumer_5(void*) { return B_ERROR; }
588 status_t BBufferConsumer::_Reserved_BufferConsumer_6(void*) { return B_ERROR; }
589 status_t BBufferConsumer::_Reserved_BufferConsumer_7(void*) { return B_ERROR; }
590 status_t BBufferConsumer::_Reserved_BufferConsumer_8(void*) { return B_ERROR; }
591 status_t BBufferConsumer::_Reserved_BufferConsumer_9(void*) { return B_ERROR; }
592 status_t BBufferConsumer::_Reserved_BufferConsumer_10(void*) { return B_ERROR; }
593 status_t BBufferConsumer::_Reserved_BufferConsumer_11(void*) { return B_ERROR; }
594 status_t BBufferConsumer::_Reserved_BufferConsumer_12(void*) { return B_ERROR; }
595 status_t BBufferConsumer::_Reserved_BufferConsumer_13(void*) { return B_ERROR; }
596 status_t BBufferConsumer::_Reserved_BufferConsumer_14(void*) { return B_ERROR; }
597 status_t BBufferConsumer::_Reserved_BufferConsumer_15(void*) { return B_ERROR; }
598 
599