2 * ESounD media addon for BeOS
4 * Copyright (c) 2006 François Revol (revol@free.fr)
6 * Based on Multi Audio addon for Haiku,
7 * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr)
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.
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>
42 #include <BufferGroup.h>
43 #include <TimeSource.h>
45 #include <ParameterWeb.h>
46 #include <MediaRoster.h>
48 #include <MediaDefs.h>
51 #include "ESDSinkNode.h"
52 #include "ESDEndpoint.h"
63 // -------------------------------------------------------- //
65 // -------------------------------------------------------- //
67 ESDSinkNode::~ESDSinkNode(void)
70 fAddOn
->GetConfigurationFor(this, NULL
);
72 BMediaEventLooper::Quit();
78 ESDSinkNode::ESDSinkNode(BMediaAddOn
*addon
, char* name
, BMessage
* config
)
80 BBufferConsumer(B_MEDIA_RAW_AUDIO
),
82 BBufferProducer(B_MEDIA_RAW_AUDIO
),
90 fTimeSourceStarted(false),
95 fInitCheckStatus
= B_NO_INIT
;
100 AddNodeKind( B_PHYSICAL_OUTPUT
);
102 AddNodeKind( B_PHYSICAL_INPUT
);
105 // initialize our preferred format object
106 memset(&fPreferredFormat
, 0, sizeof(fPreferredFormat
)); // set everything to wildcard first
107 fPreferredFormat
.type
= B_MEDIA_RAW_AUDIO
;
109 fPreferredFormat
.u
.raw_audio
.format
= media_raw_audio_format::B_AUDIO_UCHAR
;
111 fPreferredFormat
.u
.raw_audio
.format
= media_raw_audio_format::B_AUDIO_SHORT
;
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*/;
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
;
132 fDevice
= new ESDEndpoint();
135 if (fDevice->Connect(fHostname.String()) >= 0) {
136 fDevice->SetCommand();
137 fDevice->SetFormat(ESD_FMT, 2);
138 //fDevice->GetServerInfo();
139 fInitCheckStatus = fDevice->SendDefaultCommand();
145 fInitCheckStatus
= B_OK
;
148 status_t
ESDSinkNode::InitCheck(void) const
151 return fInitCheckStatus
;
155 // -------------------------------------------------------- //
156 // implementation of BMediaNode
157 // -------------------------------------------------------- //
159 BMediaAddOn
* ESDSinkNode::AddOn(
160 int32
* internal_id
) const
163 // BeBook says this only gets called if we were in an add-on.
165 // If we get a null pointer then we just won't write.
166 if (internal_id
!= 0) {
173 void ESDSinkNode::Preroll(void)
176 // XXX:Performance opportunity
177 BMediaNode::Preroll();
180 status_t
ESDSinkNode::HandleMessage(
189 void ESDSinkNode::NodeRegistered(void)
193 if (fInitCheckStatus
!= B_OK
) {
194 ReportError(B_NODE_IN_DISTRESS
);
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 */
219 bigtime_t start
= system_time();
223 int32 parameterID
= 0;
226 while(fConfig
.FindInt32("parameterID", index
, ¶meterID
) == B_OK
) {
227 if(fConfig
.FindData("parameterData", B_RAW_TYPE
, index
, &data
, &size
) == B_OK
)
228 SetParameterValue(parameterID
, TimeSource()->Now(), data
, size
);
233 PRINT(("apply configuration in : %lld\n", system_time() - start
));
236 SetPriority(B_REAL_TIME_PRIORITY
);
240 status_t
ESDSinkNode::RequestCompleted(const media_request_info
&info
)
246 void ESDSinkNode::SetTimeSource(BTimeSource
*timeSource
)
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
)
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;
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
);
313 status_t
ESDSinkNode::GetNextInput(
315 media_input
* out_input
)
319 if ((*cookie
< 1) && (*cookie
>= 0)) {
322 PRINT(("input.format : %lu\n", fInput
.format
.u
.raw_audio
.format
));
328 void ESDSinkNode::DisposeInputCookie(
332 // nothing to do since our cookies are just integers
335 void ESDSinkNode::BufferReceived(
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");
349 case B_MEDIA_RAW_AUDIO
:
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
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");
365 if (fDevice
->CanSend()) {
367 fDevice
->Write(buffer
->Data(), buffer
->SizeUsed());
373 fprintf(stderr
,"unexpected buffer type in ESDSinkNode::BufferReceived\n");
379 void ESDSinkNode::ProducerDataStatus(
380 const media_destination
& for_whom
,
382 bigtime_t at_performance_time
)
386 if(fInput
.destination
!= for_whom
) {
387 fprintf(stderr
,"invalid destination received in ESDSinkNode::ProducerDataStatus\n");
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
)
402 if ((out_latency
== 0) || (out_timesource
== 0)) {
403 fprintf(stderr
,"<- B_BAD_VALUE\n");
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;
415 netl
= fDevice
->Latency();
416 // I don't want to swap
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();
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
)
434 if(fInput
.destination
!= where
) {
435 fprintf(stderr
,"<- B_MEDIA_BAD_DESTINATION\n");
436 return B_MEDIA_BAD_DESTINATION
;
441 err
= fDevice
->WaitForConnect();
444 fDevice
->SetCommand();
445 //fDevice->GetServerInfo();
446 fDevice
->SetFormat(ESD_FMT
, 2);
447 err
= fDevice
->SendDefaultCommand();
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
;
469 void ESDSinkNode::Disconnected(
470 const media_source
& producer
,
471 const media_destination
& where
)
475 if(fInput
.destination
!= where
) {
476 fprintf(stderr
,"<- B_MEDIA_BAD_DESTINATION\n");
479 if (fInput
.source
!= producer
) {
480 fprintf(stderr
,"<- B_MEDIA_BAD_SOURCE\n");
484 fInput
.source
= media_source::null
;
485 fInput
.format
= fPreferredFormat
;
486 //GetFormat(&channel->fInput.format);
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
,
497 const media_format
& format
)
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
;
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 */
516 status_t
ESDSinkNode::SeekTagRequested(
517 const media_destination
& destination
,
518 bigtime_t in_target_time
,
520 media_seek_tag
* out_seek_tag
,
521 bigtime_t
* out_tagged_time
,
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 // -------------------------------------------------------- //
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.
543 fprintf(stderr
, "\tERROR - NULL format pointer passed in!\n");
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
;
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.
564 node_output
*channel
= FindOutput(output
);
566 // is this a proposal for our select output?
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
586 ESDSinkNode::FormatChangeRequested(const media_source
& source
, const media_destination
& destination
, media_format
* io_format
, int32
* _deprecated_
)
590 // we don't support any other formats, so we just reject any format changes.
595 ESDSinkNode::GetNextOutput(int32
* cookie
, media_output
* out_output
)
599 if ((*cookie
< fOutputs
.CountItems()) && (*cookie
>= 0)) {
600 node_output
*channel
= (node_output
*)fOutputs
.ItemAt(*cookie
);
601 *out_output
= channel
->fOutput
;
609 ESDSinkNode::DisposeOutputCookie(int32 cookie
)
612 // do nothing because we don't use the cookie for anything special
617 ESDSinkNode::SetBufferGroup(const media_source
& for_source
, BBufferGroup
* newGroup
)
621 node_output
*channel
= FindOutput(for_source
);
623 // is this our output?
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
;
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
);
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!
667 node_output
*channel
= FindOutput(what
);
669 // is this our output?
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
);
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
);
712 ESDSinkNode::Connect(status_t error
, const media_source
& source
, const media_destination
& destination
, const media_format
& format
, char* io_name
)
716 node_output
*channel
= FindOutput(source
);
718 // is this our output?
721 fprintf(stderr
, "ESDSinkNode::Connect returning (cause : B_MEDIA_BAD_SOURCE)\n");
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
730 channel
->fOutput
.destination
= media_destination::null
;
731 channel
->fOutput
.format
= channel
->fPreferredFormat
;
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.
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()
763 if (!channel
->fBufferGroup
)
764 AllocateBuffers(*channel
);
766 // we are sure the thread is started
771 ESDSinkNode::Disconnect(const media_source
& what
, const media_destination
& where
)
775 node_output
*channel
= FindOutput(what
);
777 // is this our output?
780 fprintf(stderr
, "ESDSinkNode::Disconnect() returning (cause : B_MEDIA_BAD_SOURCE)\n");
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
;
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
);
800 ESDSinkNode::LateNoticeReceived(const media_source
& what
, bigtime_t how_much
, bigtime_t performance_time
)
804 node_output
*channel
= FindOutput(what
);
806 // is this our output?
812 // If we're late, we need to catch up. Respond in a manner appropriate to our
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
828 fInternalLatency
+= how_much
;
829 SetEventLatency(fLatency
+ fInternalLatency
);
831 fprintf(stderr
, "\tincreasing latency to %Ld\n", fLatency
+ fInternalLatency
);
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");
846 ESDSinkNode::EnableOutput(const media_source
& what
, bool enabled
, int32
* _deprecated_
)
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
);
858 channel
->fOutputEnabled
= enabled
;
863 ESDSinkNode::AdditionalBufferRequested(const media_source
& source
, media_buffer_id prev_buffer
, bigtime_t prev_time
, const media_seek_tag
* prev_tag
)
866 // we don't support offline mode
871 // -------------------------------------------------------- //
872 // implementation for BMediaEventLooper
873 // -------------------------------------------------------- //
875 void ESDSinkNode::HandleEvent(
876 const media_timed_event
*event
,
881 switch (event
->type
) {
882 case BTimedEventQueue::B_START
:
883 HandleStart(event
,lateness
,realTimeEvent
);
885 case BTimedEventQueue::B_SEEK
:
886 HandleSeek(event
,lateness
,realTimeEvent
);
888 case BTimedEventQueue::B_WARP
:
889 HandleWarp(event
,lateness
,realTimeEvent
);
891 case BTimedEventQueue::B_STOP
:
892 HandleStop(event
,lateness
,realTimeEvent
);
894 case BTimedEventQueue::B_HANDLE_BUFFER
:
895 if (RunState() == BMediaEventLooper::B_STARTED
) {
896 HandleBuffer(event
,lateness
,realTimeEvent
);
899 case BTimedEventQueue::B_DATA_STATUS
:
900 HandleDataStatus(event
,lateness
,realTimeEvent
);
902 case BTimedEventQueue::B_PARAMETER
:
903 HandleParameter(event
,lateness
,realTimeEvent
);
906 fprintf(stderr
," unknown event type: %li\n",event
->type
);
913 // how should we handle late buffers? drop them?
914 // notify the producer?
915 status_t
ESDSinkNode::HandleBuffer(
916 const media_timed_event
*event
,
921 BBuffer
* buffer
= const_cast<BBuffer
*>((BBuffer
*)event
->pointer
);
923 fprintf(stderr
,"<- B_BAD_VALUE\n");
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
942 if ((RunMode() != B_OFFLINE
) && // lateness doesn't matter in offline mode...
943 (RunMode() != B_RECORDING
) && // ...or in recording mode
947 NotifyLateProducer(fInput
.source
, -how_early
, perf_time
);
948 fprintf(stderr
," <- LATE BUFFER : %lli\n", how_early
);
951 if (fDevice
->CanSend())
952 fDevice
->Write(buffer
->Data(), buffer
->SizeUsed());
957 status_t
ESDSinkNode::HandleDataStatus(
958 const media_timed_event
*event
,
963 PRINT(("ESDSinkNode::HandleDataStatus status:%li, lateness:%lli\n", event
->data
, lateness
));
964 switch(event
->data
) {
965 case B_DATA_NOT_AVAILABLE
:
967 case B_DATA_AVAILABLE
:
969 case B_PRODUCER_STOPPED
:
977 status_t
ESDSinkNode::HandleStart(
978 const media_timed_event
*event
,
983 if (RunState() != B_STARTED
) {
989 status_t
ESDSinkNode::HandleSeek(
990 const media_timed_event
*event
,
995 PRINT(("ESDSinkNode::HandleSeek(t=%lld,d=%li,bd=%lld)\n",event
->event_time
,event
->data
,event
->bigdata
));
999 status_t
ESDSinkNode::HandleWarp(
1000 const media_timed_event
*event
,
1008 status_t
ESDSinkNode::HandleStop(
1009 const media_timed_event
*event
,
1014 // flush the queue so downstreamers don't get any more
1015 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS
, true, BTimedEventQueue::B_HANDLE_BUFFER
);
1021 status_t
ESDSinkNode::HandleParameter(
1022 const media_timed_event
*event
,
1030 // -------------------------------------------------------- //
1031 // implemention of BTimeSource
1032 // -------------------------------------------------------- //
1036 ESDSinkNode::SetRunMode(run_mode mode
)
1039 PRINT(("ESDSinkNode::SetRunMode mode:%i\n", mode
));
1040 //BTimeSource::SetRunMode(mode);
1044 ESDSinkNode::TimeSourceOp(const time_source_op_info
&op
, void *_reserved
)
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
);
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);
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);
1075 case B_TIMESOURCE_SEEK
:
1076 PRINT(("TimeSourceOp op B_TIMESOURCE_SEEK\n"));
1077 BroadcastTimeWarp(op
.real_time
, op
.performance_time
);
1086 // -------------------------------------------------------- //
1087 // implemention of BControllable
1088 // -------------------------------------------------------- //
1091 ESDSinkNode::GetParameterValue(int32 id
, bigtime_t
* last_change
, void* value
, size_t* ioSize
)
1096 //PRINT(("id : %i\n", id));
1099 if (*ioSize
< sizeof(bool))
1101 *(bool *)value
= fEnabled
;
1102 *ioSize
= sizeof(bool);
1106 BString s
= fDevice
->Host();
1107 *ioSize
= MIN(*ioSize
, s
.Length());
1108 memcpy(value
, s
.String(), *ioSize
);
1114 s
<< fDevice
->Port();
1115 *ioSize
= MIN(*ioSize
, s
.Length());
1116 memcpy(value
, s
.String(), *ioSize
);
1123 BParameter
*parameter
= NULL
;
1124 for(int32 i
=0; i
<fWeb
->CountParameters(); i
++) {
1125 parameter
= fWeb
->ParameterAt(i
);
1126 if(parameter
->ID() == id
)
1135 ESDSinkNode::SetParameterValue(int32 id
, bigtime_t performance_time
, const void* value
, size_t size
)
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
)
1147 if (size
!= sizeof(bool))
1149 fEnabled
= *(bool *)value
;
1153 fprintf(stderr
, "set HOST: %s\n", (const char *)value
);
1154 fHostname
= (const char *)value
;
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();
1169 fprintf(stderr
, "set PORT: %s\n", (const char *)value
);
1170 fPort
= atoi((const char *)value
);
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();
1189 ESDSinkNode::MakeParameterWeb()
1192 BParameterWeb
* web
= new BParameterWeb
;
1193 BParameterGroup
*group
= web
->MakeGroup("Server");
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);
1205 // -------------------------------------------------------- //
1206 // ESDSinkNode specific functions
1207 // -------------------------------------------------------- //
1210 ESDSinkNode::GetConfigurationFor(BMessage
* into_message
)
1214 BParameter
*parameter
= NULL
;
1217 bigtime_t last_change
;
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
)
1231 PRINT(("getting parameter %li\n", parameter
->ID()));
1233 while((err
= GetParameterValue(parameter
->ID(), &last_change
, buffer
, &size
))==B_NO_MEMORY
) {
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);
1243 PRINT(("parameter %li err : %s\n", parameter
->ID(), strerror(err
)));
1247 //PRINT_OBJECT(*into_message);
1254 void ESDSinkNode::GetFlavor(flavor_info
* outInfo
, int32 id
)
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;
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
;
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
)
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
;