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 // avoid crashes 308 if (out_format == 0) { 309 // no place to write! 310 PRINT("\t<- B_BAD_VALUE\n"); 311 return B_BAD_VALUE; 312 } 313 314 if (cookie != 0) { 315 // it's valid but they already got our 1 file format 316 if (*cookie != 0) { 317 PRINT("\t<- B_ERROR\n"); 318 return B_ERROR; 319 } 320 // so next time they won't get the same format again 321 *cookie = 1; 322 } 323 324 GetFileFormat(out_format); 325 return B_OK; 326 } 327 328 329 void AbstractFileInterfaceNode::DisposeFileFormatCookie( 330 int32 cookie) 331 { 332 CALLED(); 333 // nothing to do since our cookies are just integers 334 } 335 336 337 status_t AbstractFileInterfaceNode::GetDuration( 338 bigtime_t * out_time) 339 { 340 CALLED(); 341 if (out_time == 0) { 342 PRINT("\t<- B_BAD_VALUE\n"); 343 return B_BAD_VALUE; 344 } 345 346 if (fCurrentFile == 0) { 347 PRINT("\t<- B_NO_INIT\n"); 348 return B_NO_INIT; 349 } 350 351 return fCurrentFile->GetSize(out_time); 352 } 353 354 355 status_t AbstractFileInterfaceNode::SniffRef( 356 const entry_ref & file, 357 char * out_mime_type, /* 256 bytes */ 358 float * out_quality) 359 { 360 CALLED(); 361 return StaticSniffRef(file,out_mime_type,out_quality); 362 } 363 364 365 status_t AbstractFileInterfaceNode::SetRef( 366 const entry_ref & file, 367 uint32 openMode, 368 bool create, 369 bigtime_t * out_time) 370 { 371 CALLED(); 372 373 if (out_time == 0) { 374 PRINT("\t<- B_BAD_VALUE\n"); 375 return B_BAD_VALUE; // no crashes today thanks 376 } 377 378 status_t status; 379 f_current_ref = file; 380 if (fCurrentFile == 0) { 381 fCurrentFile = new BFile(&f_current_ref,(openMode|(create?B_CREATE_FILE:0))); 382 status = fCurrentFile->InitCheck(); 383 } else { 384 status = fCurrentFile->SetTo(&f_current_ref,(openMode|(create?B_CREATE_FILE:0))); 385 } 386 387 if (status != B_OK) { 388 PRINT("\t<- failed BFile initialization\n"); 389 return status; 390 } 391 392 // cache the mime type for later 393 fCurrentFile->ReadAttr("BEOS:TYPE",0,0,f_current_mime_type,B_MIME_TYPE_LENGTH); 394 // compute the duration and return any error 395 return GetDuration(out_time); 396 } 397 398 399 status_t AbstractFileInterfaceNode::GetRef( 400 entry_ref * out_ref, 401 char * out_mime_type) 402 { 403 CALLED(); 404 405 if ((out_ref == 0) || (out_mime_type == 0)) { 406 PRINT("\t<- B_BAD_VALUE\n"); 407 return B_BAD_VALUE; // avoid crash 408 } 409 410 if (fCurrentFile == 0) { 411 PRINT("\t<- B_NO_INIT\n"); 412 return B_NO_INIT; // the input_ref isn't valid yet either 413 } 414 415 *out_ref = f_current_ref; 416 // they hopefully allocated enough space (no way to check :-/ ) 417 strcpy(out_mime_type,f_current_mime_type); 418 return B_OK; 419 } 420 421 422 // provided for BAbstractFileInterfaceNodeAddOn 423 status_t AbstractFileInterfaceNode::StaticSniffRef( 424 const entry_ref & file, 425 char * out_mime_type, /* 256 bytes */ 426 float * out_quality) 427 { 428 CALLED(); 429 430 if ((out_mime_type == 0) || (out_quality == 0)) { 431 PRINT("\t<- B_BAD_VALUE\n"); 432 return B_BAD_VALUE; // avoid crash 433 } 434 435 BNode node(&file); 436 status_t initCheck = node.InitCheck(); 437 if (initCheck != B_OK) { 438 PRINT("\t<- failed BNode::InitCheck()\n"); 439 return initCheck; 440 } 441 442 // they hopefully allocated enough room 443 node.ReadAttr("BEOS:TYPE",0,0,out_mime_type,B_MIME_TYPE_LENGTH); 444 *out_quality = 1.0; // we handle all files perfectly! we are so amazing! 445 return B_OK; 446 } 447 448 449 // -------------------------------------------------------- // 450 // implementation for BControllable 451 // -------------------------------------------------------- // 452 const int32 AbstractFileInterfaceNode::DEFAULT_CHUNK_SIZE_PARAM = 1; 453 const int32 AbstractFileInterfaceNode::DEFAULT_BIT_RATE_PARAM = 2; 454 const int32 AbstractFileInterfaceNode::DEFAULT_BUFFER_PERIOD_PARAM = 3; 455 456 457 status_t AbstractFileInterfaceNode::GetParameterValue( 458 int32 id, 459 bigtime_t * last_change, 460 void * value, 461 size_t * ioSize) 462 { 463 CALLED(); 464 465 if ((last_change == 0) || (value == 0) || (ioSize == 0)) 466 return B_BAD_VALUE; // no crashing 467 468 switch (id) { 469 case DEFAULT_CHUNK_SIZE_PARAM: 470 if (*ioSize < sizeof(size_t)) { 471 return B_ERROR; // not enough room 472 } 473 *last_change = fDefaultChunkSizeParamChangeTime; 474 *((size_t*)value) = fDefaultChunkSizeParam; 475 *ioSize = sizeof(size_t); 476 break; 477 478 case DEFAULT_BIT_RATE_PARAM: 479 if (*ioSize < sizeof(float)) { 480 return B_ERROR; // not enough room 481 } 482 *last_change = fDefaultBitRateParamChangeTime; 483 *((float*)value) = fDefaultBitRateParam; 484 *ioSize = sizeof(float); 485 break; 486 487 case DEFAULT_BUFFER_PERIOD_PARAM: 488 if (*ioSize < sizeof(int32)) { 489 return B_ERROR; // not enough room 490 } 491 *last_change = fDefaultBufferPeriodParamChangeTime; 492 *((int32*)value) = fDefaultBufferPeriodParam; 493 *ioSize = sizeof(int32); 494 break; 495 496 default: 497 PRINT("AbstractFileInterfaceNode::GetParameterValue unknown id (%ld)\n",id); 498 return B_ERROR; 499 } 500 501 return B_OK; 502 } 503 504 505 void AbstractFileInterfaceNode::SetParameterValue( 506 int32 id, 507 bigtime_t when, 508 const void * value, 509 size_t size) 510 { 511 PRINT("AbstractFileInterfaceNode::SetParameterValue(id=%ld,when=%lld,size=%ld)\n",id,when,int32(size)); 512 513 switch (id) { 514 case DEFAULT_CHUNK_SIZE_PARAM: 515 case DEFAULT_BIT_RATE_PARAM: 516 case DEFAULT_BUFFER_PERIOD_PARAM: 517 { 518 media_timed_event event(when, BTimedEventQueue::B_PARAMETER, 519 NULL, BTimedEventQueue::B_NO_CLEANUP, 520 size, id, (char*) value, size); 521 EventQueue()->AddEvent(event); 522 } 523 break; 524 525 default: 526 PRINT("AbstractFileInterfaceNode::SetParameterValue unknown id (%ld)\n",id); 527 break; 528 } 529 } 530 531 532 // the default implementation should call the add-on main() 533 status_t AbstractFileInterfaceNode::StartControlPanel( 534 BMessenger * out_messenger) 535 { 536 CALLED(); 537 return BControllable::StartControlPanel(out_messenger); 538 } 539 540 541 // -------------------------------------------------------- // 542 // implementation for BMediaEventLooper 543 // -------------------------------------------------------- // 544 void AbstractFileInterfaceNode::HandleEvent( 545 const media_timed_event *event, 546 bigtime_t lateness, 547 bool realTimeEvent) 548 { 549 CALLED(); 550 switch (event->type) { 551 case BTimedEventQueue::B_START: 552 HandleStart(event,lateness,realTimeEvent); 553 break; 554 case BTimedEventQueue::B_SEEK: 555 HandleSeek(event,lateness,realTimeEvent); 556 break; 557 case BTimedEventQueue::B_WARP: 558 HandleWarp(event,lateness,realTimeEvent); 559 break; 560 case BTimedEventQueue::B_STOP: 561 HandleStop(event,lateness,realTimeEvent); 562 break; 563 case BTimedEventQueue::B_HANDLE_BUFFER: 564 if (RunState() == BMediaEventLooper::B_STARTED) { 565 HandleBuffer(event,lateness,realTimeEvent); 566 } 567 break; 568 case BTimedEventQueue::B_DATA_STATUS: 569 HandleDataStatus(event,lateness,realTimeEvent); 570 break; 571 case BTimedEventQueue::B_PARAMETER: 572 HandleParameter(event,lateness,realTimeEvent); 573 break; 574 default: 575 PRINT(" unknown event type: %ld\n",event->type); 576 break; 577 } 578 } 579 580 581 /* override to clean up custom events you have added to your queue */ 582 void AbstractFileInterfaceNode::CleanUpEvent( 583 const media_timed_event *event) 584 { 585 BMediaEventLooper::CleanUpEvent(event); 586 } 587 588 589 /* called from Offline mode to determine the current time of the node */ 590 /* update your internal information whenever it changes */ 591 bigtime_t AbstractFileInterfaceNode::OfflineTime() 592 { 593 CALLED(); 594 return BMediaEventLooper::OfflineTime(); 595 // XXX: do something else? 596 // if (inputFile == 0) { 597 // return 0; 598 // } else { 599 // return inputFile->Position(); 600 // } 601 } 602 603 604 /* override only if you know what you are doing! */ 605 /* otherwise much badness could occur */ 606 /* the actual control loop function: */ 607 /* waits for messages, Pops events off the queue and calls DispatchEvent */ 608 void AbstractFileInterfaceNode::ControlLoop() { 609 BMediaEventLooper::ControlLoop(); 610 } 611 612 613 // protected: 614 status_t AbstractFileInterfaceNode::HandleStart( 615 const media_timed_event *event, 616 bigtime_t lateness, 617 bool realTimeEvent) 618 { 619 CALLED(); 620 621 if (RunState() != B_STARTED) { 622 // XXX: Either use the following line or the lines that are not commented. 623 // There doesn't seem to be a practical difference that i can tell. 624 // HandleBuffer(event,lateness,realTimeEvent); 625 media_timed_event firstBufferEvent(event->event_time, BTimedEventQueue::B_HANDLE_BUFFER); 626 HandleEvent(&firstBufferEvent, 0, false); 627 EventQueue()->AddEvent(firstBufferEvent); 628 } 629 return B_OK; 630 } 631 632 633 status_t AbstractFileInterfaceNode::HandleSeek( 634 const media_timed_event *event, 635 bigtime_t lateness, 636 bool realTimeEvent) 637 { 638 PRINT("AbstractFileInterfaceNode::HandleSeek(t=%lld,d=%ld,bd=%lld)\n",event->event_time,event->data,event->bigdata); 639 640 if (fCurrentFile != 0) 641 return fCurrentFile->Seek(event->bigdata,SEEK_SET); 642 else 643 return B_ERROR; 644 } 645 646 647 status_t AbstractFileInterfaceNode::HandleWarp( 648 const media_timed_event *event, 649 bigtime_t lateness, 650 bool realTimeEvent) 651 { 652 CALLED(); 653 return B_OK; 654 } 655 656 657 status_t AbstractFileInterfaceNode::HandleStop( 658 const media_timed_event *event, 659 bigtime_t lateness, 660 bool realTimeEvent) 661 { 662 CALLED(); 663 664 // flush the queue so downstreamers don't get any more 665 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); 666 return B_OK; 667 } 668 669 670 status_t AbstractFileInterfaceNode::HandleParameter( 671 const media_timed_event *event, 672 bigtime_t lateness, 673 bool realTimeEvent) 674 { 675 CALLED(); 676 677 status_t status = B_OK; 678 679 bool chunkSizeUpdated = false, bitRateUpdated = false, bufferPeriodUpdated = false; 680 681 size_t dataSize = size_t(event->data); 682 int64 param = int64(event->bigdata); 683 684 switch (param) { 685 case DEFAULT_CHUNK_SIZE_PARAM: 686 PRINT("(DEFAULT_CHUNK_SIZE_PARAM,size=%ld",dataSize); 687 if (dataSize < sizeof(size_t)) { 688 PRINT("\ŧ<- B_BAD_VALUE: %lld\n",param); 689 status = B_BAD_VALUE; 690 } else { 691 size_t newDefaultChunkSize = *((size_t*)event->user_data); 692 PRINT(",%ld)\n", newDefaultChunkSize); 693 // ignore non positive chunk sizes 694 // XXX: we may decide later that a 0 chunk size means ship the whole file in one chunk (!) 695 if ((newDefaultChunkSize > 0) && (newDefaultChunkSize != fDefaultChunkSizeParam)) { 696 PRINT("\tgot a new chunk size, old chunk size was %ld\n",fDefaultChunkSizeParam); 697 fDefaultChunkSizeParam = newDefaultChunkSize; 698 fDefaultChunkSizeParamChangeTime = TimeSource()->Now(); 699 chunkSizeUpdated = true; 700 if (fLeastRecentlyUpdatedParameter == DEFAULT_CHUNK_SIZE_PARAM) { 701 // Okay we were the least recently updated parameter, 702 // but we just got an update so we are no longer that. 703 // Let's figure out who the new least recently updated 704 // parameter is. We are going to prefer to compute the 705 // bit rate since you usually don't want to muck with 706 // the buffer period. However, if you just set the bitrate 707 // then we are stuck with making the buffer period the new 708 // parameter to be computed. 709 if (fLastUpdatedParameter == DEFAULT_BIT_RATE_PARAM) 710 fLeastRecentlyUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM; 711 else 712 fLeastRecentlyUpdatedParameter = DEFAULT_BIT_RATE_PARAM; 713 } 714 // we just got an update, so we are the new lastUpdatedParameter 715 fLastUpdatedParameter = DEFAULT_CHUNK_SIZE_PARAM; 716 // now we have to compute the new value for the leastRecentlyUpdatedParameter 717 // we use the chunk size change time to preserve "simultaneity" information 718 if (fLeastRecentlyUpdatedParameter == DEFAULT_BUFFER_PERIOD_PARAM) { 719 int64 value = int64(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam); 720 if (value > INT_MAX) { 721 // clamp to INT_MAX 722 fDefaultBufferPeriodParam = INT_MAX; 723 // recompute chunk size 724 fDefaultChunkSizeParam = size_t(1024/8000000*fDefaultBitRateParam*fDefaultBufferPeriodParam); 725 } else { 726 fDefaultBufferPeriodParam = MAX(1,value); 727 } 728 fDefaultBufferPeriodParamChangeTime = fDefaultChunkSizeParamChangeTime; 729 bufferPeriodUpdated = true; 730 } else { // must have been bit rate 731 fDefaultBitRateParam = MAX(0.001,8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam); 732 fDefaultBitRateParamChangeTime = fDefaultChunkSizeParamChangeTime; 733 bitRateUpdated = true; 734 } 735 } 736 } 737 break; 738 case DEFAULT_BIT_RATE_PARAM: 739 PRINT("(DEFAULT_BIT_RATE_PARAM,size=%ld",dataSize); 740 if (dataSize < sizeof(float)) { 741 PRINT("\t<- B_BAD_VALUE:lld\n",param); 742 status = B_BAD_VALUE; 743 } else { 744 float newDefaultBitRate = *((float*)event->user_data); 745 PRINT(",%f)\n",newDefaultBitRate); 746 // ignore non positive bitrates 747 if ((newDefaultBitRate > 0) && (newDefaultBitRate != fDefaultBitRateParam)) { 748 PRINT("\tgot a new bit rate, old bit rate was %ld\n",fDefaultBitRateParam); 749 fDefaultBitRateParam = newDefaultBitRate; 750 fDefaultBitRateParamChangeTime = TimeSource()->Now(); 751 bitRateUpdated = true; 752 if (fLeastRecentlyUpdatedParameter == DEFAULT_BIT_RATE_PARAM) { 753 // Okay we were the least recently updated parameter, 754 // but we just got an update so we are no longer that. 755 // Let's figure out who the new least recently updated 756 // parameter is. We are going to prefer to compute the 757 // chunk size since you usually don't want to muck with 758 // the buffer period. However, if you just set the chunk size 759 // then we are stuck with making the buffer period the new 760 // parameter to be computed. 761 if (fLastUpdatedParameter == DEFAULT_CHUNK_SIZE_PARAM) { 762 fLeastRecentlyUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM; 763 } else { 764 fLeastRecentlyUpdatedParameter = DEFAULT_CHUNK_SIZE_PARAM; 765 } 766 } 767 // we just got an update, so we are the new lastUpdatedParameter 768 fLastUpdatedParameter = DEFAULT_BIT_RATE_PARAM; 769 // now we have to compute the new value for the leastRecentlyUpdatedParameter 770 // we use the bit rate change time to preserve "simultaneity" information 771 if (fLeastRecentlyUpdatedParameter == DEFAULT_BUFFER_PERIOD_PARAM) { 772 int64 value = 773 int64(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam); 774 if (value > INT_MAX) { 775 // clamp to INT_MAX 776 fDefaultBufferPeriodParam = INT_MAX; 777 // recompute bit rate 778 fDefaultBitRateParam = MAX(0.001, 779 8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam); 780 } else { 781 fDefaultBufferPeriodParam = MAX(1,int32(value)); 782 } 783 fDefaultBufferPeriodParamChangeTime = fDefaultBitRateParamChangeTime; 784 bufferPeriodUpdated = true; 785 } else { // must have been chunk size 786 int64 value = 787 int64(1024/8000000*fDefaultBitRateParam*fDefaultBufferPeriodParam); 788 if (value > INT_MAX) { 789 // clamp to INT_MAX 790 fDefaultChunkSizeParam = INT_MAX; 791 // recompute bit rate 792 fDefaultBitRateParam = 793 MAX(0.001,8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam); 794 } else { 795 fDefaultChunkSizeParam = MAX(1,int32(value)); 796 } 797 fDefaultChunkSizeParamChangeTime = fDefaultBitRateParamChangeTime; 798 chunkSizeUpdated = true; 799 } 800 } 801 } 802 break; 803 case DEFAULT_BUFFER_PERIOD_PARAM: 804 PRINT("(DEFAULT_BUFFER_PERIOD_PARAM,size=%ld",dataSize); 805 if (dataSize < sizeof(int32)) { 806 PRINT("\t<- B_BAD_VALUE:%ld\n",param); 807 status = B_BAD_VALUE; 808 } else { 809 int32 newBufferPeriod = *((int32*)event->user_data); 810 PRINT(",%ld)\n",newBufferPeriod); 811 // ignore non positive buffer period 812 if ((newBufferPeriod > 0) && (newBufferPeriod != fDefaultBufferPeriodParam)) { 813 PRINT("\tgot a new buffer period, old buffer period was %ld\n", 814 fDefaultBufferPeriodParam); 815 fDefaultBufferPeriodParam = newBufferPeriod; 816 fDefaultBufferPeriodParamChangeTime = TimeSource()->Now(); 817 bufferPeriodUpdated = true; 818 if (fLastUpdatedParameter == DEFAULT_BUFFER_PERIOD_PARAM) { 819 // prefer to update bit rate, unless you just set it 820 if (fLastUpdatedParameter == DEFAULT_BIT_RATE_PARAM) { 821 fLeastRecentlyUpdatedParameter = DEFAULT_CHUNK_SIZE_PARAM; 822 } else { 823 fLeastRecentlyUpdatedParameter = DEFAULT_BIT_RATE_PARAM; 824 } 825 } 826 // we just got an update, so we are the new lastUpdatedParameter 827 fLastUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM; 828 // now we have to compute the new value for the leastRecentlyUpdatedParameter 829 // we use the buffer period change time to preserve "simultaneity" information 830 if (fLeastRecentlyUpdatedParameter == DEFAULT_BIT_RATE_PARAM) { 831 fDefaultBitRateParam = 832 MAX(0.001,8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam); 833 fDefaultBitRateParamChangeTime = fDefaultBufferPeriodParamChangeTime; 834 bitRateUpdated = true; 835 } else { // must have been chunk size 836 int64 value = int64(1024/8000000*fDefaultBitRateParam*fDefaultBufferPeriodParam); 837 if (value > INT_MAX) { 838 // clamp to INT_MAX 839 fDefaultChunkSizeParam = INT_MAX; 840 // recompute buffer period 841 fDefaultBufferPeriodParam = 842 size_t(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam); 843 } else { 844 fDefaultChunkSizeParam = MAX(1,int32(value)); 845 } 846 fDefaultChunkSizeParamChangeTime = fDefaultBufferPeriodParamChangeTime; 847 chunkSizeUpdated = true; 848 } 849 } 850 } 851 break; 852 default: 853 PRINT("AbstractFileInterfaceNode::HandleParameter called with unknown param id (%ld)\n",param); 854 status = B_ERROR; 855 } 856 // send updates out for all the parameters that changed 857 // in every case this should be two updates. (if I have not made an error :-) ) 858 if (chunkSizeUpdated) { 859 PRINT("\tchunk size parameter updated\n"); 860 BroadcastNewParameterValue(fDefaultChunkSizeParamChangeTime, 861 DEFAULT_CHUNK_SIZE_PARAM, 862 &fDefaultChunkSizeParam, 863 sizeof(fDefaultChunkSizeParam)); 864 } 865 if (bitRateUpdated) { 866 PRINT("\tbit rate parameter updated\n"); 867 BroadcastNewParameterValue(fDefaultBitRateParamChangeTime, 868 DEFAULT_BIT_RATE_PARAM, 869 &fDefaultBitRateParam, 870 sizeof(fDefaultBitRateParam)); 871 } 872 if (bufferPeriodUpdated) { 873 PRINT("\tbuffer period parameter updated\n"); 874 BroadcastNewParameterValue(fDefaultBufferPeriodParamChangeTime, 875 DEFAULT_BUFFER_PERIOD_PARAM, 876 &fDefaultBufferPeriodParam, 877 sizeof(fDefaultBufferPeriodParam)); 878 } 879 return status; 880 } 881 882 883 // -------------------------------------------------------- // 884 // AbstractFileInterfaceNode specific functions 885 // -------------------------------------------------------- // 886 void AbstractFileInterfaceNode::GetFlavor(flavor_info * info, int32 id) 887 { 888 CALLED(); 889 890 if (info == 0) 891 return; 892 893 info->name = strdup("AbstractFileInterfaceNode"); 894 info->info = strdup("A AbstractFileInterfaceNode node handles a file."); 895 info->kinds = B_FILE_INTERFACE | B_CONTROLLABLE; 896 info->flavor_flags = B_FLAVOR_IS_LOCAL; 897 info->possible_count = INT_MAX; 898 info->in_format_count = 0; // no inputs 899 info->in_formats = 0; 900 info->out_format_count = 0; // no outputs 901 info->out_formats = 0; 902 info->internal_id = id; 903 return; 904 } 905 906 907 void AbstractFileInterfaceNode::GetFormat(media_format * outFormat) 908 { 909 CALLED(); 910 911 if (outFormat == 0) 912 return; 913 914 outFormat->type = B_MEDIA_MULTISTREAM; 915 outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; 916 outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; 917 outFormat->u.multistream = media_multistream_format::wildcard; 918 } 919 920 921 void AbstractFileInterfaceNode::GetFileFormat(media_file_format * outFileFormat) 922 { 923 CALLED(); 924 925 if (outFileFormat == 0) 926 return; 927 928 outFileFormat->capabilities = 929 media_file_format::B_PERFECTLY_SEEKABLE 930 | media_file_format::B_IMPERFECTLY_SEEKABLE 931 | media_file_format::B_KNOWS_ANYTHING; 932 /* I don't know what to initialize this to. (or if I should) */ 933 // format.id = 934 outFileFormat->family = B_ANY_FORMAT_FAMILY; 935 outFileFormat->version = 100; 936 // see media_file_format in <MediaDefs.h> for limits 937 strncpy(outFileFormat->mime_type,"",63); 938 outFileFormat->mime_type[63]='\0'; 939 strncpy(outFileFormat->pretty_name,"any media file format",63); 940 outFileFormat->pretty_name[63]='\0'; 941 strncpy(outFileFormat->short_name,"any",31); 942 outFileFormat->short_name[31]='\0'; 943 strncpy(outFileFormat->file_extension,"",7); 944 outFileFormat->file_extension[7]='\0'; 945 } 946 947 948 // protected: 949 // Here we make some guesses based on the file's mime type. 950 // We don't have enough information to add any other requirements. 951 // This function doesn't complain if you have already decided you want 952 // the stream to be considered a different one. (so you can say that you 953 // want to read that mpeg file as avi if you are so nutty.) 954 // 955 status_t AbstractFileInterfaceNode::AddRequirements(media_format * format) 956 { 957 if (strcmp("video/x-msvideo",f_current_mime_type) == 0) { 958 if (format->u.multistream.format == media_multistream_format::wildcard.format) { 959 format->u.multistream.format = media_multistream_format::B_AVI; 960 } 961 } else 962 if (strcmp("video/mpeg",f_current_mime_type) == 0) { 963 if (format->u.multistream.format == media_multistream_format::wildcard.format) { 964 format->u.multistream.format = media_multistream_format::B_MPEG1; 965 } 966 } else 967 if (strcmp("video/quicktime",f_current_mime_type) == 0) { 968 if (format->u.multistream.format == media_multistream_format::wildcard.format) { 969 format->u.multistream.format = media_multistream_format::B_QUICKTIME; 970 } 971 } else 972 if (strcmp("audio/x-mpeg",f_current_mime_type) == 0) { 973 if (format->u.multistream.format == media_multistream_format::wildcard.format) { 974 format->u.multistream.format = media_multistream_format::B_MPEG1; 975 } 976 } 977 return B_OK; 978 } 979 980 981 // We need some sort of bit rate and chunk size, so if the other guy 982 // didn't care, we'll use our own defaults. 983 status_t AbstractFileInterfaceNode::ResolveWildcards(media_format * format) 984 { 985 CALLED(); 986 // There isn't an unknown format. hmph. 987 // if (format->u.multistream.format == media_multistream_format::wildcard.format) { 988 // format->u.multistream.format = media_multistream_format::B_UNKNOWN; 989 // } 990 if (format->u.multistream.max_bit_rate == media_multistream_format::wildcard.max_bit_rate) { 991 format->u.multistream.max_bit_rate = fDefaultBitRateParam; 992 } 993 if (format->u.multistream.max_chunk_size == media_multistream_format::wildcard.max_chunk_size) { 994 format->u.multistream.max_chunk_size = fDefaultChunkSizeParam; 995 } 996 if (format->u.multistream.avg_bit_rate == media_multistream_format::wildcard.avg_bit_rate) { 997 format->u.multistream.avg_bit_rate = fDefaultBitRateParam; 998 } 999 if (format->u.multistream.avg_chunk_size == media_multistream_format::wildcard.avg_chunk_size) { 1000 format->u.multistream.avg_chunk_size = fDefaultChunkSizeParam; 1001 } 1002 return B_OK; 1003 } 1004 1005 1006 // -------------------------------------------------------- // 1007 // stuffing 1008 // -------------------------------------------------------- // 1009 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_0(void *) { return B_ERROR; } 1010 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_1(void *) { return B_ERROR; } 1011 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_2(void *) { return B_ERROR; } 1012 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_3(void *) { return B_ERROR; } 1013 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_4(void *) { return B_ERROR; } 1014 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_5(void *) { return B_ERROR; } 1015 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_6(void *) { return B_ERROR; } 1016 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_7(void *) { return B_ERROR; } 1017 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_8(void *) { return B_ERROR; } 1018 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_9(void *) { return B_ERROR; } 1019 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_10(void *) { return B_ERROR; } 1020 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_11(void *) { return B_ERROR; } 1021 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_12(void *) { return B_ERROR; } 1022 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_13(void *) { return B_ERROR; } 1023 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_14(void *) { return B_ERROR; } 1024 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_15(void *) { return B_ERROR; } 1025