1 /* 2 * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files or portions 6 * thereof (the "Software"), to deal in the Software without restriction, 7 * including without limitation the rights to use, copy, modify, merge, 8 * publish, distribute, sublicense, and/or sell copies of the Software, 9 * and to permit persons to whom the Software is furnished to do so, subject 10 * to the following conditions: 11 * 12 * * Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * * Redistributions in binary form must reproduce the above copyright notice 16 * in the binary, as well as this list of conditions and the following 17 * disclaimer in the documentation and/or other materials provided with 18 * the distribution. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 * THE SOFTWARE. 27 * 28 */ 29 30 #include <MediaEventLooper.h> 31 #include <TimeSource.h> 32 #include <scheduler.h> 33 #include <Buffer.h> 34 #include <ServerInterface.h> 35 #include "debug.h" 36 37 /************************************************************* 38 * protected BMediaEventLooper 39 *************************************************************/ 40 41 /* virtual */ 42 BMediaEventLooper::~BMediaEventLooper() 43 { 44 CALLED(); 45 46 // don't call Quit(); here, except if the user was stupid 47 if (fControlThread != -1) { 48 printf("You MUST call BMediaEventLooper::Quit() in your destructor!\n"); 49 Quit(); 50 } 51 } 52 53 /* explicit */ 54 BMediaEventLooper::BMediaEventLooper(uint32 apiVersion) : 55 BMediaNode("called by BMediaEventLooper"), 56 fControlThread(-1), 57 fCurrentPriority(B_URGENT_PRIORITY), 58 fSetPriority(B_URGENT_PRIORITY), 59 fRunState(B_UNREGISTERED), 60 fEventLatency(0), 61 fSchedulingLatency(0), 62 fBufferDuration(0), 63 fOfflineTime(0), 64 fApiVersion(apiVersion) 65 { 66 CALLED(); 67 fEventQueue.SetCleanupHook(BMediaEventLooper::_CleanUpEntry, this); 68 fRealTimeQueue.SetCleanupHook(BMediaEventLooper::_CleanUpEntry, this); 69 } 70 71 /* virtual */ void 72 BMediaEventLooper::NodeRegistered() 73 { 74 CALLED(); 75 // Calling Run() should be done by the derived class, 76 // at least that's how it is documented by the BeBook. 77 // It appears that BeOS R5 called it here. Calling Run() 78 // twice doesn't hurt, and some nodes need it to be called here. 79 Run(); 80 } 81 82 83 /* virtual */ void 84 BMediaEventLooper::Start(bigtime_t performance_time) 85 { 86 CALLED(); 87 // This hook function is called when a node is started 88 // by a call to the BMediaRoster. The specified 89 // performanceTime, the time at which the node 90 // should start running, may be in the future. 91 fEventQueue.AddEvent(media_timed_event(performance_time, BTimedEventQueue::B_START)); 92 } 93 94 95 /* virtual */ void 96 BMediaEventLooper::Stop(bigtime_t performance_time, 97 bool immediate) 98 { 99 CALLED(); 100 // This hook function is called when a node is stopped 101 // by a call to the BMediaRoster. The specified performanceTime, 102 // the time at which the node should stop, may be in the future. 103 // If immediate is true, your node should ignore the performanceTime 104 // value and synchronously stop performance. When Stop() returns, 105 // you're promising not to write into any BBuffers you may have 106 // received from your downstream consumers, and you promise not 107 // to send any more buffers until Start() is called again. 108 109 if (immediate) { 110 // always be sure to add to the front of the queue so we can make sure it is 111 // handled before any buffers are sent! 112 performance_time = 0; 113 } 114 fEventQueue.AddEvent(media_timed_event(performance_time, BTimedEventQueue::B_STOP)); 115 } 116 117 118 /* virtual */ void 119 BMediaEventLooper::Seek(bigtime_t media_time, 120 bigtime_t performance_time) 121 { 122 CALLED(); 123 // This hook function is called when a node is asked to seek to 124 // the specified mediaTime by a call to the BMediaRoster. 125 // The specified performanceTime, the time at which the node 126 // should begin the seek operation, may be in the future. 127 fEventQueue.AddEvent(media_timed_event(performance_time, BTimedEventQueue::B_SEEK, NULL, 128 BTimedEventQueue::B_NO_CLEANUP, 0, media_time, NULL)); 129 } 130 131 132 /* virtual */ void 133 BMediaEventLooper::TimeWarp(bigtime_t at_real_time, 134 bigtime_t to_performance_time) 135 { 136 CALLED(); 137 // This hook function is called when the time source to which the 138 // node is slaved is repositioned (via a seek operation) such that 139 // there will be a sudden jump in the performance time progression 140 // as seen by the node. The to_performance_time argument indicates 141 // the new performance time; the change should occur at the real 142 // time specified by the at_real_time argument. 143 144 // place in the realtime queue 145 fRealTimeQueue.AddEvent(media_timed_event(at_real_time, BTimedEventQueue::B_WARP, 146 NULL, BTimedEventQueue::B_NO_CLEANUP, 0, to_performance_time, NULL)); 147 148 // BeBook: Your implementation of TimeWarp() should call through to BMediaNode::TimeWarp() 149 // BeBook: as well as all other inherited forms of TimeWarp() 150 // XXX should we do this here? 151 BMediaNode::TimeWarp(at_real_time, to_performance_time); 152 } 153 154 155 /* virtual */ status_t 156 BMediaEventLooper::AddTimer(bigtime_t at_performance_time, 157 int32 cookie) 158 { 159 CALLED(); 160 161 media_timed_event event(at_performance_time, 162 BTimedEventQueue::B_TIMER, NULL, 163 BTimedEventQueue::B_EXPIRE_TIMER); 164 event.data = cookie; 165 return EventQueue()->AddEvent(event); 166 } 167 168 169 /* virtual */ void 170 BMediaEventLooper::SetRunMode(run_mode mode) 171 { 172 CALLED(); 173 // The SetRunMode() hook function is called when someone requests that your node's run mode be changed. 174 175 // bump or reduce priority when switching from/to offline run mode 176 int32 priority; 177 priority = (mode == B_OFFLINE) ? min_c(B_NORMAL_PRIORITY, fSetPriority) : fSetPriority; 178 if (priority != fCurrentPriority) { 179 fCurrentPriority = priority; 180 if (fControlThread > 0) { 181 set_thread_priority(fControlThread, fCurrentPriority); 182 fSchedulingLatency = estimate_max_scheduling_latency(fControlThread); 183 printf("BMediaEventLooper: SchedulingLatency is %" B_PRId64 "\n", 184 fSchedulingLatency); 185 } 186 } 187 188 BMediaNode::SetRunMode(mode); 189 } 190 191 192 /* virtual */ void 193 BMediaEventLooper::CleanUpEvent(const media_timed_event *event) 194 { 195 CALLED(); 196 // Implement this function to clean up after custom events you've created 197 // and added to your queue. It's called when a custom event is removed from 198 // the queue, to let you handle any special tidying-up that the event might require. 199 } 200 201 202 /* virtual */ bigtime_t 203 BMediaEventLooper::OfflineTime() 204 { 205 CALLED(); 206 return fOfflineTime; 207 } 208 209 210 /* virtual */ void 211 BMediaEventLooper::ControlLoop() 212 { 213 CALLED(); 214 215 bool is_realtime = false; 216 status_t err; 217 bigtime_t latency; 218 bigtime_t waituntil; 219 bigtime_t lateness; 220 for (;;) { 221 // while there are no events or it is not time for the earliest event, 222 // process messages using WaitForMessages. Whenever this funtion times out, 223 // we need to handle the next event 224 for (;;) { 225 if (RunState() == B_QUITTING) 226 return; 227 // BMediaEventLooper compensates your performance time by adding the event latency 228 // (see SetEventLatency()) and the scheduling latency (or, for real-time events, 229 // only the scheduling latency). 230 231 latency = fEventLatency + fSchedulingLatency; 232 waituntil = B_INFINITE_TIMEOUT; 233 if (fEventQueue.HasEvents()) { 234 const media_timed_event *firstEvent = fEventQueue.FirstEvent(); 235 waituntil = TimeSource()->RealTimeFor(firstEvent->event_time, latency); 236 is_realtime = false; 237 lateness = firstEvent->queued_time - waituntil; 238 if (lateness > 0) { 239 // if (lateness > 1000) 240 // printf("node %02ld handling %12Ld at %12Ld -- %Ld late, queued at %Ld now %12Ld \n", 241 // ID(), fEventQueue.FirstEventTime(), TimeSource()->Now(), lateness, 242 // firstEvent->queued_time, TimeSource()->RealTime()); 243 is_realtime = false; 244 break; 245 } 246 // printf("node %02ld waiting for %12Ld that will happen at %12Ld\n", ID(), fEventQueue.FirstEventTime(), waituntil); 247 } 248 if (fRealTimeQueue.HasEvents()) { 249 const media_timed_event *firstEvent = fRealTimeQueue.FirstEvent(); 250 bigtime_t temp; 251 temp = firstEvent->event_time - fSchedulingLatency; 252 lateness = firstEvent->queued_time - temp; 253 if (lateness > 0) { 254 is_realtime = true; 255 break; 256 } 257 if (temp < waituntil) { 258 waituntil = temp; 259 is_realtime = true; 260 } 261 } 262 lateness = 0; // remove any extraneous value if we get this far 263 err = WaitForMessage(waituntil); 264 if (err == B_TIMED_OUT) 265 break; 266 } 267 /// we have timed out - so handle the next event 268 media_timed_event event; 269 if (is_realtime) 270 err = fRealTimeQueue.RemoveFirstEvent(&event); 271 else 272 err = fEventQueue.RemoveFirstEvent(&event); 273 274 // printf("node %02ld handling %12Ld at %12Ld\n", ID(), event.event_time, TimeSource()->Now()); 275 276 if (err == B_OK) DispatchEvent(&event, lateness, is_realtime); 277 } 278 } 279 280 281 thread_id 282 BMediaEventLooper::ControlThread() 283 { 284 CALLED(); 285 return fControlThread; 286 } 287 288 /************************************************************* 289 * protected BMediaEventLooper 290 *************************************************************/ 291 292 293 BTimedEventQueue * 294 BMediaEventLooper::EventQueue() 295 { 296 CALLED(); 297 return &fEventQueue; 298 } 299 300 301 BTimedEventQueue * 302 BMediaEventLooper::RealTimeQueue() 303 { 304 CALLED(); 305 return &fRealTimeQueue; 306 } 307 308 309 int32 310 BMediaEventLooper::Priority() const 311 { 312 CALLED(); 313 return fCurrentPriority; 314 } 315 316 317 int32 318 BMediaEventLooper::RunState() const 319 { 320 PRINT(6, "CALLED BMediaEventLooper::RunState()\n"); 321 return fRunState; 322 } 323 324 325 bigtime_t 326 BMediaEventLooper::EventLatency() const 327 { 328 CALLED(); 329 return fEventLatency; 330 } 331 332 333 bigtime_t 334 BMediaEventLooper::BufferDuration() const 335 { 336 CALLED(); 337 return fBufferDuration; 338 } 339 340 341 bigtime_t 342 BMediaEventLooper::SchedulingLatency() const 343 { 344 CALLED(); 345 return fSchedulingLatency; 346 } 347 348 349 status_t 350 BMediaEventLooper::SetPriority(int32 priority) 351 { 352 CALLED(); 353 354 // clamp to a valid value 355 if (priority < 5) 356 priority = 5; 357 358 if (priority > 120) 359 priority = 120; 360 361 fSetPriority = priority; 362 fCurrentPriority = (RunMode() == B_OFFLINE) ? min_c(B_NORMAL_PRIORITY, fSetPriority) : fSetPriority; 363 364 if (fControlThread > 0) { 365 set_thread_priority(fControlThread, fCurrentPriority); 366 fSchedulingLatency = estimate_max_scheduling_latency(fControlThread); 367 printf("BMediaEventLooper: SchedulingLatency is %" B_PRId64 "\n", 368 fSchedulingLatency); 369 } 370 371 return B_OK; 372 } 373 374 375 void 376 BMediaEventLooper::SetRunState(run_state state) 377 { 378 CALLED(); 379 380 // don't allow run state changes while quitting, 381 // also needed for correct terminating of the ControlLoop() 382 if (fRunState == B_QUITTING && state != B_TERMINATED) 383 return; 384 385 fRunState = state; 386 } 387 388 389 void 390 BMediaEventLooper::SetEventLatency(bigtime_t latency) 391 { 392 CALLED(); 393 // clamp to a valid value 394 if (latency < 0) 395 latency = 0; 396 397 fEventLatency = latency; 398 write_port_etc(ControlPort(), GENERAL_PURPOSE_WAKEUP, 0, 0, B_TIMEOUT, 0); 399 } 400 401 402 void 403 BMediaEventLooper::SetBufferDuration(bigtime_t duration) 404 { 405 CALLED(); 406 fBufferDuration = duration; 407 } 408 409 410 void 411 BMediaEventLooper::SetOfflineTime(bigtime_t offTime) 412 { 413 CALLED(); 414 fOfflineTime = offTime; 415 } 416 417 418 void 419 BMediaEventLooper::Run() 420 { 421 CALLED(); 422 423 if (fControlThread != -1) 424 return; // thread already running 425 426 // until now, the run state is B_UNREGISTERED, but we need to start in B_STOPPED state. 427 SetRunState(B_STOPPED); 428 429 char threadName[32]; 430 sprintf(threadName, "%.20s control", Name()); 431 fControlThread = spawn_thread(_ControlThreadStart, threadName, fCurrentPriority, this); 432 resume_thread(fControlThread); 433 434 // get latency information 435 fSchedulingLatency = estimate_max_scheduling_latency(fControlThread); 436 } 437 438 439 void 440 BMediaEventLooper::Quit() 441 { 442 CALLED(); 443 444 if (fRunState == B_TERMINATED) 445 return; 446 447 SetRunState(B_QUITTING); 448 close_port(ControlPort()); 449 if (fControlThread != -1) { 450 status_t err; 451 wait_for_thread(fControlThread, &err); 452 fControlThread = -1; 453 } 454 SetRunState(B_TERMINATED); 455 } 456 457 458 void 459 BMediaEventLooper::DispatchEvent(const media_timed_event *event, 460 bigtime_t lateness, 461 bool realTimeEvent) 462 { 463 PRINT(6, "CALLED BMediaEventLooper::DispatchEvent()\n"); 464 465 HandleEvent(event, lateness, realTimeEvent); 466 467 switch (event->type) { 468 case BTimedEventQueue::B_START: 469 SetRunState(B_STARTED); 470 break; 471 472 case BTimedEventQueue::B_STOP: 473 SetRunState(B_STOPPED); 474 break; 475 476 case BTimedEventQueue::B_SEEK: 477 /* nothing */ 478 break; 479 480 case BTimedEventQueue::B_WARP: 481 /* nothing */ 482 break; 483 484 case BTimedEventQueue::B_TIMER: 485 TimerExpired(event->event_time, event->data); 486 break; 487 488 default: 489 break; 490 } 491 492 _DispatchCleanUp(event); 493 } 494 495 /************************************************************* 496 * private BMediaEventLooper 497 *************************************************************/ 498 499 500 /* static */ int32 501 BMediaEventLooper::_ControlThreadStart(void *arg) 502 { 503 CALLED(); 504 ((BMediaEventLooper *)arg)->SetRunState(B_STOPPED); 505 ((BMediaEventLooper *)arg)->ControlLoop(); 506 ((BMediaEventLooper *)arg)->SetRunState(B_QUITTING); 507 return 0; 508 } 509 510 511 /* static */ void 512 BMediaEventLooper::_CleanUpEntry(const media_timed_event *event, 513 void *context) 514 { 515 PRINT(6, "CALLED BMediaEventLooper::_CleanUpEntry()\n"); 516 ((BMediaEventLooper *)context)->_DispatchCleanUp(event); 517 } 518 519 520 void 521 BMediaEventLooper::_DispatchCleanUp(const media_timed_event *event) 522 { 523 PRINT(6, "CALLED BMediaEventLooper::_DispatchCleanUp()\n"); 524 525 // this function to clean up after custom events you've created 526 if (event->cleanup >= BTimedEventQueue::B_USER_CLEANUP) 527 CleanUpEvent(event); 528 } 529 530 /* 531 // unimplemented 532 BMediaEventLooper::BMediaEventLooper(const BMediaEventLooper &) 533 BMediaEventLooper &BMediaEventLooper::operator=(const BMediaEventLooper &) 534 */ 535 536 /************************************************************* 537 * protected BMediaEventLooper 538 *************************************************************/ 539 540 541 status_t 542 BMediaEventLooper::DeleteHook(BMediaNode *node) 543 { 544 CALLED(); 545 // this is the DeleteHook that gets called by the media server 546 // before the media node is deleted 547 Quit(); 548 return BMediaNode::DeleteHook(node); 549 } 550 551 /************************************************************* 552 * private BMediaEventLooper 553 *************************************************************/ 554 555 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_0(int32 arg,...) { return B_ERROR; } 556 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_1(int32 arg,...) { return B_ERROR; } 557 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_2(int32 arg,...) { return B_ERROR; } 558 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_3(int32 arg,...) { return B_ERROR; } 559 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_4(int32 arg,...) { return B_ERROR; } 560 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_5(int32 arg,...) { return B_ERROR; } 561 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_6(int32 arg,...) { return B_ERROR; } 562 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_7(int32 arg,...) { return B_ERROR; } 563 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_8(int32 arg,...) { return B_ERROR; } 564 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_9(int32 arg,...) { return B_ERROR; } 565 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_10(int32 arg,...) { return B_ERROR; } 566 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_11(int32 arg,...) { return B_ERROR; } 567 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_12(int32 arg,...) { return B_ERROR; } 568 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_13(int32 arg,...) { return B_ERROR; } 569 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_14(int32 arg,...) { return B_ERROR; } 570 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_15(int32 arg,...) { return B_ERROR; } 571 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_16(int32 arg,...) { return B_ERROR; } 572 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_17(int32 arg,...) { return B_ERROR; } 573 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_18(int32 arg,...) { return B_ERROR; } 574 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_19(int32 arg,...) { return B_ERROR; } 575 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_20(int32 arg,...) { return B_ERROR; } 576 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_21(int32 arg,...) { return B_ERROR; } 577 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_22(int32 arg,...) { return B_ERROR; } 578 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_23(int32 arg,...) { return B_ERROR; } 579 580