- Add PTS and framerate information.
authorgb <gb@5584edef-b1fe-4b99-b61b-dd2bab72e969>
Tue, 27 Apr 2010 15:26:19 +0000 (15:26 +0000)
committerGwenole Beauchesne <gbeauchesne@splitted-desktop.com>
Mon, 20 Sep 2010 10:55:32 +0000 (12:55 +0200)
- Simplify parsing with an AVCodeParserContext.

gst-libs/gst/vaapi/gstvaapidecoder.c
gst-libs/gst/vaapi/gstvaapidecoder.h
gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.c
gst-libs/gst/vaapi/gstvaapidecoder_priv.h

index 2c07c57..d9e17eb 100644 (file)
@@ -50,66 +50,59 @@ gst_vaapi_decoder_start(GstVaapiDecoder *decoder);
 static gboolean
 gst_vaapi_decoder_stop(GstVaapiDecoder *decoder);
 
+static GstBuffer *
+pop_buffer(GstVaapiDecoder *decoder);
+
 static gpointer
 decoder_thread_cb(gpointer data)
 {
     GstVaapiDecoder * const decoder = data;
     GstVaapiDecoderPrivate * const priv = decoder->priv;
     GstVaapiDecoderClass * const klass = GST_VAAPI_DECODER_GET_CLASS(decoder);
-    GstVaapiDecoderStatus status = GST_VAAPI_DECODER_STATUS_SUCCESS;
-
-    if (!klass->decode) {
-        g_error("unimplemented GstVaapiDecoder::decode() function");
-        return NULL;
-    }
+    GstBuffer *buffer;
 
+    g_object_ref(decoder);
     while (!priv->decoder_thread_cancel) {
-        g_mutex_lock(priv->adapter_mutex);
-        while (!gst_adapter_available(priv->adapter)) {
-            g_cond_wait(priv->adapter_cond, priv->adapter_mutex);
-            if (priv->decoder_thread_cancel)
-                break;
-        }
-        g_mutex_unlock(priv->adapter_mutex);
-
-        if (!priv->decoder_thread_cancel) {
-            switch (status) {
-            case GST_VAAPI_DECODER_STATUS_SUCCESS:
-            case GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA:
-                GST_DEBUG("decode");
-                g_object_ref(decoder);
-                status = klass->decode(decoder);
-                g_object_unref(decoder);
-
-                /* Detect End-of-Stream conditions */
-                if (status != GST_VAAPI_DECODER_STATUS_SUCCESS &&
-                    priv->is_eos &&
-                    gst_vaapi_decoder_read_avail(decoder) == 0)
-                    status = GST_VAAPI_DECODER_STATUS_END_OF_STREAM;
-
-                GST_DEBUG("decode frame (status = %d)", status);
-                break;
-            default:
-                /* XXX: something went wrong, simply destroy any
-                   buffer until this decoder is destroyed */
-                g_mutex_lock(priv->adapter_mutex);
-                gst_adapter_clear(priv->adapter);
-                g_mutex_unlock(priv->adapter_mutex);
-
-                /* Signal the main thread we got an error */
-                if (status != GST_VAAPI_DECODER_STATUS_END_OF_STREAM)
-                    gst_vaapi_decoder_push_surface(decoder, NULL);
-                break;
-            }
-        }
-
-        /* End-of-Stream reached, decoder thread is no longer necessary */
-        if (status == GST_VAAPI_DECODER_STATUS_END_OF_STREAM)
+        buffer = pop_buffer(decoder);
+        priv->decoder_status = klass->decode(decoder, buffer);
+        GST_DEBUG("decode frame (status = %d)", priv->decoder_status);
+        switch (priv->decoder_status) {
+        case GST_VAAPI_DECODER_STATUS_SUCCESS:
+        case GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA:
             break;
+        default:
+            priv->decoder_thread_cancel = TRUE;
+            break;
+        }
+        gst_buffer_unref(buffer);
     }
+    g_object_unref(decoder);
     return NULL;
 }
 
+static inline void
+init_buffer(GstBuffer *buffer, const guchar *buf, guint buf_size)
+{
+    GST_BUFFER_DATA(buffer)      = (guint8 *)buf;
+    GST_BUFFER_SIZE(buffer)      = buf_size;
+    GST_BUFFER_TIMESTAMP(buffer) = GST_CLOCK_TIME_NONE;
+    GST_BUFFER_DURATION(buffer)  = GST_CLOCK_TIME_NONE;
+}
+
+static inline GstBuffer *
+create_eos_buffer(void)
+{
+    GstBuffer *buffer;
+
+    buffer = gst_buffer_new();
+    if (!buffer)
+        return NULL;
+
+    init_buffer(buffer, NULL, 0);
+    GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_FLAG_EOS);
+    return buffer;
+}
+
 static GstBuffer *
 create_buffer(const guchar *buf, guint buf_size, gboolean copy)
 {
@@ -129,13 +122,9 @@ create_buffer(const guchar *buf, guint buf_size, gboolean copy)
             return NULL;
         }
         memcpy(buffer->malloc_data, buf, buf_size);
-        GST_BUFFER_DATA(buffer) = buffer->malloc_data;
-        GST_BUFFER_SIZE(buffer) = buf_size;
-    }
-    else {
-        GST_BUFFER_DATA(buffer) = (guint8 *)buf;
-        GST_BUFFER_SIZE(buffer) = buf_size;
+        buf = buffer->malloc_data;
     }
+    init_buffer(buffer, buf, buf_size);
     return buffer;
 }
 
@@ -145,31 +134,42 @@ push_buffer(GstVaapiDecoder *decoder, GstBuffer *buffer)
     GstVaapiDecoderPrivate * const priv = decoder->priv;
 
     if (!buffer) {
-        priv->is_eos = TRUE;
-        return TRUE;
+        buffer = create_eos_buffer();
+        if (!buffer)
+            return FALSE;
     }
 
-    g_return_val_if_fail(priv->adapter_mutex && priv->adapter_cond, FALSE);
-
     GST_DEBUG("queue encoded data buffer %p (%d bytes)",
               buffer, GST_BUFFER_SIZE(buffer));
 
     if (!priv->decoder_thread && !gst_vaapi_decoder_start(decoder))
         return FALSE;
 
-    /* XXX: add a mechanism to wait for enough buffer bytes to be consumed */
-    g_mutex_lock(priv->adapter_mutex);
-    gst_adapter_push(priv->adapter, buffer);
-    g_cond_signal(priv->adapter_cond);
-    g_mutex_unlock(priv->adapter_mutex);
+    g_async_queue_push(priv->buffers, buffer);
     return TRUE;
 }
 
+static GstBuffer *
+pop_buffer(GstVaapiDecoder *decoder)
+{
+    GstVaapiDecoderPrivate * const priv = decoder->priv;
+    GstBuffer *buffer;
+
+    buffer = g_async_queue_pop(priv->buffers);
+    g_return_val_if_fail(buffer, NULL);
+
+    if (!GST_BUFFER_TIMESTAMP_IS_VALID(buffer))
+        GST_BUFFER_TIMESTAMP(buffer) = priv->next_ts;
+    return buffer;
+}
+
 static void
-unref_surface_cb(gpointer surface, gpointer user_data)
+clear_async_queue(GAsyncQueue *q)
 {
-    if (surface)
-        g_object_unref(GST_VAAPI_SURFACE(surface));
+    guint i, qlen = g_async_queue_length(q);
+
+    for (i = 0; i < qlen; i++)
+        g_object_unref(g_async_queue_pop(q));
 }
 
 static void
@@ -180,37 +180,21 @@ gst_vaapi_decoder_finalize(GObject *object)
 
     gst_vaapi_decoder_stop(decoder);
 
-    if (priv->adapter) {
-        gst_adapter_clear(priv->adapter);
-        g_object_unref(priv->adapter);
-        priv->adapter = NULL;
-    }
-
-    if (priv->adapter_cond) {
-        g_cond_free(priv->adapter_cond);
-        priv->adapter_cond = NULL;
-    }
-
-    if (priv->adapter_mutex) {
-        g_mutex_free(priv->adapter_mutex);
-        priv->adapter_mutex = NULL;
-    }
-
     if (priv->context) {
         g_object_unref(priv->context);
         priv->context = NULL;
     }
 
-    g_queue_foreach(&priv->surfaces, unref_surface_cb, NULL);
-
-    if (priv->surfaces_cond) {
-        g_cond_free(priv->surfaces_cond);
-        priv->surfaces_cond = NULL;
+    if (priv->buffers) {
+        clear_async_queue(priv->buffers);
+        g_object_unref(priv->buffers);
+        priv->buffers = NULL;
     }
 
-    if (priv->surfaces_mutex) {
-        g_mutex_free(priv->surfaces_mutex);
-        priv->surfaces_mutex = NULL;
+    if (priv->surfaces) {
+        clear_async_queue(priv->surfaces);
+        g_object_unref(priv->surfaces);
+        priv->surfaces = NULL;
     }
 
     if (priv->display) {
@@ -310,16 +294,13 @@ gst_vaapi_decoder_init(GstVaapiDecoder *decoder)
     decoder->priv               = priv;
     priv->context               = NULL;
     priv->codec                 = 0;
-    priv->adapter               = gst_adapter_new();
-    priv->adapter_mutex         = g_mutex_new();
-    priv->adapter_cond          = g_cond_new();
-    priv->surfaces_mutex        = g_mutex_new();
-    priv->surfaces_cond         = g_cond_new();
+    priv->fps_n                 = 1000;
+    priv->fps_d                 = 30;
+    priv->next_ts               = 0;
+    priv->buffers               = g_async_queue_new();
+    priv->surfaces              = g_async_queue_new();
     priv->decoder_thread        = NULL;
     priv->decoder_thread_cancel = FALSE;
-    priv->is_eos                = FALSE;
-
-    g_queue_init(&priv->surfaces);
 }
 
 /**
@@ -367,12 +348,8 @@ gst_vaapi_decoder_stop(GstVaapiDecoder *decoder)
     GstVaapiDecoderPrivate * const priv = decoder->priv;
 
     if (priv->decoder_thread) {
+        push_buffer(decoder, NULL);
         priv->decoder_thread_cancel = TRUE;
-        if (priv->adapter_mutex && priv->adapter_cond) {
-            g_mutex_lock(priv->adapter_mutex);
-            g_cond_signal(priv->adapter_cond);
-            g_mutex_unlock(priv->adapter_mutex);
-        }
         g_thread_join(priv->decoder_thread);
         priv->decoder_thread = NULL;
     }
@@ -380,6 +357,54 @@ gst_vaapi_decoder_stop(GstVaapiDecoder *decoder)
 }
 
 /**
+ * gst_vaapi_decoder_get_frame_rate:
+ * @decoder: a #GstVaapiDecoder
+ * @num: return location for the numerator of the frame rate
+ * @den: return location for the denominator of the frame rate
+ *
+ * Retrieves the current frame rate as the fraction @num / @den. The
+ * default frame rate is 30 fps.
+ */
+void
+gst_vaapi_decoder_get_frame_rate(
+    GstVaapiDecoder *decoder,
+    guint           *num,
+    guint           *den
+)
+{
+    g_return_if_fail(GST_VAAPI_IS_DECODER(decoder));
+
+    if (num)
+        *num = decoder->priv->fps_n;
+
+    if (den)
+        *den = decoder->priv->fps_d;
+}
+
+/**
+ * gst_vaapi_decoder_set_frame_rate:
+ * @decoder: a #GstVaapiDecoder
+ * @num: the numerator of the frame rate
+ * @den: the denominator of the frame rate
+ *
+ * Sets the frame rate for the stream to @num / @den. By default, the
+ * decoder will use the frame rate encoded in the elementary stream.
+ * If none is available, the decoder will default to 30 fps.
+ */
+void
+gst_vaapi_decoder_set_frame_rate(
+    GstVaapiDecoder *decoder,
+    guint            num,
+    guint            den
+)
+{
+    g_return_if_fail(GST_VAAPI_IS_DECODER(decoder));
+
+    decoder->priv->fps_n = num;
+    decoder->priv->fps_d = den;
+}
+
+/**
  * gst_vaapi_decoder_put_buffer_data:
  * @decoder: a #GstVaapiDecoder
  * @buf: pointer to buffer data
@@ -467,36 +492,35 @@ gst_vaapi_decoder_put_buffer(GstVaapiDecoder *decoder, GstBuffer *buf)
  *   or %NULL if none is available (e.g. an error). Caller owns the
  *   returned object. g_object_unref() after usage.
  */
-static GstVaapiSurface *
+static GstVaapiSurfaceProxy *
 _gst_vaapi_decoder_get_surface(
     GstVaapiDecoder       *decoder,
-    GTimeVal              *timeout,
+    GTimeVal              *end_time,
     GstVaapiDecoderStatus *pstatus
 )
 {
     GstVaapiDecoderPrivate * const priv = decoder->priv;
+    GstVaapiDecoderStatus status;
     GstVaapiSurface *surface;
+    GstVaapiSurfaceProxy *proxy = NULL;
 
-    g_mutex_lock(priv->surfaces_mutex);
-    while (g_queue_is_empty(&priv->surfaces))
-        if (!g_cond_timed_wait(priv->surfaces_cond, priv->surfaces_mutex, timeout))
-            break;
-    surface = g_queue_pop_head(&priv->surfaces);
-    g_mutex_unlock(priv->surfaces_mutex);
-
-    if (surface)
-        *pstatus = GST_VAAPI_DECODER_STATUS_SUCCESS;
-    else {
-        g_mutex_lock(priv->adapter_mutex);
-        if (gst_adapter_available(priv->adapter))
-            *pstatus = GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
-        else if (timeout)
-            *pstatus = GST_VAAPI_DECODER_STATUS_TIMEOUT;
-        else
-            *pstatus = GST_VAAPI_DECODER_STATUS_END_OF_STREAM;
-        g_mutex_unlock(priv->adapter_mutex);
+    surface = g_async_queue_timed_pop(priv->surfaces, end_time);
+
+    if (surface) {
+        proxy  = gst_vaapi_surface_proxy_new(priv->context, surface);
+        status = (proxy ?
+                  GST_VAAPI_DECODER_STATUS_SUCCESS :
+                  GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED);
+        g_object_unref(surface);
     }
-    return surface;
+    else if (end_time)
+        status = GST_VAAPI_DECODER_STATUS_TIMEOUT;
+    else
+        status = priv->decoder_status;
+
+    if (pstatus)
+        *pstatus = status;
+    return proxy;
 }
 
 GstVaapiSurfaceProxy *
@@ -505,23 +529,9 @@ gst_vaapi_decoder_get_surface(
     GstVaapiDecoderStatus *pstatus
 )
 {
-    GstVaapiSurfaceProxy *proxy = NULL;
-    GstVaapiSurface *surface;
-    GstVaapiDecoderStatus status;
-
     g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), NULL);
 
-    surface = _gst_vaapi_decoder_get_surface(decoder, NULL, &status);
-    if (surface) {
-        proxy = gst_vaapi_surface_proxy_new(decoder->priv->context, surface);
-        if (!proxy)
-            status = GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
-        g_object_unref(surface);
-    }
-
-    if (pstatus)
-        *pstatus = status;
-    return proxy;
+    return _gst_vaapi_decoder_get_surface(decoder, NULL, pstatus);
 }
 
 /**
@@ -545,9 +555,6 @@ gst_vaapi_decoder_timed_get_surface(
     GstVaapiDecoderStatus *pstatus
 )
 {
-    GstVaapiSurfaceProxy *proxy = NULL;
-    GstVaapiSurface *surface;
-    GstVaapiDecoderStatus status;
     GTimeVal end_time;
 
     g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), NULL);
@@ -555,17 +562,7 @@ gst_vaapi_decoder_timed_get_surface(
     g_get_current_time(&end_time);
     g_time_val_add(&end_time, timeout);
 
-    surface = _gst_vaapi_decoder_get_surface(decoder, &end_time, &status);
-    if (surface) {
-        proxy = gst_vaapi_surface_proxy_new(decoder->priv->context, surface);
-        if (!proxy)
-            status = GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
-        g_object_unref(surface);
-    }
-
-    if (pstatus)
-        *pstatus = status;
-    return proxy;
+    return _gst_vaapi_decoder_get_surface(decoder, &end_time, pstatus);
 }
 
 gboolean
@@ -593,73 +590,6 @@ gst_vaapi_decoder_ensure_context(
     return priv->context != NULL;
 }
 
-guint
-gst_vaapi_decoder_copy(
-    GstVaapiDecoder *decoder,
-    guint            offset,
-    guchar          *buf,
-    guint            buf_size
-)
-{
-    GstVaapiDecoderPrivate * const priv = decoder->priv;
-    guint avail;
-
-    if (!buf || !buf_size)
-        return 0;
-
-    avail = gst_vaapi_decoder_read_avail(decoder);
-    if (offset >= avail)
-        return 0;
-    if (buf_size > avail - offset)
-        buf_size = avail - offset;
-
-    if (buf_size > 0) {
-        g_mutex_lock(priv->adapter_mutex);
-        gst_adapter_copy(priv->adapter, buf, offset, buf_size);
-        g_mutex_unlock(priv->adapter_mutex);
-    }
-    return buf_size;
-}
-
-guint
-gst_vaapi_decoder_read_avail(GstVaapiDecoder *decoder)
-{
-    GstVaapiDecoderPrivate * const priv = decoder->priv;
-    guint avail;
-
-    g_mutex_lock(priv->adapter_mutex);
-    avail = gst_adapter_available(priv->adapter);
-    g_mutex_unlock(priv->adapter_mutex);
-    return avail;
-}
-
-guint
-gst_vaapi_decoder_read(GstVaapiDecoder *decoder, guchar *buf, guint buf_size)
-{
-    buf_size = gst_vaapi_decoder_copy(decoder, 0, buf, buf_size);
-    if (buf_size > 0)
-        gst_vaapi_decoder_flush(decoder, buf_size);
-    return buf_size;
-}
-
-void
-gst_vaapi_decoder_flush(GstVaapiDecoder *decoder, guint buf_size)
-{
-    GstVaapiDecoderPrivate * const priv = decoder->priv;
-    guint avail;
-
-    if (!buf_size)
-        return;
-
-    avail = gst_vaapi_decoder_read_avail(decoder);
-    if (buf_size > avail)
-        buf_size = avail;
-
-    g_mutex_lock(priv->adapter_mutex);
-    gst_adapter_flush(priv->adapter, buf_size);
-    g_mutex_unlock(priv->adapter_mutex);
-}
-
 gboolean
 gst_vaapi_decoder_push_surface(
     GstVaapiDecoder *decoder,
@@ -668,15 +598,11 @@ gst_vaapi_decoder_push_surface(
 {
     GstVaapiDecoderPrivate * const priv = decoder->priv;
 
-    if (surface)
-        GST_DEBUG("queue decoded surface %" GST_VAAPI_ID_FORMAT,
-                  GST_VAAPI_ID_ARGS(GST_VAAPI_OBJECT_ID(surface)));
-    else
-        GST_DEBUG("queue null surface to signal an error");
+    g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE);
+
+    GST_DEBUG("queue decoded surface %" GST_VAAPI_ID_FORMAT,
+              GST_VAAPI_ID_ARGS(GST_VAAPI_OBJECT_ID(surface)));
 
-    g_mutex_lock(priv->surfaces_mutex);
-    g_queue_push_tail(&priv->surfaces, surface ? g_object_ref(surface) : NULL);
-    g_cond_signal(priv->surfaces_cond);
-    g_mutex_unlock(priv->surfaces_mutex);
+    g_async_queue_push(priv->surfaces, g_object_ref(surface));
     return TRUE;
 }
index 3a33db0..4a96bb0 100644 (file)
@@ -102,12 +102,26 @@ struct _GstVaapiDecoderClass {
     /*< private >*/
     GObjectClass parent_class;
 
-    GstVaapiDecoderStatus (*decode)(GstVaapiDecoder *decoder);
+    GstVaapiDecoderStatus (*decode)(GstVaapiDecoder *decoder, GstBuffer *buffer);
 };
 
 GType
 gst_vaapi_decoder_get_type(void);
 
+void
+gst_vaapi_decoder_get_frame_rate(
+    GstVaapiDecoder *decoder,
+    guint           *num,
+    guint           *den
+);
+
+void
+gst_vaapi_decoder_set_frame_rate(
+    GstVaapiDecoder *decoder,
+    guint            num,
+    guint            den
+);
+
 gboolean
 gst_vaapi_decoder_put_buffer_data(
     GstVaapiDecoder *decoder,
index d83e94d..7a6d6ed 100644 (file)
@@ -44,9 +44,6 @@ G_DEFINE_TYPE(GstVaapiDecoderFfmpeg,
                                  GST_VAAPI_TYPE_DECODER_FFMPEG, \
                                  GstVaapiDecoderFfmpegPrivate))
 
-/** Default I/O buffer size (32 KB) */
-#define DEFAULT_IOBUF_SIZE (32 * 1024)
-
 typedef struct _GstVaapiContextFfmpeg GstVaapiContextFfmpeg;
 struct _GstVaapiContextFfmpeg {
     struct vaapi_context        base;
@@ -54,16 +51,10 @@ struct _GstVaapiContextFfmpeg {
 };
 
 struct _GstVaapiDecoderFfmpegPrivate {
-    AVPacket                    packet;
     AVFrame                    *frame;
-    guchar                     *iobuf;
-    guint                       iobuf_pos;
-    guint                       iobuf_size;
-    ByteIOContext               ioctx;
-    AVFormatContext            *fmtctx;
+    AVCodecParserContext       *pctx;
     AVCodecContext             *avctx;
     GstVaapiContextFfmpeg      *vactx;
-    guint                       video_stream_index;
     guint                       is_constructed  : 1;
 };
 
@@ -82,21 +73,6 @@ get_codec_id_from_codec(GstVaapiCodec codec)
     return CODEC_ID_NONE;
 }
 
-/** Converts codec to FFmpeg raw bitstream format */
-static const gchar *
-get_raw_format_from_codec(GstVaapiCodec codec)
-{
-    switch (codec) {
-    case GST_VAAPI_CODEC_MPEG1: return "mpegvideo";
-    case GST_VAAPI_CODEC_MPEG2: return "mpegvideo";
-    case GST_VAAPI_CODEC_MPEG4: return "m4v";
-    case GST_VAAPI_CODEC_H263:  return "h263";
-    case GST_VAAPI_CODEC_H264:  return "h264";
-    case GST_VAAPI_CODEC_VC1:   return "vc1";
-    }
-    return NULL;
-}
-
 /** Finds a suitable profile from FFmpeg context */
 static GstVaapiProfile
 get_profile(AVCodecContext *avctx)
@@ -159,101 +135,6 @@ get_profile(AVCodecContext *avctx)
     return 0;
 }
 
-/** Probes FFmpeg format from input stream */
-static AVInputFormat *
-get_probed_format(GstVaapiDecoder *decoder)
-{
-    GstVaapiDecoderFfmpegPrivate * const priv =
-        GST_VAAPI_DECODER_FFMPEG(decoder)->priv;
-
-    AVProbeData pd;
-    pd.filename = "";
-    pd.buf      = priv->iobuf;
-    pd.buf_size = MIN(gst_vaapi_decoder_read_avail(decoder), priv->iobuf_size);
-    if (!gst_vaapi_decoder_copy(decoder, 0, pd.buf, pd.buf_size))
-        return FALSE;
-
-    GST_DEBUG("probing format from buffer %p [%d bytes]", pd.buf, pd.buf_size);
-    return av_probe_input_format(&pd, 1);
-}
-
-/** Tries to get an FFmpeg format from the raw bitstream */
-static AVInputFormat *
-get_raw_format(GstVaapiDecoder *decoder)
-{
-    const gchar *raw_format;
-
-    raw_format = get_raw_format_from_codec(GST_VAAPI_DECODER_CODEC(decoder));
-    if (!raw_format)
-        return NULL;
-
-    GST_DEBUG("trying raw format %s", raw_format);
-    return av_find_input_format(raw_format);
-}
-
-/** Flushes n bytes from the stream */
-static void
-stream_flush(GstVaapiDecoder *decoder, int buf_size)
-{
-    GstVaapiDecoderFfmpegPrivate * const priv =
-        GST_VAAPI_DECODER_FFMPEG(decoder)->priv;
-
-    gst_vaapi_decoder_flush(decoder, buf_size);
-    if (priv->iobuf_pos > buf_size)
-        priv->iobuf_pos -= buf_size;
-    else
-        priv->iobuf_pos = 0;
-}
-
-/** Reads one packet */
-static int
-stream_read(void *opaque, uint8_t *buf, int buf_size)
-{
-    GstVaapiDecoder * const decoder = GST_VAAPI_DECODER(opaque);
-    GstVaapiDecoderFfmpegPrivate * const priv =
-        GST_VAAPI_DECODER_FFMPEG(decoder)->priv;
-
-    if (buf_size > 0) {
-        buf_size = gst_vaapi_decoder_copy(
-            decoder,
-            priv->iobuf_pos,
-            buf, buf_size
-        );
-        priv->iobuf_pos += buf_size;
-    }
-    return buf_size;
-}
-
-/** Seeks into stream */
-static int64_t
-stream_seek(void *opaque, int64_t offset, int whence)
-{
-    GstVaapiDecoder * const decoder = GST_VAAPI_DECODER(opaque);
-    GstVaapiDecoderFfmpegPrivate * const priv =
-        GST_VAAPI_DECODER_FFMPEG(decoder)->priv;
-
-    /* If we parsed the headers (decoder is constructed), we can no
-       longer seek into the stream */
-    if (priv->is_constructed &&
-        !((whence == SEEK_SET || whence == SEEK_CUR) && offset == 0))
-        return -1;
-
-    switch (whence) {
-    case SEEK_SET:
-        priv->iobuf_pos = offset;
-        break;
-    case SEEK_CUR:
-        priv->iobuf_pos += offset;
-        break;
-    case SEEK_END:
-        priv->iobuf_pos = gst_vaapi_decoder_read_avail(decoder) + offset;
-        break;
-    default:
-        return -1;
-    }
-    return priv->iobuf_pos;
-}
-
 /** AVCodecContext.get_format() implementation */
 static enum PixelFormat
 gst_vaapi_decoder_ffmpeg_get_format(AVCodecContext *avctx, const enum PixelFormat *fmt)
@@ -347,29 +228,22 @@ gst_vaapi_decoder_ffmpeg_destroy(GstVaapiDecoderFfmpeg *ffdecoder)
 {
     GstVaapiDecoderFfmpegPrivate * const priv = ffdecoder->priv;
 
-    if (priv->avctx) {
-        avcodec_close(priv->avctx);
-        priv->avctx = NULL;
-    }
-
     if (priv->vactx) {
         g_free(priv->vactx);
         priv->vactx = NULL;
     }
 
-    if (priv->fmtctx) {
-        av_close_input_stream(priv->fmtctx);
-        priv->fmtctx = NULL;
+    if (priv->avctx) {
+        avcodec_close(priv->avctx);
+        priv->avctx = NULL;
     }
 
-    if (priv->iobuf) {
-        g_free(priv->iobuf);
-        priv->iobuf = NULL;
-        priv->iobuf_pos = 0;
+    if (priv->pctx) {
+        av_parser_close(priv->pctx);
+        priv->pctx = NULL;
     }
 
     av_freep(&priv->frame);
-    av_free_packet(&priv->packet);
 }
 
 static gboolean
@@ -378,21 +252,16 @@ gst_vaapi_decoder_ffmpeg_create(GstVaapiDecoderFfmpeg *ffdecoder)
     GstVaapiDecoder * const decoder = GST_VAAPI_DECODER(ffdecoder);
     GstVaapiDecoderFfmpegPrivate * const priv = ffdecoder->priv;
     GstVaapiCodec codec = GST_VAAPI_DECODER_CODEC(decoder);
-    enum CodecID codec_id = get_codec_id_from_codec(codec);
-    typedef AVInputFormat *(*GetFormatFunc)(GstVaapiDecoder *);
-    GetFormatFunc get_format[2];
-    AVInputFormat *format;
-    AVStream *video_stream;
+    enum CodecID codec_id;
     AVCodec *ffcodec;
-    guint i;
 
-    if (!priv->vactx) {
-        priv->vactx = g_new(GstVaapiContextFfmpeg, 1);
-        if (!priv->vactx)
-            return FALSE;
-    }
-    memset(&priv->vactx->base, 0, sizeof(priv->vactx->base));
-    priv->vactx->decoder = ffdecoder;
+    codec_id = get_codec_id_from_codec(codec);
+    if (codec_id == CODEC_ID_NONE)
+        return FALSE;
+
+    ffcodec = avcodec_find_decoder(codec_id);
+    if (!ffcodec)
+        return FALSE;
 
     if (!priv->frame) {
         priv->frame = avcodec_alloc_frame();
@@ -400,64 +269,20 @@ gst_vaapi_decoder_ffmpeg_create(GstVaapiDecoderFfmpeg *ffdecoder)
             return FALSE;
     }
 
-    if (!priv->iobuf) {
-        priv->iobuf = g_malloc0(priv->iobuf_size + FF_INPUT_BUFFER_PADDING_SIZE);
-        if (!priv->iobuf)
+    if (!priv->avctx) {
+        priv->avctx = avcodec_alloc_context();
+        if (!priv->avctx)
             return FALSE;
     }
 
-    get_format[ !codec] = get_raw_format;
-    get_format[!!codec] = get_probed_format;
-    for (i = 0; i < 2; i++) {
-        format = get_format[i](decoder);
-        if (!format)
-            continue;
-
-        priv->iobuf_pos = 0;
-        init_put_byte(
-            &priv->ioctx,
-            priv->iobuf,
-            priv->iobuf_size,
-            0,                  /* write flags */
-            ffdecoder,
-            stream_read,
-            NULL,               /* no packet writer callback */
-            stream_seek
-        );
-        priv->ioctx.is_streamed = 1;
-
-        if (av_open_input_stream(&priv->fmtctx, &priv->ioctx, "", format, NULL) < 0)
-            continue;
-
-        if (av_find_stream_info(priv->fmtctx) >= 0)
-            break;
-
-        av_close_input_stream(priv->fmtctx);
-        priv->fmtctx = NULL;
-    }
-    if (!priv->fmtctx)
-        return FALSE;
-
-    if (av_find_stream_info(priv->fmtctx) < 0)
-        return FALSE;
-    dump_format(priv->fmtctx, 0, "", 0);
-
-    video_stream = NULL;
-    for (i = 0; i < priv->fmtctx->nb_streams; i++) {
-        AVStream * const stream = priv->fmtctx->streams[i];
-        if (!video_stream &&
-            stream->codec->codec_type == CODEC_TYPE_VIDEO &&
-            (codec ? (stream->codec->codec_id == codec_id) : 1)) {
-            video_stream = stream;
-        }
-        else
-            stream->discard = AVDISCARD_ALL;
+    if (!priv->vactx) {
+        priv->vactx = g_new(GstVaapiContextFfmpeg, 1);
+        if (!priv->vactx)
+            return FALSE;
     }
-    if (!video_stream)
-        return FALSE;
+    memset(&priv->vactx->base, 0, sizeof(priv->vactx->base));
+    priv->vactx->decoder = ffdecoder;
 
-    priv->video_stream_index     = video_stream->index;
-    priv->avctx                  = video_stream->codec;
     priv->avctx->hwaccel_context = priv->vactx;
     priv->avctx->get_format      = gst_vaapi_decoder_ffmpeg_get_format;
     priv->avctx->get_buffer      = gst_vaapi_decoder_ffmpeg_get_buffer;
@@ -467,20 +292,24 @@ gst_vaapi_decoder_ffmpeg_create(GstVaapiDecoderFfmpeg *ffdecoder)
     priv->avctx->draw_horiz_band = NULL;
     priv->avctx->slice_flags     = SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
 
-    ffcodec = avcodec_find_decoder(priv->avctx->codec_id);
-    if (!ffcodec || avcodec_open(priv->avctx, ffcodec) < 0)
+    if (priv->pctx)
+        av_parser_close(priv->pctx);
+    priv->pctx = av_parser_init(codec_id);
+    if (!priv->pctx)
         return FALSE;
 
-    av_init_packet(&priv->packet);
+    /* XXX: lock display? */
+    if (avcodec_open(priv->avctx, ffcodec) < 0)
+        return FALSE;
     return TRUE;
 }
 
-static GstVaapiSurface *
+static GstVaapiDecoderStatus
 decode_frame(GstVaapiDecoderFfmpeg *ffdecoder, guchar *buf, guint buf_size)
 {
     GstVaapiDecoderFfmpegPrivate * const priv = ffdecoder->priv;
     GstVaapiDisplay * const display = GST_VAAPI_DECODER_DISPLAY(ffdecoder);
-    GstVaapiSurface *surface = NULL;
+    GstVaapiSurface *surface;
     int bytes_read, got_picture = 0;
 
     GST_VAAPI_DISPLAY_LOCK(display);
@@ -491,51 +320,65 @@ decode_frame(GstVaapiDecoderFfmpeg *ffdecoder, guchar *buf, guint buf_size)
         buf, buf_size
     );
     GST_VAAPI_DISPLAY_UNLOCK(display);
+    if (bytes_read < 0)
+        return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
+    if (!got_picture)
+        return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
+
+    surface = gst_vaapi_context_find_surface_by_id(
+        GST_VAAPI_DECODER_CONTEXT(ffdecoder),
+        GPOINTER_TO_UINT(priv->frame->data[3])
+    );
+    if (!surface)
+        return GST_VAAPI_DECODER_STATUS_ERROR_INVALID_SURFACE;
 
-    if (bytes_read > 0)
-        stream_flush(GST_VAAPI_DECODER_CAST(ffdecoder), bytes_read);
-
-    if (got_picture) {
-        surface = gst_vaapi_context_find_surface_by_id(
-            GST_VAAPI_DECODER_CONTEXT(ffdecoder),
-            GPOINTER_TO_UINT(priv->frame->data[3])
-        );
-    }
-    return surface;
+    gst_vaapi_decoder_push_surface(GST_VAAPI_DECODER_CAST(ffdecoder), surface);
+    return GST_VAAPI_DECODER_STATUS_SUCCESS;
 }
 
 GstVaapiDecoderStatus
-gst_vaapi_decoder_ffmpeg_decode(GstVaapiDecoder *decoder)
+gst_vaapi_decoder_ffmpeg_decode(GstVaapiDecoder *decoder, GstBuffer *buffer)
 {
     GstVaapiDecoderFfmpeg * const ffdecoder = GST_VAAPI_DECODER_FFMPEG(decoder);
     GstVaapiDecoderFfmpegPrivate * const priv = ffdecoder->priv;
-    GstVaapiSurface *surface = NULL;
-    AVPacket packet;
+    GstVaapiDecoderStatus status;
+    GstClockTime inbuf_ts;
+    guchar *inbuf, *outbuf;
+    gint inbuf_size, outbuf_size;
+    gboolean got_frame;
 
     if (!priv->is_constructed) {
         priv->is_constructed = gst_vaapi_decoder_ffmpeg_create(ffdecoder);
-        if (!priv->is_constructed) {
-            gst_vaapi_decoder_ffmpeg_destroy(ffdecoder);
+        if (!priv->is_constructed)
             return GST_VAAPI_DECODER_STATUS_ERROR_INIT_FAILED;
-        }
     }
 
-    av_init_packet(&packet);
-    while (av_read_frame(priv->fmtctx, &packet) == 0) {
-        if (packet.stream_index != priv->video_stream_index)
-            continue;
+    inbuf      = GST_BUFFER_DATA(buffer);
+    inbuf_size = GST_BUFFER_SIZE(buffer);
+    inbuf_ts   = GST_BUFFER_TIMESTAMP(buffer);
+
+    do {
+        int parsed_size = av_parser_parse(
+            priv->pctx,
+            priv->avctx,
+            &outbuf, &outbuf_size,
+            inbuf, inbuf_size,
+            inbuf_ts, inbuf_ts
+        );
+        got_frame = outbuf && outbuf_size > 0;
+        GST_DEBUG("outbuf %p (%d bytes), got frame %d, parsed size %d",
+                  outbuf, outbuf_size, got_frame, parsed_size);
 
-        surface = decode_frame(ffdecoder, packet.data, packet.size);
-        if (surface) /* decode a single frame only */
-            break;
-    }
-    if (!surface)
-        surface = decode_frame(ffdecoder, NULL, 0);
-    av_free_packet(&packet);
+        if (parsed_size > 0) {
+            inbuf      += parsed_size;
+            inbuf_size -= parsed_size;
+        }
+    } while (!got_frame && inbuf_size > 0);
 
-    if (surface && gst_vaapi_decoder_push_surface(decoder, surface))
-        return GST_VAAPI_DECODER_STATUS_SUCCESS;
-    return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
+    if (!got_frame && !GST_BUFFER_IS_EOS(buffer))
+        return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
+
+    return decode_frame(ffdecoder, outbuf, outbuf_size);
 }
 
 static void
@@ -586,16 +429,10 @@ gst_vaapi_decoder_ffmpeg_init(GstVaapiDecoderFfmpeg *decoder)
     priv                        = GST_VAAPI_DECODER_FFMPEG_GET_PRIVATE(decoder);
     decoder->priv               = priv;
     priv->frame                 = NULL;
-    priv->iobuf                 = NULL;
-    priv->iobuf_pos             = 0;
-    priv->iobuf_size            = DEFAULT_IOBUF_SIZE;
-    priv->fmtctx                = NULL;
+    priv->pctx                  = NULL;
     priv->avctx                 = NULL;
     priv->vactx                 = NULL;
-    priv->video_stream_index    = 0;
     priv->is_constructed        = FALSE;
-
-    av_init_packet(&priv->packet);
 }
 
 /**
index fed1900..ab1c6f1 100644 (file)
@@ -63,16 +63,11 @@ G_BEGIN_DECLS
 #define GST_VAAPI_DECODER_CODEC(decoder) \
     GST_VAAPI_DECODER_CAST(decoder)->priv->codec
 
-/**
- * GST_VAAPI_DECODER_IS_EOS:
- * @decoder: a #GstVaapiDecoder
- *
- * Macro that checks if the @decoder reached an End-Of-Stream.
- * This is an internal macro that does not do any run-time type check.
- */
-#undef  GST_VAAPI_DECODER_IS_EOS
-#define GST_VAAPI_DECODER_IS_EOS(decoder) \
-    GST_VAAPI_DECODER_CAST(decoder)->priv->is_eos
+/* End-of-Stream buffer */
+#define GST_BUFFER_FLAG_EOS (GST_BUFFER_FLAG_LAST + 0)
+
+#define GST_BUFFER_IS_EOS(buffer) \
+    GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_EOS)
 
 #define GST_VAAPI_DECODER_GET_PRIVATE(obj)                      \
     (G_TYPE_INSTANCE_GET_PRIVATE((obj),                         \
@@ -80,18 +75,17 @@ G_BEGIN_DECLS
                                  GstVaapiDecoderPrivate))
 
 struct _GstVaapiDecoderPrivate {
-    GstVaapiDisplay    *display;
-    GstVaapiContext    *context;
-    GstVaapiCodec       codec;
-    GstAdapter         *adapter;
-    GMutex             *adapter_mutex;
-    GCond              *adapter_cond;
-    GQueue              surfaces;
-    GMutex             *surfaces_mutex;
-    GCond              *surfaces_cond;
+    GstVaapiDisplay            *display;
+    GstVaapiContext            *context;
+    GstVaapiCodec               codec;
+    guint                       fps_n;
+    guint               fps_d;
+    GstClockTime        next_ts;
+    GAsyncQueue        *buffers;
+    GAsyncQueue        *surfaces;
     GThread            *decoder_thread;
+    GstVaapiDecoderStatus decoder_status;
     guint               decoder_thread_cancel   : 1;
-    guint               is_eos                  : 1;
 };
 
 gboolean
@@ -103,26 +97,6 @@ gst_vaapi_decoder_ensure_context(
     guint               height
 ) attribute_hidden;
 
-guint
-gst_vaapi_decoder_copy(
-    GstVaapiDecoder *decoder,
-    guint            offset,
-    guchar          *buf,
-    guint            buf_size
-) attribute_hidden;
-
-guint
-gst_vaapi_decoder_read_avail(GstVaapiDecoder *decoder)
-    attribute_hidden;
-
-guint
-gst_vaapi_decoder_read(GstVaapiDecoder *decoder, guchar *buf, guint buf_size)
-    attribute_hidden;
-
-void
-gst_vaapi_decoder_flush(GstVaapiDecoder *decoder, guint buf_size)
-    attribute_hidden;
-
 gboolean
 gst_vaapi_decoder_push_surface(
     GstVaapiDecoder *decoder,