[gegl] gcut: add support for timed global filters chains



commit ec1b62481a9e0ca8f495feac481be9b7f0c69e06
Author: Øyvind Kolås <pippin gimp org>
Date:   Wed Aug 9 19:04:40 2017 +0200

    gcut: add support for timed global filters chains
    
    This adds support for chains that apply on top of the cut and spliced/faded
    video track. For now only chains with global time offstes preceding the clips
    is supported.
    
    Adding for instance:
    
    -- 10.2s 20s gegl:pixelize x-size=0.1rel y-size=0.1rel
    
    before all the clips in a video project will blur the sequence between 10.2s
    and 20s.
    
    If multiple filters apply they are applied in the order they occur in the
    timeline/file.

 gcut/default.edl     |    7 +--
 gcut/default.edl.inc |    7 +--
 gcut/gcut.c          |  126 +++++++++++++++++++++++++++++++++++++++-----------
 gcut/gcut.h          |    6 ++-
 4 files changed, 108 insertions(+), 38 deletions(-)
---
diff --git a/gcut/default.edl b/gcut/default.edl
index 6646a93..b6d83a1 100644
--- a/gcut/default.edl
+++ b/gcut/default.edl
@@ -4,15 +4,14 @@ output-path=gcut.mp4
 video-width=800
 video-height=600
 fps=25.000000
-selection-start=291
-selection-end=291
+selection-start=291.000
+selection-end=291.000
 frame-scale=0.398424
 t0=56.702866
 frame-pos=0.000
 
 gegl:color value=black gegl:crop width=400 height=400 1.240s 2.000s -- 
 gegl:color opi=0:0 gegl:crop opi=0:0 width=400 height=400 0.000s 2.000s -- [fade=1.040s] svg:src-over 
opi=0:0 aux=[  gegl:text opi=0:0 string='note: this is a gcut video\nproject for testing features\nit relies 
on synthetic GEGL\npatterns instead of\nvideo footage\nto be to minimie size\nfor inclusion in gcut 
sources\nbeing able to create this project \nfrom scratch in the gcut ui\nis a current goal.' 
size=0.071811296045780182rel color='rgb(1.0000, 1.0000, 1.0000)' width=3 height=1 gegl:translate opi=0:0 
x=0.10000000000000001rel y=0.051874876022338867rel ]
--- #annotations
 gegl:color opi=0:0 gegl:crop opi=0:0 width=400 height=400 reset-origin=true 0.000s 3.200s -- svg:src-over 
opi=0:0 aux=[  gegl:text opi=0:0 string='gcut' font='sans' size=0.20000000000000001rel color='rgb(1.0000, 
1.0000, 1.0000)' width=1 height=1 gegl:gaussian-blur opi=0:0 std-dev-x={  0.000000=0.400000rel  
2.560000=0.000084rel  }  std-dev-y={  0.000000=0.200000rel  }  clip-extent=false gegl:translate opi=0:0 
x=0.10000000000000001rel y=0.3304142951965332rel ]  gegl:lens-flare opi=0:0 pos-x={  0.000000=-2.000000  
2.400000=0.293004  4.000000=2.000000  }  pos-y={  0.000000=0.100000  2.400000=0.360697  25.000000=0.910000  }
 gegl:color opi=0:0 gegl:crop opi=0:0 width=400 height=400 0.000s 2.000s -- [fade=2.560s] svg:src-over 
opi=0:0 aux=[  gegl:text opi=0:0 string='video editing\nfileformat+render / ui' size=0.10000000000000001rel 
color='rgb(1.0000, 1.0000, 1.0000)' width=1 height=1 gegl:translate opi=0:0 x=0.10000000000000001rel 
y=0.55924856662750244rel ]
 gegl:cell-noise opi=0:0 shape={  0.000000=1.000000  1.600000=1.850000  }  palettize=true gegl:translate 
opi=0:0 x={  20.000000=50.000000  }  y={  20.000000=500.000000  }  gegl:crop opi=0:0 width=400 height=400 
0.000s 1.600s -- [fade=0.920s] 
@@ -28,6 +27,6 @@ gegl:noise-solid opi=0:0 y-size=4.6258320808410645 seed=668093247 gegl:crop opi=
 gegl:noise-solid opi=0:0 gegl:crop opi=0:0 width=400 height=400 0.000s 1.000s -- [fade=1.440s] svg:src-over 
opi=0:0 aux=[  gegl:text opi=0:0 string='crash recovery by default' size=0.081632360816001892rel 
color='rgb(1.0000, 1.0000, 0.0000)' width=1 height=1 gegl:translate opi=0:0 x=0.10000000000000001rel 
y=0.42925620079040527rel ]
 gegl:noise-solid opi=0:0 gegl:crop opi=0:0 width=400 height=400 0.000s 1.000s -- [fade=1.360s] svg:src-over 
opi=0:0 aux=[  gegl:text opi=0:0 string='overlays:audio filter' size=0.10000000000000001rel 
color='rgb(1.0000, 1.0000, 0.0000)' width=1 height=1 gegl:translate opi=0:0 x=0.10000000000000001rel 
y=0.48208630084991455rel ]
 gegl:noise-solid gegl:crop width=400 height=400 0.000s 1.000s -- [fade=1.360s] over aux=[ text size=0.1rel 
string='network rendering' color=yellow translate x=0.1rel y=0.8rel ]
---#end titles
+--   #end titles
 gegl:color opi=0:0 gegl:crop opi=0:0 width=400 height=400 0.000s 18.960s -- svg:src-over opi=0:0 aux=[  
gegl:text opi=0:0 string='end titles\n\nfull of newlines\n\nand some images and more\n\nmost of the 
time\n\n\nthough\n\nusing an image that one pans\nwill provide better typographic control\nperhaps based on a 
pdf/svg\n\nthis is is also where I should stick misc info' size=0.040000000000000001rel color='rgb(1.0000, 
1.0000, 1.0000)' width=1 height=8 gegl:translate opi=0:0 x=0.10000000000000001rel y={  0.000000=1.000000rel  
32.000000=-2.000000rel  }  ]
 -----
diff --git a/gcut/default.edl.inc b/gcut/default.edl.inc
index aeeee6d..3179c6d 100644
--- a/gcut/default.edl.inc
+++ b/gcut/default.edl.inc
@@ -4,15 +4,14 @@
 "video-width=800\n"
 "video-height=600\n"
 "fps=25.000000\n"
-"selection-start=291\n"
-"selection-end=291\n"
+"selection-start=291.000\n"
+"selection-end=291.000\n"
 "frame-scale=0.398424\n"
 "t0=56.702866\n"
 "frame-pos=0.000\n"
 "\n"
 "gegl:color value=black gegl:crop width=400 height=400 1.240s 2.000s -- \n"
 "gegl:color opi=0:0 gegl:crop opi=0:0 width=400 height=400 0.000s 2.000s -- [fade=1.040s] svg:src-over 
opi=0:0 aux=[  gegl:text opi=0:0 string='note: this is a gcut video\\nproject for testing features\\nit 
relies on synthetic GEGL\\npatterns instead of\\nvideo footage\\nto be to minimie size\\nfor inclusion in 
gcut sources\\nbeing able to create this project \\nfrom scratch in the gcut ui\\nis a current goal.' 
size=0.071811296045780182rel color='rgb(1.0000, 1.0000, 1.0000)' width=3 height=1 gegl:translate opi=0:0 
x=0.10000000000000001rel y=0.051874876022338867rel ]\n"
-"-- #annotations\n"
 "gegl:color opi=0:0 gegl:crop opi=0:0 width=400 height=400 reset-origin=true 0.000s 3.200s -- svg:src-over 
opi=0:0 aux=[  gegl:text opi=0:0 string='gcut' font='sans' size=0.20000000000000001rel color='rgb(1.0000, 
1.0000, 1.0000)' width=1 height=1 gegl:gaussian-blur opi=0:0 std-dev-x={  0.000000=0.400000rel  
2.560000=0.000084rel  }  std-dev-y={  0.000000=0.200000rel  }  clip-extent=false gegl:translate opi=0:0 
x=0.10000000000000001rel y=0.3304142951965332rel ]  gegl:lens-flare opi=0:0 pos-x={  0.000000=-2.000000  
2.400000=0.293004  4.000000=2.000000  }  pos-y={  0.000000=0.100000  2.400000=0.360697  25.000000=0.910000  
}\n"
 "gegl:color opi=0:0 gegl:crop opi=0:0 width=400 height=400 0.000s 2.000s -- [fade=2.560s] svg:src-over 
opi=0:0 aux=[  gegl:text opi=0:0 string='video editing\\nfileformat+render / ui' size=0.10000000000000001rel 
color='rgb(1.0000, 1.0000, 1.0000)' width=1 height=1 gegl:translate opi=0:0 x=0.10000000000000001rel 
y=0.55924856662750244rel ]\n"
 "gegl:cell-noise opi=0:0 shape={  0.000000=1.000000  1.600000=1.850000  }  palettize=true gegl:translate 
opi=0:0 x={  20.000000=50.000000  }  y={  20.000000=500.000000  }  gegl:crop opi=0:0 width=400 height=400 
0.000s 1.600s -- [fade=0.920s] \n"
@@ -28,6 +27,6 @@
 "gegl:noise-solid opi=0:0 gegl:crop opi=0:0 width=400 height=400 0.000s 1.000s -- [fade=1.440s] svg:src-over 
opi=0:0 aux=[  gegl:text opi=0:0 string='crash recovery by default' size=0.081632360816001892rel 
color='rgb(1.0000, 1.0000, 0.0000)' width=1 height=1 gegl:translate opi=0:0 x=0.10000000000000001rel 
y=0.42925620079040527rel ]\n"
 "gegl:noise-solid opi=0:0 gegl:crop opi=0:0 width=400 height=400 0.000s 1.000s -- [fade=1.360s] svg:src-over 
opi=0:0 aux=[  gegl:text opi=0:0 string='overlays:audio filter' size=0.10000000000000001rel 
color='rgb(1.0000, 1.0000, 0.0000)' width=1 height=1 gegl:translate opi=0:0 x=0.10000000000000001rel 
y=0.48208630084991455rel ]\n"
 "gegl:noise-solid gegl:crop width=400 height=400 0.000s 1.000s -- [fade=1.360s] over aux=[ text size=0.1rel 
string='network rendering' color=yellow translate x=0.1rel y=0.8rel ]\n"
-"--#end titles\n"
+"--   #end titles\n"
 "gegl:color opi=0:0 gegl:crop opi=0:0 width=400 height=400 0.000s 18.960s -- svg:src-over opi=0:0 aux=[  
gegl:text opi=0:0 string='end titles\\n\\nfull of newlines\\n\\nand some images and more\\n\\nmost of the 
time\\n\\n\\nthough\\n\\nusing an image that one pans\\nwill provide better typographic control\\nperhaps 
based on a pdf/svg\\n\\nthis is is also where I should stick misc info' size=0.040000000000000001rel 
color='rgb(1.0000, 1.0000, 1.0000)' width=1 height=8 gegl:translate opi=0:0 x=0.10000000000000001rel y={  
0.000000=1.000000rel  32.000000=-2.000000rel  }  ]\n"
 "-----\n"
diff --git a/gcut/gcut.c b/gcut/gcut.c
index 390f9a2..1b917bc 100644
--- a/gcut/gcut.c
+++ b/gcut/gcut.c
@@ -233,6 +233,7 @@ gchar *gcut_get_pos_hash_full (GeglEDL *edl, double pos,
                                Clip **clip1, double *clip1_pos,
                                double *mix)
 {
+  GString *str = g_string_new ("");
   GList *l;
   double clip_start = 0;
   double prev_clip_start = 0;
@@ -298,22 +299,16 @@ gchar *gcut_get_pos_hash_full (GeglEDL *edl, double pos,
         char *clip0_hash = clip_get_pos_hash (clip, clip_frame_pos);
         char *clip1_hash = clip_get_pos_hash (prev, pos - prev_clip_start + clip_get_start (prev));
         double ratio = 0.5 + ((pos -clip_start) * 1.0 / prev_fade_len)/2;
-        char *str = g_strdup_printf ("%s %s %f", clip1_hash, clip0_hash, ratio);
-        GChecksum *hash = g_checksum_new (G_CHECKSUM_MD5);
-        char *ret;
+        g_string_append_printf (str, "%s %s %f", clip1_hash, clip0_hash, ratio);
 
         g_free (clip0_hash);
         g_free (clip1_hash);
-        g_checksum_update (hash, (void*)str, -1);
-        g_free (str);
-        ret = g_strdup (g_checksum_get_string(hash));
-        g_checksum_free (hash);
         if (clip0) *clip0 = prev;
         if (clip0_pos) *clip0_pos = pos - prev_clip_start + clip_get_start (prev);
         if (clip1) *clip1 = clip;
         if (clip1_pos) *clip1_pos = clip_frame_pos;
         if (mix) *mix = ratio;
-        return ret;
+        goto done;
       }
 
       if (next && pos - clip_start > clip_duration - next_fade_len)/* out*/
@@ -321,29 +316,27 @@ gchar *gcut_get_pos_hash_full (GeglEDL *edl, double pos,
         char *clip0_hash = clip_get_pos_hash (clip, clip_frame_pos);
         char *clip1_hash = clip_get_pos_hash (next, pos - (clip_start + clip_duration) + clip_get_start 
(next));
         double ratio = (1.0-(clip_duration -(pos -clip_start)) * 1.0 / next_fade_len)/2;
-        GChecksum *hash = g_checksum_new (G_CHECKSUM_MD5);
-        char *str = g_strdup_printf ("%s %s %f", clip0_hash, clip1_hash, ratio);
-        char *ret;
+        g_string_append_printf (str, "%s %s %f", clip0_hash, clip1_hash, ratio);
         g_free (clip0_hash);
         g_free (clip1_hash);
-        g_checksum_update (hash, (void*)str, -1);
-        g_free (str);
-        ret = g_strdup (g_checksum_get_string(hash));
-        g_checksum_free (hash);
         if (clip0) *clip0 = clip;
         if (clip0_pos) *clip0_pos = clip_frame_pos;
         if (clip1_pos) *clip1_pos = pos - (clip_start +clip_duration) + clip_get_start (next);
         if (clip1) *clip1 = next;
         if (mix)   *mix = ratio;
-        return ret;
+        goto done;
       }
       else
       {
+        char *clip0_hash = clip_get_pos_hash (clip, clip_frame_pos);
+        g_string_append_printf (str, "%s ", clip0_hash);
+        g_free (clip0_hash);
+
         if (clip0) *clip0 = clip;
         if (clip0_pos) *clip0_pos = clip_frame_pos;
         if (clip1) *clip1 = NULL;
         if (mix)   *mix = 0.0;
-        return clip_get_pos_hash (clip, clip_frame_pos);
+        goto done;
       }
     }
     prev_clip_start = clip_start;
@@ -356,6 +349,29 @@ gchar *gcut_get_pos_hash_full (GeglEDL *edl, double pos,
   if (clip1)     *clip1 = NULL;
   if (mix)       *mix = 0.0;
   return NULL;
+done:
+
+  {
+    GList *l;
+    for (l = edl->clips; l; l = l->next)
+    {
+      Clip *c = l->data;
+      if (c->is_meta)
+      {
+        if (pos >= c->start && pos < c->end)
+          g_string_append_printf (str, "[%s]\n", c->filter_graph);
+      }
+    }
+  }
+
+  {
+    GChecksum *hash = g_checksum_new (G_CHECKSUM_MD5);
+    char *ret;
+    g_checksum_update (hash, (void*)str->str, -1);
+    ret = g_strdup (g_checksum_get_string(hash));
+    g_checksum_free (hash);
+    return ret;
+  }
 }
 
 gchar *gcut_get_pos_hash (GeglEDL *edl, double pos)
@@ -404,7 +420,7 @@ void gcut_set_pos (GeglEDL *edl, double pos)
   {
     Clip *clip = NULL;
     gegl_node_set (edl->cache_loader, "path", cache_path, NULL);
-    gegl_node_link_many (edl->cache_loader, edl->result, NULL);
+    gegl_node_link_many (edl->cache_loader, edl->final_result, NULL);
     clip = edl_get_clip_for_pos (edl, pos);
     if (clip)
     {
@@ -417,7 +433,7 @@ void gcut_set_pos (GeglEDL *edl, double pos)
     gegl_meta_get_audio (cache_path, clip->audio);
     }
     {
-    GeglRectangle ext = gegl_node_get_bounding_box (edl->result);
+    GeglRectangle ext = gegl_node_get_bounding_box (edl->final_result);
     gegl_buffer_set_extent (edl->buffer, &ext);
     }
     gegl_node_process (edl->store_final_buf);
@@ -436,16 +452,36 @@ void gcut_set_pos (GeglEDL *edl, double pos)
   if (clip1 == NULL)
   {
     clip_render_pos (clip0, clip0_pos);
-    gegl_node_link_many (clip0->nop_crop, edl->result, NULL);
+    gegl_node_link_many (clip0->nop_crop, edl->video_result, NULL);
   }
   else
   {
     gegl_node_set (edl->mix, "ratio", mix, NULL);
     clip_render_pos (clip0, clip0_pos);
     clip_render_pos (clip1, clip1_pos);
-    gegl_node_link_many (clip0->nop_crop, edl->mix, edl->result, NULL);
+    gegl_node_link_many (clip0->nop_crop, edl->mix, edl->video_result, NULL);
     gegl_node_connect_to (clip1->nop_crop, "output", edl->mix, "aux");
   }
+  gegl_node_link_many (edl->video_result, edl->final_result, NULL);
+
+  {
+    GList *l;
+    for (l = edl->clips; l; l = l->next)
+    {
+      Clip *c = l->data;
+      if (c->is_meta)
+      {
+        if (pos >= c->start && pos < c->end)
+        {
+          GeglNode *prev = gegl_node_get_producer (edl->final_result, "input", NULL);
+          gegl_create_chain (c->filter_graph, prev, edl->final_result,
+                             pos - c->start,
+                             edl->height, NULL, NULL);
+        }
+      }
+    }
+  }
+
   gegl_node_process (edl->store_final_buf);
   gcut_update_buffer (edl);
 
@@ -468,7 +504,7 @@ void gcut_set_pos (GeglEDL *edl, double pos)
           {
             gegl_node_set (save, "bitdepth", 8, NULL);
           }
-          gegl_node_link_many (edl->result, save, NULL);
+          gegl_node_link_many (edl->final_result, save, NULL);
           gegl_node_process (save);
           if (clip1 && clip1->audio && mix > 0.5)
             gegl_meta_set_audio (cache_path, clip1->audio);
@@ -696,7 +732,31 @@ void gcut_parse_line (GeglEDL *edl, const char *line)
   else if (start == 0 && end == 0 && rest)
   {
     Clip *clip = clip_new_full (edl, NULL, 0, 0);
+    const char *first = NULL;
+    const char *second = NULL;
+    const char *p = rest;
+    while (*p == ' ') p++;
+    if (isdigit(*p))
+    {
+      first = p;
+      while (isdigit(*p) || (*p == '.') || (*p == 's')) p++;
+      while (*p == ' ') p++;
+      if (isdigit(*p))
+      {
+        second = p;
+        while (isdigit(*p) || (*p == '.') || (*p == 's')) p++;
+      }
+      while (*p == ' ') p++;
+      rest = p;
+    }
     clip->filter_graph = g_strdup (rest);
+    if (first)
+      clip->start = g_strtod (first, NULL);
+    if (second)
+      clip->end = g_strtod (second, NULL);
+    else
+      clip->end = clip->start;
+
     edl->clips = g_list_append (edl->clips, clip);
   }
 }
@@ -990,7 +1050,8 @@ GeglEDL *gcut_new_from_path (const char *path)
 
 static void setup (GeglEDL *edl)
 {
-  edl->result = gegl_node_new_child (edl->gegl, "operation", "gegl:nop", NULL);
+  edl->video_result = gegl_node_new_child (edl->gegl, "operation", "gegl:nop", NULL);
+  edl->final_result = gegl_node_new_child (edl->gegl, "operation", "gegl:nop", NULL);
   edl->mix = gegl_node_new_child (edl->gegl, "operation", "gegl:mix", NULL);
   edl->encode = gegl_node_new_child (edl->gegl, "operation", "gegl:ff-save",
                                      "path",           edl->output_path,
@@ -1004,7 +1065,8 @@ static void setup (GeglEDL *edl)
   edl->cached_result = gegl_node_new_child (edl->gegl, "operation", "gegl:buffer-source", "buffer", 
edl->buffer, NULL);
   edl->store_final_buf = gegl_node_new_child (edl->gegl, "operation", "gegl:write-buffer", "buffer", 
edl->buffer, NULL);
 
-  gegl_node_link_many (edl->result, edl->store_final_buf, NULL);
+  gegl_node_link_many (edl->video_result, edl->final_result, NULL);
+  gegl_node_link_many (edl->final_result, edl->store_final_buf, NULL);
   gegl_node_link_many (edl->cached_result, edl->encode, NULL);
 }
 
@@ -1478,10 +1540,18 @@ char *gcut_serialize (GeglEDL *edl)
     if (!strncmp (path, edl->parent_path, strlen(edl->parent_path)))
       path += strlen (edl->parent_path);
 
-    if (strlen(path)== 0 &&
-        clip->start == 0 &&
-        clip->end == 0 &&
-        clip->filter_graph)
+    if (clip->is_meta)
+    {
+      if (clip->start == 0 && clip->end == 0)
+        g_string_append_printf (ser, "-- ");
+      else
+        g_string_append_printf (ser, "-- %.3fs %.3fs ", clip->start, clip->end);
+      g_string_append_printf (ser, "%s\n", clip->filter_graph);
+    }
+    else if (strlen(path)== 0 &&
+         clip->start == 0 &&
+         clip->end == 0 &&
+         clip->filter_graph)
     {
       g_string_append_printf (ser, "--%s\n", clip->filter_graph);
     }
diff --git a/gcut/gcut.h b/gcut/gcut.h
index bcc58f0..9528a13 100644
--- a/gcut/gcut.h
+++ b/gcut/gcut.h
@@ -179,6 +179,7 @@ struct _GeglEDL
   char         *parent_path;
   GList        *clip_db;
   GList        *clips;
+
   int           frame; /* render thread, frame_no_ui is ui side */
   double        pos;   /* render thread, frame_pos_ui is ui side */
   double        fps;
@@ -216,9 +217,10 @@ struct _GeglEDL
   int           framedrop;
   int           ui_mode;
 
-  GeglNode     *result;
-  GeglNode     *store_final_buf;
   GeglNode     *mix;
+  GeglNode     *video_result;
+  GeglNode     *final_result;
+  GeglNode     *store_final_buf;
 
   GeglNode     *encode;
   double        scale;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]