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