xref: /haiku/src/kits/media/TimeSource.cpp (revision e5430a086c769ea76c3944046b1f07cf049c1ae0)
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