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