1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28#include "interface/vcos/vcos.h"
29#include "interface/mmal/mmal_logging.h"
30#include "interface/mmal/util/mmal_list.h"
31#include "interface/mmal/util/mmal_util_rational.h"
32#include "interface/mmal/core/mmal_clock_private.h"
33
34#ifdef __VIDEOCORE__
35/* Use RTOS timer for improved accuracy */
36# include "vcfw/rtos/rtos.h"
37# define USE_RTOS_TIMER
38#endif
39
40
41/*****************************************************************************/
42#ifdef USE_RTOS_TIMER
43# define MIN_TIMER_DELAY 1 /* microseconds */
44#else
45# define MIN_TIMER_DELAY 10000 /* microseconds */
46#endif
47
48/* 1.0 in Q16 format */
49#define Q16_ONE (1 << 16)
50
51/* Maximum number of pending requests */
52#define CLOCK_REQUEST_SLOTS 32
53
54/* Number of microseconds the clock tries to service requests early
55 * to account for processing overhead */
56#define CLOCK_TARGET_OFFSET 20
57
58/* Default wait time (in microseconds) when the clock is paused. */
59#define CLOCK_WAIT_TIME 200000LL
60
61/* In order to prevent unnecessary clock jitter when updating the local media-time of the
62 * clock, an upper and lower threshold is used. If the difference between the reference
63 * media-time and local media-time is greater than the upper threshold, local media-time
64 * is set to the reference time. Below this threshold, a weighted moving average is applied
65 * to the difference. If this is greater than the lower threshold, the local media-time is
66 * adjusted by the average. Anything below the lower threshold is ignored. */
67#define CLOCK_UPDATE_THRESHOLD_LOWER 8000 /* microseconds */
68#define CLOCK_UPDATE_THRESHOLD_UPPER 50000 /* microseconds */
69
70/* Default threshold after which backward jumps in media time are treated as a discontinuity. */
71#define CLOCK_DISCONT_THRESHOLD 1000000 /* microseconds */
72
73/* Default duration for which a discontinuity applies. Used for wall time duration for which
74 * a discontinuity continues to cause affected requests to fire immediately, and as the media
75 * time span for detecting discontinuous requests. */
76#define CLOCK_DISCONT_DURATION 1000000 /* microseconds */
77
78/* Absolute value macro */
79#define ABS_VALUE(v) (((v) < 0) ? -(v) : (v))
80
81/* Macros used to make clock access thread-safe */
82#define LOCK(p) vcos_mutex_lock(&(p)->lock);
83#define UNLOCK(p) vcos_mutex_unlock(&(p)->lock);
84
85
86/*****************************************************************************/
87#ifdef USE_RTOS_TIMER
88typedef RTOS_TIMER_T MMAL_TIMER_T;
89#else
90typedef VCOS_TIMER_T MMAL_TIMER_T;
91#endif
92
93typedef struct MMAL_CLOCK_REQUEST_T
94{
95 MMAL_LIST_ELEMENT_T link; /**< must be first */
96 MMAL_CLOCK_VOID_FP priv; /**< client-supplied function pointer */
97 MMAL_CLOCK_REQUEST_CB cb; /**< client-supplied callback to invoke */
98 void *cb_data; /**< client-supplied callback data */
99 int64_t media_time; /**< media-time requested by the client (microseconds) */
100 int64_t media_time_adj; /**< adjusted media-time at which the request will
101 be serviced in microseconds (this takes
102 CLOCK_TARGET_OFFSET into account) */
103} MMAL_CLOCK_REQUEST_T;
104
105typedef struct MMAL_CLOCK_PRIVATE_T
106{
107 MMAL_CLOCK_T clock; /**< must be first */
108
109 MMAL_BOOL_T is_active; /**< TRUE -> media-time is advancing */
110
111 MMAL_BOOL_T scheduling; /**< TRUE -> client request scheduling is enabled */
112 MMAL_BOOL_T stop_thread;
113 VCOS_SEMAPHORE_T event;
114 VCOS_THREAD_T thread; /**< processing thread for client requests */
115 MMAL_TIMER_T timer; /**< used for scheduling client requests */
116
117 VCOS_MUTEX_T lock; /**< lock access to the request lists */
118
119 int32_t scale; /**< media-time scale factor (Q16 format) */
120 int32_t scale_inv; /**< 1/scale (Q16 format) */
121 MMAL_RATIONAL_T scale_rational;
122 /**< clock scale as a rational number; keep a copy since
123 converting from Q16 will result in precision errors */
124
125 int64_t average_ref_diff; /**< media-time moving average adjustment */
126 int64_t media_time; /**< current local media-time in microseconds */
127 uint32_t media_time_frac; /**< media-time fraction in microseconds (Q24 format) */
128 int64_t wall_time; /**< current local wall-time (microseconds) */
129 uint32_t rtc_at_update; /**< real-time clock value at local time update (microseconds) */
130 int64_t media_time_at_timer;
131 /**< media-time when the timer was last set */
132
133 int64_t discont_expiry; /**< wall-time when discontinuity expires; 0 = no discontinuity
134 in effect */
135 int64_t discont_start; /**< media-time at start of discontinuity
136 (n/a if discont_expiry = 0) */
137 int64_t discont_end; /**< media-time at end of discontinuity
138 (n/a if discont_expiry = 0) */
139 int64_t discont_threshold;/**< Threshold after which backward jumps in media time are treated
140 as a discontinuity (microseconds) */
141 int64_t discont_duration; /**< Duration (wall-time) for which a discontinuity applies */
142
143 int64_t request_threshold;/**< Threshold after which frames exceeding the media-time are
144 dropped (microseconds) */
145 MMAL_BOOL_T request_threshold_enable;/**< Enable the request threshold */
146 int64_t update_threshold_lower;
147 /**< Time differences below this threshold are ignored */
148 int64_t update_threshold_upper;
149 /**< Time differences above this threshold reset media time */
150
151 /* Client requests */
152 struct
153 {
154 MMAL_LIST_T* list_free;
155 MMAL_LIST_T* list_pending;
156 MMAL_CLOCK_REQUEST_T pool[CLOCK_REQUEST_SLOTS];
157 } request;
158
159} MMAL_CLOCK_PRIVATE_T;
160
161
162/*****************************************************************************/
163static void mmal_clock_wake_thread(MMAL_CLOCK_PRIVATE_T *private);
164
165/*****************************************************************************
166 * Timer-specific functions
167 *****************************************************************************/
168/* Callback invoked when timer expires */
169#ifdef USE_RTOS_TIMER
170static void mmal_clock_timer_cb(MMAL_TIMER_T *timer, void *ctx)
171{
172 MMAL_PARAM_UNUSED(timer);
173 /* Notify the worker thread */
174 mmal_clock_wake_thread((MMAL_CLOCK_PRIVATE_T*)ctx);
175}
176#else
177static void mmal_clock_timer_cb(void *ctx)
178{
179 /* Notify the worker thread */
180 mmal_clock_wake_thread((MMAL_CLOCK_PRIVATE_T*)ctx);
181}
182#endif
183
184/* Create a timer */
185static inline MMAL_BOOL_T mmal_clock_timer_create(MMAL_TIMER_T *timer, void *ctx)
186{
187#ifdef USE_RTOS_TIMER
188 return (rtos_timer_init(timer, mmal_clock_timer_cb, ctx) == 0);
189#else
190 return (vcos_timer_create(timer, "mmal-clock timer", mmal_clock_timer_cb, ctx) == VCOS_SUCCESS);
191#endif
192}
193
194/* Destroy a timer */
195static inline void mmal_clock_timer_destroy(MMAL_TIMER_T *timer)
196{
197#ifdef USE_RTOS_TIMER
198 /* Nothing to do */
199#else
200 vcos_timer_delete(timer);
201#endif
202}
203
204/* Set the timer. Delay is in microseconds. */
205static inline void mmal_clock_timer_set(MMAL_TIMER_T *timer, int64_t delay_us)
206{
207#ifdef USE_RTOS_TIMER
208 rtos_timer_set(timer, (RTOS_TIMER_TIME_T)delay_us);
209#else
210 /* VCOS timer only provides millisecond accuracy */
211 vcos_timer_set(timer, (VCOS_UNSIGNED)(delay_us / 1000));
212#endif
213}
214
215/* Stop the timer. */
216static inline void mmal_clock_timer_cancel(MMAL_TIMER_T *timer)
217{
218#ifdef USE_RTOS_TIMER
219 rtos_timer_cancel(timer);
220#else
221 vcos_timer_cancel(timer);
222#endif
223}
224
225
226/*****************************************************************************
227 * Clock module private functions
228 *****************************************************************************/
229/* Update the internal wall-time and media-time */
230static void mmal_clock_update_local_time_locked(MMAL_CLOCK_PRIVATE_T *private)
231{
232 uint32_t time_now = vcos_getmicrosecs();
233 uint32_t time_diff = (time_now > private->rtc_at_update) ? (time_now - private->rtc_at_update) : 0;
234
235 private->wall_time += time_diff;
236
237 /* For small clock scale values (i.e. slow motion), the media-time increment
238 * could potentially be rounded down when doing lots of updates, so also keep
239 * track of the fractional increment. */
240 int64_t media_diff = ((int64_t)time_diff) * (int64_t)(private->scale << 8) + private->media_time_frac;
241
242 private->media_time += media_diff >> 24;
243 private->media_time_frac = media_diff & ((1<<24)-1);
244
245 private->rtc_at_update = time_now;
246}
247
248/* Return the current local media-time */
249static int64_t mmal_clock_media_time_get_locked(MMAL_CLOCK_PRIVATE_T *private)
250{
251 mmal_clock_update_local_time_locked(private);
252 return private->media_time;
253}
254
255/* Comparison function used for inserting a request into
256 * the list of pending requests when clock scale is positive. */
257static int mmal_clock_request_compare_pos(MMAL_LIST_ELEMENT_T *lhs, MMAL_LIST_ELEMENT_T *rhs)
258{
259 return ((MMAL_CLOCK_REQUEST_T*)lhs)->media_time_adj < ((MMAL_CLOCK_REQUEST_T*)rhs)->media_time_adj;
260}
261
262/* Comparison function used for inserting a request into
263 * the list of pending requests when clock scale is negative. */
264static int mmal_clock_request_compare_neg(MMAL_LIST_ELEMENT_T *lhs, MMAL_LIST_ELEMENT_T *rhs)
265{
266 return ((MMAL_CLOCK_REQUEST_T*)lhs)->media_time_adj > ((MMAL_CLOCK_REQUEST_T*)rhs)->media_time_adj;
267}
268
269/* Insert a new request into the list of pending requests */
270static MMAL_BOOL_T mmal_clock_request_insert(MMAL_CLOCK_PRIVATE_T *private, MMAL_CLOCK_REQUEST_T *request)
271{
272 MMAL_LIST_T *list = private->request.list_pending;
273 MMAL_CLOCK_REQUEST_T *pending;
274
275 if (private->stop_thread)
276 return MMAL_FALSE; /* the clock is being destroyed */
277
278 if (list->length == 0)
279 {
280 mmal_list_push_front(list, &request->link);
281 return MMAL_TRUE;
282 }
283
284 /* It is more likely for requests to be received in sequence,
285 * so try adding to the back of the list first before doing
286 * a more expensive list insert. */
287 pending = (MMAL_CLOCK_REQUEST_T*)list->last;
288 if ((private->scale >= 0 && (request->media_time_adj >= pending->media_time_adj)) ||
289 (private->scale < 0 && (request->media_time_adj <= pending->media_time_adj)))
290 {
291 mmal_list_push_back(list, &request->link);
292 }
293 else
294 {
295 mmal_list_insert(list, &request->link,
296 (private->scale >= 0) ? mmal_clock_request_compare_pos : mmal_clock_request_compare_neg);
297 }
298 return MMAL_TRUE;
299}
300
301/* Flush all pending requests */
302static MMAL_STATUS_T mmal_clock_request_flush_locked(MMAL_CLOCK_PRIVATE_T *private,
303 int64_t media_time)
304{
305 MMAL_LIST_T *pending = private->request.list_pending;
306 MMAL_LIST_T *list_free = private->request.list_free;
307 MMAL_CLOCK_REQUEST_T *request;
308
309 while ((request = (MMAL_CLOCK_REQUEST_T *)mmal_list_pop_front(pending)) != NULL)
310 {
311 /* Inform the client */
312 request->cb(&private->clock, media_time, request->cb_data, request->priv);
313 /* Recycle request slot */
314 mmal_list_push_back(list_free, &request->link);
315 }
316
317 private->media_time_at_timer = 0;
318
319 return MMAL_SUCCESS;
320}
321
322/* Process all pending requests */
323static void mmal_clock_process_requests(MMAL_CLOCK_PRIVATE_T *private)
324{
325 int64_t media_time_now;
326 MMAL_LIST_T* free = private->request.list_free;
327 MMAL_LIST_T* pending = private->request.list_pending;
328 MMAL_CLOCK_REQUEST_T *next;
329
330 if (pending->length == 0 || !private->is_active)
331 return;
332
333 LOCK(private);
334
335 /* Detect discontinuity */
336 if (private->media_time_at_timer != 0)
337 {
338 media_time_now = mmal_clock_media_time_get_locked(private);
339 /* Currently only applied to forward speeds */
340 if (private->scale > 0 &&
341 media_time_now + private->discont_threshold < private->media_time_at_timer)
342 {
343 LOG_INFO("discontinuity: was=%" PRIi64 " now=%" PRIi64 " pending=%d",
344 private->media_time_at_timer, media_time_now, pending->length);
345
346 /* It's likely that packets from before the discontinuity will continue to arrive for
347 * a short time. Ensure these are detected and the requests fired immediately. */
348 private->discont_start = private->media_time_at_timer;
349 private->discont_end = private->discont_start + private->discont_duration;
350 private->discont_expiry = private->wall_time + private->discont_duration;
351
352 /* Fire all pending requests */
353 mmal_clock_request_flush_locked(private, media_time_now);
354 }
355 }
356
357 /* Earliest request is always at the front */
358 next = (MMAL_CLOCK_REQUEST_T*)mmal_list_pop_front(pending);
359 while (next)
360 {
361 media_time_now = mmal_clock_media_time_get_locked(private);
362
363 if (private->discont_expiry != 0 && private->wall_time > private->discont_expiry)
364 private->discont_expiry = 0;
365
366 /* Fire the request if it matches the pending discontinuity or if its requested media time
367 * has been reached. */
368 if ((private->discont_expiry != 0 &&
369 next->media_time_adj >= private->discont_start &&
370 next->media_time_adj < private->discont_end) ||
371 (private->scale > 0 && ((media_time_now + MIN_TIMER_DELAY) >= next->media_time_adj)) ||
372 (private->scale < 0 && ((media_time_now - MIN_TIMER_DELAY) <= next->media_time_adj)))
373 {
374 LOG_TRACE("servicing request: next %"PRIi64" now %"PRIi64, next->media_time_adj, media_time_now);
375 /* Inform the client */
376 next->cb(&private->clock, media_time_now, next->cb_data, next->priv);
377 /* Recycle the request slot */
378 mmal_list_push_back(free, &next->link);
379 /* Move onto next pending request */
380 next = (MMAL_CLOCK_REQUEST_T*)mmal_list_pop_front(pending);
381 }
382 else
383 {
384 /* The next request is in the future, so re-schedule the
385 * timer based on the current clock scale and media-time diff */
386 int64_t media_time_delay = ABS_VALUE(media_time_now - next->media_time_adj);
387 int64_t wall_time_delay = ABS_VALUE(((int64_t)private->scale_inv * media_time_delay) >> 16);
388
389 if (private->scale == 0)
390 wall_time_delay = CLOCK_WAIT_TIME; /* Clock is paused */
391
392 /* Put next request back into pending list */
393 mmal_list_push_front(pending, &next->link);
394 next = NULL;
395
396 /* Set the timer */
397 private->media_time_at_timer = media_time_now;
398 mmal_clock_timer_set(&private->timer, wall_time_delay);
399
400 LOG_TRACE("re-schedule timer: now %"PRIi64" delay %"PRIi64, media_time_now, wall_time_delay);
401 }
402 }
403
404 UNLOCK(private);
405}
406
407/* Trigger the worker thread (if present) */
408static void mmal_clock_wake_thread(MMAL_CLOCK_PRIVATE_T *private)
409{
410 if (private->scheduling)
411 vcos_semaphore_post(&private->event);
412}
413
414/* Stop the worker thread */
415static void mmal_clock_stop_thread(MMAL_CLOCK_PRIVATE_T *private)
416{
417 private->stop_thread = MMAL_TRUE;
418 mmal_clock_wake_thread(private);
419 vcos_thread_join(&private->thread, NULL);
420}
421
422/* Main processing thread */
423static void* mmal_clock_worker_thread(void *ctx)
424{
425 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T*)ctx;
426
427 while (1)
428 {
429 vcos_semaphore_wait(&private->event);
430
431 /* Either the timer has expired or a new request is pending */
432 mmal_clock_timer_cancel(&private->timer);
433
434 if (private->stop_thread)
435 break;
436
437 mmal_clock_process_requests(private);
438 }
439 return NULL;
440}
441
442/* Create scheduling resources */
443static MMAL_STATUS_T mmal_clock_create_scheduling(MMAL_CLOCK_PRIVATE_T *private)
444{
445 unsigned int i;
446 MMAL_BOOL_T timer_status = MMAL_FALSE;
447 VCOS_STATUS_T event_status = VCOS_EINVAL;
448 VCOS_UNSIGNED priority;
449
450 timer_status = mmal_clock_timer_create(&private->timer, private);
451 if (!timer_status)
452 {
453 LOG_ERROR("failed to create timer %p", private);
454 goto error;
455 }
456
457 event_status = vcos_semaphore_create(&private->event, "mmal-clock sema", 0);
458 if (event_status != VCOS_SUCCESS)
459 {
460 LOG_ERROR("failed to create event semaphore %d", event_status);
461 goto error;
462 }
463
464 private->request.list_free = mmal_list_create();
465 private->request.list_pending = mmal_list_create();
466 if (!private->request.list_free || !private->request.list_pending)
467 {
468 LOG_ERROR("failed to create list %p %p", private->request.list_free, private->request.list_pending);
469 goto error;
470 }
471
472 /* Populate the list of available request slots */
473 for (i = 0; i < CLOCK_REQUEST_SLOTS; ++i)
474 mmal_list_push_back(private->request.list_free, &private->request.pool[i].link);
475
476 if (vcos_thread_create(&private->thread, "mmal-clock thread", NULL,
477 mmal_clock_worker_thread, private) != VCOS_SUCCESS)
478 {
479 LOG_ERROR("failed to create worker thread");
480 goto error;
481 }
482 priority = vcos_thread_get_priority(&private->thread);
483 vcos_thread_set_priority(&private->thread, 1 | (priority & VCOS_AFFINITY_MASK));
484
485 private->scheduling = MMAL_TRUE;
486
487 return MMAL_SUCCESS;
488
489error:
490 if (event_status == VCOS_SUCCESS) vcos_semaphore_delete(&private->event);
491 if (timer_status) mmal_clock_timer_destroy(&private->timer);
492 if (private->request.list_free) mmal_list_destroy(private->request.list_free);
493 if (private->request.list_pending) mmal_list_destroy(private->request.list_pending);
494 return MMAL_ENOSPC;
495}
496
497/* Destroy all scheduling resources */
498static void mmal_clock_destroy_scheduling(MMAL_CLOCK_PRIVATE_T *private)
499{
500 mmal_clock_stop_thread(private);
501
502 mmal_clock_request_flush(&private->clock);
503
504 mmal_list_destroy(private->request.list_free);
505 mmal_list_destroy(private->request.list_pending);
506
507 vcos_semaphore_delete(&private->event);
508
509 mmal_clock_timer_destroy(&private->timer);
510}
511
512/* Start the media-time */
513static void mmal_clock_start(MMAL_CLOCK_T *clock)
514{
515 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T*)clock;
516
517 private->is_active = MMAL_TRUE;
518
519 mmal_clock_wake_thread(private);
520}
521
522/* Stop the media-time */
523static void mmal_clock_stop(MMAL_CLOCK_T *clock)
524{
525 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T*)clock;
526
527 private->is_active = MMAL_FALSE;
528
529 mmal_clock_wake_thread(private);
530}
531
532static int mmal_clock_is_paused(MMAL_CLOCK_T *clock)
533{
534 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T*)clock;
535 return private->scale == 0;
536}
537
538/*****************************************************************************
539 * Clock module public functions
540 *****************************************************************************/
541/* Create new clock instance */
542MMAL_STATUS_T mmal_clock_create(MMAL_CLOCK_T **clock)
543{
544 unsigned int size = sizeof(MMAL_CLOCK_PRIVATE_T);
545 MMAL_RATIONAL_T scale = { 1, 1 };
546 MMAL_CLOCK_PRIVATE_T *private;
547
548 /* Sanity checking */
549 if (clock == NULL)
550 return MMAL_EINVAL;
551
552 private = vcos_calloc(1, size, "mmal-clock");
553 if (!private)
554 {
555 LOG_ERROR("failed to allocate memory");
556 return MMAL_ENOMEM;
557 }
558
559 if (vcos_mutex_create(&private->lock, "mmal-clock lock") != VCOS_SUCCESS)
560 {
561 LOG_ERROR("failed to create lock mutex");
562 vcos_free(private);
563 return MMAL_ENOSPC;
564 }
565
566 /* Set the default threshold values */
567 private->update_threshold_lower = CLOCK_UPDATE_THRESHOLD_LOWER;
568 private->update_threshold_upper = CLOCK_UPDATE_THRESHOLD_UPPER;
569 private->discont_threshold = CLOCK_DISCONT_THRESHOLD;
570 private->discont_duration = CLOCK_DISCONT_DURATION;
571 private->request_threshold = 0;
572 private->request_threshold_enable = MMAL_FALSE;
573
574 /* Default scale = 1.0, i.e. normal playback speed */
575 mmal_clock_scale_set(&private->clock, scale);
576
577 *clock = &private->clock;
578 return MMAL_SUCCESS;
579}
580
581/* Destroy a clock instance */
582MMAL_STATUS_T mmal_clock_destroy(MMAL_CLOCK_T *clock)
583{
584 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T*)clock;
585
586 if (private->scheduling)
587 mmal_clock_destroy_scheduling(private);
588
589 vcos_mutex_delete(&private->lock);
590
591 vcos_free(private);
592
593 return MMAL_SUCCESS;
594}
595
596/* Add new client request to list of pending requests */
597MMAL_STATUS_T mmal_clock_request_add(MMAL_CLOCK_T *clock, int64_t media_time,
598 MMAL_CLOCK_REQUEST_CB cb, void *cb_data, MMAL_CLOCK_VOID_FP priv)
599{
600 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T*)clock;
601 MMAL_CLOCK_REQUEST_T *request;
602 MMAL_BOOL_T wake_thread = MMAL_FALSE;
603 int64_t media_time_now;
604
605 LOG_TRACE("media time %"PRIi64, media_time);
606
607 LOCK(private);
608
609 media_time_now = mmal_clock_media_time_get_locked(private);
610
611 /* Drop the request if request_threshold_enable and the frame exceeds the request threshold */
612 if (private->request_threshold_enable && (media_time > (media_time_now + private->request_threshold)))
613 {
614 LOG_TRACE("dropping request: media time %"PRIi64" now %"PRIi64, media_time, media_time_now);
615 UNLOCK(private);
616 return MMAL_ECORRUPT;
617 }
618
619 /* The clock module is usually only used for time-keeping, so all the
620 * objects needed to process client requests are not allocated by default
621 * and need to be created on the first client request received */
622 if (!private->scheduling)
623 {
624 if (mmal_clock_create_scheduling(private) != MMAL_SUCCESS)
625 {
626 LOG_ERROR("failed to create scheduling objects");
627 UNLOCK(private);
628 return MMAL_ENOSPC;
629 }
630 }
631
632 request = (MMAL_CLOCK_REQUEST_T*)mmal_list_pop_front(private->request.list_free);
633 if (request == NULL)
634 {
635 LOG_ERROR("no more free clock request slots");
636 UNLOCK(private);
637 return MMAL_ENOSPC;
638 }
639
640 request->cb = cb;
641 request->cb_data = cb_data;
642 request->priv = priv;
643 request->media_time = media_time;
644 request->media_time_adj = media_time - (int64_t)(private->scale * CLOCK_TARGET_OFFSET >> 16);
645
646 if (mmal_clock_request_insert(private, request))
647 wake_thread = private->is_active;
648
649 UNLOCK(private);
650
651 /* Notify the worker thread */
652 if (wake_thread)
653 mmal_clock_wake_thread(private);
654
655 return MMAL_SUCCESS;
656}
657
658/* Flush all pending requests */
659MMAL_STATUS_T mmal_clock_request_flush(MMAL_CLOCK_T *clock)
660{
661 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T*)clock;
662
663 LOCK(private);
664 if (private->scheduling)
665 mmal_clock_request_flush_locked(private, MMAL_TIME_UNKNOWN);
666 UNLOCK(private);
667
668 return MMAL_SUCCESS;
669}
670
671/* Update the local media-time with the given reference */
672MMAL_STATUS_T mmal_clock_media_time_set(MMAL_CLOCK_T *clock, int64_t media_time)
673{
674 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T*)clock;
675 MMAL_BOOL_T wake_thread = MMAL_TRUE;
676 int64_t time_diff;
677
678 LOCK(private);
679
680 if (!private->is_active)
681 {
682 uint32_t time_now = vcos_getmicrosecs();
683 private->wall_time = time_now;
684 private->media_time = media_time;
685 private->media_time_frac = 0;
686 private->rtc_at_update = time_now;
687
688 UNLOCK(private);
689 return MMAL_SUCCESS;
690 }
691
692 if (mmal_clock_is_paused(clock))
693 {
694 LOG_TRACE("clock is paused; ignoring update");
695 UNLOCK(private);
696 return MMAL_SUCCESS;
697 }
698
699 /* Reset the local media-time with the given time reference */
700 mmal_clock_update_local_time_locked(private);
701
702 time_diff = private->media_time - media_time;
703 if (time_diff > private->update_threshold_upper ||
704 time_diff < -private->update_threshold_upper)
705 {
706 LOG_TRACE("cur:%"PRIi64" new:%"PRIi64" diff:%"PRIi64, private->media_time, media_time, time_diff);
707 private->media_time = media_time;
708 private->average_ref_diff = 0;
709 }
710 else
711 {
712 private->average_ref_diff = ((private->average_ref_diff << 6) - private->average_ref_diff + time_diff) >> 6;
713 if(private->average_ref_diff > private->update_threshold_lower ||
714 private->average_ref_diff < -private->update_threshold_lower)
715 {
716 LOG_TRACE("cur:%"PRIi64" new:%"PRIi64" ave:%"PRIi64, private->media_time,
717 private->media_time - private->average_ref_diff, private->average_ref_diff);
718 private->media_time -= private->average_ref_diff;
719 private->average_ref_diff = 0;
720 }
721 else
722 {
723 /* Don't update the media-time */
724 wake_thread = MMAL_FALSE;
725 LOG_TRACE("cur:%"PRIi64" new:%"PRIi64" diff:%"PRIi64" ave:%"PRIi64" ignored", private->media_time,
726 media_time, private->media_time - media_time, private->average_ref_diff);
727 }
728 }
729
730 UNLOCK(private);
731
732 if (wake_thread)
733 mmal_clock_wake_thread(private);
734
735 return MMAL_SUCCESS;
736}
737
738/* Change the clock scale */
739MMAL_STATUS_T mmal_clock_scale_set(MMAL_CLOCK_T *clock, MMAL_RATIONAL_T scale)
740{
741 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T*)clock;
742
743 LOG_TRACE("new scale %d/%d", scale.num, scale.den);
744
745 LOCK(private);
746
747 mmal_clock_update_local_time_locked(private);
748
749 private->scale_rational = scale;
750 private->scale = mmal_rational_to_fixed_16_16(scale);
751
752 if (private->scale)
753 private->scale_inv = (int32_t)((1LL << 32) / (int64_t)private->scale);
754 else
755 private->scale_inv = Q16_ONE; /* clock is paused */
756
757 UNLOCK(private);
758
759 mmal_clock_wake_thread(private);
760
761 return MMAL_SUCCESS;
762}
763
764/* Set the clock state */
765MMAL_STATUS_T mmal_clock_active_set(MMAL_CLOCK_T *clock, MMAL_BOOL_T active)
766{
767 if (active)
768 mmal_clock_start(clock);
769 else
770 mmal_clock_stop(clock);
771
772 return MMAL_SUCCESS;
773}
774
775/* Get the clock's scale */
776MMAL_RATIONAL_T mmal_clock_scale_get(MMAL_CLOCK_T *clock)
777{
778 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T*)clock;
779 MMAL_RATIONAL_T scale;
780
781 LOCK(private);
782 scale = private->scale_rational;
783 UNLOCK(private);
784
785 return scale;
786}
787
788/* Return the current local media-time */
789int64_t mmal_clock_media_time_get(MMAL_CLOCK_T *clock)
790{
791 int64_t media_time;
792 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T*)clock;
793
794 LOCK(private);
795 media_time = mmal_clock_media_time_get_locked(private);
796 UNLOCK(private);
797
798 return media_time;
799}
800
801/* Get the clock's state */
802MMAL_BOOL_T mmal_clock_is_active(MMAL_CLOCK_T *clock)
803{
804 return ((MMAL_CLOCK_PRIVATE_T*)clock)->is_active;
805}
806
807/* Get the clock's media-time update threshold values */
808MMAL_STATUS_T mmal_clock_update_threshold_get(MMAL_CLOCK_T *clock, MMAL_CLOCK_UPDATE_THRESHOLD_T *update_threshold)
809{
810 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T *)clock;
811
812 LOCK(private);
813 update_threshold->threshold_lower = private->update_threshold_lower;
814 update_threshold->threshold_upper = private->update_threshold_upper;
815 UNLOCK(private);
816
817 return MMAL_SUCCESS;
818}
819
820/* Set the clock's media-time update threshold values */
821MMAL_STATUS_T mmal_clock_update_threshold_set(MMAL_CLOCK_T *clock, const MMAL_CLOCK_UPDATE_THRESHOLD_T *update_threshold)
822{
823 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T *)clock;
824
825 LOG_TRACE("new clock update thresholds: upper %"PRIi64", lower %"PRIi64,
826 update_threshold->threshold_lower, update_threshold->threshold_upper);
827
828 LOCK(private);
829 private->update_threshold_lower = update_threshold->threshold_lower;
830 private->update_threshold_upper = update_threshold->threshold_upper;
831 UNLOCK(private);
832
833 return MMAL_SUCCESS;
834}
835
836/* Get the clock's discontinuity threshold values */
837MMAL_STATUS_T mmal_clock_discont_threshold_get(MMAL_CLOCK_T *clock, MMAL_CLOCK_DISCONT_THRESHOLD_T *discont)
838{
839 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T *)clock;
840
841 LOCK(private);
842 discont->threshold = private->discont_threshold;
843 discont->duration = private->discont_duration;
844 UNLOCK(private);
845
846 return MMAL_SUCCESS;
847}
848
849/* Set the clock's discontinuity threshold values */
850MMAL_STATUS_T mmal_clock_discont_threshold_set(MMAL_CLOCK_T *clock, const MMAL_CLOCK_DISCONT_THRESHOLD_T *discont)
851{
852 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T *)clock;
853
854 LOG_TRACE("new clock discontinuity values: threshold %"PRIi64", duration %"PRIi64,
855 discont->threshold, discont->duration);
856
857 LOCK(private);
858 private->discont_threshold = discont->threshold;
859 private->discont_duration = discont->duration;
860 UNLOCK(private);
861
862 return MMAL_SUCCESS;
863}
864
865/* Get the clock's request threshold values */
866MMAL_STATUS_T mmal_clock_request_threshold_get(MMAL_CLOCK_T *clock, MMAL_CLOCK_REQUEST_THRESHOLD_T *req)
867{
868 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T *)clock;
869
870 LOCK(private);
871 req->threshold = private->request_threshold;
872 req->threshold_enable = private->request_threshold_enable;
873 UNLOCK(private);
874
875 return MMAL_SUCCESS;
876}
877
878/* Set the clock's request threshold values */
879MMAL_STATUS_T mmal_clock_request_threshold_set(MMAL_CLOCK_T *clock, const MMAL_CLOCK_REQUEST_THRESHOLD_T *req)
880{
881 MMAL_CLOCK_PRIVATE_T *private = (MMAL_CLOCK_PRIVATE_T *)clock;
882
883 LOG_TRACE("new clock request values: threshold %"PRIi64,
884 req->threshold);
885
886 LOCK(private);
887 private->request_threshold = req->threshold;
888 private->request_threshold_enable = req->threshold_enable;
889 UNLOCK(private);
890
891 return MMAL_SUCCESS;
892}
893