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