BTRFS: Implement BTree::Path and change _Find.
[haiku.git] / src / add-ons / media / media-add-ons / esound_sink / ESDSinkNode.cpp
blob9cef0a5d8da33af4dcc7eb28f3e0a336d77cf620
1 /*
2 * ESounD media addon for BeOS
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)
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:
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.
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.
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>
51 #include "ESDSinkNode.h"
52 #include "ESDEndpoint.h"
53 #ifdef DEBUG
54 #define PRINTING
55 #endif
56 #include "debug.h"
57 #include <Debug.h>
59 #include <stdio.h>
60 #include <string.h>
63 // -------------------------------------------------------- //
64 // ctor/dtor
65 // -------------------------------------------------------- //
67 ESDSinkNode::~ESDSinkNode(void)
69 CALLED();
70 fAddOn->GetConfigurationFor(this, NULL);
72 BMediaEventLooper::Quit();
74 fWeb = NULL;
75 delete fDevice;
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)
94 CALLED();
95 fInitCheckStatus = B_NO_INIT;
97 fAddOn = addon;
98 fId = 0;
100 AddNodeKind( B_PHYSICAL_OUTPUT );
101 #if ENABLE_INPUT
102 AddNodeKind( B_PHYSICAL_INPUT );
103 #endif
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;
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*/;
123 if(config) {
124 //PRINT_OBJECT(*config);
125 config->FindString("hostname", &fHostname);
127 if (fHostname.Length() < 1)
128 fHostname = "172.20.109.151";//"192.168.0.2";
129 fPort = ESD_DEFAULT_PORT;
130 fEnabled = false;
132 fDevice = new ESDEndpoint();
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();
143 if (!fDevice)
144 return;
145 fInitCheckStatus = B_OK;
148 status_t ESDSinkNode::InitCheck(void) const
150 CALLED();
151 return fInitCheckStatus;
155 // -------------------------------------------------------- //
156 // implementation of BMediaNode
157 // -------------------------------------------------------- //
159 BMediaAddOn * ESDSinkNode::AddOn(
160 int32 * internal_id) const
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;
170 return fAddOn;
173 void ESDSinkNode::Preroll(void)
175 CALLED();
176 // XXX:Performance opportunity
177 BMediaNode::Preroll();
180 status_t ESDSinkNode::HandleMessage(
181 int32 message,
182 const void * data,
183 size_t size)
185 CALLED();
186 return B_ERROR;
189 void ESDSinkNode::NodeRegistered(void)
191 CALLED();
193 if (fInitCheckStatus != B_OK) {
194 ReportError(B_NODE_IN_DISTRESS);
195 return;
198 // media_input *input = new media_input;
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);
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);
213 // Set up our parameter web
214 fWeb = MakeParameterWeb();
215 SetParameterWeb(fWeb);
217 /* apply configuration */
218 #ifdef PRINTING
219 bigtime_t start = system_time();
220 #endif
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++;
232 #ifdef PRINTING
233 PRINT(("apply configuration in : %lld\n", system_time() - start));
234 #endif
236 SetPriority(B_REAL_TIME_PRIORITY);
237 Run();
240 status_t ESDSinkNode::RequestCompleted(const media_request_info &info)
242 CALLED();
243 return B_OK;
246 void ESDSinkNode::SetTimeSource(BTimeSource *timeSource)
248 CALLED();
251 // -------------------------------------------------------- //
252 // implemention of BBufferConsumer
253 // -------------------------------------------------------- //
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)
261 status_t err;
262 CALLED();
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
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)) {
279 if ( format->type != B_MEDIA_RAW_AUDIO ) {
280 fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n");
281 return B_MEDIA_BAD_FORMAT;
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;
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*/;
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;
305 //AddRequirements(format);
307 // start connecting here
308 err = fDevice->Connect(fHostname.String(), fPort);
310 return B_OK;
313 status_t ESDSinkNode::GetNextInput(
314 int32 * cookie,
315 media_input * out_input)
317 CALLED();
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;
328 void ESDSinkNode::DisposeInputCookie(
329 int32 cookie)
331 CALLED();
332 // nothing to do since our cookies are just integers
335 void ESDSinkNode::BufferReceived(
336 BBuffer * buffer)
338 CALLED();
339 switch (buffer->Header()->type) {
340 /*case B_MEDIA_PARAMETERS:
342 status_t status = ApplyParameterData(buffer->Data(),buffer->SizeUsed());
343 if (status != B_OK) {
344 fprintf(stderr,"ApplyParameterData in ESDSinkNode::BufferReceived failed\n");
346 buffer->Recycle();
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();
364 #endif
365 if (fDevice->CanSend()) {
367 fDevice->Write(buffer->Data(), buffer->SizeUsed());
370 buffer->Recycle();
371 break;
372 default:
373 fprintf(stderr,"unexpected buffer type in ESDSinkNode::BufferReceived\n");
374 buffer->Recycle();
375 break;
379 void ESDSinkNode::ProducerDataStatus(
380 const media_destination & for_whom,
381 int32 status,
382 bigtime_t at_performance_time)
384 CALLED();
386 if(fInput.destination != for_whom) {
387 fprintf(stderr,"invalid destination received in ESDSinkNode::ProducerDataStatus\n");
388 return;
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);
396 status_t ESDSinkNode::GetLatencyFor(
397 const media_destination & for_whom,
398 bigtime_t * out_latency,
399 media_node_id * out_timesource)
401 CALLED();
402 if ((out_latency == 0) || (out_timesource == 0)) {
403 fprintf(stderr,"<- B_BAD_VALUE\n");
404 return B_BAD_VALUE;
407 if(fInput.destination != for_whom) {
408 fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
409 return B_MEDIA_BAD_DESTINATION;
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;
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)
431 status_t err;
432 CALLED();
434 if(fInput.destination != where) {
435 fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
436 return B_MEDIA_BAD_DESTINATION;
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;
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));
457 PRINT((" internal latency = %lld\n",fInternalLatency));
459 SetEventLatency(fInternalLatency);
461 // record the agreed upon values
462 fInput.source = producer;
463 fInput.format = with_format;
464 *out_input = fInput;
466 return B_OK;
469 void ESDSinkNode::Disconnected(
470 const media_source & producer,
471 const media_destination & where)
473 CALLED();
475 if(fInput.destination != where) {
476 fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
477 return;
479 if (fInput.source != producer) {
480 fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n");
481 return;
484 fInput.source = media_source::null;
485 fInput.format = fPreferredFormat;
486 //GetFormat(&channel->fInput.format);
487 if (fDevice)
488 fDevice->Disconnect();
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)
499 CALLED();
501 if(fInput.destination != consumer) {
502 fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
503 return B_MEDIA_BAD_DESTINATION;
505 if (fInput.source != producer) {
506 return B_MEDIA_BAD_SOURCE;
509 return B_ERROR;
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)
524 CALLED();
525 return BBufferConsumer::SeekTagRequested(destination,in_target_time,in_flags,
526 out_seek_tag,out_tagged_time,out_flags);
529 // -------------------------------------------------------- //
530 // implementation for BBufferProducer
531 // -------------------------------------------------------- //
532 #if 0
533 status_t
534 ESDSinkNode::FormatSuggestionRequested(media_type type, int32 /*quality*/, media_format* format)
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();
541 if (!format)
543 fprintf(stderr, "\tERROR - NULL format pointer passed in!\n");
544 return B_BAD_VALUE;
547 // this is the format we'll be returning (our preferred format)
548 *format = fPreferredFormat;
550 // a wildcard type is okay; we can specialize it
551 if (type == B_MEDIA_UNKNOWN_TYPE) type = B_MEDIA_RAW_AUDIO;
553 // we only support raw audio
554 if (type != B_MEDIA_RAW_AUDIO) return B_MEDIA_BAD_FORMAT;
555 else return B_OK;
558 status_t
559 ESDSinkNode::FormatProposal(const media_source& output, media_format* format)
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);
566 // is this a proposal for our select output?
567 if (channel == NULL)
569 fprintf(stderr, "ESDSinkNode::FormatProposal returning B_MEDIA_BAD_SOURCE\n");
570 return B_MEDIA_BAD_SOURCE;
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))
579 fprintf(stderr, "ESDSinkNode::FormatProposal returning B_MEDIA_BAD_FORMAT\n");
580 return B_MEDIA_BAD_FORMAT;
582 else return B_OK; // raw audio or wildcard type, either is okay by us
585 status_t
586 ESDSinkNode::FormatChangeRequested(const media_source& source, const media_destination& destination, media_format* io_format, int32* _deprecated_)
588 CALLED();
590 // we don't support any other formats, so we just reject any format changes.
591 return B_ERROR;
594 status_t
595 ESDSinkNode::GetNextOutput(int32* cookie, media_output* out_output)
597 CALLED();
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;
608 status_t
609 ESDSinkNode::DisposeOutputCookie(int32 cookie)
611 CALLED();
612 // do nothing because we don't use the cookie for anything special
613 return B_OK;
616 status_t
617 ESDSinkNode::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup)
619 CALLED();
621 node_output *channel = FindOutput(for_source);
623 // is this our output?
624 if (channel == NULL)
626 fprintf(stderr, "ESDSinkNode::SetBufferGroup returning B_MEDIA_BAD_SOURCE\n");
627 return B_MEDIA_BAD_SOURCE;
630 // Are we being passed the buffer group we're already using?
631 if (newGroup == channel->fBufferGroup) return B_OK;
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)
642 // we were given a valid group; just use that one from now on
643 channel->fBufferGroup = newGroup;
645 else
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);
654 return B_OK;
657 status_t
658 ESDSinkNode::PrepareToConnect(const media_source& what, const media_destination& where, media_format* format, media_source* out_source, char* out_name)
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();
667 node_output *channel = FindOutput(what);
669 // is this our output?
670 if (channel == NULL)
672 fprintf(stderr, "ESDSinkNode::PrepareToConnect returning B_MEDIA_BAD_SOURCE\n");
673 return B_MEDIA_BAD_SOURCE;
676 // are we already connected?
677 if (channel->fOutput.destination != media_destination::null)
678 return B_MEDIA_ALREADY_CONNECTED;
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)
685 fprintf(stderr, "\tnon-raw-audio format?!\n");
686 return B_MEDIA_BAD_FORMAT;
689 // !!! validate all other fields except for buffer_size here, because the consumer might have
690 // supplied different values from AcceptFormat()?
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)
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);
698 else
700 fprintf(stderr, "\tconsumer suggested buffer_size %lu\n", format->u.raw_audio.buffer_size);
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;
711 void
712 ESDSinkNode::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name)
714 CALLED();
716 node_output *channel = FindOutput(source);
718 // is this our output?
719 if (channel == NULL)
721 fprintf(stderr, "ESDSinkNode::Connect returning (cause : B_MEDIA_BAD_SOURCE)\n");
722 return;
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)
730 channel->fOutput.destination = media_destination::null;
731 channel->fOutput.format = channel->fPreferredFormat;
732 return;
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);
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));
747 SetBufferDuration(duration);
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));
755 fInternalLatency = BufferDuration();
756 PRINT(("\tbuffer-filling took %Ld usec on this machine\n", fInternalLatency));
757 //SetEventLatency(fLatency + fInternalLatency);
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);
766 // we are sure the thread is started
767 StartThread();
770 void
771 ESDSinkNode::Disconnect(const media_source& what, const media_destination& where)
773 CALLED();
775 node_output *channel = FindOutput(what);
777 // is this our output?
778 if (channel == NULL)
780 fprintf(stderr, "ESDSinkNode::Disconnect() returning (cause : B_MEDIA_BAD_SOURCE)\n");
781 return;
784 // Make sure that our connection is the one being disconnected
785 if ((where == channel->fOutput.destination) && (what == channel->fOutput.source))
787 channel->fOutput.destination = media_destination::null;
788 channel->fOutput.format = channel->fPreferredFormat;
789 delete channel->fBufferGroup;
790 channel->fBufferGroup = NULL;
792 else
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);
799 void
800 ESDSinkNode::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time)
802 CALLED();
804 node_output *channel = FindOutput(what);
806 // is this our output?
807 if (channel == NULL)
809 return;
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)
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"....
821 else if (RunMode() == B_INCREASE_LATENCY)
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);
831 fprintf(stderr, "\tincreasing latency to %Ld\n", fLatency + fInternalLatency);
833 else
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;*/
841 fprintf(stderr, "\tskipping a buffer to try to catch up\n");
845 void
846 ESDSinkNode::EnableOutput(const media_source& what, bool enabled, int32* _deprecated_)
848 CALLED();
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);
856 if (channel != NULL)
858 channel->fOutputEnabled = enabled;
862 void
863 ESDSinkNode::AdditionalBufferRequested(const media_source& source, media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag* prev_tag)
865 CALLED();
866 // we don't support offline mode
867 return;
869 #endif
871 // -------------------------------------------------------- //
872 // implementation for BMediaEventLooper
873 // -------------------------------------------------------- //
875 void ESDSinkNode::HandleEvent(
876 const media_timed_event *event,
877 bigtime_t lateness,
878 bool realTimeEvent)
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);
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;
911 // protected:
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)
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;
927 if(fInput.destination.id != buffer->Header()->destination) {
928 fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
929 return B_MEDIA_BAD_DESTINATION;
932 media_header* hdr = buffer->Header();
933 bigtime_t now = TimeSource()->Now();
934 bigtime_t perf_time = hdr->start_time;
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;
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))
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());
954 return B_OK;
957 status_t ESDSinkNode::HandleDataStatus(
958 const media_timed_event *event,
959 bigtime_t lateness,
960 bool realTimeEvent)
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;
974 return B_OK;
977 status_t ESDSinkNode::HandleStart(
978 const media_timed_event *event,
979 bigtime_t lateness,
980 bool realTimeEvent)
982 CALLED();
983 if (RunState() != B_STARTED) {
986 return B_OK;
989 status_t ESDSinkNode::HandleSeek(
990 const media_timed_event *event,
991 bigtime_t lateness,
992 bool realTimeEvent)
994 CALLED();
995 PRINT(("ESDSinkNode::HandleSeek(t=%lld,d=%li,bd=%lld)\n",event->event_time,event->data,event->bigdata));
996 return B_OK;
999 status_t ESDSinkNode::HandleWarp(
1000 const media_timed_event *event,
1001 bigtime_t lateness,
1002 bool realTimeEvent)
1004 CALLED();
1005 return B_OK;
1008 status_t ESDSinkNode::HandleStop(
1009 const media_timed_event *event,
1010 bigtime_t lateness,
1011 bool realTimeEvent)
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);
1017 //StopThread();
1018 return B_OK;
1021 status_t ESDSinkNode::HandleParameter(
1022 const media_timed_event *event,
1023 bigtime_t lateness,
1024 bool realTimeEvent)
1026 CALLED();
1027 return B_OK;
1030 // -------------------------------------------------------- //
1031 // implemention of BTimeSource
1032 // -------------------------------------------------------- //
1033 #ifdef ENABLE_TS
1035 void
1036 ESDSinkNode::SetRunMode(run_mode mode)
1038 CALLED();
1039 PRINT(("ESDSinkNode::SetRunMode mode:%i\n", mode));
1040 //BTimeSource::SetRunMode(mode);
1043 status_t
1044 ESDSinkNode::TimeSourceOp(const time_source_op_info &op, void *_reserved)
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;
1053 media_timed_event startEvent(0, BTimedEventQueue::B_START);
1054 EventQueue()->AddEvent(startEvent);
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);
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);
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;
1082 return B_OK;
1084 #endif
1086 // -------------------------------------------------------- //
1087 // implemention of BControllable
1088 // -------------------------------------------------------- //
1090 status_t
1091 ESDSinkNode::GetParameterValue(int32 id, bigtime_t* last_change, void* value, size_t* ioSize)
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:
1106 BString s = fDevice->Host();
1107 *ioSize = MIN(*ioSize, s.Length());
1108 memcpy(value, s.String(), *ioSize);
1109 return B_OK;
1111 case PARAM_PORT:
1113 BString s;
1114 s << fDevice->Port();
1115 *ioSize = MIN(*ioSize, s.Length());
1116 memcpy(value, s.String(), *ioSize);
1117 return B_OK;
1119 default:
1120 break;
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;
1129 #endif
1131 return EINVAL;
1134 void
1135 ESDSinkNode::SetParameterValue(int32 id, bigtime_t performance_time, const void* value, size_t size)
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;
1145 switch (id) {
1146 case PARAM_ENABLED:
1147 if (size != sizeof(bool))
1148 return;
1149 fEnabled = *(bool *)value;
1150 return;
1151 case PARAM_HOST:
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();
1164 #endif
1165 return;
1167 case PARAM_PORT:
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();
1180 #endif
1181 return;
1183 default:
1184 break;
1188 BParameterWeb*
1189 ESDSinkNode::MakeParameterWeb()
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;
1205 // -------------------------------------------------------- //
1206 // ESDSinkNode specific functions
1207 // -------------------------------------------------------- //
1209 status_t
1210 ESDSinkNode::GetConfigurationFor(BMessage * into_message)
1212 CALLED();
1214 BParameter *parameter = NULL;
1215 void *buffer;
1216 size_t size = 128;
1217 bigtime_t last_change;
1218 status_t err;
1220 if (!into_message)
1221 return B_BAD_VALUE;
1223 buffer = malloc(size);
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;
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);
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)));
1247 //PRINT_OBJECT(*into_message);
1249 return B_OK;
1252 // static:
1254 void ESDSinkNode::GetFlavor(flavor_info * outInfo, int32 id)
1256 CALLED();
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;
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;
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
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;
1288 void ESDSinkNode::GetFormat(media_format * outFormat)
1290 CALLED();
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;