1 /* 2 * Copyright 2002-2024, Haiku. All rights reserved. 3 * This file may be used under the terms of the MIT License. 4 * 5 * Authors: 6 * Dario Casalinuovo 7 * Marcus Overhagen 8 */ 9 10 11 #include <TimeSource.h> 12 13 #include <Autolock.h> 14 15 #include <string.h> 16 17 #include "MediaDebug.h" 18 #include "DataExchange.h" 19 #include "ServerInterface.h" 20 #include "TimeSourceObject.h" 21 #include "TMap.h" 22 23 #define DEBUG_TIMESOURCE 0 24 25 #if DEBUG_TIMESOURCE 26 #define TRACE_TIMESOURCE printf 27 #else 28 #define TRACE_TIMESOURCE if (1) {} else printf 29 #endif 30 31 namespace BPrivate { namespace media { 32 33 #define _atomic_read(p) atomic_or((p), 0) 34 35 // must be multiple of page size 36 #define TS_AREA_SIZE B_PAGE_SIZE 37 // must be power of two 38 #define TS_INDEX_COUNT 128 39 40 // sizeof(TimeSourceTransmit) must be <= TS_AREA_SIZE 41 struct TimeSourceTransmit 42 { 43 int32 readindex; 44 int32 writeindex; 45 int32 isrunning; 46 bigtime_t realtime[TS_INDEX_COUNT]; 47 bigtime_t perftime[TS_INDEX_COUNT]; 48 float drift[TS_INDEX_COUNT]; 49 }; 50 51 #define MAX_SLAVE_NODES 300 52 53 54 class SlaveNodes : public BLocker 55 { 56 public: 57 SlaveNodes(); 58 ~SlaveNodes(); 59 60 int32 CountSlaves() const; 61 bool GetNextSlave(port_id** id); 62 void Rewind(); 63 64 bool InsertSlave(const media_node& node); 65 bool RemoveSlave(const media_node& node); 66 private: 67 Map<media_node_id, port_id> fSlaveList; 68 }; 69 70 71 SlaveNodes::SlaveNodes() 72 : 73 BLocker("BTimeSource slavenodes") 74 { 75 } 76 77 78 SlaveNodes::~SlaveNodes() 79 { 80 fSlaveList.MakeEmpty(); 81 } 82 83 84 int32 85 SlaveNodes::CountSlaves() const 86 { 87 return fSlaveList.CountItems(); 88 } 89 90 91 bool 92 SlaveNodes::GetNextSlave(port_id** id) 93 { 94 return fSlaveList.GetNext(id); 95 } 96 97 98 void 99 SlaveNodes::Rewind() 100 { 101 fSlaveList.Rewind(); 102 } 103 104 105 bool 106 SlaveNodes::InsertSlave(const media_node& node) 107 { 108 return fSlaveList.Insert(node.node, node.port); 109 } 110 111 112 bool 113 SlaveNodes::RemoveSlave(const media_node& node) 114 { 115 return fSlaveList.Remove(node.node); 116 } 117 118 119 } } // namespace BPrivate::media 120 121 122 /************************************************************* 123 * protected BTimeSource 124 *************************************************************/ 125 126 BTimeSource::~BTimeSource() 127 { 128 CALLED(); 129 if (fArea > 0) 130 delete_area(fArea); 131 delete fSlaveNodes; 132 } 133 134 /************************************************************* 135 * public BTimeSource 136 *************************************************************/ 137 138 status_t 139 BTimeSource::SnoozeUntil(bigtime_t performance_time, 140 bigtime_t with_latency, bool retry_signals) 141 { 142 CALLED(); 143 bigtime_t time; 144 status_t err; 145 do { 146 time = RealTimeFor(performance_time, with_latency); 147 err = snooze_until(time, B_SYSTEM_TIMEBASE); 148 } while (err == B_INTERRUPTED && retry_signals); 149 return err; 150 } 151 152 153 bigtime_t 154 BTimeSource::Now() 155 { 156 PRINT(8, "CALLED BTimeSource::Now()\n"); 157 return PerformanceTimeFor(RealTime()); 158 } 159 160 161 bigtime_t 162 BTimeSource::PerformanceTimeFor(bigtime_t real_time) 163 { 164 PRINT(8, "CALLED BTimeSource::PerformanceTimeFor()\n"); 165 bigtime_t last_perf_time; 166 bigtime_t last_real_time; 167 float last_drift; 168 169 if (GetTime(&last_perf_time, &last_real_time, &last_drift) != B_OK) 170 debugger("BTimeSource::PerformanceTimeFor: GetTime failed"); 171 172 bigtime_t real_time_difference = real_time - last_real_time; 173 if (real_time_difference >= (1 << FLT_MANT_DIG)) { 174 // The difference is too large fit in a float. 175 if (last_drift == 1.0f) 176 return last_perf_time + real_time_difference; 177 178 debugger("BTimeSource::PerformanceTimeFor: real time too large"); 179 } 180 181 return last_perf_time + (bigtime_t)(real_time_difference * last_drift); 182 } 183 184 185 bigtime_t 186 BTimeSource::RealTimeFor(bigtime_t performance_time, 187 bigtime_t with_latency) 188 { 189 PRINT(8, "CALLED BTimeSource::RealTimeFor()\n"); 190 191 if (fIsRealtime) 192 return performance_time - with_latency; 193 194 bigtime_t last_perf_time; 195 bigtime_t last_real_time; 196 float last_drift; 197 198 if (GetTime(&last_perf_time, &last_real_time, &last_drift) != B_OK) 199 debugger("BTimeSource::RealTimeFor: GetTime failed"); 200 201 bigtime_t perf_time_difference = performance_time - last_perf_time; 202 if (perf_time_difference >= (1 << FLT_MANT_DIG)) { 203 // The difference is too large to fit in a float. 204 if (last_drift == 1.0f) 205 return last_real_time - with_latency + perf_time_difference; 206 207 debugger("BTimeSource::PerformanceTimeFor: performance time too large"); 208 } 209 210 return last_real_time - with_latency 211 + (bigtime_t)(perf_time_difference / last_drift); 212 } 213 214 215 bool 216 BTimeSource::IsRunning() 217 { 218 PRINT(8, "CALLED BTimeSource::IsRunning()\n"); 219 220 bool isrunning; 221 222 // The system time source is always running 223 if (fIsRealtime) 224 isrunning = true; 225 else 226 isrunning = fBuf ? atomic_add(&fBuf->isrunning, 0) : fStarted; 227 228 TRACE_TIMESOURCE("BTimeSource::IsRunning() node %" B_PRId32 ", port %" 229 B_PRId32 ", %s\n", fNodeID, fControlPort, isrunning ? "yes" : "no"); 230 return isrunning; 231 } 232 233 234 status_t 235 BTimeSource::GetTime(bigtime_t* performance_time, 236 bigtime_t* real_time, float* drift) 237 { 238 PRINT(8, "CALLED BTimeSource::GetTime()\n"); 239 240 if (fIsRealtime) { 241 *performance_time = *real_time = system_time(); 242 *drift = 1.0f; 243 return B_OK; 244 } 245 246 if (fBuf == NULL) 247 debugger("BTimeSource::GetTime: fBuf == NULL"); 248 249 int32 index = _atomic_read(&fBuf->readindex); 250 index &= (TS_INDEX_COUNT - 1); 251 *real_time = fBuf->realtime[index]; 252 *performance_time = fBuf->perftime[index]; 253 *drift = fBuf->drift[index]; 254 255 TRACE_TIMESOURCE("BTimeSource::GetTime timesource %" B_PRId32 256 ", perf %16" B_PRId64 ", real %16" B_PRId64 ", drift %2.2f\n", ID(), 257 *performance_time, *real_time, *drift); 258 return B_OK; 259 } 260 261 262 bigtime_t 263 BTimeSource::RealTime() 264 { 265 PRINT(8, "CALLED BTimeSource::RealTime()\n"); 266 return system_time(); 267 } 268 269 270 status_t 271 BTimeSource::GetStartLatency(bigtime_t* out_latency) 272 { 273 CALLED(); 274 *out_latency = 0; 275 return B_OK; 276 } 277 278 /************************************************************* 279 * protected BTimeSource 280 *************************************************************/ 281 282 283 BTimeSource::BTimeSource() 284 : 285 BMediaNode("This one is never called"), 286 fStarted(false), 287 fArea(-1), 288 fBuf(NULL), 289 fSlaveNodes(new BPrivate::media::SlaveNodes), 290 fIsRealtime(false) 291 { 292 CALLED(); 293 AddNodeKind(B_TIME_SOURCE); 294 // This constructor is only called by real time sources that inherit 295 // BTimeSource. We create the communication area in FinishCreate(), 296 // since we don't have a correct ID() until this node is registered. 297 } 298 299 300 status_t 301 BTimeSource::HandleMessage(int32 message, const void* rawdata, 302 size_t size) 303 { 304 PRINT(4, "BTimeSource::HandleMessage %#" B_PRIx32 ", node %" B_PRId32 "\n", 305 message, fNodeID); 306 status_t rv; 307 switch (message) { 308 case TIMESOURCE_OP: 309 { 310 const time_source_op_info* data 311 = static_cast<const time_source_op_info*>(rawdata); 312 313 status_t result; 314 result = TimeSourceOp(*data, NULL); 315 if (result != B_OK) { 316 ERROR("BTimeSource::HandleMessage: TimeSourceOp failed\n"); 317 } 318 319 switch (data->op) { 320 case B_TIMESOURCE_START: 321 DirectStart(data->real_time); 322 break; 323 case B_TIMESOURCE_STOP: 324 DirectStop(data->real_time, false); 325 break; 326 case B_TIMESOURCE_STOP_IMMEDIATELY: 327 DirectStop(data->real_time, true); 328 break; 329 case B_TIMESOURCE_SEEK: 330 DirectSeek(data->performance_time, data->real_time); 331 break; 332 } 333 return B_OK; 334 } 335 336 case TIMESOURCE_ADD_SLAVE_NODE: 337 { 338 const timesource_add_slave_node_command* data 339 = static_cast<const timesource_add_slave_node_command*>(rawdata); 340 DirectAddMe(data->node); 341 return B_OK; 342 } 343 344 case TIMESOURCE_REMOVE_SLAVE_NODE: 345 { 346 const timesource_remove_slave_node_command* data 347 = static_cast<const timesource_remove_slave_node_command*>(rawdata); 348 DirectRemoveMe(data->node); 349 return B_OK; 350 } 351 352 case TIMESOURCE_GET_START_LATENCY: 353 { 354 const timesource_get_start_latency_request* request 355 = static_cast<const timesource_get_start_latency_request*>(rawdata); 356 timesource_get_start_latency_reply reply; 357 rv = GetStartLatency(&reply.start_latency); 358 request->SendReply(rv, &reply, sizeof(reply)); 359 return B_OK; 360 } 361 } 362 return B_ERROR; 363 } 364 365 366 void 367 BTimeSource::PublishTime(bigtime_t performance_time, 368 bigtime_t real_time, float drift) 369 { 370 TRACE_TIMESOURCE("BTimeSource::PublishTime timesource %" B_PRId32 371 ", perf %16" B_PRId64 ", real %16" B_PRId64 ", drift %2.2f\n", ID(), 372 performance_time, real_time, drift); 373 if (fBuf == NULL) { 374 ERROR("BTimeSource::PublishTime timesource %" B_PRId32 375 ", fBuf = NULL\n", ID()); 376 fStarted = true; 377 return; 378 } 379 380 if (drift <= 0.0f) 381 debugger("BTimeSource::PublishTime: drift must be > 0"); 382 383 int32 index = atomic_add(&fBuf->writeindex, 1); 384 index &= (TS_INDEX_COUNT - 1); 385 fBuf->realtime[index] = real_time; 386 fBuf->perftime[index] = performance_time; 387 fBuf->drift[index] = drift; 388 atomic_add(&fBuf->readindex, 1); 389 } 390 391 392 void 393 BTimeSource::BroadcastTimeWarp(bigtime_t at_real_time, 394 bigtime_t new_performance_time) 395 { 396 CALLED(); 397 ASSERT(fSlaveNodes != NULL); 398 399 // calls BMediaNode::TimeWarp() of all slaved nodes 400 401 TRACE("BTimeSource::BroadcastTimeWarp: at_real_time %" B_PRId64 402 ", new_performance_time %" B_PRId64 "\n", at_real_time, 403 new_performance_time); 404 405 BAutolock lock(fSlaveNodes); 406 407 port_id* port = NULL; 408 while (fSlaveNodes->GetNextSlave(&port) == true) { 409 node_time_warp_command cmd; 410 cmd.at_real_time = at_real_time; 411 cmd.to_performance_time = new_performance_time; 412 SendToPort(*port, NODE_TIME_WARP, 413 &cmd, sizeof(cmd)); 414 } 415 fSlaveNodes->Rewind(); 416 } 417 418 419 void 420 BTimeSource::SendRunMode(run_mode mode) 421 { 422 CALLED(); 423 ASSERT(fSlaveNodes != NULL); 424 425 // send the run mode change to all slaved nodes 426 427 BAutolock lock(fSlaveNodes); 428 429 port_id* port = NULL; 430 while (fSlaveNodes->GetNextSlave(&port) == true) { 431 node_set_run_mode_command cmd; 432 cmd.mode = mode; 433 SendToPort(*port, NODE_SET_RUN_MODE, 434 &cmd, sizeof(cmd)); 435 } 436 fSlaveNodes->Rewind(); 437 } 438 439 440 void 441 BTimeSource::SetRunMode(run_mode mode) 442 { 443 CALLED(); 444 BMediaNode::SetRunMode(mode); 445 SendRunMode(mode); 446 } 447 /************************************************************* 448 * private BTimeSource 449 *************************************************************/ 450 451 /* 452 //unimplemented 453 BTimeSource::BTimeSource(const BTimeSource &clone) 454 BTimeSource &BTimeSource::operator=(const BTimeSource &clone) 455 */ 456 457 status_t BTimeSource::_Reserved_TimeSource_0(void *) { return B_ERROR; } 458 status_t BTimeSource::_Reserved_TimeSource_1(void *) { return B_ERROR; } 459 status_t BTimeSource::_Reserved_TimeSource_2(void *) { return B_ERROR; } 460 status_t BTimeSource::_Reserved_TimeSource_3(void *) { return B_ERROR; } 461 status_t BTimeSource::_Reserved_TimeSource_4(void *) { return B_ERROR; } 462 status_t BTimeSource::_Reserved_TimeSource_5(void *) { return B_ERROR; } 463 464 /* explicit */ 465 BTimeSource::BTimeSource(media_node_id id) 466 : 467 BMediaNode("This one is never called"), 468 fStarted(false), 469 fArea(-1), 470 fBuf(NULL), 471 fSlaveNodes(NULL), 472 fIsRealtime(false) 473 { 474 CALLED(); 475 AddNodeKind(B_TIME_SOURCE); 476 ASSERT(id > 0); 477 478 // This constructor is only called by the derived 479 // BPrivate::media::TimeSourceObject objects 480 // We create a clone of the communication area. 481 char name[32]; 482 sprintf(name, "__timesource_buf_%" B_PRId32, id); 483 area_id area = find_area(name); 484 if (area <= 0) { 485 ERROR("BTimeSource::BTimeSource couldn't find area, node %" B_PRId32 486 "\n", id); 487 return; 488 } 489 sprintf(name, "__cloned_timesource_buf_%" B_PRId32, id); 490 491 void** buf = reinterpret_cast<void**> 492 (const_cast<BPrivate::media::TimeSourceTransmit**>(&fBuf)); 493 494 fArea = clone_area(name, buf, B_ANY_ADDRESS, 495 B_READ_AREA | B_WRITE_AREA, area); 496 497 if (fArea <= 0) { 498 ERROR("BTimeSource::BTimeSource couldn't clone area, node %" B_PRId32 499 "\n", id); 500 return; 501 } 502 } 503 504 505 void 506 BTimeSource::FinishCreate() 507 { 508 CALLED(); 509 510 char name[32]; 511 sprintf(name, "__timesource_buf_%" B_PRId32, ID()); 512 513 void** buf = reinterpret_cast<void**> 514 (const_cast<BPrivate::media::TimeSourceTransmit**>(&fBuf)); 515 516 fArea = create_area(name, buf, B_ANY_ADDRESS, TS_AREA_SIZE, 517 B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA); 518 519 if (fArea <= 0) { 520 ERROR("BTimeSource::BTimeSource couldn't create area, node %" B_PRId32 521 "\n", ID()); 522 fBuf = NULL; 523 return; 524 } 525 fBuf->readindex = 0; 526 fBuf->writeindex = 1; 527 fBuf->realtime[0] = 0; 528 fBuf->perftime[0] = 0; 529 fBuf->drift[0] = 1.0f; 530 fBuf->isrunning = fStarted; 531 } 532 533 534 status_t 535 BTimeSource::RemoveMe(BMediaNode* node) 536 { 537 CALLED(); 538 if (fKinds & NODE_KIND_SHADOW_TIMESOURCE) { 539 timesource_remove_slave_node_command cmd; 540 cmd.node = node->Node(); 541 SendToPort(fControlPort, TIMESOURCE_REMOVE_SLAVE_NODE, 542 &cmd, sizeof(cmd)); 543 } else { 544 DirectRemoveMe(node->Node()); 545 } 546 return B_OK; 547 } 548 549 550 status_t 551 BTimeSource::AddMe(BMediaNode* node) 552 { 553 CALLED(); 554 if (fKinds & NODE_KIND_SHADOW_TIMESOURCE) { 555 timesource_add_slave_node_command cmd; 556 cmd.node = node->Node(); 557 SendToPort(fControlPort, TIMESOURCE_ADD_SLAVE_NODE, &cmd, sizeof(cmd)); 558 } else { 559 DirectAddMe(node->Node()); 560 } 561 return B_OK; 562 } 563 564 565 void 566 BTimeSource::DirectAddMe(const media_node& node) 567 { 568 CALLED(); 569 ASSERT(fSlaveNodes != NULL); 570 BAutolock lock(fSlaveNodes); 571 572 if (fSlaveNodes->CountSlaves() == MAX_SLAVE_NODES) { 573 ERROR("BTimeSource::DirectAddMe reached maximum number of slaves\n"); 574 return; 575 } 576 if (fNodeID == node.node) { 577 ERROR("BTimeSource::DirectAddMe should not add itself to slave nodes\n"); 578 return; 579 } 580 581 if (fSlaveNodes->InsertSlave(node) != true) { 582 ERROR("BTimeSource::DirectAddMe failed\n"); 583 return; 584 } 585 586 if (fSlaveNodes->CountSlaves() == 1) { 587 // start the time source 588 time_source_op_info msg; 589 msg.op = B_TIMESOURCE_START; 590 msg.real_time = RealTime(); 591 592 TRACE_TIMESOURCE("starting time source %" B_PRId32 "\n", ID()); 593 594 write_port(fControlPort, TIMESOURCE_OP, &msg, sizeof(msg)); 595 } 596 } 597 598 599 void 600 BTimeSource::DirectRemoveMe(const media_node& node) 601 { 602 CALLED(); 603 ASSERT(fSlaveNodes != NULL); 604 BAutolock lock(fSlaveNodes); 605 606 if (fSlaveNodes->CountSlaves() == 0) { 607 ERROR("BTimeSource::DirectRemoveMe no slots used\n"); 608 return; 609 } 610 611 if (fSlaveNodes->RemoveSlave(node) != true) { 612 ERROR("BTimeSource::DirectRemoveMe failed\n"); 613 return; 614 } 615 616 if (fSlaveNodes->CountSlaves() == 0) { 617 // stop the time source 618 time_source_op_info msg; 619 msg.op = B_TIMESOURCE_STOP_IMMEDIATELY; 620 msg.real_time = RealTime(); 621 622 TRACE_TIMESOURCE("stopping time source %" B_PRId32 "\n", ID()); 623 624 write_port(fControlPort, TIMESOURCE_OP, &msg, sizeof(msg)); 625 } 626 } 627 628 629 void 630 BTimeSource::DirectStart(bigtime_t at) 631 { 632 CALLED(); 633 if (fBuf) 634 atomic_or(&fBuf->isrunning, 1); 635 else 636 fStarted = true; 637 } 638 639 640 void 641 BTimeSource::DirectStop(bigtime_t at, bool immediate) 642 { 643 CALLED(); 644 if (fBuf) 645 atomic_and(&fBuf->isrunning, 0); 646 else 647 fStarted = false; 648 } 649 650 651 void 652 BTimeSource::DirectSeek(bigtime_t to, bigtime_t at) 653 { 654 UNIMPLEMENTED(); 655 } 656 657 658 void 659 BTimeSource::DirectSetRunMode(run_mode mode) 660 { 661 UNIMPLEMENTED(); 662 } 663