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