1 // AbstractFileInterfaceNode.cpp 2 // 3 // Andrew Bachmann, 2002 4 // 5 // The AbstractFileInterfaceNode class implements 6 // the common functionality between MediaReader 7 // and MediaWriter. 8 #include "AbstractFileInterfaceNode.h" 9 #include "debug.h" 10 11 #include <Buffer.h> 12 #include <Controllable.h> 13 #include <Entry.h> 14 #include <Errors.h> 15 #include <File.h> 16 #include <FileInterface.h> 17 #include <MediaDefs.h> 18 #include <MediaEventLooper.h> 19 #include <MediaNode.h> 20 #include <TimeSource.h> 21 #include <ParameterWeb.h> 22 #include <limits.h> 23 #include <stdio.h> 24 #include <string.h> 25 26 27 AbstractFileInterfaceNode::~AbstractFileInterfaceNode(void) 28 { 29 CALLED(); 30 31 // Stop the BMediaEventLooper thread 32 Quit(); 33 if (fCurrentFile != 0) 34 delete fCurrentFile; 35 } 36 37 38 AbstractFileInterfaceNode::AbstractFileInterfaceNode( 39 size_t defaultChunkSize, 40 float defaultBitRate, 41 const flavor_info * info, 42 BMessage * config, 43 BMediaAddOn * addOn) 44 : BMediaNode("AbstractFileInterfaceNode"), 45 BFileInterface(), 46 BControllable(), 47 BMediaEventLooper() 48 { 49 CALLED(); 50 51 // keep our creator around for AddOn calls later 52 fAddOn = addOn; 53 // null some fields 54 fCurrentFile = 0; 55 f_current_mime_type[0] = '\0'; 56 57 // initialize the parameters 58 if (defaultChunkSize <= 0) { 59 fInitCheckStatus = B_BAD_VALUE; 60 return; 61 } 62 63 fDefaultChunkSizeParam = defaultChunkSize; 64 fDefaultChunkSizeParamChangeTime = 0; 65 if (defaultBitRate <= 0) { 66 fInitCheckStatus = B_BAD_VALUE; 67 return; 68 } 69 70 fDefaultBitRateParam = defaultBitRate; 71 fDefaultBitRateParamChangeTime = 0; 72 // initialize parameter compute fields 73 fLeastRecentlyUpdatedParameter = DEFAULT_BIT_RATE_PARAM; 74 fLastUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM; 75 // From the chunk size and bit rate we compute the buffer period. 76 int64 value = int64(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam); 77 if ((value <= 0) || (value > INT_MAX)) { 78 fInitCheckStatus = B_BAD_VALUE; 79 return; 80 } 81 fDefaultBufferPeriodParam = int32(value); 82 fDefaultBufferPeriodParamChangeTime = 0; 83 84 fInitCheckStatus = B_OK; 85 } 86 87 88 status_t AbstractFileInterfaceNode::InitCheck(void) const 89 { 90 CALLED(); 91 return fInitCheckStatus; 92 } 93 94 95 status_t AbstractFileInterfaceNode::GetConfigurationFor( 96 BMessage * into_message) 97 { 98 CALLED(); 99 return B_OK; 100 } 101 102 103 // -------------------------------------------------------- // 104 // implementation of BMediaNode 105 // -------------------------------------------------------- // 106 BMediaAddOn * AbstractFileInterfaceNode::AddOn( 107 int32 * internal_id) const 108 { 109 CALLED(); 110 // BeBook says this only gets called if we were in an add-on. 111 if (fAddOn != 0) { 112 // If we get a null pointer then we just won't write. 113 if (internal_id != 0) 114 internal_id = 0; 115 } 116 return fAddOn; 117 } 118 119 120 void AbstractFileInterfaceNode::Start( 121 bigtime_t performance_time) 122 { 123 PRINT("AbstractFileInterfaceNode::Start(pt=%lld)\n",performance_time); 124 BMediaEventLooper::Start(performance_time); 125 } 126 127 128 void AbstractFileInterfaceNode::Stop( 129 bigtime_t performance_time, 130 bool immediate) 131 { 132 PRINT("AbstractFileInterfaceNode::Stop(pt=%lld, im=%d)\n", 133 performance_time, immediate); 134 BMediaEventLooper::Stop(performance_time,immediate); 135 } 136 137 138 void AbstractFileInterfaceNode::Seek( 139 bigtime_t media_time, 140 bigtime_t performance_time) 141 { 142 PRINT("AbstractFileInterfaceNode::Seek(mt=%lld,pt=%lld)\n", 143 media_time,performance_time); 144 BMediaEventLooper::Seek(media_time,performance_time); 145 } 146 147 148 void AbstractFileInterfaceNode::SetRunMode( 149 run_mode mode) 150 { 151 PRINT("AbstractFileInterfaceNode::SetRunMode(%i)\n",mode); 152 BMediaEventLooper::SetRunMode(mode); 153 } 154 155 156 void AbstractFileInterfaceNode::TimeWarp( 157 bigtime_t at_real_time, 158 bigtime_t to_performance_time) 159 { 160 PRINT("AbstractFileInterfaceNode::TimeWarp(rt=%lld,pt=%lld)\n", 161 at_real_time,to_performance_time); 162 BMediaEventLooper::TimeWarp(at_real_time,to_performance_time); 163 } 164 165 166 void AbstractFileInterfaceNode::Preroll(void) 167 { 168 CALLED(); 169 // XXX:Performance opportunity 170 BMediaNode::Preroll(); 171 } 172 173 174 void AbstractFileInterfaceNode::SetTimeSource( 175 BTimeSource * time_source) 176 { 177 CALLED(); 178 BMediaNode::SetTimeSource(time_source); 179 } 180 181 182 status_t AbstractFileInterfaceNode::HandleMessage( 183 int32 message, 184 const void * data, 185 size_t size) 186 { 187 CALLED(); 188 189 status_t status = B_OK; 190 switch (message) { 191 // no special messages for now 192 default: 193 status = BFileInterface::HandleMessage(message,data,size); 194 if (status == B_OK) { 195 break; 196 } 197 status = BControllable::HandleMessage(message, data, size); 198 if (status == B_OK) 199 break; 200 status = BMediaNode::HandleMessage(message,data,size); 201 if (status == B_OK) { 202 break; 203 } 204 BMediaNode::HandleBadMessage(message,data,size); 205 status = B_ERROR; 206 break; 207 } 208 return status; 209 } 210 211 212 status_t AbstractFileInterfaceNode::RequestCompleted( 213 const media_request_info & info) 214 { 215 CALLED(); 216 return BMediaNode::RequestCompleted(info); 217 } 218 219 220 status_t AbstractFileInterfaceNode::DeleteHook( 221 BMediaNode * node) 222 { 223 CALLED(); 224 return BMediaEventLooper::DeleteHook(node); 225 } 226 227 228 void AbstractFileInterfaceNode::NodeRegistered(void) 229 { 230 CALLED(); 231 232 // set up our parameter web 233 SetParameterWeb(MakeParameterWeb()); 234 235 // start the BMediaEventLooper thread 236 SetPriority(B_REAL_TIME_PRIORITY); 237 Run(); 238 } 239 240 241 status_t AbstractFileInterfaceNode::GetNodeAttributes( 242 media_node_attribute * outAttributes, 243 size_t inMaxCount) 244 { 245 CALLED(); 246 return BMediaNode::GetNodeAttributes(outAttributes,inMaxCount); 247 } 248 249 250 status_t AbstractFileInterfaceNode::AddTimer( 251 bigtime_t at_performance_time, 252 int32 cookie) 253 { 254 CALLED(); 255 return BMediaEventLooper::AddTimer(at_performance_time,cookie); 256 } 257 258 259 // protected: 260 BParameterWeb * AbstractFileInterfaceNode::MakeParameterWeb(void) 261 { 262 CALLED(); 263 264 BParameterWeb * web = new BParameterWeb(); 265 BParameterGroup * mainGroup = web->MakeGroup("AbstractFileInterfaceNode Parameters"); 266 267 // these three are related: 268 // DEFAULT_CHUNK_SIZE_PARAM = 269 // DEFAULT_BIT_RATE_PARAM / 1024 * DEFAULT_BUFFER_PERIOD_PARAM * 1000 270 BParameterGroup * chunkSizeGroup = mainGroup->MakeGroup("Chunk Size Group"); 271 BContinuousParameter * chunkSizeParameter 272 = chunkSizeGroup->MakeContinuousParameter( 273 DEFAULT_CHUNK_SIZE_PARAM, B_MEDIA_MULTISTREAM, 274 "Chunk Size", B_GAIN, "bytes", 1024, 32*1024, 512); 275 chunkSizeParameter->SetResponse(BContinuousParameter::B_LINEAR,1,0); 276 chunkSizeParameter->SetValue(&fDefaultChunkSizeParam,sizeof(fDefaultChunkSizeParam),0); 277 278 BParameterGroup * bitRateGroup = mainGroup->MakeGroup("Bit Rate Group"); 279 BContinuousParameter * bitRateParameter 280 = bitRateGroup->MakeContinuousParameter( 281 DEFAULT_BIT_RATE_PARAM, B_MEDIA_MULTISTREAM, 282 "Bit Rate", B_GAIN, "kbits/sec", 1, 320000, 1); 283 bitRateParameter->SetResponse(BContinuousParameter::B_LINEAR,.001,0); 284 bitRateParameter->SetValue(&fDefaultBitRateParam,sizeof(fDefaultBitRateParam),0); 285 286 BParameterGroup * bufferPeriodGroup = mainGroup->MakeGroup("Buffer Period Group"); 287 BContinuousParameter * bufferPeriodParameter 288 = bufferPeriodGroup->MakeContinuousParameter( 289 DEFAULT_BUFFER_PERIOD_PARAM, B_MEDIA_MULTISTREAM, 290 "Buffer Period", B_GAIN, "ms", 1, 10000, 1); 291 bufferPeriodParameter->SetResponse(BContinuousParameter::B_LINEAR,1,0); 292 bufferPeriodParameter->SetValue(&fDefaultBufferPeriodParam,sizeof(fDefaultBufferPeriodParam),0); 293 294 return web; 295 } 296 297 298 // -------------------------------------------------------- // 299 // implementation of BFileInterface 300 // -------------------------------------------------------- // 301 status_t AbstractFileInterfaceNode::GetNextFileFormat( 302 int32 * cookie, 303 media_file_format * out_format) 304 { 305 CALLED(); 306 307 // it's valid but they already got our 1 file format 308 if (*cookie != 0) { 309 PRINT("\t<- B_ERROR\n"); 310 return B_ERROR; 311 } 312 313 // so next time they won't get the same format again 314 *cookie = 1; 315 GetFileFormat(out_format); 316 return B_OK; 317 } 318 319 320 void AbstractFileInterfaceNode::DisposeFileFormatCookie( 321 int32 cookie) 322 { 323 CALLED(); 324 // nothing to do since our cookies are just integers 325 } 326 327 328 status_t AbstractFileInterfaceNode::GetDuration( 329 bigtime_t * out_time) 330 { 331 CALLED(); 332 if (out_time == 0) { 333 PRINT("\t<- B_BAD_VALUE\n"); 334 return B_BAD_VALUE; 335 } 336 337 if (fCurrentFile == 0) { 338 PRINT("\t<- B_NO_INIT\n"); 339 return B_NO_INIT; 340 } 341 342 return fCurrentFile->GetSize(out_time); 343 } 344 345 346 status_t AbstractFileInterfaceNode::SniffRef( 347 const entry_ref & file, 348 char * out_mime_type, /* 256 bytes */ 349 float * out_quality) 350 { 351 CALLED(); 352 return StaticSniffRef(file,out_mime_type,out_quality); 353 } 354 355 356 status_t AbstractFileInterfaceNode::SetRef( 357 const entry_ref & file, 358 uint32 openMode, 359 bool create, 360 bigtime_t * out_time) 361 { 362 CALLED(); 363 364 status_t status; 365 f_current_ref = file; 366 if (fCurrentFile == 0) { 367 fCurrentFile = new BFile(&f_current_ref,(openMode|(create?B_CREATE_FILE:0))); 368 status = fCurrentFile->InitCheck(); 369 } else { 370 status = fCurrentFile->SetTo(&f_current_ref,(openMode|(create?B_CREATE_FILE:0))); 371 } 372 373 if (status != B_OK) { 374 PRINT("\t<- failed BFile initialization\n"); 375 return status; 376 } 377 378 // cache the mime type for later 379 fCurrentFile->ReadAttr("BEOS:TYPE",0,0,f_current_mime_type,B_MIME_TYPE_LENGTH); 380 // compute the duration and return any error 381 return GetDuration(out_time); 382 } 383 384 385 status_t AbstractFileInterfaceNode::GetRef( 386 entry_ref * out_ref, 387 char * out_mime_type) 388 { 389 CALLED(); 390 391 if (fCurrentFile == 0) { 392 PRINT("\t<- B_NO_INIT\n"); 393 return B_NO_INIT; // the input_ref isn't valid yet either 394 } 395 396 *out_ref = f_current_ref; 397 // they hopefully allocated enough space (no way to check :-/ ) 398 strcpy(out_mime_type,f_current_mime_type); 399 return B_OK; 400 } 401 402 403 // provided for BAbstractFileInterfaceNodeAddOn 404 status_t AbstractFileInterfaceNode::StaticSniffRef( 405 const entry_ref & file, 406 char * out_mime_type, /* 256 bytes */ 407 float * out_quality) 408 { 409 CALLED(); 410 411 BNode node(&file); 412 status_t initCheck = node.InitCheck(); 413 if (initCheck != B_OK) { 414 PRINT("\t<- failed BNode::InitCheck()\n"); 415 return initCheck; 416 } 417 418 // they hopefully allocated enough room 419 node.ReadAttr("BEOS:TYPE",0,0,out_mime_type,B_MIME_TYPE_LENGTH); 420 *out_quality = 1.0; // we handle all files perfectly! we are so amazing! 421 return B_OK; 422 } 423 424 425 // -------------------------------------------------------- // 426 // implementation for BControllable 427 // -------------------------------------------------------- // 428 const int32 AbstractFileInterfaceNode::DEFAULT_CHUNK_SIZE_PARAM = 1; 429 const int32 AbstractFileInterfaceNode::DEFAULT_BIT_RATE_PARAM = 2; 430 const int32 AbstractFileInterfaceNode::DEFAULT_BUFFER_PERIOD_PARAM = 3; 431 432 433 status_t AbstractFileInterfaceNode::GetParameterValue( 434 int32 id, 435 bigtime_t * last_change, 436 void * value, 437 size_t * ioSize) 438 { 439 CALLED(); 440 441 switch (id) { 442 case DEFAULT_CHUNK_SIZE_PARAM: 443 if (*ioSize < sizeof(size_t)) { 444 return B_ERROR; // not enough room 445 } 446 *last_change = fDefaultChunkSizeParamChangeTime; 447 *((size_t*)value) = fDefaultChunkSizeParam; 448 *ioSize = sizeof(size_t); 449 break; 450 451 case DEFAULT_BIT_RATE_PARAM: 452 if (*ioSize < sizeof(float)) { 453 return B_ERROR; // not enough room 454 } 455 *last_change = fDefaultBitRateParamChangeTime; 456 *((float*)value) = fDefaultBitRateParam; 457 *ioSize = sizeof(float); 458 break; 459 460 case DEFAULT_BUFFER_PERIOD_PARAM: 461 if (*ioSize < sizeof(int32)) { 462 return B_ERROR; // not enough room 463 } 464 *last_change = fDefaultBufferPeriodParamChangeTime; 465 *((int32*)value) = fDefaultBufferPeriodParam; 466 *ioSize = sizeof(int32); 467 break; 468 469 default: 470 PRINT("AbstractFileInterfaceNode::GetParameterValue unknown id (%ld)\n",id); 471 return B_ERROR; 472 } 473 474 return B_OK; 475 } 476 477 478 void AbstractFileInterfaceNode::SetParameterValue( 479 int32 id, 480 bigtime_t when, 481 const void * value, 482 size_t size) 483 { 484 PRINT("AbstractFileInterfaceNode::SetParameterValue(id=%ld,when=%lld,size=%ld)\n",id,when,int32(size)); 485 486 switch (id) { 487 case DEFAULT_CHUNK_SIZE_PARAM: 488 case DEFAULT_BIT_RATE_PARAM: 489 case DEFAULT_BUFFER_PERIOD_PARAM: 490 { 491 media_timed_event event(when, BTimedEventQueue::B_PARAMETER, 492 NULL, BTimedEventQueue::B_NO_CLEANUP, 493 size, id, (char*) value, size); 494 EventQueue()->AddEvent(event); 495 } 496 break; 497 498 default: 499 PRINT("AbstractFileInterfaceNode::SetParameterValue unknown id (%ld)\n",id); 500 break; 501 } 502 } 503 504 505 // the default implementation should call the add-on main() 506 status_t AbstractFileInterfaceNode::StartControlPanel( 507 BMessenger * out_messenger) 508 { 509 CALLED(); 510 return BControllable::StartControlPanel(out_messenger); 511 } 512 513 514 // -------------------------------------------------------- // 515 // implementation for BMediaEventLooper 516 // -------------------------------------------------------- // 517 void AbstractFileInterfaceNode::HandleEvent( 518 const media_timed_event *event, 519 bigtime_t lateness, 520 bool realTimeEvent) 521 { 522 CALLED(); 523 switch (event->type) { 524 case BTimedEventQueue::B_START: 525 HandleStart(event,lateness,realTimeEvent); 526 break; 527 case BTimedEventQueue::B_SEEK: 528 HandleSeek(event,lateness,realTimeEvent); 529 break; 530 case BTimedEventQueue::B_WARP: 531 HandleWarp(event,lateness,realTimeEvent); 532 break; 533 case BTimedEventQueue::B_STOP: 534 HandleStop(event,lateness,realTimeEvent); 535 break; 536 case BTimedEventQueue::B_HANDLE_BUFFER: 537 if (RunState() == BMediaEventLooper::B_STARTED) { 538 HandleBuffer(event,lateness,realTimeEvent); 539 } 540 break; 541 case BTimedEventQueue::B_DATA_STATUS: 542 HandleDataStatus(event,lateness,realTimeEvent); 543 break; 544 case BTimedEventQueue::B_PARAMETER: 545 HandleParameter(event,lateness,realTimeEvent); 546 break; 547 default: 548 PRINT(" unknown event type: %ld\n",event->type); 549 break; 550 } 551 } 552 553 554 /* override to clean up custom events you have added to your queue */ 555 void AbstractFileInterfaceNode::CleanUpEvent( 556 const media_timed_event *event) 557 { 558 BMediaEventLooper::CleanUpEvent(event); 559 } 560 561 562 /* called from Offline mode to determine the current time of the node */ 563 /* update your internal information whenever it changes */ 564 bigtime_t AbstractFileInterfaceNode::OfflineTime() 565 { 566 CALLED(); 567 return BMediaEventLooper::OfflineTime(); 568 // XXX: do something else? 569 // if (inputFile == 0) { 570 // return 0; 571 // } else { 572 // return inputFile->Position(); 573 // } 574 } 575 576 577 /* override only if you know what you are doing! */ 578 /* otherwise much badness could occur */ 579 /* the actual control loop function: */ 580 /* waits for messages, Pops events off the queue and calls DispatchEvent */ 581 void AbstractFileInterfaceNode::ControlLoop() { 582 BMediaEventLooper::ControlLoop(); 583 } 584 585 586 // protected: 587 status_t AbstractFileInterfaceNode::HandleStart( 588 const media_timed_event *event, 589 bigtime_t lateness, 590 bool realTimeEvent) 591 { 592 CALLED(); 593 594 if (RunState() != B_STARTED) { 595 // XXX: Either use the following line or the lines that are not commented. 596 // There doesn't seem to be a practical difference that i can tell. 597 // HandleBuffer(event,lateness,realTimeEvent); 598 media_timed_event firstBufferEvent(event->event_time, BTimedEventQueue::B_HANDLE_BUFFER); 599 HandleEvent(&firstBufferEvent, 0, false); 600 EventQueue()->AddEvent(firstBufferEvent); 601 } 602 return B_OK; 603 } 604 605 606 status_t AbstractFileInterfaceNode::HandleSeek( 607 const media_timed_event *event, 608 bigtime_t lateness, 609 bool realTimeEvent) 610 { 611 PRINT("AbstractFileInterfaceNode::HandleSeek(t=%lld,d=%ld,bd=%lld)\n",event->event_time,event->data,event->bigdata); 612 613 if (fCurrentFile != 0) 614 return fCurrentFile->Seek(event->bigdata,SEEK_SET); 615 else 616 return B_ERROR; 617 } 618 619 620 status_t AbstractFileInterfaceNode::HandleWarp( 621 const media_timed_event *event, 622 bigtime_t lateness, 623 bool realTimeEvent) 624 { 625 CALLED(); 626 return B_OK; 627 } 628 629 630 status_t AbstractFileInterfaceNode::HandleStop( 631 const media_timed_event *event, 632 bigtime_t lateness, 633 bool realTimeEvent) 634 { 635 CALLED(); 636 637 // flush the queue so downstreamers don't get any more 638 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); 639 return B_OK; 640 } 641 642 643 status_t AbstractFileInterfaceNode::HandleParameter( 644 const media_timed_event *event, 645 bigtime_t lateness, 646 bool realTimeEvent) 647 { 648 CALLED(); 649 650 status_t status = B_OK; 651 652 bool chunkSizeUpdated = false, bitRateUpdated = false, bufferPeriodUpdated = false; 653 654 size_t dataSize = size_t(event->data); 655 int64 param = int64(event->bigdata); 656 657 switch (param) { 658 case DEFAULT_CHUNK_SIZE_PARAM: 659 PRINT("(DEFAULT_CHUNK_SIZE_PARAM,size=%ld",dataSize); 660 if (dataSize < sizeof(size_t)) { 661 PRINT("\ŧ<- B_BAD_VALUE: %lld\n",param); 662 status = B_BAD_VALUE; 663 } else { 664 size_t newDefaultChunkSize = *((size_t*)event->user_data); 665 PRINT(",%ld)\n", newDefaultChunkSize); 666 // ignore non positive chunk sizes 667 // XXX: we may decide later that a 0 chunk size means ship the whole file in one chunk (!) 668 if ((newDefaultChunkSize > 0) && (newDefaultChunkSize != fDefaultChunkSizeParam)) { 669 PRINT("\tgot a new chunk size, old chunk size was %ld\n",fDefaultChunkSizeParam); 670 fDefaultChunkSizeParam = newDefaultChunkSize; 671 fDefaultChunkSizeParamChangeTime = TimeSource()->Now(); 672 chunkSizeUpdated = true; 673 if (fLeastRecentlyUpdatedParameter == DEFAULT_CHUNK_SIZE_PARAM) { 674 // Okay we were the least recently updated parameter, 675 // but we just got an update so we are no longer that. 676 // Let's figure out who the new least recently updated 677 // parameter is. We are going to prefer to compute the 678 // bit rate since you usually don't want to muck with 679 // the buffer period. However, if you just set the bitrate 680 // then we are stuck with making the buffer period the new 681 // parameter to be computed. 682 if (fLastUpdatedParameter == DEFAULT_BIT_RATE_PARAM) 683 fLeastRecentlyUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM; 684 else 685 fLeastRecentlyUpdatedParameter = DEFAULT_BIT_RATE_PARAM; 686 } 687 // we just got an update, so we are the new lastUpdatedParameter 688 fLastUpdatedParameter = DEFAULT_CHUNK_SIZE_PARAM; 689 // now we have to compute the new value for the leastRecentlyUpdatedParameter 690 // we use the chunk size change time to preserve "simultaneity" information 691 if (fLeastRecentlyUpdatedParameter == DEFAULT_BUFFER_PERIOD_PARAM) { 692 int64 value = int64(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam); 693 if (value > INT_MAX) { 694 // clamp to INT_MAX 695 fDefaultBufferPeriodParam = INT_MAX; 696 // recompute chunk size 697 fDefaultChunkSizeParam = size_t(1024/8000000*fDefaultBitRateParam*fDefaultBufferPeriodParam); 698 } else { 699 fDefaultBufferPeriodParam = MAX(1,value); 700 } 701 fDefaultBufferPeriodParamChangeTime = fDefaultChunkSizeParamChangeTime; 702 bufferPeriodUpdated = true; 703 } else { // must have been bit rate 704 fDefaultBitRateParam = MAX(0.001,8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam); 705 fDefaultBitRateParamChangeTime = fDefaultChunkSizeParamChangeTime; 706 bitRateUpdated = true; 707 } 708 } 709 } 710 break; 711 case DEFAULT_BIT_RATE_PARAM: 712 PRINT("(DEFAULT_BIT_RATE_PARAM,size=%ld",dataSize); 713 if (dataSize < sizeof(float)) { 714 PRINT("\t<- B_BAD_VALUE:lld\n",param); 715 status = B_BAD_VALUE; 716 } else { 717 float newDefaultBitRate = *((float*)event->user_data); 718 PRINT(",%f)\n",newDefaultBitRate); 719 // ignore non positive bitrates 720 if ((newDefaultBitRate > 0) && (newDefaultBitRate != fDefaultBitRateParam)) { 721 PRINT("\tgot a new bit rate, old bit rate was %ld\n",fDefaultBitRateParam); 722 fDefaultBitRateParam = newDefaultBitRate; 723 fDefaultBitRateParamChangeTime = TimeSource()->Now(); 724 bitRateUpdated = true; 725 if (fLeastRecentlyUpdatedParameter == DEFAULT_BIT_RATE_PARAM) { 726 // Okay we were the least recently updated parameter, 727 // but we just got an update so we are no longer that. 728 // Let's figure out who the new least recently updated 729 // parameter is. We are going to prefer to compute the 730 // chunk size since you usually don't want to muck with 731 // the buffer period. However, if you just set the chunk size 732 // then we are stuck with making the buffer period the new 733 // parameter to be computed. 734 if (fLastUpdatedParameter == DEFAULT_CHUNK_SIZE_PARAM) { 735 fLeastRecentlyUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM; 736 } else { 737 fLeastRecentlyUpdatedParameter = DEFAULT_CHUNK_SIZE_PARAM; 738 } 739 } 740 // we just got an update, so we are the new lastUpdatedParameter 741 fLastUpdatedParameter = DEFAULT_BIT_RATE_PARAM; 742 // now we have to compute the new value for the leastRecentlyUpdatedParameter 743 // we use the bit rate change time to preserve "simultaneity" information 744 if (fLeastRecentlyUpdatedParameter == DEFAULT_BUFFER_PERIOD_PARAM) { 745 int64 value = 746 int64(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam); 747 if (value > INT_MAX) { 748 // clamp to INT_MAX 749 fDefaultBufferPeriodParam = INT_MAX; 750 // recompute bit rate 751 fDefaultBitRateParam = MAX(0.001, 752 8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam); 753 } else { 754 fDefaultBufferPeriodParam = MAX(1,int32(value)); 755 } 756 fDefaultBufferPeriodParamChangeTime = fDefaultBitRateParamChangeTime; 757 bufferPeriodUpdated = true; 758 } else { // must have been chunk size 759 int64 value = 760 int64(1024/8000000*fDefaultBitRateParam*fDefaultBufferPeriodParam); 761 if (value > INT_MAX) { 762 // clamp to INT_MAX 763 fDefaultChunkSizeParam = INT_MAX; 764 // recompute bit rate 765 fDefaultBitRateParam = 766 MAX(0.001,8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam); 767 } else { 768 fDefaultChunkSizeParam = MAX(1,int32(value)); 769 } 770 fDefaultChunkSizeParamChangeTime = fDefaultBitRateParamChangeTime; 771 chunkSizeUpdated = true; 772 } 773 } 774 } 775 break; 776 case DEFAULT_BUFFER_PERIOD_PARAM: 777 PRINT("(DEFAULT_BUFFER_PERIOD_PARAM,size=%ld",dataSize); 778 if (dataSize < sizeof(int32)) { 779 PRINT("\t<- B_BAD_VALUE:%ld\n",param); 780 status = B_BAD_VALUE; 781 } else { 782 int32 newBufferPeriod = *((int32*)event->user_data); 783 PRINT(",%ld)\n",newBufferPeriod); 784 // ignore non positive buffer period 785 if ((newBufferPeriod > 0) && (newBufferPeriod != fDefaultBufferPeriodParam)) { 786 PRINT("\tgot a new buffer period, old buffer period was %ld\n", 787 fDefaultBufferPeriodParam); 788 fDefaultBufferPeriodParam = newBufferPeriod; 789 fDefaultBufferPeriodParamChangeTime = TimeSource()->Now(); 790 bufferPeriodUpdated = true; 791 if (fLastUpdatedParameter == DEFAULT_BUFFER_PERIOD_PARAM) { 792 // prefer to update bit rate, unless you just set it 793 if (fLastUpdatedParameter == DEFAULT_BIT_RATE_PARAM) { 794 fLeastRecentlyUpdatedParameter = DEFAULT_CHUNK_SIZE_PARAM; 795 } else { 796 fLeastRecentlyUpdatedParameter = DEFAULT_BIT_RATE_PARAM; 797 } 798 } 799 // we just got an update, so we are the new lastUpdatedParameter 800 fLastUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM; 801 // now we have to compute the new value for the leastRecentlyUpdatedParameter 802 // we use the buffer period change time to preserve "simultaneity" information 803 if (fLeastRecentlyUpdatedParameter == DEFAULT_BIT_RATE_PARAM) { 804 fDefaultBitRateParam = 805 MAX(0.001,8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam); 806 fDefaultBitRateParamChangeTime = fDefaultBufferPeriodParamChangeTime; 807 bitRateUpdated = true; 808 } else { // must have been chunk size 809 int64 value = int64(1024/8000000*fDefaultBitRateParam*fDefaultBufferPeriodParam); 810 if (value > INT_MAX) { 811 // clamp to INT_MAX 812 fDefaultChunkSizeParam = INT_MAX; 813 // recompute buffer period 814 fDefaultBufferPeriodParam = 815 size_t(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam); 816 } else { 817 fDefaultChunkSizeParam = MAX(1,int32(value)); 818 } 819 fDefaultChunkSizeParamChangeTime = fDefaultBufferPeriodParamChangeTime; 820 chunkSizeUpdated = true; 821 } 822 } 823 } 824 break; 825 default: 826 PRINT("AbstractFileInterfaceNode::HandleParameter called with unknown param id (%ld)\n",param); 827 status = B_ERROR; 828 } 829 // send updates out for all the parameters that changed 830 // in every case this should be two updates. (if I have not made an error :-) ) 831 if (chunkSizeUpdated) { 832 PRINT("\tchunk size parameter updated\n"); 833 BroadcastNewParameterValue(fDefaultChunkSizeParamChangeTime, 834 DEFAULT_CHUNK_SIZE_PARAM, 835 &fDefaultChunkSizeParam, 836 sizeof(fDefaultChunkSizeParam)); 837 } 838 if (bitRateUpdated) { 839 PRINT("\tbit rate parameter updated\n"); 840 BroadcastNewParameterValue(fDefaultBitRateParamChangeTime, 841 DEFAULT_BIT_RATE_PARAM, 842 &fDefaultBitRateParam, 843 sizeof(fDefaultBitRateParam)); 844 } 845 if (bufferPeriodUpdated) { 846 PRINT("\tbuffer period parameter updated\n"); 847 BroadcastNewParameterValue(fDefaultBufferPeriodParamChangeTime, 848 DEFAULT_BUFFER_PERIOD_PARAM, 849 &fDefaultBufferPeriodParam, 850 sizeof(fDefaultBufferPeriodParam)); 851 } 852 return status; 853 } 854 855 856 // -------------------------------------------------------- // 857 // AbstractFileInterfaceNode specific functions 858 // -------------------------------------------------------- // 859 void AbstractFileInterfaceNode::GetFlavor(flavor_info * info, int32 id) 860 { 861 CALLED(); 862 863 if (info == 0) 864 return; 865 866 info->name = strdup("AbstractFileInterfaceNode"); 867 info->info = strdup("A AbstractFileInterfaceNode node handles a file."); 868 info->kinds = B_FILE_INTERFACE | B_CONTROLLABLE; 869 info->flavor_flags = B_FLAVOR_IS_LOCAL; 870 info->possible_count = INT_MAX; 871 info->in_format_count = 0; // no inputs 872 info->in_formats = 0; 873 info->out_format_count = 0; // no outputs 874 info->out_formats = 0; 875 info->internal_id = id; 876 return; 877 } 878 879 880 void AbstractFileInterfaceNode::GetFormat(media_format * outFormat) 881 { 882 CALLED(); 883 884 if (outFormat == 0) 885 return; 886 887 outFormat->type = B_MEDIA_MULTISTREAM; 888 outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; 889 outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; 890 outFormat->u.multistream = media_multistream_format::wildcard; 891 } 892 893 894 void AbstractFileInterfaceNode::GetFileFormat(media_file_format * outFileFormat) 895 { 896 CALLED(); 897 898 if (outFileFormat == 0) 899 return; 900 901 outFileFormat->capabilities = 902 media_file_format::B_PERFECTLY_SEEKABLE 903 | media_file_format::B_IMPERFECTLY_SEEKABLE 904 | media_file_format::B_KNOWS_ANYTHING; 905 /* I don't know what to initialize this to. (or if I should) */ 906 // format.id = 907 outFileFormat->family = B_ANY_FORMAT_FAMILY; 908 outFileFormat->version = 100; 909 // see media_file_format in <MediaDefs.h> for limits 910 strncpy(outFileFormat->mime_type,"",63); 911 outFileFormat->mime_type[63]='\0'; 912 strncpy(outFileFormat->pretty_name,"any media file format",63); 913 outFileFormat->pretty_name[63]='\0'; 914 strncpy(outFileFormat->short_name,"any",31); 915 outFileFormat->short_name[31]='\0'; 916 strncpy(outFileFormat->file_extension,"",7); 917 outFileFormat->file_extension[7]='\0'; 918 } 919 920 921 // protected: 922 // Here we make some guesses based on the file's mime type. 923 // We don't have enough information to add any other requirements. 924 // This function doesn't complain if you have already decided you want 925 // the stream to be considered a different one. (so you can say that you 926 // want to read that mpeg file as avi if you are so nutty.) 927 // 928 status_t AbstractFileInterfaceNode::AddRequirements(media_format * format) 929 { 930 if (strcmp("video/x-msvideo",f_current_mime_type) == 0) { 931 if (format->u.multistream.format == media_multistream_format::wildcard.format) { 932 format->u.multistream.format = media_multistream_format::B_AVI; 933 } 934 } else 935 if (strcmp("video/mpeg",f_current_mime_type) == 0) { 936 if (format->u.multistream.format == media_multistream_format::wildcard.format) { 937 format->u.multistream.format = media_multistream_format::B_MPEG1; 938 } 939 } else 940 if (strcmp("video/quicktime",f_current_mime_type) == 0) { 941 if (format->u.multistream.format == media_multistream_format::wildcard.format) { 942 format->u.multistream.format = media_multistream_format::B_QUICKTIME; 943 } 944 } else 945 if (strcmp("audio/x-mpeg",f_current_mime_type) == 0) { 946 if (format->u.multistream.format == media_multistream_format::wildcard.format) { 947 format->u.multistream.format = media_multistream_format::B_MPEG1; 948 } 949 } 950 return B_OK; 951 } 952 953 954 // We need some sort of bit rate and chunk size, so if the other guy 955 // didn't care, we'll use our own defaults. 956 status_t AbstractFileInterfaceNode::ResolveWildcards(media_format * format) 957 { 958 CALLED(); 959 // There isn't an unknown format. hmph. 960 // if (format->u.multistream.format == media_multistream_format::wildcard.format) { 961 // format->u.multistream.format = media_multistream_format::B_UNKNOWN; 962 // } 963 if (format->u.multistream.max_bit_rate == media_multistream_format::wildcard.max_bit_rate) { 964 format->u.multistream.max_bit_rate = fDefaultBitRateParam; 965 } 966 if (format->u.multistream.max_chunk_size == media_multistream_format::wildcard.max_chunk_size) { 967 format->u.multistream.max_chunk_size = fDefaultChunkSizeParam; 968 } 969 if (format->u.multistream.avg_bit_rate == media_multistream_format::wildcard.avg_bit_rate) { 970 format->u.multistream.avg_bit_rate = fDefaultBitRateParam; 971 } 972 if (format->u.multistream.avg_chunk_size == media_multistream_format::wildcard.avg_chunk_size) { 973 format->u.multistream.avg_chunk_size = fDefaultChunkSizeParam; 974 } 975 return B_OK; 976 } 977 978 979 // -------------------------------------------------------- // 980 // stuffing 981 // -------------------------------------------------------- // 982 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_0(void *) { return B_ERROR; } 983 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_1(void *) { return B_ERROR; } 984 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_2(void *) { return B_ERROR; } 985 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_3(void *) { return B_ERROR; } 986 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_4(void *) { return B_ERROR; } 987 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_5(void *) { return B_ERROR; } 988 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_6(void *) { return B_ERROR; } 989 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_7(void *) { return B_ERROR; } 990 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_8(void *) { return B_ERROR; } 991 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_9(void *) { return B_ERROR; } 992 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_10(void *) { return B_ERROR; } 993 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_11(void *) { return B_ERROR; } 994 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_12(void *) { return B_ERROR; } 995 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_13(void *) { return B_ERROR; } 996 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_14(void *) { return B_ERROR; } 997 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_15(void *) { return B_ERROR; } 998