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
21BBufferProducer::~BBufferProducer()
22{
23	CALLED();
24}
25
26
27// #pragma mark - public BBufferProducer
28
29
30/*static*/ status_t
31BBufferProducer::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
44media_type
45BBufferProducer::ProducerType()
46{
47	CALLED();
48	return fProducerType;
49}
50
51
52// #pragma mark - protected BBufferProducer
53
54
55BBufferProducer::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
69status_t
70BBufferProducer::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
80status_t
81BBufferProducer::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
122status_t
123BBufferProducer::SetPlayRate(int32 numer, int32 denom)
124{
125	CALLED();
126	// may be implemented by derived classes
127	return B_ERROR;
128}
129
130
131status_t
132BBufferProducer::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
406void
407BBufferProducer::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
416void
417BBufferProducer::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
425status_t
426BBufferProducer::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
455status_t
456BBufferProducer::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
473status_t
474BBufferProducer::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
496status_t
497BBufferProducer::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
519status_t
520BBufferProducer::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
543status_t
544BBufferProducer::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
571void
572BBufferProducer::SetInitialLatency(bigtime_t initialLatency, uint32 flags)
573{
574	fInitialLatency = initialLatency;
575	fInitialFlags = flags;
576}
577
578
579// #pragma mark - private BBufferProducer
580
581
582/*
583private unimplemented
584BBufferProducer::BBufferProducer()
585BBufferProducer::BBufferProducer(const BBufferProducer &clone)
586BBufferProducer & BBufferProducer::operator=(const BBufferProducer &clone)
587*/
588
589status_t BBufferProducer::_Reserved_BufferProducer_0(void*) { return B_ERROR; }
590status_t BBufferProducer::_Reserved_BufferProducer_1(void*) { return B_ERROR; }
591status_t BBufferProducer::_Reserved_BufferProducer_2(void*) { return B_ERROR; }
592status_t BBufferProducer::_Reserved_BufferProducer_3(void*) { return B_ERROR; }
593status_t BBufferProducer::_Reserved_BufferProducer_4(void*) { return B_ERROR; }
594status_t BBufferProducer::_Reserved_BufferProducer_5(void*) { return B_ERROR; }
595status_t BBufferProducer::_Reserved_BufferProducer_6(void*) { return B_ERROR; }
596status_t BBufferProducer::_Reserved_BufferProducer_7(void*) { return B_ERROR; }
597status_t BBufferProducer::_Reserved_BufferProducer_8(void*) { return B_ERROR; }
598status_t BBufferProducer::_Reserved_BufferProducer_9(void*) { return B_ERROR; }
599status_t BBufferProducer::_Reserved_BufferProducer_10(void*) { return B_ERROR; }
600status_t BBufferProducer::_Reserved_BufferProducer_11(void*) { return B_ERROR; }
601status_t BBufferProducer::_Reserved_BufferProducer_12(void*) { return B_ERROR; }
602status_t BBufferProducer::_Reserved_BufferProducer_13(void*) { return B_ERROR; }
603status_t BBufferProducer::_Reserved_BufferProducer_14(void*) { return B_ERROR; }
604status_t BBufferProducer::_Reserved_BufferProducer_15(void*) { return B_ERROR; }
605
606
607//! Deprecated.
608status_t
609BBufferProducer::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
625status_t
626BBufferProducer::clip_shorts_to_region(const int16* data, int count,
627	BRegion* output)
628{
629	UNIMPLEMENTED();
630	return B_ERROR;
631}
632
633
634status_t
635BBufferProducer::clip_region_to_shorts(const BRegion* input, int16* data,
636	int maxCount, int* _count)
637{
638	UNIMPLEMENTED();
639	return B_ERROR;
640}
641