[Spice-devel] [client v2 03/12] gstreamer: Improve the statistics collection

Frediano Ziglio fziglio at redhat.com
Tue Jun 18 07:07:08 UTC 2019


> 
> By the time schedule_frame() pulls a sample off GStreamer's pipeline the
> frame may have been decoded for hundreds of milliseconds, making the
> decoding time all but meaningless.
> This patch ensures the statistics are always collected by
> sink_event_probe() which is called as soon as the decoded frame is
> available.
> Note that even so the decoding time may be overestimated as the frame
> may have been waiting for a while in encoded form for a spot to be freed
> in the GStreamer pipeline's sink queue.
> 
> Signed-off-by: Francois Gouget <fgouget at codeweavers.com>

Acked

> ---
> 
> v2: No change.
> 
>  src/channel-display-gst.c | 105 +++++++++++++++++++++-----------------
>  1 file changed, 59 insertions(+), 46 deletions(-)
> 
> diff --git a/src/channel-display-gst.c b/src/channel-display-gst.c
> index 93064625..c2b24ea7 100644
> --- a/src/channel-display-gst.c
> +++ b/src/channel-display-gst.c
> @@ -1,6 +1,6 @@
>  /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
>  /*
> -   Copyright (C) 2015-2016 CodeWeavers, Inc
> +   Copyright (C) 2015-2016, 2019 CodeWeavers, Inc
>  
>     This library is free software; you can redistribute it and/or
>     modify it under the terms of the GNU Lesser General Public
> @@ -185,13 +185,16 @@ static gboolean display_frame(gpointer video_decoder)
>      return G_SOURCE_REMOVE;
>  }
>  
> -/* Get the decoded frame relative to buffer or NULL if not found.
> - * Dequeue the frame from decoding_queue and return it, caller
> - * is responsible to free the pointer.
> +/* Returns the decoding queue entry that matches the specified GStreamer
> buffer.
> + *
> + * The entry is identified based on the buffer timestamp. However sometimes
> + * GStreamer returns the same buffer twice (and the second time the entry
> may
> + * have been removed already) or buffers that have a modified, and thus
> + * unrecognizable, timestamp. In such cases NULL is returned.
>   *
>   * queues_mutex must be held.
>   */
> -static SpiceGstFrame *get_decoded_frame(SpiceGstDecoder *decoder, GstBuffer
> *buffer)
> +static GList *find_frame_entry(SpiceGstDecoder *decoder, GstBuffer *buffer)
>  {
>      GstClockTime buffer_ts = GST_BUFFER_PTS(buffer);
>  #if GST_CHECK_VERSION(1,14,0)
> @@ -203,49 +206,34 @@ static SpiceGstFrame *get_decoded_frame(SpiceGstDecoder
> *decoder, GstBuffer *buf
>      }
>  #endif
>  
> -    /* Gstreamer sometimes returns the same buffer twice
> -     * or buffers that have a modified, and thus unrecognizable, PTS.
> -     * Blindly removing frames from the decoding_queue until we find a
> -     * match would only empty the queue, resulting in later buffers not
> -     * finding a match either, etc. So check the buffer has a matching
> -     * frame first.
> -     */
> -    SpiceGstFrame *gstframe = NULL;
>      GList *l = g_queue_peek_head_link(decoder->decoding_queue);
>      while (l) {
> -        gstframe = l->data;
> +        const SpiceGstFrame *gstframe = l->data;
>          if (gstframe->timestamp == buffer_ts) {
> -            break;
> +            return l;
>          }
> -        gstframe = NULL;
>          l = l->next;
>      }
>  
> -    if (gstframe != NULL) {
> -        /* Now that we know there is a match, remove it and the older
> -         * frames from the decoding queue */
> -        SpiceGstFrame *late_frame;
> -        guint num_frames_dropped = 0;
> -
> -        /* The GStreamer pipeline dropped the corresponding buffer. */
> -        while ((late_frame = g_queue_pop_head(decoder->decoding_queue)) !=
> gstframe) {
> -            num_frames_dropped++;
> -            free_gst_frame(late_frame);
> -        }
> +    return NULL;
> +}
>  
> -        if (num_frames_dropped != 0) {
> -            SPICE_DEBUG("the GStreamer pipeline dropped %u frames",
> num_frames_dropped);
> -        }
> +/* Pops the queued frames up to and including the specified frame.
> + * All frames are freed except that last frame which belongs to the caller.
> + * Returns the number of freed frames.
> + *
> + * queues_mutex must be held.
> + */
> +static guint32 pop_up_to_frame(SpiceGstDecoder *decoder, const SpiceGstFrame
> *popframe)
> +{
> +    SpiceGstFrame *gstframe;
> +    guint32 freed = 0;
>  
> -        const SpiceFrame *frame = gstframe->encoded_frame;
> -        int64_t duration = g_get_monotonic_time() - frame->creation_time;
> -        record(frames_stats,
> -               "frame mm_time %u size %u creation time %" PRId64
> -               " decoded time %" PRId64 " queue %u",
> -               frame->mm_time, frame->size, frame->creation_time,
> -               duration, decoder->decoding_queue->length);
> +    while ((gstframe = g_queue_pop_head(decoder->decoding_queue)) !=
> popframe) {
> +        free_gst_frame(gstframe);
> +        freed++;
>      }
> -    return gstframe;
> +    return freed;
>  }
>  
>  /* Helper for schedule_frame().
> @@ -268,8 +256,16 @@ static void fetch_pending_sample(SpiceGstDecoder
> *decoder)
>           * finding a match either, etc. So check the buffer has a matching
>           * frame first.
>           */
> -        SpiceGstFrame *gstframe = get_decoded_frame(decoder, buffer);
> -        if (gstframe) {
> +        GList *l = find_frame_entry(decoder, buffer);
> +        if (l) {
> +            SpiceGstFrame *gstframe = l->data;
> +
> +            /* Dequeue this and any dropped frames */
> +            guint32 dropped = pop_up_to_frame(decoder, gstframe);
> +            if (dropped) {
> +                SPICE_DEBUG("the GStreamer pipeline dropped %u frames",
> dropped);
> +            }
> +
>              /* The frame is now ready for display */
>              gstframe->decoded_sample = sample;
>              decoder->display_frame = gstframe;
> @@ -438,10 +434,28 @@ sink_event_probe(GstPad *pad, GstPadProbeInfo *info,
> gpointer data)
>      if (info->type & GST_PAD_PROBE_TYPE_BUFFER) { // Buffer arrived
>          GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER(info);
>          g_mutex_lock(&decoder->queues_mutex);
> -        SpiceGstFrame *gstframe = get_decoded_frame(decoder, buffer);
> -        if (gstframe) {
> -            free_gst_frame(gstframe);
> +
> +        GList *l = find_frame_entry(decoder, buffer);
> +        if (l) {
> +            SpiceGstFrame *gstframe = l->data;
> +            const SpiceFrame *frame = gstframe->encoded_frame;
> +            int64_t duration = g_get_monotonic_time() -
> frame->creation_time;
> +            record(frames_stats,
> +                   "frame mm_time %u size %u creation time %" PRId64
> +                   " decoded time %" PRId64 " queue %u",
> +                   frame->mm_time, frame->size, frame->creation_time,
> duration,
> +                   decoder->decoding_queue->length);
> +
> +            if (!decoder->appsink) {
> +                /* The sink will display the frame directly so this
> +                 * SpiceGstFrame and those of any dropped frame are no
> longer
> +                 * needed.
> +                 */
> +                pop_up_to_frame(decoder, gstframe);
> +                free_gst_frame(gstframe);
> +            }
>          }
> +
>          g_mutex_unlock(&decoder->queues_mutex);
>      }
>      return GST_PAD_PROBE_OK;
> @@ -452,9 +466,8 @@ static void
>  deep_element_added_cb(GstBin *pipeline, GstBin *bin, GstElement *element,
>                        SpiceGstDecoder *decoder)
>  {
> -    /* attach a probe to the sink in case we don't have appsink (that
> -     * is the frames does not go to new_sample */
> -    if (GST_IS_BASE_SINK(element) && decoder->appsink == NULL) {
> +    /* Attach a probe to the sink to update the statistics */
> +    if (GST_IS_BASE_SINK(element)) {
>          GstPad *pad = gst_element_get_static_pad(element, "sink");
>          gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, sink_event_probe,
>          decoder, NULL);
>          gst_object_unref(pad);



More information about the Spice-devel mailing list