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;
}
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;
}
Example #3
0
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_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;
  }
}
Example #5
0
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 int
app_run (App * app)
{
  GstVaapiImage *image;
  GstVaapiVideoPool *pool;
  GThread *buffer_thread;
  gsize id;
  int ret = EXIT_FAILURE;

  image = gst_vaapi_image_new (app->display, GST_VIDEO_FORMAT_I420,
      app->parser->width, app->parser->height);

  {
    GstVideoInfo vi;
    gst_video_info_set_format (&vi, GST_VIDEO_FORMAT_ENCODED,
        app->parser->width, app->parser->height);
    pool = gst_vaapi_surface_pool_new_full (app->display, &vi, 0);
  }

  buffer_thread = g_thread_new ("get buffer thread", get_buffer_thread, app);

  while (1) {
    GstVaapiSurfaceProxy *proxy;
    GstVaapiSurface *surface;

    if (!load_frame (app, image))
      break;

    if (!gst_vaapi_image_unmap (image))
      break;

    proxy =
        gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL (pool));
    if (!proxy) {
      g_warning ("Could not get surface proxy from pool.");
      break;
    }
    surface = gst_vaapi_surface_proxy_get_surface (proxy);
    if (!surface) {
      g_warning ("Could not get surface from proxy.");
      break;
    }

    if (!gst_vaapi_surface_put_image (surface, image)) {
      g_warning ("Could not update surface");
      break;
    }

    if (!upload_frame (app->encoder, proxy)) {
      g_warning ("put frame failed");
      break;
    }

    app->read_frames++;
    id = gst_vaapi_surface_get_id (surface);
    g_debug ("input frame %d, surface id = %" G_GSIZE_FORMAT, app->read_frames,
        id);

    gst_vaapi_surface_proxy_unref (proxy);
  }

  app->input_stopped = TRUE;

  g_thread_join (buffer_thread);

  if (!app->encode_failed && feof (app->parser->fp))
    ret = EXIT_SUCCESS;

  gst_vaapi_video_pool_replace (&pool, NULL);
  gst_vaapi_object_unref (image);
  return ret;
}