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	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
241status_t ESDSinkNode::RequestCompleted(const media_request_info &info)
242{
243	CALLED();
244	return B_OK;
245}
246
247void 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.
258status_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
314status_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
329void ESDSinkNode::DisposeInputCookie(
330				int32 cookie)
331{
332	CALLED();
333	// nothing to do since our cookies are just integers
334}
335
336void 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
380void 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
397status_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
426status_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
470void 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. */
494status_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. */
517status_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
534status_t
535ESDSinkNode::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
559status_t
560ESDSinkNode::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
586status_t
587ESDSinkNode::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
595status_t
596ESDSinkNode::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
609status_t
610ESDSinkNode::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
617status_t
618ESDSinkNode::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
658status_t
659ESDSinkNode::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
712void
713ESDSinkNode::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
771void
772ESDSinkNode::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
800void
801ESDSinkNode::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
846void
847ESDSinkNode::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
863void
864ESDSinkNode::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
876void 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?
916status_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
958status_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
978status_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
990status_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
1000status_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
1009status_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
1022status_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
1036void
1037ESDSinkNode::SetRunMode(run_mode mode)
1038{
1039	CALLED();
1040	PRINT(("ESDSinkNode::SetRunMode mode:%i\n", mode));
1041	//BTimeSource::SetRunMode(mode);
1042}
1043
1044status_t
1045ESDSinkNode::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
1091status_t
1092ESDSinkNode::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
1132return EINVAL;
1133}
1134
1135void
1136ESDSinkNode::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
1189BParameterWeb*
1190ESDSinkNode::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
1210status_t
1211ESDSinkNode::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
1255void 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
1289void 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