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