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