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