gstreamer/sys/androidmedia/gstamcvideodec.c

2605 lines
79 KiB
C
Raw Normal View History

2012-08-20 11:45:53 +00:00
/*
* Initially based on gst-omx/omx/gstomxvideodec.c
*
* Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
* Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
*
* Copyright (C) 2012, Collabora Ltd.
* Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* Copyright (C) 2012, Rafaël Carré <funman@videolanorg>
*
* Copyright (C) 2015, Sebastian Dröge <sebastian@centricular.com>
*
* Copyright (C) 2014-2015, Collabora Ltd.
* Author: Matthieu Bouron <matthieu.bouron@gcollabora.com>
*
* Copyright (C) 2015, Edward Hervey
* Author: Edward Hervey <bilboed@gmail.com>
*
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
* Copyright (C) 2015, Matthew Waters <matthew@centricular.com>
*
2012-08-20 11:45:53 +00:00
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include <gst/gl/gl.h>
2012-10-18 14:41:07 +00:00
#include <gst/video/gstvideometa.h>
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
#include <gst/video/gstvideoaffinetransformationmeta.h>
2012-10-18 14:41:07 +00:00
#include <gst/video/gstvideopool.h>
2012-08-20 11:45:53 +00:00
#include <string.h>
#ifdef HAVE_ORC
#include <orc/orc.h>
#else
#define orc_memcpy memcpy
#endif
2012-08-20 11:45:53 +00:00
#include "gstamcvideodec.h"
#include "gstamc-constants.h"
GST_DEBUG_CATEGORY_STATIC (gst_amc_video_dec_debug_category);
#define GST_CAT_DEFAULT gst_amc_video_dec_debug_category
#define GST_VIDEO_DECODER_ERROR_FROM_ERROR(el, err) G_STMT_START { \
gchar *__dbg = g_strdup (err->message); \
GstVideoDecoder *__dec = GST_VIDEO_DECODER (el); \
GST_WARNING_OBJECT (el, "error: %s", __dbg); \
_gst_video_decoder_error (__dec, 1, \
err->domain, err->code, \
NULL, __dbg, __FILE__, GST_FUNCTION, __LINE__); \
g_clear_error (&err); \
} G_STMT_END
#if GLIB_SIZEOF_VOID_P == 8
#define JLONG_TO_GST_AMC_VIDEO_DEC(value) (GstAmcVideoDec *)(value)
#define GST_AMC_VIDEO_DEC_TO_JLONG(value) (jlong)(value)
#else
#define JLONG_TO_GST_AMC_VIDEO_DEC(value) (GstAmcVideoDec *)(jint)(value)
#define GST_AMC_VIDEO_DEC_TO_JLONG(value) (jlong)(jint)(value)
#endif
2012-08-20 11:45:53 +00:00
typedef struct _BufferIdentification BufferIdentification;
struct _BufferIdentification
{
guint64 timestamp;
};
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
struct gl_sync_result
{
gint refcount;
gint64 frame_available_ts;
gboolean updated; /* only every call update_tex_image once */
gboolean released; /* only every call release_output_buffer once */
gboolean rendered; /* whether the release resulted in a render */
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
};
static struct gl_sync_result *
_gl_sync_result_ref (struct gl_sync_result *result)
{
g_assert (result != NULL);
g_atomic_int_inc (&result->refcount);
GST_TRACE ("gl_sync result %p ref", result);
return result;
}
static void
_gl_sync_result_unref (struct gl_sync_result *result)
{
g_assert (result != NULL);
GST_TRACE ("gl_sync result %p unref", result);
if (g_atomic_int_dec_and_test (&result->refcount)) {
GST_TRACE ("freeing gl_sync result %p", result);
g_free (result);
}
}
struct gl_sync
{
gint refcount;
2015-11-17 14:21:10 +00:00
GstAmcVideoDec *sink; /* back reference for statistics, lock, cond, etc */
gint buffer_idx; /* idx of the AMC buffer we should render */
2015-11-17 14:21:10 +00:00
GstBuffer *buffer; /* back reference to the buffer */
GstGLMemory *oes_mem; /* where amc is rendering into. The same for every gl_sync */
GstAmcSurface *surface; /* java wrapper for where amc is rendering into */
guint gl_frame_no; /* effectively the frame id */
gint64 released_ts; /* microseconds from g_get_monotonic_time() */
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
struct gl_sync_result *result;
};
static struct gl_sync *
_gl_sync_ref (struct gl_sync *sync)
{
g_assert (sync != NULL);
g_atomic_int_inc (&sync->refcount);
GST_TRACE ("gl_sync %p ref", sync);
return sync;
}
static void
_gl_sync_unref (struct gl_sync *sync)
{
g_assert (sync != NULL);
GST_TRACE ("gl_sync %p unref", sync);
if (g_atomic_int_dec_and_test (&sync->refcount)) {
GST_TRACE ("freeing gl_sync %p", sync);
_gl_sync_result_unref (sync->result);
g_object_unref (sync->surface);
gst_memory_unref ((GstMemory *) sync->oes_mem);
g_free (sync);
}
}
static gint
_queue_compare_gl_sync (gconstpointer a, gconstpointer b)
{
const struct gl_sync *sync = a;
guint frame = GPOINTER_TO_INT (b);
return sync->gl_frame_no - frame;
}
static GList *
_find_gl_sync_for_frame (GstAmcVideoDec * dec, guint frame)
{
return g_queue_find_custom (dec->gl_queue, GINT_TO_POINTER (frame),
(GCompareFunc) _queue_compare_gl_sync);
}
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
static void
_attach_mem_to_context (GstGLContext * context, GstAmcVideoDec * self)
{
GST_TRACE_OBJECT (self, "attaching texture %p id %u to current context",
self->surface->texture, self->oes_mem->tex_id);
if (!gst_amc_surface_texture_attach_to_gl_context (self->surface->texture,
self->oes_mem->tex_id, &self->gl_error)) {
GST_ERROR_OBJECT (self, "Failed to attach texture to the GL context");
GST_ELEMENT_ERROR_FROM_ERROR (self, self->gl_error);
} else {
self->gl_mem_attached = TRUE;
}
}
static void
_dettach_mem_from_context (GstGLContext * context, GstAmcVideoDec * self)
{
if (self->surface) {
guint tex_id = self->oes_mem ? self->oes_mem->tex_id : 0;
GST_TRACE_OBJECT (self, "detaching texture %p id %u from current context",
self->surface->texture, tex_id);
if (!gst_amc_surface_texture_detach_from_gl_context (self->surface->texture,
&self->gl_error)) {
GST_ERROR_OBJECT (self, "Failed to attach texture to the GL context");
GST_ELEMENT_ERROR_FROM_ERROR (self, self->gl_error);
}
}
self->gl_mem_attached = FALSE;
}
2012-08-20 11:45:53 +00:00
static BufferIdentification *
buffer_identification_new (GstClockTime timestamp)
{
BufferIdentification *id = g_slice_new (BufferIdentification);
id->timestamp = timestamp;
return id;
}
static void
buffer_identification_free (BufferIdentification * id)
{
g_slice_free (BufferIdentification, id);
}
/* prototypes */
static void gst_amc_video_dec_finalize (GObject * object);
static GstStateChangeReturn
gst_amc_video_dec_change_state (GstElement * element,
GstStateChange transition);
static void gst_amc_video_dec_set_context (GstElement * element,
GstContext * context);
2012-08-20 11:45:53 +00:00
static gboolean gst_amc_video_dec_open (GstVideoDecoder * decoder);
static gboolean gst_amc_video_dec_close (GstVideoDecoder * decoder);
static gboolean gst_amc_video_dec_start (GstVideoDecoder * decoder);
static gboolean gst_amc_video_dec_stop (GstVideoDecoder * decoder);
static gboolean gst_amc_video_dec_set_format (GstVideoDecoder * decoder,
GstVideoCodecState * state);
static gboolean gst_amc_video_dec_flush (GstVideoDecoder * decoder);
2012-08-20 11:45:53 +00:00
static GstFlowReturn gst_amc_video_dec_handle_frame (GstVideoDecoder * decoder,
GstVideoCodecFrame * frame);
static GstFlowReturn gst_amc_video_dec_finish (GstVideoDecoder * decoder);
2012-10-18 14:41:07 +00:00
static gboolean gst_amc_video_dec_decide_allocation (GstVideoDecoder * bdec,
GstQuery * query);
static gboolean gst_amc_video_dec_src_query (GstVideoDecoder * bdec,
GstQuery * query);
2012-08-20 11:45:53 +00:00
static GstFlowReturn gst_amc_video_dec_drain (GstAmcVideoDec * self);
static gboolean gst_amc_video_dec_check_codec_config (GstAmcVideoDec * self);
static void
gst_amc_video_dec_on_frame_available (JNIEnv * env, jobject thiz,
long long context, jobject surfaceTexture);
2012-08-20 11:45:53 +00:00
enum
{
PROP_0
};
/* class initialization */
static void gst_amc_video_dec_class_init (GstAmcVideoDecClass * klass);
static void gst_amc_video_dec_init (GstAmcVideoDec * self);
static void gst_amc_video_dec_base_init (gpointer g_class);
static GstVideoDecoderClass *parent_class = NULL;
GType
gst_amc_video_dec_get_type (void)
{
static volatile gsize type = 0;
if (g_once_init_enter (&type)) {
GType _type;
static const GTypeInfo info = {
sizeof (GstAmcVideoDecClass),
gst_amc_video_dec_base_init,
NULL,
(GClassInitFunc) gst_amc_video_dec_class_init,
NULL,
NULL,
sizeof (GstAmcVideoDec),
0,
(GInstanceInitFunc) gst_amc_video_dec_init,
NULL
};
_type = g_type_register_static (GST_TYPE_VIDEO_DECODER, "GstAmcVideoDec",
&info, 0);
2013-05-20 11:24:07 +00:00
GST_DEBUG_CATEGORY_INIT (gst_amc_video_dec_debug_category, "amcvideodec", 0,
"Android MediaCodec video decoder");
g_once_init_leave (&type, _type);
}
return type;
}
2012-08-20 11:45:53 +00:00
static const gchar *
caps_to_mime (GstCaps * caps)
{
GstStructure *s;
const gchar *name;
s = gst_caps_get_structure (caps, 0);
if (!s)
return NULL;
name = gst_structure_get_name (s);
if (strcmp (name, "video/mpeg") == 0) {
gint mpegversion;
if (!gst_structure_get_int (s, "mpegversion", &mpegversion))
return NULL;
if (mpegversion == 4)
return "video/mp4v-es";
2012-09-24 13:14:09 +00:00
else if (mpegversion == 1 || mpegversion == 2)
return "video/mpeg2";
2012-08-20 11:45:53 +00:00
} else if (strcmp (name, "video/x-h263") == 0) {
return "video/3gpp";
} else if (strcmp (name, "video/x-h264") == 0) {
return "video/avc";
} else if (strcmp (name, "video/x-h265") == 0) {
return "video/hevc";
2012-08-20 11:45:53 +00:00
} else if (strcmp (name, "video/x-vp8") == 0) {
return "video/x-vnd.on2.vp8";
2017-01-04 18:59:39 +00:00
} else if (strcmp (name, "video/x-vp9") == 0) {
return "video/x-vnd.on2.vp9";
} else if (strcmp (name, "video/x-divx") == 0) {
return "video/mp4v-es";
2012-08-20 11:45:53 +00:00
}
return NULL;
}
static void
gst_amc_video_dec_base_init (gpointer g_class)
2012-08-20 11:45:53 +00:00
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
GstAmcVideoDecClass *amcvideodec_class = GST_AMC_VIDEO_DEC_CLASS (g_class);
2012-08-20 11:45:53 +00:00
const GstAmcCodecInfo *codec_info;
GstPadTemplate *templ;
GstCaps *sink_caps, *src_caps, *all_src_caps;
gchar *longname;
2012-08-20 11:45:53 +00:00
codec_info =
g_type_get_qdata (G_TYPE_FROM_CLASS (g_class), gst_amc_codec_info_quark);
2012-08-20 11:45:53 +00:00
/* This happens for the base class and abstract subclasses */
if (!codec_info)
return;
2012-10-18 14:41:07 +00:00
amcvideodec_class->codec_info = codec_info;
2012-08-20 11:45:53 +00:00
gst_amc_codec_info_to_caps (codec_info, &sink_caps, &src_caps);
all_src_caps =
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
gst_caps_from_string ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY
"), format = (string) RGBA, texture-target = (string) external-oes");
if (codec_info->gl_output_only) {
gst_caps_unref (src_caps);
} else {
gst_caps_append (all_src_caps, src_caps);
}
2012-08-20 11:45:53 +00:00
/* Add pad templates */
templ =
gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, sink_caps);
2012-08-20 11:45:53 +00:00
gst_element_class_add_pad_template (element_class, templ);
gst_caps_unref (sink_caps);
2012-08-20 11:45:53 +00:00
templ =
gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, all_src_caps);
2012-08-20 11:45:53 +00:00
gst_element_class_add_pad_template (element_class, templ);
gst_caps_unref (all_src_caps);
longname = g_strdup_printf ("Android MediaCodec %s", codec_info->name);
2012-10-18 14:41:07 +00:00
gst_element_class_set_metadata (element_class,
codec_info->name,
"Codec/Decoder/Video",
longname, "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
g_free (longname);
2012-08-20 11:45:53 +00:00
}
static void
gst_amc_video_dec_class_init (GstAmcVideoDecClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstVideoDecoderClass *videodec_class = GST_VIDEO_DECODER_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
gobject_class->finalize = gst_amc_video_dec_finalize;
element_class->change_state =
GST_DEBUG_FUNCPTR (gst_amc_video_dec_change_state);
element_class->set_context =
GST_DEBUG_FUNCPTR (gst_amc_video_dec_set_context);
videodec_class->start = GST_DEBUG_FUNCPTR (gst_amc_video_dec_start);
videodec_class->stop = GST_DEBUG_FUNCPTR (gst_amc_video_dec_stop);
videodec_class->open = GST_DEBUG_FUNCPTR (gst_amc_video_dec_open);
videodec_class->close = GST_DEBUG_FUNCPTR (gst_amc_video_dec_close);
videodec_class->flush = GST_DEBUG_FUNCPTR (gst_amc_video_dec_flush);
videodec_class->set_format = GST_DEBUG_FUNCPTR (gst_amc_video_dec_set_format);
videodec_class->handle_frame =
GST_DEBUG_FUNCPTR (gst_amc_video_dec_handle_frame);
videodec_class->finish = GST_DEBUG_FUNCPTR (gst_amc_video_dec_finish);
videodec_class->decide_allocation =
GST_DEBUG_FUNCPTR (gst_amc_video_dec_decide_allocation);
videodec_class->src_query = GST_DEBUG_FUNCPTR (gst_amc_video_dec_src_query);
}
2012-08-20 11:45:53 +00:00
static void
2012-10-18 14:41:07 +00:00
gst_amc_video_dec_init (GstAmcVideoDec * self)
2012-08-20 11:45:53 +00:00
{
gst_video_decoder_set_packetized (GST_VIDEO_DECODER (self), TRUE);
gst_video_decoder_set_needs_format (GST_VIDEO_DECODER (self), TRUE);
2012-08-20 11:45:53 +00:00
g_mutex_init (&self->drain_lock);
g_cond_init (&self->drain_cond);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
g_mutex_init (&self->gl_lock);
g_cond_init (&self->gl_cond);
self->gl_queue = g_queue_new ();
2012-08-20 11:45:53 +00:00
}
static gboolean
gst_amc_video_dec_open (GstVideoDecoder * decoder)
{
GstAmcVideoDec *self = GST_AMC_VIDEO_DEC (decoder);
GstAmcVideoDecClass *klass = GST_AMC_VIDEO_DEC_GET_CLASS (self);
GError *err = NULL;
2012-08-20 11:45:53 +00:00
GST_DEBUG_OBJECT (self, "Opening decoder");
self->codec = gst_amc_codec_new (klass->codec_info->name, &err);
if (!self->codec) {
GST_ELEMENT_ERROR_FROM_ERROR (self, err);
2012-08-20 11:45:53 +00:00
return FALSE;
}
self->codec_config = AMC_CODEC_CONFIG_NONE;
2012-08-20 11:45:53 +00:00
self->started = FALSE;
self->flushing = TRUE;
GST_DEBUG_OBJECT (self, "Opened decoder");
return TRUE;
}
static gboolean
gst_amc_video_dec_close (GstVideoDecoder * decoder)
{
GstAmcVideoDec *self = GST_AMC_VIDEO_DEC (decoder);
GST_DEBUG_OBJECT (self, "Closing decoder");
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
if (self->downstream_supports_gl
&& self->codec_config == AMC_CODEC_CONFIG_WITH_SURFACE) {
g_mutex_lock (&self->gl_lock);
GST_INFO_OBJECT (self, "shutting down gl queue pushed %u ready %u "
"released %u", self->gl_pushed_frame_count, self->gl_ready_frame_count,
self->gl_released_frame_count);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
g_queue_free_full (self->gl_queue, (GDestroyNotify) _gl_sync_unref);
self->gl_queue = g_queue_new ();
g_mutex_unlock (&self->gl_lock);
if (self->gl_mem_attached)
gst_gl_context_thread_add (self->gl_context,
(GstGLContextThreadFunc) _dettach_mem_from_context, self);
}
self->gl_pushed_frame_count = 0;
self->gl_ready_frame_count = 0;
self->gl_released_frame_count = 0;
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
self->gl_last_rendered_frame = 0;
if (self->surface) {
gst_object_unref (self->surface);
self->surface = NULL;
}
2012-08-20 11:45:53 +00:00
if (self->listener) {
JNIEnv *env = gst_amc_jni_get_env ();
GError *err = NULL;
if (!gst_amc_jni_call_void_method (env, &err, self->listener,
self->set_context_id, GST_AMC_VIDEO_DEC_TO_JLONG (NULL))) {
GST_ERROR_OBJECT (self, "Failed to unset back pointer on the listener. "
"crashes/hangs may ensue: %s", err ? err->message : "Unknown");
GST_ELEMENT_ERROR_FROM_ERROR (self, err);
}
gst_amc_jni_object_unref (env, self->listener);
}
self->listener = NULL;
if (self->codec) {
GError *err = NULL;
gst_amc_codec_release (self->codec, &err);
if (err)
GST_ELEMENT_WARNING_FROM_ERROR (self, err);
gst_amc_codec_free (self->codec);
}
2012-08-20 11:45:53 +00:00
self->started = FALSE;
self->flushing = TRUE;
self->downstream_supports_gl = FALSE;
2012-08-20 11:45:53 +00:00
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
self->codec = NULL;
self->codec_config = AMC_CODEC_CONFIG_NONE;
GST_DEBUG_OBJECT (self, "Freeing GL context: %" GST_PTR_FORMAT,
self->gl_context);
if (self->gl_context) {
gst_object_unref (self->gl_context);
self->gl_context = NULL;
}
if (self->oes_mem) {
gst_memory_unref ((GstMemory *) self->oes_mem);
self->oes_mem = NULL;
}
if (self->gl_display) {
gst_object_unref (self->gl_display);
self->gl_display = NULL;
}
if (self->other_gl_context) {
gst_object_unref (self->other_gl_context);
self->other_gl_context = NULL;
}
2012-08-20 11:45:53 +00:00
GST_DEBUG_OBJECT (self, "Closed decoder");
return TRUE;
}
static void
gst_amc_video_dec_finalize (GObject * object)
{
GstAmcVideoDec *self = GST_AMC_VIDEO_DEC (object);
g_mutex_clear (&self->drain_lock);
g_cond_clear (&self->drain_cond);
2012-08-20 11:45:53 +00:00
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
g_mutex_clear (&self->gl_lock);
g_cond_clear (&self->gl_cond);
if (self->gl_queue) {
g_queue_free_full (self->gl_queue, (GDestroyNotify) _gl_sync_unref);
self->gl_queue = NULL;
}
2012-08-20 11:45:53 +00:00
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_amc_video_dec_set_context (GstElement * element, GstContext * context)
{
GstAmcVideoDec *self = GST_AMC_VIDEO_DEC (element);
gst_gl_handle_set_context (element, context, &self->gl_display,
&self->other_gl_context);
GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
}
2012-08-20 11:45:53 +00:00
static GstStateChangeReturn
gst_amc_video_dec_change_state (GstElement * element, GstStateChange transition)
{
GstAmcVideoDec *self;
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GError *err = NULL;
2012-08-20 11:45:53 +00:00
g_return_val_if_fail (GST_IS_AMC_VIDEO_DEC (element),
GST_STATE_CHANGE_FAILURE);
self = GST_AMC_VIDEO_DEC (element);
GST_DEBUG_OBJECT (element, "changing state: %s => %s",
gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
2012-08-20 11:45:53 +00:00
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
self->downstream_flow_ret = GST_FLOW_OK;
self->draining = FALSE;
self->started = FALSE;
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
2012-09-07 12:22:54 +00:00
self->flushing = TRUE;
if (self->started) {
gst_amc_codec_flush (self->codec, &err);
if (err)
GST_ELEMENT_WARNING_FROM_ERROR (self, err);
}
g_mutex_lock (&self->drain_lock);
2012-08-20 11:45:53 +00:00
self->draining = FALSE;
g_cond_broadcast (&self->drain_cond);
g_mutex_unlock (&self->drain_lock);
2012-08-20 11:45:53 +00:00
break;
default:
break;
}
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
switch (transition) {
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
2012-10-18 14:41:07 +00:00
self->downstream_flow_ret = GST_FLOW_FLUSHING;
2012-08-20 11:45:53 +00:00
self->started = FALSE;
break;
default:
break;
}
return ret;
}
#define MAX_FRAME_DIST_TIME (5 * GST_SECOND)
#define MAX_FRAME_DIST_FRAMES (100)
static GstVideoCodecFrame *
_find_nearest_frame (GstAmcVideoDec * self, GstClockTime reference_timestamp)
{
GList *l, *best_l = NULL;
GList *finish_frames = NULL;
GstVideoCodecFrame *best = NULL;
guint64 best_timestamp = 0;
guint64 best_diff = G_MAXUINT64;
BufferIdentification *best_id = NULL;
GList *frames;
frames = gst_video_decoder_get_frames (GST_VIDEO_DECODER (self));
for (l = frames; l; l = l->next) {
GstVideoCodecFrame *tmp = l->data;
BufferIdentification *id = gst_video_codec_frame_get_user_data (tmp);
guint64 timestamp, diff;
/* This happens for frames that were just added but
* which were not passed to the component yet. Ignore
* them here!
*/
if (!id)
continue;
timestamp = id->timestamp;
if (timestamp > reference_timestamp)
diff = timestamp - reference_timestamp;
else
diff = reference_timestamp - timestamp;
if (best == NULL || diff < best_diff) {
best = tmp;
best_timestamp = timestamp;
best_diff = diff;
best_l = l;
best_id = id;
/* For frames without timestamp we simply take the first frame */
if ((reference_timestamp == 0 && !GST_CLOCK_TIME_IS_VALID (timestamp))
|| diff == 0)
2012-08-20 11:45:53 +00:00
break;
}
}
if (best_id) {
for (l = frames; l && l != best_l; l = l->next) {
GstVideoCodecFrame *tmp = l->data;
BufferIdentification *id = gst_video_codec_frame_get_user_data (tmp);
guint64 diff_time, diff_frames;
if (id->timestamp > best_timestamp)
break;
if (id->timestamp == 0 || best_timestamp == 0)
diff_time = 0;
else
diff_time = best_timestamp - id->timestamp;
diff_frames = best->system_frame_number - tmp->system_frame_number;
if (diff_time > MAX_FRAME_DIST_TIME
|| diff_frames > MAX_FRAME_DIST_FRAMES) {
finish_frames =
g_list_prepend (finish_frames, gst_video_codec_frame_ref (tmp));
2012-08-20 11:45:53 +00:00
}
}
}
if (finish_frames) {
g_warning ("%s: Too old frames, bug in decoder -- please file a bug",
GST_ELEMENT_NAME (self));
for (l = finish_frames; l; l = l->next) {
gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), l->data);
}
}
2012-09-06 15:07:49 +00:00
if (best)
gst_video_codec_frame_ref (best);
2012-08-20 11:45:53 +00:00
g_list_foreach (frames, (GFunc) gst_video_codec_frame_unref, NULL);
g_list_free (frames);
return best;
}
static gboolean
gst_amc_video_dec_check_codec_config (GstAmcVideoDec * self)
{
gboolean ret = (self->codec_config == AMC_CODEC_CONFIG_NONE
|| (self->codec_config == AMC_CODEC_CONFIG_WITH_SURFACE
&& self->downstream_supports_gl)
|| (self->codec_config == AMC_CODEC_CONFIG_WITHOUT_SURFACE
&& !self->downstream_supports_gl));
if (!ret) {
GST_ERROR_OBJECT
(self,
"Codec configuration (%d) is not compatible with downstream which %s support GL output",
self->codec_config, self->downstream_supports_gl ? "does" : "does not");
}
return ret;
}
2012-08-20 11:45:53 +00:00
static gboolean
gst_amc_video_dec_set_src_caps (GstAmcVideoDec * self, GstAmcFormat * format)
{
GstVideoCodecState *output_state;
const gchar *mime;
2012-08-20 11:45:53 +00:00
gint color_format, width, height;
gint stride, slice_height;
gint crop_left, crop_right;
gint crop_top, crop_bottom;
GstVideoFormat gst_format;
GstAmcVideoDecClass *klass = GST_AMC_VIDEO_DEC_GET_CLASS (self);
GError *err = NULL;
gboolean ret;
if (!gst_amc_format_get_int (format, "color-format", &color_format, &err) ||
!gst_amc_format_get_int (format, "width", &width, &err) ||
!gst_amc_format_get_int (format, "height", &height, &err)) {
GST_ERROR_OBJECT (self, "Failed to get output format metadata: %s",
err->message);
g_clear_error (&err);
2012-08-20 11:45:53 +00:00
return FALSE;
}
if (!gst_amc_format_get_int (format, "stride", &stride, &err) ||
!gst_amc_format_get_int (format, "slice-height", &slice_height, &err)) {
GST_ERROR_OBJECT (self, "Failed to get stride and slice-height: %s",
err->message);
g_clear_error (&err);
2012-08-20 11:45:53 +00:00
return FALSE;
}
if (!gst_amc_format_get_int (format, "crop-left", &crop_left, &err) ||
!gst_amc_format_get_int (format, "crop-right", &crop_right, &err) ||
!gst_amc_format_get_int (format, "crop-top", &crop_top, &err) ||
!gst_amc_format_get_int (format, "crop-bottom", &crop_bottom, &err)) {
GST_ERROR_OBJECT (self, "Failed to get crop rectangle: %s", err->message);
g_clear_error (&err);
2012-08-20 11:45:53 +00:00
return FALSE;
}
if (width == 0 || height == 0) {
GST_ERROR_OBJECT (self, "Height or width not set");
return FALSE;
}
if (crop_bottom)
height = height - (height - crop_bottom - 1);
if (crop_top)
height = height - crop_top;
if (crop_right)
width = width - (width - crop_right - 1);
if (crop_left)
width = width - crop_left;
mime = caps_to_mime (self->input_state->caps);
if (!mime) {
GST_ERROR_OBJECT (self, "Failed to convert caps to mime");
return FALSE;
}
if (self->codec_config == AMC_CODEC_CONFIG_WITH_SURFACE) {
gst_format = GST_VIDEO_FORMAT_RGBA;
} else {
gst_format =
gst_amc_color_format_to_video_format (klass->codec_info, mime,
color_format);
}
2012-08-20 11:45:53 +00:00
if (gst_format == GST_VIDEO_FORMAT_UNKNOWN) {
GST_ERROR_OBJECT (self, "Unknown color format 0x%08x", color_format);
return FALSE;
}
output_state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (self),
gst_format, width, height, self->input_state);
/* FIXME: Special handling for multiview, untested */
if (color_format == COLOR_QCOM_FormatYVU420SemiPlanar32mMultiView) {
gst_video_multiview_video_info_change_mode (&output_state->info,
GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM, GST_VIDEO_MULTIVIEW_FLAGS_NONE);
}
memset (&self->color_format_info, 0, sizeof (self->color_format_info));
if (self->codec_config == AMC_CODEC_CONFIG_WITH_SURFACE) {
if (output_state->caps)
gst_caps_unref (output_state->caps);
output_state->caps = gst_video_info_to_caps (&output_state->info);
gst_caps_set_features (output_state->caps, 0,
gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, NULL));
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
gst_caps_set_simple (output_state->caps, "texture-target", G_TYPE_STRING,
"external-oes", NULL);
GST_DEBUG_OBJECT (self, "Configuring for Surface output");
/* The width/height values are used in other places for
* checking if the resolution changed. Set everything
* that makes sense here
*/
self->color_format_info.color_format = COLOR_FormatAndroidOpaque;
self->color_format_info.width = width;
self->color_format_info.height = height;
self->color_format_info.crop_left = crop_left;
self->color_format_info.crop_right = crop_right;
self->color_format_info.crop_top = crop_top;
self->color_format_info.crop_bottom = crop_bottom;
goto out;
}
2012-08-20 11:45:53 +00:00
self->format = gst_format;
self->width = width;
self->height = height;
if (!gst_amc_color_format_info_set (&self->color_format_info,
klass->codec_info, mime, color_format, width, height, stride,
slice_height, crop_left, crop_right, crop_top, crop_bottom)) {
GST_ERROR_OBJECT (self, "Failed to set up GstAmcColorFormatInfo");
return FALSE;
}
GST_DEBUG_OBJECT (self,
"Color format info: {color_format=%d (0x%08x), width=%d, height=%d, "
"stride=%d, slice-height=%d, crop-left=%d, crop-top=%d, "
"crop-right=%d, crop-bottom=%d, frame-size=%d}",
self->color_format_info.color_format,
self->color_format_info.color_format, self->color_format_info.width,
self->color_format_info.height, self->color_format_info.stride,
self->color_format_info.slice_height, self->color_format_info.crop_left,
self->color_format_info.crop_top, self->color_format_info.crop_right,
self->color_format_info.crop_bottom, self->color_format_info.frame_size);
2012-08-20 11:45:53 +00:00
out:
ret = gst_video_decoder_negotiate (GST_VIDEO_DECODER (self));
2012-08-20 11:45:53 +00:00
gst_video_codec_state_unref (output_state);
self->input_state_changed = FALSE;
return ret;
2012-08-20 11:45:53 +00:00
}
static gboolean
gst_amc_video_dec_fill_buffer (GstAmcVideoDec * self, GstAmcBuffer * buf,
const GstAmcBufferInfo * buffer_info, GstBuffer * outbuf)
2012-08-20 11:45:53 +00:00
{
GstVideoCodecState *state =
gst_video_decoder_get_output_state (GST_VIDEO_DECODER (self));
GstVideoInfo *info = &state->info;
gboolean ret = FALSE;
if (self->color_format_info.color_format == COLOR_FormatAndroidOpaque)
return FALSE;
ret =
gst_amc_color_format_copy (&self->color_format_info, buf, buffer_info,
info, outbuf, COLOR_FORMAT_COPY_OUT);
gst_video_codec_state_unref (state);
return ret;
2012-08-20 11:45:53 +00:00
}
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
static const gfloat yflip_matrix[16] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 1.0f
};
static void
_amc_gl_set_sync (GstGLSyncMeta * sync_meta, GstGLContext * context)
{
}
static void
_gl_sync_release_buffer (struct gl_sync *sync, gboolean render)
{
GError *error = NULL;
if (!sync->result->released) {
sync->released_ts = g_get_monotonic_time ();
if ((gint) (sync->sink->gl_released_frame_count -
sync->sink->gl_ready_frame_count) > 0) {
guint diff =
sync->sink->gl_released_frame_count -
sync->sink->gl_ready_frame_count - 1u;
sync->sink->gl_ready_frame_count += diff;
GST_LOG ("gl_sync %p possible \'on_frame_available\' listener miss "
"detected, attempting to work around. Jumping forward %u "
"frames for frame %u", sync, diff, sync->gl_frame_no);
}
GST_TRACE ("gl_sync %p release_output_buffer idx %u frame %u render %s",
sync, sync->buffer_idx, sync->gl_frame_no, render ? "TRUE" : "FALSE");
/* Release the frame into the surface */
sync->sink->gl_released_frame_count++;
if (!render) {
/* Advance the ready counter ourselves if we aren't going to render
* and therefore receive a listener callback */
sync->sink->gl_ready_frame_count++;
}
if (!gst_amc_codec_release_output_buffer (sync->sink->codec,
sync->buffer_idx, render, &error)) {
GST_ERROR_OBJECT (sync->sink,
"gl_sync %p Failed to render buffer, index %d frame %u", sync,
sync->buffer_idx, sync->gl_frame_no);
goto out;
}
sync->result->released = TRUE;
sync->result->rendered = render;
}
out:
if (error) {
if (sync->sink->gl_error == NULL)
sync->sink->gl_error = error;
else
g_clear_error (&error);
}
}
static void
_gl_sync_release_next_buffer (struct gl_sync *sync, gboolean render)
{
GList *l;
if ((l = _find_gl_sync_for_frame (sync->sink, sync->gl_frame_no + 1))) {
struct gl_sync *next = l->data;
_gl_sync_release_buffer (next, render);
} else {
GST_TRACE ("gl_sync %p no next frame available", sync);
}
}
#define I(x,y) ((y)*4+(x))
static int affine_inverse (float in[], float out[]) {
float s0, s1, s2, s3, s4, s5;
float c0, c1, c2, c3, c4, c5;
float det, invdet;
s0 = in[0] * in[I(1,1)] - in[I(1,0)] * in[I(0,1)];
s1 = in[0] * in[I(1,2)] - in[I(1,0)] * in[I(0,2)];
s2 = in[0] * in[I(1,3)] - in[I(1,0)] * in[I(0,3)];
s3 = in[1] * in[I(1,2)] - in[I(1,1)] * in[I(0,2)];
s4 = in[1] * in[I(1,3)] - in[I(1,1)] * in[I(0,3)];
s5 = in[2] * in[I(1,3)] - in[I(1,2)] * in[I(0,3)];
c0 = in[I(2,0)] * in[I(3,1)] - in[I(3,0)] * in[I(2,1)];
c1 = in[I(2,0)] * in[I(3,2)] - in[I(3,0)] * in[I(2,2)];
c2 = in[I(2,0)] * in[I(3,3)] - in[I(3,0)] * in[I(2,3)];
c3 = in[I(2,1)] * in[I(3,2)] - in[I(3,1)] * in[I(2,2)];
c4 = in[I(2,1)] * in[I(3,3)] - in[I(3,1)] * in[I(2,3)];
c5 = in[I(2,2)] * in[I(3,3)] - in[I(3,2)] * in[I(2,3)];
det = s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0;
if (det == 0.0)
return 0;
invdet = 1.0 / det;
out[I(0,0)] = ( in[I(1,1)] * c5 - in[I(1,2)] * c4 + in[I(1,3)] * c3) * invdet;
out[I(0,1)] = (-in[I(0,1)] * c5 + in[I(0,2)] * c4 - in[I(0,3)] * c3) * invdet;
out[I(0,2)] = ( in[I(3,1)] * s5 - in[I(3,2)] * s4 + in[I(3,3)] * s3) * invdet;
out[I(0,3)] = (-in[I(2,1)] * s5 + in[I(2,2)] * s4 - in[I(2,3)] * s3) * invdet;
out[I(1,0)] = (-in[I(1,0)] * c5 + in[I(1,2)] * c2 - in[I(1,3)] * c1) * invdet;
out[I(1,1)] = ( in[I(0,0)] * c5 - in[I(0,2)] * c2 + in[I(0,3)] * c1) * invdet;
out[I(1,2)] = (-in[I(3,0)] * s5 + in[I(3,2)] * s2 - in[I(3,3)] * s1) * invdet;
out[I(1,3)] = ( in[I(2,0)] * s5 - in[I(2,2)] * s2 + in[I(2,3)] * s1) * invdet;
out[I(2,0)] = ( in[I(1,0)] * c4 - in[I(1,1)] * c2 + in[I(1,3)] * c0) * invdet;
out[I(2,1)] = (-in[I(0,0)] * c4 + in[I(0,1)] * c2 - in[I(0,3)] * c0) * invdet;
out[I(2,2)] = ( in[I(3,0)] * s4 - in[I(3,1)] * s2 + in[I(3,3)] * s0) * invdet;
out[I(2,3)] = (-in[I(2,0)] * s4 + in[I(2,1)] * s2 - in[I(2,3)] * s0) * invdet;
out[I(3,0)] = (-in[I(1,0)] * c3 + in[I(1,1)] * c1 - in[I(1,2)] * c0) * invdet;
out[I(3,1)] = ( in[I(0,0)] * c3 - in[I(0,1)] * c1 + in[I(0,2)] * c0) * invdet;
out[I(3,2)] = (-in[I(3,0)] * s3 + in[I(3,1)] * s1 - in[I(3,2)] * s0) * invdet;
out[I(3,3)] = ( in[I(2,0)] * s3 - in[I(2,1)] * s1 + in[I(2,2)] * s0) * invdet;
return 1;
}
#undef I
/* caller should remove from the gl_queue after calling this function.
* _gl_sync_release_buffer must be called before this function */
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
static void
_gl_sync_render_unlocked (struct gl_sync *sync)
{
GstVideoAffineTransformationMeta *af_meta;
GError *error = NULL;
gfloat matrix[16];
gint64 ts = 0;
GST_TRACE ("gl_sync %p result %p render (updated:%u)", sync, sync->result,
sync->result->updated);
if (sync->result->updated || !sync->result->rendered)
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
return;
/* FIXME: if this ever starts returning valid values we should attempt
* to use it */
2015-11-17 14:21:10 +00:00
if (!gst_amc_surface_texture_get_timestamp (sync->surface->texture, &ts,
&error)) {
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
GST_ERROR_OBJECT (sync->sink, "Failed to update texture image");
GST_ELEMENT_ERROR_FROM_ERROR (sync->sink, error);
goto out;
}
2015-11-17 14:21:10 +00:00
GST_TRACE ("gl_sync %p rendering timestamp before update %" G_GINT64_FORMAT,
sync, ts);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
GST_TRACE ("gl_sync %p update_tex_image", sync);
if (!gst_amc_surface_texture_update_tex_image (sync->surface->texture,
&error)) {
GST_ERROR_OBJECT (sync->sink, "Failed to update texture image");
GST_ELEMENT_ERROR_FROM_ERROR (sync->sink, error);
goto out;
}
GST_TRACE ("gl_sync result %p updated", sync->result);
sync->result->updated = TRUE;
sync->sink->gl_last_rendered_frame = sync->gl_frame_no;
2015-11-17 14:21:10 +00:00
if (!gst_amc_surface_texture_get_timestamp (sync->surface->texture, &ts,
&error)) {
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
GST_ERROR_OBJECT (sync->sink, "Failed to update texture image");
GST_ELEMENT_ERROR_FROM_ERROR (sync->sink, error);
goto out;
}
2015-11-17 14:21:10 +00:00
GST_TRACE ("gl_sync %p rendering timestamp after update %" G_GINT64_FORMAT,
sync, ts);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
af_meta = gst_buffer_get_video_affine_transformation_meta (sync->buffer);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
if (!af_meta) {
GST_WARNING ("Failed to retreive the transformation meta from the "
"gl_sync %p buffer %p", sync, sync->buffer);
} else if (gst_amc_surface_texture_get_transform_matrix (sync->surface->
texture, matrix, &error)) {
gfloat inv_mat[16];
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
/* The transform from mediacodec applies to the texture coords, but
* GStreamer affine meta applies to the video geometry, which is the
* opposite - so we invert it */
if (affine_inverse(matrix, inv_mat)) {
gst_video_affine_transformation_meta_apply_matrix (af_meta, inv_mat);
}
else {
GST_WARNING ("Failed to invert display transform - the video won't display right. "
"Transform matrix [ %f %f %f %f, %f %f %f %f, %f %f %f %f, %f %f %f %f ]",
matrix[0], matrix[1], matrix[2], matrix[3],
matrix[4], matrix[5], matrix[6], matrix[7],
matrix[8], matrix[9], matrix[10], matrix[11],
matrix[12], matrix[13], matrix[14], matrix[15]);
}
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
gst_video_affine_transformation_meta_apply_matrix (af_meta, yflip_matrix);
}
GST_LOG ("gl_sync %p successfully updated SurfaceTexture %p into "
"OES texture %u", sync, sync->surface->texture, sync->oes_mem->tex_id);
out:
if (error) {
if (sync->sink->gl_error == NULL)
sync->sink->gl_error = error;
else
g_clear_error (&error);
}
_gl_sync_release_next_buffer (sync, TRUE);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
}
static gboolean
_amc_gl_possibly_wait_for_gl_sync (struct gl_sync *sync, gint64 end_time)
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
{
GST_TRACE ("gl_sync %p waiting for frame %u current %u updated %u ", sync,
sync->gl_frame_no, sync->sink->gl_ready_frame_count,
sync->result->updated);
if ((gint) (sync->sink->gl_last_rendered_frame - sync->gl_frame_no) > 0) {
GST_ERROR ("gl_sync %p unsuccessfully waited for frame %u. out of order "
"wait detected", sync, sync->gl_frame_no);
return FALSE;
}
/* The number of frame callbacks (gl_ready_frame_count) is not a direct
* relationship with the number of pushed buffers (gl_pushed_frame_count)
* or even, the number of released buffers (gl_released_frame_count)
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
* as, from the frameworks/native/include/gui/ConsumerBase.h file,
*
* "...frames that are queued while in asynchronous mode only trigger the
* callback if no previous frames are pending."
*
* As a result, we need to advance the ready counter somehow ourselves when
* such events happen. There is no reliable way of knowing when/if the frame
* listener is going to fire. The only uniqueu identifier,
* SurfaceTexture::get_timestamp seems to always return 0.
*
* The maximum queue size as defined in
* frameworks/native/include/gui/BufferQueue.h
* is 32 of which a maximum of 30 can be acquired at a time so we picked a
* number less than that to wait for before updating the ready frame count.
*/
2015-11-17 14:21:10 +00:00
while (!sync->result->updated
&& (gint) (sync->sink->gl_ready_frame_count - sync->gl_frame_no) < 0) {
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
/* The time limit is need otherwise when amc decides to not emit the
* frame listener (say, on orientation changes) we don't wait foreever */
if (end_time == -1 || !g_cond_wait_until (&sync->sink->gl_cond,
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
&sync->sink->gl_lock, end_time)) {
GST_LOG ("gl_sync %p unsuccessfully waited for frame %u", sync,
sync->gl_frame_no);
return FALSE;
}
}
GST_LOG ("gl_sync %p successfully waited for frame %u", sync,
sync->gl_frame_no);
return TRUE;
}
static gboolean
_amc_gl_iterate_queue_unlocked (GstGLSyncMeta * sync_meta, gboolean wait)
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
{
struct gl_sync *sync = sync_meta->data;
struct gl_sync *tmp;
gboolean ret = TRUE;
gint64 end_time;
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
while ((tmp = g_queue_peek_head (sync->sink->gl_queue))) {
/* skip frames that are ahead of the current wait frame */
if ((gint) (sync->gl_frame_no - tmp->gl_frame_no) < 0) {
GST_TRACE ("gl_sync %p frame %u is ahead of gl_sync %p frame %u", tmp,
tmp->gl_frame_no, sync, sync->gl_frame_no);
break;
}
_gl_sync_release_buffer (tmp, wait);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
/* Frames are currently pushed in order and waits need to be performed
* in the same order */
end_time = wait ? 30 * G_TIME_SPAN_MILLISECOND + tmp->released_ts : -1;
if (!_amc_gl_possibly_wait_for_gl_sync (tmp, end_time))
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
ret = FALSE;
_gl_sync_render_unlocked (tmp);
g_queue_pop_head (tmp->sink->gl_queue);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
_gl_sync_unref (tmp);
}
return ret;
}
struct gl_wait
{
GstGLSyncMeta *sync_meta;
gboolean ret;
};
static void
2015-11-17 14:21:10 +00:00
_amc_gl_wait_gl (GstGLContext * context, struct gl_wait *wait)
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
{
struct gl_sync *sync = wait->sync_meta->data;
g_mutex_lock (&sync->sink->gl_lock);
wait->ret = _amc_gl_iterate_queue_unlocked (wait->sync_meta, TRUE);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
g_mutex_unlock (&sync->sink->gl_lock);
}
static void
_amc_gl_wait (GstGLSyncMeta * sync_meta, GstGLContext * context)
{
struct gl_sync *sync = sync_meta->data;
struct gl_wait wait;
wait.sync_meta = sync_meta;
wait.ret = FALSE;
gst_gl_context_thread_add (context,
(GstGLContextThreadFunc) _amc_gl_wait_gl, &wait);
if (!wait.ret)
GST_WARNING ("gl_sync %p could not wait for frame, took too long", sync);
}
static void
_amc_gl_copy (GstGLSyncMeta * src, GstBuffer * sbuffer, GstGLSyncMeta * dest,
GstBuffer * dbuffer)
{
struct gl_sync *sync = src->data;
struct gl_sync *tmp;
tmp = g_new0 (struct gl_sync, 1);
GST_TRACE ("copying gl_sync %p to %p", sync, tmp);
g_mutex_lock (&sync->sink->gl_lock);
tmp->refcount = 1;
tmp->sink = sync->sink;
tmp->buffer = dbuffer;
tmp->oes_mem = (GstGLMemory *) gst_memory_ref ((GstMemory *) sync->oes_mem);
tmp->surface = g_object_ref (sync->surface);
tmp->gl_frame_no = sync->gl_frame_no;
tmp->released_ts = sync->released_ts;
tmp->result = sync->result;
_gl_sync_result_ref (tmp->result);
dest->data = tmp;
g_mutex_unlock (&sync->sink->gl_lock);
}
static void
_amc_gl_render_on_free (GstGLContext * context, GstGLSyncMeta * sync_meta)
{
struct gl_sync *sync = sync_meta->data;
g_mutex_lock (&sync->sink->gl_lock);
/* just render as many frames as we have */
_amc_gl_iterate_queue_unlocked (sync_meta, FALSE);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
g_mutex_unlock (&sync->sink->gl_lock);
}
static void
2015-11-17 14:21:10 +00:00
_amc_gl_free (GstGLSyncMeta * sync_meta, GstGLContext * context)
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
{
struct gl_sync *sync = sync_meta->data;
/* The wait render queue inside android is not very deep so when we drop
* frames we need to signal that we have rendered them if we have any chance
* of keeping up between the decoder, the android GL queue and downstream
* OpenGL. If we don't do this, once we start dropping frames downstream,
* it is very near to impossible for the pipeline to catch up. */
gst_gl_context_thread_add (context,
(GstGLContextThreadFunc) _amc_gl_render_on_free, sync_meta);
_gl_sync_unref (sync);
}
2012-08-20 11:45:53 +00:00
static void
gst_amc_video_dec_loop (GstAmcVideoDec * self)
{
GstVideoCodecFrame *frame;
GstFlowReturn flow_ret = GST_FLOW_OK;
GstClockTimeDiff deadline;
gboolean is_eos;
GstAmcBuffer *buf;
2012-08-20 11:45:53 +00:00
GstAmcBufferInfo buffer_info;
gint idx;
GError *err = NULL;
gboolean release_buffer = TRUE;
2012-08-20 11:45:53 +00:00
GST_VIDEO_DECODER_STREAM_LOCK (self);
retry:
/*if (self->input_state_changed) {
idx = INFO_OUTPUT_FORMAT_CHANGED;
} else { */
2012-09-11 11:08:48 +00:00
GST_DEBUG_OBJECT (self, "Waiting for available output buffer");
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
/* Wait at most 100ms here, some codecs don't fail dequeueing if
* the codec is flushing, causing deadlocks during shutdown */
idx =
gst_amc_codec_dequeue_output_buffer (self->codec, &buffer_info, 100000,
&err);
GST_VIDEO_DECODER_STREAM_LOCK (self);
/*} */
2012-08-20 11:45:53 +00:00
GST_DEBUG_OBJECT (self, "dequeueOutputBuffer() returned %d (0x%x)", idx, idx);
2012-08-20 11:45:53 +00:00
if (idx < 0) {
if (self->flushing) {
g_clear_error (&err);
2012-09-07 12:22:54 +00:00
goto flushing;
}
2012-09-07 12:22:54 +00:00
2012-08-20 11:45:53 +00:00
switch (idx) {
case INFO_OUTPUT_BUFFERS_CHANGED:
/* Handled internally */
g_assert_not_reached ();
2012-08-20 11:45:53 +00:00
break;
case INFO_OUTPUT_FORMAT_CHANGED:{
GstAmcFormat *format;
gchar *format_string;
2012-08-20 11:45:53 +00:00
GST_DEBUG_OBJECT (self, "Output format has changed");
format = gst_amc_codec_get_output_format (self->codec, &err);
2012-08-20 11:45:53 +00:00
if (!format)
goto format_error;
format_string = gst_amc_format_to_string (format, &err);
if (!format) {
gst_amc_format_free (format);
goto format_error;
}
GST_DEBUG_OBJECT (self, "Got new output format: %s", format_string);
g_free (format_string);
2012-08-20 11:45:53 +00:00
if (!gst_amc_video_dec_set_src_caps (self, format)) {
gst_amc_format_free (format);
goto format_error;
}
gst_amc_format_free (format);
goto retry;
}
case INFO_TRY_AGAIN_LATER:
GST_DEBUG_OBJECT (self, "Dequeueing output buffer timed out");
2012-09-07 12:22:54 +00:00
goto retry;
2012-08-20 11:45:53 +00:00
case G_MININT:
GST_ERROR_OBJECT (self, "Failure dequeueing output buffer");
2012-08-20 11:45:53 +00:00
goto dequeue_error;
default:
g_assert_not_reached ();
break;
}
goto retry;
2012-08-20 11:45:53 +00:00
}
2012-09-10 13:31:04 +00:00
GST_DEBUG_OBJECT (self,
"Got output buffer at index %d: offset %d size %d time %" G_GINT64_FORMAT
" flags 0x%08x", idx, buffer_info.offset, buffer_info.size,
buffer_info.presentation_time_us, buffer_info.flags);
2012-08-20 11:45:53 +00:00
buf = gst_amc_codec_get_output_buffer (self->codec, idx, &err);
if (err) {
if (self->flushing) {
g_clear_error (&err);
goto flushing;
}
goto failed_to_get_output_buffer;
}
if (self->codec_config != AMC_CODEC_CONFIG_WITH_SURFACE && !buf)
goto got_null_output_buffer;
frame =
_find_nearest_frame (self,
gst_util_uint64_scale (buffer_info.presentation_time_us, GST_USECOND, 1));
is_eos = ! !(buffer_info.flags & BUFFER_FLAG_END_OF_STREAM);
2012-08-20 11:45:53 +00:00
if (frame
&& (deadline =
gst_video_decoder_get_max_decode_time (GST_VIDEO_DECODER (self),
frame)) < 0) {
GST_WARNING_OBJECT (self,
"Frame is too late, dropping (deadline %" GST_STIME_FORMAT ")",
GST_STIME_ARGS (deadline));
2012-08-20 11:45:53 +00:00
flow_ret = gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), frame);
} else if (frame && self->codec_config == AMC_CODEC_CONFIG_WITH_SURFACE) {
GstBuffer *outbuf;
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
GstGLSyncMeta *sync_meta;
GstVideoCodecState *state;
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
struct gl_sync *sync;
gboolean first_buffer = FALSE;
g_mutex_lock (&self->gl_lock);
if (self->gl_error) {
GST_ELEMENT_ERROR_FROM_ERROR (self, self->gl_error);
g_mutex_unlock (&self->gl_lock);
goto gl_output_error;
}
g_mutex_unlock (&self->gl_lock);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
outbuf = gst_buffer_new ();
state = gst_video_decoder_get_output_state (GST_VIDEO_DECODER (self));
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
if (!self->oes_mem) {
GstGLBaseMemoryAllocator *base_mem_alloc;
GstGLVideoAllocationParams *params;
base_mem_alloc =
GST_GL_BASE_MEMORY_ALLOCATOR (gst_allocator_find
(GST_GL_MEMORY_ALLOCATOR_NAME));
params = gst_gl_video_allocation_params_new (self->gl_context, NULL,
&state->info, 0, NULL, GST_GL_TEXTURE_TARGET_EXTERNAL_OES,
GST_GL_RGBA);
self->oes_mem = (GstGLMemory *) gst_gl_base_memory_alloc (base_mem_alloc,
(GstGLAllocationParams *) params);
gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
gst_object_unref (base_mem_alloc);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
gst_gl_context_thread_add (self->gl_context,
(GstGLContextThreadFunc) _attach_mem_to_context, self);
first_buffer = TRUE;
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
}
gst_video_codec_state_unref (state);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
gst_buffer_append_memory (outbuf,
gst_memory_ref ((GstMemory *) self->oes_mem));
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
sync = g_new0 (struct gl_sync, 1);
sync->refcount = 1;
sync->sink = self;
sync->buffer = outbuf;
sync->surface = g_object_ref (self->surface);
sync->oes_mem =
(GstGLMemory *) gst_memory_ref ((GstMemory *) self->oes_mem);
sync->buffer_idx = idx;
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
sync->result = g_new0 (struct gl_sync_result, 1);
sync->result->refcount = 1;
sync->result->updated = FALSE;
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
GST_TRACE ("new gl_sync %p result %p", sync, sync->result);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
sync_meta = gst_buffer_add_gl_sync_meta_full (self->gl_context, outbuf,
sync);
sync_meta->set_sync = _amc_gl_set_sync;
sync_meta->wait = _amc_gl_wait;
sync_meta->wait_cpu = _amc_gl_wait;
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
sync_meta->copy = _amc_gl_copy;
sync_meta->free = _amc_gl_free;
/* The meta needs to be created now:
* Later (in _gl_sync_render_unlocked) the buffer will be locked.
*/
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
gst_buffer_add_video_affine_transformation_meta (outbuf);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
g_mutex_lock (&self->gl_lock);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
self->gl_pushed_frame_count++;
sync->gl_frame_no = self->gl_pushed_frame_count;
g_queue_push_tail (self->gl_queue, _gl_sync_ref (sync));
if (first_buffer) {
_gl_sync_release_buffer (sync, TRUE);
if (self->gl_error) {
gst_buffer_unref (outbuf);
g_mutex_unlock (&self->gl_lock);
goto gl_output_error;
}
}
g_mutex_unlock (&self->gl_lock);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
GST_DEBUG_OBJECT (self, "push GL frame %u", sync->gl_frame_no);
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
frame->output_buffer = outbuf;
flow_ret = gst_video_decoder_finish_frame (GST_VIDEO_DECODER (self), frame);
release_buffer = FALSE;
} else if (self->codec_config == AMC_CODEC_CONFIG_WITHOUT_SURFACE && !frame
&& buffer_info.size > 0) {
2012-08-20 11:45:53 +00:00
GstBuffer *outbuf;
/* This sometimes happens at EOS or if the input is not properly framed,
* let's handle it gracefully by allocating a new buffer for the current
* caps and filling it
*/
GST_ERROR_OBJECT (self, "No corresponding frame found");
2012-10-18 14:41:07 +00:00
outbuf =
gst_video_decoder_allocate_output_buffer (GST_VIDEO_DECODER (self));
2012-08-20 11:45:53 +00:00
if (!gst_amc_video_dec_fill_buffer (self, buf, &buffer_info, outbuf)) {
2012-08-20 11:45:53 +00:00
gst_buffer_unref (outbuf);
if (!gst_amc_codec_release_output_buffer (self->codec, idx, FALSE, &err))
2012-08-20 11:45:53 +00:00
GST_ERROR_OBJECT (self, "Failed to release output buffer index %d",
idx);
if (err && !self->flushing)
GST_ELEMENT_WARNING_FROM_ERROR (self, err);
g_clear_error (&err);
gst_amc_buffer_free (buf);
buf = NULL;
2012-08-20 11:45:53 +00:00
goto invalid_buffer;
}
2012-10-18 14:41:07 +00:00
GST_BUFFER_PTS (outbuf) =
2012-08-20 11:45:53 +00:00
gst_util_uint64_scale (buffer_info.presentation_time_us, GST_USECOND,
1);
2012-08-20 11:45:53 +00:00
flow_ret = gst_pad_push (GST_VIDEO_DECODER_SRC_PAD (self), outbuf);
} else if (self->codec_config == AMC_CODEC_CONFIG_WITHOUT_SURFACE && frame
&& buffer_info.size > 0) {
if ((flow_ret =
gst_video_decoder_allocate_output_frame (GST_VIDEO_DECODER (self),
frame)) != GST_FLOW_OK) {
2012-08-20 11:45:53 +00:00
GST_ERROR_OBJECT (self, "Failed to allocate buffer");
if (!gst_amc_codec_release_output_buffer (self->codec, idx, FALSE, &err))
GST_ERROR_OBJECT (self, "Failed to release output buffer index %d",
idx);
if (err && !self->flushing)
GST_ELEMENT_WARNING_FROM_ERROR (self, err);
g_clear_error (&err);
gst_amc_buffer_free (buf);
buf = NULL;
goto flow_error;
2012-08-20 11:45:53 +00:00
}
if (!gst_amc_video_dec_fill_buffer (self, buf, &buffer_info,
2012-08-20 11:45:53 +00:00
frame->output_buffer)) {
gst_buffer_replace (&frame->output_buffer, NULL);
gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), frame);
if (!gst_amc_codec_release_output_buffer (self->codec, idx, FALSE, &err))
2012-08-20 11:45:53 +00:00
GST_ERROR_OBJECT (self, "Failed to release output buffer index %d",
idx);
if (err && !self->flushing)
GST_ELEMENT_WARNING_FROM_ERROR (self, err);
g_clear_error (&err);
gst_amc_buffer_free (buf);
buf = NULL;
2012-08-20 11:45:53 +00:00
goto invalid_buffer;
}
flow_ret = gst_video_decoder_finish_frame (GST_VIDEO_DECODER (self), frame);
} else if (frame != NULL) {
flow_ret = gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), frame);
}
if (buf) {
gst_amc_buffer_free (buf);
buf = NULL;
}
if (release_buffer) {
if (!gst_amc_codec_release_output_buffer (self->codec, idx, FALSE, &err)) {
if (self->flushing) {
g_clear_error (&err);
goto flushing;
}
goto failed_release;
}
}
2012-09-14 14:11:33 +00:00
2012-10-18 14:41:07 +00:00
if (is_eos || flow_ret == GST_FLOW_EOS) {
2012-08-20 11:45:53 +00:00
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
g_mutex_lock (&self->drain_lock);
2012-08-20 11:45:53 +00:00
if (self->draining) {
GST_DEBUG_OBJECT (self, "Drained");
self->draining = FALSE;
g_cond_broadcast (&self->drain_cond);
2012-08-20 11:45:53 +00:00
} else if (flow_ret == GST_FLOW_OK) {
GST_DEBUG_OBJECT (self, "Component signalled EOS");
2012-10-18 14:41:07 +00:00
flow_ret = GST_FLOW_EOS;
2012-08-20 11:45:53 +00:00
}
g_mutex_unlock (&self->drain_lock);
2012-08-20 11:45:53 +00:00
GST_VIDEO_DECODER_STREAM_LOCK (self);
} else {
GST_DEBUG_OBJECT (self, "Finished frame: %s", gst_flow_get_name (flow_ret));
}
self->downstream_flow_ret = flow_ret;
if (flow_ret != GST_FLOW_OK)
goto flow_error;
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
return;
dequeue_error:
{
GST_ELEMENT_ERROR_FROM_ERROR (self, err);
2012-08-20 11:45:53 +00:00
gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self), gst_event_new_eos ());
gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self));
self->downstream_flow_ret = GST_FLOW_ERROR;
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
g_mutex_lock (&self->drain_lock);
self->draining = FALSE;
g_cond_broadcast (&self->drain_cond);
g_mutex_unlock (&self->drain_lock);
2012-08-20 11:45:53 +00:00
return;
}
format_error:
{
if (err)
GST_ELEMENT_ERROR_FROM_ERROR (self, err);
else
GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL),
("Failed to handle format"));
2012-08-20 11:45:53 +00:00
gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self), gst_event_new_eos ());
gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self));
self->downstream_flow_ret = GST_FLOW_ERROR;
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
g_mutex_lock (&self->drain_lock);
self->draining = FALSE;
g_cond_broadcast (&self->drain_cond);
g_mutex_unlock (&self->drain_lock);
2012-08-20 11:45:53 +00:00
return;
}
failed_release:
{
GST_VIDEO_DECODER_ERROR_FROM_ERROR (self, err);
2012-08-20 11:45:53 +00:00
gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self), gst_event_new_eos ());
gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self));
self->downstream_flow_ret = GST_FLOW_ERROR;
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
g_mutex_lock (&self->drain_lock);
self->draining = FALSE;
g_cond_broadcast (&self->drain_cond);
g_mutex_unlock (&self->drain_lock);
2012-08-20 11:45:53 +00:00
return;
}
flushing:
{
GST_DEBUG_OBJECT (self, "Flushing -- stopping task");
gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self));
2012-10-18 14:41:07 +00:00
self->downstream_flow_ret = GST_FLOW_FLUSHING;
2012-08-20 11:45:53 +00:00
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
return;
}
flow_error:
{
2012-10-18 14:41:07 +00:00
if (flow_ret == GST_FLOW_EOS) {
2012-08-20 11:45:53 +00:00
GST_DEBUG_OBJECT (self, "EOS");
gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self),
gst_event_new_eos ());
gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self));
} else if (flow_ret < GST_FLOW_EOS) {
GST_ELEMENT_FLOW_ERROR (self, flow_ret);
2012-08-20 11:45:53 +00:00
gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self),
gst_event_new_eos ());
gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self));
} else if (flow_ret == GST_FLOW_FLUSHING) {
GST_DEBUG_OBJECT (self, "Flushing -- stopping task");
gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self));
2012-08-20 11:45:53 +00:00
}
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
g_mutex_lock (&self->drain_lock);
self->draining = FALSE;
g_cond_broadcast (&self->drain_cond);
g_mutex_unlock (&self->drain_lock);
2012-08-20 11:45:53 +00:00
return;
}
failed_to_get_output_buffer:
{
GST_VIDEO_DECODER_ERROR_FROM_ERROR (self, err);
gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self), gst_event_new_eos ());
gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self));
self->downstream_flow_ret = GST_FLOW_ERROR;
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
g_mutex_lock (&self->drain_lock);
self->draining = FALSE;
g_cond_broadcast (&self->drain_cond);
g_mutex_unlock (&self->drain_lock);
return;
}
got_null_output_buffer:
{
GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL),
("Got no output buffer"));
gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self), gst_event_new_eos ());
gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self));
self->downstream_flow_ret = GST_FLOW_ERROR;
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
g_mutex_lock (&self->drain_lock);
self->draining = FALSE;
g_cond_broadcast (&self->drain_cond);
g_mutex_unlock (&self->drain_lock);
return;
}
2012-08-20 11:45:53 +00:00
invalid_buffer:
{
GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL),
("Invalid sized input buffer"));
gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self), gst_event_new_eos ());
gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self));
self->downstream_flow_ret = GST_FLOW_NOT_NEGOTIATED;
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
g_mutex_lock (&self->drain_lock);
self->draining = FALSE;
g_cond_broadcast (&self->drain_cond);
g_mutex_unlock (&self->drain_lock);
2012-08-20 11:45:53 +00:00
return;
}
gl_output_error:
{
if (buf) {
gst_amc_buffer_free (buf);
buf = NULL;
}
gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self), gst_event_new_eos ());
gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self));
self->downstream_flow_ret = GST_FLOW_NOT_NEGOTIATED;
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
g_mutex_lock (&self->drain_lock);
self->draining = FALSE;
g_cond_broadcast (&self->drain_cond);
g_mutex_unlock (&self->drain_lock);
return;
}
2012-08-20 11:45:53 +00:00
}
static gboolean
gst_amc_video_dec_start (GstVideoDecoder * decoder)
{
GstAmcVideoDec *self;
self = GST_AMC_VIDEO_DEC (decoder);
self->last_upstream_ts = 0;
self->drained = TRUE;
2012-08-20 11:45:53 +00:00
self->downstream_flow_ret = GST_FLOW_OK;
self->started = FALSE;
self->flushing = TRUE;
return TRUE;
}
static gboolean
gst_amc_video_dec_stop (GstVideoDecoder * decoder)
{
GstAmcVideoDec *self;
GError *err = NULL;
2012-08-20 11:45:53 +00:00
self = GST_AMC_VIDEO_DEC (decoder);
GST_DEBUG_OBJECT (self, "Stopping decoder");
self->flushing = TRUE;
2012-08-20 11:45:53 +00:00
if (self->started) {
gst_amc_codec_flush (self->codec, &err);
if (err)
GST_ELEMENT_WARNING_FROM_ERROR (self, err);
gst_amc_codec_stop (self->codec, &err);
if (err)
GST_ELEMENT_WARNING_FROM_ERROR (self, err);
2012-08-20 11:45:53 +00:00
self->started = FALSE;
}
gst_pad_stop_task (GST_VIDEO_DECODER_SRC_PAD (decoder));
2012-08-20 11:45:53 +00:00
2012-10-18 14:41:07 +00:00
self->downstream_flow_ret = GST_FLOW_FLUSHING;
self->drained = TRUE;
g_mutex_lock (&self->drain_lock);
2012-08-20 11:45:53 +00:00
self->draining = FALSE;
g_cond_broadcast (&self->drain_cond);
g_mutex_unlock (&self->drain_lock);
2012-10-18 14:41:07 +00:00
g_free (self->codec_data);
self->codec_data_size = 0;
if (self->input_state)
gst_video_codec_state_unref (self->input_state);
self->input_state = NULL;
2012-08-20 11:45:53 +00:00
GST_DEBUG_OBJECT (self, "Stopped decoder");
return TRUE;
}
static jobject
gst_amc_video_dec_new_on_frame_available_listener (GstAmcVideoDec * decoder,
JNIEnv * env, GError ** err)
{
jobject listener = NULL;
jclass listener_cls = NULL;
jmethodID constructor_id = 0;
JNINativeMethod amcOnFrameAvailableListener = {
"native_onFrameAvailable",
"(JLandroid/graphics/SurfaceTexture;)V",
(void *) gst_amc_video_dec_on_frame_available,
};
listener_cls =
gst_amc_jni_get_application_class (env,
"org/freedesktop/gstreamer/androidmedia/GstAmcOnFrameAvailableListener",
err);
if (!listener_cls) {
return FALSE;
}
(*env)->RegisterNatives (env, listener_cls, &amcOnFrameAvailableListener, 1);
if ((*env)->ExceptionCheck (env)) {
(*env)->ExceptionClear (env);
goto done;
}
constructor_id =
gst_amc_jni_get_method_id (env, err, listener_cls, "<init>", "()V");
if (!constructor_id) {
goto done;
}
decoder->set_context_id =
gst_amc_jni_get_method_id (env, err, listener_cls, "setContext", "(J)V");
if (!decoder->set_context_id) {
goto done;
}
listener =
gst_amc_jni_new_object (env, err, TRUE, listener_cls, constructor_id);
if (!listener) {
goto done;
}
if (!gst_amc_jni_call_void_method (env, err, listener,
decoder->set_context_id, GST_AMC_VIDEO_DEC_TO_JLONG (decoder))) {
gst_amc_jni_object_unref (env, listener);
listener = NULL;
}
done:
gst_amc_jni_object_unref (env, listener_cls);
return listener;
}
2012-08-20 11:45:53 +00:00
static gboolean
gst_amc_video_dec_set_format (GstVideoDecoder * decoder,
GstVideoCodecState * state)
{
GstAmcVideoDec *self;
GstAmcVideoDecClass *klass;
2012-08-20 11:45:53 +00:00
GstAmcFormat *format;
const gchar *mime;
gboolean is_format_change = FALSE;
gboolean needs_disable = FALSE;
gchar *format_string;
2012-10-18 14:41:07 +00:00
guint8 *codec_data = NULL;
gsize codec_data_size = 0;
GError *err = NULL;
jobject jsurface = NULL;
2012-08-20 11:45:53 +00:00
self = GST_AMC_VIDEO_DEC (decoder);
klass = GST_AMC_VIDEO_DEC_GET_CLASS (self);
2012-08-20 11:45:53 +00:00
GST_DEBUG_OBJECT (self, "Setting new caps %" GST_PTR_FORMAT, state->caps);
/* Check if the caps change is a real format change or if only irrelevant
* parts of the caps have changed or nothing at all.
*/
is_format_change |= self->color_format_info.width != state->info.width;
is_format_change |= self->color_format_info.height != state->info.height;
2012-10-18 14:41:07 +00:00
if (state->codec_data) {
GstMapInfo cminfo;
gst_buffer_map (state->codec_data, &cminfo, GST_MAP_READ);
codec_data = g_memdup (cminfo.data, cminfo.size);
codec_data_size = cminfo.size;
is_format_change |= (!self->codec_data
|| self->codec_data_size != codec_data_size
|| memcmp (self->codec_data, codec_data, codec_data_size) != 0);
gst_buffer_unmap (state->codec_data, &cminfo);
} else if (self->codec_data) {
is_format_change |= TRUE;
}
2012-08-20 11:45:53 +00:00
needs_disable = self->started;
/* If the component is not started and a real format change happens
* we have to restart the component. If no real format change
* happened we can just exit here.
*/
if (needs_disable && !is_format_change) {
2012-10-18 14:41:07 +00:00
g_free (codec_data);
codec_data = NULL;
codec_data_size = 0;
2012-08-20 11:45:53 +00:00
/* Framerate or something minor changed */
self->input_state_changed = TRUE;
if (self->input_state)
gst_video_codec_state_unref (self->input_state);
self->input_state = gst_video_codec_state_ref (state);
2012-08-20 11:45:53 +00:00
GST_DEBUG_OBJECT (self,
"Already running and caps did not change the format");
return TRUE;
}
if (needs_disable && is_format_change) {
gst_amc_video_dec_drain (self);
2012-08-20 11:45:53 +00:00
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
gst_amc_video_dec_stop (GST_VIDEO_DECODER (self));
GST_VIDEO_DECODER_STREAM_LOCK (self);
gst_amc_video_dec_close (GST_VIDEO_DECODER (self));
if (!gst_amc_video_dec_open (GST_VIDEO_DECODER (self))) {
GST_ERROR_OBJECT (self, "Failed to open codec again");
return FALSE;
}
if (!gst_amc_video_dec_start (GST_VIDEO_DECODER (self))) {
GST_ERROR_OBJECT (self, "Failed to start codec again");
}
2012-08-20 11:45:53 +00:00
}
/* srcpad task is not running at this point */
if (self->input_state)
gst_video_codec_state_unref (self->input_state);
self->input_state = NULL;
2012-08-20 11:45:53 +00:00
2012-10-18 14:41:07 +00:00
g_free (self->codec_data);
self->codec_data = codec_data;
self->codec_data_size = codec_data_size;
2012-08-20 11:45:53 +00:00
mime = caps_to_mime (state->caps);
if (!mime) {
GST_ERROR_OBJECT (self, "Failed to convert caps to mime");
return FALSE;
}
format =
gst_amc_format_new_video (mime, state->info.width, state->info.height,
&err);
2012-08-20 11:45:53 +00:00
if (!format) {
GST_ERROR_OBJECT (self, "Failed to create video format");
GST_ELEMENT_ERROR_FROM_ERROR (self, err);
2012-08-20 11:45:53 +00:00
return FALSE;
}
2012-08-20 12:13:30 +00:00
/* FIXME: This buffer needs to be valid until the codec is stopped again */
if (self->codec_data) {
2012-10-18 14:41:07 +00:00
gst_amc_format_set_buffer (format, "csd-0", self->codec_data,
self->codec_data_size, &err);
if (err)
GST_ELEMENT_WARNING_FROM_ERROR (self, err);
}
2012-08-20 11:45:53 +00:00
{
gboolean downstream_supports_gl = FALSE;
GstVideoDecoder *decoder = GST_VIDEO_DECODER (self);
GstPad *src_pad = GST_VIDEO_DECODER_SRC_PAD (decoder);
GstCaps *templ_caps = gst_pad_get_pad_template_caps (src_pad);
GstCaps *downstream_caps = gst_pad_peer_query_caps (src_pad, templ_caps);
gst_caps_unref (templ_caps);
if (downstream_caps) {
guint i, n;
GstStaticCaps static_caps =
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
(GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA"));
GstCaps *gl_memory_caps = gst_static_caps_get (&static_caps);
GST_DEBUG_OBJECT (self, "Available downstream caps: %" GST_PTR_FORMAT,
downstream_caps);
/* Check if downstream caps supports
* video/x-raw(memory:GLMemory),format=RGBA */
n = gst_caps_get_size (downstream_caps);
for (i = 0; i < n; i++) {
GstCaps *caps = NULL;
GstStructure *structure = gst_caps_get_structure (downstream_caps, i);
GstCapsFeatures *features = gst_caps_get_features (downstream_caps, i);
caps = gst_caps_new_full (gst_structure_copy (structure), NULL);
if (!caps)
continue;
gst_caps_set_features (caps, 0, gst_caps_features_copy (features));
if (gst_caps_can_intersect (caps, gl_memory_caps)) {
downstream_supports_gl = TRUE;
}
gst_caps_unref (caps);
if (downstream_supports_gl)
break;
}
gst_caps_unref (gl_memory_caps);
/* If video/x-raw(memory:GLMemory),format=RGBA is supported,
* update the video decoder output state accordingly and negotiate */
if (downstream_supports_gl) {
GstVideoCodecState *output_state = NULL;
GstVideoCodecState *prev_output_state = NULL;
prev_output_state = gst_video_decoder_get_output_state (decoder);
output_state =
gst_video_decoder_set_output_state (decoder, GST_VIDEO_FORMAT_RGBA,
state->info.width, state->info.height, state);
if (output_state->caps) {
gst_caps_unref (output_state->caps);
}
output_state->caps = gst_video_info_to_caps (&output_state->info);
gst_caps_set_features (output_state->caps, 0,
gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, NULL));
/* gst_amc_video_dec_decide_allocation will update
* self->downstream_supports_gl */
if (!gst_video_decoder_negotiate (decoder)) {
GST_ERROR_OBJECT (self, "Failed to negotiate");
/* Rollback output state changes */
if (prev_output_state) {
output_state->info = prev_output_state->info;
gst_caps_replace (&output_state->caps, prev_output_state->caps);
} else {
gst_video_info_init (&output_state->info);
gst_caps_replace (&output_state->caps, NULL);
}
}
if (prev_output_state) {
gst_video_codec_state_unref (prev_output_state);
}
}
gst_caps_unref (downstream_caps);
}
}
GST_INFO_OBJECT (self, "GL output: %s",
self->downstream_supports_gl ? "enabled" : "disabled");
if (klass->codec_info->gl_output_only && !self->downstream_supports_gl) {
GST_ERROR_OBJECT (self,
"Codec only supports GL output but downstream does not");
return FALSE;
}
if (self->downstream_supports_gl && self->surface) {
jsurface = self->surface->jobject;
} else if (self->downstream_supports_gl && !self->surface) {
int ret = TRUE;
JNIEnv *env = NULL;
GstAmcSurfaceTexture *surface_texture = NULL;
env = gst_amc_jni_get_env ();
surface_texture = gst_amc_surface_texture_new (&err);
if (!surface_texture) {
GST_ELEMENT_ERROR_FROM_ERROR (self, err);
return FALSE;
}
if (self->listener) {
if (!gst_amc_jni_call_void_method (env, &err, self->listener,
self->set_context_id, GST_AMC_VIDEO_DEC_TO_JLONG (NULL))) {
ret = FALSE;
goto done;
}
gst_amc_jni_object_unref (env, self->listener);
}
self->listener =
gst_amc_video_dec_new_on_frame_available_listener (self, env, &err);
if (!self->listener) {
ret = FALSE;
goto done;
}
if (!gst_amc_surface_texture_set_on_frame_available_listener
(surface_texture, self->listener, &err)) {
ret = FALSE;
goto done;
}
self->surface = gst_amc_surface_new (surface_texture, &err);
jsurface = self->surface->jobject;
done:
g_object_unref (surface_texture);
if (!ret) {
GST_ELEMENT_ERROR_FROM_ERROR (self, err);
return FALSE;
}
}
format_string = gst_amc_format_to_string (format, &err);
if (err)
GST_ELEMENT_WARNING_FROM_ERROR (self, err);
GST_DEBUG_OBJECT (self, "Configuring codec with format: %s",
GST_STR_NULL (format_string));
g_free (format_string);
if (!gst_amc_codec_configure (self->codec, format, jsurface, 0, &err)) {
2012-08-20 11:45:53 +00:00
GST_ERROR_OBJECT (self, "Failed to configure codec");
GST_ELEMENT_ERROR_FROM_ERROR (self, err);
2012-08-20 11:45:53 +00:00
return FALSE;
}
if (jsurface) {
self->codec_config = AMC_CODEC_CONFIG_WITH_SURFACE;
} else {
self->codec_config = AMC_CODEC_CONFIG_WITHOUT_SURFACE;
}
2012-08-20 11:45:53 +00:00
gst_amc_format_free (format);
if (!gst_amc_codec_start (self->codec, &err)) {
2012-08-20 11:45:53 +00:00
GST_ERROR_OBJECT (self, "Failed to start codec");
GST_ELEMENT_ERROR_FROM_ERROR (self, err);
2012-08-20 11:45:53 +00:00
return FALSE;
}
self->started = TRUE;
self->input_state = gst_video_codec_state_ref (state);
2012-08-20 11:45:53 +00:00
self->input_state_changed = TRUE;
/* Start the srcpad loop again */
self->flushing = FALSE;
self->downstream_flow_ret = GST_FLOW_OK;
gst_pad_start_task (GST_VIDEO_DECODER_SRC_PAD (self),
2012-10-18 14:41:07 +00:00
(GstTaskFunction) gst_amc_video_dec_loop, decoder, NULL);
2012-08-20 11:45:53 +00:00
return TRUE;
}
static gboolean
gst_amc_video_dec_flush (GstVideoDecoder * decoder)
2012-08-20 11:45:53 +00:00
{
GstAmcVideoDec *self;
GError *err = NULL;
2012-08-20 11:45:53 +00:00
self = GST_AMC_VIDEO_DEC (decoder);
GST_DEBUG_OBJECT (self, "Flushing decoder");
2012-08-20 11:45:53 +00:00
if (!self->started) {
GST_DEBUG_OBJECT (self, "Codec not started yet");
return TRUE;
}
self->flushing = TRUE;
/* Wait until the srcpad loop is finished,
* unlock GST_VIDEO_DECODER_STREAM_LOCK to prevent deadlocks
* caused by using this lock from inside the loop function */
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
GST_PAD_STREAM_LOCK (GST_VIDEO_DECODER_SRC_PAD (self));
GST_PAD_STREAM_UNLOCK (GST_VIDEO_DECODER_SRC_PAD (self));
GST_VIDEO_DECODER_STREAM_LOCK (self);
gst_amc_codec_flush (self->codec, &err);
if (err)
GST_ELEMENT_WARNING_FROM_ERROR (self, err);
self->flushing = FALSE;
2012-08-20 11:45:53 +00:00
/* Start the srcpad loop again */
self->last_upstream_ts = 0;
self->drained = TRUE;
2012-08-20 11:45:53 +00:00
self->downstream_flow_ret = GST_FLOW_OK;
gst_pad_start_task (GST_VIDEO_DECODER_SRC_PAD (self),
2012-10-18 14:41:07 +00:00
(GstTaskFunction) gst_amc_video_dec_loop, decoder, NULL);
2012-08-20 11:45:53 +00:00
GST_DEBUG_OBJECT (self, "Flushed decoder");
2012-08-20 11:45:53 +00:00
return TRUE;
}
static GstFlowReturn
gst_amc_video_dec_handle_frame (GstVideoDecoder * decoder,
GstVideoCodecFrame * frame)
{
GstAmcVideoDec *self;
gint idx;
GstAmcBuffer *buf;
GstAmcBufferInfo buffer_info;
guint offset = 0;
GstClockTime timestamp, duration, timestamp_offset = 0;
2012-10-18 14:41:07 +00:00
GstMapInfo minfo;
GError *err = NULL;
2012-10-18 14:41:07 +00:00
memset (&minfo, 0, sizeof (minfo));
2012-08-20 11:45:53 +00:00
self = GST_AMC_VIDEO_DEC (decoder);
GST_DEBUG_OBJECT (self, "Handling frame");
if (!self->started) {
GST_ERROR_OBJECT (self, "Codec not started yet");
gst_video_codec_frame_unref (frame);
return GST_FLOW_NOT_NEGOTIATED;
}
if (self->flushing)
goto flushing;
if (self->downstream_flow_ret != GST_FLOW_OK)
goto downstream_error;
timestamp = frame->pts;
duration = frame->duration;
2012-10-18 14:41:07 +00:00
gst_buffer_map (frame->input_buffer, &minfo, GST_MAP_READ);
while (offset < minfo.size) {
2012-08-20 11:45:53 +00:00
/* Make sure to release the base class stream lock, otherwise
* _loop() can't call _finish_frame() and we might block forever
* because no input buffers are released */
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
/* Wait at most 100ms here, some codecs don't fail dequeueing if
* the codec is flushing, causing deadlocks during shutdown */
idx = gst_amc_codec_dequeue_input_buffer (self->codec, 100000, &err);
2012-08-20 11:45:53 +00:00
GST_VIDEO_DECODER_STREAM_LOCK (self);
if (idx < 0) {
if (self->flushing || self->downstream_flow_ret == GST_FLOW_FLUSHING) {
g_clear_error (&err);
2012-09-07 12:22:54 +00:00
goto flushing;
}
2012-08-20 11:45:53 +00:00
switch (idx) {
case INFO_TRY_AGAIN_LATER:
GST_DEBUG_OBJECT (self, "Dequeueing input buffer timed out");
2012-09-07 12:22:54 +00:00
continue; /* next try */
2012-08-20 11:45:53 +00:00
break;
case G_MININT:
GST_ERROR_OBJECT (self, "Failed to dequeue input buffer");
goto dequeue_error;
default:
g_assert_not_reached ();
break;
}
continue;
2012-08-20 11:45:53 +00:00
}
if (self->flushing) {
memset (&buffer_info, 0, sizeof (buffer_info));
gst_amc_codec_queue_input_buffer (self->codec, idx, &buffer_info, NULL);
2012-08-20 11:45:53 +00:00
goto flushing;
}
2012-08-20 11:45:53 +00:00
if (self->downstream_flow_ret != GST_FLOW_OK) {
memset (&buffer_info, 0, sizeof (buffer_info));
gst_amc_codec_queue_input_buffer (self->codec, idx, &buffer_info, &err);
if (err && !self->flushing)
GST_ELEMENT_WARNING_FROM_ERROR (self, err);
g_clear_error (&err);
2012-08-20 11:45:53 +00:00
goto downstream_error;
}
/* Now handle the frame */
/* Copy the buffer content in chunks of size as requested
* by the port */
buf = gst_amc_codec_get_input_buffer (self->codec, idx, &err);
if (err)
goto failed_to_get_input_buffer;
else if (!buf)
goto got_null_input_buffer;
2012-08-20 11:45:53 +00:00
memset (&buffer_info, 0, sizeof (buffer_info));
buffer_info.offset = 0;
2012-10-18 14:41:07 +00:00
buffer_info.size = MIN (minfo.size - offset, buf->size);
gst_amc_buffer_set_position_and_limit (buf, NULL, buffer_info.offset,
buffer_info.size);
2012-10-18 14:41:07 +00:00
orc_memcpy (buf->data, minfo.data + offset, buffer_info.size);
2012-08-20 11:45:53 +00:00
gst_amc_buffer_free (buf);
buf = NULL;
2012-08-20 11:45:53 +00:00
/* Interpolate timestamps if we're passing the buffer
* in multiple chunks */
if (offset != 0 && duration != GST_CLOCK_TIME_NONE) {
2012-10-18 14:41:07 +00:00
timestamp_offset = gst_util_uint64_scale (offset, duration, minfo.size);
2012-08-20 11:45:53 +00:00
}
if (timestamp != GST_CLOCK_TIME_NONE) {
buffer_info.presentation_time_us =
gst_util_uint64_scale (timestamp + timestamp_offset, 1, GST_USECOND);
2012-08-20 11:45:53 +00:00
self->last_upstream_ts = timestamp + timestamp_offset;
}
if (duration != GST_CLOCK_TIME_NONE)
self->last_upstream_ts += duration;
if (offset == 0) {
BufferIdentification *id =
buffer_identification_new (timestamp + timestamp_offset);
if (GST_VIDEO_CODEC_FRAME_IS_SYNC_POINT (frame))
buffer_info.flags |= BUFFER_FLAG_SYNC_FRAME;
gst_video_codec_frame_set_user_data (frame, id,
(GDestroyNotify) buffer_identification_free);
}
offset += buffer_info.size;
2012-09-10 13:31:04 +00:00
GST_DEBUG_OBJECT (self,
"Queueing buffer %d: size %d time %" G_GINT64_FORMAT
" flags 0x%08x", idx, buffer_info.size,
buffer_info.presentation_time_us, buffer_info.flags);
if (!gst_amc_codec_queue_input_buffer (self->codec, idx, &buffer_info,
&err)) {
if (self->flushing) {
g_clear_error (&err);
goto flushing;
}
2012-08-20 11:45:53 +00:00
goto queue_error;
}
self->drained = FALSE;
2012-08-20 11:45:53 +00:00
}
2012-10-18 14:41:07 +00:00
gst_buffer_unmap (frame->input_buffer, &minfo);
2012-08-20 11:45:53 +00:00
gst_video_codec_frame_unref (frame);
return self->downstream_flow_ret;
downstream_error:
{
GST_ERROR_OBJECT (self, "Downstream returned %s",
gst_flow_get_name (self->downstream_flow_ret));
2012-10-18 14:41:07 +00:00
if (minfo.data)
gst_buffer_unmap (frame->input_buffer, &minfo);
2012-08-20 11:45:53 +00:00
gst_video_codec_frame_unref (frame);
return self->downstream_flow_ret;
}
failed_to_get_input_buffer:
2012-08-20 11:45:53 +00:00
{
GST_ELEMENT_ERROR_FROM_ERROR (self, err);
2012-10-18 14:41:07 +00:00
if (minfo.data)
gst_buffer_unmap (frame->input_buffer, &minfo);
2012-08-20 11:45:53 +00:00
gst_video_codec_frame_unref (frame);
return GST_FLOW_ERROR;
}
got_null_input_buffer:
{
GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL),
("Got no input buffer"));
if (minfo.data)
gst_buffer_unmap (frame->input_buffer, &minfo);
gst_video_codec_frame_unref (frame);
return GST_FLOW_ERROR;
}
2012-08-20 11:45:53 +00:00
dequeue_error:
{
GST_ELEMENT_ERROR_FROM_ERROR (self, err);
2012-10-18 14:41:07 +00:00
if (minfo.data)
gst_buffer_unmap (frame->input_buffer, &minfo);
2012-08-20 11:45:53 +00:00
gst_video_codec_frame_unref (frame);
return GST_FLOW_ERROR;
}
queue_error:
{
GST_VIDEO_DECODER_ERROR_FROM_ERROR (self, err);
2012-10-18 14:41:07 +00:00
if (minfo.data)
gst_buffer_unmap (frame->input_buffer, &minfo);
2012-08-20 11:45:53 +00:00
gst_video_codec_frame_unref (frame);
return GST_FLOW_ERROR;
}
flushing:
{
2012-10-18 14:41:07 +00:00
GST_DEBUG_OBJECT (self, "Flushing -- returning FLUSHING");
if (minfo.data)
gst_buffer_unmap (frame->input_buffer, &minfo);
2012-08-20 11:45:53 +00:00
gst_video_codec_frame_unref (frame);
2012-10-18 14:41:07 +00:00
return GST_FLOW_FLUSHING;
2012-08-20 11:45:53 +00:00
}
}
static GstFlowReturn
gst_amc_video_dec_finish (GstVideoDecoder * decoder)
{
GstAmcVideoDec *self;
self = GST_AMC_VIDEO_DEC (decoder);
return gst_amc_video_dec_drain (self);
2012-08-20 11:45:53 +00:00
}
static GstFlowReturn
gst_amc_video_dec_drain (GstAmcVideoDec * self)
2012-08-20 11:45:53 +00:00
{
GstFlowReturn ret;
gint idx;
GError *err = NULL;
2012-08-20 11:45:53 +00:00
GST_DEBUG_OBJECT (self, "Draining codec");
if (!self->started) {
GST_DEBUG_OBJECT (self, "Codec not started yet");
return GST_FLOW_OK;
}
/* Don't send drain buffer twice, this doesn't work */
if (self->drained) {
GST_DEBUG_OBJECT (self, "Codec is drained already");
2012-08-20 11:45:53 +00:00
return GST_FLOW_OK;
}
/* Make sure to release the base class stream lock, otherwise
* _loop() can't call _finish_frame() and we might block forever
* because no input buffers are released */
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
/* Send an EOS buffer to the component and let the base
* class drop the EOS event. We will send it later when
* the EOS buffer arrives on the output port.
* Wait at most 0.5s here. */
idx = gst_amc_codec_dequeue_input_buffer (self->codec, 500000, &err);
2012-08-20 11:45:53 +00:00
GST_VIDEO_DECODER_STREAM_LOCK (self);
if (idx >= 0) {
GstAmcBuffer *buf;
2012-08-20 11:45:53 +00:00
GstAmcBufferInfo buffer_info;
buf = gst_amc_codec_get_input_buffer (self->codec, idx, &err);
if (buf) {
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
g_mutex_lock (&self->drain_lock);
self->draining = TRUE;
2012-08-20 11:45:53 +00:00
memset (&buffer_info, 0, sizeof (buffer_info));
buffer_info.size = 0;
buffer_info.presentation_time_us =
gst_util_uint64_scale (self->last_upstream_ts, 1, GST_USECOND);
buffer_info.flags |= BUFFER_FLAG_END_OF_STREAM;
gst_amc_buffer_set_position_and_limit (buf, NULL, 0, 0);
gst_amc_buffer_free (buf);
buf = NULL;
if (gst_amc_codec_queue_input_buffer (self->codec, idx, &buffer_info,
&err)) {
GST_DEBUG_OBJECT (self, "Waiting until codec is drained");
g_cond_wait (&self->drain_cond, &self->drain_lock);
GST_DEBUG_OBJECT (self, "Drained codec");
ret = GST_FLOW_OK;
} else {
GST_ERROR_OBJECT (self, "Failed to queue input buffer");
if (self->flushing) {
g_clear_error (&err);
ret = GST_FLOW_FLUSHING;
} else {
GST_ELEMENT_WARNING_FROM_ERROR (self, err);
ret = GST_FLOW_ERROR;
}
}
2012-08-20 11:45:53 +00:00
self->drained = TRUE;
self->draining = FALSE;
g_mutex_unlock (&self->drain_lock);
GST_VIDEO_DECODER_STREAM_LOCK (self);
} else {
GST_ERROR_OBJECT (self, "Failed to get buffer for EOS: %d", idx);
if (err)
GST_ELEMENT_WARNING_FROM_ERROR (self, err);
ret = GST_FLOW_ERROR;
}
2012-08-20 11:45:53 +00:00
} else {
GST_ERROR_OBJECT (self, "Failed to acquire buffer for EOS: %d", idx);
if (err)
GST_ELEMENT_WARNING_FROM_ERROR (self, err);
2012-08-20 11:45:53 +00:00
ret = GST_FLOW_ERROR;
}
return ret;
}
2012-10-18 14:41:07 +00:00
static gboolean
gst_amc_video_dec_src_query (GstVideoDecoder * bdec, GstQuery * query)
{
2017-01-13 13:46:12 +00:00
GstAmcVideoDec *self = GST_AMC_VIDEO_DEC (bdec);
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CONTEXT:
{
if (gst_gl_handle_context_query ((GstElement *) self, query,
self->gl_display, self->gl_context, self->other_gl_context))
return TRUE;
break;
}
default:
break;
}
return GST_VIDEO_DECODER_CLASS (parent_class)->src_query (bdec, query);
}
static gboolean
_caps_are_rgba_with_gl_memory (GstCaps * caps)
{
GstVideoInfo info;
GstCapsFeatures *features;
if (!caps)
return FALSE;
if (!gst_video_info_from_caps (&info, caps))
return FALSE;
if (info.finfo->format != GST_VIDEO_FORMAT_RGBA)
return FALSE;
if (!(features = gst_caps_get_features (caps, 0)))
return FALSE;
return gst_caps_features_contains (features,
GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
}
static gboolean
_find_local_gl_context (GstAmcVideoDec * self)
{
if (gst_gl_query_local_gl_context (GST_ELEMENT (self), GST_PAD_SRC,
&self->gl_context))
return TRUE;
return FALSE;
}
2012-10-18 14:41:07 +00:00
static gboolean
gst_amc_video_dec_decide_allocation (GstVideoDecoder * bdec, GstQuery * query)
{
GstAmcVideoDec *self = GST_AMC_VIDEO_DEC (bdec);
gboolean need_pool = FALSE;
GstCaps *caps = NULL;
// GError *error = NULL;
2012-10-18 14:41:07 +00:00
if (!GST_VIDEO_DECODER_CLASS (parent_class)->decide_allocation (bdec, query))
return FALSE;
self->downstream_supports_gl = FALSE;
gst_query_parse_allocation (query, &caps, &need_pool);
if (_caps_are_rgba_with_gl_memory (caps)) {
if (!gst_gl_ensure_element_data (self, &self->gl_display,
&self->other_gl_context))
return FALSE;
if (!_find_local_gl_context (self))
goto out;
#if 0
if (!self->gl_context) {
GST_OBJECT_LOCK (self->gl_display);
do {
if (self->gl_context) {
gst_object_unref (self->gl_context);
self->gl_context = NULL;
}
/* just get a GL context. we don't care */
self->gl_context =
gst_gl_display_get_gl_context_for_thread (self->gl_display, NULL);
if (!self->gl_context) {
if (!gst_gl_display_create_context (self->gl_display,
self->other_gl_context, &self->gl_context, &error)) {
GST_OBJECT_UNLOCK (mix->display);
goto context_error;
}
}
} while (!gst_gl_display_add_context (self->gl_display,
self->gl_context));
GST_OBJECT_UNLOCK (self->gl_display);
}
#endif
self->downstream_supports_gl = TRUE;
2012-10-18 14:41:07 +00:00
}
out:
return gst_amc_video_dec_check_codec_config (self);
#if 0
context_error:
{
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND, ("%s", error->message),
(NULL));
g_clear_error (&error);
return FALSE;
}
#endif
}
static void
gst_amc_video_dec_on_frame_available (JNIEnv * env, jobject thiz,
long long context, jobject surfaceTexture)
{
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
GstAmcVideoDec *self = JLONG_TO_GST_AMC_VIDEO_DEC (context);
/* apparently we can be called after the decoder has been closed */
if (!self)
return;
amcviddec: output external-oes textures This provides a performance and power usage improvement by removing the texture copy from an OES texture to 2D texture. The flow is as follows 1. Generate the output buffer with the required sync meta with the incrementing push counter and OES GL memory 1.1 release_output_buffer (buf, render=true) and push downstream 2. Downstream waits for on the sync meta (timed wait) or drops the frame (no wait) 2.1 Timed wait for the frame number to reach the number of frame callbacks fired 2.2 Unconditionally update the image when the wait completes (success or fail). Sets the affine transformation matrix meta on the buffer. 3. Downstream renders as usual. At *some* point through this the on_frame_callback may or may not fire. If it does fire, we can finish waiting early and render. Otherwise we have to wait for a timeout to occur which may cause more buffers to be pused into the internal GL queue which siginificantly decreases the chances of the on_frame_callback to fire again. This is because the frame callback only occurs when the internal GL queue changes state from empty to non-empty. Because there is no way to reliably correlate between the number of buffers pushed and the number of frame callbacks received, there are a number of workarounds in place. 1. We self-increment the ready counter when it falls behind the push counter 2. Time based waits as the frame callback may not be fired for a certain frame. 3. It is assumed that the device can render at speed or performs some QoS of the interal GL queue (which may not match the GStreamer QoS). It holds that we call SurfaceTexture::updateTexImage for each buffer pushed downstream however there's no guarentee that updateTexImage will result in the exact next frame (it could skip or duplicate) so synchronization is not guaranteed to be accurate although it seems to be close enough to be unable to discern visually. This has not changed from before this patch. The current requirement for synchronization is that updateTexImage is called at the point in time when the buffers is to be rendered. https://bugzilla.gnome.org/show_bug.cgi?id=757285
2015-11-03 02:19:41 +00:00
g_mutex_lock (&self->gl_lock);
self->gl_ready_frame_count++;
GST_LOG_OBJECT (self, "frame %u available", self->gl_ready_frame_count);
g_cond_broadcast (&self->gl_cond);
g_mutex_unlock (&self->gl_lock);
2012-10-18 14:41:07 +00:00
}