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