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