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