[Cogl] [PATCH 3/4] cogl-gst: video-sink: rework color conversion to care about colorimetry
Lionel Landwerlin
llandwerlin at gmail.com
Fri Jan 10 14:45:29 PST 2014
---
cogl-gst/cogl-gst-video-sink.c | 350 +++++++++++++++++++++++++++++------------
1 file changed, 249 insertions(+), 101 deletions(-)
diff --git a/cogl-gst/cogl-gst-video-sink.c b/cogl-gst/cogl-gst-video-sink.c
index 40dbb02..3742c6e 100644
--- a/cogl-gst/cogl-gst-video-sink.c
+++ b/cogl-gst/cogl-gst-video-sink.c
@@ -172,6 +172,139 @@ struct _CoglGstVideoSinkPrivate
GstVideoInfo info;
};
+/* Snippet cache */
+
+static SnippetCacheEntry *
+get_layer_cache_entry (CoglGstVideoSink *sink,
+ SnippetCache *cache)
+{
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+ GList *l;
+
+ for (l = cache->entries.head; l; l = l->next)
+ {
+ SnippetCacheEntry *entry = l->data;
+
+ if (entry->start_position == priv->custom_start)
+ return entry;
+ }
+
+ return NULL;
+}
+
+static SnippetCacheEntry *
+add_layer_cache_entry (CoglGstVideoSink *sink,
+ SnippetCache *cache,
+ const char *decl)
+{
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+ SnippetCacheEntry *entry = g_slice_new (SnippetCacheEntry);
+ char *default_source;
+
+ entry->start_position = priv->custom_start;
+
+ entry->vertex_snippet =
+ cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX_GLOBALS,
+ decl,
+ NULL /* post */);
+ entry->fragment_snippet =
+ cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS,
+ decl,
+ NULL /* post */);
+
+ default_source =
+ g_strdup_printf (" cogl_layer *= cogl_gst_sample_video%i "
+ "(cogl_tex_coord%i_in.st);\n",
+ priv->custom_start,
+ priv->custom_start);
+ entry->default_sample_snippet =
+ cogl_snippet_new (COGL_SNIPPET_HOOK_LAYER_FRAGMENT,
+ NULL, /* declarations */
+ default_source);
+ g_free (default_source);
+
+ g_queue_push_head (&cache->entries, entry);
+
+ return entry;
+}
+
+static SnippetCacheEntry *
+get_global_cache_entry (SnippetCache *cache, int param)
+{
+ GList *l;
+
+ for (l = cache->entries.head; l; l = l->next)
+ {
+ SnippetCacheEntry *entry = l->data;
+
+ if (entry->start_position == param)
+ return entry;
+ }
+
+ return NULL;
+}
+
+static SnippetCacheEntry *
+add_global_cache_entry (SnippetCache *cache,
+ const char *decl,
+ int param)
+{
+ SnippetCacheEntry *entry = g_slice_new (SnippetCacheEntry);
+
+ entry->start_position = param;
+
+ entry->vertex_snippet =
+ cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX_GLOBALS,
+ decl,
+ NULL /* post */);
+ entry->fragment_snippet =
+ cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS,
+ decl,
+ NULL /* post */);
+
+ g_queue_push_head (&cache->entries, entry);
+
+ return entry;
+}
+
+static void
+setup_pipeline_from_cache_entry (CoglGstVideoSink *sink,
+ CoglPipeline *pipeline,
+ SnippetCacheEntry *cache_entry,
+ int n_layers)
+{
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+
+ if (cache_entry)
+ {
+ int i;
+
+ /* The global sampling function gets added to both the fragment
+ * and vertex stages. The hope is that the GLSL compiler will
+ * easily remove the dead code if it's not actually used */
+ cogl_pipeline_add_snippet (pipeline, cache_entry->vertex_snippet);
+ cogl_pipeline_add_snippet (pipeline, cache_entry->fragment_snippet);
+
+ /* Set all of the layers to just directly copy from the previous
+ * layer so that it won't redundantly generate code to sample
+ * the intermediate textures */
+ for (i = 0; i < n_layers; i++) {
+ cogl_pipeline_set_layer_combine (pipeline,
+ priv->custom_start + i,
+ "RGBA=REPLACE(PREVIOUS)",
+ NULL);
+ }
+
+ if (priv->default_sample) {
+ cogl_pipeline_add_layer_snippet (pipeline,
+ priv->custom_start + n_layers - 1,
+ cache_entry->default_sample_snippet);
+ }
+ }
+
+ priv->frame_dirty = TRUE;
+}
+
static void
cogl_gst_source_finalize (GSource *source)
{
@@ -204,6 +337,112 @@ cogl_gst_video_sink_attach_frame (CoglGstVideoSink *sink,
priv->frame[i]);
}
+/* YUV <-> RGB conversions */
+
+static const gchar *color_conversions_shaders =
+ "\n"
+ "/* These conversion functions take : */\n"
+ "/* Y = [0, 1] */\n"
+ "/* U = [-0.5, 0.5] */\n"
+ "/* V = [-0.5, 0.5] */\n"
+ "vec3\n"
+ "cogl_gst_yuv_bt601_to_srgb (vec3 yuv)\n"
+ "{\n"
+ " return mat3 (1.0, 1.0, 1.0,\n"
+ " 0.0, -0.344136, 1.772,\n"
+ " 1.402, -0.714136, 0.0 ) * yuv;\n"
+ "}\n"
+ "\n"
+ "vec3\n"
+ "cogl_gst_yuv_bt709_to_srgb (vec3 yuv)\n"
+ "{\n"
+ " return mat3 (1.0, 1.0, 1.0,\n"
+ " 0.0, -0.187324, 1.8556,\n"
+ " 1.5748, -0.468124, 0.0 ) * yuv;\n"
+ "}\n"
+ "\n"
+ "vec3\n"
+ "cogl_gst_yuv_bt2020_to_srgb (vec3 yuv)\n"
+ "{\n"
+ " return mat3 (1.0, 1.0, 1.0,\n"
+ " 0.0, 0.571353, 1.8814,\n"
+ " 1.4746, 0.164553, 0.0 ) * yuv;\n"
+ "}\n"
+ "/* Original transformation, still no idea where these values come from... */\n"
+ "vec3\n"
+ "cogl_gst_yuv_originalyuv_to_srgb (vec3 yuv)\n"
+ "{\n"
+ " return mat3 (1.0, 1.0, 1.0,\n"
+ " 0.0, -0.390625, 2.015625,\n"
+ " 1.59765625, -0.8125, 0.0 ) * yuv;\n"
+ "}\n"
+ "\n"
+ "vec3\n"
+ "cogl_gst_yuv_srgb_to_bt601 (vec3 rgb)\n"
+ "{\n"
+ " return mat3 (0.299, 0.5, -0.168736,\n"
+ " 0.587, -0.418688, -0.331264,\n"
+ " 0.114, -0.081312, 0.5 ) * rgb;\n"
+ "}\n"
+ "\n"
+ "vec3\n"
+ "cogl_gst_yuv_srgb_to_bt709 (vec3 rgb)\n"
+ "{\n"
+ " return mat3 (0.2126, -0.114626, 0.5,\n"
+ " 0.7152, -0.385428, -0.454153,\n"
+ " 0.0722, 0.5, 0.045847 ) * rgb;\n"
+ "}\n"
+ "\n"
+ "vec3\n"
+ "cogl_gst_yuv_srgb_to_bt2020 (vec3 rgb)\n"
+ "{\n"
+ " return mat3 (0.2627, -0.139630, 0.503380,\n"
+ " 0.6780, -0.360370, -0.462893,\n"
+ " 0.0593, 0.5, -0.040486 ) * rgb;\n"
+ "}\n"
+ "\n"
+ "#define cogl_gst_default_yuv_to_srgb(arg) cogl_gst_yuv_%s_to_srgb(arg)\n"
+ "\n";
+
+static const char *
+_gst_video_color_matrix_to_string (GstVideoColorMatrix matrix)
+{
+ switch (matrix)
+ {
+ case GST_VIDEO_COLOR_MATRIX_BT601:
+ return "bt601";
+ case GST_VIDEO_COLOR_MATRIX_BT709:
+ return "bt709";
+
+ default:
+ return "bt709";
+ }
+}
+
+static void
+cogl_gst_video_sink_setup_conversions (CoglGstVideoSink *sink,
+ CoglPipeline *pipeline)
+{
+ CoglGstVideoSinkPrivate *priv = sink->priv;
+ GstVideoColorMatrix matrix = priv->info.colorimetry.matrix;
+ static SnippetCache snippet_cache;
+ SnippetCacheEntry *entry = get_global_cache_entry (&snippet_cache, matrix);
+
+ if (entry == NULL)
+ {
+ char *source = g_strdup_printf (color_conversions_shaders,
+ _gst_video_color_matrix_to_string (matrix));
+
+ entry = add_global_cache_entry (&snippet_cache, source, matrix);
+ g_free (source);
+ }
+
+ cogl_pipeline_add_snippet (pipeline, entry->vertex_snippet);
+ cogl_pipeline_add_snippet (pipeline, entry->fragment_snippet);
+}
+
+/**/
+
static CoglBool
cogl_gst_source_prepare (GSource *source,
int *timeout)
@@ -280,97 +519,10 @@ cogl_gst_video_sink_setup_pipeline (CoglGstVideoSink *sink,
g_return_if_fail (COGL_GST_IS_VIDEO_SINK (sink));
if (sink->priv->renderer)
- sink->priv->renderer->setup_pipeline (sink, pipeline);
-}
-
-static SnippetCacheEntry *
-get_cache_entry (CoglGstVideoSink *sink,
- SnippetCache *cache)
-{
- CoglGstVideoSinkPrivate *priv = sink->priv;
- GList *l;
-
- for (l = cache->entries.head; l; l = l->next)
{
- SnippetCacheEntry *entry = l->data;
-
- if (entry->start_position == priv->custom_start)
- return entry;
+ cogl_gst_video_sink_setup_conversions (sink, pipeline);
+ sink->priv->renderer->setup_pipeline (sink, pipeline);
}
-
- return NULL;
-}
-
-static SnippetCacheEntry *
-add_cache_entry (CoglGstVideoSink *sink,
- SnippetCache *cache,
- const char *decl)
-{
- CoglGstVideoSinkPrivate *priv = sink->priv;
- SnippetCacheEntry *entry = g_slice_new (SnippetCacheEntry);
- char *default_source;
-
- entry->start_position = priv->custom_start;
-
- entry->vertex_snippet =
- cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX_GLOBALS,
- decl,
- NULL /* post */);
- entry->fragment_snippet =
- cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS,
- decl,
- NULL /* post */);
-
- default_source =
- g_strdup_printf (" cogl_layer *= cogl_gst_sample_video%i "
- "(cogl_tex_coord%i_in.st);\n",
- priv->custom_start,
- priv->custom_start);
- entry->default_sample_snippet =
- cogl_snippet_new (COGL_SNIPPET_HOOK_LAYER_FRAGMENT,
- NULL, /* declarations */
- default_source);
- g_free (default_source);
-
- g_queue_push_head (&cache->entries, entry);
-
- return entry;
-}
-
-static void
-setup_pipeline_from_cache_entry (CoglGstVideoSink *sink,
- CoglPipeline *pipeline,
- SnippetCacheEntry *cache_entry,
- int n_layers)
-{
- CoglGstVideoSinkPrivate *priv = sink->priv;
-
- if (cache_entry)
- {
- int i;
-
- /* The global sampling function gets added to both the fragment
- * and vertex stages. The hope is that the GLSL compiler will
- * easily remove the dead code if it's not actually used */
- cogl_pipeline_add_snippet (pipeline, cache_entry->vertex_snippet);
- cogl_pipeline_add_snippet (pipeline, cache_entry->fragment_snippet);
-
- /* Set all of the layers to just directly copy from the previous
- * layer so that it won't redundantly generate code to sample
- * the intermediate textures */
- for (i = 0; i < n_layers; i++)
- cogl_pipeline_set_layer_combine (pipeline,
- priv->custom_start + i,
- "RGBA=REPLACE(PREVIOUS)",
- NULL);
-
- if (priv->default_sample)
- cogl_pipeline_add_layer_snippet (pipeline,
- priv->custom_start + n_layers - 1,
- cache_entry->default_sample_snippet);
- }
-
- priv->frame_dirty = TRUE;
}
CoglPipeline *
@@ -430,7 +582,7 @@ cogl_gst_rgb_setup_pipeline (CoglGstVideoSink *sink,
if (cogl_has_feature (priv->ctx, COGL_FEATURE_ID_GLSL))
{
static SnippetCache snippet_cache;
- SnippetCacheEntry *entry = get_cache_entry (sink, &snippet_cache);
+ SnippetCacheEntry *entry = get_layer_cache_entry (sink, &snippet_cache);
if (entry == NULL)
{
@@ -445,7 +597,7 @@ cogl_gst_rgb_setup_pipeline (CoglGstVideoSink *sink,
priv->custom_start,
priv->custom_start);
- entry = add_cache_entry (sink, &snippet_cache, source);
+ entry = add_layer_cache_entry (sink, &snippet_cache, source);
g_free (source);
}
@@ -709,7 +861,7 @@ cogl_gst_yv12_glsl_setup_pipeline (CoglGstVideoSink *sink,
static SnippetCache snippet_cache;
SnippetCacheEntry *entry;
- entry = get_cache_entry (sink, &snippet_cache);
+ entry = get_layer_cache_entry (sink, &snippet_cache);
if (entry == NULL)
{
@@ -724,9 +876,7 @@ cogl_gst_yv12_glsl_setup_pipeline (CoglGstVideoSink *sink,
" float u = texture2D (cogl_sampler%i, UV).a - 0.5;\n"
" float v = texture2D (cogl_sampler%i, UV).a - 0.5;\n"
" vec4 color;\n"
- " color.r = y + 1.59765625 * v;\n"
- " color.g = y - 0.390625 * u - 0.8125 * v;\n"
- " color.b = y + 2.015625 * u;\n"
+ " color.rgb = cogl_gst_default_yuv_to_srgb (corrected);\n"
" color.a = 1.0;\n"
" return color;\n"
"}\n",
@@ -735,7 +885,7 @@ cogl_gst_yv12_glsl_setup_pipeline (CoglGstVideoSink *sink,
priv->custom_start + 1,
priv->custom_start + 2);
- entry = add_cache_entry (sink, &snippet_cache, source);
+ entry = add_layer_cache_entry (sink, &snippet_cache, source);
g_free (source);
}
@@ -772,7 +922,7 @@ cogl_gst_ayuv_glsl_setup_pipeline (CoglGstVideoSink *sink,
static SnippetCache snippet_cache;
SnippetCacheEntry *entry;
- entry = get_cache_entry (sink, &snippet_cache);
+ entry = get_layer_cache_entry (sink, &snippet_cache);
if (entry == NULL)
{
@@ -787,14 +937,12 @@ cogl_gst_ayuv_glsl_setup_pipeline (CoglGstVideoSink *sink,
" float u = color.b - 0.5;\n"
" float v = color.a - 0.5;\n"
" color.a = color.r;\n"
- " color.r = y + 1.59765625 * v;\n"
- " color.g = y - 0.390625 * u - 0.8125 * v;\n"
- " color.b = y + 2.015625 * u;\n"
+ " color.rgb = cogl_gst_default_yuv_to_srgb (corrected);\n"
" return color;\n"
"}\n", priv->custom_start,
priv->custom_start);
- entry = add_cache_entry (sink, &snippet_cache, source);
+ entry = add_layer_cache_entry (sink, &snippet_cache, source);
g_free (source);
}
--
1.8.5.2
More information about the Cogl
mailing list