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