1/* 2 * Copyright (c) 2015 Dario Casalinuovo <b.vitruvio@gmail.com> 3 * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de> 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining 6 * a copy of this software and associated documentation files or portions 7 * thereof (the "Software"), to deal in the Software without restriction, 8 * including without limitation the rights to use, copy, modify, merge, 9 * publish, distribute, sublicense, and/or sell copies of the Software, 10 * and to permit persons to whom the Software is furnished to do so, subject 11 * to the following conditions: 12 * 13 * * Redistributions of source code must retain the above copyright notice, 14 * this list of conditions and the following disclaimer. 15 * 16 * * Redistributions in binary form must reproduce the above copyright notice 17 * in the binary, as well as this list of conditions and the following 18 * disclaimer in the documentation and/or other materials provided with 19 * the distribution. 20 * 21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 * THE SOFTWARE. 28 * 29 */ 30 31#include <MediaEventLooper.h> 32#include <TimeSource.h> 33#include <scheduler.h> 34#include <Buffer.h> 35#include <ServerInterface.h> 36#include "MediaDebug.h" 37 38/************************************************************* 39 * protected BMediaEventLooper 40 *************************************************************/ 41 42/* virtual */ 43BMediaEventLooper::~BMediaEventLooper() 44{ 45 CALLED(); 46 47 // don't call Quit(); here, except if the user was stupid 48 if (fControlThread != -1) { 49 printf("You MUST call BMediaEventLooper::Quit() in your destructor!\n"); 50 Quit(); 51 } 52} 53 54/* explicit */ 55BMediaEventLooper::BMediaEventLooper(uint32 apiVersion) : 56 BMediaNode("called by BMediaEventLooper"), 57 fControlThread(-1), 58 fCurrentPriority(B_URGENT_PRIORITY), 59 fSetPriority(B_URGENT_PRIORITY), 60 fRunState(B_UNREGISTERED), 61 fEventLatency(0), 62 fSchedulingLatency(0), 63 fBufferDuration(0), 64 fOfflineTime(0), 65 fApiVersion(apiVersion) 66{ 67 CALLED(); 68 fEventQueue.SetCleanupHook(BMediaEventLooper::_CleanUpEntry, this); 69 fRealTimeQueue.SetCleanupHook(BMediaEventLooper::_CleanUpEntry, this); 70} 71 72/* virtual */ void 73BMediaEventLooper::NodeRegistered() 74{ 75 CALLED(); 76 // Calling Run() should be done by the derived class, 77 // at least that's how it is documented by the BeBook. 78 // It appears that BeOS R5 called it here. Calling Run() 79 // twice doesn't hurt, and some nodes need it to be called here. 80 Run(); 81} 82 83 84/* virtual */ void 85BMediaEventLooper::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 97BMediaEventLooper::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 120BMediaEventLooper::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 134BMediaEventLooper::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 157BMediaEventLooper::AddTimer(bigtime_t at_performance_time, 158 int32 cookie) 159{ 160 CALLED(); 161 162 media_timed_event event(at_performance_time, 163 BTimedEventQueue::B_TIMER, NULL, 164 BTimedEventQueue::B_EXPIRE_TIMER); 165 event.data = cookie; 166 return EventQueue()->AddEvent(event); 167} 168 169 170/* virtual */ void 171BMediaEventLooper::SetRunMode(run_mode mode) 172{ 173 CALLED(); 174 // The SetRunMode() hook function is called when someone requests that your node's run mode be changed. 175 176 // bump or reduce priority when switching from/to offline run mode 177 int32 priority; 178 priority = (mode == B_OFFLINE) ? min_c(B_NORMAL_PRIORITY, fSetPriority) : fSetPriority; 179 if (priority != fCurrentPriority) { 180 fCurrentPriority = priority; 181 if (fControlThread > 0) { 182 set_thread_priority(fControlThread, fCurrentPriority); 183 fSchedulingLatency = estimate_max_scheduling_latency(fControlThread); 184 printf("BMediaEventLooper: SchedulingLatency is %" B_PRId64 "\n", 185 fSchedulingLatency); 186 } 187 } 188 189 BMediaNode::SetRunMode(mode); 190} 191 192 193/* virtual */ void 194BMediaEventLooper::CleanUpEvent(const media_timed_event *event) 195{ 196 CALLED(); 197 // Implement this function to clean up after custom events you've created 198 // and added to your queue. It's called when a custom event is removed from 199 // the queue, to let you handle any special tidying-up that the event might require. 200} 201 202 203/* virtual */ bigtime_t 204BMediaEventLooper::OfflineTime() 205{ 206 CALLED(); 207 return fOfflineTime; 208} 209 210 211/* virtual */ void 212BMediaEventLooper::ControlLoop() 213{ 214 CALLED(); 215 216 status_t err = B_OK; 217 bigtime_t waitUntil = B_INFINITE_TIMEOUT; 218 bool hasRealtime = false; 219 bool hasEvent = false; 220 221 // While there are no events or it is not time for the earliest event, 222 // process messages using WaitForMessages. Whenever this funtion times out, 223 // we need to handle the next event 224 225 fSchedulingLatency = estimate_max_scheduling_latency(fControlThread); 226 while (RunState() != B_QUITTING) { 227 if (err == B_TIMED_OUT 228 || err == B_WOULD_BLOCK) { 229 // NOTE: The reference for doing the lateness calculus this way can 230 // be found in the BeBook article "A BMediaEventLooper Example". 231 // The value which we are going to calculate, is referred there as 232 // 'lateness'. 233 media_timed_event event; 234 if (hasEvent) 235 err = fEventQueue.RemoveFirstEvent(&event); 236 else if (hasRealtime) 237 err = fRealTimeQueue.RemoveFirstEvent(&event); 238 239 if (err == B_OK) { 240 // The general idea of lateness is to allow 241 // the client code to detect when the buffer 242 // is handled late or early. 243 bigtime_t lateness = TimeSource()->RealTime() - waitUntil; 244 245 DispatchEvent(&event, lateness, hasRealtime); 246 } 247 } else if (err != B_OK) 248 return; 249 250 // BMediaEventLooper compensates your performance time by adding 251 // the event latency (see SetEventLatency()) and the scheduling 252 // latency (or, for real-time events, only the scheduling latency). 253 254 hasRealtime = fRealTimeQueue.HasEvents(); 255 hasEvent = fEventQueue.HasEvents(); 256 257 if (hasEvent) { 258 waitUntil = TimeSource()->RealTimeFor( 259 fEventQueue.FirstEventTime(), 260 fEventLatency + fSchedulingLatency); 261 } else if (!hasRealtime) 262 waitUntil = B_INFINITE_TIMEOUT; 263 264 if (hasRealtime) { 265 bigtime_t realtimeWait = fRealTimeQueue.FirstEventTime() 266 - fSchedulingLatency; 267 268 if (!hasEvent || realtimeWait <= waitUntil) { 269 waitUntil = realtimeWait; 270 hasEvent = false; 271 } else 272 hasRealtime = false; 273 } 274 275 if (waitUntil != B_INFINITE_TIMEOUT 276 && TimeSource()->RealTime() >= waitUntil) { 277 err = WaitForMessage(0); 278 } else 279 err = WaitForMessage(waitUntil); 280 } 281} 282 283 284thread_id 285BMediaEventLooper::ControlThread() 286{ 287 CALLED(); 288 return fControlThread; 289} 290 291/************************************************************* 292 * protected BMediaEventLooper 293 *************************************************************/ 294 295 296BTimedEventQueue * 297BMediaEventLooper::EventQueue() 298{ 299 CALLED(); 300 return &fEventQueue; 301} 302 303 304BTimedEventQueue * 305BMediaEventLooper::RealTimeQueue() 306{ 307 CALLED(); 308 return &fRealTimeQueue; 309} 310 311 312int32 313BMediaEventLooper::Priority() const 314{ 315 CALLED(); 316 return fCurrentPriority; 317} 318 319 320int32 321BMediaEventLooper::RunState() const 322{ 323 PRINT(6, "CALLED BMediaEventLooper::RunState()\n"); 324 return fRunState; 325} 326 327 328bigtime_t 329BMediaEventLooper::EventLatency() const 330{ 331 CALLED(); 332 return fEventLatency; 333} 334 335 336bigtime_t 337BMediaEventLooper::BufferDuration() const 338{ 339 CALLED(); 340 return fBufferDuration; 341} 342 343 344bigtime_t 345BMediaEventLooper::SchedulingLatency() const 346{ 347 CALLED(); 348 return fSchedulingLatency; 349} 350 351 352status_t 353BMediaEventLooper::SetPriority(int32 priority) 354{ 355 CALLED(); 356 357 // clamp to a valid value 358 if (priority < 5) 359 priority = 5; 360 361 if (priority > 120) 362 priority = 120; 363 364 fSetPriority = priority; 365 fCurrentPriority = (RunMode() == B_OFFLINE) ? min_c(B_NORMAL_PRIORITY, fSetPriority) : fSetPriority; 366 367 if (fControlThread > 0) { 368 set_thread_priority(fControlThread, fCurrentPriority); 369 fSchedulingLatency = estimate_max_scheduling_latency(fControlThread); 370 printf("BMediaEventLooper: SchedulingLatency is %" B_PRId64 "\n", 371 fSchedulingLatency); 372 } 373 374 return B_OK; 375} 376 377 378void 379BMediaEventLooper::SetRunState(run_state state) 380{ 381 CALLED(); 382 383 // don't allow run state changes while quitting, 384 // also needed for correct terminating of the ControlLoop() 385 if (fRunState == B_QUITTING && state != B_TERMINATED) 386 return; 387 388 fRunState = state; 389} 390 391 392void 393BMediaEventLooper::SetEventLatency(bigtime_t latency) 394{ 395 CALLED(); 396 // clamp to a valid value 397 if (latency < 0) 398 latency = 0; 399 400 fEventLatency = latency; 401 write_port_etc(ControlPort(), GENERAL_PURPOSE_WAKEUP, 0, 0, B_TIMEOUT, 0); 402} 403 404 405void 406BMediaEventLooper::SetBufferDuration(bigtime_t duration) 407{ 408 CALLED(); 409 410 if (duration < 0) 411 duration = 0; 412 413 fBufferDuration = duration; 414} 415 416 417void 418BMediaEventLooper::SetOfflineTime(bigtime_t offTime) 419{ 420 CALLED(); 421 fOfflineTime = offTime; 422} 423 424 425void 426BMediaEventLooper::Run() 427{ 428 CALLED(); 429 430 if (fControlThread != -1) 431 return; // thread already running 432 433 // until now, the run state is B_UNREGISTERED, but we need to start in B_STOPPED state. 434 SetRunState(B_STOPPED); 435 436 char threadName[32]; 437 sprintf(threadName, "%.20s control", Name()); 438 fControlThread = spawn_thread(_ControlThreadStart, threadName, fCurrentPriority, this); 439 resume_thread(fControlThread); 440 441 // get latency information 442 fSchedulingLatency = estimate_max_scheduling_latency(fControlThread); 443} 444 445 446void 447BMediaEventLooper::Quit() 448{ 449 CALLED(); 450 451 if (fRunState == B_TERMINATED) 452 return; 453 454 SetRunState(B_QUITTING); 455 close_port(ControlPort()); 456 if (fControlThread != -1) { 457 status_t err; 458 wait_for_thread(fControlThread, &err); 459 fControlThread = -1; 460 } 461 SetRunState(B_TERMINATED); 462} 463 464 465void 466BMediaEventLooper::DispatchEvent(const media_timed_event *event, 467 bigtime_t lateness, 468 bool realTimeEvent) 469{ 470 PRINT(6, "CALLED BMediaEventLooper::DispatchEvent()\n"); 471 472 HandleEvent(event, lateness, realTimeEvent); 473 474 switch (event->type) { 475 case BTimedEventQueue::B_START: 476 SetRunState(B_STARTED); 477 break; 478 479 case BTimedEventQueue::B_STOP: 480 SetRunState(B_STOPPED); 481 break; 482 483 case BTimedEventQueue::B_SEEK: 484 /* nothing */ 485 break; 486 487 case BTimedEventQueue::B_WARP: 488 /* nothing */ 489 break; 490 491 case BTimedEventQueue::B_TIMER: 492 TimerExpired(event->event_time, event->data); 493 break; 494 495 default: 496 break; 497 } 498 499 _DispatchCleanUp(event); 500} 501 502/************************************************************* 503 * private BMediaEventLooper 504 *************************************************************/ 505 506 507/* static */ int32 508BMediaEventLooper::_ControlThreadStart(void *arg) 509{ 510 CALLED(); 511 ((BMediaEventLooper *)arg)->SetRunState(B_STOPPED); 512 ((BMediaEventLooper *)arg)->ControlLoop(); 513 ((BMediaEventLooper *)arg)->SetRunState(B_QUITTING); 514 return 0; 515} 516 517 518/* static */ void 519BMediaEventLooper::_CleanUpEntry(const media_timed_event *event, 520 void *context) 521{ 522 PRINT(6, "CALLED BMediaEventLooper::_CleanUpEntry()\n"); 523 ((BMediaEventLooper *)context)->_DispatchCleanUp(event); 524} 525 526 527void 528BMediaEventLooper::_DispatchCleanUp(const media_timed_event *event) 529{ 530 PRINT(6, "CALLED BMediaEventLooper::_DispatchCleanUp()\n"); 531 532 // this function to clean up after custom events you've created 533 if (event->cleanup >= BTimedEventQueue::B_USER_CLEANUP) 534 CleanUpEvent(event); 535} 536 537/* 538// unimplemented 539BMediaEventLooper::BMediaEventLooper(const BMediaEventLooper &) 540BMediaEventLooper &BMediaEventLooper::operator=(const BMediaEventLooper &) 541*/ 542 543/************************************************************* 544 * protected BMediaEventLooper 545 *************************************************************/ 546 547 548status_t 549BMediaEventLooper::DeleteHook(BMediaNode *node) 550{ 551 CALLED(); 552 // this is the DeleteHook that gets called by the media server 553 // before the media node is deleted 554 Quit(); 555 return BMediaNode::DeleteHook(node); 556} 557 558/************************************************************* 559 * private BMediaEventLooper 560 *************************************************************/ 561 562status_t BMediaEventLooper::_Reserved_BMediaEventLooper_0(int32 arg,...) { return B_ERROR; } 563status_t BMediaEventLooper::_Reserved_BMediaEventLooper_1(int32 arg,...) { return B_ERROR; } 564status_t BMediaEventLooper::_Reserved_BMediaEventLooper_2(int32 arg,...) { return B_ERROR; } 565status_t BMediaEventLooper::_Reserved_BMediaEventLooper_3(int32 arg,...) { return B_ERROR; } 566status_t BMediaEventLooper::_Reserved_BMediaEventLooper_4(int32 arg,...) { return B_ERROR; } 567status_t BMediaEventLooper::_Reserved_BMediaEventLooper_5(int32 arg,...) { return B_ERROR; } 568status_t BMediaEventLooper::_Reserved_BMediaEventLooper_6(int32 arg,...) { return B_ERROR; } 569status_t BMediaEventLooper::_Reserved_BMediaEventLooper_7(int32 arg,...) { return B_ERROR; } 570status_t BMediaEventLooper::_Reserved_BMediaEventLooper_8(int32 arg,...) { return B_ERROR; } 571status_t BMediaEventLooper::_Reserved_BMediaEventLooper_9(int32 arg,...) { return B_ERROR; } 572status_t BMediaEventLooper::_Reserved_BMediaEventLooper_10(int32 arg,...) { return B_ERROR; } 573status_t BMediaEventLooper::_Reserved_BMediaEventLooper_11(int32 arg,...) { return B_ERROR; } 574status_t BMediaEventLooper::_Reserved_BMediaEventLooper_12(int32 arg,...) { return B_ERROR; } 575status_t BMediaEventLooper::_Reserved_BMediaEventLooper_13(int32 arg,...) { return B_ERROR; } 576status_t BMediaEventLooper::_Reserved_BMediaEventLooper_14(int32 arg,...) { return B_ERROR; } 577status_t BMediaEventLooper::_Reserved_BMediaEventLooper_15(int32 arg,...) { return B_ERROR; } 578status_t BMediaEventLooper::_Reserved_BMediaEventLooper_16(int32 arg,...) { return B_ERROR; } 579status_t BMediaEventLooper::_Reserved_BMediaEventLooper_17(int32 arg,...) { return B_ERROR; } 580status_t BMediaEventLooper::_Reserved_BMediaEventLooper_18(int32 arg,...) { return B_ERROR; } 581status_t BMediaEventLooper::_Reserved_BMediaEventLooper_19(int32 arg,...) { return B_ERROR; } 582status_t BMediaEventLooper::_Reserved_BMediaEventLooper_20(int32 arg,...) { return B_ERROR; } 583status_t BMediaEventLooper::_Reserved_BMediaEventLooper_21(int32 arg,...) { return B_ERROR; } 584status_t BMediaEventLooper::_Reserved_BMediaEventLooper_22(int32 arg,...) { return B_ERROR; } 585status_t BMediaEventLooper::_Reserved_BMediaEventLooper_23(int32 arg,...) { return B_ERROR; } 586 587