From f06e8ed85bd5937d704d57b65e0fb26ed288996d Mon Sep 17 00:00:00 2001
From: Good Guy <good1.2guy@gmail.com>
Date: Wed, 24 Apr 2019 19:32:33 -0600
Subject: [PATCH] tweak ffmpeg mpeg hw probe, update bld prepare, insert tracer
 source

---
 cinelerra-5.1/blds/bld_prepare.sh             |  16 +-
 cinelerra-5.1/plugins/tracer/.gdb_history     |   1 +
 cinelerra-5.1/plugins/tracer/Makefile         |  13 +
 cinelerra-5.1/plugins/tracer/tracer.C         | 687 ++++++++++++++++++
 cinelerra-5.1/plugins/tracer/tracer.h         | 130 ++++
 cinelerra-5.1/plugins/tracer/tracerwindow.C   | 619 ++++++++++++++++
 cinelerra-5.1/plugins/tracer/tracerwindow.h   | 245 +++++++
 .../thirdparty/src/ffmpeg-4.1.patch6          |  11 +
 8 files changed, 1714 insertions(+), 8 deletions(-)
 create mode 100644 cinelerra-5.1/plugins/tracer/.gdb_history
 create mode 100644 cinelerra-5.1/plugins/tracer/Makefile
 create mode 100644 cinelerra-5.1/plugins/tracer/tracer.C
 create mode 100644 cinelerra-5.1/plugins/tracer/tracer.h
 create mode 100644 cinelerra-5.1/plugins/tracer/tracerwindow.C
 create mode 100644 cinelerra-5.1/plugins/tracer/tracerwindow.h
 create mode 100644 cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch6

diff --git a/cinelerra-5.1/blds/bld_prepare.sh b/cinelerra-5.1/blds/bld_prepare.sh
index c79c8ef5..7c1eec81 100755
--- a/cinelerra-5.1/blds/bld_prepare.sh
+++ b/cinelerra-5.1/blds/bld_prepare.sh
@@ -26,8 +26,8 @@ case "$dir" in
     libavc1394 festival-devel libiec61883-devel flac-devel inkscape \
     libsndfile-devel libtheora-devel linux-firmware ivtv-firmware \
     libvorbis-devel texinfo xz-devel lzma-devel cmake udftools git \
-    autoconf automake rpm-build jbigkit-devel libvdpau-devel alsa-lib-devel \
-    gtk2-devel
+    autoconf automake rpm-build jbigkit-devel libvdpau-devel libva-devel \
+    alsa-lib-devel gtk2-devel
     yasm=yasm-1.3.0-3.fc24.x86_64.rpm
     release=http://archives.fedoraproject.org/pub/fedora/linux/releases/24
     url=$release/Everything/x86_64/os/Packages/y/$yasm
@@ -51,7 +51,7 @@ case "$dir" in
     ivtv-firmware libvorbis-devel texinfo xz-devel lzma-devel cmake git \
     ctags patch gcc-c++ perl-XML-XPath libtiff-devel python dvdauthor \
     gettext-devel inkscape udftools autoconf automake numactl-devel \
-    jbigkit-devel libvdpau-devel gtk2-devel
+    jbigkit-devel libvdpau-devel libva-devel gtk2-devel
   ;;
 "suse" | "leap")
   zypper -n install nasm gcc gcc-c++ zlib-devel texinfo libpng16-devel \
@@ -62,8 +62,8 @@ case "$dir" in
     libdvdnav-devel libdvdread-devel libiec61883-devel libuuid-devel \
     ilmbase-devel fftw3-devel libsndfile-devel libtheora-devel flac-devel \
     libtiff-devel inkscape cmake patch libnuma-devel lzma-devel udftools git \
-    yasm autoconf automake rpm-build libjbig-devel libvdpau-devel gtk2-devel \
-    libusb-1_0-devel
+    yasm autoconf automake rpm-build libjbig-devel libvdpau-devel libva-devel \
+    gtk2-devel libusb-1_0-devel
     if [ ! -f /usr/lib64/libtermcap.so ]; then
       ln -s libtermcap.so.2 /usr/lib64/libtermcap.so
     fi
@@ -75,7 +75,7 @@ case "$dir" in
     libncurses5-dev libxinerama-dev libfreetype6-dev libxft-dev libgif-dev \
     libtiff5-dev exuberant-ctags ttf-bitstream-vera xfonts-75dpi xfonts-100dpi \
     fonts-dejavu libopenexr-dev festival libfftw3-dev gdb libusb-1.0-0-dev \
-    libdc1394-22-dev libflac-dev libjbig-dev libvdpau-dev \
+    libdc1394-22-dev libflac-dev libjbig-dev libvdpau-dev libva-dev \
     inkscape libsndfile1-dev libtheora-dev cmake udftools libxml2-utils git \
     autoconf automake debhelper libgtk2.0-dev
   ;;
@@ -88,8 +88,8 @@ case "$dir" in
     libtiff5-dev exuberant-ctags ttf-bitstream-vera xfonts-75dpi xfonts-100dpi \
     fonts-dejavu libopenexr-dev libavc1394-dev festival-dev fftw3-dev gdb \
     libdc1394-22-dev libiec61883-dev libflac-dev libjbig-dev libusb-1.0-0-dev \
-    libvdpau-dev libsndfile1-dev libtheora-dev cmake udftools libxml2-utils \
-    git inkscape autoconf automake debhelper libgtk2.0-dev
+    libvdpau-dev libva-dev libsndfile1-dev libtheora-dev cmake udftools \
+    libxml2-utils git inkscape autoconf automake debhelper libgtk2.0-dev
   ;;
  *)
   echo "unknown os: $dir"
diff --git a/cinelerra-5.1/plugins/tracer/.gdb_history b/cinelerra-5.1/plugins/tracer/.gdb_history
new file mode 100644
index 00000000..a1ff470a
--- /dev/null
+++ b/cinelerra-5.1/plugins/tracer/.gdb_history
@@ -0,0 +1 @@
+p 82*541/352
diff --git a/cinelerra-5.1/plugins/tracer/Makefile b/cinelerra-5.1/plugins/tracer/Makefile
new file mode 100644
index 00000000..be988037
--- /dev/null
+++ b/cinelerra-5.1/plugins/tracer/Makefile
@@ -0,0 +1,13 @@
+include ../../plugin_defs
+
+OBJS := \
+	$(OBJDIR)/tracer.o \
+	$(OBJDIR)/tracerwindow.o
+
+PLUGIN = tracer
+
+include ../../plugin_config
+
+$(OBJDIR)/tracer.o: tracer.C
+$(OBJDIR)/tracerwindow.o: tracerwindow.C
+
diff --git a/cinelerra-5.1/plugins/tracer/tracer.C b/cinelerra-5.1/plugins/tracer/tracer.C
new file mode 100644
index 00000000..be58496d
--- /dev/null
+++ b/cinelerra-5.1/plugins/tracer/tracer.C
@@ -0,0 +1,687 @@
+/*
+ * CINELERRA
+ * Copyright (C) 1997-2015 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include<stdio.h>
+#include<stdint.h>
+#include<math.h>
+#include<string.h>
+#include<float.h>
+
+#include "arraylist.h"
+#include "bccmodels.h"
+#include "bccolors.h"
+#include "clip.h"
+#include "edlsession.h"
+#include "filexml.h"
+#include "tracer.h"
+#include "tracerwindow.h"
+#include "language.h"
+#include "vframe.h"
+
+REGISTER_PLUGIN(Tracer)
+
+void tracer_pgm(const char *fn,VFrame *vfrm)
+{
+	FILE *fp = fopen(fn,"w");
+	int w = vfrm->get_w(), h = vfrm->get_h();
+	fprintf(fp,"P5\n%d %d\n255\n",w,h);
+	fwrite(vfrm->get_data(),w,h,fp);
+	fclose(fp);
+}
+
+TracerPoint::TracerPoint(float x, float y)
+{
+	this->x = x;      this->y = y;
+}
+TracerPoint::~TracerPoint()
+{
+}
+
+TracerConfig::TracerConfig()
+{
+	drag = draw = fill = 0;
+	radius = 0;  scale = 1;
+	selected = 0;
+}
+TracerConfig::~TracerConfig()
+{
+}
+
+int TracerConfig::equivalent(TracerConfig &that)
+{
+	if( this->drag != that.drag ) return 0;
+	if( this->draw != that.draw ) return 0;
+	if( this->fill != that.fill ) return 0;
+	if( this->radius != that.radius ) return 0;
+	if( this->scale != that.scale ) return 0;
+	if( this->points.size() != that.points.size() ) return 0;
+	for( int i=0, n=points.size(); i<n; ++i ) {
+		TracerPoint *ap = this->points[i], *bp = that.points[i];
+		if( !EQUIV(ap->x, bp->x) ) return 0;
+		if( !EQUIV(ap->y, bp->y) ) return 0;
+	}
+	return 1;
+}
+
+void TracerConfig::copy_from(TracerConfig &that)
+{
+	this->drag = that.drag;
+	this->draw = that.draw;
+	this->fill = that.fill;
+	this->selected = that.selected;
+	this->radius = that.radius;
+	this->scale = that.scale;
+	points.remove_all_objects();
+	for( int i=0,n=that.points.size(); i<n; ++i ) {
+		TracerPoint *pt = that.points[i];
+		add_point(pt->x, pt->y);
+	}
+}
+
+void TracerConfig::interpolate(TracerConfig &prev, TracerConfig &next,
+		long prev_frame, long next_frame, long current_frame)
+{
+	copy_from(prev);
+}
+
+void TracerConfig::limits()
+{
+}
+
+int TracerConfig::add_point(float x, float y)
+{
+	int i = points.size();
+	points.append(new TracerPoint(x, y));
+	return i;
+}
+
+void TracerConfig::del_point(int i)
+{
+	points.remove_object_number(i);
+}
+
+
+Tracer::Tracer(PluginServer *server)
+ : PluginVClient(server)
+{
+	frm = 0; frm_rows = 0;
+	msk = 0; msk_rows = 0;
+	edg = 0; edg_rows = 0;
+	w = 0;   w1 = w-1;
+	h = 0;   h1 = h-1;
+	color_model = bpp = 0;
+	is_float = is_yuv = 0;
+	comps = comp = 0;
+	ax = 0;  ay = 0;
+	bx = 0;  by = 0;
+	cx = 0;  cy = 0;
+	ex = 0;  ey = 0;
+}
+
+Tracer::~Tracer()
+{
+	delete edg;
+	delete msk;
+}
+
+const char* Tracer::plugin_title() { return N_("Tracer"); }
+int Tracer::is_realtime() { return 1; }
+
+NEW_WINDOW_MACRO(Tracer, TracerWindow);
+int Tracer::load_configuration1()
+{
+	KeyFrame *prev_keyframe = get_prev_keyframe(get_source_position());
+	if( prev_keyframe->position == get_source_position() ) {
+		read_data(prev_keyframe);
+		return 1;
+	}
+	return load_configuration();
+}
+LOAD_CONFIGURATION_MACRO(Tracer, TracerConfig);
+
+int Tracer::new_point()
+{
+	EDLSession *session = get_edlsession();
+	float x = !session ? 0.f : session->output_w / 2.f;
+	float y = !session ? 0.f : session->output_h / 2.f;
+	return config.add_point(x, y);
+}
+
+void Tracer::save_data(KeyFrame *keyframe)
+{
+	FileXML output;
+
+// cause data to be stored directly in text
+	output.set_shared_output(keyframe->xbuf);
+
+	output.tag.set_title("TRACER");
+	output.tag.set_property("DRAG", config.drag);
+	output.tag.set_property("DRAW", config.draw);
+	output.tag.set_property("FILL", config.fill);
+	output.tag.set_property("RADIUS", config.radius);
+	output.tag.set_property("SCALE", config.scale);
+	output.tag.set_property("SELECTED", config.selected);
+	output.append_tag();
+	output.append_newline();
+	output.tag.set_title("/TRACER");
+	output.append_tag();
+	output.append_newline();
+	for( int i=0, n=config.points.size(); i<n; ++i ) {
+		TracerPoint *pt = config.points[i];
+		char point[BCSTRLEN];
+		sprintf(point,"/POINT_%d",i+1);
+		output.tag.set_title(point+1);
+		output.tag.set_property("X", pt->x);
+		output.tag.set_property("Y", pt->y);
+		output.append_tag();
+		output.tag.set_title(point+0);
+		output.append_tag();
+		output.append_newline();
+	}
+	output.terminate_string();
+}
+
+void Tracer::read_data(KeyFrame *keyframe)
+{
+	FileXML input;
+	input.set_shared_input(keyframe->xbuf);
+	config.points.remove_all_objects();
+	int result = 0;
+
+	while( !(result=input.read_tag()) ) {
+		if( input.tag.title_is("TRACER") ) {
+			config.drag = input.tag.get_property("DRAG", config.drag);
+			config.draw = input.tag.get_property("DRAW", config.draw);
+			config.fill = input.tag.get_property("FILL", config.fill);
+			config.radius = input.tag.get_property("RADIUS", config.radius);
+			config.scale = input.tag.get_property("SCALE", config.scale);
+			config.selected = input.tag.get_property("SELECTED", 0);
+			config.limits();
+		}
+		else if( !strncmp(input.tag.get_title(),"POINT_",6) ) {
+			float x = input.tag.get_property("X", 0.f);
+			float y = input.tag.get_property("Y", 0.f);
+			config.add_point(x, y);
+		}
+	}
+
+	if( !config.points.size() ) new_point();
+}
+
+void Tracer::update_gui()
+{
+	if( !thread ) return;
+	thread->window->lock_window("Tracer::update_gui");
+	TracerWindow *window = (TracerWindow*)thread->window;
+	if( load_configuration1() ) {
+		window->update_gui();
+		window->flush();
+	}
+	thread->window->unlock_window();
+}
+
+void Tracer::draw_point(TracerPoint *pt)
+{
+	int d = bmax(w,h) / 200 + 2;
+	int r = d/2+1, x = pt->x, y = pt->y;
+	frm->draw_smooth(x-r,y+0, x-r, y-r, x+0,y-r);
+	frm->draw_smooth(x+0,y-r, x+r, y-r, x+r,y+0);
+	frm->draw_smooth(x+r,y+0, x+r, y+r, x+0,y+r);
+	frm->draw_smooth(x+0,y+r, x-r, y+r, x-r,y+0);
+}
+
+void Tracer::draw_points()
+{
+	for( int i=0, n=config.points.size(); i<n; ++i ) {
+		TracerPoint *pt = config.points[i];
+		frm->set_pixel_color(config.selected == i ? GREEN : WHITE);
+		draw_point(pt);
+	}
+}
+void Tracer::draw_edge()
+{
+	float scale = 1 / 255.0f;
+	int color_model = frm->get_color_model();
+	int bpp = BC_CModels::calculate_pixelsize(color_model);
+	switch( color_model ) {
+	case BC_RGB_FLOAT:
+		for( int y=0; y<h; ++y ) {
+			uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
+			for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
+				if( !*ep ) continue;
+				float a = *ep * scale;
+				float *px = (float *)sp;
+				px[0] = px[1] = px[2] = a;
+			}
+		}
+		break;
+	case BC_RGBA_FLOAT:
+		for( int y=0; y<h; ++y ) {
+			uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
+			for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
+				if( !*ep ) continue;
+				float a = *ep * scale;
+				float *px = (float *)sp;
+				px[0] = px[1] = px[2] = a;
+				px[3] = 1.0f;
+			}
+		}
+		break;
+	case BC_RGB888:
+		for( int y=0; y<h; ++y ) {
+			uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
+			for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
+				if( !*ep ) continue;
+				float a = *ep * scale;
+				uint8_t *px = sp;
+				px[0] = px[1] = px[2] = a * 255;
+			}
+		}
+		break;
+	case BC_RGBA8888:
+		for( int y=0; y<h; ++y ) {
+			uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
+			for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
+				if( !*ep ) continue;
+				float a = *ep * scale;
+				uint8_t *px = sp;
+				px[0] = px[1] = px[2] = a * 255;
+				px[3] = 0xff;
+			}
+		}
+		break;
+	case BC_YUV888:
+		for( int y=0; y<h; ++y ) {
+			uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
+			for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
+				if( !*ep ) continue;
+				float a = *ep * scale;
+				uint8_t *px = sp;
+				px[0] = a * 255;
+				px[1] = px[2] = 0x80;
+			}
+		}
+		break;
+	case BC_YUVA8888:
+		for( int y=0; y<h; ++y ) {
+			uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
+			for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
+				if( !*ep ) continue;
+				float a = *ep * scale;
+				uint8_t *px = sp;
+				px[0] = a * 255;
+				px[1] = px[2] = 0x80;
+				px[3] = 0xff;
+			}
+		}
+		break;
+	}
+}
+
+
+#define PIX_GRADIENT(type, ix, iy) do { \
+	int xi = vx+ix, yi = vy+iy; \
+	if( edg_rows[yi][xi] ) break; \
+	type *px = (type *)(frm_rows[yi] + xi*bpp); \
+	float dv = px[0]-xp[0], v = dv*dv; \
+	for( int c=1; c<comp; ++c ) { \
+		dv = px[c]-xp[c]; v += dv*dv; \
+	} \
+	v = sqrt(v); \
+	if( vmax < v ) vmax = v; \
+} while(0)
+#define ROW_GRADIENT(type, iy) do { \
+	if( vx > 0 ) PIX_GRADIENT(type,-1, iy); \
+	if( iy != 0) PIX_GRADIENT(type, 0, iy); \
+	if( vx < w1) PIX_GRADIENT(type, 1, iy); \
+} while(0)
+#define MAX_GRADIENT(type) do { \
+	type *xp = (type *)(frm_rows[vy] + vx*bpp); \
+	if( vy > 0 ) ROW_GRADIENT(type,-1); \
+	ROW_GRADIENT(type, 0); \
+	if( vy < h1 ) ROW_GRADIENT(type, 1); \
+} while(0)
+
+#define MAX_PIXEL(type, ix, iy) do { \
+	int vx = cx + ix, vy = cy + iy; \
+	if( edg_rows[vy][vx] ) break; \
+	float vv = FLT_MAX; \
+	int dx = ex-vx, dy = ey-vy; \
+	int rr = dx*dx + dy*dy; \
+	if( rr > dd ) break; \
+	if( rr > 0 ) { \
+		float r = (float)(ix*dx + iy*dy) / rr; \
+		float vmax = 0; \
+		MAX_GRADIENT(type); \
+		vv = r + vmax; \
+	} \
+	if( maxv < vv ) { \
+		maxv = vv; \
+		nx = vx;  ny = vy; \
+	} \
+} while(0)
+#define ROW_MAX(type, iy) do { \
+	if( cx >  0 ) MAX_PIXEL(type,-1, iy); \
+	if( iy != 0 ) MAX_PIXEL(type, 0, iy); \
+	if( cx < w1 ) MAX_PIXEL(type, 1, iy); \
+} while(0)
+
+int Tracer::step()
+{
+	int ret = 0;
+	if( !edg_rows[cy][cx] ) {
+		points.add(cx,cy);
+		edg_rows[cy][cx] = 0xff;
+	}
+	int dx = ex-cx, dy = ey-cy;
+	int dd = dx*dx + dy*dy;
+	if( !dd ) return ret;
+	int nx = cx, ny = cy;
+	double maxv = -FLT_MAX;
+	if( cy > 0 )  ROW_MAX(uint8_t,-1);
+	ROW_MAX(uint8_t, 0);
+	if( cy < h1 ) ROW_MAX(uint8_t, 1);
+	cx = nx;  cy = ny;
+	return maxv > 0 ? 1 : 0;
+}
+
+void Tracer::trace(int i0, int i1)
+{
+	TracerPoint *pt0 = config.points[i0];
+	TracerPoint *pt1 = config.points[i1];
+	cx = pt0->x;  bclamp(cx, 0, w1);
+	cy = pt0->y;  bclamp(cy, 0, h1);
+	ex = pt1->x;  bclamp(ex, 0, w1);
+	ey = pt1->y;  bclamp(ey, 0, h1);
+	while( step() );
+}
+
+int Tracer::smooth()
+{
+	int &n = points.total, m = 0;
+	if( n < 3 ) return m;
+	TracePoint *ap;
+	TracePoint *bp = &points[0];
+	TracePoint *cp = &points[1];
+	for( ; n>=3; --n ) {
+		ap = &points[n-1];
+		if( abs(ap->x-cp->x)<2 && abs(ap->y-cp->y)<2 ) {
+			edg_rows[bp->y][bp->x] = 0;
+			++m;
+		}
+		else
+			break;
+	}
+	if( n < 3 ) return m;
+	ap = &points[0];
+	bp = &points[1];
+	for( int i=2; i<n; ++i ) {
+		cp = &points[i];
+		if( abs(ap->x-cp->x)<2 && abs(ap->y-cp->y)<2 &&
+		    ( (bp->x==ap->x || bp->x==cp->x) &&
+		      (bp->y==ap->y || bp->y==cp->y) ) ) {
+			edg_rows[bp->y][bp->x] = 0;
+			++m;
+		}
+		else {
+			++ap;  ++bp;
+		}
+		bp->x = cp->x;  bp->y = cp->y;
+	}
+	n -= m;
+	return m;
+}
+
+#if 0
+int winding2(int x, int y, TracePoints &pts, int n)
+{
+	int w = 0;
+	int x0 = pts[0].x-x, y0 = pts[0].y-y;
+	for( int x1,y1,i=1; i<n; x0=x1,y0=y1,++i ) { 
+		x1 = pts[i].x-x;  y1 = pts[i].y-y;
+		if( y0*y1 < 0 ) {			   // crosses x axis
+			int xi = x0 - y0*(x1-x0)/(y1-y0);  // x-intercept
+			if( xi > 0 ) w += y0<0 ? 2 : -2;   // crosses x on plus side
+		}
+		else if( !y0 && x0 > 0 ) w += y1>0 ? 1 : -1;
+		else if( !y1 && x1 > 0 ) w += y0>0 ? 1 : -1;
+	}
+	return w;
+}
+#endif
+
+class FillRegion
+{
+	class segment { public: int y, lt, rt; };
+	ArrayList<segment> stack;
+
+	void push(int y, int lt, int rt) {
+		segment &seg = stack.append();
+		seg.y = y;  seg.lt = lt;  seg.rt = rt;
+	}
+	void pop(int &y, int &lt, int &rt) {
+		segment &seg = stack.last();
+		y = seg.y;  lt = seg.lt;  rt = seg.rt;
+		stack.remove();
+	}
+ 
+	int w, h;
+	uint8_t *edg;
+	uint8_t *msk;
+	bool edge_pixel(int i) { return edg[i] > 0; }
+
+public:
+	void fill(int x, int y);
+	void run();
+
+	FillRegion(VFrame *edg, VFrame *msk);
+};
+
+FillRegion::FillRegion(VFrame *edg, VFrame *msk)
+{
+	this->w = msk->get_w();
+	this->h = msk->get_h();
+	this->msk = (uint8_t*) msk->get_data();
+	this->edg = (uint8_t*) edg->get_data();
+}
+
+void FillRegion::fill(int x, int y)
+{
+	push(y, x, x);
+}
+
+void FillRegion::run()
+{
+	while( stack.size() > 0 ) {
+		int y, ilt, irt;
+		pop(y, ilt, irt);
+		int ofs = y*w + ilt;
+		for( int x=ilt; x<=irt; ++x,++ofs ) {
+			if( msk[ofs] ) continue;
+			msk[ofs] = 0xff;
+			if( edge_pixel(ofs) ) continue;
+			int lt = x, rt = x;
+			int lofs = ofs;
+			for( int i=lt; --i>=0; lt=i ) {
+				if( msk[--lofs] ) break;
+				msk[lofs] = 0xff;
+				if( edge_pixel(lofs) ) break;
+			}
+			int rofs = ofs;
+			for( int i=rt; ++i< w; rt=i ) {
+				if( msk[++rofs] ) break;
+				msk[rofs] = 0xff;
+				if( edge_pixel(rofs) ) break;
+			}
+			if( y+1 <  h ) push(y+1, lt, rt);
+			if( y-1 >= 0 ) push(y-1, lt, rt);
+		}
+	}
+}
+
+void Tracer::feather(int r, double s)
+{
+	if( !r || !s ) return;
+	int dir = r<0 ? (r=-r, -1) : 1;
+	if( dir < 0 ) s = 1./s;
+	int rr = r * r;
+	int psf[rr];  // pt spot fn
+	int k = dir>=0 ? 0 : rr-1;
+	for( int i=0; i<rr; ++i,k+=dir )
+		psf[k] = 255*exp(-s*((double)i/rr));
+	int mx = dir > 0 ? 0xff : 0;
+	for( int i=0,n=points.size(); i<n; ++i ) {
+		TracePoint *pt = &points[i];
+		int xs = pt->x-r, xn=pt->x+r;
+		bclamp(xs, 0, w);
+		bclamp(xn, 0, w);
+		int ys = pt->y-r, yn=pt->y+r;
+		bclamp(ys, 0, h);
+		bclamp(yn, 0, h);
+		for( int y=ys ; y<yn; ++y ) {
+			for( int x=xs; x<xn; ++x ) {
+				if( msk_rows[y][x] == mx ) continue;
+				int dx = x-pt->x, dy = y-pt->y;
+				int dd = dx*dx + dy*dy;
+				if( dd >= rr ) continue;
+				int pix = msk_rows[y][x], v = psf[dd];
+				msk_rows[y][x] = dir >= 0 ? pix+v-(pix*v)/255 : (pix*v)/255;
+			}
+		}
+	}
+}
+
+void Tracer::draw_mask()
+{
+	switch( color_model ) {
+	case BC_RGB888:
+		for( int y=0; y<h; ++y ) {
+			uint8_t *mp = msk_rows[y];
+			uint8_t *rp = frm_rows[y];
+			for( int x=0; x<w; rp+=bpp,++x ) {
+				if( !mp[x] ) continue;
+				int a = 0xff - mp[x];
+				rp[0] = a*rp[0] / 0xff;
+				rp[1] = a*rp[1] / 0xff;
+				rp[2] = a*rp[2] / 0xff;
+			}
+		}
+		break;
+	case BC_YUV888:
+		for( int y=0; y<h; ++y ) {
+			uint8_t *mp = msk_rows[y];
+			uint8_t *rp = frm_rows[y];
+			for( int x=0; x<w; rp+=bpp,++x ) {
+				if( !mp[x] ) continue;
+				int a = 0xff - mp[x];
+				rp[0] = a*rp[0] / 0xff;
+				rp[1] = a*(rp[1]-0x80)/0xff + 0x80;
+				rp[2] = a*(rp[2]-0x80)/0xff + 0x80;
+			}
+		}
+		break;
+	case BC_RGB_FLOAT:
+		for( int y=0; y<h; ++y ) {
+			uint8_t *mp = msk_rows[y];
+			uint8_t *rp = frm_rows[y];
+			for( int x=0; x<w; rp+=bpp,++x ) {
+				if( !mp[x] ) continue;
+				float a = 1 - mp[x]/255.f;
+				float *fp = (float*)rp;
+				fp[0] *= a;  fp[1] *= a;  fp[2] *= a;
+			}
+		}
+		break;
+	case BC_RGBA8888:
+	case BC_YUVA8888:
+		for( int y=0; y<h; ++y ) {
+			uint8_t *mp = msk_rows[y];
+			uint8_t *rp = frm_rows[y];
+			for( int x=0; x<w; rp+=bpp,++x ) {
+				rp[3] = 0xff - mp[x];
+			}
+		}
+		break;
+	case BC_RGBA_FLOAT:
+		for( int y=0; y<h; ++y ) {
+			uint8_t *mp = msk_rows[y];
+			uint8_t *rp = frm_rows[y];
+			for( int x=0; x<w; rp+=bpp,++x ) {
+				float *fp = (float*)rp;
+				fp[3] = 1 - mp[x]/255.f;
+			}
+		}
+		break;
+	}
+}
+
+int Tracer::process_buffer(VFrame *frame, int64_t start_position, double frame_rate)
+{
+	int redraw = load_configuration1();
+	frm = frame;
+	frm_rows = frm->get_rows();
+	w = frm->get_w();  w1 = w-1;
+	h = frm->get_h();  h1 = h-1;
+	color_model = frm->get_color_model();
+	bpp = BC_CModels::calculate_pixelsize(color_model);
+	is_float = BC_CModels::is_float(color_model);
+	is_yuv = BC_CModels::is_yuv(color_model);
+	has_alpha = BC_CModels::has_alpha(color_model);
+	comps = BC_CModels::components(color_model);
+	comp = bmin(comps, 3);
+	read_frame(frm, 0, start_position, frame_rate, 0);
+	if( !edg ) redraw = 1;
+	VFrame::get_temp(edg, w, h, BC_GREY8);
+	if( redraw ) {
+		edg->clear_frame();
+		edg_rows = edg->get_rows();
+
+		int n = config.points.size()-1;
+		points.clear();
+		for( int i=0; i<n; ++i )
+			trace(i,i+1);
+		if( n > 0 )
+			trace(n,0);
+		while( smooth() > 0 );
+
+		if( config.fill && points.size() > 2 ) {
+			int l = points.size(), l2 = l/2;
+			TracePoint *pt0 = &points[0], *pt1 = &points[l2];
+			int cx = (pt0->x+pt1->x)/2, cy = (pt0->y+pt1->y)/2;
+			VFrame::get_temp(msk, w, h, BC_GREY8);
+			msk->clear_frame();
+			msk_rows = msk->get_rows();
+
+			FillRegion fill_region(edg, msk);
+			fill_region.fill(cx, cy);
+			fill_region.run();
+
+			feather(config.radius, config.scale);
+		}
+	}
+	if( config.fill )
+		draw_mask();
+	if( config.draw )
+		draw_edge();
+	if( config.drag )
+		draw_points();
+	return 0;
+}
+
diff --git a/cinelerra-5.1/plugins/tracer/tracer.h b/cinelerra-5.1/plugins/tracer/tracer.h
new file mode 100644
index 00000000..cc2364e5
--- /dev/null
+++ b/cinelerra-5.1/plugins/tracer/tracer.h
@@ -0,0 +1,130 @@
+/*
+ * CINELERRA
+ * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+
+#ifndef __TRACER_H__
+#define __TRACER_H__
+
+#include "loadbalance.h"
+#include "pluginvclient.h"
+
+class Tracer;
+
+enum { PT_X, PT_Y, PT_SZ };
+
+class TracerPoint
+{
+public:
+	float x, y;
+
+	TracerPoint(float x, float y);
+	~TracerPoint();
+};
+class TracerPoints : public ArrayList<TracerPoint *>
+{
+public:
+	TracerPoints() {}
+	~TracerPoints() { remove_all_objects(); }
+};
+
+class TracerConfig
+{
+public:
+	TracerConfig();
+	~TracerConfig();
+
+	int equivalent(TracerConfig &that);
+	void copy_from(TracerConfig &that);
+	void interpolate(TracerConfig &prev, TracerConfig &next,
+		long prev_frame, long next_frame, long current_frame);
+	void limits();
+
+	TracerPoints points;
+	int add_point(float x, float y);
+	int add_point();
+	void del_point(int i);
+
+	int drag, draw, fill;
+	int radius;
+	double scale; 
+	int selected;
+};
+
+class TracePoint
+{
+public:
+	int x, y, n;
+	TracePoint() {}
+	TracePoint(int x, int y) {
+		this->x = x; this->y = y;
+		this->n = 0;
+	}
+};
+
+class TracePoints : public ArrayList<TracePoint>
+{
+public:
+	TracePoints() {}
+	void add(int x, int y) {
+		TracePoint *np = &append();
+		np->x = x;  np->y = y;
+	}
+	void clear() { remove_all(); }
+};
+
+
+class Tracer : public PluginVClient
+{
+public:
+	Tracer(PluginServer *server);
+	~Tracer();
+// required for all realtime plugins
+	PLUGIN_CLASS_MEMBERS2(TracerConfig)
+	int is_realtime();
+	void update_gui();
+	int new_point();
+	void save_data(KeyFrame *keyframe);
+	void read_data(KeyFrame *keyframe);
+	int process_buffer(VFrame *frame, int64_t start_position, double frame_rate);
+	void draw_edge();
+	void draw_mask();
+	void draw_point(TracerPoint *pt);
+	void draw_points();
+	int step();
+	void trace(int i0, int i1);
+	int smooth();
+	void feather(int r, double s);
+	int load_configuration1();
+
+	VFrame *edg, *msk, *frm;
+	uint8_t **edg_rows;
+	uint8_t **msk_rows;
+	uint8_t **frm_rows;
+	TracePoints points;
+	int w, w1, h, h1, pts;
+	int ax, ay, bx, by, cx, cy;
+	int ex, ey, nx, ny;
+	int color_model, bpp;
+	int is_float, is_yuv, has_alpha;
+	int comps, comp;
+};
+
+#endif
diff --git a/cinelerra-5.1/plugins/tracer/tracerwindow.C b/cinelerra-5.1/plugins/tracer/tracerwindow.C
new file mode 100644
index 00000000..af0ec2f2
--- /dev/null
+++ b/cinelerra-5.1/plugins/tracer/tracerwindow.C
@@ -0,0 +1,619 @@
+/*
+ * CINELERRA
+ * Copyright (C) 2014 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "automation.h"
+#include "bcdisplayinfo.h"
+#include "clip.h"
+#include "tracer.h"
+#include "tracerwindow.h"
+#include "cstrdup.h"
+#include "cwindow.h"
+#include "cwindowgui.h"
+#include "edl.h"
+#include "edlsession.h"
+#include "language.h"
+#include "mainerror.h"
+#include "mwindow.h"
+#include "plugin.h"
+#include "pluginserver.h"
+#include "theme.h"
+#include "track.h"
+
+#define COLOR_W 50
+#define COLOR_H 30
+
+TracerNum::TracerNum(TracerWindow *gui, int x, int y, float output)
+ : BC_TumbleTextBox(gui, output, -32767.0f, 32767.0f, x, y, 120)
+{
+	this->gui = gui;
+	set_increment(1);
+	set_precision(1);
+}
+
+TracerNum::~TracerNum()
+{
+}
+
+int TracerPointX::handle_event()
+{
+	if( !TracerNum::handle_event() ) return 0;
+	TracerPointList *point_list = gui->point_list;
+	int hot_point = point_list->get_selection_number(0, 0);
+	TracerPoints &points = gui->plugin->config.points;
+	int sz = points.size();
+	if( hot_point >= 0 && hot_point < sz ) {
+		float v = atof(get_text());
+		points[hot_point]->x = v;
+		point_list->set_point(hot_point, PT_X, v);
+	}
+	point_list->update_list(hot_point);
+	gui->send_configure_change();
+	return 1;
+}
+int TracerPointY::handle_event()
+{
+	if( !TracerNum::handle_event() ) return 0;
+	TracerPointList *point_list = gui->point_list;
+	int hot_point = point_list->get_selection_number(0, 0);
+	TracerPoints &points = gui->plugin->config.points;
+	int sz = points.size();
+	if( hot_point >= 0 && hot_point < sz ) {
+		float v = atof(get_text());
+		points[hot_point]->y = v;
+		point_list->set_point(hot_point, PT_Y, v);
+	}
+	point_list->update_list(hot_point);
+	gui->send_configure_change();
+	return 1;
+}
+
+TracerWindow::TracerWindow(Tracer *plugin)
+ : PluginClientWindow(plugin, 400, 300, 400, 300, 0)
+{
+	this->plugin = plugin;
+	this->title_x = 0;    this->point_x = 0;
+	this->title_y = 0;    this->point_y = 0;
+	this->new_point = 0;  this->del_point = 0;
+	this->point_up = 0;   this->point_dn = 0;
+	this->drag = 0;       this->draw = 0;
+	this->dragging = 0;
+	this->title_r = 0;    this->title_s = 0;
+	this->radius = 0;     this->scale = 0;
+	this->last_x = 0;     this->last_y = 0;
+	this->point_list = 0; this->pending_config = 0;
+}
+
+TracerWindow::~TracerWindow()
+{
+	delete point_x;
+	delete point_y;
+}
+
+void TracerWindow::create_objects()
+{
+	int x = 10, y = 10;
+	int margin = plugin->get_theme()->widget_border;
+	TracerPoint *pt = plugin->config.points[plugin->config.selected];
+	add_subwindow(title_x = new BC_Title(x, y, _("X:")));
+	int x1 = x + title_x->get_w() + margin;
+	point_x = new TracerPointX(this, x1, y, pt->x);
+	point_x->create_objects();
+	x1 += point_x->get_w() + margin;
+	add_subwindow(new_point = new TracerNewPoint(this, plugin, x1, y));
+	x1 += new_point->get_w() + margin;
+	add_subwindow(point_up = new TracerPointUp(this, x1, y));
+	y += point_x->get_h() + margin;
+	add_subwindow(title_y = new BC_Title(x, y, _("Y:")));
+	x1 = x + title_y->get_w() + margin;
+	point_y = new TracerPointY(this, x1, y, pt->y);
+	point_y->create_objects();
+	x1 += point_y->get_w() + margin;
+	add_subwindow(del_point = new TracerDelPoint(this, plugin, x1, y));
+	x1 += del_point->get_w() + margin;
+	add_subwindow(point_dn = new TracerPointDn(this, x1, y));
+	y += point_y->get_h() + margin + 10;
+
+	add_subwindow(drag = new TracerDrag(this, x, y));
+	if( plugin->config.drag ) {
+		if( !grab(plugin->server->mwindow->cwindow->gui) )
+			eprintf("drag enabled, but compositor already grabbed\n");
+	}
+	x1 = x + drag->get_w() + margin + 20;
+	add_subwindow(draw = new TracerDraw(this, x1, y));
+	x1 += draw->get_w() + margin + 20;
+	add_subwindow(fill = new TracerFill(this, x1, y));
+	x1 += drag->get_w() + margin + 20;
+	add_subwindow(reset = new TracerReset(this, plugin, x1, y+3));
+	y += drag->get_h() + margin;
+
+	add_subwindow(title_r = new BC_Title(x1=x, y, _("Radius:")));
+	x1 += title_r->get_w() + margin;
+	add_subwindow(radius = new TracerRadius(this, x1, y, 100));
+	x1 += radius->get_w() + margin + 20;
+	add_subwindow(title_s = new BC_Title(x1, y, _("Scale:")));
+	x1 += title_s->get_w() + margin;
+	add_subwindow(scale = new TracerScale(this, x1, y, 100));
+	y += radius->get_h() + margin + 20;
+
+	add_subwindow(point_list = new TracerPointList(this, plugin, x, y));
+	point_list->update(plugin->config.selected);
+//	y += point_list->get_h() + 10;
+
+	show_window(1);
+}
+
+void TracerWindow::send_configure_change()
+{
+	pending_config = 0;
+	plugin->send_configure_change();
+}
+
+int TracerWindow::grab_event(XEvent *event)
+{
+	int ret = do_grab_event(event);
+	if( pending_config && !grab_event_count() )
+		send_configure_change();
+	return ret;
+}
+
+int TracerWindow::do_grab_event(XEvent *event)
+{
+	switch( event->type ) {
+	case ButtonPress: break;
+	case ButtonRelease: break;
+	case MotionNotify: break;
+	default:
+		return 0;
+	}
+
+	MWindow *mwindow = plugin->server->mwindow;
+	CWindowGUI *cwindow_gui = mwindow->cwindow->gui;
+	CWindowCanvas *canvas = cwindow_gui->canvas;
+	int cx, cy;  cwindow_gui->get_relative_cursor(cx, cy);
+	cx -= canvas->view_x;
+	cy -= canvas->view_y;
+
+	if( !dragging ) {
+		if( cx < 0 || cx >= canvas->view_w ||
+		    cy < 0 || cy >= canvas->view_h )
+			return 0;
+	}
+
+	switch( event->type ) {
+	case ButtonPress:
+		if( dragging ) return 0;
+		dragging = event->xbutton.state & ShiftMask ? -1 : 1;
+		break;
+	case ButtonRelease:
+		if( !dragging ) return 0;
+		dragging = 0;
+		return 1;
+	case MotionNotify:
+		if( !dragging ) return 0;
+		break;
+	default:
+		return 0;
+	}
+
+	float cursor_x = cx, cursor_y = cy;
+	canvas->canvas_to_output(mwindow->edl, 0, cursor_x, cursor_y);
+	int64_t position = plugin->get_source_position();
+	float projector_x, projector_y, projector_z;
+	Track *track = plugin->server->plugin->track;
+	int track_w = track->track_w, track_h = track->track_h;
+	track->automation->get_projector(
+		&projector_x, &projector_y, &projector_z,
+		position, PLAY_FORWARD);
+	projector_x += mwindow->edl->session->output_w / 2;
+	projector_y += mwindow->edl->session->output_h / 2;
+	float output_x = (cursor_x - projector_x) / projector_z + track_w / 2;
+	float output_y = (cursor_y - projector_y) / projector_z + track_h / 2;
+	point_x->update((int64_t)(output_x));
+	point_y->update((int64_t)(output_y));
+	TracerPoints &points = plugin->config.points;
+
+	if( dragging > 0 ) {
+		switch( event->type ) {
+		case ButtonPress: {
+			int button_no = event->xbutton.button;
+			int hot_point = -1;
+			if( button_no == RIGHT_BUTTON ) {
+				hot_point = plugin->new_point();
+				TracerPoint *pt = points[hot_point];
+				pt->x = output_x;  pt->y = output_y;
+				point_list->update(hot_point);
+				break;
+			}
+			int sz = points.size();
+			if( hot_point < 0 && sz > 0 ) {
+				TracerPoint *pt = points[hot_point=0];
+				double dist = DISTANCE(output_x,output_y, pt->x,pt->y);
+				for( int i=1; i<sz; ++i ) {
+					pt = points[i];
+					double d = DISTANCE(output_x,output_y, pt->x,pt->y);
+					if( d >= dist ) continue;
+					dist = d;  hot_point = i;
+				}
+				pt = points[hot_point];
+				float px = (pt->x - track_w / 2) * projector_z + projector_x;
+				float py = (pt->y - track_h / 2) * projector_z + projector_y;
+				dist = DISTANCE(px, py, cursor_x,cursor_y);
+				if( dist >= HANDLE_W ) hot_point = -1;
+			}
+			if( hot_point >= 0 && sz > 0 ) {
+				TracerPoint *pt = points[hot_point];
+				point_list->set_point(hot_point, PT_X, pt->x = output_x);
+				point_list->set_point(hot_point, PT_Y, pt->y = output_y);
+				point_list->update_list(hot_point);
+			}
+			break; }
+		case MotionNotify: {
+			int hot_point = point_list->get_selection_number(0, 0);
+			if( hot_point >= 0 && hot_point < points.size() ) {
+				TracerPoint *pt = points[hot_point];
+				if( pt->x == output_x && pt->y == output_y ) break;
+				point_list->set_point(hot_point, PT_X, pt->x = output_x);
+				point_list->set_point(hot_point, PT_Y, pt->y = output_y);
+				point_x->update(pt->x);
+				point_y->update(pt->y);
+				point_list->update_list(hot_point);
+			}
+			break; }
+		}
+	}
+	else {
+		switch( event->type ) {
+		case MotionNotify: {
+			float dx = output_x - last_x, dy = output_y - last_y;
+			int sz = points.size();
+			for( int i=0; i<sz; ++i ) {
+				TracerPoint *pt = points[i];
+				point_list->set_point(i, PT_X, pt->x += dx);
+				point_list->set_point(i, PT_Y, pt->y += dy);
+			}
+			int hot_point = point_list->get_selection_number(0, 0);
+			if( hot_point >= 0 && hot_point < sz ) {
+				TracerPoint *pt = points[hot_point];
+				point_x->update(pt->x);
+				point_y->update(pt->y);
+				point_list->update_list(hot_point);
+			}
+			break; }
+		}
+	}
+
+	last_x = output_x;  last_y = output_y;
+	pending_config = 1;
+	return 1;
+}
+
+void TracerWindow::done_event(int result)
+{
+	ungrab(client->server->mwindow->cwindow->gui);
+}
+
+TracerPointList::TracerPointList(TracerWindow *gui, Tracer *plugin, int x, int y)
+ : BC_ListBox(x, y, 360, 130, LISTBOX_TEXT)
+{
+	this->gui = gui;
+	this->plugin = plugin;
+	titles[PT_X] = _("X");    widths[PT_X] = 90;
+	titles[PT_Y] = _("Y");    widths[PT_Y] = 90;
+}
+TracerPointList::~TracerPointList()
+{
+	clear();
+}
+void TracerPointList::clear()
+{
+	for( int i=PT_SZ; --i>=0; )
+		cols[i].remove_all_objects();
+}
+
+int TracerPointList::column_resize_event()
+{
+	for( int i=PT_SZ; --i>=0; )
+		widths[i] = get_column_width(i);
+	return 1;
+}
+
+int TracerPointList::handle_event()
+{
+	int hot_point = get_selection_number(0, 0);
+	const char *x_text = "", *y_text = "";
+	TracerPoints &points = plugin->config.points;
+
+	int sz = points.size();
+	if( hot_point >= 0 && sz > 0 ) {
+		x_text = gui->point_list->cols[PT_X].get(hot_point)->get_text();
+		y_text = gui->point_list->cols[PT_Y].get(hot_point)->get_text();
+	}
+	gui->point_x->update(x_text);
+	gui->point_y->update(y_text);
+	update(hot_point);
+	gui->send_configure_change();
+	return 1;
+}
+
+int TracerPointList::selection_changed()
+{
+	handle_event();
+	return 1;
+}
+
+void TracerPointList::new_point(const char *xp, const char *yp)
+{
+	cols[PT_X].append(new BC_ListBoxItem(xp));
+	cols[PT_Y].append(new BC_ListBoxItem(yp));
+}
+
+void TracerPointList::del_point(int i)
+{
+	for( int sz1=cols[0].size()-1, c=PT_SZ; --c>=0; )
+		cols[c].remove_object_number(sz1-i);
+}
+
+void TracerPointList::set_point(int i, int c, float v)
+{
+	char s[BCSTRLEN]; sprintf(s,"%0.4f",v);
+	set_point(i,c,s);
+}
+void TracerPointList::set_point(int i, int c, const char *cp)
+{
+	cols[c].get(i)->set_text(cp);
+}
+
+int TracerPointList::set_selected(int k)
+{
+	TracerPoints &points = plugin->config.points;
+	int sz = points.size();
+	if( !sz ) return -1;
+	bclamp(k, 0, sz-1);
+	update_selection(&cols[0], k);
+	return k;
+}
+void TracerPointList::update_list(int k)
+{
+	int xpos = get_xposition(), ypos = get_yposition();
+	if( k < 0 ) k = get_selection_number(0, 0);
+	update_selection(&cols[0], k);
+	BC_ListBox::update(&cols[0], &titles[0],&widths[0],PT_SZ, xpos,ypos,k);
+	center_selection();
+}
+void TracerPointList::update(int k)
+{
+	clear();
+	TracerPoints &points = plugin->config.points;
+	int sz = points.size();
+	for( int i=0; i<sz; ++i ) {
+		TracerPoint *pt = points[i];
+		char xtxt[BCSTRLEN];  sprintf(xtxt,"%0.4f", pt->x);
+		char ytxt[BCSTRLEN];  sprintf(ytxt,"%0.4f", pt->y);
+		new_point(xtxt, ytxt);
+	}
+	if( k >= 0 && k < sz ) {
+		gui->point_x->update(gui->point_list->cols[PT_X].get(k)->get_text());
+		gui->point_y->update(gui->point_list->cols[PT_Y].get(k)->get_text());
+		plugin->config.selected = k;
+	}
+
+	update_list(k);
+}
+
+void TracerWindow::update_gui()
+{
+	TracerConfig &config = plugin->config;
+	drag->update(config.drag);
+	draw->update(config.draw);
+	fill->update(config.fill);
+	radius->update(config.radius);
+	scale->update(config.scale);
+	drag->update(config.drag);
+	point_list->update(-1);
+}
+
+
+TracerPointUp::TracerPointUp(TracerWindow *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Up"))
+{
+	this->gui = gui;
+}
+TracerPointUp::~TracerPointUp()
+{
+}
+
+int TracerPointUp::handle_event()
+{
+	TracerPoints &points = gui->plugin->config.points;
+	int sz = points.size();
+	int hot_point = gui->point_list->get_selection_number(0, 0);
+
+	if( sz > 1 && hot_point > 0 ) {
+		TracerPoint *&pt0 = points[hot_point];
+		TracerPoint *&pt1 = points[--hot_point];
+		TracerPoint *t = pt0;  pt0 = pt1;  pt1 = t;
+		gui->point_list->update(hot_point);
+	}
+	gui->send_configure_change();
+	return 1;
+}
+
+TracerPointDn::TracerPointDn(TracerWindow *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Dn"))
+{
+	this->gui = gui;
+}
+TracerPointDn::~TracerPointDn()
+{
+}
+
+int TracerPointDn::handle_event()
+{
+	TracerPoints &points = gui->plugin->config.points;
+	int sz = points.size();
+	int hot_point = gui->point_list->get_selection_number(0, 0);
+	if( sz > 1 && hot_point < sz-1 ) {
+		TracerPoint *&pt0 = points[hot_point];
+		TracerPoint *&pt1 = points[++hot_point];
+		TracerPoint *t = pt0;  pt0 = pt1;  pt1 = t;
+		gui->point_list->update(hot_point);
+	}
+	gui->send_configure_change();
+	return 1;
+}
+
+TracerDrag::TracerDrag(TracerWindow *gui, int x, int y)
+ : BC_CheckBox(x, y, gui->plugin->config.drag, _("Drag"))
+{
+	this->gui = gui;
+}
+int TracerDrag::handle_event()
+{
+	CWindowGUI *cwindow_gui = gui->plugin->server->mwindow->cwindow->gui;
+	int value = get_value();
+	if( value ) {
+		if( !gui->grab(cwindow_gui) ) {
+			update(value = 0);
+			flicker(10,50);
+		}
+	}
+	else
+		gui->ungrab(cwindow_gui);
+	gui->plugin->config.drag = value;
+	gui->send_configure_change();
+	return 1;
+}
+
+TracerDraw::TracerDraw(TracerWindow *gui, int x, int y)
+ : BC_CheckBox(x, y, gui->plugin->config.draw, _("Draw"))
+{
+	this->gui = gui;
+}
+int TracerDraw::handle_event()
+{
+	gui->plugin->config.draw = get_value();
+	gui->send_configure_change();
+	return 1;
+}
+
+TracerFill::TracerFill(TracerWindow *gui, int x, int y)
+ : BC_CheckBox(x, y, gui->plugin->config.fill, _("Fill"))
+{
+	this->gui = gui;
+}
+int TracerFill::handle_event()
+{
+	gui->plugin->config.fill = get_value();
+	gui->send_configure_change();
+	return 1;
+}
+
+TracerRadius::TracerRadius(TracerWindow *gui, int x, int y, int w)
+ : BC_ISlider(x,y,0,w,w, -50,50, gui->plugin->config.radius)
+{
+	this->gui = gui;
+}
+int TracerRadius::handle_event()
+{
+	gui->plugin->config.radius = get_value();
+	gui->send_configure_change();
+	return 1;
+}
+
+TracerScale::TracerScale(TracerWindow *gui, int x, int y, int w)
+ : BC_FSlider(x,y, 0,w,w, 1.f,10.f, gui->plugin->config.scale)
+{
+	this->gui = gui;
+}
+int TracerScale::handle_event()
+{
+	gui->plugin->config.scale = get_value();
+	gui->send_configure_change();
+	return 1;
+}
+
+TracerNewPoint::TracerNewPoint(TracerWindow *gui, Tracer *plugin, int x, int y)
+ : BC_GenericButton(x, y, 80, _("New"))
+{
+	this->gui = gui;
+	this->plugin = plugin;
+}
+TracerNewPoint::~TracerNewPoint()
+{
+}
+int TracerNewPoint::handle_event()
+{
+	int k = plugin->new_point();
+	gui->point_list->update(k);
+	gui->send_configure_change();
+	return 1;
+}
+
+TracerDelPoint::TracerDelPoint(TracerWindow *gui, Tracer *plugin, int x, int y)
+ : BC_GenericButton(x, y, 80, C_("Del"))
+{
+	this->gui = gui;
+	this->plugin = plugin;
+}
+TracerDelPoint::~TracerDelPoint()
+{
+}
+int TracerDelPoint::handle_event()
+{
+	int hot_point = gui->point_list->get_selection_number(0, 0);
+	TracerPoints &points = plugin->config.points;
+	if( hot_point >= 0 && hot_point < points.size() ) {
+		plugin->config.del_point(hot_point);
+		if( !points.size() ) plugin->new_point();
+		int sz = points.size();
+		if( hot_point >= sz && hot_point > 0 ) --hot_point;
+		gui->point_list->update(hot_point);
+		gui->send_configure_change();
+	}
+	return 1;
+}
+
+TracerReset::TracerReset(TracerWindow *gui, Tracer *plugin, int x, int y)
+ : BC_GenericButton(x, y, _("Reset"))
+{
+	this->gui = gui;
+	this->plugin = plugin;
+}
+TracerReset::~TracerReset()
+{
+}
+int TracerReset::handle_event()
+{
+	TracerConfig &config = plugin->config;
+	config.drag = 0;
+	config.draw = 0;
+	config.fill = 0;
+	config.radius = 0;
+	config.scale = 1;
+	config.selected = 0;
+	TracerPoints &points = plugin->config.points;
+	points.remove_all_objects();
+	plugin->new_point();
+	gui->point_list->update(0);
+	gui->update_gui();
+	gui->send_configure_change();
+	return 1;
+}
+
diff --git a/cinelerra-5.1/plugins/tracer/tracerwindow.h b/cinelerra-5.1/plugins/tracer/tracerwindow.h
new file mode 100644
index 00000000..b6231cf7
--- /dev/null
+++ b/cinelerra-5.1/plugins/tracer/tracerwindow.h
@@ -0,0 +1,245 @@
+/*
+ * CINELERRA
+ * Copyright (C) 2008-2015 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef __TRACERWINDOW_H__
+#define __TRACERWINDOW_H__
+
+#include "guicast.h"
+
+class Tracer;
+class TracerWindow;
+class TracerNum;
+class TracerPointX;
+class TracerPointY;
+class TracerDrag;
+class TracerDraw;
+class TracerFill;
+class TracerRadius;
+class TracerScale;
+class TracerPointList;
+class TracerNewPoint;
+class TracerDelPoint;
+class TracerPointUp;
+class TracerPointDn;
+class TracerReset;
+
+
+class TracerNum : public BC_TumbleTextBox
+{
+public:
+	TracerWindow *gui;
+
+	TracerNum(TracerWindow *gui, int x, int y, float output);
+	~TracerNum();
+};
+class TracerPointX : public TracerNum
+{
+public:
+	TracerPointX(TracerWindow *gui, int x, int y, float output)
+	 : TracerNum(gui, x, y, output) {}
+	~TracerPointX() {}
+
+	int handle_event();
+};
+class TracerPointY : public TracerNum
+{
+public:
+	TracerPointY(TracerWindow *gui, int x, int y, float output)
+	 : TracerNum(gui, x, y, output) {}
+	~TracerPointY() {}
+
+	int handle_event();
+};
+
+class TracerThreshold : public BC_FSlider
+{
+public:
+	TracerThreshold(TracerWindow *gui, int x, int y, int w);
+	int handle_event();
+	int wheel_event(int v);
+	TracerWindow *gui;
+};
+
+class TracerDrag : public BC_CheckBox
+{
+public:
+	TracerDrag(TracerWindow *gui, int x, int y);
+
+	int handle_event();
+	TracerWindow *gui;
+};
+
+class TracerDraw : public BC_CheckBox
+{
+public:
+	TracerDraw(TracerWindow *gui, int x, int y);
+
+	int handle_event();
+	TracerWindow *gui;
+};
+
+class TracerFill : public BC_CheckBox
+{
+public:
+	TracerFill(TracerWindow *gui, int x, int y);
+
+	int handle_event();
+	TracerWindow *gui;
+};
+
+class TracerRadius : public BC_ISlider
+{
+public:
+	TracerRadius(TracerWindow *gui, int x, int y, int w);
+
+	int handle_event();
+	TracerWindow *gui;
+};
+
+class TracerScale : public BC_FSlider
+{
+public:
+	TracerScale(TracerWindow *gui, int x, int y, int w);
+
+	int handle_event();
+	TracerWindow *gui;
+};
+
+class TracerPointList : public BC_ListBox
+{
+public:
+	TracerPointList(TracerWindow *gui, Tracer *plugin, int x, int y);
+	~TracerPointList();
+
+	int handle_event();
+	int selection_changed();
+	int column_resize_event();
+	ArrayList<BC_ListBoxItem*> cols[PT_SZ];
+	void clear();
+	void new_point(const char *xp, const char *yp);
+	void del_point(int i);
+	void set_point(int i, int c, float v);
+	void set_point(int i, int c, const char *cp);
+	int set_selected(int k);
+	void update(int k);
+	void update_list(int k);
+
+
+	TracerWindow *gui;
+	Tracer *plugin;
+	const char *titles[PT_SZ];
+	int widths[PT_SZ];
+};
+
+class TracerNewPoint : public BC_GenericButton
+{
+public:
+	TracerNewPoint(TracerWindow *gui, Tracer *plugin, int x, int y);
+	~TracerNewPoint();
+
+	int handle_event();
+
+	TracerWindow *gui;
+	Tracer *plugin;
+};
+
+class TracerDelPoint : public BC_GenericButton
+{
+public:
+	TracerDelPoint(TracerWindow *gui, Tracer *plugin, int x, int y);
+	~TracerDelPoint();
+
+	int handle_event();
+
+	Tracer *plugin;
+	TracerWindow *gui;
+};
+
+class TracerPointUp : public BC_GenericButton
+{
+public:
+	TracerPointUp(TracerWindow *gui, int x, int y);
+	~TracerPointUp();
+
+	int handle_event();
+
+	TracerWindow *gui;
+};
+
+class TracerPointDn : public BC_GenericButton
+{
+public:
+	TracerPointDn(TracerWindow *gui, int x, int y);
+	~TracerPointDn();
+
+	int handle_event();
+
+	TracerWindow *gui;
+};
+
+class TracerReset : public BC_GenericButton
+{
+public:
+	TracerReset(TracerWindow *gui, Tracer *plugin, int x, int y);
+	~TracerReset();
+
+	int handle_event();
+
+	Tracer *plugin;
+	TracerWindow *gui;
+};
+
+
+class TracerWindow : public PluginClientWindow
+{
+public:
+	TracerWindow(Tracer *plugin);
+	~TracerWindow();
+
+	void create_objects();
+	void update_gui();
+	void start_color_thread();
+	int grab_event(XEvent *event);
+	int do_grab_event(XEvent *event);
+	void done_event(int result);
+	void send_configure_change();
+
+	Tracer *plugin;
+	BC_Title *title_x, *title_y;
+	TracerPointX *point_x;
+	TracerPointY *point_y;
+	TracerNewPoint *new_point;
+	TracerDelPoint *del_point;
+	TracerPointUp *point_up;
+	TracerPointDn *point_dn;
+	int dragging, pending_config;
+	float last_x, last_y;
+	TracerDrag *drag;
+	TracerDraw *draw;
+	TracerFill *fill;
+	BC_Title *title_r, *title_s;
+	TracerRadius *radius;
+	TracerScale *scale;
+	TracerPointList *point_list;
+	TracerReset *reset;
+};
+
+#endif
+
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch6 b/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch6
new file mode 100644
index 00000000..b4a5c3fb
--- /dev/null
+++ b/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch6
@@ -0,0 +1,11 @@
+diff -u a/libavcodec/vdpau_mpeg12.c b/libavcodec/vdpau_mpeg12.c
+--- a/libavcodec/vdpau_mpeg12.c	2018-11-02 18:17:29.000000000 -0600
++++ b/libavcodec/vdpau_mpeg12.c	2019-04-22 10:28:41.762275864 -0600
+@@ -114,6 +114,7 @@
+     .frame_priv_data_size = sizeof(struct vdpau_picture_context),
+     .init           = vdpau_mpeg1_init,
+     .uninit         = ff_vdpau_common_uninit,
++    .frame_params   = ff_vdpau_common_frame_params,
+     .priv_data_size = sizeof(VDPAUContext),
+     .caps_internal  = HWACCEL_CAP_ASYNC_SAFE,
+ };
-- 
2.26.2