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