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 // XXX The bebook says that the latency is always calculated in realtime 37 // XXX This is not currently done in this code 38 39 /************************************************************* 40 * protected BMediaEventLooper 41 *************************************************************/ 42 43 /* virtual */ 44 BMediaEventLooper::~BMediaEventLooper() 45 { 46 CALLED(); 47 48 // don't call Quit(); here, except if the user was stupid 49 if (fControlThread != -1) { 50 printf("You MUST call BMediaEventLooper::Quit() in your destructor!\n"); 51 Quit(); 52 } 53 } 54 55 /* explicit */ 56 BMediaEventLooper::BMediaEventLooper(uint32 apiVersion) : 57 BMediaNode("called by BMediaEventLooper"), 58 fControlThread(-1), 59 fCurrentPriority(B_URGENT_PRIORITY), 60 fSetPriority(B_URGENT_PRIORITY), 61 fRunState(B_UNREGISTERED), 62 fEventLatency(0), 63 fSchedulingLatency(0), 64 fBufferDuration(0), 65 fOfflineTime(0), 66 fApiVersion(apiVersion) 67 { 68 CALLED(); 69 fEventQueue.SetCleanupHook(BMediaEventLooper::_CleanUpEntry, this); 70 fRealTimeQueue.SetCleanupHook(BMediaEventLooper::_CleanUpEntry, this); 71 } 72 73 /* virtual */ void 74 BMediaEventLooper::NodeRegistered() 75 { 76 CALLED(); 77 // don't call Run(); here, must be done by the derived class (yes, that's stupid) 78 } 79 80 81 /* virtual */ void 82 BMediaEventLooper::Start(bigtime_t performance_time) 83 { 84 CALLED(); 85 // This hook function is called when a node is started 86 // by a call to the BMediaRoster. The specified 87 // performanceTime, the time at which the node 88 // should start running, may be in the future. 89 fEventQueue.AddEvent(media_timed_event(performance_time, BTimedEventQueue::B_START)); 90 } 91 92 93 /* virtual */ void 94 BMediaEventLooper::Stop(bigtime_t performance_time, 95 bool immediate) 96 { 97 CALLED(); 98 // This hook function is called when a node is stopped 99 // by a call to the BMediaRoster. The specified performanceTime, 100 // the time at which the node should stop, may be in the future. 101 // If immediate is true, your node should ignore the performanceTime 102 // value and synchronously stop performance. When Stop() returns, 103 // you're promising not to write into any BBuffers you may have 104 // received from your downstream consumers, and you promise not 105 // to send any more buffers until Start() is called again. 106 107 if (immediate) { 108 // always be sure to add to the front of the queue so we can make sure it is 109 // handled before any buffers are sent! 110 performance_time = 0; 111 } 112 fEventQueue.AddEvent(media_timed_event(performance_time, BTimedEventQueue::B_STOP)); 113 } 114 115 116 /* virtual */ void 117 BMediaEventLooper::Seek(bigtime_t media_time, 118 bigtime_t performance_time) 119 { 120 CALLED(); 121 // This hook function is called when a node is asked to seek to 122 // the specified mediaTime by a call to the BMediaRoster. 123 // The specified performanceTime, the time at which the node 124 // should begin the seek operation, may be in the future. 125 fEventQueue.AddEvent(media_timed_event(performance_time, BTimedEventQueue::B_SEEK, NULL, 126 BTimedEventQueue::B_NO_CLEANUP, 0, media_time, NULL)); 127 } 128 129 130 /* virtual */ void 131 BMediaEventLooper::TimeWarp(bigtime_t at_real_time, 132 bigtime_t to_performance_time) 133 { 134 CALLED(); 135 // This hook function is called when the time source to which the 136 // node is slaved is repositioned (via a seek operation) such that 137 // there will be a sudden jump in the performance time progression 138 // as seen by the node. The to_performance_time argument indicates 139 // the new performance time; the change should occur at the real 140 // time specified by the at_real_time argument. 141 142 // place in the realtime queue 143 fRealTimeQueue.AddEvent(media_timed_event(at_real_time, BTimedEventQueue::B_WARP, 144 NULL, BTimedEventQueue::B_NO_CLEANUP, 0, to_performance_time, NULL)); 145 146 // BeBook: Your implementation of TimeWarp() should call through to BMediaNode::TimeWarp() 147 // BeBook: as well as all other inherited forms of TimeWarp() 148 // XXX should we do this here? 149 BMediaNode::TimeWarp(at_real_time, to_performance_time); 150 } 151 152 153 /* virtual */ status_t 154 BMediaEventLooper::AddTimer(bigtime_t at_performance_time, 155 int32 cookie) 156 { 157 CALLED(); 158 // XXX what do we need to do here? 159 return BMediaNode::AddTimer(at_performance_time,cookie); 160 } 161 162 163 /* virtual */ void 164 BMediaEventLooper::SetRunMode(run_mode mode) 165 { 166 CALLED(); 167 // The SetRunMode() hook function is called when someone requests that your node's run mode be changed. 168 169 // bump or reduce priority when switching from/to offline run mode 170 int32 priority; 171 priority = (mode == B_OFFLINE) ? min_c(B_NORMAL_PRIORITY, fSetPriority) : fSetPriority; 172 if (priority != fCurrentPriority) { 173 fCurrentPriority = priority; 174 if(fControlThread > 0) { 175 set_thread_priority(fControlThread, fCurrentPriority); 176 fSchedulingLatency = estimate_max_scheduling_latency(fControlThread); 177 printf("BMediaEventLooper: SchedulingLatency is %Ld\n", fSchedulingLatency); 178 } 179 } 180 181 BMediaNode::SetRunMode(mode); 182 } 183 184 185 /* virtual */ void 186 BMediaEventLooper::CleanUpEvent(const media_timed_event *event) 187 { 188 CALLED(); 189 // Implement this function to clean up after custom events you've created 190 // and added to your queue. It's called when a custom event is removed from 191 // the queue, to let you handle any special tidying-up that the event might require. 192 } 193 194 195 /* virtual */ bigtime_t 196 BMediaEventLooper::OfflineTime() 197 { 198 CALLED(); 199 return fOfflineTime; 200 } 201 202 203 /* virtual */ void 204 BMediaEventLooper::ControlLoop() 205 { 206 CALLED(); 207 208 bool is_realtime = false; 209 status_t err; 210 bigtime_t latency; 211 bigtime_t waituntil; 212 for (;;) { 213 // while there are no events or it is not time for the earliest event, 214 // process messages using WaitForMessages. Whenever this funtion times out, 215 // we need to handle the next event 216 for (;;) { 217 if (RunState() == B_QUITTING) 218 return; 219 // BMediaEventLooper compensates your performance time by adding the event latency 220 // (see SetEventLatency()) and the scheduling latency (or, for real-time events, 221 // only the scheduling latency). 222 // XXX well, fix this later 223 latency = fEventLatency + fSchedulingLatency; 224 // printf("node %02d, latency %Ld\n", ID(), latency); 225 226 if (fEventQueue.HasEvents() && (TimeSource()->Now() - latency) >= fEventQueue.FirstEventTime()) { 227 // printf("node %02d waiting for %12Ld that has already happened, now %12Ld\n", ID(), fEventQueue.FirstEventTime(), system_time()); 228 is_realtime = false; 229 break; 230 } 231 if (fRealTimeQueue.HasEvents() && (TimeSource()->RealTimeFor(TimeSource()->Now(),fSchedulingLatency)) >= fRealTimeQueue.FirstEventTime()) { 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 = TimeSource()->RealTimeFor(TimeSource()->PerformanceTimeFor(fRealTimeQueue.FirstEventTime()), fSchedulingLatency); 244 if (temp < waituntil) { 245 waituntil = temp; 246 is_realtime = true; 247 } 248 } 249 err = WaitForMessage(waituntil); 250 if (err == B_TIMED_OUT) 251 break; 252 } 253 /// we have timed out - so handle the next event 254 media_timed_event event; 255 if (is_realtime) 256 err = fRealTimeQueue.RemoveFirstEvent(&event); 257 else 258 err = fEventQueue.RemoveFirstEvent(&event); 259 260 // printf("node %02d handling %12Ld at %12Ld\n", ID(), event.event_time, system_time()); 261 262 if (err == B_OK) { 263 bigtime_t lateness; 264 if (is_realtime) 265 lateness = TimeSource()->RealTime() - event.event_time; 266 else 267 lateness = TimeSource()->Now() + fEventLatency - event.event_time; 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 PRINT(6, "CALLED BMediaEventLooper::RunState()\n"); 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 < 5) 349 priority = 5; 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 PRINT(6, "CALLED BMediaEventLooper::DispatchEvent()\n"); 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 PRINT(6, "CALLED BMediaEventLooper::_CleanUpEntry()\n"); 502 ((BMediaEventLooper *)context)->_DispatchCleanUp(event); 503 } 504 505 506 void 507 BMediaEventLooper::_DispatchCleanUp(const media_timed_event *event) 508 { 509 PRINT(6, "CALLED BMediaEventLooper::_DispatchCleanUp()\n"); 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