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