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
67ESDSinkNode::~ESDSinkNode(void)
68{
69	CALLED();
70	fAddOn->GetConfigurationFor(this, NULL);
71
72	BMediaEventLooper::Quit();
73
74	fWeb = NULL;
75	delete fDevice;
76}
77
78ESDSinkNode::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
148status_t ESDSinkNode::InitCheck(void) const
149{
150	CALLED();
151	return fInitCheckStatus;
152}
153
154
155// -------------------------------------------------------- //
156// implementation of BMediaNode
157// -------------------------------------------------------- //
158
159BMediaAddOn * 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
173void ESDSinkNode::Preroll(void)
174{
175	CALLED();
176	// XXX:Performance opportunity
177	BMediaNode::Preroll();
178}
179
180status_t ESDSinkNode::HandleMessage(
181				int32 message,
182				const void * data,
183				size_t size)
184{
185	CALLED();
186	return B_ERROR;
187}
188
189void 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
240status_t ESDSinkNode::RequestCompleted(const media_request_info &info)
241{
242	CALLED();
243	return B_OK;
244}
245
246void 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.
257status_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
313status_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
328void ESDSinkNode::DisposeInputCookie(
329				int32 cookie)
330{
331	CALLED();
332	// nothing to do since our cookies are just integers
333}
334
335void 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
379void 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
396status_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
425status_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
469void 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. */
493status_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. */
516status_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
533status_t
534ESDSinkNode::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
558status_t
559ESDSinkNode::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
585status_t
586ESDSinkNode::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
594status_t
595ESDSinkNode::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
608status_t
609ESDSinkNode::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
616status_t
617ESDSinkNode::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
657status_t
658ESDSinkNode::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
711void
712ESDSinkNode::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
770void
771ESDSinkNode::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
799void
800ESDSinkNode::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
845void
846ESDSinkNode::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
862void
863ESDSinkNode::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
875void 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?
915status_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
957status_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
977status_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
989status_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
999status_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
1008status_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
1021status_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
1035void
1036ESDSinkNode::SetRunMode(run_mode mode)
1037{
1038	CALLED();
1039	PRINT(("ESDSinkNode::SetRunMode mode:%i\n", mode));
1040	//BTimeSource::SetRunMode(mode);
1041}
1042
1043status_t
1044ESDSinkNode::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
1090status_t
1091ESDSinkNode::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
1131return EINVAL;
1132}
1133
1134void
1135ESDSinkNode::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
1188BParameterWeb*
1189ESDSinkNode::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
1209status_t
1210ESDSinkNode::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
1254void 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
1288void 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