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