1 /* 2 * Copyright 2002-2012, 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 return last_perf_time 173 + (bigtime_t)((real_time - last_real_time) * last_drift); 174 } 175 176 177 bigtime_t 178 BTimeSource::RealTimeFor(bigtime_t performance_time, 179 bigtime_t with_latency) 180 { 181 PRINT(8, "CALLED BTimeSource::RealTimeFor()\n"); 182 183 if (fIsRealtime) { 184 return performance_time - with_latency; 185 } 186 187 bigtime_t last_perf_time; 188 bigtime_t last_real_time; 189 float last_drift; 190 191 if (GetTime(&last_perf_time, &last_real_time, &last_drift) != B_OK) 192 debugger("BTimeSource::RealTimeFor: GetTime failed"); 193 194 return last_real_time - with_latency 195 + (bigtime_t)((performance_time - last_perf_time) / last_drift); 196 } 197 198 199 bool 200 BTimeSource::IsRunning() 201 { 202 PRINT(8, "CALLED BTimeSource::IsRunning()\n"); 203 204 bool isrunning; 205 206 // The system time source is always running 207 if (fIsRealtime) 208 isrunning = true; 209 else 210 isrunning = fBuf ? atomic_add(&fBuf->isrunning, 0) : fStarted; 211 212 TRACE_TIMESOURCE("BTimeSource::IsRunning() node %" B_PRId32 ", port %" 213 B_PRId32 ", %s\n", fNodeID, fControlPort, isrunning ? "yes" : "no"); 214 return isrunning; 215 } 216 217 218 status_t 219 BTimeSource::GetTime(bigtime_t* performance_time, 220 bigtime_t* real_time, float* drift) 221 { 222 PRINT(8, "CALLED BTimeSource::GetTime()\n"); 223 224 if (fIsRealtime) { 225 *performance_time = *real_time = system_time(); 226 *drift = 1.0f; 227 return B_OK; 228 } 229 230 if (fBuf == NULL) 231 debugger("BTimeSource::GetTime: fBuf == NULL"); 232 233 int32 index = _atomic_read(&fBuf->readindex); 234 index &= (TS_INDEX_COUNT - 1); 235 *real_time = fBuf->realtime[index]; 236 *performance_time = fBuf->perftime[index]; 237 *drift = fBuf->drift[index]; 238 239 TRACE_TIMESOURCE("BTimeSource::GetTime timesource %" B_PRId32 240 ", perf %16" B_PRId64 ", real %16" B_PRId64 ", drift %2.2f\n", ID(), 241 *performance_time, *real_time, *drift); 242 return B_OK; 243 } 244 245 246 bigtime_t 247 BTimeSource::RealTime() 248 { 249 PRINT(8, "CALLED BTimeSource::RealTime()\n"); 250 return system_time(); 251 } 252 253 254 status_t 255 BTimeSource::GetStartLatency(bigtime_t* out_latency) 256 { 257 CALLED(); 258 *out_latency = 0; 259 return B_OK; 260 } 261 262 /************************************************************* 263 * protected BTimeSource 264 *************************************************************/ 265 266 267 BTimeSource::BTimeSource() 268 : 269 BMediaNode("This one is never called"), 270 fStarted(false), 271 fArea(-1), 272 fBuf(NULL), 273 fSlaveNodes(new BPrivate::media::SlaveNodes), 274 fIsRealtime(false) 275 { 276 CALLED(); 277 AddNodeKind(B_TIME_SOURCE); 278 // This constructor is only called by real time sources that inherit 279 // BTimeSource. We create the communication area in FinishCreate(), 280 // since we don't have a correct ID() until this node is registered. 281 } 282 283 284 status_t 285 BTimeSource::HandleMessage(int32 message, const void* rawdata, 286 size_t size) 287 { 288 PRINT(4, "BTimeSource::HandleMessage %#" B_PRIx32 ", node %" B_PRId32 "\n", 289 message, fNodeID); 290 status_t rv; 291 switch (message) { 292 case TIMESOURCE_OP: 293 { 294 const time_source_op_info* data 295 = static_cast<const time_source_op_info*>(rawdata); 296 297 status_t result; 298 result = TimeSourceOp(*data, NULL); 299 if (result != B_OK) { 300 ERROR("BTimeSource::HandleMessage: TimeSourceOp failed\n"); 301 } 302 303 switch (data->op) { 304 case B_TIMESOURCE_START: 305 DirectStart(data->real_time); 306 break; 307 case B_TIMESOURCE_STOP: 308 DirectStop(data->real_time, false); 309 break; 310 case B_TIMESOURCE_STOP_IMMEDIATELY: 311 DirectStop(data->real_time, true); 312 break; 313 case B_TIMESOURCE_SEEK: 314 DirectSeek(data->performance_time, data->real_time); 315 break; 316 } 317 return B_OK; 318 } 319 320 case TIMESOURCE_ADD_SLAVE_NODE: 321 { 322 const timesource_add_slave_node_command* data 323 = static_cast<const timesource_add_slave_node_command*>(rawdata); 324 DirectAddMe(data->node); 325 return B_OK; 326 } 327 328 case TIMESOURCE_REMOVE_SLAVE_NODE: 329 { 330 const timesource_remove_slave_node_command* data 331 = static_cast<const timesource_remove_slave_node_command*>(rawdata); 332 DirectRemoveMe(data->node); 333 return B_OK; 334 } 335 336 case TIMESOURCE_GET_START_LATENCY: 337 { 338 const timesource_get_start_latency_request* request 339 = static_cast<const timesource_get_start_latency_request*>(rawdata); 340 timesource_get_start_latency_reply reply; 341 rv = GetStartLatency(&reply.start_latency); 342 request->SendReply(rv, &reply, sizeof(reply)); 343 return B_OK; 344 } 345 } 346 return B_ERROR; 347 } 348 349 350 void 351 BTimeSource::PublishTime(bigtime_t performance_time, 352 bigtime_t real_time, float drift) 353 { 354 TRACE_TIMESOURCE("BTimeSource::PublishTime timesource %" B_PRId32 355 ", perf %16" B_PRId64 ", real %16" B_PRId64 ", drift %2.2f\n", ID(), 356 performance_time, real_time, drift); 357 if (0 == fBuf) { 358 ERROR("BTimeSource::PublishTime timesource %" B_PRId32 359 ", fBuf = NULL\n", ID()); 360 fStarted = true; 361 return; 362 } 363 364 int32 index = atomic_add(&fBuf->writeindex, 1); 365 index &= (TS_INDEX_COUNT - 1); 366 fBuf->realtime[index] = real_time; 367 fBuf->perftime[index] = performance_time; 368 fBuf->drift[index] = drift; 369 atomic_add(&fBuf->readindex, 1); 370 } 371 372 373 void 374 BTimeSource::BroadcastTimeWarp(bigtime_t at_real_time, 375 bigtime_t new_performance_time) 376 { 377 CALLED(); 378 ASSERT(fSlaveNodes != NULL); 379 380 // calls BMediaNode::TimeWarp() of all slaved nodes 381 382 TRACE("BTimeSource::BroadcastTimeWarp: at_real_time %" B_PRId64 383 ", new_performance_time %" B_PRId64 "\n", at_real_time, 384 new_performance_time); 385 386 BAutolock lock(fSlaveNodes); 387 388 port_id* port = NULL; 389 while (fSlaveNodes->GetNextSlave(&port) == true) { 390 node_time_warp_command cmd; 391 cmd.at_real_time = at_real_time; 392 cmd.to_performance_time = new_performance_time; 393 SendToPort(*port, NODE_TIME_WARP, 394 &cmd, sizeof(cmd)); 395 } 396 fSlaveNodes->Rewind(); 397 } 398 399 400 void 401 BTimeSource::SendRunMode(run_mode mode) 402 { 403 CALLED(); 404 ASSERT(fSlaveNodes != NULL); 405 406 // send the run mode change to all slaved nodes 407 408 BAutolock lock(fSlaveNodes); 409 410 port_id* port = NULL; 411 while (fSlaveNodes->GetNextSlave(&port) == true) { 412 node_set_run_mode_command cmd; 413 cmd.mode = mode; 414 SendToPort(*port, NODE_SET_RUN_MODE, 415 &cmd, sizeof(cmd)); 416 } 417 fSlaveNodes->Rewind(); 418 } 419 420 421 void 422 BTimeSource::SetRunMode(run_mode mode) 423 { 424 CALLED(); 425 BMediaNode::SetRunMode(mode); 426 SendRunMode(mode); 427 } 428 /************************************************************* 429 * private BTimeSource 430 *************************************************************/ 431 432 /* 433 //unimplemented 434 BTimeSource::BTimeSource(const BTimeSource &clone) 435 BTimeSource &BTimeSource::operator=(const BTimeSource &clone) 436 */ 437 438 status_t BTimeSource::_Reserved_TimeSource_0(void *) { return B_ERROR; } 439 status_t BTimeSource::_Reserved_TimeSource_1(void *) { return B_ERROR; } 440 status_t BTimeSource::_Reserved_TimeSource_2(void *) { return B_ERROR; } 441 status_t BTimeSource::_Reserved_TimeSource_3(void *) { return B_ERROR; } 442 status_t BTimeSource::_Reserved_TimeSource_4(void *) { return B_ERROR; } 443 status_t BTimeSource::_Reserved_TimeSource_5(void *) { return B_ERROR; } 444 445 /* explicit */ 446 BTimeSource::BTimeSource(media_node_id id) 447 : 448 BMediaNode("This one is never called"), 449 fStarted(false), 450 fArea(-1), 451 fBuf(NULL), 452 fSlaveNodes(NULL), 453 fIsRealtime(false) 454 { 455 CALLED(); 456 AddNodeKind(B_TIME_SOURCE); 457 ASSERT(id > 0); 458 459 // This constructor is only called by the derived 460 // BPrivate::media::TimeSourceObject objects 461 // We create a clone of the communication area. 462 char name[32]; 463 sprintf(name, "__timesource_buf_%" B_PRId32, id); 464 area_id area = find_area(name); 465 if (area <= 0) { 466 ERROR("BTimeSource::BTimeSource couldn't find area, node %" B_PRId32 467 "\n", id); 468 return; 469 } 470 sprintf(name, "__cloned_timesource_buf_%" B_PRId32, id); 471 472 void** buf = reinterpret_cast<void**> 473 (const_cast<BPrivate::media::TimeSourceTransmit**>(&fBuf)); 474 475 fArea = clone_area(name, buf, B_ANY_ADDRESS, 476 B_READ_AREA | B_WRITE_AREA, area); 477 478 if (fArea <= 0) { 479 ERROR("BTimeSource::BTimeSource couldn't clone area, node %" B_PRId32 480 "\n", id); 481 return; 482 } 483 } 484 485 486 void 487 BTimeSource::FinishCreate() 488 { 489 CALLED(); 490 491 char name[32]; 492 sprintf(name, "__timesource_buf_%" B_PRId32, ID()); 493 494 void** buf = reinterpret_cast<void**> 495 (const_cast<BPrivate::media::TimeSourceTransmit**>(&fBuf)); 496 497 fArea = create_area(name, buf, B_ANY_ADDRESS, TS_AREA_SIZE, 498 B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA); 499 500 if (fArea <= 0) { 501 ERROR("BTimeSource::BTimeSource couldn't create area, node %" B_PRId32 502 "\n", ID()); 503 fBuf = NULL; 504 return; 505 } 506 fBuf->readindex = 0; 507 fBuf->writeindex = 1; 508 fBuf->realtime[0] = 0; 509 fBuf->perftime[0] = 0; 510 fBuf->drift[0] = 1.0f; 511 fBuf->isrunning = fStarted; 512 } 513 514 515 status_t 516 BTimeSource::RemoveMe(BMediaNode* node) 517 { 518 CALLED(); 519 if (fKinds & NODE_KIND_SHADOW_TIMESOURCE) { 520 timesource_remove_slave_node_command cmd; 521 cmd.node = node->Node(); 522 SendToPort(fControlPort, TIMESOURCE_REMOVE_SLAVE_NODE, 523 &cmd, sizeof(cmd)); 524 } else { 525 DirectRemoveMe(node->Node()); 526 } 527 return B_OK; 528 } 529 530 531 status_t 532 BTimeSource::AddMe(BMediaNode* node) 533 { 534 CALLED(); 535 if (fKinds & NODE_KIND_SHADOW_TIMESOURCE) { 536 timesource_add_slave_node_command cmd; 537 cmd.node = node->Node(); 538 SendToPort(fControlPort, TIMESOURCE_ADD_SLAVE_NODE, &cmd, sizeof(cmd)); 539 } else { 540 DirectAddMe(node->Node()); 541 } 542 return B_OK; 543 } 544 545 546 void 547 BTimeSource::DirectAddMe(const media_node& node) 548 { 549 CALLED(); 550 ASSERT(fSlaveNodes != NULL); 551 BAutolock lock(fSlaveNodes); 552 553 if (fSlaveNodes->CountSlaves() == MAX_SLAVE_NODES) { 554 ERROR("BTimeSource::DirectAddMe reached maximum number of slaves\n"); 555 return; 556 } 557 if (fNodeID == node.node) { 558 ERROR("BTimeSource::DirectAddMe should not add itself to slave nodes\n"); 559 return; 560 } 561 562 if (fSlaveNodes->InsertSlave(node) != true) { 563 ERROR("BTimeSource::DirectAddMe failed\n"); 564 return; 565 } 566 567 if (fSlaveNodes->CountSlaves() == 1) { 568 // start the time source 569 time_source_op_info msg; 570 msg.op = B_TIMESOURCE_START; 571 msg.real_time = RealTime(); 572 573 TRACE_TIMESOURCE("starting time source %" B_PRId32 "\n", ID()); 574 575 write_port(fControlPort, TIMESOURCE_OP, &msg, sizeof(msg)); 576 } 577 } 578 579 580 void 581 BTimeSource::DirectRemoveMe(const media_node& node) 582 { 583 CALLED(); 584 ASSERT(fSlaveNodes != NULL); 585 BAutolock lock(fSlaveNodes); 586 587 if (fSlaveNodes->CountSlaves() == 0) { 588 ERROR("BTimeSource::DirectRemoveMe no slots used\n"); 589 return; 590 } 591 592 if (fSlaveNodes->RemoveSlave(node) != true) { 593 ERROR("BTimeSource::DirectRemoveMe failed\n"); 594 return; 595 } 596 597 if (fSlaveNodes->CountSlaves() == 0) { 598 // stop the time source 599 time_source_op_info msg; 600 msg.op = B_TIMESOURCE_STOP_IMMEDIATELY; 601 msg.real_time = RealTime(); 602 603 TRACE_TIMESOURCE("stopping time source %" B_PRId32 "\n", ID()); 604 605 write_port(fControlPort, TIMESOURCE_OP, &msg, sizeof(msg)); 606 } 607 } 608 609 610 void 611 BTimeSource::DirectStart(bigtime_t at) 612 { 613 CALLED(); 614 if (fBuf) 615 atomic_or(&fBuf->isrunning, 1); 616 else 617 fStarted = true; 618 } 619 620 621 void 622 BTimeSource::DirectStop(bigtime_t at, bool immediate) 623 { 624 CALLED(); 625 if (fBuf) 626 atomic_and(&fBuf->isrunning, 0); 627 else 628 fStarted = false; 629 } 630 631 632 void 633 BTimeSource::DirectSeek(bigtime_t to, bigtime_t at) 634 { 635 UNIMPLEMENTED(); 636 } 637 638 639 void 640 BTimeSource::DirectSetRunMode(run_mode mode) 641 { 642 UNIMPLEMENTED(); 643 } 644