GstVaapiSurfaceProxy * decoder_get_surface (GstVaapiDecoder * decoder) { GstVaapiSurfaceProxy *proxy; GstVaapiDecoderStatus status; g_return_val_if_fail (decoder != NULL, NULL); status = gst_vaapi_decoder_get_surface (decoder, &proxy); if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) { GST_ERROR ("failed to get decoded surface (decoder status %d)", status); return NULL; } return proxy; }
static gpointer decoder_thread (gpointer data) { App *const app = data; GError *error = NULL; GstVaapiDecoderStatus status; GstVaapiSurfaceProxy *proxy; RenderFrame *rfp; GstBuffer *buffer; GstClockTime pts; gboolean got_surface, got_eos = FALSE; gint64 end_time; guint ofs; g_print ("Decoder thread started\n"); #define SEND_ERROR(...) \ do { \ error = g_error_new(APP_ERROR, APP_ERROR_DECODER, __VA_ARGS__); \ goto send_error; \ } while (0) pts = g_get_monotonic_time (); ofs = 0; while (!app->decoder_thread_cancel) { if (G_UNLIKELY (ofs == app->file_size)) buffer = NULL; else { const gsize size = MIN (4096, app->file_size - ofs); buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, app->file_data, app->file_size, ofs, size, NULL, NULL); if (!buffer) SEND_ERROR ("failed to allocate new buffer"); ofs += size; } if (!gst_vaapi_decoder_put_buffer (app->decoder, buffer)) SEND_ERROR ("failed to push buffer to decoder"); gst_buffer_replace (&buffer, NULL); get_surface: status = gst_vaapi_decoder_get_surface (app->decoder, &proxy); switch (status) { case GST_VAAPI_DECODER_STATUS_SUCCESS: gst_vaapi_surface_proxy_set_destroy_notify (proxy, (GDestroyNotify) decoder_release, app); rfp = render_frame_new (); if (!rfp) SEND_ERROR ("failed to allocate render frame"); rfp->proxy = proxy; rfp->pts = pts; rfp->duration = app->frame_duration; pts += app->frame_duration; g_async_queue_push (app->decoder_queue, rfp); break; case GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA: /* nothing to do, just continue to the next iteration */ break; case GST_VAAPI_DECODER_STATUS_END_OF_STREAM: gst_vaapi_decoder_flush (app->decoder); if (got_eos) goto send_eos; got_eos = TRUE; break; case GST_VAAPI_DECODER_STATUS_ERROR_NO_SURFACE: end_time = g_get_monotonic_time () + G_TIME_SPAN_SECOND; g_mutex_lock (&app->mutex); got_surface = g_cond_wait_until (&app->decoder_ready, &app->mutex, end_time); g_mutex_unlock (&app->mutex); if (got_surface) goto get_surface; SEND_ERROR ("failed to acquire a surface within one second"); break; default: SEND_ERROR ("%s", get_decoder_status_string (status)); break; } } return NULL; #undef SEND_ERROR send_eos: app_send_eos (app); return NULL; send_error: app_send_error (app, error); return NULL; }
static GstFlowReturn gst_vaapidecode_step(GstVaapiDecode *decode) { GstVaapiSurfaceProxy *proxy; GstVaapiDecoderStatus status; GstBuffer *buffer; GstFlowReturn ret; GstClockTime timestamp; gint64 end_time; for (;;) { end_time = decode->render_time_base; if (!end_time) end_time = g_get_monotonic_time(); end_time += GST_TIME_AS_USECONDS(decode->last_buffer_time); end_time += G_TIME_SPAN_SECOND; proxy = gst_vaapi_decoder_get_surface(decode->decoder, &status); if (!proxy) { if (status == GST_VAAPI_DECODER_STATUS_ERROR_NO_SURFACE) { gboolean was_signalled; g_mutex_lock(decode->decoder_mutex); was_signalled = g_cond_wait_until( decode->decoder_ready, decode->decoder_mutex, end_time ); g_mutex_unlock(decode->decoder_mutex); if (was_signalled) continue; goto error_decode_timeout; } if (status != GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA) goto error_decode; /* More data is needed */ break; } g_object_weak_ref( G_OBJECT(proxy), (GWeakNotify)gst_vaapidecode_release, decode ); buffer = gst_vaapi_video_buffer_new(decode->display); if (!buffer) goto error_create_buffer; timestamp = GST_VAAPI_SURFACE_PROXY_TIMESTAMP(proxy); if (!decode->render_time_base) decode->render_time_base = g_get_monotonic_time(); decode->last_buffer_time = timestamp; GST_BUFFER_TIMESTAMP(buffer) = timestamp; GST_BUFFER_DURATION(buffer) = GST_VAAPI_SURFACE_PROXY_DURATION(proxy); gst_buffer_set_caps(buffer, GST_PAD_CAPS(decode->srcpad)); if (GST_VAAPI_SURFACE_PROXY_TFF(proxy)) GST_BUFFER_FLAG_SET(buffer, GST_VIDEO_BUFFER_TFF); gst_vaapi_video_buffer_set_surface_proxy( GST_VAAPI_VIDEO_BUFFER(buffer), proxy ); ret = gst_pad_push(decode->srcpad, buffer); if (ret != GST_FLOW_OK) goto error_commit_buffer; g_object_unref(proxy); } return GST_FLOW_OK; /* ERRORS */ error_decode_timeout: { GST_DEBUG("decode timeout. Decoder required a VA surface but none " "got available within one second"); return GST_FLOW_UNEXPECTED; } error_decode: { GST_DEBUG("decode error %d", status); switch (status) { case GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_CODEC: case GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE: case GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_CHROMA_FORMAT: ret = GST_FLOW_NOT_SUPPORTED; break; default: ret = GST_FLOW_UNEXPECTED; break; } return ret; } error_create_buffer: { const GstVaapiID surface_id = gst_vaapi_surface_get_id(GST_VAAPI_SURFACE_PROXY_SURFACE(proxy)); GST_DEBUG("video sink failed to create video buffer for proxy'ed " "surface %" GST_VAAPI_ID_FORMAT " (error %d)", GST_VAAPI_ID_ARGS(surface_id), ret); g_object_unref(proxy); return GST_FLOW_UNEXPECTED; } error_commit_buffer: { GST_DEBUG("video sink rejected the video buffer (error %d)", ret); g_object_unref(proxy); return GST_FLOW_UNEXPECTED; } }