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 67ESDSinkNode::~ESDSinkNode(void) 68{ 69 CALLED(); 70 fAddOn->GetConfigurationFor(this, NULL); 71 72 BMediaEventLooper::Quit(); 73 74 fWeb = NULL; 75 delete fDevice; 76} 77 78ESDSinkNode::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 148status_t ESDSinkNode::InitCheck(void) const 149{ 150 CALLED(); 151 return fInitCheckStatus; 152} 153 154 155// -------------------------------------------------------- // 156// implementation of BMediaNode 157// -------------------------------------------------------- // 158 159BMediaAddOn * 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 173void ESDSinkNode::Preroll(void) 174{ 175 CALLED(); 176 // XXX:Performance opportunity 177 BMediaNode::Preroll(); 178} 179 180status_t ESDSinkNode::HandleMessage( 181 int32 message, 182 const void * data, 183 size_t size) 184{ 185 CALLED(); 186 return B_ERROR; 187} 188 189void 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 241status_t ESDSinkNode::RequestCompleted(const media_request_info &info) 242{ 243 CALLED(); 244 return B_OK; 245} 246 247void 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. 258status_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 314status_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 329void ESDSinkNode::DisposeInputCookie( 330 int32 cookie) 331{ 332 CALLED(); 333 // nothing to do since our cookies are just integers 334} 335 336void 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 380void 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 397status_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 426status_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 470void 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. */ 494status_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. */ 517status_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 534status_t 535ESDSinkNode::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 559status_t 560ESDSinkNode::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 586status_t 587ESDSinkNode::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 595status_t 596ESDSinkNode::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 609status_t 610ESDSinkNode::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 617status_t 618ESDSinkNode::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 658status_t 659ESDSinkNode::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 712void 713ESDSinkNode::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 771void 772ESDSinkNode::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 800void 801ESDSinkNode::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 846void 847ESDSinkNode::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 863void 864ESDSinkNode::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 876void 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? 916status_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 958status_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 978status_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 990status_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 1000status_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 1009status_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 1022status_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 1036void 1037ESDSinkNode::SetRunMode(run_mode mode) 1038{ 1039 CALLED(); 1040 PRINT(("ESDSinkNode::SetRunMode mode:%i\n", mode)); 1041 //BTimeSource::SetRunMode(mode); 1042} 1043 1044status_t 1045ESDSinkNode::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 1091status_t 1092ESDSinkNode::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 1132return EINVAL; 1133} 1134 1135void 1136ESDSinkNode::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 1189BParameterWeb* 1190ESDSinkNode::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 1210status_t 1211ESDSinkNode::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 1255void 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 1289void 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