xref: /haiku/src/kits/media/TimeSource.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
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 "MediaDebug.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 	if (fBuf == NULL)
231 		debugger("BTimeSource::GetTime: fBuf == NULL");
232 
233 	int32 index = _atomic_read(&fBuf->readindex);
234 	index &= (TS_INDEX_COUNT - 1);
235 	*real_time = fBuf->realtime[index];
236 	*performance_time = fBuf->perftime[index];
237 	*drift = fBuf->drift[index];
238 
239 	TRACE_TIMESOURCE("BTimeSource::GetTime     timesource %" B_PRId32
240 		", perf %16" B_PRId64 ", real %16" B_PRId64 ", drift %2.2f\n", ID(),
241 		*performance_time, *real_time, *drift);
242 	return B_OK;
243 }
244 
245 
246 bigtime_t
247 BTimeSource::RealTime()
248 {
249 	PRINT(8, "CALLED BTimeSource::RealTime()\n");
250 	return system_time();
251 }
252 
253 
254 status_t
255 BTimeSource::GetStartLatency(bigtime_t* out_latency)
256 {
257 	CALLED();
258 	*out_latency = 0;
259 	return B_OK;
260 }
261 
262 /*************************************************************
263  * protected BTimeSource
264  *************************************************************/
265 
266 
267 BTimeSource::BTimeSource()
268 	:
269 	BMediaNode("This one is never called"),
270 	fStarted(false),
271 	fArea(-1),
272 	fBuf(NULL),
273 	fSlaveNodes(new BPrivate::media::SlaveNodes),
274 	fIsRealtime(false)
275 {
276 	CALLED();
277 	AddNodeKind(B_TIME_SOURCE);
278 	// This constructor is only called by real time sources that inherit
279 	// BTimeSource. We create the communication area in FinishCreate(),
280 	// since we don't have a correct ID() until this node is registered.
281 }
282 
283 
284 status_t
285 BTimeSource::HandleMessage(int32 message, const void* rawdata,
286 	size_t size)
287 {
288 	PRINT(4, "BTimeSource::HandleMessage %#" B_PRIx32 ", node %" B_PRId32 "\n",
289 		message, fNodeID);
290 	status_t rv;
291 	switch (message) {
292 		case TIMESOURCE_OP:
293 		{
294 			const time_source_op_info* data
295 				= static_cast<const time_source_op_info*>(rawdata);
296 
297 			status_t result;
298 			result = TimeSourceOp(*data, NULL);
299 			if (result != B_OK) {
300 				ERROR("BTimeSource::HandleMessage: TimeSourceOp failed\n");
301 			}
302 
303 			switch (data->op) {
304 				case B_TIMESOURCE_START:
305 					DirectStart(data->real_time);
306 					break;
307 				case B_TIMESOURCE_STOP:
308 					DirectStop(data->real_time, false);
309 					break;
310 				case B_TIMESOURCE_STOP_IMMEDIATELY:
311 					DirectStop(data->real_time, true);
312 					break;
313 				case B_TIMESOURCE_SEEK:
314 					DirectSeek(data->performance_time, data->real_time);
315 					break;
316 			}
317 			return B_OK;
318 		}
319 
320 		case TIMESOURCE_ADD_SLAVE_NODE:
321 		{
322 			const timesource_add_slave_node_command* data
323 				= static_cast<const timesource_add_slave_node_command*>(rawdata);
324 			DirectAddMe(data->node);
325 			return B_OK;
326 		}
327 
328 		case TIMESOURCE_REMOVE_SLAVE_NODE:
329 		{
330 			const timesource_remove_slave_node_command* data
331 				= static_cast<const timesource_remove_slave_node_command*>(rawdata);
332 			DirectRemoveMe(data->node);
333 			return B_OK;
334 		}
335 
336 		case TIMESOURCE_GET_START_LATENCY:
337 		{
338 			const timesource_get_start_latency_request* request
339 				= static_cast<const timesource_get_start_latency_request*>(rawdata);
340 			timesource_get_start_latency_reply reply;
341 			rv = GetStartLatency(&reply.start_latency);
342 			request->SendReply(rv, &reply, sizeof(reply));
343 			return B_OK;
344 		}
345 	}
346 	return B_ERROR;
347 }
348 
349 
350 void
351 BTimeSource::PublishTime(bigtime_t performance_time,
352 	bigtime_t real_time, float drift)
353 {
354 	TRACE_TIMESOURCE("BTimeSource::PublishTime timesource %" B_PRId32
355 		", perf %16" B_PRId64 ", real %16" B_PRId64 ", drift %2.2f\n", ID(),
356 		performance_time, real_time, drift);
357 	if (0 == fBuf) {
358 		ERROR("BTimeSource::PublishTime timesource %" B_PRId32
359 			", fBuf = NULL\n", ID());
360 		fStarted = true;
361 		return;
362 	}
363 
364 	int32 index = atomic_add(&fBuf->writeindex, 1);
365 	index &= (TS_INDEX_COUNT - 1);
366 	fBuf->realtime[index] = real_time;
367 	fBuf->perftime[index] = performance_time;
368 	fBuf->drift[index] = drift;
369 	atomic_add(&fBuf->readindex, 1);
370 }
371 
372 
373 void
374 BTimeSource::BroadcastTimeWarp(bigtime_t at_real_time,
375 	bigtime_t new_performance_time)
376 {
377 	CALLED();
378 	ASSERT(fSlaveNodes != NULL);
379 
380 	// calls BMediaNode::TimeWarp() of all slaved nodes
381 
382 	TRACE("BTimeSource::BroadcastTimeWarp: at_real_time %" B_PRId64
383 		", new_performance_time %" B_PRId64 "\n", at_real_time,
384 		new_performance_time);
385 
386 	BAutolock lock(fSlaveNodes);
387 
388 	port_id* port = NULL;
389 	while (fSlaveNodes->GetNextSlave(&port) == true) {
390 		node_time_warp_command cmd;
391 		cmd.at_real_time = at_real_time;
392 		cmd.to_performance_time = new_performance_time;
393 		SendToPort(*port, NODE_TIME_WARP,
394 			&cmd, sizeof(cmd));
395 	}
396 	fSlaveNodes->Rewind();
397 }
398 
399 
400 void
401 BTimeSource::SendRunMode(run_mode mode)
402 {
403 	CALLED();
404 	ASSERT(fSlaveNodes != NULL);
405 
406 	// send the run mode change to all slaved nodes
407 
408 	BAutolock lock(fSlaveNodes);
409 
410 	port_id* port = NULL;
411 	while (fSlaveNodes->GetNextSlave(&port) == true) {
412 		node_set_run_mode_command cmd;
413 		cmd.mode = mode;
414 		SendToPort(*port, NODE_SET_RUN_MODE,
415 			&cmd, sizeof(cmd));
416 	}
417 	fSlaveNodes->Rewind();
418 }
419 
420 
421 void
422 BTimeSource::SetRunMode(run_mode mode)
423 {
424 	CALLED();
425 	BMediaNode::SetRunMode(mode);
426 	SendRunMode(mode);
427 }
428 /*************************************************************
429  * private BTimeSource
430  *************************************************************/
431 
432 /*
433 //unimplemented
434 BTimeSource::BTimeSource(const BTimeSource &clone)
435 BTimeSource &BTimeSource::operator=(const BTimeSource &clone)
436 */
437 
438 status_t BTimeSource::_Reserved_TimeSource_0(void *) { return B_ERROR; }
439 status_t BTimeSource::_Reserved_TimeSource_1(void *) { return B_ERROR; }
440 status_t BTimeSource::_Reserved_TimeSource_2(void *) { return B_ERROR; }
441 status_t BTimeSource::_Reserved_TimeSource_3(void *) { return B_ERROR; }
442 status_t BTimeSource::_Reserved_TimeSource_4(void *) { return B_ERROR; }
443 status_t BTimeSource::_Reserved_TimeSource_5(void *) { return B_ERROR; }
444 
445 /* explicit */
446 BTimeSource::BTimeSource(media_node_id id)
447 	:
448 	BMediaNode("This one is never called"),
449 	fStarted(false),
450 	fArea(-1),
451 	fBuf(NULL),
452 	fSlaveNodes(NULL),
453 	fIsRealtime(false)
454 {
455 	CALLED();
456 	AddNodeKind(B_TIME_SOURCE);
457 	ASSERT(id > 0);
458 
459 	// This constructor is only called by the derived
460 	// BPrivate::media::TimeSourceObject objects
461 	// We create a clone of the communication area.
462 	char name[32];
463 	sprintf(name, "__timesource_buf_%" B_PRId32, id);
464 	area_id area = find_area(name);
465 	if (area <= 0) {
466 		ERROR("BTimeSource::BTimeSource couldn't find area, node %" B_PRId32
467 			"\n", id);
468 		return;
469 	}
470 	sprintf(name, "__cloned_timesource_buf_%" B_PRId32, id);
471 
472 	void** buf = reinterpret_cast<void**>
473 		(const_cast<BPrivate::media::TimeSourceTransmit**>(&fBuf));
474 
475 	fArea = clone_area(name, buf, B_ANY_ADDRESS,
476 		B_READ_AREA | B_WRITE_AREA, area);
477 
478 	if (fArea <= 0) {
479 		ERROR("BTimeSource::BTimeSource couldn't clone area, node %" B_PRId32
480 			"\n", id);
481 		return;
482 	}
483 }
484 
485 
486 void
487 BTimeSource::FinishCreate()
488 {
489 	CALLED();
490 
491 	char name[32];
492 	sprintf(name, "__timesource_buf_%" B_PRId32, ID());
493 
494 	void** buf = reinterpret_cast<void**>
495 		(const_cast<BPrivate::media::TimeSourceTransmit**>(&fBuf));
496 
497 	fArea = create_area(name, buf, B_ANY_ADDRESS, TS_AREA_SIZE,
498 		B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA);
499 
500 	if (fArea <= 0) {
501 		ERROR("BTimeSource::BTimeSource couldn't create area, node %" B_PRId32
502 			"\n", ID());
503 		fBuf = NULL;
504 		return;
505 	}
506 	fBuf->readindex = 0;
507 	fBuf->writeindex = 1;
508 	fBuf->realtime[0] = 0;
509 	fBuf->perftime[0] = 0;
510 	fBuf->drift[0] = 1.0f;
511 	fBuf->isrunning = fStarted;
512 }
513 
514 
515 status_t
516 BTimeSource::RemoveMe(BMediaNode* node)
517 {
518 	CALLED();
519 	if (fKinds & NODE_KIND_SHADOW_TIMESOURCE) {
520 		timesource_remove_slave_node_command cmd;
521 		cmd.node = node->Node();
522 		SendToPort(fControlPort, TIMESOURCE_REMOVE_SLAVE_NODE,
523 			&cmd, sizeof(cmd));
524 	} else {
525 		DirectRemoveMe(node->Node());
526 	}
527 	return B_OK;
528 }
529 
530 
531 status_t
532 BTimeSource::AddMe(BMediaNode* node)
533 {
534 	CALLED();
535 	if (fKinds & NODE_KIND_SHADOW_TIMESOURCE) {
536 		timesource_add_slave_node_command cmd;
537 		cmd.node = node->Node();
538 		SendToPort(fControlPort, TIMESOURCE_ADD_SLAVE_NODE, &cmd, sizeof(cmd));
539 	} else {
540 		DirectAddMe(node->Node());
541 	}
542 	return B_OK;
543 }
544 
545 
546 void
547 BTimeSource::DirectAddMe(const media_node& node)
548 {
549 	CALLED();
550 	ASSERT(fSlaveNodes != NULL);
551 	BAutolock lock(fSlaveNodes);
552 
553 	if (fSlaveNodes->CountSlaves() == MAX_SLAVE_NODES) {
554 		ERROR("BTimeSource::DirectAddMe reached maximum number of slaves\n");
555 		return;
556 	}
557 	if (fNodeID == node.node) {
558 		ERROR("BTimeSource::DirectAddMe should not add itself to slave nodes\n");
559 		return;
560 	}
561 
562 	if (fSlaveNodes->InsertSlave(node) != true) {
563 		ERROR("BTimeSource::DirectAddMe failed\n");
564 		return;
565 	}
566 
567 	if (fSlaveNodes->CountSlaves() == 1) {
568 		// start the time source
569 		time_source_op_info msg;
570 		msg.op = B_TIMESOURCE_START;
571 		msg.real_time = RealTime();
572 
573 		TRACE_TIMESOURCE("starting time source %" B_PRId32 "\n", ID());
574 
575 		write_port(fControlPort, TIMESOURCE_OP, &msg, sizeof(msg));
576 	}
577  }
578 
579 
580 void
581 BTimeSource::DirectRemoveMe(const media_node& node)
582 {
583 	CALLED();
584 	ASSERT(fSlaveNodes != NULL);
585 	BAutolock lock(fSlaveNodes);
586 
587 	if (fSlaveNodes->CountSlaves() == 0) {
588 		ERROR("BTimeSource::DirectRemoveMe no slots used\n");
589 		return;
590 	}
591 
592 	if (fSlaveNodes->RemoveSlave(node) != true) {
593 		ERROR("BTimeSource::DirectRemoveMe failed\n");
594 		return;
595 	}
596 
597 	if (fSlaveNodes->CountSlaves() == 0) {
598 		// stop the time source
599 		time_source_op_info msg;
600 		msg.op = B_TIMESOURCE_STOP_IMMEDIATELY;
601 		msg.real_time = RealTime();
602 
603 		TRACE_TIMESOURCE("stopping time source %" B_PRId32 "\n", ID());
604 
605 		write_port(fControlPort, TIMESOURCE_OP, &msg, sizeof(msg));
606 	}
607 }
608 
609 
610 void
611 BTimeSource::DirectStart(bigtime_t at)
612 {
613 	CALLED();
614 	if (fBuf)
615 		atomic_or(&fBuf->isrunning, 1);
616 	else
617 		fStarted = true;
618 }
619 
620 
621 void
622 BTimeSource::DirectStop(bigtime_t at, bool immediate)
623 {
624 	CALLED();
625 	if (fBuf)
626 		atomic_and(&fBuf->isrunning, 0);
627 	else
628 		fStarted = false;
629 }
630 
631 
632 void
633 BTimeSource::DirectSeek(bigtime_t to, bigtime_t at)
634 {
635 	UNIMPLEMENTED();
636 }
637 
638 
639 void
640 BTimeSource::DirectSetRunMode(run_mode mode)
641 {
642 	UNIMPLEMENTED();
643 }
644