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