static void gst_vaapi_subpicture_destroy (GstVaapiSubpicture * subpicture) { GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (subpicture); VASubpictureID subpicture_id; VAStatus status; subpicture_id = GST_VAAPI_OBJECT_ID (subpicture); GST_DEBUG ("subpicture %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (subpicture_id)); if (subpicture_id != VA_INVALID_ID) { if (display) { GST_VAAPI_DISPLAY_LOCK (display); status = vaDestroySubpicture (GST_VAAPI_DISPLAY_VADISPLAY (display), subpicture_id); GST_VAAPI_DISPLAY_UNLOCK (display); if (!vaapi_check_status (status, "vaDestroySubpicture()")) g_warning ("failed to destroy subpicture %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (subpicture_id)); } GST_VAAPI_OBJECT_ID (subpicture) = VA_INVALID_ID; } gst_vaapi_object_replace (&subpicture->image, NULL); }
/** * gst_vaapi_surface_deassociate_subpicture: * @surface: a #GstVaapiSurface * @subpicture: a #GstVaapiSubpicture * * Deassociates @subpicture from @surface. Other associations are kept. * * Return value: %TRUE on success */ gboolean gst_vaapi_surface_deassociate_subpicture (GstVaapiSurface * surface, GstVaapiSubpicture * subpicture) { gboolean success; g_return_val_if_fail (surface != NULL, FALSE); g_return_val_if_fail (subpicture != NULL, FALSE); if (!surface->subpictures) return TRUE; /* First, check subpicture was really associated with this surface */ if (!g_ptr_array_remove_fast (surface->subpictures, subpicture)) { GST_DEBUG ("subpicture %" GST_VAAPI_ID_FORMAT " was not bound to " "surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (GST_VAAPI_OBJECT_ID (subpicture)), GST_VAAPI_ID_ARGS (GST_VAAPI_OBJECT_ID (surface))); return TRUE; } success = _gst_vaapi_surface_deassociate_subpicture (surface, subpicture); gst_vaapi_object_unref (subpicture); return success; }
static void gst_vaapi_surface_destroy (GstVaapiSurface * surface) { GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (surface); VASurfaceID surface_id; VAStatus status; surface_id = GST_VAAPI_OBJECT_ID (surface); GST_DEBUG ("surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (surface_id)); gst_vaapi_surface_destroy_subpictures (surface); gst_vaapi_surface_set_parent_context (surface, NULL); if (surface_id != VA_INVALID_SURFACE) { GST_VAAPI_DISPLAY_LOCK (display); status = vaDestroySurfaces (GST_VAAPI_DISPLAY_VADISPLAY (display), &surface_id, 1); GST_VAAPI_DISPLAY_UNLOCK (display); if (!vaapi_check_status (status, "vaDestroySurfaces()")) g_warning ("failed to destroy surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (surface_id)); GST_VAAPI_OBJECT_ID (surface) = VA_INVALID_SURFACE; } gst_vaapi_buffer_proxy_replace (&surface->extbuf_proxy, NULL); }
static gboolean renderer_process (App * app, RenderFrame * rfp) { GError *error = NULL; GstVaapiSurface *surface; const GstVaapiRectangle *crop_rect; #define SEND_ERROR(...) \ do { \ error = g_error_new(APP_ERROR, APP_ERROR_RENDERER, __VA_ARGS__); \ goto send_error; \ } while (0) surface = gst_vaapi_surface_proxy_get_surface (rfp->proxy); if (!surface) SEND_ERROR ("failed to get decoded surface from render frame"); ensure_window_size (app, surface); crop_rect = gst_vaapi_surface_proxy_get_crop_rect (rfp->proxy); if (g_use_pixmap && !ensure_pixmaps (app, surface, crop_rect)) SEND_ERROR ("failed to create intermediate pixmaps"); if (!gst_vaapi_surface_sync (surface)) SEND_ERROR ("failed to sync decoded surface"); if (G_LIKELY (!g_benchmark)) renderer_wait_until (app, rfp->pts); if (G_UNLIKELY (g_use_pixmap)) { GstVaapiPixmap *const pixmap = app->pixmaps[app->pixmap_id]; if (!gst_vaapi_pixmap_put_surface (pixmap, surface, crop_rect, GST_VAAPI_PICTURE_STRUCTURE_FRAME)) SEND_ERROR ("failed to render to pixmap"); if (!gst_vaapi_window_put_pixmap (app->window, pixmap, NULL, NULL)) SEND_ERROR ("failed to render surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (pixmap)); app->pixmap_id = (app->pixmap_id + 1) % G_N_ELEMENTS (app->pixmaps); } else if (!gst_vaapi_window_put_surface (app->window, surface, crop_rect, NULL, GST_VAAPI_PICTURE_STRUCTURE_FRAME)) SEND_ERROR ("failed to render surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (gst_vaapi_surface_get_id (surface))); app->num_frames++; render_frame_replace (&app->last_frame, rfp); return TRUE; #undef SEND_ERROR send_error: app_send_error (app, error); return FALSE; }
static gboolean gst_vaapi_surface_create (GstVaapiSurface * surface, GstVaapiChromaType chroma_type, guint width, guint height) { GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (surface); VASurfaceID surface_id; VAStatus status; guint va_chroma_format; va_chroma_format = from_GstVaapiChromaType (chroma_type); if (!va_chroma_format) goto error_unsupported_chroma_type; GST_VAAPI_DISPLAY_LOCK (display); status = vaCreateSurfaces (GST_VAAPI_DISPLAY_VADISPLAY (display), width, height, va_chroma_format, 1, &surface_id); GST_VAAPI_DISPLAY_UNLOCK (display); if (!vaapi_check_status (status, "vaCreateSurfaces()")) return FALSE; surface->format = GST_VIDEO_FORMAT_UNKNOWN; surface->chroma_type = chroma_type; surface->width = width; surface->height = height; GST_DEBUG ("surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (surface_id)); GST_VAAPI_OBJECT_ID (surface) = surface_id; return TRUE; /* ERRORS */ error_unsupported_chroma_type: GST_ERROR ("unsupported chroma-type %u", chroma_type); return FALSE; }
/** AVCodecContext.get_buffer() implementation */ static int gst_vaapi_decoder_ffmpeg_get_buffer(AVCodecContext *avctx, AVFrame *pic) { GstVaapiContextFfmpeg * const vactx = avctx->hwaccel_context; GstVaapiContext *context; GstVaapiSurface *surface; GstVaapiID surface_id; context = get_context(avctx); if (!context) return -1; surface = gst_vaapi_context_get_surface(context); if (!surface) { GST_DEBUG("failed to get a free VA surface"); return -1; } surface_id = GST_VAAPI_OBJECT_ID(surface); GST_DEBUG("surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(surface_id)); pic->type = FF_BUFFER_TYPE_USER; pic->age = 1; pic->data[0] = (uint8_t *)surface; pic->data[1] = NULL; pic->data[2] = NULL; pic->data[3] = (uint8_t *)(uintptr_t)surface_id; pic->linesize[0] = 0; pic->linesize[1] = 0; pic->linesize[2] = 0; pic->linesize[3] = 0; pic->pts = vactx->decoder->priv->in_timestamp; return 0; }
/** * gst_vaapi_subpicture_new: * @image: a #GstVaapiImage * @flags: #GstVaapiSubpictureFlags, or zero * * Creates a new #GstVaapiSubpicture with @image as source pixels. The * newly created object holds a reference on @image. * * Return value: the newly allocated #GstVaapiSubpicture object */ GstVaapiSubpicture *gst_vaapi_subpicture_new (GstVaapiImage * image, guint flags) { GstVaapiSubpicture *subpicture; GstVaapiDisplay *display; GstVideoFormat format; guint va_flags; g_return_val_if_fail (image != NULL, NULL); GST_DEBUG ("create from image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (GST_VAAPI_OBJECT_ID (image))); display = GST_VAAPI_OBJECT_DISPLAY (image); format = GST_VAAPI_IMAGE_FORMAT (image); if (!gst_vaapi_display_has_subpicture_format (display, format, &va_flags)) return NULL; if (flags & ~va_flags) return NULL; subpicture = gst_vaapi_object_new (gst_vaapi_subpicture_class (), display); if (!subpicture) return NULL; subpicture->global_alpha = 1.0f; if (!gst_vaapi_subpicture_set_image (subpicture, image)) goto error; return subpicture; error: gst_vaapi_object_unref (subpicture); return NULL; }
/** AVCodecContext.release_buffer() implementation */ static void gst_vaapi_decoder_ffmpeg_release_buffer(AVCodecContext *avctx, AVFrame *pic) { GstVaapiID surface_id = GST_VAAPI_ID(GPOINTER_TO_UINT(pic->data[3])); GST_DEBUG("surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(surface_id)); pic->data[0] = NULL; pic->data[1] = NULL; pic->data[2] = NULL; pic->data[3] = NULL; }
static void gst_vaapi_image_destroy(GstVaapiImage *image) { GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(image); VAImageID image_id; VAStatus status; _gst_vaapi_image_unmap(image); image_id = GST_VAAPI_OBJECT_ID(image); GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id)); if (image_id != VA_INVALID_ID) { GST_VAAPI_DISPLAY_LOCK(display); status = vaDestroyImage(GST_VAAPI_DISPLAY_VADISPLAY(display), image_id); GST_VAAPI_DISPLAY_UNLOCK(display); if (!vaapi_check_status(status, "vaDestroyImage()")) g_warning("failed to destroy image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id)); GST_VAAPI_OBJECT_ID(image) = VA_INVALID_ID; } }
static gboolean gst_vaapi_image_create(GstVaapiImage *image) { GstVaapiImagePrivate * const priv = image->priv; GstVaapiImageFormat format = priv->format; const VAImageFormat *va_format; VAImageID image_id; if (!priv->create_image) return (priv->image.image_id != VA_INVALID_ID && priv->image.buf != VA_INVALID_ID); if (!_gst_vaapi_image_create(image, format)) { switch (format) { case GST_VAAPI_IMAGE_I420: format = GST_VAAPI_IMAGE_YV12; break; case GST_VAAPI_IMAGE_YV12: format = GST_VAAPI_IMAGE_I420; break; default: format = 0; break; } if (!format || !_gst_vaapi_image_create(image, format)) return FALSE; } priv->image = priv->internal_image; image_id = priv->image.image_id; if (priv->format != priv->internal_format) { switch (priv->format) { case GST_VAAPI_IMAGE_YV12: case GST_VAAPI_IMAGE_I420: va_format = gst_vaapi_image_format_get_va_format(priv->format); if (!va_format) return FALSE; priv->image.format = *va_format; SWAP_UINT(priv->image.offsets[1], priv->image.offsets[2]); SWAP_UINT(priv->image.pitches[1], priv->image.pitches[2]); break; default: break; } } priv->is_linear = vaapi_image_is_linear(&priv->image); GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id)); GST_VAAPI_OBJECT_ID(image) = image_id; return TRUE; }
static void coded_buffer_destroy (GstVaapiCodedBuffer * buf) { GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (buf); VABufferID buf_id; buf_id = GST_VAAPI_OBJECT_ID (buf); GST_DEBUG ("coded buffer %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (buf_id)); if (buf_id != VA_INVALID_ID) { GST_VAAPI_DISPLAY_LOCK (display); vaapi_destroy_buffer (GST_VAAPI_DISPLAY_VADISPLAY (display), &buf_id); GST_VAAPI_DISPLAY_UNLOCK (display); GST_VAAPI_OBJECT_ID (buf) = VA_INVALID_ID; } }
static gboolean coded_buffer_create (GstVaapiCodedBuffer * buf, guint buf_size, GstVaapiContext * context) { GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (buf); VABufferID buf_id; gboolean success; GST_VAAPI_DISPLAY_LOCK (display); success = vaapi_create_buffer (GST_VAAPI_DISPLAY_VADISPLAY (display), GST_VAAPI_OBJECT_ID (context), VAEncCodedBufferType, buf_size, NULL, &buf_id, NULL); GST_VAAPI_DISPLAY_UNLOCK (display); if (!success) return FALSE; GST_DEBUG ("coded buffer %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (buf_id)); GST_VAAPI_OBJECT_ID (buf) = buf_id; return TRUE; }
static gboolean gst_vaapi_subpicture_create (GstVaapiSubpicture * subpicture, GstVaapiImage * image) { GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (subpicture); VASubpictureID subpicture_id; VAStatus status; GST_VAAPI_DISPLAY_LOCK (display); status = vaCreateSubpicture (GST_VAAPI_DISPLAY_VADISPLAY (display), GST_VAAPI_OBJECT_ID (image), &subpicture_id); GST_VAAPI_DISPLAY_UNLOCK (display); if (!vaapi_check_status (status, "vaCreateSubpicture()")) return FALSE; GST_DEBUG ("subpicture %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (subpicture_id)); GST_VAAPI_OBJECT_ID (subpicture) = subpicture_id; subpicture->image = gst_vaapi_object_ref (image); return TRUE; }
static gboolean gst_vaapi_surface_create(GstVaapiSurface *surface) { GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(surface); GstVaapiSurfacePrivate * const priv = surface->priv; VASurfaceID surface_id; VAStatus status; guint format; switch (priv->chroma_type) { case GST_VAAPI_CHROMA_TYPE_YUV420: format = VA_RT_FORMAT_YUV420; break; case GST_VAAPI_CHROMA_TYPE_YUV422: format = VA_RT_FORMAT_YUV422; break; case GST_VAAPI_CHROMA_TYPE_YUV444: format = VA_RT_FORMAT_YUV444; break; default: GST_DEBUG("unsupported chroma-type %u\n", priv->chroma_type); return FALSE; } GST_VAAPI_DISPLAY_LOCK(display); status = vaCreateSurfaces( GST_VAAPI_DISPLAY_VADISPLAY(display), priv->width, priv->height, format, 1, &surface_id ); GST_VAAPI_DISPLAY_UNLOCK(display); if (!vaapi_check_status(status, "vaCreateSurfaces()")) return FALSE; GST_DEBUG("surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(surface_id)); GST_VAAPI_OBJECT_ID(surface) = surface_id; return TRUE; }
int main (int argc, char *argv[]) { GstVaapiDisplay *display; GstVaapiSurface *surface; GstVaapiID surface_id; GstVaapiSurface *surfaces[MAX_SURFACES]; GstVaapiVideoPool *pool; gint i; static const GstVaapiChromaType chroma_type = GST_VAAPI_CHROMA_TYPE_YUV420; static const guint width = 320; static const guint height = 240; if (!video_output_init (&argc, argv, NULL)) g_error ("failed to initialize video output subsystem"); display = video_output_create_display (NULL); if (!display) g_error ("could not create Gst/VA display"); surface = gst_vaapi_surface_new (display, chroma_type, width, height); if (!surface) g_error ("could not create Gst/VA surface"); surface_id = gst_vaapi_surface_get_id (surface); g_print ("created surface %" GST_VAAPI_ID_FORMAT "\n", GST_VAAPI_ID_ARGS (surface_id)); gst_vaapi_object_unref (surface); pool = gst_vaapi_surface_pool_new (display, GST_VIDEO_FORMAT_ENCODED, width, height); if (!pool) g_error ("could not create Gst/VA surface pool"); for (i = 0; i < MAX_SURFACES; i++) { surface = gst_vaapi_video_pool_get_object (pool); if (!surface) g_error ("could not allocate Gst/VA surface from pool"); g_print ("created surface %" GST_VAAPI_ID_FORMAT " from pool\n", GST_VAAPI_ID_ARGS (gst_vaapi_surface_get_id (surface))); surfaces[i] = surface; } /* Check the pool doesn't return the last free'd surface */ surface = gst_vaapi_object_ref (surfaces[1]); for (i = 0; i < 2; i++) gst_vaapi_video_pool_put_object (pool, surfaces[i]); for (i = 0; i < 2; i++) { surfaces[i] = gst_vaapi_video_pool_get_object (pool); if (!surfaces[i]) g_error ("could not re-allocate Gst/VA surface%d from pool", i); g_print ("created surface %" GST_VAAPI_ID_FORMAT " from pool (realloc)\n", GST_VAAPI_ID_ARGS (gst_vaapi_surface_get_id (surfaces[i]))); } if (surface == surfaces[0]) g_error ("Gst/VA pool doesn't queue free surfaces"); for (i = MAX_SURFACES - 1; i >= 0; i--) { if (!surfaces[i]) continue; gst_vaapi_video_pool_put_object (pool, surfaces[i]); surfaces[i] = NULL; } /* Unref in random order to check objects are correctly refcounted */ gst_vaapi_display_unref (display); gst_vaapi_video_pool_unref (pool); gst_vaapi_object_unref (surface); video_output_exit (); return 0; }
static GstVaapiSurface * vpp_convert (GstVaapiWindow * window, GstVaapiSurface * surface, const GstVaapiRectangle * src_rect, const GstVaapiRectangle * dst_rect, guint flags) { GstVaapiWindowWaylandPrivate *const priv = GST_VAAPI_WINDOW_WAYLAND_GET_PRIVATE (window); GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (window); GstVaapiSurface *vpp_surface = NULL; GstVaapiFilterStatus status; /* Ensure VA surface pool is created */ /* XXX: optimize the surface format to use. e.g. YUY2 */ if (!priv->surface_pool) { priv->surface_pool = gst_vaapi_surface_pool_new (display, priv->surface_format, window->width, window->height); if (!priv->surface_pool) return NULL; gst_vaapi_filter_replace (&priv->filter, NULL); } /* Ensure VPP pipeline is built */ if (!priv->filter) { priv->filter = gst_vaapi_filter_new (display); if (!priv->filter) goto error_create_filter; if (!gst_vaapi_filter_set_format (priv->filter, priv->surface_format)) goto error_unsupported_format; } if (!gst_vaapi_filter_set_cropping_rectangle (priv->filter, src_rect)) return NULL; if (!gst_vaapi_filter_set_target_rectangle (priv->filter, dst_rect)) return NULL; /* Post-process the decoded source surface */ vpp_surface = gst_vaapi_video_pool_get_object (priv->surface_pool); if (!vpp_surface) return NULL; status = gst_vaapi_filter_process (priv->filter, surface, vpp_surface, flags); if (status != GST_VAAPI_FILTER_STATUS_SUCCESS) goto error_process_filter; return vpp_surface; /* ERRORS */ error_create_filter: { GST_WARNING ("failed to create VPP filter. Disabling"); priv->use_vpp = FALSE; return NULL; } error_unsupported_format: { GST_ERROR ("unsupported render target format %s", gst_vaapi_video_format_to_string (priv->surface_format)); priv->use_vpp = FALSE; return NULL; } error_process_filter: { GST_ERROR ("failed to process surface %" GST_VAAPI_ID_FORMAT " (error %d)", GST_VAAPI_ID_ARGS (GST_VAAPI_OBJECT_ID (surface)), status); gst_vaapi_video_pool_put_object (priv->surface_pool, vpp_surface); return NULL; } }
gboolean gst_video_meta_map_vaapi_memory (GstVideoMeta * meta, guint plane, GstMapInfo * info, gpointer * data, gint * stride, GstMapFlags flags) { GstVaapiVideoMemory *const mem = GST_VAAPI_VIDEO_MEMORY_CAST (gst_buffer_peek_memory (meta->buffer, 0)); g_return_val_if_fail (mem, FALSE); g_return_val_if_fail (GST_VAAPI_IS_VIDEO_ALLOCATOR (mem->parent_instance. allocator), FALSE); g_return_val_if_fail (mem->meta, FALSE); if (mem->map_type && mem->map_type != GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_PLANAR) goto error_incompatible_map; /* Map for writing */ if (++mem->map_count == 1) { if (!ensure_surface (mem)) goto error_ensure_surface; if (!ensure_image (mem)) goto error_ensure_image; // Load VA image from surface if ((flags & GST_MAP_READ) && !ensure_image_is_current (mem)) goto error_no_current_image; if (!gst_vaapi_image_map (mem->image)) goto error_map_image; mem->map_type = GST_VAAPI_VIDEO_MEMORY_MAP_TYPE_PLANAR; // Mark surface as dirty and expect updates from image if (flags & GST_MAP_WRITE) GST_VAAPI_VIDEO_MEMORY_FLAG_UNSET (mem, GST_VAAPI_VIDEO_MEMORY_FLAG_SURFACE_IS_CURRENT); } *data = gst_vaapi_image_get_plane (mem->image, plane); *stride = gst_vaapi_image_get_pitch (mem->image, plane); info->flags = flags; return TRUE; /* ERRORS */ error_incompatible_map: { GST_ERROR ("incompatible map type (%d)", mem->map_type); return FALSE; } error_ensure_surface: { const GstVideoInfo *const vip = mem->surface_info; GST_ERROR ("failed to create %s surface of size %ux%u", GST_VIDEO_INFO_FORMAT_STRING (vip), GST_VIDEO_INFO_WIDTH (vip), GST_VIDEO_INFO_HEIGHT (vip)); return FALSE; } error_ensure_image: { const GstVideoInfo *const vip = mem->image_info; GST_ERROR ("failed to create %s image of size %ux%u", GST_VIDEO_INFO_FORMAT_STRING (vip), GST_VIDEO_INFO_WIDTH (vip), GST_VIDEO_INFO_HEIGHT (vip)); return FALSE; } error_map_image: { GST_ERROR ("failed to map image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (gst_vaapi_image_get_id (mem->image))); return FALSE; } error_no_current_image: { GST_ERROR ("failed to make image current"); return FALSE; } }
static GstFlowReturn gst_vaapidecode_push_decoded_frame (GstVideoDecoder * vdec, GstVideoCodecFrame * out_frame) { GstVaapiDecode *const decode = GST_VAAPIDECODE (vdec); GstVaapiSurfaceProxy *proxy; GstVaapiSurface *surface; GstFlowReturn ret; const GstVaapiRectangle *crop_rect; GstVaapiVideoMeta *meta; GstBufferPoolAcquireParams *params = NULL; GstVaapiVideoBufferPoolAcquireParams vaapi_params = { {0,}, }; guint flags, out_flags = 0; gboolean alloc_renegotiate, caps_renegotiate; if (!GST_VIDEO_CODEC_FRAME_IS_DECODE_ONLY (out_frame)) { proxy = gst_video_codec_frame_get_user_data (out_frame); surface = GST_VAAPI_SURFACE_PROXY_SURFACE (proxy); crop_rect = gst_vaapi_surface_proxy_get_crop_rect (proxy); /* in theory, we are not supposed to check the surface resolution * change here since it should be advertised before from ligstvaapi. * But there are issues with it especially for some vp9 streams where * upstream element set un-cropped values in set_format() which make * everything a mess. So better doing the explicit check here irrespective * of what notification we get from upstream or libgstvaapi.Also, even if * we received notification from libgstvaapi, the frame we are going to * be pushed at this point might not have the notified resolution if there * are queued frames in decoded picture buffer. */ alloc_renegotiate = is_surface_resolution_changed (decode, surface); caps_renegotiate = is_display_resolution_changed (decode, crop_rect); if (gst_pad_needs_reconfigure (GST_VIDEO_DECODER_SRC_PAD (vdec)) || alloc_renegotiate || caps_renegotiate || decode->do_renego) { g_atomic_int_set (&decode->do_renego, FALSE); if (!gst_vaapidecode_negotiate (decode)) return GST_FLOW_ERROR; } gst_vaapi_surface_proxy_set_destroy_notify (proxy, (GDestroyNotify) gst_vaapidecode_release, gst_object_ref (decode)); if (is_src_allocator_dmabuf (decode)) { vaapi_params.proxy = gst_vaapi_surface_proxy_ref (proxy); params = (GstBufferPoolAcquireParams *) & vaapi_params; } ret = gst_video_decoder_allocate_output_frame_with_params (vdec, out_frame, params); if (params) gst_vaapi_surface_proxy_unref (vaapi_params.proxy); if (ret != GST_FLOW_OK) goto error_create_buffer; /* if not dmabuf is negotiated set the vaapi video meta in the * proxy */ if (!params) { meta = gst_buffer_get_vaapi_video_meta (out_frame->output_buffer); if (!meta) goto error_get_meta; gst_vaapi_video_meta_set_surface_proxy (meta, proxy); } flags = gst_vaapi_surface_proxy_get_flags (proxy); if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_CORRUPTED) out_flags |= GST_BUFFER_FLAG_CORRUPTED; if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_INTERLACED) { out_flags |= GST_VIDEO_BUFFER_FLAG_INTERLACED; if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_TFF) out_flags |= GST_VIDEO_BUFFER_FLAG_TFF; if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_RFF) out_flags |= GST_VIDEO_BUFFER_FLAG_RFF; if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_ONEFIELD) out_flags |= GST_VIDEO_BUFFER_FLAG_ONEFIELD; } GST_BUFFER_FLAG_SET (out_frame->output_buffer, out_flags); if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_FFB) { GST_BUFFER_FLAG_SET (out_frame->output_buffer, GST_VIDEO_BUFFER_FLAG_FIRST_IN_BUNDLE); } #if (USE_GLX || USE_EGL) if (decode->has_texture_upload_meta) gst_buffer_ensure_texture_upload_meta (out_frame->output_buffer); #endif } if (decode->in_segment.rate < 0.0 && !GST_VIDEO_CODEC_FRAME_IS_SYNC_POINT (out_frame)) { GST_TRACE_OBJECT (decode, "drop frame in reverse playback"); gst_video_decoder_release_frame (GST_VIDEO_DECODER (decode), out_frame); return GST_FLOW_OK; } ret = gst_video_decoder_finish_frame (vdec, out_frame); if (ret != GST_FLOW_OK) goto error_commit_buffer; return GST_FLOW_OK; /* ERRORS */ error_create_buffer: { const GstVaapiID surface_id = gst_vaapi_surface_get_id (GST_VAAPI_SURFACE_PROXY_SURFACE (proxy)); GST_ELEMENT_ERROR (vdec, STREAM, FAILED, ("Failed to create sink buffer"), ("video sink failed to create video buffer for proxy'ed " "surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (surface_id))); gst_video_decoder_drop_frame (vdec, out_frame); return GST_FLOW_ERROR; } error_get_meta: { GST_ELEMENT_ERROR (vdec, STREAM, FAILED, ("Failed to get vaapi video meta attached to video buffer"), ("Failed to get vaapi video meta attached to video buffer")); gst_video_decoder_drop_frame (vdec, out_frame); return GST_FLOW_ERROR; } error_commit_buffer: { GST_INFO_OBJECT (decode, "downstream element rejected the frame (%s [%d])", gst_flow_get_name (ret), ret); return ret; } }
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; } }
static GstFlowReturn gst_vaapidecode_push_decoded_frame (GstVideoDecoder * vdec, GstVideoCodecFrame * out_frame) { GstVaapiDecode *const decode = GST_VAAPIDECODE (vdec); GstVaapiSurfaceProxy *proxy; GstFlowReturn ret; const GstVaapiRectangle *crop_rect; GstVaapiVideoMeta *meta; guint flags, out_flags = 0; if (!GST_VIDEO_CODEC_FRAME_IS_DECODE_ONLY (out_frame)) { proxy = gst_video_codec_frame_get_user_data (out_frame); /* reconfigure if un-cropped surface resolution changed */ if (is_surface_resolution_changed (vdec, GST_VAAPI_SURFACE_PROXY_SURFACE (proxy))) gst_vaapidecode_negotiate (decode); gst_vaapi_surface_proxy_set_destroy_notify (proxy, (GDestroyNotify) gst_vaapidecode_release, gst_object_ref (decode)); ret = gst_video_decoder_allocate_output_frame (vdec, out_frame); if (ret != GST_FLOW_OK) goto error_create_buffer; meta = gst_buffer_get_vaapi_video_meta (out_frame->output_buffer); if (!meta) goto error_get_meta; gst_vaapi_video_meta_set_surface_proxy (meta, proxy); flags = gst_vaapi_surface_proxy_get_flags (proxy); if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_CORRUPTED) out_flags |= GST_BUFFER_FLAG_CORRUPTED; if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_INTERLACED) { out_flags |= GST_VIDEO_BUFFER_FLAG_INTERLACED; if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_TFF) out_flags |= GST_VIDEO_BUFFER_FLAG_TFF; if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_RFF) out_flags |= GST_VIDEO_BUFFER_FLAG_RFF; if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_ONEFIELD) out_flags |= GST_VIDEO_BUFFER_FLAG_ONEFIELD; } GST_BUFFER_FLAG_SET (out_frame->output_buffer, out_flags); #if GST_CHECK_VERSION(1,5,0) /* First-in-bundle flag only appeared in 1.5 dev */ if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_FFB) { GST_BUFFER_FLAG_SET (out_frame->output_buffer, GST_VIDEO_BUFFER_FLAG_FIRST_IN_BUNDLE); } #endif crop_rect = gst_vaapi_surface_proxy_get_crop_rect (proxy); if (crop_rect) { GstVideoCropMeta *const crop_meta = gst_buffer_add_video_crop_meta (out_frame->output_buffer); if (crop_meta) { crop_meta->x = crop_rect->x; crop_meta->y = crop_rect->y; crop_meta->width = crop_rect->width; crop_meta->height = crop_rect->height; } } #if (USE_GLX || USE_EGL) if (decode->has_texture_upload_meta) gst_buffer_ensure_texture_upload_meta (out_frame->output_buffer); #endif } ret = gst_video_decoder_finish_frame (vdec, out_frame); if (ret != GST_FLOW_OK) goto error_commit_buffer; gst_video_codec_frame_unref (out_frame); return GST_FLOW_OK; /* ERRORS */ error_create_buffer: { const GstVaapiID surface_id = gst_vaapi_surface_get_id (GST_VAAPI_SURFACE_PROXY_SURFACE (proxy)); GST_ELEMENT_ERROR (vdec, STREAM, FAILED, ("Failed to create sink buffer"), ("video sink failed to create video buffer for proxy'ed " "surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (surface_id))); gst_video_decoder_drop_frame (vdec, out_frame); gst_video_codec_frame_unref (out_frame); return GST_FLOW_ERROR; } error_get_meta: { GST_ELEMENT_ERROR (vdec, STREAM, FAILED, ("Failed to get vaapi video meta attached to video buffer"), ("Failed to get vaapi video meta attached to video buffer")); gst_video_decoder_drop_frame (vdec, out_frame); gst_video_codec_frame_unref (out_frame); return GST_FLOW_ERROR; } error_commit_buffer: { GST_INFO_OBJECT (decode, "downstream element rejected the frame (%s [%d])", gst_flow_get_name (ret), ret); gst_video_codec_frame_unref (out_frame); return ret; } }
static gboolean gst_vaapi_surface_create_full (GstVaapiSurface * surface, const GstVideoInfo * vip, guint flags) { #if VA_CHECK_VERSION(0,34,0) GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (surface); const GstVideoFormat format = GST_VIDEO_INFO_FORMAT (vip); VASurfaceID surface_id; VAStatus status; guint chroma_type, va_chroma_format, i; const VAImageFormat *va_format; VASurfaceAttrib attribs[3], *attrib; VASurfaceAttribExternalBuffers extbuf; gboolean extbuf_needed = FALSE; va_format = gst_vaapi_video_format_to_va_format (format); if (!va_format) goto error_unsupported_format; chroma_type = gst_vaapi_video_format_get_chroma_type (format); if (!chroma_type) goto error_unsupported_format; va_chroma_format = from_GstVaapiChromaType (chroma_type); if (!va_chroma_format) goto error_unsupported_format; memset (&extbuf, 0, sizeof (extbuf)); extbuf.pixel_format = va_format->fourcc; extbuf.width = GST_VIDEO_INFO_WIDTH (vip); extbuf.height = GST_VIDEO_INFO_HEIGHT (vip); extbuf_needed = ! !(flags & GST_VAAPI_SURFACE_ALLOC_FLAG_LINEAR_STORAGE); extbuf.num_planes = GST_VIDEO_INFO_N_PLANES (vip); if (flags & GST_VAAPI_SURFACE_ALLOC_FLAG_FIXED_STRIDES) { for (i = 0; i < extbuf.num_planes; i++) extbuf.pitches[i] = GST_VIDEO_INFO_PLANE_STRIDE (vip, i); extbuf_needed = TRUE; } if (flags & GST_VAAPI_SURFACE_ALLOC_FLAG_FIXED_OFFSETS) { for (i = 0; i < extbuf.num_planes; i++) extbuf.offsets[i] = GST_VIDEO_INFO_PLANE_OFFSET (vip, i); extbuf_needed = TRUE; } attrib = attribs; attrib->flags = VA_SURFACE_ATTRIB_SETTABLE; attrib->type = VASurfaceAttribPixelFormat; attrib->value.type = VAGenericValueTypeInteger; attrib->value.value.i = va_format->fourcc; attrib++; if (extbuf_needed) { attrib->flags = VA_SURFACE_ATTRIB_SETTABLE; attrib->type = VASurfaceAttribMemoryType; attrib->value.type = VAGenericValueTypeInteger; attrib->value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA; attrib++; attrib->flags = VA_SURFACE_ATTRIB_SETTABLE; attrib->type = VASurfaceAttribExternalBufferDescriptor; attrib->value.type = VAGenericValueTypePointer; attrib->value.value.p = &extbuf; attrib++; } GST_VAAPI_DISPLAY_LOCK (display); status = vaCreateSurfaces (GST_VAAPI_DISPLAY_VADISPLAY (display), va_chroma_format, extbuf.width, extbuf.height, &surface_id, 1, attribs, attrib - attribs); GST_VAAPI_DISPLAY_UNLOCK (display); if (!vaapi_check_status (status, "vaCreateSurfaces()")) return FALSE; surface->format = format; surface->chroma_type = chroma_type; surface->width = extbuf.width; surface->height = extbuf.height; GST_DEBUG ("surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (surface_id)); GST_VAAPI_OBJECT_ID (surface) = surface_id; return TRUE; /* ERRORS */ error_unsupported_format: GST_ERROR ("unsupported format %s", gst_vaapi_video_format_to_string (format)); return FALSE; #else return FALSE; #endif }
static gboolean gst_vaapi_surface_create_from_buffer_proxy (GstVaapiSurface * surface, GstVaapiBufferProxy * proxy, const GstVideoInfo * vip) { #if VA_CHECK_VERSION (0,36,0) GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (surface); GstVideoFormat format; VASurfaceID surface_id; VAStatus status; guint chroma_type, va_chroma_format; const VAImageFormat *va_format; VASurfaceAttrib attribs[2], *attrib; VASurfaceAttribExternalBuffers extbuf; unsigned long extbuf_handle; guint i, width, height; format = GST_VIDEO_INFO_FORMAT (vip); width = GST_VIDEO_INFO_WIDTH (vip); height = GST_VIDEO_INFO_HEIGHT (vip); gst_vaapi_buffer_proxy_replace (&surface->extbuf_proxy, proxy); va_format = gst_vaapi_video_format_to_va_format (format); if (!va_format) goto error_unsupported_format; chroma_type = gst_vaapi_video_format_get_chroma_type (format); if (!chroma_type) goto error_unsupported_format; va_chroma_format = from_GstVaapiChromaType (chroma_type); if (!va_chroma_format) goto error_unsupported_format; extbuf_handle = GST_VAAPI_BUFFER_PROXY_HANDLE (proxy); extbuf.pixel_format = va_format->fourcc; extbuf.width = width; extbuf.height = height; extbuf.data_size = GST_VAAPI_BUFFER_PROXY_SIZE (proxy); extbuf.num_planes = GST_VIDEO_INFO_N_PLANES (vip); for (i = 0; i < extbuf.num_planes; i++) { extbuf.pitches[i] = GST_VIDEO_INFO_PLANE_STRIDE (vip, i); extbuf.offsets[i] = GST_VIDEO_INFO_PLANE_OFFSET (vip, i); } extbuf.buffers = &extbuf_handle; extbuf.num_buffers = 1; extbuf.flags = 0; extbuf.private_data = NULL; attrib = attribs; attrib->type = VASurfaceAttribExternalBufferDescriptor; attrib->flags = VA_SURFACE_ATTRIB_SETTABLE; attrib->value.type = VAGenericValueTypePointer; attrib->value.value.p = &extbuf; attrib++; attrib->type = VASurfaceAttribMemoryType; attrib->flags = VA_SURFACE_ATTRIB_SETTABLE; attrib->value.type = VAGenericValueTypeInteger; attrib->value.value.i = from_GstVaapiBufferMemoryType (GST_VAAPI_BUFFER_PROXY_TYPE (proxy)); attrib++; GST_VAAPI_DISPLAY_LOCK (display); status = vaCreateSurfaces (GST_VAAPI_DISPLAY_VADISPLAY (display), va_chroma_format, width, height, &surface_id, 1, attribs, attrib - attribs); GST_VAAPI_DISPLAY_UNLOCK (display); if (!vaapi_check_status (status, "vaCreateSurfaces()")) return FALSE; surface->format = format; surface->chroma_type = chroma_type; surface->width = width; surface->height = height; GST_DEBUG ("surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (surface_id)); GST_VAAPI_OBJECT_ID (surface) = surface_id; return TRUE; /* ERRORS */ error_unsupported_format: GST_ERROR ("unsupported format %s", gst_vaapi_video_format_to_string (format)); return FALSE; #else return FALSE; #endif }