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