PROP_CODEC_DATA
};
-/* Wait _at most_ 10 ms for encoded buffers between each decoding step */
-#define GST_VAAPI_DECODER_TIMEOUT (10000)
-
-static GstBuffer *
-pop_buffer(GstVaapiDecoder *decoder);
-
-static gboolean
-push_surface(GstVaapiDecoder *decoder, GstVaapiSurface *surface);
-
-static DecodedSurface *
-pop_surface(GstVaapiDecoder *decoder, GTimeVal *end_time);
-
-static void
-decoder_task(gpointer data)
-{
- GstVaapiDecoder * const decoder = GST_VAAPI_DECODER_CAST(data);
- GstVaapiDecoderPrivate * const priv = decoder->priv;
- GstVaapiDecoderClass * const klass = GST_VAAPI_DECODER_GET_CLASS(decoder);
- GstBuffer *buffer;
-
- buffer = pop_buffer(decoder);
- if (!buffer)
- return;
-
- 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:
- /* Send an empty surface to signal an error */
- push_surface(decoder, NULL);
- gst_task_pause(priv->decoder_task);
- break;
- }
-}
-
static void
update_clock(GstVaapiDecoder *decoder, GstBuffer *buffer)
{
GST_DEBUG("queue encoded data buffer %p (%d bytes)",
buffer, GST_BUFFER_SIZE(buffer));
- g_async_queue_push(priv->buffers, buffer);
+ g_queue_push_tail(priv->buffers, buffer);
return TRUE;
}
pop_buffer(GstVaapiDecoder *decoder)
{
GstVaapiDecoderPrivate * const priv = decoder->priv;
- GTimeVal end_time;
GstBuffer *buffer;
- g_get_current_time(&end_time);
- g_time_val_add(&end_time, GST_VAAPI_DECODER_TIMEOUT);
-
- buffer = g_async_queue_timed_pop(priv->buffers, &end_time);
+ buffer = g_queue_pop_head(priv->buffers);
if (!buffer)
return NULL;
return buffer;
}
+static GstVaapiDecoderStatus
+decode_step(GstVaapiDecoder *decoder)
+{
+ GstVaapiDecoderStatus status;
+ GstBuffer *buffer;
+
+ do {
+ buffer = pop_buffer(decoder);
+ if (!buffer)
+ return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
+
+ status = GST_VAAPI_DECODER_GET_CLASS(decoder)->decode(decoder, buffer);
+ GST_DEBUG("decode frame (status = %d)", status);
+ if (status == GST_VAAPI_DECODER_STATUS_SUCCESS)
+ return status;
+
+ if (GST_BUFFER_IS_EOS(buffer))
+ return GST_VAAPI_DECODER_STATUS_END_OF_STREAM;
+ } while (status == GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA);
+ return status;
+}
+
static inline DecodedSurface *
create_surface(void)
{
if (!ds)
return FALSE;
- if (surface) {
- GST_DEBUG("queue decoded surface %" GST_VAAPI_ID_FORMAT,
- GST_VAAPI_ID_ARGS(GST_VAAPI_OBJECT_ID(surface)));
- ds->proxy = gst_vaapi_surface_proxy_new(priv->context, surface);
- if (ds->proxy) {
- ds->status = GST_VAAPI_DECODER_STATUS_SUCCESS;
- gst_vaapi_surface_proxy_set_timestamp(
- ds->proxy, priv->surface_timestamp);
- }
- else
- ds->status = GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
+ GST_DEBUG("queue decoded surface %" GST_VAAPI_ID_FORMAT,
+ GST_VAAPI_ID_ARGS(GST_VAAPI_OBJECT_ID(surface)));
+ ds->proxy = gst_vaapi_surface_proxy_new(priv->context, surface);
+ if (ds->proxy) {
+ ds->status = GST_VAAPI_DECODER_STATUS_SUCCESS;
+ gst_vaapi_surface_proxy_set_timestamp(ds->proxy, priv->surface_timestamp);
}
else
- ds->status = priv->decoder_status;
+ ds->status = GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
- g_async_queue_push(priv->surfaces, ds);
+ g_queue_push_tail(priv->surfaces, ds);
return TRUE;
}
static inline DecodedSurface *
-pop_surface(GstVaapiDecoder *decoder, GTimeVal *end_time)
+pop_surface(GstVaapiDecoder *decoder)
{
GstVaapiDecoderPrivate * const priv = decoder->priv;
- if (!gst_vaapi_decoder_start(decoder))
- return NULL;
-
- return g_async_queue_timed_pop(priv->surfaces, end_time);
+ return g_queue_pop_head(priv->surfaces);
}
static inline void
}
static void
-clear_async_queue(GAsyncQueue *q, GDestroyNotify destroy)
+clear_queue(GQueue *q, GDestroyNotify destroy)
{
- guint i, qlen = g_async_queue_length(q);
-
- for (i = 0; i < qlen; i++)
- destroy(g_async_queue_pop(q));
+ while (!g_queue_is_empty(q))
+ destroy(g_queue_pop_head(q));
}
static void
GstVaapiDecoder * const decoder = GST_VAAPI_DECODER(object);
GstVaapiDecoderPrivate * const priv = decoder->priv;
- gst_vaapi_decoder_stop(decoder);
-
set_codec_data(decoder, NULL);
if (priv->context) {
}
if (priv->buffers) {
- clear_async_queue(priv->buffers, (GDestroyNotify)gst_buffer_unref);
- g_async_queue_unref(priv->buffers);
+ clear_queue(priv->buffers, (GDestroyNotify)gst_buffer_unref);
+ g_queue_free(priv->buffers);
priv->buffers = NULL;
}
if (priv->surfaces) {
- clear_async_queue(priv->surfaces, (GDestroyNotify)destroy_surface);
- g_async_queue_unref(priv->surfaces);
+ clear_queue(priv->surfaces, (GDestroyNotify)destroy_surface);
+ g_queue_free(priv->surfaces);
priv->surfaces = NULL;
}
priv->fps_d = 30;
priv->surface_timestamp = GST_CLOCK_TIME_NONE;
priv->surface_duration = GST_CLOCK_TIME_NONE;
- priv->buffers = g_async_queue_new();
- priv->surfaces = g_async_queue_new();
- priv->decoder_task = NULL;
-
- g_static_rec_mutex_init(&priv->decoder_task_lock);
-}
-
-/**
- * gst_vaapi_decoder_start:
- * @decoder: a #GstVaapiDecoder
- *
- * Starts the decoder. This creates the internal decoder thread, if
- * necessary.
- *
- * Return value: %TRUE on success
- */
-gboolean
-gst_vaapi_decoder_start(GstVaapiDecoder *decoder)
-{
- g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), FALSE);
-
- if (decoder->priv->decoder_task)
- return TRUE;
-
- decoder->priv->decoder_task = gst_task_create(decoder_task, decoder);
- if (!decoder->priv->decoder_task)
- return FALSE;
-
- gst_task_set_lock(decoder->priv->decoder_task, &decoder->priv->decoder_task_lock);
- return gst_task_start(decoder->priv->decoder_task);
-}
-
-/**
- * gst_vaapi_decoder_pause:
- * @decoder: a #GstVaapiDecoder
- *
- * Pauses the decoder. It can be made active again through
- * gst_vaapi_decoder_start() or definitely stopped through
- * gst_vaapi_decoder_stop().
- *
- * Return value: %TRUE on success
- */
-gboolean
-gst_vaapi_decoder_pause(GstVaapiDecoder *decoder)
-{
- g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), FALSE);
-
- return gst_task_pause(decoder->priv->decoder_task);
-}
-
-/**
- * gst_vaapi_decoder_stop:
- * @decoder: a #GstVaapiDecoder
- *
- * Stops the decoder. This destroys any decoding thread that was
- * previously created by gst_vaapi_decoder_start(). Only
- * gst_vaapi_decoder_get_surface() on the queued surfaces will be
- * allowed at this point.
- *
- * Return value: %FALSE on success
- */
-gboolean
-gst_vaapi_decoder_stop(GstVaapiDecoder *decoder)
-{
- gboolean success;
-
- g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), FALSE);
-
- if (!decoder->priv->decoder_task)
- return FALSE;
-
- success = gst_task_join(decoder->priv->decoder_task);
- g_object_unref(decoder->priv->decoder_task);
- decoder->priv->decoder_task = NULL;
- return success;
+ priv->buffers = g_queue_new();
+ priv->surfaces = g_queue_new();
}
/**
* @decoder: a #GstVaapiDecoder
* @pstatus: return location for the decoder status, or %NULL
*
- * Waits for a decoded surface to arrive. This functions blocks until
- * the @decoder has a surface ready for the caller. @pstatus is
- * optional but it can help to know what went wrong during the
- * decoding process.
+ * Flushes encoded buffers to the decoder and returns a decoded
+ * surface, if any.
*
* Return value: a #GstVaapiSurfaceProxy holding the decoded surface,
* or %NULL if none is available (e.g. an error). Caller owns the
* returned object. g_object_unref() after usage.
*/
-static GstVaapiSurfaceProxy *
-_gst_vaapi_decoder_get_surface(
+GstVaapiSurfaceProxy *
+gst_vaapi_decoder_get_surface(
GstVaapiDecoder *decoder,
- GTimeVal *end_time,
GstVaapiDecoderStatus *pstatus
)
{
- GstVaapiDecoderStatus status;
- GstVaapiSurfaceProxy *proxy;
+ GstVaapiSurfaceProxy *proxy = NULL;
+ GstVaapiDecoderStatus status = GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
DecodedSurface *ds;
- ds = pop_surface(decoder, end_time);
+ g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), NULL);
+
+ ds = pop_surface(decoder);
+ if (!ds) {
+ do {
+ status = decode_step(decoder);
+ } while (status == GST_VAAPI_DECODER_STATUS_SUCCESS);
+ ds = pop_surface(decoder);
+ }
+
if (ds) {
proxy = ds->proxy;
status = ds->status;
destroy_surface(ds);
}
- else {
- proxy = NULL;
- status = GST_VAAPI_DECODER_STATUS_TIMEOUT;
- }
if (pstatus)
*pstatus = status;
return proxy;
}
-GstVaapiSurfaceProxy *
-gst_vaapi_decoder_get_surface(
- GstVaapiDecoder *decoder,
- GstVaapiDecoderStatus *pstatus
-)
-{
- g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), NULL);
-
- return _gst_vaapi_decoder_get_surface(decoder, NULL, pstatus);
-}
-
-/**
- * gst_vaapi_decoder_timed_get_surface:
- * @decoder: a #GstVaapiDecoder
- * @timeout: the number of microseconds to wait for the decoded surface
- * @pstatus: return location for the decoder status, or %NULL
- *
- * Waits for a decoded surface to arrive. This function blocks for at
- * least @timeout microseconds. @pstatus is optional but it can help
- * to know what went wrong during the decoding process.
- *
- * Return value: a #GstVaapiSurfaceProxy holding the decoded surface,
- * or %NULL if none is available (e.g. an error). Caller owns the
- * returned object. g_object_unref() after usage.
- */
-GstVaapiSurfaceProxy *
-gst_vaapi_decoder_timed_get_surface(
- GstVaapiDecoder *decoder,
- guint32 timeout,
- GstVaapiDecoderStatus *pstatus
-)
-{
- GTimeVal end_time;
-
- g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), NULL);
-
- g_get_current_time(&end_time);
- g_time_val_add(&end_time, timeout);
-
- return _gst_vaapi_decoder_get_surface(decoder, &end_time, pstatus);
-}
-
gboolean
gst_vaapi_decoder_ensure_context(
GstVaapiDecoder *decoder,
PROP_USE_FFMPEG,
};
-static void
-gst_vaapidecode_task_cb(gpointer data)
+static GstFlowReturn
+gst_vaapidecode_step(GstVaapiDecode *decode)
{
- GstVaapiDecode * const decode = GST_VAAPIDECODE(data);
- GstVaapiDecoderStatus status;
GstVaapiSurfaceProxy *proxy;
+ GstVaapiDecoderStatus status;
GstBuffer *buffer;
GstFlowReturn ret;
- proxy = gst_vaapi_decoder_timed_get_surface(decode->decoder, 10000, &status);
- if (!proxy || status != GST_VAAPI_DECODER_STATUS_SUCCESS)
- return;
+ proxy = gst_vaapi_decoder_get_surface(decode->decoder, &status);
+ if (!proxy) {
+ if (status != GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA)
+ goto error_decode;
+ /* More data is needed */
+ return GST_FLOW_OK;
+ }
buffer = NULL;
ret = gst_pad_alloc_buffer(
goto error_commit_buffer;
g_object_unref(proxy);
- return;
+ return GST_FLOW_OK;
/* ERRORS */
+error_decode:
+ {
+ GST_DEBUG("decode error %d", status);
+ return GST_FLOW_UNEXPECTED;
+ }
error_create_buffer:
{
const GstVaapiID surface_id =
"surface %" GST_VAAPI_ID_FORMAT " (error %d)",
GST_VAAPI_ID_ARGS(surface_id), ret);
g_object_unref(proxy);
- return;
+ return GST_FLOW_UNEXPECTED;
}
error_commit_buffer:
{
GST_DEBUG("video sink rejected the video buffer (error %d)", ret);
g_object_unref(proxy);
- return;
+ return GST_FLOW_UNEXPECTED;
}
}
static gboolean
-gst_vaapidecode_start(GstVaapiDecode *decode)
-{
- if (gst_task_get_state(decode->decoder_task) == GST_TASK_STARTED)
- return TRUE;
-
- GST_DEBUG("start decoding threads");
- if (!gst_vaapi_decoder_start(decode->decoder))
- return FALSE;
- return gst_task_start(decode->decoder_task);
-}
-
-static gboolean
-gst_vaapidecode_pause(GstVaapiDecode *decode)
-{
- if (gst_task_get_state(decode->decoder_task) == GST_TASK_PAUSED)
- return TRUE;
-
- GST_DEBUG("pause decoding threads");
- if (!gst_vaapi_decoder_pause(decode->decoder))
- return FALSE;
- return gst_task_pause(decode->decoder_task);
-}
-
-static gboolean
-gst_vaapidecode_stop(GstVaapiDecode *decode)
-{
- if (gst_task_get_state(decode->decoder_task) == GST_TASK_STOPPED)
- return TRUE;
-
- GST_DEBUG("stop decoding threads");
- if (!gst_vaapi_decoder_stop(decode->decoder))
- return FALSE;
- return gst_task_stop(decode->decoder_task);
-}
-
-static gboolean
gst_vaapidecode_create(GstVaapiDecode *decode)
{
GstVaapiVideoSink *sink;
if (decode->use_ffmpeg)
decode->decoder =
gst_vaapi_decoder_ffmpeg_new(display, codec, decode->codec_data);
- if (!decode->decoder)
- return FALSE;
-
- decode->decoder_task = gst_task_create(gst_vaapidecode_task_cb, decode);
- if (!decode->decoder_task)
- return FALSE;
-
- gst_task_set_lock(decode->decoder_task, &decode->decoder_task_lock);
- return TRUE;
+ return decode->decoder != NULL;
}
static void
gst_vaapidecode_destroy(GstVaapiDecode *decode)
{
- if (decode->decoder_task) {
- gst_task_join(decode->decoder_task);
- g_object_unref(decode->decoder_task);
- decode->decoder_task = NULL;
- }
-
if (decode->decoder) {
gst_vaapi_decoder_put_buffer(decode->decoder, NULL);
g_object_unref(decode->decoder);
case GST_STATE_CHANGE_READY_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- if (!gst_vaapidecode_start(decode))
- return GST_STATE_CHANGE_FAILURE;
break;
default:
break;
switch (transition) {
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- if (!gst_vaapidecode_pause(decode))
- return GST_STATE_CHANGE_FAILURE;
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
- if (!gst_vaapidecode_stop(decode))
- return GST_STATE_CHANGE_FAILURE;
break;
default:
break;
goto error_create_decoder;
}
- if (!gst_vaapidecode_start(decode))
- goto error_start_decoder;
-
if (!gst_vaapi_decoder_put_buffer(decode->decoder, buf))
goto error_push_buffer;
gst_buffer_unref(buf);
- return GST_FLOW_OK;
+ return gst_vaapidecode_step(decode);
/* ERRORS */
error_create_decoder:
gst_buffer_unref(buf);
return GST_FLOW_UNEXPECTED;
}
-error_start_decoder:
- {
- GST_DEBUG("failed to start decoder");
- gst_buffer_unref(buf);
- return GST_FLOW_UNEXPECTED;
- }
error_push_buffer:
{
GST_DEBUG("failed to push input buffer to decoder");
{
GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
- decode->display = NULL;
- decode->profile = 0;
- decode->codec_data = NULL;
- decode->decoder = NULL;
- decode->decoder_task = NULL;
- decode->use_ffmpeg = TRUE;
-
- g_static_rec_mutex_init(&decode->decoder_task_lock);
+ decode->display = NULL;
+ decode->profile = 0;
+ decode->codec_data = NULL;
+ decode->decoder = NULL;
+ decode->use_ffmpeg = TRUE;
/* Pad through which data comes in to the element */
decode->sinkpad = gst_pad_new_from_template(