123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900 |
- /*
- Copyright (c) 2012, Broadcom Europe Ltd
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- * Neither the name of the copyright holder nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
- #include "mmal.h"
- #include "core/mmal_component_private.h"
- #include "core/mmal_port_private.h"
- #include "util/mmal_util_rational.h"
- #include "util/mmal_list.h"
- #include "mmal_logging.h"
-
-
- #define CLOCK_PORTS_NUM 5
-
- #define MAX_CLOCK_EVENT_SLOTS 16
-
- #define DEFAULT_FRAME_RATE 30 /* frames per second */
- #define DEFAULT_CLOCK_LATENCY 60000 /* microseconds */
-
- #define FILTER_DURATION 2 /* seconds */
- #define MAX_FILTER_LENGTH 180 /* samples */
-
- #define MAX_TIME (~(1LL << 63)) /* microseconds */
- #define MIN_TIME (1LL << 63) /* microseconds */
-
- #define ABS(a) ((a) < 0 ? -(a) : (a))
- #define MIN(a,b) ((a) < (b) ? (a) : (b))
- #define MAX(a,b) ((a) > (b) ? (a) : (b))
-
- /** Set to 1 to enable additional stream timing log
- * messages used for debugging the clock algorithm */
- #define ENABLE_ADDITIONAL_LOGGING 0
- static int clock_additional_logging = ENABLE_ADDITIONAL_LOGGING;
-
- /*****************************************************************************/
- typedef int64_t TIME_T;
-
- typedef struct FILTER_T
- {
- uint32_t first; /**< index to the oldest sample */
- uint32_t last; /**< index to the most recent sample*/
- uint32_t count; /**< total number of samples in the filter */
- uint32_t length; /**< maximum number of samples */
- TIME_T sum; /**< sum of all samples currently in the filter */
- TIME_T h[MAX_FILTER_LENGTH]; /**< filter history */
- } FILTER_T;
-
- /** Frame statistics for a stream */
- typedef struct CLOCK_STREAM_T
- {
- uint32_t id; /**< for debug purposes */
-
- MMAL_BOOL_T started; /**< TRUE at least one frame has been received */
-
- TIME_T pts; /**< most recent time-stamp seen */
- TIME_T stc; /**< most recent wall-time seen */
-
- TIME_T mt_off; /**< offset of the current time stamp from the
- arrival time, i.e. PTS - STC */
- TIME_T mt_off_avg; /**< rolling average of the media time offset */
- TIME_T mt_off_std; /**< approximate standard deviation of the media
- time offset */
-
- FILTER_T avg_filter; /**< moving average filter */
- FILTER_T std_filter; /**< (approximate) standard deviation filter */
- } CLOCK_STREAM_T;
-
- /** Clock stream events */
- typedef enum CLOCK_STREAM_EVENT_T
- {
- CLOCK_STREAM_EVENT_NONE,
- CLOCK_STREAM_EVENT_STARTED, /**< first data received */
- CLOCK_STREAM_EVENT_DISCONT, /**< discontinuity detected */
- CLOCK_STREAM_EVENT_FRAME_COMPLETE, /**< complete frame received */
- } CLOCK_STREAM_EVENT_T;
-
- /** Clock port event */
- typedef struct CLOCK_PORT_EVENT_T
- {
- MMAL_LIST_ELEMENT_T link; /**< must be first */
- MMAL_PORT_T *port; /**< clock port where the event occurred */
- MMAL_CLOCK_EVENT_T event; /**< event data */
- } CLOCK_PORT_EVENT_T;
-
- /** Clock component context */
- typedef struct MMAL_COMPONENT_MODULE_T
- {
- MMAL_STATUS_T status; /**< current status of the component */
-
- MMAL_BOOL_T clock_discont; /**< TRUE -> clock discontinuity detected */
-
- uint32_t stream_min_id; /**< id of selected minimum stream (debugging only) */
- uint32_t stream_max_id; /**< if of selected maximum stream (debugging only) */
-
- TIME_T mt_off_target; /**< target clock media time offset */
- TIME_T mt_off_clk; /**< current clock media time offset */
-
- TIME_T adj_p; /**< proportional clock adjustment */
- TIME_T adj_m; /**< clock adjustment factor (between 1 and 0) */
- TIME_T adj; /**< final clock adjustment */
-
- TIME_T stc_at_update; /**< the value of the STC the last time the clocks
- were updated */
-
- TIME_T frame_duration; /**< one frame period (microseconds) */
- MMAL_RATIONAL_T frame_rate; /**< frame rate set by the client */
- uint32_t frame_rate_log2; /**< frame rate expressed as a power of two */
-
- MMAL_RATIONAL_T scale; /**< current clock scale factor */
- MMAL_BOOL_T pending_scale; /**< TRUE -> scale change is pending */
-
- MMAL_CLOCK_LATENCY_T latency;
- MMAL_CLOCK_UPDATE_THRESHOLD_T update_threshold;
- MMAL_CLOCK_DISCONT_THRESHOLD_T discont_threshold;
- MMAL_CLOCK_REQUEST_THRESHOLD_T request_threshold;
-
- /** Clock port events */
- struct
- {
- MMAL_LIST_T* queue; /**< pending events */
- MMAL_LIST_T* free; /**< available event slots */
- CLOCK_PORT_EVENT_T pool[MAX_CLOCK_EVENT_SLOTS];
- } events;
- } MMAL_COMPONENT_MODULE_T;
-
- /** Clock port context */
- typedef struct MMAL_PORT_MODULE_T
- {
- CLOCK_STREAM_T *stream; /**< stream associated with this clock port */
- } MMAL_PORT_MODULE_T;
-
-
- /*****************************************************************************/
- /** Round x up to the next power of two */
- static uint32_t next_pow2(uint32_t x)
- {
- x--;
- x = (x >> 1) | x;
- x = (x >> 2) | x;
- x = (x >> 4) | x;
- x = (x >> 8) | x;
- x = (x >> 16) | x;
- return ++x;
- }
-
- /** Given a power of 2 value, return the number of bit shifts */
- static uint32_t pow2_shift(uint32_t x)
- {
- static const uint32_t BIT_POSITIONS[32] =
- {
- 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
- 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
- };
-
- return BIT_POSITIONS[((x & -x) * 0x077CB531U) >> 27];
- }
-
- /** Add 2 values with saturation */
- static inline TIME_T saturate_add(TIME_T a, TIME_T b)
- {
- TIME_T sum = a + b;
- if (a > 0 && b > 0 && sum < 0)
- sum = MAX_TIME;
- else if (a < 0 && b < 0 && sum > 0)
- sum = MIN_TIME;
- return sum;
- }
-
- /*****************************************************************************/
- /** Filter reset */
- static void filter_init(FILTER_T *filter, uint32_t length)
- {
- memset(filter, 0, sizeof(*filter));
- filter->last = length - 1;
- filter->length = length;
- };
-
- /** Increment filter index modulo the length */
- static inline uint32_t filter_index_wrap(uint32_t index, uint32_t length)
- {
- return (++index < length) ? index : 0;
- }
-
- /** Remove the oldest sample from the filter */
- static void filter_drop(FILTER_T *filter)
- {
- if (!filter->count)
- return;
-
- filter->sum -= filter->h[filter->first];
- filter->first = filter_index_wrap(filter->first, filter->length);
- filter->count--;
- }
-
- /** Add a new sample (and drop the oldest when full) */
- static void filter_insert(FILTER_T *filter, TIME_T sample)
- {
- if (filter->count == filter->length)
- filter_drop(filter);
-
- filter->last = filter_index_wrap(filter->last, filter->length);
- filter->h[filter->last] = sample;
- filter->sum = saturate_add(filter->sum, sample);
- filter->count++;
- }
-
- /*****************************************************************************/
- /** Create and initialise a clock stream */
- static MMAL_BOOL_T clock_create_stream(CLOCK_STREAM_T **stream, uint32_t id, uint32_t filter_length)
- {
- CLOCK_STREAM_T *s = vcos_calloc(1, sizeof(CLOCK_STREAM_T), "clock stream");
- if (!s)
- {
- LOG_ERROR("failed to allocate stream");
- return MMAL_FALSE;
- }
-
- s->id = id;
-
- filter_init(&s->avg_filter, filter_length);
- filter_init(&s->std_filter, filter_length);
-
- *stream = s;
- return MMAL_TRUE;
- }
-
- /** Flag this stream as started */
- static void clock_start_stream(CLOCK_STREAM_T *stream, TIME_T stc, TIME_T pts)
- {
- stream->started = MMAL_TRUE;
- stream->pts = pts;
- stream->stc = stc;
- }
-
- /** Reset the internal state of a stream */
- static void clock_reset_stream(CLOCK_STREAM_T *stream)
- {
- if (!stream)
- return;
-
- stream->pts = 0;
- stream->stc = 0;
- stream->mt_off = 0;
- stream->mt_off_avg = 0;
- stream->mt_off_std = 0;
- stream->started = MMAL_FALSE;
-
- filter_init(&stream->avg_filter, stream->avg_filter.length);
- filter_init(&stream->std_filter, stream->std_filter.length);
- }
-
- /** Update the internal state of a stream */
- static CLOCK_STREAM_EVENT_T clock_update_stream(CLOCK_STREAM_T *stream, TIME_T stc, TIME_T pts,
- TIME_T discont_threshold)
- {
- CLOCK_STREAM_EVENT_T event = CLOCK_STREAM_EVENT_NONE;
- TIME_T pts_delta, stc_delta;
-
- if (pts == MMAL_TIME_UNKNOWN)
- {
- LOG_TRACE("ignoring invalid timestamp received at %"PRIi64, stc);
- return CLOCK_STREAM_EVENT_NONE;
- }
-
- if (!stream->started)
- {
- LOG_TRACE("stream %d started %"PRIi64" %"PRIi64, stream->id, stc, pts);
- clock_start_stream(stream, stc, pts);
- return CLOCK_STREAM_EVENT_STARTED;
- }
-
- /* XXX: This should really use the buffer flags to determine if a complete
- * frame has been received. However, not all clients set MMAL buffer flags
- * correctly (if at all). */
- pts_delta = pts - stream->pts;
- stc_delta = stc - stream->stc;
-
- /* Check for discontinuities. */
- if ((ABS(pts_delta) > discont_threshold) || (ABS(stc_delta) > discont_threshold))
- {
- LOG_ERROR("discontinuity detected on stream %d %"PRIi64" %"PRIi64" %"PRIi64,
- stream->id, pts_delta, stc_delta, discont_threshold);
- return CLOCK_STREAM_EVENT_DISCONT;
- }
-
- if (pts_delta)
- {
- /* A complete frame has now been received, so update the stream's notion of media time */
- stream->mt_off = stream->pts - stream->stc;
-
- filter_insert(&stream->avg_filter, stream->mt_off);
- stream->mt_off_avg = stream->avg_filter.sum / stream->avg_filter.count;
-
- filter_insert(&stream->std_filter, ABS(stream->mt_off - stream->mt_off_avg));
- stream->mt_off_std = stream->std_filter.sum / stream->std_filter.count;
-
- LOG_TRACE("stream %d %"PRIi64" %"PRIi64" %"PRIi64" %"PRIi64,
- stream->id, stream->stc, stream->pts, stream->mt_off_avg, stream->mt_off);
-
- event = CLOCK_STREAM_EVENT_FRAME_COMPLETE;
- }
-
- stream->pts = pts;
- stream->stc = stc;
-
- return event;
- }
-
- /*****************************************************************************/
- /** Start all enabled clock ports, making sure all use the same thresholds */
- static void clock_start_clocks(MMAL_COMPONENT_T *component, TIME_T media_time)
- {
- MMAL_COMPONENT_MODULE_T *module = component->priv->module;
- unsigned i;
-
- for (i = 0; i < component->clock_num; ++i)
- {
- MMAL_PORT_T *port = component->clock[i];
- if (port->is_enabled)
- {
- LOG_TRACE("starting clock %d with time %"PRIi64, port->index, media_time);
- mmal_port_clock_reference_set(port, MMAL_TRUE);
- mmal_port_clock_media_time_set(port, media_time);
- mmal_port_clock_update_threshold_set(port, &module->update_threshold);
- mmal_port_clock_discont_threshold_set(port, &module->discont_threshold);
- mmal_port_clock_request_threshold_set(port, &module->request_threshold);
- mmal_port_clock_active_set(port, MMAL_TRUE);
- }
- }
- }
-
- /** Stop (and flush) all enabled clock ports */
- static void clock_stop_clocks(MMAL_COMPONENT_T *component)
- {
- unsigned i;
-
- for (i = 0; i < component->clock_num; ++i)
- {
- MMAL_PORT_T *port = component->clock[i];
- if (port->is_enabled)
- {
- LOG_TRACE("stopping clock %d", port->index);
- mmal_port_clock_request_flush(port);
- mmal_port_clock_active_set(port, MMAL_FALSE);
- }
- }
- }
-
- /** Reset the internal state of all streams in order to rebase clock
- * adjustment calculations */
- static void clock_reset_clocks(MMAL_COMPONENT_T *component)
- {
- MMAL_COMPONENT_MODULE_T *module = component->priv->module;
- unsigned i;
-
- for (i = 0; i < component->clock_num; ++i)
- clock_reset_stream(component->clock[i]->priv->module->stream);
-
- module->clock_discont = MMAL_TRUE;
- }
-
- /** Change the media-time for all enabled clock ports */
- static void clock_set_media_time(MMAL_COMPONENT_T *component, TIME_T media_time)
- {
- unsigned i;
-
- for (i = 0; i < component->clock_num; ++i)
- {
- MMAL_PORT_T *port = component->clock[i];
- if (port->is_enabled)
- mmal_port_clock_media_time_set(port, media_time);
- }
- }
-
- /** Change the scale for all clock ports */
- static void clock_set_scale(MMAL_COMPONENT_T *component, MMAL_RATIONAL_T scale)
- {
- unsigned i;
-
- for (i = 0; i < component->clock_num; ++i)
- mmal_port_clock_scale_set(component->clock[i], scale);
-
- component->priv->module->pending_scale = 0;
- }
-
- /** Update the average and standard deviation calculations for all streams
- * (dropping samples where necessary) and return the minimum and maximum
- * streams */
- static MMAL_BOOL_T clock_get_mt_off_avg(MMAL_COMPONENT_T *component, TIME_T stc,
- CLOCK_STREAM_T **minimum, CLOCK_STREAM_T **maximum)
- {
- MMAL_COMPONENT_MODULE_T *module = component->priv->module;
- TIME_T drop_threshold = 6 * module->frame_duration;
- TIME_T reset_threshold = module->latency.target << 1;
- TIME_T avg_min = MAX_TIME;
- TIME_T avg_max = MIN_TIME;
- TIME_T avg_bias;
- TIME_T stc_delta;
- unsigned i;
-
- *minimum = 0;
- *maximum = 0;
-
- for (i = 0; i < component->clock_num; ++i)
- {
- CLOCK_STREAM_T *stream = component->clock[i]->priv->module->stream;
- if (stream)
- {
- stc_delta = stc - stream->stc;
-
- /* Drop samples from the moving average and standard deviation filters */
- if (stc_delta > reset_threshold)
- {
- filter_init(&stream->avg_filter, stream->avg_filter.length);
- filter_init(&stream->std_filter, stream->std_filter.length);
- LOG_TRACE("reset stream %d filters due to stc_delta %"PRIi64, stream->id, stc_delta);
- }
- else if (stc_delta > drop_threshold)
- {
- filter_drop(&stream->avg_filter);
- filter_drop(&stream->std_filter);
- LOG_TRACE("drop stream %d filter samples due to stc_delta %"PRIi64, stream->id, stc_delta);
- }
-
- /* No point in continuing if filters are empty */
- if (!stream->avg_filter.count)
- continue;
-
- /* Calculate new average and standard deviation for the stream */
- stream->mt_off_avg = stream->avg_filter.sum / stream->avg_filter.count;
- stream->mt_off_std = stream->std_filter.sum / stream->std_filter.count;
-
- /* Select the minimum and maximum average between all active streams */
- avg_bias = (stream->avg_filter.length - stream->avg_filter.count) * ABS(stream->mt_off_avg) / stream->avg_filter.length;
- if ((stream->mt_off_avg + avg_bias) < avg_min)
- {
- avg_min = stream->mt_off_avg;
- *minimum = stream;
- LOG_TRACE("found min on %d mt_off_avg %"PRIi64" mt_off_std %"PRIi64" avg_bias %"PRIi64" count %d",
- stream->id, stream->mt_off_avg, stream->mt_off_std, avg_bias, stream->avg_filter.count);
- }
- if ((stream->mt_off_avg - avg_bias) > avg_max)
- {
- avg_max = stream->mt_off_avg;
- *maximum = stream;
- LOG_TRACE("found max on %d mt_off_avg %"PRIi64" mt_off_std %"PRIi64" avg_bias %"PRIi64" count %d",
- stream->id, stream->mt_off_avg, stream->mt_off_std, avg_bias, stream->avg_filter.count);
- }
- }
- }
-
- return (*minimum) && (*maximum);
- }
-
- /** Adjust the media-time of the playback clocks based on current timing statistics */
- static void clock_adjust_clocks(MMAL_COMPONENT_T *component, TIME_T stc)
- {
- MMAL_COMPONENT_MODULE_T *module = component->priv->module;
- CLOCK_STREAM_T *stream_min;
- CLOCK_STREAM_T *stream_max;
- TIME_T mt_off_clk;
- TIME_T stc_prev;
-
- if (!clock_get_mt_off_avg(component, stc, &stream_min, &stream_max))
- return;
-
- module->stream_min_id = stream_min->id;
- module->stream_max_id = stream_max->id;
-
- /* Calculate the actual media-time offset seen by the clock */
- mt_off_clk = mmal_port_clock_media_time_get(component->clock[0]) - stc;
-
- stc_prev = module->stc_at_update;
- module->stc_at_update = stc;
-
- /* If there has been a discontinuity, restart the clock,
- * else use the clock control loop to apply a clock adjustment */
- if (module->clock_discont)
- {
- module->clock_discont = MMAL_FALSE;
-
- module->mt_off_clk = stream_min->mt_off_avg - module->latency.target;
- module->mt_off_target = module->mt_off_clk;
-
- clock_stop_clocks(component);
- clock_start_clocks(component, module->mt_off_clk + stc);
- }
- else
- {
- /* Determine the new clock target */
- TIME_T mt_off_target_max = stream_max->mt_off_avg - module->latency.target;
- TIME_T mt_off_target_min = stream_min->mt_off_avg - module->frame_duration;
- module->mt_off_target = MIN(mt_off_target_max, mt_off_target_min);
-
- /* Calculate the proportional adjustment, capped by the attack rate
- * set by the client */
- TIME_T stc_delta = (stc > stc_prev) ? (stc - stc_prev) : 0;
- TIME_T adj_p_max = stc_delta * module->latency.attack_rate / module->latency.attack_period;
-
- module->adj_p = module->mt_off_target - module->mt_off_clk;
- if (module->adj_p < -adj_p_max)
- module->adj_p = -adj_p_max;
- else if (module->adj_p > adj_p_max)
- module->adj_p = adj_p_max;
-
- /* Calculate the confidence of the adjustment using the approximate
- * standard deviation for the selected stream:
- *
- * adj_m = 1.0 - STD * FPS / 4
- *
- * The adjustment factor is scaled up by 2^20 which is an approximation
- * of 1000000 (microseconds per second) and the frame rate is assumed
- * to be either 32 or 64 which are approximations for 24/25/30 and 60
- * fps to avoid divisions. This has a lower limit of 0. */
- module->adj_m =
- MAX((1 << 20) - ((stream_min->mt_off_std << module->frame_rate_log2) >> 2), 0);
-
- /* Modulate the proportional adjustment by the sample confidence
- * and apply the adjustment to the current clock */
- module->adj = (module->adj_p * module->adj_m) >> 20;
- module->adj = (module->adj * (stream_min->avg_filter.count << 8) / stream_min->avg_filter.length) >> 8;
- module->mt_off_clk += module->adj;
-
- clock_set_media_time(component, module->mt_off_clk + stc);
- }
-
- /* Any pending clock scale changes can now be applied */
- if (component->priv->module->pending_scale)
- clock_set_scale(component, component->priv->module->scale);
- }
-
- /*****************************************************************************/
- static void clock_process_stream_event(MMAL_COMPONENT_T *component, CLOCK_STREAM_T *stream,
- CLOCK_STREAM_EVENT_T event, TIME_T stc)
- {
- MMAL_COMPONENT_MODULE_T *module = component->priv->module;
-
- switch (event)
- {
- case CLOCK_STREAM_EVENT_FRAME_COMPLETE:
- clock_adjust_clocks(component, stc);
- if (clock_additional_logging)
- {
- VCOS_ALERT("STRM_%d %"PRIi64" %"PRIi64" %"PRIi64" %"PRIi64" %"PRIi64" %"PRIi64" %d %"
- PRIi64" %"PRIi64" %"PRIi64" %"PRIi64" %"PRIi64" %u %u",
- stream->id, stream->stc, stream->pts, stream->mt_off_avg, stream->mt_off,
- stream->mt_off_std, ABS(stream->mt_off - stream->mt_off_avg), stream->avg_filter.count,
- module->mt_off_clk, module->mt_off_target, module->adj_p, module->adj_m, module->adj,
- module->stream_min_id, module->stream_max_id);
- }
- break;
- case CLOCK_STREAM_EVENT_DISCONT:
- clock_reset_clocks(component);
- break;
- default:
- /* ignore all other events */
- break;
- }
- }
-
- /** Handler for input buffer events */
- static void clock_process_input_buffer_info_event(MMAL_COMPONENT_T *component, MMAL_PORT_T *port,
- const MMAL_CLOCK_BUFFER_INFO_T *info)
- {
- CLOCK_STREAM_EVENT_T stream_event = CLOCK_STREAM_EVENT_NONE;
- MMAL_COMPONENT_MODULE_T *module = component->priv->module;
- MMAL_PORT_MODULE_T *port_module = port->priv->module;
- TIME_T stc = (TIME_T)((uint64_t)info->arrival_time);
- TIME_T pts = info->time_stamp;
-
- LOG_TRACE("port %d %"PRIi64" %"PRIi64, port->index, stc, pts);
-
- if (!port_module->stream)
- {
- /* First data received for this stream */
- uint32_t filter_length = module->frame_rate.num * FILTER_DURATION /
- module->frame_rate.den;
- if (!clock_create_stream(&port_module->stream, port->index, filter_length))
- return;
- }
-
- stream_event = clock_update_stream(port_module->stream, stc, pts, module->discont_threshold.threshold);
-
- clock_process_stream_event(component, port_module->stream, stream_event, stc);
- }
-
- /** Handler for clock scale events */
- static void clock_process_scale_event(MMAL_COMPONENT_T *component, MMAL_RATIONAL_T scale)
- {
- /* When pausing the clock (i.e. scale = 0.0), apply the scale change
- * immediately. However, when resuming the clock (i.e. scale = 1.0),
- * the scale change can only be applied the next time new buffer timing
- * information is received. This ensures that clocks resume with the
- * correct media-time. */
- if (scale.num == 0)
- {
- component->priv->module->scale = scale;
- clock_set_scale(component, scale);
- }
- else
- {
- /* Only support scale == 1.0 */
- if (!mmal_rational_equal(component->priv->module->scale, scale) &&
- (scale.num == scale.den))
- {
- component->priv->module->scale = scale;
- component->priv->module->pending_scale = 1;
- clock_reset_clocks(component);
- }
- }
- }
-
- /** Handler for update threshold events */
- static void clock_process_update_threshold_event(MMAL_COMPONENT_T *component, const MMAL_CLOCK_UPDATE_THRESHOLD_T *threshold)
- {
- unsigned i;
-
- component->priv->module->update_threshold = *threshold;
-
- for (i = 0; i < component->clock_num; ++i)
- mmal_port_clock_update_threshold_set(component->clock[i], threshold);
- }
-
- /** Handler for discontinuity threshold events */
- static void clock_process_discont_threshold_event(MMAL_COMPONENT_T *component, const MMAL_CLOCK_DISCONT_THRESHOLD_T *threshold)
- {
- unsigned i;
-
- component->priv->module->discont_threshold = *threshold;
-
- for (i = 0; i < component->clock_num; ++i)
- mmal_port_clock_discont_threshold_set(component->clock[i], threshold);
- }
-
- /** Handler for request threshold events */
- static void clock_process_request_threshold_event(MMAL_COMPONENT_T *component, const MMAL_CLOCK_REQUEST_THRESHOLD_T *threshold)
- {
- unsigned i;
-
- component->priv->module->request_threshold = *threshold;
-
- for (i = 0; i < component->clock_num; ++i)
- mmal_port_clock_request_threshold_set(component->clock[i], threshold);
- }
-
- /** Handler for latency events */
- static void clock_process_latency_event(MMAL_COMPONENT_T *component, const MMAL_CLOCK_LATENCY_T *latency)
- {
- component->priv->module->latency = *latency;
-
- clock_reset_clocks(component);
- }
-
- /** Add a clock port event to the queue and trigger the action thread */
- static MMAL_STATUS_T clock_event_queue(MMAL_COMPONENT_T *component, MMAL_PORT_T *port, const MMAL_CLOCK_EVENT_T *event)
- {
- CLOCK_PORT_EVENT_T *slot = (CLOCK_PORT_EVENT_T*)mmal_list_pop_front(component->priv->module->events.free);
- if (!slot)
- {
- LOG_ERROR("no event slots available");
- return MMAL_ENOSPC;
- }
-
- slot->port = port;
- slot->event = *event;
- mmal_list_push_back(component->priv->module->events.queue, &slot->link);
-
- return mmal_component_action_trigger(component);
- }
-
- /** Get the next clock port event in the queue */
- static MMAL_STATUS_T clock_event_dequeue(MMAL_COMPONENT_T *component, CLOCK_PORT_EVENT_T *port_event)
- {
- CLOCK_PORT_EVENT_T *slot = (CLOCK_PORT_EVENT_T*)mmal_list_pop_front(component->priv->module->events.queue);
- if (!slot)
- return MMAL_EINVAL;
-
- port_event->port = slot->port;
- port_event->event = slot->event;
- mmal_list_push_back(component->priv->module->events.free, &slot->link);
-
- if (port_event->event.buffer)
- {
- port_event->event.buffer->length = 0;
- mmal_port_buffer_header_callback(port_event->port, port_event->event.buffer);
- }
-
- return MMAL_SUCCESS;
- }
-
- /** Event callback from a clock port */
- static void clock_event_cb(MMAL_PORT_T *port, const MMAL_CLOCK_EVENT_T *event)
- {
- clock_event_queue(port->component, port, event);
- }
-
-
- /*****************************************************************************/
- /** Actual processing function */
- static MMAL_BOOL_T clock_do_processing(MMAL_COMPONENT_T *component)
- {
- CLOCK_PORT_EVENT_T port_event;
-
- if (clock_event_dequeue(component, &port_event) != MMAL_SUCCESS)
- return MMAL_FALSE; /* No more external events to process */
-
- /* Process external events (coming from clock ports) */
- switch (port_event.event.id)
- {
- case MMAL_CLOCK_EVENT_SCALE:
- clock_process_scale_event(component, port_event.event.data.scale);
- break;
- case MMAL_CLOCK_EVENT_UPDATE_THRESHOLD:
- clock_process_update_threshold_event(component, &port_event.event.data.update_threshold);
- break;
- case MMAL_CLOCK_EVENT_DISCONT_THRESHOLD:
- clock_process_discont_threshold_event(component, &port_event.event.data.discont_threshold);
- break;
- case MMAL_CLOCK_EVENT_REQUEST_THRESHOLD:
- clock_process_request_threshold_event(component, &port_event.event.data.request_threshold);
- break;
- case MMAL_CLOCK_EVENT_LATENCY:
- clock_process_latency_event(component, &port_event.event.data.latency);
- break;
- case MMAL_CLOCK_EVENT_INPUT_BUFFER_INFO:
- clock_process_input_buffer_info_event(component, port_event.port, &port_event.event.data.buffer);
- break;
- default:
- break;
- }
-
- return MMAL_TRUE;
- }
-
- /** Component action thread */
- static void clock_do_processing_loop(MMAL_COMPONENT_T *component)
- {
- while (clock_do_processing(component));
- }
-
-
- /*****************************************************************************/
- /** Set a parameter on the clock component's control port */
- static MMAL_STATUS_T clock_control_parameter_set(MMAL_PORT_T *port, const MMAL_PARAMETER_HEADER_T *param)
- {
- MMAL_COMPONENT_T *component = port->component;
- MMAL_COMPONENT_MODULE_T *module = component->priv->module;
- MMAL_STATUS_T status = MMAL_SUCCESS;
-
- switch (param->id)
- {
- case MMAL_PARAMETER_CLOCK_FRAME_RATE:
- {
- const MMAL_PARAMETER_FRAME_RATE_T *p = (const MMAL_PARAMETER_FRAME_RATE_T *)param;
- module->frame_rate = p->frame_rate;
- /* XXX: take frame_rate.den into account */
- module->frame_rate_log2 = pow2_shift(next_pow2(module->frame_rate.num));
- module->frame_duration = p->frame_rate.den * 1000000 / p->frame_rate.num;
- LOG_TRACE("frame rate %d/%d (%u) duration %"PRIi64,
- module->frame_rate.num, module->frame_rate.den,
- module->frame_rate_log2, module->frame_duration);
- }
- break;
- case MMAL_PARAMETER_CLOCK_LATENCY:
- {
- /* Changing the latency setting requires a reset of the clock algorithm, but
- * that can only be safely done from within the component's worker thread.
- * So, queue the new latency setting as a clock event. */
- const MMAL_PARAMETER_CLOCK_LATENCY_T *p = (const MMAL_PARAMETER_CLOCK_LATENCY_T *)param;
- MMAL_CLOCK_EVENT_T event = { MMAL_CLOCK_EVENT_LATENCY, MMAL_CLOCK_EVENT_MAGIC };
-
- LOG_TRACE("latency target %"PRIi64" attack %"PRIi64"/%"PRIi64,
- p->value.target, p->value.attack_rate, p->value.attack_period);
-
- event.data.latency = p->value;
- status = clock_event_queue(port->component, port, &event);
- }
- break;
- default:
- LOG_ERROR("parameter not supported (0x%x)", param->id);
- status = MMAL_ENOSYS;
- break;
- }
- return status;
- }
-
- /** Destroy a previously created component */
- static MMAL_STATUS_T clock_component_destroy(MMAL_COMPONENT_T *component)
- {
- MMAL_COMPONENT_MODULE_T *module = component->priv->module;
- unsigned int i;
-
- if (module->events.free)
- mmal_list_destroy(module->events.free);
-
- if (module->events.queue)
- mmal_list_destroy(module->events.queue);
-
- if (component->clock_num)
- {
- for (i = 0; i < component->clock_num; ++i)
- vcos_free(component->clock[i]->priv->module->stream);
-
- mmal_ports_clock_free(component->clock, component->clock_num);
- }
-
- vcos_free(module);
-
- return MMAL_SUCCESS;
- }
-
- /** Create an instance of a clock component */
- static MMAL_STATUS_T mmal_component_create_clock(const char *name, MMAL_COMPONENT_T *component)
- {
- int i;
- MMAL_COMPONENT_MODULE_T *module;
- MMAL_STATUS_T status = MMAL_ENOMEM;
- MMAL_PARAM_UNUSED(name);
-
- /* Allocate the context for our module */
- component->priv->module = module = vcos_malloc(sizeof(*module), "mmal module");
- if (!module)
- return MMAL_ENOMEM;
- memset(module, 0, sizeof(*module));
-
- component->priv->pf_destroy = clock_component_destroy;
-
- /* Create the clock ports (clock ports are managed by the framework) */
- component->clock = mmal_ports_clock_alloc(component, CLOCK_PORTS_NUM,
- sizeof(MMAL_PORT_MODULE_T), clock_event_cb);
- if (!component->clock)
- goto error;
- component->clock_num = CLOCK_PORTS_NUM;
-
- component->control->priv->pf_parameter_set = clock_control_parameter_set;
-
- /* Setup event slots */
- module->events.free = mmal_list_create();
- module->events.queue = mmal_list_create();
- if (!module->events.free || !module->events.queue)
- {
- LOG_ERROR("failed to create list %p %p", module->events.free, module->events.queue);
- goto error;
- }
- for (i = 0; i < MAX_CLOCK_EVENT_SLOTS; ++i)
- mmal_list_push_back(module->events.free, &module->events.pool[i].link);
-
- component->priv->priority = VCOS_THREAD_PRI_REALTIME;
- status = mmal_component_action_register(component, clock_do_processing_loop);
-
- module->clock_discont = MMAL_TRUE;
- module->frame_rate.num = DEFAULT_FRAME_RATE;
- module->frame_rate.den = 1;
-
- module->scale = mmal_port_clock_scale_get(component->clock[0]);
-
- memset(&module->latency, 0, sizeof(module->latency));
- module->latency.target = DEFAULT_CLOCK_LATENCY;
-
- mmal_port_clock_update_threshold_get(component->clock[0], &module->update_threshold);
- mmal_port_clock_discont_threshold_get(component->clock[0], &module->discont_threshold);
- mmal_port_clock_request_threshold_get(component->clock[0], &module->request_threshold);
-
- return status;
-
- error:
- clock_component_destroy(component);
- return status;
- }
-
-
- /*****************************************************************************/
- MMAL_CONSTRUCTOR(mmal_register_component_clock);
- void mmal_register_component_clock(void)
- {
- mmal_component_supplier_register("clock", mmal_component_create_clock);
- }
|