xref: /haiku/src/add-ons/media/media-add-ons/esound_sink/ESDSinkNode.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
1 /*
2  * ESounD media addon for BeOS
3  *
4  * Copyright (c) 2006 François Revol (revol@free.fr)
5  *
6  * Based on Multi Audio addon for Haiku,
7  * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr)
8  *
9  * All rights reserved.
10  * Redistribution and use in source and binary forms, with or without modification,
11  * are permitted provided that the following conditions are met:
12  *
13  * - Redistributions of source code must retain the above copyright notice,
14  *   this list of conditions and the following disclaimer.
15  * - Redistributions in binary form must reproduce the above copyright notice,
16  *   this list of conditions and the following disclaimer in the documentation
17  *   and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  */
31 //#define DEBUG 4
32 #include <MediaDefs.h>
33 #include <MediaNode.h>
34 #include <MediaAddOn.h>
35 #include <BufferConsumer.h>
36 #include <FileInterface.h>
37 #include <Controllable.h>
38 #include <MediaEventLooper.h>
39 #include <File.h>
40 #include <Errors.h>
41 #include <Entry.h>
42 #include <BufferGroup.h>
43 #include <TimeSource.h>
44 #include <Buffer.h>
45 #include <ParameterWeb.h>
46 #include <MediaRoster.h>
47 #include <limits.h>
48 #include <MediaDefs.h>
49 #include <Message.h>
50 
51 #include "ESDSinkNode.h"
52 #include "ESDEndpoint.h"
53 #ifdef DEBUG
54   #define PRINTING
55 #endif
56 #include "debug.h"
57 #include <Debug.h>
58 
59 #include <stdio.h>
60 #include <string.h>
61 
62 
63 // -------------------------------------------------------- //
64 // ctor/dtor
65 // -------------------------------------------------------- //
66 
67 ESDSinkNode::~ESDSinkNode(void)
68 {
69 	CALLED();
70 	fAddOn->GetConfigurationFor(this, NULL);
71 
72 	BMediaEventLooper::Quit();
73 
74 	fWeb = NULL;
75 	delete fDevice;
76 }
77 
78 ESDSinkNode::ESDSinkNode(BMediaAddOn *addon, char* name, BMessage * config)
79 	: BMediaNode(name),
80 	  BBufferConsumer(B_MEDIA_RAW_AUDIO),
81 #if ENABLE_INPUT
82 	  BBufferProducer(B_MEDIA_RAW_AUDIO),
83 #endif
84 #ifdef ENABLE_TS
85 	  BTimeSource(),
86 #endif
87 	  BMediaEventLooper(),
88 	  fThread(-1),
89 	  fDevice(NULL),
90 	  fTimeSourceStarted(false),
91 	  fWeb(NULL),
92 	  fConfig(*config)
93 {
94 	CALLED();
95 	fInitCheckStatus = B_NO_INIT;
96 
97 	fAddOn = addon;
98 	fId = 0;
99 
100 	AddNodeKind( B_PHYSICAL_OUTPUT );
101 #if ENABLE_INPUT
102 	AddNodeKind( B_PHYSICAL_INPUT );
103 #endif
104 
105 	// initialize our preferred format object
106 	memset(&fPreferredFormat, 0, sizeof(fPreferredFormat)); // set everything to wildcard first
107 	fPreferredFormat.type = B_MEDIA_RAW_AUDIO;
108 #if ESD_FMT == 8
109 	fPreferredFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_UCHAR;
110 #else
111 	fPreferredFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT;
112 #endif
113 	fPreferredFormat.u.raw_audio.valid_bits = 0;
114 	fPreferredFormat.u.raw_audio.channel_count = 2;
115 	fPreferredFormat.u.raw_audio.frame_rate = ESD_DEFAULT_RATE;
116 	fPreferredFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
117 
118 	// we'll use the consumer's preferred buffer size, if any
119 	fPreferredFormat.u.raw_audio.buffer_size = ESD_MAX_BUF / 4
120 /*						* (fPreferredFormat.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
121 						* fPreferredFormat.u.raw_audio.channel_count*/;
122 
123 	if(config) {
124 		//PRINT_OBJECT(*config);
125 		config->FindString("hostname", &fHostname);
126 	}
127 	if (fHostname.Length() < 1)
128 		fHostname = "172.20.109.151";//"192.168.0.2";
129 	fPort = ESD_DEFAULT_PORT;
130 	fEnabled = false;
131 
132 	fDevice = new ESDEndpoint();
133 	/*
134 	if (fDevice) {
135 		if (fDevice->Connect(fHostname.String()) >= 0) {
136 			fDevice->SetCommand();
137 			fDevice->SetFormat(ESD_FMT, 2);
138 			//fDevice->GetServerInfo();
139 			fInitCheckStatus = fDevice->SendDefaultCommand();
140 		}
141 	}
142 	*/
143 	if (!fDevice)
144 		return;
145 	fInitCheckStatus = B_OK;
146 }
147 
148 status_t ESDSinkNode::InitCheck(void) const
149 {
150 	CALLED();
151 	return fInitCheckStatus;
152 }
153 
154 
155 // -------------------------------------------------------- //
156 // implementation of BMediaNode
157 // -------------------------------------------------------- //
158 
159 BMediaAddOn * ESDSinkNode::AddOn(
160 				int32 * internal_id) const
161 {
162 	CALLED();
163 	// BeBook says this only gets called if we were in an add-on.
164 	if (fAddOn != 0) {
165 		// If we get a null pointer then we just won't write.
166 		if (internal_id != 0) {
167 			*internal_id = fId;
168 		}
169 	}
170 	return fAddOn;
171 }
172 
173 void ESDSinkNode::Preroll(void)
174 {
175 	CALLED();
176 	// XXX:Performance opportunity
177 	BMediaNode::Preroll();
178 }
179 
180 status_t ESDSinkNode::HandleMessage(
181 				int32 message,
182 				const void * data,
183 				size_t size)
184 {
185 	CALLED();
186 	return B_ERROR;
187 }
188 
189 void ESDSinkNode::NodeRegistered(void)
190 {
191 	CALLED();
192 
193 	if (fInitCheckStatus != B_OK) {
194 		ReportError(B_NODE_IN_DISTRESS);
195 		return;
196 	}
197 
198 	SetPriority(B_REAL_TIME_PRIORITY);
199 
200 	Run();
201 
202 //	media_input *input = new media_input;
203 
204 	fInput.format = fPreferredFormat;
205 	fInput.destination.port = ControlPort();
206 	fInput.destination.id = 0;
207 	fInput.node = Node();
208 	sprintf(fInput.name, "output %ld", fInput.destination.id);
209 
210 	fOutput.format = fPreferredFormat;
211 	fOutput.destination = media_destination::null;
212 	fOutput.source.port = ControlPort();
213 	fOutput.source.id = 0;
214 	fOutput.node = Node();
215 	sprintf(fOutput.name, "input %ld", fOutput.source.id);
216 
217 	// Set up our parameter web
218 	fWeb = MakeParameterWeb();
219 	SetParameterWeb(fWeb);
220 
221 	/* apply configuration */
222 #ifdef PRINTING
223 	bigtime_t start = system_time();
224 #endif
225 
226 	int32 index = 0;
227 	int32 parameterID = 0;
228 	const void *data;
229 	ssize_t size;
230 	while(fConfig.FindInt32("parameterID", index, &parameterID) == B_OK) {
231 		if(fConfig.FindData("parameterData", B_RAW_TYPE, index, &data, &size) == B_OK)
232 			SetParameterValue(parameterID, TimeSource()->Now(), data, size);
233 		index++;
234 	}
235 
236 #ifdef PRINTING
237 	PRINT(("apply configuration in : %lld\n", system_time() - start));
238 #endif
239 }
240 
241 status_t ESDSinkNode::RequestCompleted(const media_request_info &info)
242 {
243 	CALLED();
244 	return B_OK;
245 }
246 
247 void ESDSinkNode::SetTimeSource(BTimeSource *timeSource)
248 {
249 	CALLED();
250 }
251 
252 // -------------------------------------------------------- //
253 // implemention of BBufferConsumer
254 // -------------------------------------------------------- //
255 
256 // Check to make sure the format is okay, then remove
257 // any wildcards corresponding to our requirements.
258 status_t ESDSinkNode::AcceptFormat(
259 				const media_destination & dest,
260 				media_format * format)
261 {
262 	status_t err;
263 	CALLED();
264 
265 	if(fInput.destination != dest) {
266 		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION");
267 		return B_MEDIA_BAD_DESTINATION; // we only have one input so that better be it
268 	}
269 
270 /*	media_format * myFormat = GetFormat();
271 	fprintf(stderr,"proposed format: ");
272 	print_media_format(format);
273 	fprintf(stderr,"\n");
274 	fprintf(stderr,"my format: ");
275 	print_media_format(myFormat);
276 	fprintf(stderr,"\n");*/
277 	// Be's format_is_compatible doesn't work.
278 //	if (!format_is_compatible(*format,*myFormat)) {
279 
280 	if ( format->type != B_MEDIA_RAW_AUDIO ) {
281 		fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n");
282 		return B_MEDIA_BAD_FORMAT;
283 	}
284 
285 	/*if(format->u.raw_audio.format == media_raw_audio_format::B_AUDIO_FLOAT
286 		&& channel->fPreferredFormat.u.raw_audio.format == media_raw_audio_format::B_AUDIO_SHORT)
287 		format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
288 	else*/
289 	format->u.raw_audio.format = fPreferredFormat.u.raw_audio.format;
290 	format->u.raw_audio.valid_bits = fPreferredFormat.u.raw_audio.valid_bits;
291 
292 	format->u.raw_audio.frame_rate    = fPreferredFormat.u.raw_audio.frame_rate;
293 	format->u.raw_audio.channel_count = fPreferredFormat.u.raw_audio.channel_count;
294 	format->u.raw_audio.byte_order    = B_MEDIA_HOST_ENDIAN;
295 	format->u.raw_audio.buffer_size   = ESD_MAX_BUF / 4
296 /*						* (format->u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
297 						* format->u.raw_audio.channel_count*/;
298 
299 
300 	/*media_format myFormat;
301 	GetFormat(&myFormat);
302 	if (!format_is_acceptible(*format,myFormat)) {
303 		fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n");
304 		return B_MEDIA_BAD_FORMAT;
305 	}*/
306 	//AddRequirements(format);
307 
308 	// start connecting here
309 	err = fDevice->Connect(fHostname.String(), fPort);
310 
311 	return B_OK;
312 }
313 
314 status_t ESDSinkNode::GetNextInput(
315 				int32 * cookie,
316 				media_input * out_input)
317 {
318 	CALLED();
319 
320 	if ((*cookie < 1) && (*cookie >= 0)) {
321 		*out_input = fInput;
322 		*cookie += 1;
323 		PRINT(("input.format : %lu\n", fInput.format.u.raw_audio.format));
324 		return B_OK;
325 	} else
326 		return B_BAD_INDEX;
327 }
328 
329 void ESDSinkNode::DisposeInputCookie(
330 				int32 cookie)
331 {
332 	CALLED();
333 	// nothing to do since our cookies are just integers
334 }
335 
336 void ESDSinkNode::BufferReceived(
337 				BBuffer * buffer)
338 {
339 	CALLED();
340 	switch (buffer->Header()->type) {
341 		/*case B_MEDIA_PARAMETERS:
342 			{
343 			status_t status = ApplyParameterData(buffer->Data(),buffer->SizeUsed());
344 			if (status != B_OK) {
345 				fprintf(stderr,"ApplyParameterData in ESDSinkNode::BufferReceived failed\n");
346 			}
347 			buffer->Recycle();
348 			}
349 			break;*/
350 		case B_MEDIA_RAW_AUDIO:
351 #if 0
352 			if (buffer->Flags() & BBuffer::B_SMALL_BUFFER) {
353 				fprintf(stderr,"NOT IMPLEMENTED: B_SMALL_BUFFER in ESDSinkNode::BufferReceived\n");
354 				// XXX: implement this part
355 				buffer->Recycle();
356 			} else {
357 				media_timed_event event(buffer->Header()->start_time, BTimedEventQueue::B_HANDLE_BUFFER,
358 										buffer, BTimedEventQueue::B_RECYCLE_BUFFER);
359 				status_t status = EventQueue()->AddEvent(event);
360 				if (status != B_OK) {
361 					fprintf(stderr,"EventQueue()->AddEvent(event) in ESDSinkNode::BufferReceived failed\n");
362 					buffer->Recycle();
363 				}
364 			}
365 #endif
366 			if (fDevice->CanSend()) {
367 
368 				fDevice->Write(buffer->Data(), buffer->SizeUsed());
369 
370 			}
371 			buffer->Recycle();
372 			break;
373 		default:
374 			fprintf(stderr,"unexpected buffer type in ESDSinkNode::BufferReceived\n");
375 			buffer->Recycle();
376 			break;
377 	}
378 }
379 
380 void ESDSinkNode::ProducerDataStatus(
381 				const media_destination & for_whom,
382 				int32 status,
383 				bigtime_t at_performance_time)
384 {
385 	CALLED();
386 
387 	if(fInput.destination != for_whom) {
388 		fprintf(stderr,"invalid destination received in ESDSinkNode::ProducerDataStatus\n");
389 		return;
390 	}
391 
392 	media_timed_event event(at_performance_time, BTimedEventQueue::B_DATA_STATUS,
393 			&fInput, BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL);
394 	EventQueue()->AddEvent(event);
395 }
396 
397 status_t ESDSinkNode::GetLatencyFor(
398 				const media_destination & for_whom,
399 				bigtime_t * out_latency,
400 				media_node_id * out_timesource)
401 {
402 	CALLED();
403 	if ((out_latency == 0) || (out_timesource == 0)) {
404 		fprintf(stderr,"<- B_BAD_VALUE\n");
405 		return B_BAD_VALUE;
406 	}
407 
408 	if(fInput.destination != for_whom) {
409 		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
410 		return B_MEDIA_BAD_DESTINATION;
411 	}
412 
413 	bigtime_t intl = EventLatency();
414 	bigtime_t netl = 0LL;
415 	if (fDevice)
416 		netl = fDevice->Latency();
417 	// I don't want to swap
418 	if (netl > 500000)
419 		netl = 500000;
420 	*out_latency = intl + netl;
421 	fprintf(stderr, "int latency %Ld, net latency %Ld, total latency %Ld\n", intl, netl, *out_latency);
422 	*out_timesource = TimeSource()->ID();
423 	return B_OK;
424 }
425 
426 status_t ESDSinkNode::Connected(
427 				const media_source & producer,	/* here's a good place to request buffer group usage */
428 				const media_destination & where,
429 				const media_format & with_format,
430 				media_input * out_input)
431 {
432 	status_t err;
433 	CALLED();
434 
435 	if(fInput.destination != where) {
436 		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
437 		return B_MEDIA_BAD_DESTINATION;
438 	}
439 
440 	//
441 	if (fDevice) {
442 		err = fDevice->WaitForConnect();
443 		if (err < B_OK)
444 			return err;
445 		fDevice->SetCommand();
446 		//fDevice->GetServerInfo();
447 		fDevice->SetFormat(ESD_FMT, 2);
448 		err = fDevice->SendDefaultCommand();
449 		if (err < B_OK)
450 			return err;
451 	}
452 	// use one buffer length latency
453 	fInternalLatency = with_format.u.raw_audio.buffer_size * 10000 / 2
454 			/ ( (with_format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
455 				* with_format.u.raw_audio.channel_count)
456 			/ ((int32)(with_format.u.raw_audio.frame_rate / 100));
457 
458 	PRINT(("  internal latency = %lld\n",fInternalLatency));
459 
460 	SetEventLatency(fInternalLatency);
461 
462 	// record the agreed upon values
463 	fInput.source = producer;
464 	fInput.format = with_format;
465 	*out_input = fInput;
466 
467 	return B_OK;
468 }
469 
470 void ESDSinkNode::Disconnected(
471 				const media_source & producer,
472 				const media_destination & where)
473 {
474 	CALLED();
475 
476 	if(fInput.destination != where) {
477 		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
478 		return;
479 	}
480 	if (fInput.source != producer) {
481 		fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n");
482 		return;
483 	}
484 
485 	fInput.source = media_source::null;
486 	fInput.format = fPreferredFormat;
487 	//GetFormat(&channel->fInput.format);
488 	if (fDevice)
489 		fDevice->Disconnect();
490 }
491 
492 	/* The notification comes from the upstream producer, so he's already cool with */
493 	/* the format; you should not ask him about it in here. */
494 status_t ESDSinkNode::FormatChanged(
495 				const media_source & producer,
496 				const media_destination & consumer,
497 				int32 change_tag,
498 				const media_format & format)
499 {
500 	CALLED();
501 
502 	if(fInput.destination != consumer) {
503 		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
504 		return B_MEDIA_BAD_DESTINATION;
505 	}
506 	if (fInput.source != producer) {
507 		return B_MEDIA_BAD_SOURCE;
508 	}
509 
510 	return B_ERROR;
511 }
512 
513 	/* Given a performance time of some previous buffer, retrieve the remembered tag */
514 	/* of the closest (previous or exact) performance time. Set *out_flags to 0; the */
515 	/* idea being that flags can be added later, and the understood flags returned in */
516 	/* *out_flags. */
517 status_t ESDSinkNode::SeekTagRequested(
518 				const media_destination & destination,
519 				bigtime_t in_target_time,
520 				uint32 in_flags,
521 				media_seek_tag * out_seek_tag,
522 				bigtime_t * out_tagged_time,
523 				uint32 * out_flags)
524 {
525 	CALLED();
526 	return BBufferConsumer::SeekTagRequested(destination,in_target_time,in_flags,
527 											out_seek_tag,out_tagged_time,out_flags);
528 }
529 
530 // -------------------------------------------------------- //
531 // implementation for BBufferProducer
532 // -------------------------------------------------------- //
533 #if 0
534 status_t
535 ESDSinkNode::FormatSuggestionRequested(media_type type, int32 /*quality*/, media_format* format)
536 {
537 	// FormatSuggestionRequested() is not necessarily part of the format negotiation
538 	// process; it's simply an interrogation -- the caller wants to see what the node's
539 	// preferred data format is, given a suggestion by the caller.
540 	CALLED();
541 
542 	if (!format)
543 	{
544 		fprintf(stderr, "\tERROR - NULL format pointer passed in!\n");
545 		return B_BAD_VALUE;
546 	}
547 
548 	// this is the format we'll be returning (our preferred format)
549 	*format = fPreferredFormat;
550 
551 	// a wildcard type is okay; we can specialize it
552 	if (type == B_MEDIA_UNKNOWN_TYPE) type = B_MEDIA_RAW_AUDIO;
553 
554 	// we only support raw audio
555 	if (type != B_MEDIA_RAW_AUDIO) return B_MEDIA_BAD_FORMAT;
556 	else return B_OK;
557 }
558 
559 status_t
560 ESDSinkNode::FormatProposal(const media_source& output, media_format* format)
561 {
562 	// FormatProposal() is the first stage in the BMediaRoster::Connect() process.  We hand
563 	// out a suggested format, with wildcards for any variations we support.
564 	CALLED();
565 	node_output *channel = FindOutput(output);
566 
567 	// is this a proposal for our select output?
568 	if (channel == NULL)
569 	{
570 		fprintf(stderr, "ESDSinkNode::FormatProposal returning B_MEDIA_BAD_SOURCE\n");
571 		return B_MEDIA_BAD_SOURCE;
572 	}
573 
574 	// we only support floating-point raw audio, so we always return that, but we
575 	// supply an error code depending on whether we found the proposal acceptable.
576 	media_type requestedType = format->type;
577 	*format = channel->fPreferredFormat;
578 	if ((requestedType != B_MEDIA_UNKNOWN_TYPE) && (requestedType != B_MEDIA_RAW_AUDIO))
579 	{
580 		fprintf(stderr, "ESDSinkNode::FormatProposal returning B_MEDIA_BAD_FORMAT\n");
581 		return B_MEDIA_BAD_FORMAT;
582 	}
583 	else return B_OK;		// raw audio or wildcard type, either is okay by us
584 }
585 
586 status_t
587 ESDSinkNode::FormatChangeRequested(const media_source& source, const media_destination& destination, media_format* io_format, int32* _deprecated_)
588 {
589 	CALLED();
590 
591 	// we don't support any other formats, so we just reject any format changes.
592 	return B_ERROR;
593 }
594 
595 status_t
596 ESDSinkNode::GetNextOutput(int32* cookie, media_output* out_output)
597 {
598 	CALLED();
599 
600 	if ((*cookie < fOutputs.CountItems()) && (*cookie >= 0)) {
601 		node_output *channel = (node_output *)fOutputs.ItemAt(*cookie);
602 		*out_output = channel->fOutput;
603 		*cookie += 1;
604 		return B_OK;
605 	} else
606 		return B_BAD_INDEX;
607 }
608 
609 status_t
610 ESDSinkNode::DisposeOutputCookie(int32 cookie)
611 {
612 	CALLED();
613 	// do nothing because we don't use the cookie for anything special
614 	return B_OK;
615 }
616 
617 status_t
618 ESDSinkNode::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup)
619 {
620 	CALLED();
621 
622 	node_output *channel = FindOutput(for_source);
623 
624 	// is this our output?
625 	if (channel == NULL)
626 	{
627 		fprintf(stderr, "ESDSinkNode::SetBufferGroup returning B_MEDIA_BAD_SOURCE\n");
628 		return B_MEDIA_BAD_SOURCE;
629 	}
630 
631 	// Are we being passed the buffer group we're already using?
632 	if (newGroup == channel->fBufferGroup) return B_OK;
633 
634 	// Ahh, someone wants us to use a different buffer group.  At this point we delete
635 	// the one we are using and use the specified one instead.  If the specified group is
636 	// NULL, we need to recreate one ourselves, and use *that*.  Note that if we're
637 	// caching a BBuffer that we requested earlier, we have to Recycle() that buffer
638 	// *before* deleting the buffer group, otherwise we'll deadlock waiting for that
639 	// buffer to be recycled!
640 	delete channel->fBufferGroup;		// waits for all buffers to recycle
641 	if (newGroup != NULL)
642 	{
643 		// we were given a valid group; just use that one from now on
644 		channel->fBufferGroup = newGroup;
645 	}
646 	else
647 	{
648 		// we were passed a NULL group pointer; that means we construct
649 		// our own buffer group to use from now on
650 		size_t size = channel->fOutput.format.u.raw_audio.buffer_size;
651 		int32 count = int32(fLatency / BufferDuration() + 1 + 1);
652 		channel->fBufferGroup = new BBufferGroup(size, count);
653 	}
654 
655 	return B_OK;
656 }
657 
658 status_t
659 ESDSinkNode::PrepareToConnect(const media_source& what, const media_destination& where, media_format* format, media_source* out_source, char* out_name)
660 {
661 	// PrepareToConnect() is the second stage of format negotiations that happens
662 	// inside BMediaRoster::Connect().  At this point, the consumer's AcceptFormat()
663 	// method has been called, and that node has potentially changed the proposed
664 	// format.  It may also have left wildcards in the format.  PrepareToConnect()
665 	// *must* fully specialize the format before returning!
666 	CALLED();
667 
668 	node_output *channel = FindOutput(what);
669 
670 	// is this our output?
671 	if (channel == NULL)
672 	{
673 		fprintf(stderr, "ESDSinkNode::PrepareToConnect returning B_MEDIA_BAD_SOURCE\n");
674 		return B_MEDIA_BAD_SOURCE;
675 	}
676 
677 	// are we already connected?
678 	if (channel->fOutput.destination != media_destination::null)
679 		return B_MEDIA_ALREADY_CONNECTED;
680 
681 	// the format may not yet be fully specialized (the consumer might have
682 	// passed back some wildcards).  Finish specializing it now, and return an
683 	// error if we don't support the requested format.
684 	if (format->type != B_MEDIA_RAW_AUDIO)
685 	{
686 		fprintf(stderr, "\tnon-raw-audio format?!\n");
687 		return B_MEDIA_BAD_FORMAT;
688 	}
689 
690 	 // !!! validate all other fields except for buffer_size here, because the consumer might have
691 	// supplied different values from AcceptFormat()?
692 
693 	// check the buffer size, which may still be wildcarded
694 	if (format->u.raw_audio.buffer_size == media_raw_audio_format::wildcard.buffer_size)
695 	{
696 		format->u.raw_audio.buffer_size = 2048;		// pick something comfortable to suggest
697 		fprintf(stderr, "\tno buffer size provided, suggesting %lu\n", format->u.raw_audio.buffer_size);
698 	}
699 	else
700 	{
701 		fprintf(stderr, "\tconsumer suggested buffer_size %lu\n", format->u.raw_audio.buffer_size);
702 	}
703 
704 	// Now reserve the connection, and return information about it
705 	channel->fOutput.destination = where;
706 	channel->fOutput.format = *format;
707 	*out_source = channel->fOutput.source;
708 	strncpy(out_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH);
709 	return B_OK;
710 }
711 
712 void
713 ESDSinkNode::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name)
714 {
715 	CALLED();
716 
717 	node_output *channel = FindOutput(source);
718 
719 	// is this our output?
720 	if (channel == NULL)
721 	{
722 		fprintf(stderr, "ESDSinkNode::Connect returning (cause : B_MEDIA_BAD_SOURCE)\n");
723 		return;
724 	}
725 
726 	// If something earlier failed, Connect() might still be called, but with a non-zero
727 	// error code.  When that happens we simply unreserve the connection and do
728 	// nothing else.
729 	if (error)
730 	{
731 		channel->fOutput.destination = media_destination::null;
732 		channel->fOutput.format = channel->fPreferredFormat;
733 		return;
734 	}
735 
736 	// Okay, the connection has been confirmed.  Record the destination and format
737 	// that we agreed on, and report our connection name again.
738 	channel->fOutput.destination = destination;
739 	channel->fOutput.format = format;
740 	strncpy(io_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH);
741 
742 	// reset our buffer duration, etc. to avoid later calculations
743 	bigtime_t duration = channel->fOutput.format.u.raw_audio.buffer_size * 10000
744 			/ ( (channel->fOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
745 				* channel->fOutput.format.u.raw_audio.channel_count)
746 			/ ((int32)(channel->fOutput.format.u.raw_audio.frame_rate / 100));
747 
748 	SetBufferDuration(duration);
749 
750 	// Now that we're connected, we can determine our downstream latency.
751 	// Do so, then make sure we get our events early enough.
752 	media_node_id id;
753 	FindLatencyFor(channel->fOutput.destination, &fLatency, &id);
754 	PRINT(("\tdownstream latency = %Ld\n", fLatency));
755 
756 	fInternalLatency = BufferDuration();
757 	PRINT(("\tbuffer-filling took %Ld usec on this machine\n", fInternalLatency));
758 	//SetEventLatency(fLatency + fInternalLatency);
759 
760 	// Set up the buffer group for our connection, as long as nobody handed us a
761 	// buffer group (via SetBufferGroup()) prior to this.  That can happen, for example,
762 	// if the consumer calls SetOutputBuffersFor() on us from within its Connected()
763 	// method.
764 	if (!channel->fBufferGroup)
765 		AllocateBuffers(*channel);
766 
767 	// we are sure the thread is started
768 	StartThread();
769 }
770 
771 void
772 ESDSinkNode::Disconnect(const media_source& what, const media_destination& where)
773 {
774 	CALLED();
775 
776 	node_output *channel = FindOutput(what);
777 
778 	// is this our output?
779 	if (channel == NULL)
780 	{
781 		fprintf(stderr, "ESDSinkNode::Disconnect() returning (cause : B_MEDIA_BAD_SOURCE)\n");
782 		return;
783 	}
784 
785 	// Make sure that our connection is the one being disconnected
786 	if ((where == channel->fOutput.destination) && (what == channel->fOutput.source))
787 	{
788 		channel->fOutput.destination = media_destination::null;
789 		channel->fOutput.format = channel->fPreferredFormat;
790 		delete channel->fBufferGroup;
791 		channel->fBufferGroup = NULL;
792 	}
793 	else
794 	{
795 		fprintf(stderr, "\tDisconnect() called with wrong source/destination (%ld/%ld), ours is (%ld/%ld)\n",
796 			what.id, where.id, channel->fOutput.source.id, channel->fOutput.destination.id);
797 	}
798 }
799 
800 void
801 ESDSinkNode::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time)
802 {
803 	CALLED();
804 
805 	node_output *channel = FindOutput(what);
806 
807 	// is this our output?
808 	if (channel == NULL)
809 	{
810 		return;
811 	}
812 
813 	// If we're late, we need to catch up.  Respond in a manner appropriate to our
814 	// current run mode.
815 	if (RunMode() == B_RECORDING)
816 	{
817 		// A hardware capture node can't adjust; it simply emits buffers at
818 		// appropriate points.  We (partially) simulate this by not adjusting
819 		// our behavior upon receiving late notices -- after all, the hardware
820 		// can't choose to capture "sooner"....
821 	}
822 	else if (RunMode() == B_INCREASE_LATENCY)
823 	{
824 		// We're late, and our run mode dictates that we try to produce buffers
825 		// earlier in order to catch up.  This argues that the downstream nodes are
826 		// not properly reporting their latency, but there's not much we can do about
827 		// that at the moment, so we try to start producing buffers earlier to
828 		// compensate.
829 		fInternalLatency += how_much;
830 		SetEventLatency(fLatency + fInternalLatency);
831 
832 		fprintf(stderr, "\tincreasing latency to %Ld\n", fLatency + fInternalLatency);
833 	}
834 	else
835 	{
836 		// The other run modes dictate various strategies for sacrificing data quality
837 		// in the interests of timely data delivery.  The way *we* do this is to skip
838 		// a buffer, which catches us up in time by one buffer duration.
839 		/*size_t nSamples = fOutput.format.u.raw_audio.buffer_size / sizeof(float);
840 		mSamplesSent += nSamples;*/
841 
842 		fprintf(stderr, "\tskipping a buffer to try to catch up\n");
843 	}
844 }
845 
846 void
847 ESDSinkNode::EnableOutput(const media_source& what, bool enabled, int32* _deprecated_)
848 {
849 	CALLED();
850 
851 	// If I had more than one output, I'd have to walk my list of output records to see
852 	// which one matched the given source, and then enable/disable that one.  But this
853 	// node only has one output, so I just make sure the given source matches, then set
854 	// the enable state accordingly.
855 	node_output *channel = FindOutput(what);
856 
857 	if (channel != NULL)
858 	{
859 		channel->fOutputEnabled = enabled;
860 	}
861 }
862 
863 void
864 ESDSinkNode::AdditionalBufferRequested(const media_source& source, media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag* prev_tag)
865 {
866 	CALLED();
867 	// we don't support offline mode
868 	return;
869 }
870 #endif
871 
872 // -------------------------------------------------------- //
873 // implementation for BMediaEventLooper
874 // -------------------------------------------------------- //
875 
876 void ESDSinkNode::HandleEvent(
877 				const media_timed_event *event,
878 				bigtime_t lateness,
879 				bool realTimeEvent)
880 {
881 	CALLED();
882 	switch (event->type) {
883 		case BTimedEventQueue::B_START:
884 			HandleStart(event,lateness,realTimeEvent);
885 			break;
886 		case BTimedEventQueue::B_SEEK:
887 			HandleSeek(event,lateness,realTimeEvent);
888 			break;
889 		case BTimedEventQueue::B_WARP:
890 			HandleWarp(event,lateness,realTimeEvent);
891 			break;
892 		case BTimedEventQueue::B_STOP:
893 			HandleStop(event,lateness,realTimeEvent);
894 			break;
895 		case BTimedEventQueue::B_HANDLE_BUFFER:
896 			if (RunState() == BMediaEventLooper::B_STARTED) {
897 				HandleBuffer(event,lateness,realTimeEvent);
898 			}
899 			break;
900 		case BTimedEventQueue::B_DATA_STATUS:
901 			HandleDataStatus(event,lateness,realTimeEvent);
902 			break;
903 		case BTimedEventQueue::B_PARAMETER:
904 			HandleParameter(event,lateness,realTimeEvent);
905 			break;
906 		default:
907 			fprintf(stderr,"  unknown event type: %li\n",event->type);
908 			break;
909 	}
910 }
911 
912 // protected:
913 
914 // how should we handle late buffers?  drop them?
915 // notify the producer?
916 status_t ESDSinkNode::HandleBuffer(
917 				const media_timed_event *event,
918 				bigtime_t lateness,
919 				bool realTimeEvent)
920 {
921 	CALLED();
922 	BBuffer * buffer = const_cast<BBuffer*>((BBuffer*)event->pointer);
923 	if (buffer == 0) {
924 		fprintf(stderr,"<- B_BAD_VALUE\n");
925 		return B_BAD_VALUE;
926 	}
927 
928 	if(fInput.destination.id != buffer->Header()->destination) {
929 		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
930 		return B_MEDIA_BAD_DESTINATION;
931 	}
932 
933 	media_header* hdr = buffer->Header();
934 	bigtime_t now = TimeSource()->Now();
935 	bigtime_t perf_time = hdr->start_time;
936 
937 	// the how_early calculate here doesn't include scheduling latency because
938 	// we've already been scheduled to handle the buffer
939 	bigtime_t how_early = perf_time - EventLatency() - now;
940 
941 	// if the buffer is late, we ignore it and report the fact to the producer
942 	// who sent it to us
943 	if ((RunMode() != B_OFFLINE) &&				// lateness doesn't matter in offline mode...
944 		(RunMode() != B_RECORDING) &&		// ...or in recording mode
945 		(how_early < 0LL))
946 	{
947 		//mLateBuffers++;
948 		NotifyLateProducer(fInput.source, -how_early, perf_time);
949 		fprintf(stderr,"	<- LATE BUFFER : %lli\n", how_early);
950 		buffer->Recycle();
951 	} else {
952 		if (fDevice->CanSend())
953 			fDevice->Write(buffer->Data(), buffer->SizeUsed());
954 	}
955 	return B_OK;
956 }
957 
958 status_t ESDSinkNode::HandleDataStatus(
959 						const media_timed_event *event,
960 						bigtime_t lateness,
961 						bool realTimeEvent)
962 {
963 	CALLED();
964 	PRINT(("ESDSinkNode::HandleDataStatus status:%li, lateness:%lli\n", event->data, lateness));
965 	switch(event->data) {
966 		case B_DATA_NOT_AVAILABLE:
967 			break;
968 		case B_DATA_AVAILABLE:
969 			break;
970 		case B_PRODUCER_STOPPED:
971 			break;
972 		default:
973 			break;
974 	}
975 	return B_OK;
976 }
977 
978 status_t ESDSinkNode::HandleStart(
979 						const media_timed_event *event,
980 						bigtime_t lateness,
981 						bool realTimeEvent)
982 {
983 	CALLED();
984 	if (RunState() != B_STARTED) {
985 
986 	}
987 	return B_OK;
988 }
989 
990 status_t ESDSinkNode::HandleSeek(
991 						const media_timed_event *event,
992 						bigtime_t lateness,
993 						bool realTimeEvent)
994 {
995 	CALLED();
996 	PRINT(("ESDSinkNode::HandleSeek(t=%lld,d=%li,bd=%lld)\n",event->event_time,event->data,event->bigdata));
997 	return B_OK;
998 }
999 
1000 status_t ESDSinkNode::HandleWarp(
1001 						const media_timed_event *event,
1002 						bigtime_t lateness,
1003 						bool realTimeEvent)
1004 {
1005 	CALLED();
1006 	return B_OK;
1007 }
1008 
1009 status_t ESDSinkNode::HandleStop(
1010 						const media_timed_event *event,
1011 						bigtime_t lateness,
1012 						bool realTimeEvent)
1013 {
1014 	CALLED();
1015 	// flush the queue so downstreamers don't get any more
1016 	EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
1017 
1018 	//StopThread();
1019 	return B_OK;
1020 }
1021 
1022 status_t ESDSinkNode::HandleParameter(
1023 				const media_timed_event *event,
1024 				bigtime_t lateness,
1025 				bool realTimeEvent)
1026 {
1027 	CALLED();
1028 	return B_OK;
1029 }
1030 
1031 // -------------------------------------------------------- //
1032 // implemention of BTimeSource
1033 // -------------------------------------------------------- //
1034 #ifdef ENABLE_TS
1035 
1036 void
1037 ESDSinkNode::SetRunMode(run_mode mode)
1038 {
1039 	CALLED();
1040 	PRINT(("ESDSinkNode::SetRunMode mode:%i\n", mode));
1041 	//BTimeSource::SetRunMode(mode);
1042 }
1043 
1044 status_t
1045 ESDSinkNode::TimeSourceOp(const time_source_op_info &op, void *_reserved)
1046 {
1047 	CALLED();
1048 	switch(op.op) {
1049 		case B_TIMESOURCE_START:
1050 			PRINT(("TimeSourceOp op B_TIMESOURCE_START\n"));
1051 			if (RunState() != BMediaEventLooper::B_STARTED) {
1052 				fTimeSourceStarted = true;
1053 
1054 				media_timed_event startEvent(0, BTimedEventQueue::B_START);
1055 				EventQueue()->AddEvent(startEvent);
1056 			}
1057 			break;
1058 		case B_TIMESOURCE_STOP:
1059 			PRINT(("TimeSourceOp op B_TIMESOURCE_STOP\n"));
1060 			if (RunState() == BMediaEventLooper::B_STARTED) {
1061 				media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
1062 				EventQueue()->AddEvent(stopEvent);
1063 				fTimeSourceStarted = false;
1064 				PublishTime(0, 0, 0);
1065 			}
1066 			break;
1067 		case B_TIMESOURCE_STOP_IMMEDIATELY:
1068 			PRINT(("TimeSourceOp op B_TIMESOURCE_STOP_IMMEDIATELY\n"));
1069 			if (RunState() == BMediaEventLooper::B_STARTED) {
1070 				media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
1071 				EventQueue()->AddEvent(stopEvent);
1072 				fTimeSourceStarted = false;
1073 				PublishTime(0, 0, 0);
1074 			}
1075 			break;
1076 		case B_TIMESOURCE_SEEK:
1077 			PRINT(("TimeSourceOp op B_TIMESOURCE_SEEK\n"));
1078 			BroadcastTimeWarp(op.real_time, op.performance_time);
1079 			break;
1080 		default:
1081 			break;
1082 	}
1083 	return B_OK;
1084 }
1085 #endif
1086 
1087 // -------------------------------------------------------- //
1088 // implemention of BControllable
1089 // -------------------------------------------------------- //
1090 
1091 status_t
1092 ESDSinkNode::GetParameterValue(int32 id, bigtime_t* last_change, void* value, size_t* ioSize)
1093 {
1094 	CALLED();
1095 	if (!fDevice)
1096 		return B_ERROR;
1097 	//PRINT(("id : %i\n", id));
1098 	switch (id) {
1099 		case PARAM_ENABLED:
1100 			if (*ioSize < sizeof(bool))
1101 				return B_NO_MEMORY;
1102 			*(bool *)value = fEnabled;
1103 			*ioSize = sizeof(bool);
1104 			return B_OK;
1105 		case PARAM_HOST:
1106 		{
1107 			BString s = fDevice->Host();
1108 			*ioSize = MIN(*ioSize, s.Length());
1109 			memcpy(value, s.String(), *ioSize);
1110 			return B_OK;
1111 		}
1112 		case PARAM_PORT:
1113 		{
1114 			BString s;
1115 			s << fDevice->Port();
1116 			*ioSize = MIN(*ioSize, s.Length());
1117 			memcpy(value, s.String(), *ioSize);
1118 			return B_OK;
1119 		}
1120 		default:
1121 			break;
1122 	}
1123 #if 0
1124 	BParameter *parameter = NULL;
1125 	for(int32 i=0; i<fWeb->CountParameters(); i++) {
1126 		parameter = fWeb->ParameterAt(i);
1127 		if(parameter->ID() == id)
1128 			break;
1129 	}
1130 #endif
1131 
1132 return EINVAL;
1133 }
1134 
1135 void
1136 ESDSinkNode::SetParameterValue(int32 id, bigtime_t performance_time, const void* value, size_t size)
1137 {
1138 	CALLED();
1139 	PRINT(("id : %li, performance_time : %lld, size : %li\n", id, performance_time, size));
1140 	BParameter *parameter = NULL;
1141 	for(int32 i=0; i<fWeb->CountParameters(); i++) {
1142 		parameter = fWeb->ParameterAt(i);
1143 		if(parameter->ID() == id)
1144 			break;
1145 	}
1146 	switch (id) {
1147 		case PARAM_ENABLED:
1148 			if (size != sizeof(bool))
1149 				return;
1150 			fEnabled = *(bool *)value;
1151 			return;
1152 		case PARAM_HOST:
1153 		{
1154 			fprintf(stderr, "set HOST: %s\n", (const char *)value);
1155 			fHostname = (const char *)value;
1156 #if 0
1157 			if (fDevice && fDevice->Connected()) {
1158 				if (fDevice->Connect(fHostname.String(), fPort) >= 0) {
1159 					fDevice->SetCommand();
1160 					fDevice->SetFormat(ESD_FMT, 2);
1161 					//fDevice->GetServerInfo();
1162 					fInitCheckStatus = fDevice->SendDefaultCommand();
1163 				}
1164 			}
1165 #endif
1166 			return;
1167 		}
1168 		case PARAM_PORT:
1169 		{
1170 			fprintf(stderr, "set PORT: %s\n", (const char *)value);
1171 			fPort = atoi((const char *)value);
1172 #if 0
1173 			if (fDevice && fDevice->Connected()) {
1174 				if (fDevice->Connect(fHostname.String(), fPort) >= 0) {
1175 					fDevice->SetCommand();
1176 					fDevice->SetFormat(ESD_FMT, 2);
1177 					//fDevice->GetServerInfo();
1178 					fInitCheckStatus = fDevice->SendDefaultCommand();
1179 				}
1180 			}
1181 #endif
1182 			return;
1183 		}
1184 		default:
1185 			break;
1186 	}
1187 }
1188 
1189 BParameterWeb*
1190 ESDSinkNode::MakeParameterWeb()
1191 {
1192 	CALLED();
1193 	BParameterWeb* web = new BParameterWeb;
1194 	BParameterGroup *group = web->MakeGroup("Server");
1195 	BParameter *p;
1196 	// XXX: use B_MEDIA_UNKNOWN_TYPE or _NO_TYPE ?
1197 	// keep in sync with enum { PARAM_* } !
1198 	p = group->MakeDiscreteParameter(PARAM_ENABLED, B_MEDIA_RAW_AUDIO, "Enable", B_ENABLE);
1199 #if defined(B_BEOS_VERSION_DANO) || defined(__HAIKU__)
1200 	p = group->MakeTextParameter(PARAM_HOST, B_MEDIA_RAW_AUDIO, "Hostname", B_GENERIC, 128);
1201 	p = group->MakeTextParameter(PARAM_PORT, B_MEDIA_RAW_AUDIO, "Port", B_GENERIC, 16);
1202 #endif
1203 	return web;
1204 }
1205 
1206 // -------------------------------------------------------- //
1207 // ESDSinkNode specific functions
1208 // -------------------------------------------------------- //
1209 
1210 status_t
1211 ESDSinkNode::GetConfigurationFor(BMessage * into_message)
1212 {
1213 	CALLED();
1214 
1215 	BParameter *parameter = NULL;
1216 	void *buffer;
1217 	size_t size = 128;
1218 	bigtime_t last_change;
1219 	status_t err;
1220 
1221 	if (!into_message)
1222 		return B_BAD_VALUE;
1223 
1224 	buffer = malloc(size);
1225 
1226 	for(int32 i=0; i<fWeb->CountParameters(); i++) {
1227 		parameter = fWeb->ParameterAt(i);
1228 		if(parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER
1229 			&& parameter->Type() != BParameter::B_DISCRETE_PARAMETER)
1230 			continue;
1231 
1232 		PRINT(("getting parameter %li\n", parameter->ID()));
1233 		size = 128;
1234 		while((err = GetParameterValue(parameter->ID(), &last_change, buffer, &size))==B_NO_MEMORY) {
1235 			size += 128;
1236 			free(buffer);
1237 			buffer = malloc(size);
1238 		}
1239 
1240 		if(err == B_OK && size > 0) {
1241 			into_message->AddInt32("parameterID", parameter->ID());
1242 			into_message->AddData("parameterData", B_RAW_TYPE, buffer, size, false);
1243 		} else {
1244 			PRINT(("parameter %li err : %s\n", parameter->ID(), strerror(err)));
1245 		}
1246 	}
1247 
1248 	//PRINT_OBJECT(*into_message);
1249 
1250 	return B_OK;
1251 }
1252 
1253 // static:
1254 
1255 void ESDSinkNode::GetFlavor(flavor_info * outInfo, int32 id)
1256 {
1257 	CALLED();
1258 
1259 	outInfo->flavor_flags = B_FLAVOR_IS_GLOBAL;
1260 //	outInfo->possible_count = 0;	// any number
1261 	outInfo->possible_count = 1;	// only 1
1262 	outInfo->in_format_count = 0; // no inputs
1263 	outInfo->in_formats = 0;
1264 	outInfo->out_format_count = 0; // no outputs
1265 	outInfo->out_formats = 0;
1266 	outInfo->internal_id = id;
1267 
1268 	outInfo->name = new char[256];
1269 		strcpy(outInfo->name, "ESounD Out");
1270 	outInfo->info = new char[256];
1271 		strcpy(outInfo->info, "The ESounD Sink node outputs a network Enlightenment Sound Daemon.");
1272 	outInfo->kinds = /*B_TIME_SOURCE | *//*B_CONTROLLABLE | */ 0;
1273 
1274 #if ENABLE_INPUT
1275 	outInfo->kinds |= B_BUFFER_PRODUCER | B_PHYSICAL_INPUT;
1276 	outInfo->out_format_count = 1; // 1 output
1277 	media_format * outformats = new media_format[outInfo->out_format_count];
1278 	GetFormat(&outformats[0]);
1279 	outInfo->out_formats = outformats;
1280 #endif
1281 
1282 	outInfo->kinds |= B_BUFFER_CONSUMER | B_PHYSICAL_OUTPUT;
1283 	outInfo->in_format_count = 1; // 1 input
1284 	media_format * informats = new media_format[outInfo->in_format_count];
1285 	GetFormat(&informats[0]);
1286 	outInfo->in_formats = informats;
1287 }
1288 
1289 void ESDSinkNode::GetFormat(media_format * outFormat)
1290 {
1291 	CALLED();
1292 
1293 	outFormat->type = B_MEDIA_RAW_AUDIO;
1294 	outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
1295 	outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
1296 	outFormat->u.raw_audio = media_raw_audio_format::wildcard;
1297 }
1298