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