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 = 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 // XXX what do we need to do here? 162 return BMediaNode::AddTimer(at_performance_time,cookie); 163 } 164 165 166 /* virtual */ void 167 BMediaEventLooper::SetRunMode(run_mode mode) 168 { 169 CALLED(); 170 // The SetRunMode() hook function is called when someone requests that your node's run mode be changed. 171 172 // bump or reduce priority when switching from/to offline run mode 173 int32 priority; 174 priority = (mode == B_OFFLINE) ? min_c(B_NORMAL_PRIORITY, fSetPriority) : fSetPriority; 175 if (priority != fCurrentPriority) { 176 fCurrentPriority = priority; 177 if(fControlThread > 0) { 178 set_thread_priority(fControlThread, fCurrentPriority); 179 fSchedulingLatency = estimate_max_scheduling_latency(fControlThread); 180 printf("BMediaEventLooper: SchedulingLatency is %Ld\n", fSchedulingLatency); 181 } 182 } 183 184 BMediaNode::SetRunMode(mode); 185 } 186 187 188 /* virtual */ void 189 BMediaEventLooper::CleanUpEvent(const media_timed_event *event) 190 { 191 CALLED(); 192 // Implement this function to clean up after custom events you've created 193 // and added to your queue. It's called when a custom event is removed from 194 // the queue, to let you handle any special tidying-up that the event might require. 195 } 196 197 198 /* virtual */ bigtime_t 199 BMediaEventLooper::OfflineTime() 200 { 201 CALLED(); 202 return fOfflineTime; 203 } 204 205 206 /* virtual */ void 207 BMediaEventLooper::ControlLoop() 208 { 209 CALLED(); 210 211 bool is_realtime = false; 212 status_t err; 213 bigtime_t latency; 214 bigtime_t waituntil; 215 for (;;) { 216 // while there are no events or it is not time for the earliest event, 217 // process messages using WaitForMessages. Whenever this funtion times out, 218 // we need to handle the next event 219 for (;;) { 220 if (RunState() == B_QUITTING) 221 return; 222 // BMediaEventLooper compensates your performance time by adding the event latency 223 // (see SetEventLatency()) and the scheduling latency (or, for real-time events, 224 // only the scheduling latency). 225 // XXX well, fix this later 226 latency = fEventLatency + fSchedulingLatency; 227 // printf("node %02d, latency %Ld\n", ID(), latency); 228 229 if (fEventQueue.HasEvents() && (TimeSource()->Now() - latency) >= fEventQueue.FirstEventTime()) { 230 // printf("node %02d waiting for %12Ld that has already happened, now %12Ld\n", ID(), fEventQueue.FirstEventTime(), system_time()); 231 is_realtime = false; 232 break; 233 } 234 if (fRealTimeQueue.HasEvents() && (TimeSource()->RealTimeFor(TimeSource()->Now(),fSchedulingLatency)) >= fRealTimeQueue.FirstEventTime()) { 235 is_realtime = true; 236 break; 237 } 238 waituntil = B_INFINITE_TIMEOUT; 239 if (fEventQueue.HasEvents()) { 240 waituntil = TimeSource()->RealTimeFor(fEventQueue.FirstEventTime(), latency); 241 // printf("node %02d waiting for %12Ld that will happen at %12Ld\n", ID(), fEventQueue.FirstEventTime(), waituntil); 242 is_realtime = false; 243 } 244 if (fRealTimeQueue.HasEvents()) { 245 bigtime_t temp; 246 temp = TimeSource()->RealTimeFor(TimeSource()->PerformanceTimeFor(fRealTimeQueue.FirstEventTime()), fSchedulingLatency); 247 if (temp < waituntil) { 248 waituntil = temp; 249 is_realtime = true; 250 } 251 } 252 err = WaitForMessage(waituntil); 253 if (err == B_TIMED_OUT) 254 break; 255 } 256 /// we have timed out - so handle the next event 257 media_timed_event event; 258 if (is_realtime) 259 err = fRealTimeQueue.RemoveFirstEvent(&event); 260 else 261 err = fEventQueue.RemoveFirstEvent(&event); 262 263 // printf("node %02d handling %12Ld at %12Ld\n", ID(), event.event_time, system_time()); 264 265 if (err == B_OK) { 266 bigtime_t lateness; 267 if (is_realtime) 268 lateness = TimeSource()->RealTime() - event.event_time; 269 else 270 lateness = TimeSource()->Now() + fEventLatency - event.event_time; 271 DispatchEvent(&event, lateness, is_realtime); 272 } 273 } 274 } 275 276 277 thread_id 278 BMediaEventLooper::ControlThread() 279 { 280 CALLED(); 281 return fControlThread; 282 } 283 284 /************************************************************* 285 * protected BMediaEventLooper 286 *************************************************************/ 287 288 289 BTimedEventQueue * 290 BMediaEventLooper::EventQueue() 291 { 292 CALLED(); 293 return &fEventQueue; 294 } 295 296 297 BTimedEventQueue * 298 BMediaEventLooper::RealTimeQueue() 299 { 300 CALLED(); 301 return &fRealTimeQueue; 302 } 303 304 305 int32 306 BMediaEventLooper::Priority() const 307 { 308 CALLED(); 309 return fCurrentPriority; 310 } 311 312 313 int32 314 BMediaEventLooper::RunState() const 315 { 316 CALLED(); 317 return fRunState; 318 } 319 320 321 bigtime_t 322 BMediaEventLooper::EventLatency() const 323 { 324 CALLED(); 325 return fEventLatency; 326 } 327 328 329 bigtime_t 330 BMediaEventLooper::BufferDuration() const 331 { 332 CALLED(); 333 return fBufferDuration; 334 } 335 336 337 bigtime_t 338 BMediaEventLooper::SchedulingLatency() const 339 { 340 CALLED(); 341 return fSchedulingLatency; 342 } 343 344 345 status_t 346 BMediaEventLooper::SetPriority(int32 priority) 347 { 348 CALLED(); 349 350 // clamp to a valid value 351 if (priority < 1) 352 priority = 1; 353 354 if (priority > 120) 355 priority = 120; 356 357 fSetPriority = priority; 358 fCurrentPriority = (RunMode() == B_OFFLINE) ? min_c(B_NORMAL_PRIORITY, fSetPriority) : fSetPriority; 359 360 if(fControlThread > 0) { 361 set_thread_priority(fControlThread, fCurrentPriority); 362 fSchedulingLatency = estimate_max_scheduling_latency(fControlThread); 363 printf("BMediaEventLooper: SchedulingLatency is %Ld\n", fSchedulingLatency); 364 } 365 366 return B_OK; 367 } 368 369 370 void 371 BMediaEventLooper::SetRunState(run_state state) 372 { 373 CALLED(); 374 375 // don't allow run state changes while quitting, 376 // also needed for correct terminating of the ControlLoop() 377 if (fRunState == B_QUITTING && state != B_TERMINATED) 378 return; 379 380 fRunState = state; 381 } 382 383 384 void 385 BMediaEventLooper::SetEventLatency(bigtime_t latency) 386 { 387 CALLED(); 388 // clamp to a valid value 389 if (latency < 0) 390 latency = 0; 391 392 fEventLatency = latency; 393 } 394 395 396 void 397 BMediaEventLooper::SetBufferDuration(bigtime_t duration) 398 { 399 CALLED(); 400 fBufferDuration = duration; 401 } 402 403 404 void 405 BMediaEventLooper::SetOfflineTime(bigtime_t offTime) 406 { 407 CALLED(); 408 fOfflineTime = offTime; 409 } 410 411 412 void 413 BMediaEventLooper::Run() 414 { 415 CALLED(); 416 417 if (fControlThread != -1) 418 return; // thread already running 419 420 char threadName[32]; 421 sprintf(threadName, "%.20s control", Name()); 422 fControlThread = spawn_thread(_ControlThreadStart, threadName, fCurrentPriority, this); 423 resume_thread(fControlThread); 424 425 // get latency information 426 fSchedulingLatency = estimate_max_scheduling_latency(fControlThread); 427 printf("BMediaEventLooper: SchedulingLatency is %Ld\n", fSchedulingLatency); 428 } 429 430 431 void 432 BMediaEventLooper::Quit() 433 { 434 CALLED(); 435 status_t err; 436 437 if (fRunState == B_TERMINATED) 438 return; 439 440 SetRunState(B_QUITTING); 441 442 close_port(ControlPort()); 443 if (fControlThread != -1) 444 wait_for_thread(fControlThread, &err); 445 fControlThread = -1; 446 447 SetRunState(B_TERMINATED); 448 } 449 450 451 void 452 BMediaEventLooper::DispatchEvent(const media_timed_event *event, 453 bigtime_t lateness, 454 bool realTimeEvent) 455 { 456 CALLED(); 457 458 HandleEvent(event,lateness,realTimeEvent); 459 460 switch (event->type) { 461 case BTimedEventQueue::B_START: 462 SetRunState(B_STARTED); 463 break; 464 465 case BTimedEventQueue::B_STOP: 466 SetRunState(B_STOPPED); 467 break; 468 469 case BTimedEventQueue::B_SEEK: 470 /* nothing */ 471 break; 472 473 case BTimedEventQueue::B_WARP: 474 /* nothing */ 475 break; 476 477 default: 478 break; 479 } 480 481 _DispatchCleanUp(event); 482 } 483 484 /************************************************************* 485 * private BMediaEventLooper 486 *************************************************************/ 487 488 489 /* static */ int32 490 BMediaEventLooper::_ControlThreadStart(void *arg) 491 { 492 CALLED(); 493 ((BMediaEventLooper *)arg)->SetRunState(B_STOPPED); 494 ((BMediaEventLooper *)arg)->ControlLoop(); 495 ((BMediaEventLooper *)arg)->SetRunState(B_QUITTING); 496 return 0; 497 } 498 499 500 /* static */ void 501 BMediaEventLooper::_CleanUpEntry(const media_timed_event *event, 502 void *context) 503 { 504 CALLED(); 505 ((BMediaEventLooper *)context)->_DispatchCleanUp(event); 506 } 507 508 509 void 510 BMediaEventLooper::_DispatchCleanUp(const media_timed_event *event) 511 { 512 CALLED(); 513 514 // this function to clean up after custom events you've created 515 if (event->cleanup >= BTimedEventQueue::B_USER_CLEANUP) 516 CleanUpEvent(event); 517 } 518 519 /* 520 // unimplemented 521 BMediaEventLooper::BMediaEventLooper(const BMediaEventLooper &) 522 BMediaEventLooper &BMediaEventLooper::operator=(const BMediaEventLooper &) 523 */ 524 525 /************************************************************* 526 * protected BMediaEventLooper 527 *************************************************************/ 528 529 530 status_t 531 BMediaEventLooper::DeleteHook(BMediaNode *node) 532 { 533 CALLED(); 534 // this is the DeleteHook that gets called by the media server 535 // before the media node is deleted 536 Quit(); 537 return BMediaNode::DeleteHook(node); 538 } 539 540 /************************************************************* 541 * private BMediaEventLooper 542 *************************************************************/ 543 544 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_0(int32 arg,...) { return B_ERROR; } 545 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_1(int32 arg,...) { return B_ERROR; } 546 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_2(int32 arg,...) { return B_ERROR; } 547 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_3(int32 arg,...) { return B_ERROR; } 548 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_4(int32 arg,...) { return B_ERROR; } 549 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_5(int32 arg,...) { return B_ERROR; } 550 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_6(int32 arg,...) { return B_ERROR; } 551 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_7(int32 arg,...) { return B_ERROR; } 552 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_8(int32 arg,...) { return B_ERROR; } 553 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_9(int32 arg,...) { return B_ERROR; } 554 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_10(int32 arg,...) { return B_ERROR; } 555 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_11(int32 arg,...) { return B_ERROR; } 556 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_12(int32 arg,...) { return B_ERROR; } 557 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_13(int32 arg,...) { return B_ERROR; } 558 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_14(int32 arg,...) { return B_ERROR; } 559 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_15(int32 arg,...) { return B_ERROR; } 560 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_16(int32 arg,...) { return B_ERROR; } 561 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_17(int32 arg,...) { return B_ERROR; } 562 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_18(int32 arg,...) { return B_ERROR; } 563 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_19(int32 arg,...) { return B_ERROR; } 564 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_20(int32 arg,...) { return B_ERROR; } 565 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_21(int32 arg,...) { return B_ERROR; } 566 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_22(int32 arg,...) { return B_ERROR; } 567 status_t BMediaEventLooper::_Reserved_BMediaEventLooper_23(int32 arg,...) { return B_ERROR; } 568 569