From: Good Guy Date: Mon, 12 Nov 2018 20:37:03 +0000 (-0700) Subject: update ffmpeg to 4.1, add sketcher plugin, crikey tweaks, titler colorpicker, keyfram... X-Git-Tag: 2019-08~186 X-Git-Url: https://git.cinelerra-gg.org/?a=commitdiff_plain;h=83bfb86d01b353bab485d0ccc336e0572bcb63c6;p=goodguy%2Fcinelerra.git update ffmpeg to 4.1, add sketcher plugin, crikey tweaks, titler colorpicker, keyframe locks+tweaks --- diff --git a/cinelerra-5.1/cinelerra/colorpicker.C b/cinelerra-5.1/cinelerra/colorpicker.C index 1b40fcf0..097bcec8 100644 --- a/cinelerra-5.1/cinelerra/colorpicker.C +++ b/cinelerra-5.1/cinelerra/colorpicker.C @@ -43,8 +43,8 @@ ColorPicker::ColorPicker(int do_alpha, const char *title) this->title = title; this->do_alpha = do_alpha; this->do_okcancel = 0; - this->output = BLACK; - this->alpha = 255; + this->output = this->orig_color = BLACK; + this->alpha = this->orig_alpha = 255; } ColorPicker::~ColorPicker() @@ -63,6 +63,8 @@ void ColorPicker::start_window(int output, int alpha, int do_okcancel) } return; } + this->orig_color = output; + this->orig_alpha = alpha; this->output = output; this->alpha = alpha; this->do_okcancel = do_okcancel; diff --git a/cinelerra-5.1/cinelerra/colorpicker.h b/cinelerra-5.1/cinelerra/colorpicker.h index 0ae021ed..ed3f5a95 100644 --- a/cinelerra-5.1/cinelerra/colorpicker.h +++ b/cinelerra-5.1/cinelerra/colorpicker.h @@ -69,6 +69,7 @@ public: void update_gui(int output, int alpha); BC_Window* new_gui(); + int orig_color, orig_alpha; int output, alpha; int do_alpha, do_okcancel; const char *title; diff --git a/cinelerra-5.1/cinelerra/filexml.C b/cinelerra-5.1/cinelerra/filexml.C index 8914ff44..8b7315ca 100644 --- a/cinelerra-5.1/cinelerra/filexml.C +++ b/cinelerra-5.1/cinelerra/filexml.C @@ -46,6 +46,7 @@ XMLBuffer::XMLBuffer(long buf_size, long max_size, int del) lmt = bfr + bsz; isz = max_size; destroy = del; + share_lock = new Mutex("XMLBuffer::share_lock"); } XMLBuffer::XMLBuffer(const char *buf, long buf_size, int del) @@ -56,6 +57,7 @@ XMLBuffer::XMLBuffer(const char *buf, long buf_size, int del) lmt = inp = bfr+bsz; isz = bsz; destroy = del; + share_lock = new Mutex("XMLBuffer::share_lock"); } XMLBuffer::XMLBuffer(long buf_size, char *buf, int del) @@ -66,11 +68,13 @@ XMLBuffer::XMLBuffer(long buf_size, char *buf, int del) lmt = outp = bfr+bsz; isz = bsz; destroy = del; + share_lock = new Mutex("XMLBuffer::share_lock"); } XMLBuffer::~XMLBuffer() { if( destroy ) delete [] bfr; + delete share_lock; } int XMLBuffer::demand(long len) @@ -111,15 +115,13 @@ int XMLBuffer::read(char *bp, int len) void XMLBuffer::copy_from(XMLBuffer *xbuf) { - if( bsz != xbuf->bsz ) { delete [] bfr; bfr = 0; } - if( !bfr ) bfr = new unsigned char[bsz = xbuf->bsz]; - lmt = bfr + bsz; - long ilen = xbuf->otell(), olen = xbuf->itell(); - inp = pos(ilen); - outp = pos(olen); - if( ilen > 0 ) - memmove(bfr, xbuf->bfr, ilen); - destroy = xbuf->destroy; + xbuf->share_lock->lock("XMLBuffer::copy_from"); + share_lock->lock("XMLBuffer::copy_from"); + oseek(0); + write((const char*)xbuf->pos(0), xbuf->otell()); + iseek(xbuf->itell()); + xbuf->share_lock->unlock(); + share_lock->unlock(); } @@ -382,6 +384,7 @@ FileXML::FileXML(int coded) FileXML::~FileXML() { if( !shared ) delete buffer; + else buffer->share_lock->unlock(); delete [] output; } @@ -633,9 +636,10 @@ int FileXML::set_shared_input(XMLBuffer *xbuf) strcpy(this->filename, ""); delete buffer; buffer = xbuf; + xbuf->share_lock->lock("FileXML::set_shared_input"); xbuf->iseek(0); - set_coding(coded); shared = 1; + set_coding(coded); return 0; } @@ -644,9 +648,10 @@ int FileXML::set_shared_output(XMLBuffer *xbuf) strcpy(this->filename, ""); delete buffer; buffer = xbuf; + xbuf->share_lock->lock("FileXML::set_shared_output"); xbuf->oseek(0); - set_coding(coded); shared = 1; + set_coding(coded); return 0; } diff --git a/cinelerra-5.1/cinelerra/filexml.h b/cinelerra-5.1/cinelerra/filexml.h index 70950384..602a411e 100644 --- a/cinelerra-5.1/cinelerra/filexml.h +++ b/cinelerra-5.1/cinelerra/filexml.h @@ -27,6 +27,7 @@ #include #include "arraylist.h" +#include "mutex.h" #include "keyframe.inc" #include "filexml.inc" #include "sizes.h" @@ -38,9 +39,11 @@ class XMLBuffer long bsz, isz; unsigned char *inp, *outp, *bfr, *lmt; int destroy; + Mutex *share_lock; int demand(long len); friend class KeyFrame; + friend class FileXML; public: XMLBuffer(long buf_size=0x1000, long max_size=LONG_MAX, int del=1); XMLBuffer(long buf_size, char *buf, int del=0); // writing diff --git a/cinelerra-5.1/cinelerra/keyframe.C b/cinelerra-5.1/cinelerra/keyframe.C index faad855b..8d6a5755 100644 --- a/cinelerra-5.1/cinelerra/keyframe.C +++ b/cinelerra-5.1/cinelerra/keyframe.C @@ -37,6 +37,12 @@ KeyFrame::KeyFrame() xbuf = new XMLBuffer(); } +KeyFrame::KeyFrame(const char *buf, long len) + : Auto() +{ + xbuf = new XMLBuffer(buf, len, 0); +} + KeyFrame::KeyFrame(EDL *edl, KeyFrames *autos) : Auto(edl, (Autos*)autos) { diff --git a/cinelerra-5.1/cinelerra/keyframe.h b/cinelerra-5.1/cinelerra/keyframe.h index 89edf1f9..d766b915 100644 --- a/cinelerra-5.1/cinelerra/keyframe.h +++ b/cinelerra-5.1/cinelerra/keyframe.h @@ -37,6 +37,7 @@ class KeyFrame : public Auto KeyFrame &operator =(KeyFrame &k) { return k; } //illegal public: KeyFrame(); + KeyFrame(const char *buf, long len); KeyFrame(EDL *edl, KeyFrames *autos); virtual ~KeyFrame(); diff --git a/cinelerra-5.1/cinelerra/pluginclient.C b/cinelerra-5.1/cinelerra/pluginclient.C index 606f83d9..4e2c2984 100644 --- a/cinelerra-5.1/cinelerra/pluginclient.C +++ b/cinelerra-5.1/cinelerra/pluginclient.C @@ -43,13 +43,13 @@ #include "track.h" #include "transportque.inc" - +#include +#include +#include +#include +#include #include #include -#include - - - PluginClientThread::PluginClientThread(PluginClient *client) @@ -551,38 +551,38 @@ void PluginClient::load_defaults_xml() using_defaults = 1; //printf("PluginClient::load_defaults_xml %d %s\n", __LINE__, path); - KeyFrame temp_keyframe; - FILE *fp = fopen(path, "r"); - if( fp ) { - struct stat st; int fd = fileno(fp); - int64_t sz = !fstat(fd, &st) ? st.st_size : BCTEXTLEN; - char *data = temp_keyframe.get_data(sz+1); - int data_size = fread(data, 1, sz, fp); - if( data_size < 0 ) data_size = 0; - if( data_size > 0 ) { - data[data_size] = 0; - temp_keyframe.xbuf->oseek(data_size); + char *data = 0; + int64_t len = -1; + struct stat st; + int fd = open(path, O_RDONLY); + if( fd >= 0 && !fstat(fd, &st) ) { + int64_t sz = st.st_size; + data = new char[sz+1]; + len = read(fd, data, sz); + close(fd); + } + if( data && len >= 0 ) { + data[len] = 0; // Get window extents - int i = 0; - for( int state=0; i<(data_size-8) && state>=0; ++i ) { - if( !data[i] || data[i] == '<' ) break; - if( !isdigit(data[i]) ) continue; - if( !state ) { - window_x = atoi(data + i); - state = 1; - } - else { - window_y = atoi(data + i); - state = -1; - } - while( i=0; ++i ) { + if( !data[i] || data[i] == '<' ) break; + if( !isdigit(data[i]) ) continue; + if( !state ) { + window_x = atoi(data+i); + state = 1; + } + else { + window_y = atoi(data+i); + state = -1; } - temp_keyframe.xbuf->iseek(i); - read_data(&temp_keyframe); + while( i Main Menu (- is a checkbox) - 06/26/2018 update + 11/08/2018 update
@@ -1380,6 +1380,12 @@ Double click On fade/speed, synch video/audio ganged + +
+
+ Double MMB + On fade/speed, select keyframe position +
'U' @@ -1464,6 +1470,18 @@

+ + Patchbay + Shift/Move + Hold LMB + On Fade slider bar, sets gain to 100% or 0db + + +
+
+
+
+ Compositor
diff --git a/cinelerra-5.1/ffmpeg/plugin.opts b/cinelerra-5.1/ffmpeg/plugin.opts index dca6523b..27fcfaed 100644 --- a/cinelerra-5.1/ffmpeg/plugin.opts +++ b/cinelerra-5.1/ffmpeg/plugin.opts @@ -320,7 +320,6 @@ tlut2 ; new in 4.0 #acontrast Contrast=33 #afir -aiir #convolve #deconvolve drmeter @@ -334,3 +333,34 @@ normalize setrange #unpremultiply vfrdet +; new in 4.1 +acue +#adeclick +#adeclip +aderivative +afftdn +aintegral +amplify +chromahold +cue +deblock +fttdnoiz +#graphmonitor +greyedge +#highshelf +#lowshelf +lut1d +pal75bars +pal100bars +setparams +#sinc +tmix +vibrance +; broken in 4.1 +#acrossover +#aiir +#amultiply +#bm3d +#sr +#xstack +#agraphmonitor diff --git a/cinelerra-5.1/guicast/bcpopupmenu.C b/cinelerra-5.1/guicast/bcpopupmenu.C index 4c639fe2..2fb69d1c 100644 --- a/cinelerra-5.1/guicast/bcpopupmenu.C +++ b/cinelerra-5.1/guicast/bcpopupmenu.C @@ -53,11 +53,8 @@ BC_PopupMenu::BC_PopupMenu(int x, highlighted = popup_down = 0; menu_popup = 0; icon = 0; - if(margin >= 0) - this->margin = margin; - else - this->margin = BC_WindowBase::get_resources()->popupmenu_margin; - + this->margin = margin >= 0 ? margin : + BC_WindowBase::get_resources()->popupmenu_margin; this->use_title = use_title; strcpy(this->text, text); for(int i = 0; i < TOTAL_IMAGES; i++) @@ -80,6 +77,7 @@ BC_PopupMenu::BC_PopupMenu(int x, highlighted = popup_down = 0; menu_popup = 0; icon = 0; + this->margin = BC_WindowBase::get_resources()->popupmenu_margin; this->use_title = use_title; strcpy(this->text, text); for(int i = 0; i < TOTAL_IMAGES; i++) diff --git a/cinelerra-5.1/guicast/bctextbox.C b/cinelerra-5.1/guicast/bctextbox.C index a7c8f5f7..c404877c 100644 --- a/cinelerra-5.1/guicast/bctextbox.C +++ b/cinelerra-5.1/guicast/bctextbox.C @@ -2686,12 +2686,8 @@ int BC_TumbleTextBoxText::button_press_event() BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window, - int64_t default_value, - int64_t min, - int64_t max, - int x, - int y, - int text_w) + int64_t default_value, int64_t min, int64_t max, + int x, int y, int text_w) { reset(); this->x = x; @@ -2707,12 +2703,8 @@ BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window, } BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window, - int default_value, - int min, - int max, - int x, - int y, - int text_w) + int default_value, int min, int max, + int x, int y, int text_w) { reset(); this->x = x; @@ -2728,12 +2720,8 @@ BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window, } BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window, - float default_value_f, - float min_f, - float max_f, - int x, - int y, - int text_w) + float default_value_f, float min_f, float max_f, + int x, int y, int text_w, int precision) { reset(); this->x = x; @@ -2742,9 +2730,9 @@ BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window, this->max_f = max_f; this->default_value_f = default_value_f; this->text_w = text_w; + this->precision = precision; this->parent_window = parent_window; use_float = 1; - precision = 4; increment = 1; } diff --git a/cinelerra-5.1/guicast/bctextbox.h b/cinelerra-5.1/guicast/bctextbox.h index ffc5c0e4..70c99003 100644 --- a/cinelerra-5.1/guicast/bctextbox.h +++ b/cinelerra-5.1/guicast/bctextbox.h @@ -403,26 +403,14 @@ class BC_TumbleTextBox { public: BC_TumbleTextBox(BC_WindowBase *parent_window, - int64_t default_value, - int64_t min, - int64_t max, - int x, - int y, - int text_w); + int64_t default_value, int64_t min, int64_t max, + int x, int y, int text_w); BC_TumbleTextBox(BC_WindowBase *parent_window, - int default_value, - int min, - int max, - int x, - int y, - int text_w); + int default_value, int min, int max, + int x, int y, int text_w); BC_TumbleTextBox(BC_WindowBase *parent_window, - float default_value, - float min, - float max, - int x, - int y, - int text_w); + float default_value, float min, float max, + int x, int y, int text_w, int precision=4); virtual ~BC_TumbleTextBox(); int create_objects(); diff --git a/cinelerra-5.1/guicast/bcwindowbase.h b/cinelerra-5.1/guicast/bcwindowbase.h index ca73b39e..84cb0896 100644 --- a/cinelerra-5.1/guicast/bcwindowbase.h +++ b/cinelerra-5.1/guicast/bcwindowbase.h @@ -310,6 +310,7 @@ public: int get_dragging(); wchar_t* get_wkeystring(int *length = 0); int get_keypress(); + int get_keysym() { return keysym; } #ifdef X_HAVE_UTF8_STRING char* get_keypress_utf8(); #endif diff --git a/cinelerra-5.1/guicast/test9.C b/cinelerra-5.1/guicast/test9.C new file mode 100644 index 00000000..a777d7f2 --- /dev/null +++ b/cinelerra-5.1/guicast/test9.C @@ -0,0 +1,117 @@ +#include "bcsignals.h" +#include "guicast.h" +#include "pys_icon_png.h" + +class TestList : public BC_ListBox +{ +public: + TestList(int x, int y, int w, int h, + ArrayList *items); + int handle_event(); + int selection_changed(); +}; + +TestList::TestList(int x, int y, int w, int h, + ArrayList *items) + : BC_ListBox(x, y, w, h, LISTBOX_TEXT, items, + 0, 0, 1, 0, 0, LISTBOX_SINGLE, ICON_LEFT, 0) +{ +} + +int TestList::handle_event() +{ + printf("handle_event\n"); + return 1; +} + +int TestList::selection_changed() +{ + BC_ListBoxItem *item = get_selection(0, 0); + printf("selection_changed %s\n", !item ? "" : item->get_text()); + return 1; +} + +class TestWindow : public BC_Window +{ +public: + TestWindow() : BC_Window("Test9", 0, 0, 320, 240) {}; + void create_objects(); + int keypress_event(); + BC_ListBox *list; + ArrayList items; +}; + +void TestWindow::create_objects() +{ + lock_window("AWindowRemovePluginGUI::create_objects"); + set_color(BLACK); + set_font(LARGEFONT); + int x = 10, y = 20; + draw_text(x, y, "Hello world"); + y += 25; + BC_Button *ok_button = new BC_OKButton(this); + add_subwindow(ok_button); + BC_Button *cancel_button = new BC_CancelButton(this); + add_subwindow(cancel_button); + BC_ListBoxItem *thing; + ArrayList *sublist; + items.append(thing = new BC_ListBoxItem("thing 1")); + VFrame *pys_icon = new VFramePng(pys_icon_png); + thing->set_icon_vframe(pys_icon); + int pw = pys_icon->get_w(), ph = pys_icon->get_h(); + BC_Pixmap *pys_picon = new BC_Pixmap(this, pw, ph); + pys_picon->draw_vframe(pys_icon, 0, 0, pw, pw, 0, 0); + thing->set_icon(pys_picon); + sublist = thing->new_sublist(1); + BC_ListBoxItem *fish, *cat, *hat; + sublist->append(fish = new BC_ListBoxItem("fish")); + ArrayList *fish_list = fish->new_sublist(1); + fish_list->append(new BC_ListBoxItem("green")); + fish_list->append(new BC_ListBoxItem("eggs")); + fish_list->append(new BC_ListBoxItem("ham")); + sublist->append(cat = new BC_ListBoxItem("cat")); + ArrayList *cat_list = cat->new_sublist(1); + cat_list->append(new BC_ListBoxItem("videos")); + sublist->append(hat = new BC_ListBoxItem("hat")); + ArrayList *hat_list = hat->new_sublist(1); + hat_list->append(new BC_ListBoxItem("bonnet")); + hat_list->append(new BC_ListBoxItem("cap")); + hat_list->append(new BC_ListBoxItem("sombrero")); + items.append(thing = new BC_ListBoxItem("thing 2")); + int lw = get_w()-x-10, lh = ok_button->get_y() - y - 5; + add_subwindow(list = new TestList(x, y, lw, lh, &items)); + show_window(); + unlock_window(); +} + +int TestWindow::keypress_event() +{ + switch( get_keypress() ) { + case 'v': + switch( list->get_format() ) { + case LISTBOX_TEXT: + list->update_format(LISTBOX_ICONS, 1); + break; + case LISTBOX_ICONS: + list->update_format(LISTBOX_ICONS_PACKED, 1); + break; + case LISTBOX_ICONS_PACKED: + list->update_format(LISTBOX_ICON_LIST, 1); + break; + case LISTBOX_ICON_LIST: + list->update_format(LISTBOX_TEXT, 1); + break; + } + break; + } + return 1; +} + +int main() +{ + new BC_Signals; + TestWindow window; + window.create_objects(); + window.run_window(); +} + diff --git a/cinelerra-5.1/plugin_defs b/cinelerra-5.1/plugin_defs index 9d94dd20..a5f8b45a 100644 --- a/cinelerra-5.1/plugin_defs +++ b/cinelerra-5.1/plugin_defs @@ -92,6 +92,7 @@ video := \ rumbler \ scale \ scaleratio \ + sketcher \ seltempavg \ shapewipe \ sharpen \ diff --git a/cinelerra-5.1/plugins/Makefile b/cinelerra-5.1/plugins/Makefile index 125de335..7833156e 100644 --- a/cinelerra-5.1/plugins/Makefile +++ b/cinelerra-5.1/plugins/Makefile @@ -117,6 +117,7 @@ DIRS = $(OPENCV_OBJS) \ rotate \ rumbler \ scale \ + sketcher \ shapewipe \ sharpen \ shiftinterlace \ diff --git a/cinelerra-5.1/plugins/crikey/crikey.C b/cinelerra-5.1/plugins/crikey/crikey.C index 2f22fed9..46d2402f 100644 --- a/cinelerra-5.1/plugins/crikey/crikey.C +++ b/cinelerra-5.1/plugins/crikey/crikey.C @@ -334,11 +334,12 @@ void CriKey::read_data(KeyFrame *keyframe) void CriKey::update_gui() { if( !thread ) return; - if( !load_configuration() ) return; thread->window->lock_window("CriKey::update_gui"); CriKeyWindow *window = (CriKeyWindow*)thread->window; - window->update_gui(); - window->flush(); + if( load_configuration() ) { + window->update_gui(); + window->flush(); + } thread->window->unlock_window(); } diff --git a/cinelerra-5.1/plugins/crikey/crikeywindow.C b/cinelerra-5.1/plugins/crikey/crikeywindow.C index eaaa0b5c..8d6d8644 100644 --- a/cinelerra-5.1/plugins/crikey/crikeywindow.C +++ b/cinelerra-5.1/plugins/crikey/crikeywindow.C @@ -129,6 +129,8 @@ CriKeyWindow::CriKeyWindow(CriKey *plugin) CriKeyWindow::~CriKeyWindow() { + delete point_x; + delete point_y; } void CriKeyWindow::create_objects() @@ -192,21 +194,23 @@ void CriKeyWindow::send_configure_change() pending_config = 0; plugin->send_configure_change(); } -int CriKeyWindow::check_configure_change(int ret) + +int CriKeyWindow::grab_event(XEvent *event) { + int ret = do_grab_event(event); if( pending_config && !grab_event_count() ) send_configure_change(); return ret; } -int CriKeyWindow::grab_event(XEvent *event) +int CriKeyWindow::do_grab_event(XEvent *event) { switch( event->type ) { case ButtonPress: break; case ButtonRelease: break; case MotionNotify: break; default: - return check_configure_change(0); + return 0; } MWindow *mwindow = plugin->server->mwindow; @@ -219,25 +223,25 @@ int CriKeyWindow::grab_event(XEvent *event) if( !dragging ) { if( cx < 0 || cx >= mwindow->theme->ccanvas_w || cy < 0 || cy >= mwindow->theme->ccanvas_h ) - return check_configure_change(0); + return 0; } switch( event->type ) { case ButtonPress: - if( dragging ) return check_configure_change(0); + if( dragging ) return 0; if( event->xbutton.button == WHEEL_UP ) return threshold->wheel_event(1); if( event->xbutton.button == WHEEL_DOWN ) return threshold->wheel_event(-1); dragging = event->xbutton.state & ShiftMask ? -1 : 1; break; case ButtonRelease: - if( !dragging ) return check_configure_change(0); + if( !dragging ) return 0; dragging = 0; return 1; case MotionNotify: - if( !dragging ) return check_configure_change(0); + if( !dragging ) return 0; break; default: - return check_configure_change(0); + return 0; } float cursor_x = cx, cursor_y = cy; @@ -288,6 +292,7 @@ int CriKeyWindow::grab_event(XEvent *event) if( hot_point >= 0 && sz > 0 ) { CriKeyPoint *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); for( int i=0; ie = i==hot_point ? !pt->e : 0; @@ -332,10 +337,7 @@ int CriKeyWindow::grab_event(XEvent *event) } last_x = output_x; last_y = output_y; - if( !grab_event_count() ) - send_configure_change(); - else - pending_config = 1; + pending_config = 1; return 1; } diff --git a/cinelerra-5.1/plugins/crikey/crikeywindow.h b/cinelerra-5.1/plugins/crikey/crikeywindow.h index 7338d2dd..3e897f80 100644 --- a/cinelerra-5.1/plugins/crikey/crikeywindow.h +++ b/cinelerra-5.1/plugins/crikey/crikeywindow.h @@ -203,8 +203,8 @@ public: void update_gui(); void start_color_thread(); int grab_event(XEvent *event); + int do_grab_event(XEvent *event); void done_event(int result); - int check_configure_change(int ret); void send_configure_change(); CriKey *plugin; diff --git a/cinelerra-5.1/plugins/sketcher/Makefile b/cinelerra-5.1/plugins/sketcher/Makefile new file mode 100644 index 00000000..a53e62f1 --- /dev/null +++ b/cinelerra-5.1/plugins/sketcher/Makefile @@ -0,0 +1,13 @@ +include ../../plugin_defs + +OBJS := \ + $(OBJDIR)/sketcher.o \ + $(OBJDIR)/sketcherwindow.o + +PLUGIN = sketcher + +include ../../plugin_config + +$(OBJDIR)/sketcher.o: sketcher.C +$(OBJDIR)/sketcherwindow.o: sketcherwindow.C + diff --git a/cinelerra-5.1/plugins/sketcher/sketcher.C b/cinelerra-5.1/plugins/sketcher/sketcher.C new file mode 100644 index 00000000..b9d729c9 --- /dev/null +++ b/cinelerra-5.1/plugins/sketcher/sketcher.C @@ -0,0 +1,652 @@ +/* + * CINELERRA + * Copyright (C) 1997-2015 Adam Williams + * + * 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 +#include +#include +#include + +#include "arraylist.h" +#include "bccmodels.h" +#include "bccolors.h" +#include "clip.h" +#include "edlsession.h" +#include "filexml.h" +#include "sketcher.h" +#include "sketcherwindow.h" +#include "language.h" +#include "vframe.h" + +void SketcherPoint::init(int id, int x, int y) +{ + this->id = id; + this->x = x; this->y = y; +} +SketcherPoint::SketcherPoint(int id) +{ + init(id, 0, 0); +} +SketcherPoint::SketcherPoint(int id, int x, int y) +{ + init(id, x, y); +} +SketcherPoint::~SketcherPoint() +{ +} +SketcherPoint::SketcherPoint(SketcherPoint &pt) +{ + copy_from(pt); +} +int SketcherPoint::equivalent(SketcherPoint &that) +{ + return this->id == that.id && + this->x == that.x && + this->y == that.y ? 1 : 0; +} +void SketcherPoint::copy_from(SketcherPoint &that) +{ + this->id = that.id; + this->x = that.x; this->y = that.y; +} +void SketcherPoint::save_data(FileXML &output) +{ + char point[BCSTRLEN]; + sprintf(point,"/POINT_%d",id); + output.tag.set_title(point+1); + output.tag.set_property("X", x); + output.tag.set_property("Y", y); + output.append_tag(); + output.tag.set_title(point+0); + output.append_tag(); + output.append_newline(); +} +void SketcherPoint::read_data(FileXML &input) +{ + id = atoi(input.tag.get_title() + 6); + x = input.tag.get_property("X", 0.f); + y = input.tag.get_property("Y", 0.f); +} + +void SketcherCurve::init(int id, int ty, int radius, int pen, int color) +{ + this->id = id; + this->ty = ty; + this->radius = radius; + this->pen = pen; + this->color = color; +} +SketcherCurve::SketcherCurve(int id) +{ + init(id, 0, 1, 0, BLACK); +} +SketcherCurve::SketcherCurve(int id, int ty, int radius,int pen, int color) +{ + init(id, ty, radius, pen, color); +} +SketcherCurve::~SketcherCurve() +{ +} +SketcherCurve::SketcherCurve(SketcherCurve &cv) +{ + copy_from(cv); +} +int SketcherCurve::equivalent(SketcherCurve &that) +{ + if( this->id != that.id ) return 0; + if( this->ty != that.ty ) return 0; + if( this->radius != that.radius ) return 0; + if( this->pen != that.pen ) return 0; + if( this->color != that.color ) return 0; + int n = this->points.size(); + if( n != that.points.size() ) return 0; + for( int i=0; iequivalent(*that.points[i]) ) return 0; + } + return 1; +} +void SketcherCurve::copy_from(SketcherCurve &that) +{ + this->id = that.id; + this->ty = that.ty; + this->radius = that.radius; + this->pen = that.pen; + this->color = that.color; + int m = points.size(), n = that.points.size(); + while( m > n ) points.remove_object_number(--m); + while( m < n ) { points.append(new SketcherPoint()); ++m; } + for( int i=0; icopy_from(*that.points[i]); +} +void SketcherCurve::save_data(FileXML &output) +{ + this->ty = ty; + this->pen = pen; this->color = color; + char curve[BCSTRLEN]; + sprintf(curve,"/CURVE_%d",id); + output.tag.set_title(curve+1); + output.tag.set_property("TYPE", ty); + output.tag.set_property("RADIUS", radius); + output.tag.set_property("PEN", pen); + output.tag.set_property("COLOR", color); + output.append_tag(); + output.append_newline(); + for( int i=0,n=points.size(); isave_data(output); + output.tag.set_title(curve+0); + output.append_tag(); + output.append_newline(); +} +void SketcherCurve::read_data(FileXML &input) +{ + id = atoi(input.tag.get_title() + 6); + ty = input.tag.get_property("TYPE", 0); + radius = input.tag.get_property("RADIUS", 1.); + pen = input.tag.get_property("PEN", 0); + color = input.tag.get_property("COLOR", BLACK); +} + +int Sketcher::new_curve(int ty, int radius, int pen, int color) +{ + SketcherCurves &curves = config.curves; + int k = curves.size(), id = 1; + for( int i=k; --i>=0; ) { + int n = config.curves[i]->id; + if( n >= id ) id = n + 1; + } + SketcherCurve *cv = new SketcherCurve(id, ty, radius, pen, color); + curves.append(cv); + config.cv_selected = k; + return k; +} + +int Sketcher::new_curve() +{ + return new_curve(0, 1, 0, BLACK); +} + +int Sketcher::new_point(SketcherCurve *cv, int x, int y) +{ + int k = cv->points.size(), id = 1; + for( int i=k; --i>=0; ) { + int n = cv->points[i]->id; + if( n >= id ) id = n + 1; + } + SketcherPoint *pt = new SketcherPoint(id, x, y); + cv->points.append(pt); + return k; +} + +int Sketcher::new_point() +{ + int ci = config.cv_selected; + if( ci < 0 || ci >= config.curves.size() ) + return -1; + SketcherCurve *cv = config.curves[ci]; + EDLSession *session = get_edlsession(); + int x = !session ? 0.f : session->output_w / 2.f; + int y = !session ? 0.f : session->output_h / 2.f; + return new_point(cv, x, y); +} + +REGISTER_PLUGIN(Sketcher) + +SketcherConfig::SketcherConfig() +{ + drag = 1; + cv_selected = 0; + pt_selected = 0; +} +SketcherConfig::~SketcherConfig() +{ +} + +int SketcherConfig::equivalent(SketcherConfig &that) +{ + if( this->drag != that.drag ) return 0; + if( this->cv_selected != that.cv_selected ) return 0; + if( this->pt_selected != that.pt_selected ) return 0; + if( this->curves.size() != that.curves.size() ) return 0; + for( int i=0, n=curves.size(); iequivalent(*that.curves[i]) ) return 0; + } + return 1; +} + +void SketcherConfig::copy_from(SketcherConfig &that) +{ + this->drag = that.drag; + this->cv_selected = that.cv_selected; + this->pt_selected = that.pt_selected; + int m = curves.size(), n = that.curves.size(); + while( m > n ) curves.remove_object_number(--m); + while( m < n ) { curves.append(new SketcherCurve()); ++m; } + for( int i=0; icopy_from(*that.curves[i]); +} + +void SketcherConfig::interpolate(SketcherConfig &prev, SketcherConfig &next, + long prev_frame, long next_frame, long current_frame) +{ + this->cv_selected = prev.cv_selected; + this->pt_selected = prev.pt_selected; + this->drag = prev.drag; + + double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame); + double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame); + + curves.remove_all_objects(); + int prev_cv_sz = prev.curves.size(); + int next_cv_sz = next.curves.size(); + for( int i=0; i= 0 && pcv->id != (ncv=next.curves[k])->id ); + if( k >= 0 ) { + cv->id = pcv->id; + cv->ty = pcv->ty; + cv->radius = pcv->radius; + cv->pen = pcv->pen; + cv->color = pcv->color; + int prev_pt_sz = pcv->points.size(), next_pt_sz = ncv->points.size(); + for( int j=0; jpoints[j], *nt = 0; + k = next_pt_sz; // associated by id in next + while( --k >= 0 && pt.id != (nt=ncv->points[k])->id ); + int x = pt.x, y = pt.y; + if( k >= 0 ) { + x = x * prev_scale + nt->x * next_scale; + y = y * prev_scale + nt->y * next_scale; + } + cv->points.append(new SketcherPoint(pt.id, x, y)); + } + } + else + cv->copy_from(*pcv); + } +} + +void SketcherConfig::limits() +{ +} + + +Sketcher::Sketcher(PluginServer *server) + : PluginVClient(server) +{ +} + +Sketcher::~Sketcher() +{ +} + +const char* Sketcher::plugin_title() { return N_("Sketcher"); } +int Sketcher::is_realtime() { return 1; } + +NEW_WINDOW_MACRO(Sketcher, SketcherWindow); +LOAD_CONFIGURATION_MACRO(Sketcher, SketcherConfig) + +void Sketcher::save_data(KeyFrame *keyframe) +{ + FileXML output; +// cause data to be stored directly in text + output.set_shared_output(keyframe->xbuf); + + output.tag.set_title("SKETCHER"); + output.tag.set_property("DRAG", config.drag); + output.tag.set_property("CURVE_SELECTED", config.cv_selected); + output.tag.set_property("POINT_SELECTED", config.pt_selected); + output.append_tag(); + output.append_newline(); + for( int i=0,n=config.curves.size(); isave_data(output); + } + output.tag.set_title("/SKETCHER"); + output.append_tag(); + output.append_newline(); + output.terminate_string(); +} + +void Sketcher::read_data(KeyFrame *keyframe) +{ + FileXML input; + input.set_shared_input(keyframe->xbuf); + config.curves.remove_all_objects(); + int result = 0; + SketcherCurve *cv = 0; + + while( !(result=input.read_tag()) ) { + if( input.tag.title_is("SKETCHER") ) { + config.drag = input.tag.get_property("DRAG", config.drag); + config.cv_selected = input.tag.get_property("CV_SELECTED", 0); + config.pt_selected = input.tag.get_property("PT_SELECTED", 0); + } + else if( !strncmp(input.tag.get_title(),"CURVE_",6) ) { + cv = new SketcherCurve(); + cv->read_data(input); + config.curves.append(cv); + } + else if( !strncmp(input.tag.get_title(),"/CURVE_",7) ) + cv = 0; + else if( !strncmp(input.tag.get_title(),"POINT_",6) ) { + if( cv ) { + SketcherPoint *pt = new SketcherPoint(); + pt->read_data(input); + cv->points.append(pt); + } + else + printf("Sketcher::read_data: no curve for point\n"); + } + } + + if( !config.curves.size() ) { + new_curve(0, 1, 0, BLACK); + } + config.limits(); +} + +void Sketcher::update_gui() +{ + if( !thread ) return; + thread->window->lock_window("Sketcher::update_gui"); + if( load_configuration() ) { + SketcherWindow *window = (SketcherWindow*)thread->window; + window->update_gui(); + window->flush(); + } + thread->window->unlock_window(); +} + +void Sketcher::draw_point(VFrame *vfrm, SketcherPoint *pt, int color, int d) +{ + int r = d/2+1, x = pt->x, y = pt->y; + vfrm->draw_smooth(x-r,y+0, x-r, y-r, x+0,y-r); + vfrm->draw_smooth(x+0,y-r, x+r, y-r, x+r,y+0); + vfrm->draw_smooth(x+r,y+0, x+r, y+r, x+0,y+r); + vfrm->draw_smooth(x+0,y+r, x-r, y+r, x-r,y+0); + vfrm->set_pixel_color(color); + vfrm->draw_x(pt->x, pt->y, d); +} +void Sketcher::draw_point(VFrame *vfrm, SketcherPoint *pt, int color) +{ + draw_point(vfrm, pt, color, bmax(w,h)/200 + 2); +} + + +int SketcherPenSquare::draw_pixel(int x, int y) +{ + vfrm->draw_line(x-n, y, x+n, y); + for( int i=-n; idraw_line(x-n, y+i, x+n, y+i); + return 0; +} +int SketcherPenPlus::draw_pixel(int x, int y) +{ + if( n > 1 ) { + vfrm->draw_line(x-n, y, x+n, y); + vfrm->draw_line(x, y-n, x, y+n); + } + else + vfrm->draw_pixel(x, y); + return 0; +} +int SketcherPenSlant::draw_pixel(int x, int y) +{ + vfrm->draw_line(x-n, y+n, x+n, y-n); + vfrm->draw_line(x-n+1, y+n, x+n+1, y-n); + vfrm->draw_line(x-n, y+n+1, x+n, y-n+1); + return 0; +} +int SketcherPenXlant::draw_pixel(int x, int y) +{ + vfrm->draw_line(x-n, y+n, x+n, y-n); + vfrm->draw_line(x-n+1, y+n, x+n+1, y-n); + vfrm->draw_line(x-n, y+n+1, x+n, y-n+1); + vfrm->draw_line(x-n, y-n, x+n, y+n); + vfrm->draw_line(x-n+1, y-n, x+n+1, y+n); + vfrm->draw_line(x-n, y-n+1, x+n, y-n+1); + return 0; +} + + +VFrame *SketcherCurve::new_vpen(VFrame *out) +{ + switch( pen ) { + case PEN_SQUARE: return new SketcherPenSquare(out, radius); + case PEN_PLUS: return new SketcherPenPlus(out, radius); + case PEN_SLANT: return new SketcherPenSlant(out, radius); + case PEN_XLANT: return new SketcherPenXlant(out, radius); + } + return 0; +} + +void SketcherCurve::draw_line(VFrame *out) +{ + SketcherPoint *pt0 = points[0]; + VFrame *vpen = new_vpen(out); + out->set_pixel_color(color); + int n = points.size(); + if( n >= 2 ) { + for( int pi=1; pidraw_line(pt0->x, pt0->y, pt1->x, pt1->y); + pt0 = pt1; + } + } + else + vpen->draw_pixel(pt0->x, pt0->y); + delete vpen; +} + +/* +# python +from sympy import * +var("x,y, ax,ay, bx,by, cx,cy, dx,dy") + +var("abdx,abdy, acdx,acdy, bddx,bddy, cddx,cddy"); +abdx = bx-ax; abdy = by-ay; +acdx = cx-ax; acdy = cy-ay; +bddx = dx-bx; bddy = dy-by; +cddx = dx-cx; cddy = dy-cy; + +var("xc,yc, xd,yd, sx,sy"); +xc = (bx+dx)/2; yc = (by+dy)/2; +xd = cx-xc; yd = cy-yc; +ax = xc-xd; ay = yc-yd; +# line thru b with slope (c-a) intersects line thru c with slope (d-b) +sx = solve(((x - bx) * acdy/acdx + by) - ((x - cx) * bddy/bddx + cy),x) +sy = solve(((y - by) * acdx/acdy + bx) - ((y - cy) * bddx/bddy + cx),y) + +var("zx,zy, zdx,zdy, sx,sy, px,py, qx,qy"); +# point z = (b+c)/2 +zx = (bx+cx)/2; zy = (by+cy)/2; +zdx = (abdx+cddx)/2; zdy = (abdy+cddy)/2; +# line thru z with slope (d-a) intersects line thru b with slope (c-a) +px = solve(((x-zx)*zdy/zdx + zy) - ((x-bx) * acdy/acdx + by),x); +py = solve(((y-zy)*zdx/zdy + zx) - ((y-by) * acdx/acdy + bx),y); +# line thru z with slope (c-a + d-b)/2 intersects line thru c with slope (d-b) +qx = solve(((x-zx)*zdy/zdx + zy) - ((x-cx) * bddy/bddx + cy),x); +qy = solve(((y-zy)*zdx/zdy + zx) - ((y-cy) * bddx/bddy + cx),y); +*/ + +static void smooth_sxy( + float ax, float ay, float bx, float by, + float cx, float cy, float dx, float dy, + float &sx, float &sy) +{ + float acdx = cx-ax, acdy = cy-ay; + float bddx = dx-bx, bddy = dy-by; + float d = acdx*bddy - acdy*bddx; + if( fabsf(d) < 1 ) d = 1; + sx = (acdx*bddx*by - acdx*bddx*cy + acdx*bddy*cx - acdy*bddx*bx) / d; + sy = (acdx*bddy*by - acdy*bddx*cy - acdy*bddy*bx + acdy*bddy*cx) / d; + bclamp(sx, -32767.f, 32767.f); + bclamp(sy, -32767.f, 32767.f); +} + +static void smooth_pxy( + float ax, float ay, float bx, float by, + float cx, float cy, float dx, float dy, + float &px, float &py) +{ + float abdx = bx - ax, abdy = by - ay; + float acdx = cx - ax, acdy = cy - ay; + float cddx = dx - cx, cddy = dy - cy; + float d = (2*(abdx*acdy - abdy*acdx - acdx*cddy + acdy*cddx)); + if( fabsf(d) < 1 ) d = 1; + px = (-abdx*acdx*by + abdx*acdx*cy + 2*abdx*acdy*bx - abdy*acdx*bx - abdy*acdx*cx - + acdx*bx*cddy - acdx*by*cddx + acdx*cddx*cy - acdx*cddy*cx + 2*acdy*bx*cddx) / d; + py = (abdx*acdy*by + abdx*acdy*cy - 2*abdy*acdx*by + abdy*acdy*bx - abdy*acdy*cx - + 2*acdx*by*cddy + acdy*bx*cddy + acdy*by*cddx + acdy*cddx*cy - acdy*cddy*cx) / d; + bclamp(px, -32767.f, 32767.f); + bclamp(py, -32767.f, 32767.f); +} +static void smooth_qxy( + float ax, float ay, float bx, float by, + float cx, float cy, float dx, float dy, + float &qx, float &qy) +{ + float abdx = bx - ax, abdy = by - ay; + float bddx = dx - bx, bddy = dy - by; + float cddx = dx - cx, cddy = dy - cy; + float d = (2*(abdx*bddy - abdy*bddx - bddx*cddy + bddy*cddx)); + if( fabsf(d) < 1 ) d = 1; + qx = (abdx*bddx*by - abdx*bddx*cy + 2*abdx*bddy*cx - abdy*bddx*bx - abdy*bddx*cx - + bddx*bx*cddy + bddx*by*cddx - bddx*cddx*cy - bddx*cddy*cx + 2*bddy*cddx*cx) / d; + qy = (abdx*bddy*by + abdx*bddy*cy - 2*abdy*bddx*cy - abdy*bddy*bx + abdy*bddy*cx - + 2*bddx*cddy*cy - bddy*bx*cddy + bddy*by*cddx + bddy*cddx*cy + bddy*cddy*cx) / d; + bclamp(qx, -32767.f, 32767.f); + bclamp(qy, -32767.f, 32767.f); +} + + +static int convex(float ax,float ay, float bx,float by, + float cx,float cy, float dx,float dy) +{ + float abdx = bx - ax, abdy = by - ay; + float acdx = cx - ax, acdy = cy - ay; + float bcdx = cx - bx, bcdy = cy - by; + float bddx = dx - bx, bddy = dy - by; + float abc = abdx*acdy - abdy*acdx; + float bcd = bcdx*bddy - bcdy*bddx; + float v = abc * bcd; + return !v ? 0 : v>0 ? 1 : -1; +} + +void SketcherCurve::draw_smooth(VFrame *out) +{ + VFrame *vpen = new_vpen(out); + out->set_pixel_color(color); + int n = points.size(); + if( !n ) return; + if( n > 2 ) { + SketcherPoint *pt0 = points[0], *pt1 = points[1], *pt2 = points[2]; + float bx = pt0->x, by = pt0->y; + float cx = pt1->x, cy = pt1->y; + float dx = pt2->x, dy = pt2->y; + float xc = (bx+dx)/2.f, yc = (by+dy)/2.f; + float xd = cx - xc, yd = cy - yc; + float ax = xc - xd, ay = yc - yd; + float sx, sy; + for( int pi=0,n2=n-2; pix, dy = points[pi+2]->y; + if( convex(ax,ay, bx,by, cx,cy, dx,dy) >= 0 ) { + smooth_sxy(ax,ay, bx,by, cx,cy, dx,dy, sx, sy); + vpen->draw_smooth(bx,by, sx,sy, cx,cy); + } + else { + float zx = (bx+cx)/2.f, zy = (by+cy)/2.f; + smooth_pxy(ax,ay, bx,by, cx,cy, dx,dy, sx,sy); + vpen->draw_smooth(bx,by, sx,sy, zx,zy); + smooth_qxy(ax,ay, bx,by, cx,cy, dx,dy, sx,sy); + vpen->draw_smooth(zx,zy, sx,sy, cx,cy); + } + ax = bx; ay = by; + bx = cx; by = cy; + cx = dx; cy = dy; + } + xc = (ax+cx)/2.f; yc = (ay+cy)/2.f; + xd = bx - xc, yd = by - yc; + dx = xc - xd, dy = yc - yd; + smooth_sxy(ax, ay, bx, by, cx, cy, dx, dy, sx, sy); + vpen->draw_smooth(bx, by, sx, sy, cx, cy); + } + else if( n == 2 ) { + SketcherPoint *pt0 = points[0], *pt1 = points[1]; + vpen->draw_line(pt0->x, pt0->y, pt1->x, pt1->y); + } + else if( n > 0 ) { + SketcherPoint *pt0 = points[0]; + vpen->draw_pixel(pt0->x, pt0->y); + } + delete vpen; +} + +int Sketcher::process_realtime(VFrame *input, VFrame *output) +{ + this->input = input; this->output = output; + w = output->get_w(); h = output->get_h(); + if( input != output ) output->transfer_from(input); + + load_configuration(); + + for( int ci=0, n=config.curves.size(); cipoints.size(); + if( !m ) continue; + switch( cv->ty ) { + case TYP_OFF: + break; + case TYP_SKETCHER: + cv->draw_line(output); + break; + case TYP_SMOOTH: + cv->draw_smooth(output); + break; + } + } + + if( config.drag ) { + for( int ci=0, n=config.curves.size(); cipoints.size(); pipoints[pi], cv->color); + } + } + + return 0; +} + +void SketcherCurves::dump() +{ + for( int i=0; iid, cv_type[cv->ty], cv->radius, cv_pen[cv->pen], + (cv->color>>16)&0xff, (cv->color>>8)&0xff, (cv->color>>0)&0xff); + cv->points.dump(); + } +} +void SketcherPoints::dump() +{ + for( int i=0; iid, pt->x, pt->y); + } +} + diff --git a/cinelerra-5.1/plugins/sketcher/sketcher.h b/cinelerra-5.1/plugins/sketcher/sketcher.h new file mode 100644 index 00000000..d0429cb1 --- /dev/null +++ b/cinelerra-5.1/plugins/sketcher/sketcher.h @@ -0,0 +1,174 @@ +/* + * CINELERRA + * Copyright (C) 1997-2014 Adam Williams + * + * 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 __SKETCHERS_H__ +#define __SKETCHERS_H__ + +#include "pluginvclient.h" + +class Sketcher; + +enum { PT_ID, PT_X, PT_Y, PT_SZ }; +enum { CV_ID, CV_TY, CV_RAD, CV_PEN, CV_CLR, CV_SZ }; +enum { TYP_OFF, TYP_SKETCHER, TYP_SMOOTH, TYP_SZ }; +enum { PEN_SQUARE, PEN_PLUS, PEN_SLANT, PEN_XLANT, PEN_SZ }; + +class SketcherVPen : public VFrame +{ +public: + SketcherVPen(VFrame *vfrm, int n) + : VFrame(vfrm->get_data(), -1, vfrm->get_y()-vfrm->get_data(), + vfrm->get_u()-vfrm->get_data(), vfrm->get_v()-vfrm->get_data(), + vfrm->get_w(), vfrm->get_h(), vfrm->get_color_model(), + vfrm->get_bytes_per_line()) { + this->vfrm = vfrm; this->n = n; + } + virtual int draw_pixel(int x, int y) = 0; + VFrame *vfrm; + int n; +}; + +class SketcherPenSquare : public SketcherVPen +{ +public: + SketcherPenSquare(VFrame *vfrm, int n) : SketcherVPen(vfrm, n) {} + int draw_pixel(int x, int y); +}; +class SketcherPenPlus : public SketcherVPen +{ +public: + SketcherPenPlus(VFrame *vfrm, int n) : SketcherVPen(vfrm, n) {} + int draw_pixel(int x, int y); +}; +class SketcherPenSlant : public SketcherVPen +{ +public: + SketcherPenSlant(VFrame *vfrm, int n) : SketcherVPen(vfrm, n) {} + int draw_pixel(int x, int y); +}; +class SketcherPenXlant : public SketcherVPen +{ +public: + SketcherPenXlant(VFrame *vfrm, int n) : SketcherVPen(vfrm, n) {} + int draw_pixel(int x, int y); +}; + + +class SketcherPoint +{ +public: + int id; + int x, y; + + void init(int id, int x, int y); + SketcherPoint(int id, int x, int y); + SketcherPoint(int id=-1); + SketcherPoint(SketcherPoint &pt); + ~SketcherPoint(); + int equivalent(SketcherPoint &that); + void copy_from(SketcherPoint &that); + void save_data(FileXML &output); + void read_data(FileXML &input); +}; +class SketcherPoints : public ArrayList +{ +public: + SketcherPoints() {} + ~SketcherPoints() { remove_all_objects(); } + void dump(); +}; + +#define cv_type SketcherCurve::types +#define cv_pen SketcherCurve::pens + +class SketcherCurve +{ +public: + int id, ty, radius, pen, color; + static const char *types[TYP_SZ]; + static const char *pens[PEN_SZ]; + + SketcherPoints points; + + void init(int id, int ty, int radius, int pen, int color); + SketcherCurve(int id, int ty, int radius, int pen, int color); + SketcherCurve(int id=-1); + ~SketcherCurve(); + SketcherCurve(SketcherCurve &cv); + int equivalent(SketcherCurve &that); + void copy_from(SketcherCurve &that); + void save_data(FileXML &output); + void read_data(FileXML &input); + VFrame *new_vpen(VFrame *out); + void draw_line(VFrame *out); + void draw_smooth(VFrame *out); +}; +class SketcherCurves : public ArrayList +{ +public: + SketcherCurves() {} + ~SketcherCurves() { remove_all_objects(); } + void dump(); +}; + +class SketcherConfig +{ +public: + SketcherConfig(); + ~SketcherConfig(); + + SketcherCurves curves; + int equivalent(SketcherConfig &that); + void copy_from(SketcherConfig &that); + void interpolate(SketcherConfig &prev, SketcherConfig &next, + long prev_frame, long next_frame, long current_frame); + void limits(); + + int drag; + int cv_selected, pt_selected; +}; + +class Sketcher : public PluginVClient +{ +public: + Sketcher(PluginServer *server); + ~Sketcher(); +// required for all realtime plugins + PLUGIN_CLASS_MEMBERS2(SketcherConfig) + int is_realtime(); + void update_gui(); + void save_data(KeyFrame *keyframe); + void read_data(KeyFrame *keyframe); + int new_curve(int ty, int radius, int pen, int color); + int new_curve(); + int new_point(SketcherCurve *cv, int x, int y); + int new_point(); + int process_realtime(VFrame *input, VFrame *output); + static void draw_point(VFrame *vfrm, SketcherPoint *pt, int color, int d); + void draw_point(VFrame *vfrm, SketcherPoint *pt, int color); + + VFrame *input, *output; + int w, h, color_model, bpp, comp; + int is_yuv, is_float; +}; + +#endif diff --git a/cinelerra-5.1/plugins/sketcher/sketcherwindow.C b/cinelerra-5.1/plugins/sketcher/sketcherwindow.C new file mode 100644 index 00000000..5e55f29e --- /dev/null +++ b/cinelerra-5.1/plugins/sketcher/sketcherwindow.C @@ -0,0 +1,1218 @@ +/* + * CINELERRA + * Copyright (C) 2014 Adam Williams + * + * 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 "sketcher.h" +#include "sketcherwindow.h" +#include "cstrdup.h" +#include "cwindow.h" +#include "cwindowgui.h" +#include "edl.h" +#include "edlsession.h" +#include "keys.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 30 +#define COLOR_H 30 + +const char *SketcherCurve::types[] = { + N_("off"), + N_("line"), + N_("smooth"), +}; +const char *SketcherCurve::pens[] = { + N_("box"), + N_("+"), + N_("/"), + N_("X"), +}; + +SketcherCurveTypeItem::SketcherCurveTypeItem(int ty) + : BC_MenuItem(_(cv_type[ty])) +{ + this->ty = ty; +} +int SketcherCurveTypeItem::handle_event() +{ + SketcherCurveType *popup = (SketcherCurveType*)get_popup_menu(); + popup->update(ty); + SketcherWindow *gui = popup->gui; + SketcherConfig &config = gui->plugin->config; + int ci = config.cv_selected; + if( ci >= 0 && ci < config.curves.size() ) { + SketcherCurve *cv = config.curves[ci]; + cv->ty = ty; + gui->curve_list->update(ci); + gui->send_configure_change(); + } + return 1; +} + +SketcherCurveType::SketcherCurveType(SketcherWindow *gui, int x, int y, int ty) + : BC_PopupMenu(x,y,64,_(cv_type[ty])) +{ + this->gui = gui; +} +void SketcherCurveType::create_objects() +{ + int n = sizeof(cv_type)/sizeof(cv_type[0]); + for( int ty=0; typen = pen; +} +int SketcherCurvePenItem::handle_event() +{ + SketcherCurvePen *popup = (SketcherCurvePen*)get_popup_menu(); + popup->update(pen); + SketcherWindow *gui = popup->gui; + SketcherConfig &config = gui->plugin->config; + int ci = config.cv_selected; + if( ci >= 0 && ci < config.curves.size() ) { + SketcherCurve *cv = config.curves[ci]; + cv->pen = pen; + gui->curve_list->update(ci); + gui->send_configure_change(); + } + return 1; +} + +SketcherCurvePen::SketcherCurvePen(SketcherWindow *gui, int x, int y, int pen) + : BC_PopupMenu(x,y,72,_(cv_pen[pen])) +{ + this->gui = gui; +} +void SketcherCurvePen::create_objects() +{ + int n = sizeof(cv_pen)/sizeof(cv_pen[0]); + for( int pen=0; pengui = gui; + this->color = BLACK; + for( int i=0; i<3; ++i ) { + vframes[i] = new VFrame(w, w, BC_RGBA8888); + vframes[i]->clear_frame(); + } +} + +SketcherCurveColor::~SketcherCurveColor() +{ + for( int i=0; i<3; ++i ) + delete vframes[i]; +} + +void SketcherCurveColor::set_color(int color) +{ + this->color = color; + int r = (color>>16) & 0xff; + int g = (color>>8) & 0xff; + int b = (color>>0) & 0xff; + for( int i=0; i<3; ++i ) { + VFrame *vframe = vframes[i]; + int ww = vframe->get_w(), hh = vframe->get_h(); + int cx = (ww+1)/2, cy = hh/2; + double cc = (cx*cx + cy*cy) / 4.; + uint8_t *bp = vframe->get_data(), *dp = bp; + uint8_t *ep = dp + vframe->get_data_size(); + int rr = r, gg = g, bb = b; + int bpl = vframe->get_bytes_per_line(); + switch( i ) { + case BUTTON_UP: + break; + case BUTTON_UPHI: + if( (rr+=48) > 0xff ) rr = 0xff; + if( (gg+=48) > 0xff ) gg = 0xff; + if( (bb+=48) > 0xff ) bb = 0xff; + break; + case BUTTON_DOWNHI: + if( (rr-=48) < 0x00 ) rr = 0x00; + if( (gg-=48) < 0x00 ) gg = 0x00; + if( (bb-=48) < 0x00 ) bb = 0x00; + break; + } + while( dp < ep ) { + int yy = (dp-bp) / bpl, xx = ((dp-bp) % bpl) >> 2; + int dy = cy - yy, dx = cx - xx; + double s = dx*dx + dy*dy - cc; + double ss = s < 0 ? 1 : s >= cc ? 0 : 1 - s/cc; + int aa = ss * 0xff; + *dp++ = rr; *dp++ = gg; *dp++ = bb; *dp++ = aa; + } + } + set_images(vframes); +} + +void SketcherCurveColor::update_gui(int color) +{ + set_color(color); + draw_face(); +} + +int SketcherCurveColor::handle_event() +{ + gui->start_color_thread(this); + return 1; +} + +SketcherCurveColorPicker::SketcherCurveColorPicker(SketcherWindow *gui, SketcherCurveColor *color_button) + : ColorPicker(0, _("Color")) +{ + this->gui = gui; + this->color_button = color_button; + this->color = 0; + color_update = new SketcherCurveColorThread(this); +} + +SketcherCurveColorPicker::~SketcherCurveColorPicker() +{ + delete color_update; +} + +void SketcherCurveColorPicker::start(int color) +{ + start_window(color, 0, 1); + color_update->start(); +} + +void SketcherCurveColorPicker::handle_done_event(int result) +{ + color_update->stop(); + gui->lock_window("SketcherCurveColorPicker::handle_done_event"); + if( result ) color = orig_color; + color_button->update_gui(color); + gui->unlock_window(); + SketcherConfig &config = gui->plugin->config; + int ci = config.cv_selected; + if( ci >= 0 && ci < config.curves.size() ) { + SketcherCurve *cv = config.curves[ci]; + cv->color = color; + gui->curve_list->update(ci); + gui->send_configure_change(); + } +} + +int SketcherCurveColorPicker::handle_new_color(int color, int alpha) +{ + this->color = color; + color_update->update_lock->unlock(); + return 1; +} + +void SketcherCurveColorPicker::update_gui() +{ + gui->lock_window("SketcherCurveColorPicker::update_gui"); + color_button->update_gui(color); + SketcherConfig &config = gui->plugin->config; + int ci = config.cv_selected; + if( ci >= 0 ) { + SketcherCurve *cv = config.curves[ci]; + cv->color = color; + gui->curve_list->update(ci); + gui->send_configure_change(); + } + gui->unlock_window(); +} + +SketcherCurveColorThread::SketcherCurveColorThread(SketcherCurveColorPicker *color_picker) + : Thread(1, 0, 0) +{ + this->color_picker = color_picker; + this->update_lock = new Condition(0,"SketcherCurveColorThread::update_lock"); + done = 1; +} + +SketcherCurveColorThread::~SketcherCurveColorThread() +{ + stop(); + delete update_lock; +} + +void SketcherCurveColorThread::start() +{ + if( done ) { + done = 0; + Thread::start(); + } +} + +void SketcherCurveColorThread::stop() +{ + if( !done ) { + done = 1; + update_lock->unlock(); + join(); + } +} + +void SketcherCurveColorThread::run() +{ + while( !done ) { + update_lock->lock("SketcherCurveColorThread::run"); + if( done ) break; + color_picker->update_gui(); + } +} + + +SketcherNum::SketcherNum(SketcherWindow *gui, int x, int y, int output, + int mn, int mx) + : BC_TumbleTextBox(gui, output, mn, mx, x, y, 64) +{ + this->gui = gui; + set_increment(1); +} + +SketcherNum::~SketcherNum() +{ +} + +int SketcherPointX::handle_event() +{ + if( !SketcherNum::handle_event() ) return 0; + SketcherConfig &config = gui->plugin->config; + int ci = config.cv_selected; + if( ci >= 0 && ci < config.curves.size() ) { + SketcherCurve *cv = config.curves[ci]; + SketcherPointList *point_list = gui->point_list; + int hot_point = point_list->get_selection_number(0, 0); + SketcherPoints &points = cv->points; + if( hot_point >= 0 && hot_point < points.size() ) { + int v = atoi(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 SketcherPointY::handle_event() +{ + if( !SketcherNum::handle_event() ) return 0; + SketcherConfig &config = gui->plugin->config; + int ci = config.cv_selected; + if( ci >= 0 && ci < config.curves.size() ) { + SketcherCurve *cv = config.curves[ci]; + SketcherPointList *point_list = gui->point_list; + int hot_point = point_list->get_selection_number(0, 0); + SketcherPoints &points = cv->points; + if( hot_point >= 0 && hot_point < points.size() ) { + int v = atoi(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; +} + +int SketcherCurveRadius::handle_event() +{ + if( !SketcherNum::handle_event() ) return 0; + SketcherConfig &config = gui->plugin->config; + int ci = config.cv_selected; + if( ci >= 0 && ci < config.curves.size() ) { + SketcherCurve *cv = config.curves[ci]; + int v = atoi(get_text()); + cv->radius = v; + gui->curve_list->update(ci); + gui->send_configure_change(); + } + return 1; +} + + +SketcherWindow::SketcherWindow(Sketcher *plugin) + : PluginClientWindow(plugin, 380, 580, 380, 580, 0) +{ + this->plugin = plugin; + this->title_type = 0; this->curve_type = 0; + this->title_pen = 0; this->curve_pen = 0; + this->title_color = 0; this->curve_color = 0; + this->color_picker = 0; this->new_points = 0; + this->new_curve = 0; this->del_curve = 0; + this->curve_up = 0; this->curve_dn = 0; + 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->dragging = 0; + this->last_x = 0; this->last_y = 0; + this->point_list = 0; this->pending_config = 0; +} + +SketcherWindow::~SketcherWindow() +{ + delete curve_radius; + delete point_x; + delete point_y; + delete color_picker; +} + +void SketcherWindow::create_objects() +{ + int x = 10, y = 10, x1, y1; + int margin = plugin->get_theme()->widget_border; + BC_Title *title; + int ci = plugin->config.cv_selected; + if( ci < 0 || ci >= plugin->config.curves.size() ) + ci = plugin->new_curve(0, 1, 0, BLACK); + SketcherCurve *cv = plugin->config.curves[ci]; + add_subwindow(reset_curves = new SketcherResetCurves(this, plugin, x1=x, y+3)); + x1 += reset_curves->get_w() + 2*margin; + const char *curve_text = _("Curve"); + add_subwindow(title_radius = new BC_Title(x1, y, _("Width:"))); + x1 += title_radius->get_w() + margin; + curve_radius = new SketcherCurveRadius(this, x1, y, cv->radius); + curve_radius->create_objects(); + y += reset_curves->get_h() + 2*margin; + x1 = get_w()-x - BC_Title::calculate_w(this, curve_text, LARGEFONT); + y1 = y-margin - BC_Title::calculate_h(this, curve_text, LARGEFONT); + add_subwindow(title = new BC_Title(x1, y1, curve_text, LARGEFONT, + get_resources()->menu_highlighted_fontcolor)); + add_subwindow(curve_list = new SketcherCurveList(this, plugin, x, y)); + y += curve_list->get_h() + margin; + add_subwindow(title_type = new BC_Title(x, y, _("Type:"))); + x1 = x + title_type->get_w() + margin; + add_subwindow(curve_type = new SketcherCurveType(this, x1, y, cv->ty)); + curve_type->create_objects(); + x1 += curve_type->get_w() + margin; + add_subwindow(new_curve = new SketcherNewCurve(this, plugin, x1, y)); + x1 += new_curve->get_w() + margin; + add_subwindow(curve_up = new SketcherCurveUp(this, x1, y)); + x1 += curve_up->get_w() + 2*margin; + add_subwindow(title_color = new BC_Title(x1, y, _("Color:"))); + y += curve_type->get_h() + margin; + + add_subwindow(title_pen = new BC_Title(x, y, _("Pen:"))); + x1 = x + title_pen->get_w() + margin; + add_subwindow(curve_pen = new SketcherCurvePen(this, x1, y, cv->pen)); + curve_pen->create_objects(); + x1 += curve_pen->get_w() + margin; + add_subwindow(del_curve = new SketcherDelCurve(this, plugin, x1, y)); + x1 += del_curve->get_w() + margin; + add_subwindow(curve_dn = new SketcherCurveDn(this, x1, y)); + x1 += curve_dn->get_w() + 2*margin; + add_subwindow(curve_color = new SketcherCurveColor(this, x1, y, COLOR_W)); + curve_color->create_objects(); + curve_color->set_color(cv->color); + curve_color->draw_face(); + y += COLOR_H + margin; + curve_list->update(ci); + + BC_Bar *bar; + add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x)); + y += bar->get_h() + 2*margin; + + int pi = plugin->config.pt_selected; + SketcherPoint *pt = pi >= 0 && pi < cv->points.size() ? cv->points[pi] : 0; + add_subwindow(reset_points = new SketcherResetPoints(this, plugin, x1=x, y+3)); + x1 += reset_points->get_w() + 2*margin; + add_subwindow(drag = new SketcherDrag(this, x1, y)); + y += drag->get_h() + margin; + if( plugin->config.drag ) { + if( !grab(plugin->server->mwindow->cwindow->gui) ) + eprintf("drag enabled, but compositor already grabbed\n"); + } + const char *point_text = _("Point"); + x1 = get_w()-x - BC_Title::calculate_w(this, point_text, LARGEFONT); + y1 = y-margin - BC_Title::calculate_h(this, point_text, LARGEFONT); + add_subwindow(title = new BC_Title(x1, y1, point_text, LARGEFONT, + get_resources()->menu_highlighted_fontcolor)); + add_subwindow(point_list = new SketcherPointList(this, plugin, x, y)); + + y += point_list->get_h() + margin; + add_subwindow(title_x = new BC_Title(x, y, _("X:"))); + x1 = x + title_x->get_w() + margin; + point_x = new SketcherPointX(this, x1, y, !pt ? 0.f : pt->x); + point_x->create_objects(); + x1 += point_x->get_w() + 2*margin; + add_subwindow(new_point = new SketcherNewPoint(this, plugin, x1, y)); + x1 += new_point->get_w() + margin; + add_subwindow(point_up = new SketcherPointUp(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 SketcherPointY(this, x1, y, !pt ? 0.f : pt->y); + point_y->create_objects(); + x1 += point_y->get_w() + 2*margin; + add_subwindow(del_point = new SketcherDelPoint(this, plugin, x1, y)); + x1 += del_point->get_w() + margin; + add_subwindow(point_dn = new SketcherPointDn(this, x1, y)); + y += point_y->get_h() + margin + 10; + point_list->update(pi); + + add_subwindow(notes0 = new BC_Title(x, y, + _("\n" + "LMB=\n" + "Alt+LMB=\n" + "MMB=\n" + "DEL=\n"))); + add_subwindow(notes1 = new BC_Title(x+80, y, + _(" No Shift\n" + "select point\n" + "drag curve\n" + "next curve type\n" + "deletes point\n"))); + add_subwindow(notes2 = new BC_Title(x+200, y, + _(" Shift\n" + "append new points\n" + "drag image\n" + "append new curve\n" + "delete curve\n"))); + show_window(1); +} + +void SketcherWindow::send_configure_change() +{ + pending_config = 0; + plugin->send_configure_change(); +} + +int SketcherWindow::grab_event(XEvent *event) +{ + int ret = do_grab_event(event); + if( pending_config && !grab_event_count() ) + send_configure_change(); + return ret; +} + +int SketcherWindow::do_grab_event(XEvent *event) +{ + switch( event->type ) { + case ButtonPress: break; + case ButtonRelease: break; + case MotionNotify: break; + case KeyPress: + if( keysym_lookup(event) > 0 ) { + switch( get_keysym() ) { + case XK_Delete: + return (event->xkey.state & ShiftMask) ? + del_curve->handle_event() : + del_point->handle_event() ; + } + } // fall thru + 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 -= mwindow->theme->ccanvas_x; + cy -= mwindow->theme->ccanvas_y; + + if( !dragging ) { + if( cx < 0 || cx >= mwindow->theme->ccanvas_w || + cy < 0 || cy >= mwindow->theme->ccanvas_h ) + return 0; + } + + switch( event->type ) { + case ButtonPress: + if( dragging ) return 0; + dragging = event->xbutton.state & Mod1Mask ? -1 : 1; // alt_down + break; + case ButtonRelease: + case MotionNotify: + if( !dragging ) return 0; + break; + default: + return 0; + } + + + int ci = plugin->config.cv_selected; + if( ci < 0 || ci >= plugin->config.curves.size() ) + return 1; + + 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; + SketcherCurve *cv = plugin->config.curves[ci]; + SketcherPoints &points = cv->points; + int state = event->xmotion.state; + + switch( event->type ) { + case ButtonPress: { + if( dragging < 0 ) break; + int hot_point = -1; + int button_no = event->xbutton.button; + if( button_no == LEFT_BUTTON ) { +// create new point string + if( (state & ShiftMask) ) { + ++new_points; + hot_point = plugin->new_point(cv, output_x, output_y); + point_list->update(hot_point); + } + else { +// select point + int sz = points.size(); + int last_point = hot_point; + if( sz > 0 ) { + SketcherPoint *pt = points[hot_point=0]; + double dist = DISTANCE(output_x,output_y, pt->x,pt->y); + for( int i=1; ix,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 != last_point ) { + SketcherPoint *pt = 0; + if( hot_point >= 0 && hot_point < sz ) { + 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); + point_x->update(pt ? pt->x : 0.f); + point_y->update(pt ? pt->y : 0.f); + } + } + } + else if( button_no == MIDDLE_BUTTON ) { + if( (state & ShiftMask) ) { + int ci = plugin->new_curve(cv->ty, cv->radius, cv->pen, cv->color); + curve_list->update(ci); + point_list->update(-1); + } + else { + int ty = cv->ty + 1; + if( ty >= TYP_SZ ) ty = 0; + cv->ty = ty; + curve_type->update(ty); + curve_list->update(ci); + } + } + break; } + case MotionNotify: { + int hot_point = point_list->get_selection_number(0, 0); + if( dragging < 0 ) { + SketcherCurves &curves = plugin->config.curves; + int dx = round(output_x - last_x); + int dy = round(output_y - last_y); + int mnc = (state & ShiftMask) || ci<0 ? 0 : ci; + int mxc = (state & ShiftMask) ? curves.size() : ci+1; + for( int i=mnc; iconfig.curves[i]; + int pts = crv->points.size(); + for( int k=0; kpoints[k]; + pt->x += dx; pt->y += dy; + } + } + SketcherPoint *pt = hot_point >= 0 && hot_point < points.size() ? + points[hot_point] : 0; + point_x->update(pt ? pt->x : 0.f); + point_y->update(pt ? pt->y : 0.f); + point_list->update(hot_point); + break; + } + if( (state & Button1Mask) ) { + SketcherPoint *pt = hot_point >= 0 && hot_point < points.size() ? + points[hot_point] : 0; + if( pt && pt->x == output_x && pt->y == output_y ) break; + if( new_points ) { + if( pt ) { + float frac_w = DISTANCE(pt->x, pt->y, output_x, output_y) / get_w(); + if( frac_w < 0.01 ) break; // 1 percent w + } + if( (state & ShiftMask) ) { + ++new_points; + hot_point = plugin->new_point(cv, output_x, output_y); + point_list->update(hot_point); + } + } + else if( pt ) { + 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); + point_x->update(pt->x); + point_y->update(pt->y); + } + } + break; } + case ButtonRelease: { + new_points = 0; + dragging = 0; + break; } + } + + last_x = output_x; last_y = output_y; + pending_config = 1; + return 1; +} + +int SketcherWindow::keypress_event() +{ + int key = get_keypress(); + switch( key ) { + case DELETE: return shift_down() ? + del_curve->handle_event() : + del_point->handle_event() ; + } + return 0; +} + +void SketcherWindow::done_event(int result) +{ + ungrab(client->server->mwindow->cwindow->gui); +} + +void SketcherWindow::start_color_thread(SketcherCurveColor *color_button) +{ + unlock_window(); + delete color_picker; + color_picker = new SketcherCurveColorPicker(this, color_button); + int color = BLACK, ci = plugin->config.cv_selected; + if( ci >= 0 && ci < plugin->config.curves.size() ) { + SketcherCurve *cv = plugin->config.curves[ci]; + color = cv->color; + } + color_picker->start(color); + lock_window("SketcherWindow::start_color_thread"); +} + + +SketcherCurveList::SketcherCurveList(SketcherWindow *gui, Sketcher *plugin, int x, int y) + : BC_ListBox(x, y, 360, 130, LISTBOX_TEXT) +{ + this->gui = gui; + this->plugin = plugin; + titles[CV_ID] = _("id"); widths[CV_ID] = 64; + titles[CV_TY] = _("type"); widths[CV_TY] = 64; + titles[CV_RAD] = _("radius"); widths[CV_RAD] = 64; + titles[CV_PEN] = _("pen"); widths[CV_PEN] = 64; + titles[CV_CLR] = _("color"); widths[CV_CLR] = 64; +} +SketcherCurveList::~SketcherCurveList() +{ + clear(); +} +void SketcherCurveList::clear() +{ + for( int i=CV_SZ; --i>=0; ) + cols[i].remove_all_objects(); +} + +int SketcherCurveList::column_resize_event() +{ + for( int i=CV_SZ; --i>=0; ) + widths[i] = get_column_width(i); + return 1; +} + +int SketcherCurveList::handle_event() +{ + int ci = get_selection_number(0, 0); + set_selected(ci); + gui->point_list->update(0); + gui->send_configure_change(); + return 1; +} + +int SketcherCurveList::selection_changed() +{ + handle_event(); + return 1; +} + +void SketcherCurveList::set_selected(int k) +{ + int ci = -1; + if( k >= 0 && k < plugin->config.curves.size() ) { + SketcherCurve *cv = plugin->config.curves[k]; + gui->curve_type->update(cv->ty); + gui->curve_radius->update(cv->radius); + gui->curve_pen->update(cv->pen); + gui->curve_color->update_gui(cv->color); + ci = k; + } + plugin->config.cv_selected = ci; + update_selection(&cols[0], ci); + update_list(-1); +} + +void SketcherCurveList::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],CV_SZ, xpos,ypos,k); + center_selection(); +} + +void SketcherCurveList::update(int k) +{ + clear(); + SketcherCurves &curves = plugin->config.curves; + int sz = curves.size(); + for( int i=0; iid); + char ttxt[BCSTRLEN]; sprintf(ttxt,"%s", cv_type[cv->ty]); + char rtxt[BCSTRLEN]; sprintf(rtxt,"%d", cv->radius); + char ptxt[BCSTRLEN]; sprintf(ptxt,"%s", cv_pen[cv->pen]); + int color = cv->color; + int r = (color>>16)&0xff, g = (color>>8)&0xff, b = (color>>0)&0xff; + char ctxt[BCSTRLEN]; sprintf(ctxt,"#%02x%02x%02x", r, g, b); + add_curve(itxt, ttxt, rtxt, ptxt, ctxt); + } + set_selected(k); +} + +void SketcherCurveList::add_curve(const char *id, const char *type, + const char *radius, const char *pen, const char *color) +{ + cols[CV_ID].append(new BC_ListBoxItem(id)); + cols[CV_TY].append(new BC_ListBoxItem(type)); + cols[CV_RAD].append(new BC_ListBoxItem(radius)); + cols[CV_PEN].append(new BC_ListBoxItem(pen)); + cols[CV_CLR].append(new BC_ListBoxItem(color)); +} + +SketcherNewCurve::SketcherNewCurve(SketcherWindow *gui, Sketcher *plugin, int x, int y) + : BC_GenericButton(x, y, 64, _("New")) +{ + this->gui = gui; + this->plugin = plugin; +} +SketcherNewCurve::~SketcherNewCurve() +{ +} +int SketcherNewCurve::handle_event() +{ + int ty = 1, pen = 0, color = 0, radius = 1; + int ci = plugin->config.cv_selected; + if( ci >= 0 && ci < plugin->config.curves.size() ) { + SketcherCurve *cv = plugin->config.curves[ci]; + ty = cv->ty; pen = cv->pen; + color = cv->color; radius = cv->radius; + } + int k = plugin->new_curve(ty, radius, pen, color); + gui->curve_list->update(k); + gui->point_list->update(-1); + gui->send_configure_change(); + return 1; +} + +SketcherDelCurve::SketcherDelCurve(SketcherWindow *gui, Sketcher *plugin, int x, int y) + : BC_GenericButton(x, y, 64, C_("Del")) +{ + this->gui = gui; + this->plugin = plugin; +} +SketcherDelCurve::~SketcherDelCurve() +{ +} +int SketcherDelCurve::handle_event() +{ + int hot_curve = gui->curve_list->get_selection_number(0, 0); + SketcherCurves &curves = plugin->config.curves; + if( hot_curve >= 0 && hot_curve < curves.size() ) { + curves.remove_object_number(hot_curve); + if( --hot_curve < 0 ) + hot_curve = plugin->new_curve(0, 1, 0, BLACK); + gui->curve_list->update(hot_curve); + gui->point_list->update(-1); + gui->send_configure_change(); + } + return 1; +} + +SketcherCurveUp::SketcherCurveUp(SketcherWindow *gui, int x, int y) + : BC_GenericButton(x, y, _("Up")) +{ + this->gui = gui; +} +SketcherCurveUp::~SketcherCurveUp() +{ +} + +int SketcherCurveUp::handle_event() +{ + SketcherCurves &curves = gui->plugin->config.curves; + int hot_curve = gui->curve_list->get_selection_number(0, 0); + + if( hot_curve > 0 && hot_curve < curves.size() ) { + SketcherCurve *&cv0 = curves[hot_curve]; + SketcherCurve *&cv1 = curves[--hot_curve]; + SketcherCurve *t = cv0; cv0 = cv1; cv1 = t; + gui->curve_list->update(hot_curve); + } + gui->send_configure_change(); + return 1; +} + +SketcherCurveDn::SketcherCurveDn(SketcherWindow *gui, int x, int y) + : BC_GenericButton(x, y, _("Dn")) +{ + this->gui = gui; +} +SketcherCurveDn::~SketcherCurveDn() +{ +} + +int SketcherCurveDn::handle_event() +{ + SketcherCurves &curves = gui->plugin->config.curves; + int hot_curve = gui->curve_list->get_selection_number(0, 0); + + if( hot_curve >= 0 && hot_curve < curves.size()-1 ) { + SketcherCurve *&cv0 = curves[hot_curve]; + SketcherCurve *&cv1 = curves[++hot_curve]; + SketcherCurve *t = cv0; cv0 = cv1; cv1 = t; + gui->curve_list->update(hot_curve); + } + gui->send_configure_change(); + return 1; +} + + +SketcherPointList::SketcherPointList(SketcherWindow *gui, Sketcher *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; + titles[PT_ID] = _("ID"); widths[PT_ID] = 50; +} +SketcherPointList::~SketcherPointList() +{ + clear(); +} +void SketcherPointList::clear() +{ + for( int i=PT_SZ; --i>=0; ) + cols[i].remove_all_objects(); +} + +int SketcherPointList::column_resize_event() +{ + for( int i=PT_SZ; --i>=0; ) + widths[i] = get_column_width(i); + return 1; +} + +int SketcherPointList::handle_event() +{ + int pi = get_selection_number(0, 0); + set_selected(pi); + gui->send_configure_change(); + return 1; +} + +int SketcherPointList::selection_changed() +{ + handle_event(); + return 1; +} + +void SketcherPointList::add_point(const char *id, const char *xp, const char *yp) +{ + cols[PT_ID].append(new BC_ListBoxItem(id)); + cols[PT_X].append(new BC_ListBoxItem(xp)); + cols[PT_Y].append(new BC_ListBoxItem(yp)); +} + +void SketcherPointList::set_point(int i, int c, int v) +{ + char stxt[BCSTRLEN]; + sprintf(stxt,"%d",v); + set_point(i,c,stxt); +} +void SketcherPointList::set_point(int i, int c, const char *cp) +{ + cols[c].get(i)->set_text(cp); +} + +void SketcherPointList::set_selected(int k) +{ + SketcherPoint *pt = 0; + int ci = plugin->config.cv_selected, pi = -1; + if( ci >= 0 && ci < plugin->config.curves.size() ) { + SketcherCurve *cv = plugin->config.curves[ci]; + pt = k >= 0 && k < cv->points.size() ? cv->points[pi=k] : 0; + } + gui->point_x->update(pt ? pt->x : 0.f); + gui->point_y->update(pt ? pt->y : 0.f); + plugin->config.pt_selected = pi; + update_selection(&cols[0], pi); + update_list(k); +} +void SketcherPointList::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 SketcherPointList::update(int k) +{ + clear(); + int ci = plugin->config.cv_selected, sz = 0; + if( ci >= 0 && ci < plugin->config.curves.size() ) { + SketcherCurve *cv = plugin->config.curves[ci]; + SketcherPoints &points = cv->points; + sz = points.size(); + for( int i=0; iid); + char xtxt[BCSTRLEN]; sprintf(xtxt,"%d", pt->x); + char ytxt[BCSTRLEN]; sprintf(ytxt,"%d", pt->y); + add_point(itxt, xtxt, ytxt); + } + } + set_selected(k); +} + +void SketcherWindow::update_gui() +{ + SketcherConfig &config = plugin->config; + int ci = config.cv_selected; + int pi = config.pt_selected; + curve_list->update(ci); + point_list->update(pi); + SketcherCurve *cv = ci >= 0 ? config.curves[ci] : 0; + curve_radius->update(cv ? cv->radius : 1); + curve_type->update(cv ? cv->ty : TYP_OFF); + curve_pen->update(cv ? cv->pen : PEN_SQUARE); + curve_color->set_color(cv ? cv->color : BLACK); + SketcherPoint *pt = pi >= 0 ? cv->points[pi] : 0; + point_x->update(pt ? pt->x : 0); + point_y->update(pt ? pt->y : 0); + drag->update(plugin->config.drag); +} + + +SketcherPointUp::SketcherPointUp(SketcherWindow *gui, int x, int y) + : BC_GenericButton(x, y, _("Up")) +{ + this->gui = gui; +} +SketcherPointUp::~SketcherPointUp() +{ +} + +int SketcherPointUp::handle_event() +{ + SketcherConfig &config = gui->plugin->config; + int ci = config.cv_selected; + if( ci < 0 || ci >= config.curves.size() ) + return 1; + SketcherCurve *cv = config.curves[ci]; + SketcherPoints &points = cv->points; + int sz = points.size(); + int hot_point = gui->point_list->get_selection_number(0, 0); + + if( sz > 1 && hot_point > 0 ) { + SketcherPoint *&pt0 = points[hot_point]; + SketcherPoint *&pt1 = points[--hot_point]; + SketcherPoint *t = pt0; pt0 = pt1; pt1 = t; + gui->point_list->update(hot_point); + } + gui->send_configure_change(); + return 1; +} + +SketcherPointDn::SketcherPointDn(SketcherWindow *gui, int x, int y) + : BC_GenericButton(x, y, _("Dn")) +{ + this->gui = gui; +} +SketcherPointDn::~SketcherPointDn() +{ +} + +int SketcherPointDn::handle_event() +{ + SketcherConfig &config = gui->plugin->config; + int ci = config.cv_selected; + if( ci < 0 || ci >= config.curves.size() ) + return 1; + SketcherCurve *cv = config.curves[ci]; + SketcherPoints &points = cv->points; + int sz = points.size(); + int hot_point = gui->point_list->get_selection_number(0, 0); + if( sz > 1 && hot_point < sz-1 ) { + SketcherPoint *&pt0 = points[hot_point]; + SketcherPoint *&pt1 = points[++hot_point]; + SketcherPoint *t = pt0; pt0 = pt1; pt1 = t; + gui->point_list->update(hot_point); + } + gui->send_configure_change(); + return 1; +} + +SketcherDrag::SketcherDrag(SketcherWindow *gui, int x, int y) + : BC_CheckBox(x, y, gui->plugin->config.drag, _("Drag")) +{ + this->gui = gui; +} +int SketcherDrag::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; +} + +SketcherNewPoint::SketcherNewPoint(SketcherWindow *gui, Sketcher *plugin, int x, int y) + : BC_GenericButton(x, y, 64, _("New")) +{ + this->gui = gui; + this->plugin = plugin; +} +SketcherNewPoint::~SketcherNewPoint() +{ +} +int SketcherNewPoint::handle_event() +{ + int k = plugin->new_point(); + gui->point_list->update(k); + gui->send_configure_change(); + return 1; +} + +SketcherDelPoint::SketcherDelPoint(SketcherWindow *gui, Sketcher *plugin, int x, int y) + : BC_GenericButton(x, y, 64, C_("Del")) +{ + this->gui = gui; + this->plugin = plugin; +} +SketcherDelPoint::~SketcherDelPoint() +{ +} +int SketcherDelPoint::handle_event() +{ + SketcherConfig &config = gui->plugin->config; + int ci = config.cv_selected; + if( ci >= 0 && ci < config.curves.size() ) { + SketcherCurve *cv = config.curves[ci]; + SketcherPoints &points = cv->points; + int hot_point = gui->point_list->get_selection_number(0, 0); + if( hot_point >= 0 && hot_point < points.size() ) { + points.remove_object_number(hot_point); + gui->point_list->update(--hot_point); + gui->send_configure_change(); + } + } + return 1; +} + +SketcherResetCurves::SketcherResetCurves(SketcherWindow *gui, Sketcher *plugin, int x, int y) + : BC_GenericButton(x, y, _("Reset")) +{ + this->gui = gui; + this->plugin = plugin; +} +SketcherResetCurves::~SketcherResetCurves() +{ +} +int SketcherResetCurves::handle_event() +{ + SketcherConfig &config = plugin->config; + config.curves.remove_all_objects(); + int ci = plugin->new_curve(0, 1, 0, BLACK); + gui->curve_list->update(ci); + gui->point_list->update(-1); + gui->send_configure_change(); + return 1; +} + +SketcherResetPoints::SketcherResetPoints(SketcherWindow *gui, Sketcher *plugin, int x, int y) + : BC_GenericButton(x, y, _("Reset")) +{ + this->gui = gui; + this->plugin = plugin; +} +SketcherResetPoints::~SketcherResetPoints() +{ +} +int SketcherResetPoints::handle_event() +{ + SketcherConfig &config = gui->plugin->config; + int ci = config.cv_selected; + if( ci >= 0 && ci < config.curves.size() ) { + SketcherCurve *cv = config.curves[ci]; + cv->points.remove_all_objects(); + gui->point_list->update(-1); + gui->send_configure_change(); + } + return 1; +} + diff --git a/cinelerra-5.1/plugins/sketcher/sketcherwindow.h b/cinelerra-5.1/plugins/sketcher/sketcherwindow.h new file mode 100644 index 00000000..a0cc4ae2 --- /dev/null +++ b/cinelerra-5.1/plugins/sketcher/sketcherwindow.h @@ -0,0 +1,401 @@ +/* + * CINELERRA + * Copyright (C) 2008-2015 Adam Williams + * + * 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 __CRIKEYWINDOW_H__ +#define __CRIKEYWINDOW_H__ + +#include "guicast.h" +#include "colorpicker.h" + +class Sketcher; +class SketcherNum; +class SketcherCurveTypeItem; +class SketcherCurveType; +class SketcherCurvePenItem; +class SketcherCurvePen; +class SketcherCurveColor; +class SketcherCurveColorPicker; +class SketcherCurveColorThread; +class SketcherNewCurve; +class SketcherDelCurve; +class SketcherCurveUp; +class SketcherCurveDn; +class SketcherCurveRadius; +class SketcherCurveList; +class SketcherPointX; +class SketcherPointY; +class SketcherDrag; +class SketcherPointList; +class SketcherNewPoint; +class SketcherDelPoint; +class SketcherPointUp; +class SketcherPointDn; +class SketcherResetCurves; +class SketcherResetPoints; +class SketcherWindow; + +class SketcherNum : public BC_TumbleTextBox +{ +public: + SketcherWindow *gui; + + SketcherNum(SketcherWindow *gui, int x, int y, int output, + int mn=-32767, int mx=32767); + ~SketcherNum(); + int update(int v) { return BC_TumbleTextBox::update((int64_t)v); } +}; + +class SketcherCurveTypeItem : public BC_MenuItem +{ +public: + SketcherCurveTypeItem(int ty); + int handle_event(); + int ty; +}; + +class SketcherCurveType : public BC_PopupMenu +{ +public: + SketcherCurveType(SketcherWindow *gui, int x, int y, int ty); + void create_objects(); + void update(int ty); + + SketcherWindow *gui; +}; + +class SketcherCurvePenItem : public BC_MenuItem +{ +public: + SketcherCurvePenItem(int pen); + int handle_event(); + int pen; +}; + +class SketcherCurvePen : public BC_PopupMenu +{ +public: + SketcherCurvePen(SketcherWindow *gui, int x, int y, int pen); + void create_objects(); + void update(int ty); + + SketcherWindow *gui; +}; + +class SketcherCurveColor : public BC_Button +{ +public: + SketcherCurveColor(SketcherWindow *gui, int x, int y, int w); + ~SketcherCurveColor(); + + void set_color(int color); + void update_gui(int color); + int handle_event(); + + int color; + VFrame *vframes[3]; + SketcherWindow *gui; +}; + +class SketcherCurveColorPicker : public ColorPicker +{ +public: + SketcherCurveColorPicker(SketcherWindow *gui, SketcherCurveColor *curve_color); + ~SketcherCurveColorPicker(); + void start(int color); + int handle_new_color(int color, int alpha); + void update_gui(); + void handle_done_event(int result); + + SketcherWindow *gui; + int color; + SketcherCurveColor *color_button; + SketcherCurveColorThread *color_update; +}; + +class SketcherCurveColorThread : public Thread +{ +public: + SketcherCurveColorThread(SketcherCurveColorPicker *color_picker); + ~SketcherCurveColorThread(); + + void start(); + void stop(); + void run(); + + SketcherCurveColorPicker *color_picker; + int done; + Condition *update_lock; +}; + +class SketcherNewCurve : public BC_GenericButton +{ +public: + SketcherNewCurve(SketcherWindow *gui, Sketcher *plugin, int x, int y); + ~SketcherNewCurve(); + + int handle_event(); + + SketcherWindow *gui; + Sketcher *plugin; +}; + +class SketcherDelCurve : public BC_GenericButton +{ +public: + SketcherDelCurve(SketcherWindow *gui, Sketcher *plugin, int x, int y); + ~SketcherDelCurve(); + + int handle_event(); + + Sketcher *plugin; + SketcherWindow *gui; +}; + +class SketcherCurveUp : public BC_GenericButton +{ +public: + SketcherCurveUp(SketcherWindow *gui, int x, int y); + ~SketcherCurveUp(); + + int handle_event(); + + SketcherWindow *gui; +}; + +class SketcherCurveDn : public BC_GenericButton +{ +public: + SketcherCurveDn(SketcherWindow *gui, int x, int y); + ~SketcherCurveDn(); + + int handle_event(); + + SketcherWindow *gui; +}; + +class SketcherCurveRadius : public SketcherNum +{ +public: + SketcherCurveRadius(SketcherWindow *gui, int x, int y, float output) + : SketcherNum(gui, x, y, output, 0, 255) {} + ~SketcherCurveRadius() {} + + int handle_event(); +}; + +class SketcherCurveList : public BC_ListBox +{ +public: + SketcherCurveList(SketcherWindow *gui, Sketcher *plugin, int x, int y); + ~SketcherCurveList(); + + int handle_event(); + int selection_changed(); + int column_resize_event(); + ArrayList cols[CV_SZ]; + void clear(); + void add_curve(const char *id, const char *type, + const char *radius, const char *pen, const char *color); + void del_curve(int i); + void set_selected(int k); + void update(int k); + void update_list(int k); + + SketcherWindow *gui; + Sketcher *plugin; + const char *titles[CV_SZ]; + int widths[CV_SZ]; +}; + + +class SketcherPointX : public SketcherNum +{ +public: + SketcherPointX(SketcherWindow *gui, int x, int y, float output) + : SketcherNum(gui, x, y, output) {} + ~SketcherPointX() {} + + int handle_event(); +}; +class SketcherPointY : public SketcherNum +{ +public: + SketcherPointY(SketcherWindow *gui, int x, int y, float output) + : SketcherNum(gui, x, y, output) {} + ~SketcherPointY() {} + + int handle_event(); +}; + + +class SketcherDrag : public BC_CheckBox +{ +public: + SketcherDrag(SketcherWindow *gui, int x, int y); + + int handle_event(); + SketcherWindow *gui; +}; + + +class SketcherPointList : public BC_ListBox +{ +public: + SketcherPointList(SketcherWindow *gui, Sketcher *plugin, int x, int y); + ~SketcherPointList(); + + int handle_event(); + int selection_changed(); + int column_resize_event(); + ArrayList cols[PT_SZ]; + void clear(); + void add_point(const char *id, const char *xp, const char *yp); + void set_point(int i, int c, int v); + void set_point(int i, int c, const char *cp); + void set_selected(int k); + void update(int k); + void update_list(int k); + + + SketcherWindow *gui; + Sketcher *plugin; + const char *titles[PT_SZ]; + int widths[PT_SZ]; +}; + +class SketcherNewPoint : public BC_GenericButton +{ +public: + SketcherNewPoint(SketcherWindow *gui, Sketcher *plugin, int x, int y); + ~SketcherNewPoint(); + + int handle_event(); + + SketcherWindow *gui; + Sketcher *plugin; +}; + +class SketcherDelPoint : public BC_GenericButton +{ +public: + SketcherDelPoint(SketcherWindow *gui, Sketcher *plugin, int x, int y); + ~SketcherDelPoint(); + + int handle_event(); + + Sketcher *plugin; + SketcherWindow *gui; +}; + +class SketcherPointUp : public BC_GenericButton +{ +public: + SketcherPointUp(SketcherWindow *gui, int x, int y); + ~SketcherPointUp(); + + int handle_event(); + + SketcherWindow *gui; +}; + +class SketcherPointDn : public BC_GenericButton +{ +public: + SketcherPointDn(SketcherWindow *gui, int x, int y); + ~SketcherPointDn(); + + int handle_event(); + + SketcherWindow *gui; +}; + +class SketcherResetCurves : public BC_GenericButton +{ +public: + SketcherResetCurves(SketcherWindow *gui, Sketcher *plugin, int x, int y); + ~SketcherResetCurves(); + + int handle_event(); + + Sketcher *plugin; + SketcherWindow *gui; +}; + +class SketcherResetPoints : public BC_GenericButton +{ +public: + SketcherResetPoints(SketcherWindow *gui, Sketcher *plugin, int x, int y); + ~SketcherResetPoints(); + + int handle_event(); + + Sketcher *plugin; + SketcherWindow *gui; +}; + + +class SketcherWindow : public PluginClientWindow +{ +public: + SketcherWindow(Sketcher *plugin); + ~SketcherWindow(); + + void create_objects(); + void update_gui(); + void start_color_thread(SketcherCurveColor *curve_color); + int grab_event(XEvent *event); + int do_grab_event(XEvent *event); + void done_event(int result); + void send_configure_change(); + int keypress_event(); + + Sketcher *plugin; + + BC_Title *title_type, *title_pen; + BC_Title *title_color, *title_radius; + SketcherCurveType *curve_type; + SketcherCurvePen *curve_pen; + SketcherCurveColor *curve_color; + SketcherCurveColorPicker *color_picker; + SketcherNewCurve *new_curve; + SketcherDelCurve *del_curve; + SketcherCurveUp *curve_up; + SketcherCurveDn *curve_dn; + SketcherCurveRadius *curve_radius; + SketcherCurveList *curve_list; + SketcherResetCurves *reset_curves; + + BC_Title *title_x, *title_y; + SketcherPointX *point_x; + SketcherPointY *point_y; + SketcherNewPoint *new_point; + SketcherDelPoint *del_point; + SketcherPointUp *point_up; + SketcherPointDn *point_dn; + int dragging, pending_config; + int new_points; + float last_x, last_y; + SketcherDrag *drag; + SketcherPointList *point_list; + SketcherResetPoints *reset_points; + BC_Title *notes0, *notes1, *notes2; +}; +#endif + diff --git a/cinelerra-5.1/plugins/titler/titlerwindow.C b/cinelerra-5.1/plugins/titler/titlerwindow.C index bddb7993..c34e2903 100644 --- a/cinelerra-5.1/plugins/titler/titlerwindow.C +++ b/cinelerra-5.1/plugins/titler/titlerwindow.C @@ -785,7 +785,7 @@ TitleColorButton::TitleColorButton(TitleMain *client, TitleWindow *window, int x int TitleColorButton::handle_event() { window->color_thread->start_window(client->config.color, - client->config.alpha); + client->config.alpha, 1); return 1; } TitleOutlineColorButton::TitleOutlineColorButton(TitleMain *client, TitleWindow *window, int x, int y) @@ -797,7 +797,7 @@ TitleOutlineColorButton::TitleOutlineColorButton(TitleMain *client, TitleWindow int TitleOutlineColorButton::handle_event() { window->outline_color_thread->start_window(client->config.outline_color, - client->config.outline_alpha); + client->config.outline_alpha, 1); return 1; } @@ -1234,6 +1234,17 @@ int TitleColorThread::handle_new_color(int output, int alpha) return 1; } +void TitleColorThread::handle_done_event(int result) +{ + if( result ) { + client->config.color = orig_color; + client->config.alpha = orig_alpha; + handle_new_color(orig_color, orig_alpha); + window->update_color(); + window->send_configure_change(); + } +} + TitleDrag::TitleDrag(TitleMain *client, TitleWindow *window, int x, int y) : DragCheckBox(client->server->mwindow, x, y, _("Drag"), &client->config.drag, client->config.title_x, client->config.title_y, @@ -1528,9 +1539,10 @@ int TitleColorPopup::activate() } void TitleColorPopup::handle_done_event(int result) { - if( result ) return; - char txt[BCSTRLEN]; sprintf(txt, "<%s #%06x>", _(KW_COLOR), color_value); - window->insert_ibeam(txt); + if( !result ) { + char txt[BCSTRLEN]; sprintf(txt, "<%s #%06x>", _(KW_COLOR), color_value); + window->insert_ibeam(txt); + } } TitlePngPopup::TitlePngPopup(TitleMain *client, TitleWindow *window) diff --git a/cinelerra-5.1/plugins/titler/titlerwindow.h b/cinelerra-5.1/plugins/titler/titlerwindow.h index 99cd3e18..4dcaa6dd 100644 --- a/cinelerra-5.1/plugins/titler/titlerwindow.h +++ b/cinelerra-5.1/plugins/titler/titlerwindow.h @@ -503,6 +503,7 @@ class TitleColorThread : public ColorPicker public: TitleColorThread(TitleMain *client, TitleWindow *window, int is_outline); virtual int handle_new_color(int output, int alpha); + void handle_done_event(int result); TitleMain *client; TitleWindow *window; int is_outline; diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch0 b/cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch0 deleted file mode 100644 index 5dd31100..00000000 --- a/cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch0 +++ /dev/null @@ -1,11 +0,0 @@ -diff -urN a/fftools/cmdutils.c b/fftools/cmdutils.c ---- a/fftools/cmdutils.c 2018-10-01 10:52:48.866784675 -0600 -+++ b/fftools/cmdutils.c 2018-10-01 10:52:55.550799827 -0600 -@@ -1179,6 +1179,7 @@ - - void show_banner(int argc, char **argv, const OptionDef *options) - { -+ return; - int idx = locate_option(argc, argv, options, "version"); - if (hide_banner || idx) - return; diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch1 b/cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch1 deleted file mode 100644 index 831ea60f..00000000 --- a/cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch1 +++ /dev/null @@ -1,12 +0,0 @@ -diff -urN a/libavformat/bluray.c b/libavformat/bluray.c ---- a/libavformat/bluray.c 2018-04-13 17:34:28.000000000 -0600 -+++ b/libavformat/bluray.c 2018-04-24 11:02:19.724232178 -0600 -@@ -28,7 +28,7 @@ - #include "libavutil/opt.h" - - #define BLURAY_PROTO_PREFIX "bluray:" --#define MIN_PLAYLIST_LENGTH 180 /* 3 min */ -+#define MIN_PLAYLIST_LENGTH 0 - - typedef struct { - const AVClass *class; diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch2 b/cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch2 deleted file mode 100644 index e05f3372..00000000 --- a/cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch2 +++ /dev/null @@ -1,498 +0,0 @@ -diff -urN a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c ---- a/libavformat/mpegtsenc.c 2018-04-20 04:02:57.000000000 -0600 -+++ b/libavformat/mpegtsenc.c 2018-04-24 10:27:57.193689213 -0600 -@@ -56,9 +56,8 @@ - int sid; /* service ID */ - char *name; - char *provider_name; -- int pcr_pid; -- int pcr_packet_count; -- int pcr_packet_period; -+ int64_t pcr, pcr_packet_timer, pcr_packet_period; -+ int pcr_sid, pcr_pid; - AVProgram *program; - } MpegTSService; - -@@ -78,14 +77,12 @@ - MpegTSSection pat; /* MPEG-2 PAT table */ - MpegTSSection sdt; /* MPEG-2 SDT table context */ - MpegTSService **services; -- int sdt_packet_count; -- int sdt_packet_period; -- int pat_packet_count; -- int pat_packet_period; -+ int64_t sdt_packet_timer, sdt_packet_period; -+ int64_t pat_packet_timer, pat_packet_period; - int nb_services; - int onid; - int tsid; -- int64_t first_pcr; -+ int64_t pcr, first_pcr, delay; - int mux_rate; ///< set to 1 when VBR - int pes_payload_size; - -@@ -95,12 +92,14 @@ - int service_type; - - int pmt_start_pid; -+ int pcr_start_pid; - int start_pid; - int m2ts_mode; -+ int64_t ts_offset; - - int reemit_pat_pmt; // backward compatibility - -- int pcr_period; -+ double pcr_period; - #define MPEGTS_FLAG_REEMIT_PAT_PMT 0x01 - #define MPEGTS_FLAG_AAC_LATM 0x02 - #define MPEGTS_FLAG_PAT_PMT_AT_FRAMES 0x04 -@@ -111,8 +110,6 @@ - int tables_version; - double pat_period; - double sdt_period; -- int64_t last_pat_ts; -- int64_t last_sdt_ts; - - int omit_video_pes_length; - } MpegTSWrite; -@@ -222,10 +219,10 @@ - #define DEFAULT_PROVIDER_NAME "FFmpeg" - #define DEFAULT_SERVICE_NAME "Service01" - --/* we retransmit the SI info at this rate */ -+/* we retransmit the SI info at this rate (ms) */ - #define SDT_RETRANS_TIME 500 - #define PAT_RETRANS_TIME 100 --#define PCR_RETRANS_TIME 20 -+#define PCR_RETRANS_TIME 50 - - typedef struct MpegTSWriteStream { - struct MpegTSService *service; -@@ -721,6 +718,7 @@ - service->pmt.pid = ts->pmt_start_pid + ts->nb_services; - service->sid = sid; - service->pcr_pid = 0x1fff; -+ service->pcr_sid = 0x1fff; - service->provider_name = av_strdup(provider_name); - service->name = av_strdup(name); - if (!service->provider_name || !service->name) -@@ -736,18 +734,11 @@ - return NULL; - } - --static int64_t get_pcr(const MpegTSWrite *ts, AVIOContext *pb) --{ -- return av_rescale(avio_tell(pb) + 11, 8 * PCR_TIME_BASE, ts->mux_rate) + -- ts->first_pcr; --} -- - static void mpegts_prefix_m2ts_header(AVFormatContext *s) - { - MpegTSWrite *ts = s->priv_data; - if (ts->m2ts_mode) { -- int64_t pcr = get_pcr(s->priv_data, s->pb); -- uint32_t tp_extra_header = pcr % 0x3fffffff; -+ uint32_t tp_extra_header = ts->pcr % 0x3fffffff; - tp_extra_header = AV_RB32(&tp_extra_header); - avio_write(s->pb, (unsigned char *) &tp_extra_header, - sizeof(tp_extra_header)); -@@ -768,6 +759,7 @@ - MpegTSService *service; - AVStream *st, *pcr_st = NULL; - AVDictionaryEntry *title, *provider; -+ double clk_rate; - int i, j; - const char *service_name; - const char *provider_name; -@@ -776,6 +768,15 @@ - - if (s->max_delay < 0) /* Not set by the caller */ - s->max_delay = 0; -+ ts->delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE); -+ -+ if (ts->m2ts_mode == -1) { -+ if (av_match_ext(s->url, "m2ts")) { -+ ts->m2ts_mode = 1; -+ } else { -+ ts->m2ts_mode = 0; -+ } -+ } - - // round up to a whole number of TS packets - ts->pes_payload_size = (ts->pes_payload_size + 14 + 183) / 184 * 184 - 14; -@@ -822,6 +823,8 @@ - service->program = program; - } - } -+ if (ts->m2ts_mode > 1) -+ service->pmt.pid = 0x00ff + ts->service_id; - - ts->pat.pid = PAT_PID; - /* Initialize at 15 so that it wraps and is equal to 0 for the -@@ -907,10 +910,9 @@ - ts_st->discontinuity = ts->flags & MPEGTS_FLAG_DISCONT; - /* update PCR pid by using the first video stream */ - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && -- service->pcr_pid == 0x1fff) { -- service->pcr_pid = ts_st->pid; -+ service->pcr_sid == 0x1fff) - pcr_st = st; -- } -+ - if (st->codecpar->codec_id == AV_CODEC_ID_AAC && - st->codecpar->extradata_size > 0) { - AVStream *ast; -@@ -946,78 +948,47 @@ - av_freep(&pids); - - /* if no video stream, use the first stream as PCR */ -- if (service->pcr_pid == 0x1fff && s->nb_streams > 0) { -- pcr_st = s->streams[0]; -- ts_st = pcr_st->priv_data; -- service->pcr_pid = ts_st->pid; -- } else -- ts_st = pcr_st->priv_data; -- -- if (ts->mux_rate > 1) { -- service->pcr_packet_period = (int64_t)ts->mux_rate * ts->pcr_period / -- (TS_PACKET_SIZE * 8 * 1000); -- ts->sdt_packet_period = (int64_t)ts->mux_rate * SDT_RETRANS_TIME / -- (TS_PACKET_SIZE * 8 * 1000); -- ts->pat_packet_period = (int64_t)ts->mux_rate * PAT_RETRANS_TIME / -- (TS_PACKET_SIZE * 8 * 1000); -- -- if (ts->copyts < 1) -- ts->first_pcr = av_rescale(s->max_delay, PCR_TIME_BASE, AV_TIME_BASE); -- } else { -- /* Arbitrary values, PAT/PMT will also be written on video key frames */ -- ts->sdt_packet_period = 200; -- ts->pat_packet_period = 40; -- if (pcr_st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { -- int frame_size = av_get_audio_frame_duration2(pcr_st->codecpar, 0); -- if (!frame_size) { -- av_log(s, AV_LOG_WARNING, "frame size not set\n"); -- service->pcr_packet_period = -- pcr_st->codecpar->sample_rate / (10 * 512); -- } else { -- service->pcr_packet_period = -- pcr_st->codecpar->sample_rate / (10 * frame_size); -- } -- } else { -- // max delta PCR 0.1s -- // TODO: should be avg_frame_rate -- service->pcr_packet_period = -- ts_st->user_tb.den / (10 * ts_st->user_tb.num); -- } -- if (!service->pcr_packet_period) -- service->pcr_packet_period = 1; -- } -- -- ts->last_pat_ts = AV_NOPTS_VALUE; -- ts->last_sdt_ts = AV_NOPTS_VALUE; -- // The user specified a period, use only it -- if (ts->pat_period < INT_MAX/2) { -- ts->pat_packet_period = INT_MAX; -+ if (!pcr_st && s->nb_streams > 0) -+ pcr_st = s->streams[0]; -+ if (!pcr_st) { -+ av_log(s, AV_LOG_ERROR, "no streams\n"); -+ ret = AVERROR(EINVAL); -+ goto fail; - } -- if (ts->sdt_period < INT_MAX/2) { -- ts->sdt_packet_period = INT_MAX; -+ ts_st = pcr_st->priv_data; -+ if (service->pcr_sid == 0x1fff) -+ service->pcr_sid = ts_st->pid; -+ if (service->pcr_pid == 0x1fff) -+ service->pcr_pid = ts->m2ts_mode > 1 ? -+ 0x1000 + ts->service_id : service->pcr_sid ; -+ if (service->pmt.pid == service->pcr_pid) { -+ av_log(s, AV_LOG_ERROR, "Duplicate stream id %d\n", service->pcr_pid); -+ ret = AVERROR(EINVAL); -+ goto fail; - } - -+ clk_rate = ts->mux_rate > 1 ? ts->mux_rate : PCR_TIME_BASE; -+ ts->sdt_packet_period = ts->sdt_period < 0 ? -1 : ts->sdt_period/1000 * clk_rate; -+ ts->pat_packet_period = ts->pat_period/1000 * clk_rate; -+ service->pcr_packet_period = ts->pcr_period/1000 * clk_rate; -+ if (service->pcr_packet_period < (TS_PACKET_SIZE*8*10)) -+ service->pcr_packet_period = (TS_PACKET_SIZE*8*10); -+ av_log(s, AV_LOG_VERBOSE, "clk_rate %f: ticks/pkt %d pcr, %d sdt, %d pmt\n", clk_rate, -+ (int)service->pcr_packet_period, (int)ts->sdt_packet_period, (int)ts->pat_packet_period); -+ -+ if (ts->copyts < 1) -+ ts->first_pcr = av_rescale(s->max_delay, PCR_TIME_BASE, AV_TIME_BASE); -+ - // output a PCR as soon as possible -- service->pcr_packet_count = service->pcr_packet_period; -- ts->pat_packet_count = ts->pat_packet_period - 1; -- ts->sdt_packet_count = ts->sdt_packet_period - 1; -+ ts->pcr = 0; -+ service->pcr_packet_timer = 0; -+ ts->pat_packet_timer = 0; -+ ts->sdt_packet_timer = 0; - - if (ts->mux_rate == 1) - av_log(s, AV_LOG_VERBOSE, "muxrate VBR, "); - else - av_log(s, AV_LOG_VERBOSE, "muxrate %d, ", ts->mux_rate); -- av_log(s, AV_LOG_VERBOSE, -- "pcr every %d pkts, sdt every %d, pat/pmt every %d pkts\n", -- service->pcr_packet_period, -- ts->sdt_packet_period, ts->pat_packet_period); -- -- if (ts->m2ts_mode == -1) { -- if (av_match_ext(s->url, "m2ts")) { -- ts->m2ts_mode = 1; -- } else { -- ts->m2ts_mode = 0; -- } -- } - - return 0; - -@@ -1032,22 +1003,12 @@ - MpegTSWrite *ts = s->priv_data; - int i; - -- if (++ts->sdt_packet_count == ts->sdt_packet_period || -- (dts != AV_NOPTS_VALUE && ts->last_sdt_ts == AV_NOPTS_VALUE) || -- (dts != AV_NOPTS_VALUE && dts - ts->last_sdt_ts >= ts->sdt_period*90000.0) -- ) { -- ts->sdt_packet_count = 0; -- if (dts != AV_NOPTS_VALUE) -- ts->last_sdt_ts = FFMAX(dts, ts->last_sdt_ts); -+ if ( ts->sdt_packet_period >= 0 && ts->pcr >= ts->sdt_packet_timer ) { -+ ts->sdt_packet_timer = ts->pcr + ts->sdt_packet_period; - mpegts_write_sdt(s); - } -- if (++ts->pat_packet_count == ts->pat_packet_period || -- (dts != AV_NOPTS_VALUE && ts->last_pat_ts == AV_NOPTS_VALUE) || -- (dts != AV_NOPTS_VALUE && dts - ts->last_pat_ts >= ts->pat_period*90000.0) || -- force_pat) { -- ts->pat_packet_count = 0; -- if (dts != AV_NOPTS_VALUE) -- ts->last_pat_ts = FFMAX(dts, ts->last_pat_ts); -+ if (ts->pcr >= ts->pat_packet_timer || force_pat) { -+ ts->pat_packet_timer = ts->pcr + ts->pat_packet_period; - mpegts_write_pat(s); - for (i = 0; i < ts->nb_services; i++) - mpegts_write_pmt(s, ts->services[i]); -@@ -1089,13 +1050,14 @@ - { - MpegTSWrite *ts = s->priv_data; - MpegTSWriteStream *ts_st = st->priv_data; -+ uint32_t pcr_pid = ts_st->service->pcr_pid; - uint8_t *q; - uint8_t buf[TS_PACKET_SIZE]; - - q = buf; - *q++ = 0x47; -- *q++ = ts_st->pid >> 8; -- *q++ = ts_st->pid; -+ *q++ = pcr_pid >> 8; -+ *q++ = pcr_pid; - *q++ = 0x20 | ts_st->cc; /* Adaptation only */ - /* Continuity Count field does not increment (see 13818-1 section 2.4.3.3) */ - *q++ = TS_PACKET_SIZE - 5; /* Adaptation Field Length */ -@@ -1106,7 +1068,7 @@ - } - - /* PCR coded into 6 bytes */ -- q += write_pcr_bits(q, get_pcr(ts, s->pb)); -+ q += write_pcr_bits(q, ts->pcr); - - /* stuffing bytes */ - memset(q, 0xFF, TS_PACKET_SIZE - (q - buf)); -@@ -1175,8 +1137,6 @@ - uint8_t *q; - int val, is_start, len, header_len, write_pcr, is_dvb_subtitle, is_dvb_teletext, flags; - int afc_len, stuffing_len; -- int64_t pcr = -1; /* avoid warning */ -- int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE); - int force_pat = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && key && !ts_st->prev_payload_key; - - av_assert0(ts_st->payload != buf || st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO); -@@ -1186,28 +1146,33 @@ - - is_start = 1; - while (payload_size > 0) { -+ ts->pcr = ts->first_pcr + (ts->mux_rate == 1 ? -+ (dts == AV_NOPTS_VALUE ? 0 : (dts - ts->delay) * 300) : -+ // add 11, pcr references the last byte of program clock reference base -+ av_rescale(avio_tell(s->pb) + 11, 8 * PCR_TIME_BASE, ts->mux_rate)); -+ - retransmit_si_info(s, force_pat, dts); - force_pat = 0; - - write_pcr = 0; -- if (ts_st->pid == ts_st->service->pcr_pid) { -- if (ts->mux_rate > 1 || is_start) // VBR pcr period is based on frames -- ts_st->service->pcr_packet_count++; -- if (ts_st->service->pcr_packet_count >= -- ts_st->service->pcr_packet_period) { -- ts_st->service->pcr_packet_count = 0; -+ if (ts_st->pid == ts_st->service->pcr_sid) { -+ if( ts->pcr >= ts_st->service->pcr_packet_timer ) { -+ ts_st->service->pcr_packet_timer = ts->pcr + ts_st->service->pcr_packet_period; - write_pcr = 1; - } - } -- -+ if (write_pcr && ts_st->service->pcr_sid != ts_st->service->pcr_pid) { -+ mpegts_insert_pcr_only(s, st); -+ continue; -+ } - if (ts->mux_rate > 1 && dts != AV_NOPTS_VALUE && -- (dts - get_pcr(ts, s->pb) / 300) > delay) { -- /* pcr insert gets priority over null packet insert */ -- if (write_pcr) -- mpegts_insert_pcr_only(s, st); -+ (dts - ts->pcr / 300) > ts->delay) { -+ /* pcr insert gets priority over null packet insert */ -+ if (write_pcr) -+ mpegts_insert_pcr_only(s, st); - else -- mpegts_insert_null_packet(s); -- /* recalculate write_pcr and possibly retransmit si_info */ -+ mpegts_insert_null_packet(s); -+ /* recalculate write_pcr and possibly retransimit si_info */ - continue; - } - -@@ -1217,6 +1182,10 @@ - val = ts_st->pid >> 8; - if (is_start) - val |= 0x40; -+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && -+ st->codecpar->codec_id == AV_CODEC_ID_AC3 && -+ ts->m2ts_mode > 1) -+ val |= 0x20; - *q++ = val; - *q++ = ts_st->pid; - ts_st->cc = ts_st->cc + 1 & 0xf; -@@ -1228,7 +1197,7 @@ - } - if (key && is_start && pts != AV_NOPTS_VALUE) { - // set Random Access for key frames -- if (ts_st->pid == ts_st->service->pcr_pid) -+ if (ts_st->pid == ts_st->service->pcr_sid) - write_pcr = 1; - set_af_flag(buf, 0x40); - q = get_ts_payload_start(buf); -@@ -1236,14 +1205,10 @@ - if (write_pcr) { - set_af_flag(buf, 0x10); - q = get_ts_payload_start(buf); -- // add 11, pcr references the last byte of program clock reference base - if (ts->mux_rate > 1) -- pcr = get_pcr(ts, s->pb); -- else -- pcr = (dts - delay) * 300; -- if (dts != AV_NOPTS_VALUE && dts < pcr / 300) -+ if (dts != AV_NOPTS_VALUE && dts < ts->pcr / 300) - av_log(s, AV_LOG_WARNING, "dts < pcr, TS is invalid\n"); -- extend_af(buf, write_pcr_bits(q, pcr)); -+ extend_af(buf, write_pcr_bits(q, ts->pcr)); - q = get_ts_payload_start(buf); - } - if (is_start) { -@@ -1344,11 +1309,13 @@ - *q++ = flags; - *q++ = header_len; - if (pts != AV_NOPTS_VALUE) { -- write_pts(q, flags >> 6, pts); -+ int64_t ts_pts = pts + ts->ts_offset; -+ write_pts(q, flags >> 6, ts_pts); - q += 5; - } - if (dts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE && dts != pts) { -- write_pts(q, 1, dts); -+ int64_t ts_dts = dts + ts->ts_offset; -+ write_pts(q, 1, ts_dts); - q += 5; - } - if (pes_extension && st->codecpar->codec_id == AV_CODEC_ID_DIRAC) { -@@ -1519,7 +1486,6 @@ - uint8_t *data = NULL; - MpegTSWrite *ts = s->priv_data; - MpegTSWriteStream *ts_st = st->priv_data; -- const int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE) * 2; - int64_t dts = pkt->dts, pts = pkt->pts; - int opus_samples = 0; - int side_data_size; -@@ -1540,16 +1506,15 @@ - } - - if (ts->flags & MPEGTS_FLAG_REEMIT_PAT_PMT) { -- ts->pat_packet_count = ts->pat_packet_period - 1; -- ts->sdt_packet_count = ts->sdt_packet_period - 1; -+ ts->pat_packet_timer = ts->sdt_packet_timer = 0; - ts->flags &= ~MPEGTS_FLAG_REEMIT_PAT_PMT; - } - - if (ts->copyts < 1) { - if (pts != AV_NOPTS_VALUE) -- pts += delay; -+ pts += 2*ts->delay; - if (dts != AV_NOPTS_VALUE) -- dts += delay; -+ dts += 2*ts->delay; - } - - if (ts_st->first_pts_check && pts == AV_NOPTS_VALUE) { -@@ -1737,7 +1702,7 @@ - AVStream *st2 = s->streams[i]; - MpegTSWriteStream *ts_st2 = st2->priv_data; - if ( ts_st2->payload_size -- && (ts_st2->payload_dts == AV_NOPTS_VALUE || dts - ts_st2->payload_dts > delay/2)) { -+ && (ts_st2->payload_dts == AV_NOPTS_VALUE || dts - ts_st2->payload_dts > ts->delay)) { - mpegts_write_pes(s, st2, ts_st2->payload, ts_st2->payload_size, - ts_st2->payload_pts, ts_st2->payload_dts, - ts_st2->payload_flags & AV_PKT_FLAG_KEY, stream_id); -@@ -1908,12 +1873,18 @@ - { "mpegts_pmt_start_pid", "Set the first pid of the PMT.", - offsetof(MpegTSWrite, pmt_start_pid), AV_OPT_TYPE_INT, - { .i64 = 0x1000 }, 0x0010, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM }, -+ { "mpegts_pcr_start_pid", "Set the first pid of the PCR.", -+ offsetof(MpegTSWrite, pcr_start_pid), AV_OPT_TYPE_INT, -+ { .i64 = 0x1000 }, 0x0010, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM }, - { "mpegts_start_pid", "Set the first pid.", - offsetof(MpegTSWrite, start_pid), AV_OPT_TYPE_INT, - { .i64 = 0x0100 }, 0x0010, 0x0f00, AV_OPT_FLAG_ENCODING_PARAM }, - { "mpegts_m2ts_mode", "Enable m2ts mode.", - offsetof(MpegTSWrite, m2ts_mode), AV_OPT_TYPE_BOOL, -- { .i64 = -1 }, -1, 1, AV_OPT_FLAG_ENCODING_PARAM }, -+ { .i64 = -1 }, -1, 2, AV_OPT_FLAG_ENCODING_PARAM }, -+ { "mpegts_pcr_offset", "clock offset.", -+ offsetof(MpegTSWrite, ts_offset), AV_OPT_TYPE_BOOL, -+ { .i64 = 0 }, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, - { "muxrate", NULL, - offsetof(MpegTSWrite, mux_rate), AV_OPT_TYPE_INT, - { .i64 = 1 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, -@@ -1951,15 +1922,15 @@ - { "omit_video_pes_length", "Omit the PES packet length for video packets", - offsetof(MpegTSWrite, omit_video_pes_length), AV_OPT_TYPE_BOOL, - { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, -- { "pcr_period", "PCR retransmission time in milliseconds", -- offsetof(MpegTSWrite, pcr_period), AV_OPT_TYPE_INT, -- { .i64 = PCR_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, -- { "pat_period", "PAT/PMT retransmission time limit in seconds", -+ { "pcr_period", "PCR retransmission time limit in msecs", -+ offsetof(MpegTSWrite, pcr_period), AV_OPT_TYPE_DOUBLE, -+ { .dbl = PCR_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, -+ { "pat_period", "PAT/PMT retransmission time limit in msecs", - offsetof(MpegTSWrite, pat_period), AV_OPT_TYPE_DOUBLE, -- { .dbl = INT_MAX }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, -- { "sdt_period", "SDT retransmission time limit in seconds", -+ { .dbl = PAT_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, -+ { "sdt_period", "SDT retransmission time limit in msecs", - offsetof(MpegTSWrite, sdt_period), AV_OPT_TYPE_DOUBLE, -- { .dbl = INT_MAX }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, -+ { .dbl = SDT_RETRANS_TIME }, -1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, - { NULL }, - }; - diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch3 b/cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch3 deleted file mode 100644 index 3d64695a..00000000 --- a/cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch3 +++ /dev/null @@ -1,70 +0,0 @@ -diff -urN a/libavformat/avformat.h b/libavformat/avformat.h ---- a/libavformat/avformat.h 2018-04-20 04:02:57.000000000 -0600 -+++ b/libavformat/avformat.h 2018-04-24 11:02:20.777232001 -0600 -@@ -487,6 +487,9 @@ - The user or muxer can override this through - AVFormatContext.avoid_negative_ts - */ -+#define AVFMT_SEEK_NOSTREAMS 0x80000 /**< Stream index ignored by seek, -+ or some streams fail to seek -+ */ - - #define AVFMT_SEEK_TO_PTS 0x4000000 /**< Seeking is based on PTS */ - -@@ -647,7 +650,8 @@ - /** - * Can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS, - * AVFMT_GENERIC_INDEX, AVFMT_TS_DISCONT, AVFMT_NOBINSEARCH, -- * AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS. -+ * AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS, -+ * AVFMT_SEEK_NOSTREAMS - */ - int flags; - -diff -urN a/libavformat/dv.c b/libavformat/dv.c ---- a/libavformat/dv.c 2018-04-13 17:34:28.000000000 -0600 -+++ b/libavformat/dv.c 2018-04-24 11:02:20.778232001 -0600 -@@ -632,6 +632,7 @@ - AVInputFormat ff_dv_demuxer = { - .name = "dv", - .long_name = NULL_IF_CONFIG_SMALL("DV (Digital Video)"), -+ .flags = AVFMT_SEEK_NOSTREAMS, - .priv_data_size = sizeof(RawDVContext), - .read_probe = dv_probe, - .read_header = dv_read_header, -diff -urN a/libavformat/matroskadec.c b/libavformat/matroskadec.c ---- a/libavformat/matroskadec.c 2018-04-20 04:02:57.000000000 -0600 -+++ b/libavformat/matroskadec.c 2018-04-24 11:02:20.779232001 -0600 -@@ -4026,6 +4026,7 @@ - AVInputFormat ff_matroska_demuxer = { - .name = "matroska,webm", - .long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"), -+ .flags = AVFMT_SEEK_NOSTREAMS, - .extensions = "mkv,mk3d,mka,mks", - .priv_data_size = sizeof(MatroskaDemuxContext), - .read_probe = matroska_probe, -@@ -4039,6 +4040,7 @@ - AVInputFormat ff_webm_dash_manifest_demuxer = { - .name = "webm_dash_manifest", - .long_name = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"), -+ .flags = AVFMT_SEEK_NOSTREAMS, - .priv_data_size = sizeof(MatroskaDemuxContext), - .read_header = webm_dash_manifest_read_header, - .read_packet = webm_dash_manifest_read_packet, -diff -urN a/libavformat/utils.c b/libavformat/utils.c ---- a/libavformat/utils.c 2018-04-20 04:02:58.000000000 -0600 -+++ b/libavformat/utils.c 2018-04-24 11:02:20.780232001 -0600 -@@ -2471,6 +2471,13 @@ - return seek_frame_byte(s, stream_index, timestamp, flags); - } - -+ if (stream_index != -1 && (s->iformat->flags & AVFMT_SEEK_NOSTREAMS)) { -+ timestamp = av_rescale_q(timestamp, -+ s->streams[stream_index]->time_base, -+ AV_TIME_BASE_Q); -+ stream_index = -1; -+ } -+ - if (stream_index < 0) { - stream_index = av_find_default_stream_index(s); - if (stream_index < 0) diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-4.0.tar.xz b/cinelerra-5.1/thirdparty/src/ffmpeg-4.0.tar.xz deleted file mode 100644 index 38e95dc0..00000000 Binary files a/cinelerra-5.1/thirdparty/src/ffmpeg-4.0.tar.xz and /dev/null differ diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch0 b/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch0 new file mode 100644 index 00000000..5dd31100 --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch0 @@ -0,0 +1,11 @@ +diff -urN a/fftools/cmdutils.c b/fftools/cmdutils.c +--- a/fftools/cmdutils.c 2018-10-01 10:52:48.866784675 -0600 ++++ b/fftools/cmdutils.c 2018-10-01 10:52:55.550799827 -0600 +@@ -1179,6 +1179,7 @@ + + void show_banner(int argc, char **argv, const OptionDef *options) + { ++ return; + int idx = locate_option(argc, argv, options, "version"); + if (hide_banner || idx) + return; diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch1 b/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch1 new file mode 100644 index 00000000..831ea60f --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch1 @@ -0,0 +1,12 @@ +diff -urN a/libavformat/bluray.c b/libavformat/bluray.c +--- a/libavformat/bluray.c 2018-04-13 17:34:28.000000000 -0600 ++++ b/libavformat/bluray.c 2018-04-24 11:02:19.724232178 -0600 +@@ -28,7 +28,7 @@ + #include "libavutil/opt.h" + + #define BLURAY_PROTO_PREFIX "bluray:" +-#define MIN_PLAYLIST_LENGTH 180 /* 3 min */ ++#define MIN_PLAYLIST_LENGTH 0 + + typedef struct { + const AVClass *class; diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch2 b/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch2 new file mode 100644 index 00000000..e05f3372 --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch2 @@ -0,0 +1,498 @@ +diff -urN a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c +--- a/libavformat/mpegtsenc.c 2018-04-20 04:02:57.000000000 -0600 ++++ b/libavformat/mpegtsenc.c 2018-04-24 10:27:57.193689213 -0600 +@@ -56,9 +56,8 @@ + int sid; /* service ID */ + char *name; + char *provider_name; +- int pcr_pid; +- int pcr_packet_count; +- int pcr_packet_period; ++ int64_t pcr, pcr_packet_timer, pcr_packet_period; ++ int pcr_sid, pcr_pid; + AVProgram *program; + } MpegTSService; + +@@ -78,14 +77,12 @@ + MpegTSSection pat; /* MPEG-2 PAT table */ + MpegTSSection sdt; /* MPEG-2 SDT table context */ + MpegTSService **services; +- int sdt_packet_count; +- int sdt_packet_period; +- int pat_packet_count; +- int pat_packet_period; ++ int64_t sdt_packet_timer, sdt_packet_period; ++ int64_t pat_packet_timer, pat_packet_period; + int nb_services; + int onid; + int tsid; +- int64_t first_pcr; ++ int64_t pcr, first_pcr, delay; + int mux_rate; ///< set to 1 when VBR + int pes_payload_size; + +@@ -95,12 +92,14 @@ + int service_type; + + int pmt_start_pid; ++ int pcr_start_pid; + int start_pid; + int m2ts_mode; ++ int64_t ts_offset; + + int reemit_pat_pmt; // backward compatibility + +- int pcr_period; ++ double pcr_period; + #define MPEGTS_FLAG_REEMIT_PAT_PMT 0x01 + #define MPEGTS_FLAG_AAC_LATM 0x02 + #define MPEGTS_FLAG_PAT_PMT_AT_FRAMES 0x04 +@@ -111,8 +110,6 @@ + int tables_version; + double pat_period; + double sdt_period; +- int64_t last_pat_ts; +- int64_t last_sdt_ts; + + int omit_video_pes_length; + } MpegTSWrite; +@@ -222,10 +219,10 @@ + #define DEFAULT_PROVIDER_NAME "FFmpeg" + #define DEFAULT_SERVICE_NAME "Service01" + +-/* we retransmit the SI info at this rate */ ++/* we retransmit the SI info at this rate (ms) */ + #define SDT_RETRANS_TIME 500 + #define PAT_RETRANS_TIME 100 +-#define PCR_RETRANS_TIME 20 ++#define PCR_RETRANS_TIME 50 + + typedef struct MpegTSWriteStream { + struct MpegTSService *service; +@@ -721,6 +718,7 @@ + service->pmt.pid = ts->pmt_start_pid + ts->nb_services; + service->sid = sid; + service->pcr_pid = 0x1fff; ++ service->pcr_sid = 0x1fff; + service->provider_name = av_strdup(provider_name); + service->name = av_strdup(name); + if (!service->provider_name || !service->name) +@@ -736,18 +734,11 @@ + return NULL; + } + +-static int64_t get_pcr(const MpegTSWrite *ts, AVIOContext *pb) +-{ +- return av_rescale(avio_tell(pb) + 11, 8 * PCR_TIME_BASE, ts->mux_rate) + +- ts->first_pcr; +-} +- + static void mpegts_prefix_m2ts_header(AVFormatContext *s) + { + MpegTSWrite *ts = s->priv_data; + if (ts->m2ts_mode) { +- int64_t pcr = get_pcr(s->priv_data, s->pb); +- uint32_t tp_extra_header = pcr % 0x3fffffff; ++ uint32_t tp_extra_header = ts->pcr % 0x3fffffff; + tp_extra_header = AV_RB32(&tp_extra_header); + avio_write(s->pb, (unsigned char *) &tp_extra_header, + sizeof(tp_extra_header)); +@@ -768,6 +759,7 @@ + MpegTSService *service; + AVStream *st, *pcr_st = NULL; + AVDictionaryEntry *title, *provider; ++ double clk_rate; + int i, j; + const char *service_name; + const char *provider_name; +@@ -776,6 +768,15 @@ + + if (s->max_delay < 0) /* Not set by the caller */ + s->max_delay = 0; ++ ts->delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE); ++ ++ if (ts->m2ts_mode == -1) { ++ if (av_match_ext(s->url, "m2ts")) { ++ ts->m2ts_mode = 1; ++ } else { ++ ts->m2ts_mode = 0; ++ } ++ } + + // round up to a whole number of TS packets + ts->pes_payload_size = (ts->pes_payload_size + 14 + 183) / 184 * 184 - 14; +@@ -822,6 +823,8 @@ + service->program = program; + } + } ++ if (ts->m2ts_mode > 1) ++ service->pmt.pid = 0x00ff + ts->service_id; + + ts->pat.pid = PAT_PID; + /* Initialize at 15 so that it wraps and is equal to 0 for the +@@ -907,10 +910,9 @@ + ts_st->discontinuity = ts->flags & MPEGTS_FLAG_DISCONT; + /* update PCR pid by using the first video stream */ + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && +- service->pcr_pid == 0x1fff) { +- service->pcr_pid = ts_st->pid; ++ service->pcr_sid == 0x1fff) + pcr_st = st; +- } ++ + if (st->codecpar->codec_id == AV_CODEC_ID_AAC && + st->codecpar->extradata_size > 0) { + AVStream *ast; +@@ -946,78 +948,47 @@ + av_freep(&pids); + + /* if no video stream, use the first stream as PCR */ +- if (service->pcr_pid == 0x1fff && s->nb_streams > 0) { +- pcr_st = s->streams[0]; +- ts_st = pcr_st->priv_data; +- service->pcr_pid = ts_st->pid; +- } else +- ts_st = pcr_st->priv_data; +- +- if (ts->mux_rate > 1) { +- service->pcr_packet_period = (int64_t)ts->mux_rate * ts->pcr_period / +- (TS_PACKET_SIZE * 8 * 1000); +- ts->sdt_packet_period = (int64_t)ts->mux_rate * SDT_RETRANS_TIME / +- (TS_PACKET_SIZE * 8 * 1000); +- ts->pat_packet_period = (int64_t)ts->mux_rate * PAT_RETRANS_TIME / +- (TS_PACKET_SIZE * 8 * 1000); +- +- if (ts->copyts < 1) +- ts->first_pcr = av_rescale(s->max_delay, PCR_TIME_BASE, AV_TIME_BASE); +- } else { +- /* Arbitrary values, PAT/PMT will also be written on video key frames */ +- ts->sdt_packet_period = 200; +- ts->pat_packet_period = 40; +- if (pcr_st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { +- int frame_size = av_get_audio_frame_duration2(pcr_st->codecpar, 0); +- if (!frame_size) { +- av_log(s, AV_LOG_WARNING, "frame size not set\n"); +- service->pcr_packet_period = +- pcr_st->codecpar->sample_rate / (10 * 512); +- } else { +- service->pcr_packet_period = +- pcr_st->codecpar->sample_rate / (10 * frame_size); +- } +- } else { +- // max delta PCR 0.1s +- // TODO: should be avg_frame_rate +- service->pcr_packet_period = +- ts_st->user_tb.den / (10 * ts_st->user_tb.num); +- } +- if (!service->pcr_packet_period) +- service->pcr_packet_period = 1; +- } +- +- ts->last_pat_ts = AV_NOPTS_VALUE; +- ts->last_sdt_ts = AV_NOPTS_VALUE; +- // The user specified a period, use only it +- if (ts->pat_period < INT_MAX/2) { +- ts->pat_packet_period = INT_MAX; ++ if (!pcr_st && s->nb_streams > 0) ++ pcr_st = s->streams[0]; ++ if (!pcr_st) { ++ av_log(s, AV_LOG_ERROR, "no streams\n"); ++ ret = AVERROR(EINVAL); ++ goto fail; + } +- if (ts->sdt_period < INT_MAX/2) { +- ts->sdt_packet_period = INT_MAX; ++ ts_st = pcr_st->priv_data; ++ if (service->pcr_sid == 0x1fff) ++ service->pcr_sid = ts_st->pid; ++ if (service->pcr_pid == 0x1fff) ++ service->pcr_pid = ts->m2ts_mode > 1 ? ++ 0x1000 + ts->service_id : service->pcr_sid ; ++ if (service->pmt.pid == service->pcr_pid) { ++ av_log(s, AV_LOG_ERROR, "Duplicate stream id %d\n", service->pcr_pid); ++ ret = AVERROR(EINVAL); ++ goto fail; + } + ++ clk_rate = ts->mux_rate > 1 ? ts->mux_rate : PCR_TIME_BASE; ++ ts->sdt_packet_period = ts->sdt_period < 0 ? -1 : ts->sdt_period/1000 * clk_rate; ++ ts->pat_packet_period = ts->pat_period/1000 * clk_rate; ++ service->pcr_packet_period = ts->pcr_period/1000 * clk_rate; ++ if (service->pcr_packet_period < (TS_PACKET_SIZE*8*10)) ++ service->pcr_packet_period = (TS_PACKET_SIZE*8*10); ++ av_log(s, AV_LOG_VERBOSE, "clk_rate %f: ticks/pkt %d pcr, %d sdt, %d pmt\n", clk_rate, ++ (int)service->pcr_packet_period, (int)ts->sdt_packet_period, (int)ts->pat_packet_period); ++ ++ if (ts->copyts < 1) ++ ts->first_pcr = av_rescale(s->max_delay, PCR_TIME_BASE, AV_TIME_BASE); ++ + // output a PCR as soon as possible +- service->pcr_packet_count = service->pcr_packet_period; +- ts->pat_packet_count = ts->pat_packet_period - 1; +- ts->sdt_packet_count = ts->sdt_packet_period - 1; ++ ts->pcr = 0; ++ service->pcr_packet_timer = 0; ++ ts->pat_packet_timer = 0; ++ ts->sdt_packet_timer = 0; + + if (ts->mux_rate == 1) + av_log(s, AV_LOG_VERBOSE, "muxrate VBR, "); + else + av_log(s, AV_LOG_VERBOSE, "muxrate %d, ", ts->mux_rate); +- av_log(s, AV_LOG_VERBOSE, +- "pcr every %d pkts, sdt every %d, pat/pmt every %d pkts\n", +- service->pcr_packet_period, +- ts->sdt_packet_period, ts->pat_packet_period); +- +- if (ts->m2ts_mode == -1) { +- if (av_match_ext(s->url, "m2ts")) { +- ts->m2ts_mode = 1; +- } else { +- ts->m2ts_mode = 0; +- } +- } + + return 0; + +@@ -1032,22 +1003,12 @@ + MpegTSWrite *ts = s->priv_data; + int i; + +- if (++ts->sdt_packet_count == ts->sdt_packet_period || +- (dts != AV_NOPTS_VALUE && ts->last_sdt_ts == AV_NOPTS_VALUE) || +- (dts != AV_NOPTS_VALUE && dts - ts->last_sdt_ts >= ts->sdt_period*90000.0) +- ) { +- ts->sdt_packet_count = 0; +- if (dts != AV_NOPTS_VALUE) +- ts->last_sdt_ts = FFMAX(dts, ts->last_sdt_ts); ++ if ( ts->sdt_packet_period >= 0 && ts->pcr >= ts->sdt_packet_timer ) { ++ ts->sdt_packet_timer = ts->pcr + ts->sdt_packet_period; + mpegts_write_sdt(s); + } +- if (++ts->pat_packet_count == ts->pat_packet_period || +- (dts != AV_NOPTS_VALUE && ts->last_pat_ts == AV_NOPTS_VALUE) || +- (dts != AV_NOPTS_VALUE && dts - ts->last_pat_ts >= ts->pat_period*90000.0) || +- force_pat) { +- ts->pat_packet_count = 0; +- if (dts != AV_NOPTS_VALUE) +- ts->last_pat_ts = FFMAX(dts, ts->last_pat_ts); ++ if (ts->pcr >= ts->pat_packet_timer || force_pat) { ++ ts->pat_packet_timer = ts->pcr + ts->pat_packet_period; + mpegts_write_pat(s); + for (i = 0; i < ts->nb_services; i++) + mpegts_write_pmt(s, ts->services[i]); +@@ -1089,13 +1050,14 @@ + { + MpegTSWrite *ts = s->priv_data; + MpegTSWriteStream *ts_st = st->priv_data; ++ uint32_t pcr_pid = ts_st->service->pcr_pid; + uint8_t *q; + uint8_t buf[TS_PACKET_SIZE]; + + q = buf; + *q++ = 0x47; +- *q++ = ts_st->pid >> 8; +- *q++ = ts_st->pid; ++ *q++ = pcr_pid >> 8; ++ *q++ = pcr_pid; + *q++ = 0x20 | ts_st->cc; /* Adaptation only */ + /* Continuity Count field does not increment (see 13818-1 section 2.4.3.3) */ + *q++ = TS_PACKET_SIZE - 5; /* Adaptation Field Length */ +@@ -1106,7 +1068,7 @@ + } + + /* PCR coded into 6 bytes */ +- q += write_pcr_bits(q, get_pcr(ts, s->pb)); ++ q += write_pcr_bits(q, ts->pcr); + + /* stuffing bytes */ + memset(q, 0xFF, TS_PACKET_SIZE - (q - buf)); +@@ -1175,8 +1137,6 @@ + uint8_t *q; + int val, is_start, len, header_len, write_pcr, is_dvb_subtitle, is_dvb_teletext, flags; + int afc_len, stuffing_len; +- int64_t pcr = -1; /* avoid warning */ +- int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE); + int force_pat = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && key && !ts_st->prev_payload_key; + + av_assert0(ts_st->payload != buf || st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO); +@@ -1186,28 +1146,33 @@ + + is_start = 1; + while (payload_size > 0) { ++ ts->pcr = ts->first_pcr + (ts->mux_rate == 1 ? ++ (dts == AV_NOPTS_VALUE ? 0 : (dts - ts->delay) * 300) : ++ // add 11, pcr references the last byte of program clock reference base ++ av_rescale(avio_tell(s->pb) + 11, 8 * PCR_TIME_BASE, ts->mux_rate)); ++ + retransmit_si_info(s, force_pat, dts); + force_pat = 0; + + write_pcr = 0; +- if (ts_st->pid == ts_st->service->pcr_pid) { +- if (ts->mux_rate > 1 || is_start) // VBR pcr period is based on frames +- ts_st->service->pcr_packet_count++; +- if (ts_st->service->pcr_packet_count >= +- ts_st->service->pcr_packet_period) { +- ts_st->service->pcr_packet_count = 0; ++ if (ts_st->pid == ts_st->service->pcr_sid) { ++ if( ts->pcr >= ts_st->service->pcr_packet_timer ) { ++ ts_st->service->pcr_packet_timer = ts->pcr + ts_st->service->pcr_packet_period; + write_pcr = 1; + } + } +- ++ if (write_pcr && ts_st->service->pcr_sid != ts_st->service->pcr_pid) { ++ mpegts_insert_pcr_only(s, st); ++ continue; ++ } + if (ts->mux_rate > 1 && dts != AV_NOPTS_VALUE && +- (dts - get_pcr(ts, s->pb) / 300) > delay) { +- /* pcr insert gets priority over null packet insert */ +- if (write_pcr) +- mpegts_insert_pcr_only(s, st); ++ (dts - ts->pcr / 300) > ts->delay) { ++ /* pcr insert gets priority over null packet insert */ ++ if (write_pcr) ++ mpegts_insert_pcr_only(s, st); + else +- mpegts_insert_null_packet(s); +- /* recalculate write_pcr and possibly retransmit si_info */ ++ mpegts_insert_null_packet(s); ++ /* recalculate write_pcr and possibly retransimit si_info */ + continue; + } + +@@ -1217,6 +1182,10 @@ + val = ts_st->pid >> 8; + if (is_start) + val |= 0x40; ++ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && ++ st->codecpar->codec_id == AV_CODEC_ID_AC3 && ++ ts->m2ts_mode > 1) ++ val |= 0x20; + *q++ = val; + *q++ = ts_st->pid; + ts_st->cc = ts_st->cc + 1 & 0xf; +@@ -1228,7 +1197,7 @@ + } + if (key && is_start && pts != AV_NOPTS_VALUE) { + // set Random Access for key frames +- if (ts_st->pid == ts_st->service->pcr_pid) ++ if (ts_st->pid == ts_st->service->pcr_sid) + write_pcr = 1; + set_af_flag(buf, 0x40); + q = get_ts_payload_start(buf); +@@ -1236,14 +1205,10 @@ + if (write_pcr) { + set_af_flag(buf, 0x10); + q = get_ts_payload_start(buf); +- // add 11, pcr references the last byte of program clock reference base + if (ts->mux_rate > 1) +- pcr = get_pcr(ts, s->pb); +- else +- pcr = (dts - delay) * 300; +- if (dts != AV_NOPTS_VALUE && dts < pcr / 300) ++ if (dts != AV_NOPTS_VALUE && dts < ts->pcr / 300) + av_log(s, AV_LOG_WARNING, "dts < pcr, TS is invalid\n"); +- extend_af(buf, write_pcr_bits(q, pcr)); ++ extend_af(buf, write_pcr_bits(q, ts->pcr)); + q = get_ts_payload_start(buf); + } + if (is_start) { +@@ -1344,11 +1309,13 @@ + *q++ = flags; + *q++ = header_len; + if (pts != AV_NOPTS_VALUE) { +- write_pts(q, flags >> 6, pts); ++ int64_t ts_pts = pts + ts->ts_offset; ++ write_pts(q, flags >> 6, ts_pts); + q += 5; + } + if (dts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE && dts != pts) { +- write_pts(q, 1, dts); ++ int64_t ts_dts = dts + ts->ts_offset; ++ write_pts(q, 1, ts_dts); + q += 5; + } + if (pes_extension && st->codecpar->codec_id == AV_CODEC_ID_DIRAC) { +@@ -1519,7 +1486,6 @@ + uint8_t *data = NULL; + MpegTSWrite *ts = s->priv_data; + MpegTSWriteStream *ts_st = st->priv_data; +- const int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE) * 2; + int64_t dts = pkt->dts, pts = pkt->pts; + int opus_samples = 0; + int side_data_size; +@@ -1540,16 +1506,15 @@ + } + + if (ts->flags & MPEGTS_FLAG_REEMIT_PAT_PMT) { +- ts->pat_packet_count = ts->pat_packet_period - 1; +- ts->sdt_packet_count = ts->sdt_packet_period - 1; ++ ts->pat_packet_timer = ts->sdt_packet_timer = 0; + ts->flags &= ~MPEGTS_FLAG_REEMIT_PAT_PMT; + } + + if (ts->copyts < 1) { + if (pts != AV_NOPTS_VALUE) +- pts += delay; ++ pts += 2*ts->delay; + if (dts != AV_NOPTS_VALUE) +- dts += delay; ++ dts += 2*ts->delay; + } + + if (ts_st->first_pts_check && pts == AV_NOPTS_VALUE) { +@@ -1737,7 +1702,7 @@ + AVStream *st2 = s->streams[i]; + MpegTSWriteStream *ts_st2 = st2->priv_data; + if ( ts_st2->payload_size +- && (ts_st2->payload_dts == AV_NOPTS_VALUE || dts - ts_st2->payload_dts > delay/2)) { ++ && (ts_st2->payload_dts == AV_NOPTS_VALUE || dts - ts_st2->payload_dts > ts->delay)) { + mpegts_write_pes(s, st2, ts_st2->payload, ts_st2->payload_size, + ts_st2->payload_pts, ts_st2->payload_dts, + ts_st2->payload_flags & AV_PKT_FLAG_KEY, stream_id); +@@ -1908,12 +1873,18 @@ + { "mpegts_pmt_start_pid", "Set the first pid of the PMT.", + offsetof(MpegTSWrite, pmt_start_pid), AV_OPT_TYPE_INT, + { .i64 = 0x1000 }, 0x0010, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "mpegts_pcr_start_pid", "Set the first pid of the PCR.", ++ offsetof(MpegTSWrite, pcr_start_pid), AV_OPT_TYPE_INT, ++ { .i64 = 0x1000 }, 0x0010, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM }, + { "mpegts_start_pid", "Set the first pid.", + offsetof(MpegTSWrite, start_pid), AV_OPT_TYPE_INT, + { .i64 = 0x0100 }, 0x0010, 0x0f00, AV_OPT_FLAG_ENCODING_PARAM }, + { "mpegts_m2ts_mode", "Enable m2ts mode.", + offsetof(MpegTSWrite, m2ts_mode), AV_OPT_TYPE_BOOL, +- { .i64 = -1 }, -1, 1, AV_OPT_FLAG_ENCODING_PARAM }, ++ { .i64 = -1 }, -1, 2, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "mpegts_pcr_offset", "clock offset.", ++ offsetof(MpegTSWrite, ts_offset), AV_OPT_TYPE_BOOL, ++ { .i64 = 0 }, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { "muxrate", NULL, + offsetof(MpegTSWrite, mux_rate), AV_OPT_TYPE_INT, + { .i64 = 1 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, +@@ -1951,15 +1922,15 @@ + { "omit_video_pes_length", "Omit the PES packet length for video packets", + offsetof(MpegTSWrite, omit_video_pes_length), AV_OPT_TYPE_BOOL, + { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, +- { "pcr_period", "PCR retransmission time in milliseconds", +- offsetof(MpegTSWrite, pcr_period), AV_OPT_TYPE_INT, +- { .i64 = PCR_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, +- { "pat_period", "PAT/PMT retransmission time limit in seconds", ++ { "pcr_period", "PCR retransmission time limit in msecs", ++ offsetof(MpegTSWrite, pcr_period), AV_OPT_TYPE_DOUBLE, ++ { .dbl = PCR_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "pat_period", "PAT/PMT retransmission time limit in msecs", + offsetof(MpegTSWrite, pat_period), AV_OPT_TYPE_DOUBLE, +- { .dbl = INT_MAX }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, +- { "sdt_period", "SDT retransmission time limit in seconds", ++ { .dbl = PAT_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "sdt_period", "SDT retransmission time limit in msecs", + offsetof(MpegTSWrite, sdt_period), AV_OPT_TYPE_DOUBLE, +- { .dbl = INT_MAX }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, ++ { .dbl = SDT_RETRANS_TIME }, -1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { NULL }, + }; + diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch3 b/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch3 new file mode 100644 index 00000000..6e2ebbcd --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch3 @@ -0,0 +1,70 @@ +diff -urN a/libavformat/avformat.h b/libavformat/avformat.h +--- a/libavformat/avformat.h 2018-11-05 16:22:26.000000000 -0700 ++++ b/libavformat/avformat.h 2018-11-08 07:25:17.066799941 -0700 +@@ -487,6 +487,9 @@ + The user or muxer can override this through + AVFormatContext.avoid_negative_ts + */ ++#define AVFMT_SEEK_NOSTREAMS 0x80000 /**< Stream index ignored by seek, ++ or some streams fail to seek ++ */ + + #define AVFMT_SEEK_TO_PTS 0x4000000 /**< Seeking is based on PTS */ + +@@ -647,7 +650,8 @@ + /** + * Can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS, + * AVFMT_GENERIC_INDEX, AVFMT_TS_DISCONT, AVFMT_NOBINSEARCH, +- * AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS. ++ * AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS, ++ * AVFMT_SEEK_NOSTREAMS + */ + int flags; + +diff -urN a/libavformat/dv.c b/libavformat/dv.c +--- a/libavformat/dv.c 2018-11-01 12:34:26.000000000 -0600 ++++ b/libavformat/dv.c 2018-11-08 07:25:17.066799941 -0700 +@@ -632,6 +632,7 @@ + AVInputFormat ff_dv_demuxer = { + .name = "dv", + .long_name = NULL_IF_CONFIG_SMALL("DV (Digital Video)"), ++ .flags = AVFMT_SEEK_NOSTREAMS, + .priv_data_size = sizeof(RawDVContext), + .read_probe = dv_probe, + .read_header = dv_read_header, +diff -urN a/libavformat/matroskadec.c b/libavformat/matroskadec.c +--- a/libavformat/matroskadec.c 2018-11-05 16:22:26.000000000 -0700 ++++ b/libavformat/matroskadec.c 2018-11-08 07:25:17.067799930 -0700 +@@ -4030,6 +4030,7 @@ + AVInputFormat ff_matroska_demuxer = { + .name = "matroska,webm", + .long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"), ++ .flags = AVFMT_SEEK_NOSTREAMS, + .extensions = "mkv,mk3d,mka,mks", + .priv_data_size = sizeof(MatroskaDemuxContext), + .read_probe = matroska_probe, +@@ -4043,6 +4044,7 @@ + AVInputFormat ff_webm_dash_manifest_demuxer = { + .name = "webm_dash_manifest", + .long_name = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"), ++ .flags = AVFMT_SEEK_NOSTREAMS, + .priv_data_size = sizeof(MatroskaDemuxContext), + .read_header = webm_dash_manifest_read_header, + .read_packet = webm_dash_manifest_read_packet, +diff -urN a/libavformat/utils.c b/libavformat/utils.c +--- a/libavformat/utils.c 2018-11-05 16:22:26.000000000 -0700 ++++ b/libavformat/utils.c 2018-11-08 07:25:17.069799908 -0700 +@@ -2472,6 +2472,13 @@ + return seek_frame_byte(s, stream_index, timestamp, flags); + } + ++ if (stream_index != -1 && (s->iformat->flags & AVFMT_SEEK_NOSTREAMS)) { ++ timestamp = av_rescale_q(timestamp, ++ s->streams[stream_index]->time_base, ++ AV_TIME_BASE_Q); ++ stream_index = -1; ++ } ++ + if (stream_index < 0) { + stream_index = av_find_default_stream_index(s); + if (stream_index < 0) diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.tar.xz b/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.tar.xz new file mode 100644 index 00000000..9ce41056 Binary files /dev/null and b/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.tar.xz differ diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg.git.patch3 b/cinelerra-5.1/thirdparty/src/ffmpeg.git.patch3 index 3d64695a..6e2ebbcd 100644 --- a/cinelerra-5.1/thirdparty/src/ffmpeg.git.patch3 +++ b/cinelerra-5.1/thirdparty/src/ffmpeg.git.patch3 @@ -1,6 +1,6 @@ diff -urN a/libavformat/avformat.h b/libavformat/avformat.h ---- a/libavformat/avformat.h 2018-04-20 04:02:57.000000000 -0600 -+++ b/libavformat/avformat.h 2018-04-24 11:02:20.777232001 -0600 +--- a/libavformat/avformat.h 2018-11-05 16:22:26.000000000 -0700 ++++ b/libavformat/avformat.h 2018-11-08 07:25:17.066799941 -0700 @@ -487,6 +487,9 @@ The user or muxer can override this through AVFormatContext.avoid_negative_ts @@ -22,8 +22,8 @@ diff -urN a/libavformat/avformat.h b/libavformat/avformat.h int flags; diff -urN a/libavformat/dv.c b/libavformat/dv.c ---- a/libavformat/dv.c 2018-04-13 17:34:28.000000000 -0600 -+++ b/libavformat/dv.c 2018-04-24 11:02:20.778232001 -0600 +--- a/libavformat/dv.c 2018-11-01 12:34:26.000000000 -0600 ++++ b/libavformat/dv.c 2018-11-08 07:25:17.066799941 -0700 @@ -632,6 +632,7 @@ AVInputFormat ff_dv_demuxer = { .name = "dv", @@ -33,9 +33,9 @@ diff -urN a/libavformat/dv.c b/libavformat/dv.c .read_probe = dv_probe, .read_header = dv_read_header, diff -urN a/libavformat/matroskadec.c b/libavformat/matroskadec.c ---- a/libavformat/matroskadec.c 2018-04-20 04:02:57.000000000 -0600 -+++ b/libavformat/matroskadec.c 2018-04-24 11:02:20.779232001 -0600 -@@ -4026,6 +4026,7 @@ +--- a/libavformat/matroskadec.c 2018-11-05 16:22:26.000000000 -0700 ++++ b/libavformat/matroskadec.c 2018-11-08 07:25:17.067799930 -0700 +@@ -4030,6 +4030,7 @@ AVInputFormat ff_matroska_demuxer = { .name = "matroska,webm", .long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"), @@ -43,7 +43,7 @@ diff -urN a/libavformat/matroskadec.c b/libavformat/matroskadec.c .extensions = "mkv,mk3d,mka,mks", .priv_data_size = sizeof(MatroskaDemuxContext), .read_probe = matroska_probe, -@@ -4039,6 +4040,7 @@ +@@ -4043,6 +4044,7 @@ AVInputFormat ff_webm_dash_manifest_demuxer = { .name = "webm_dash_manifest", .long_name = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"), @@ -52,9 +52,9 @@ diff -urN a/libavformat/matroskadec.c b/libavformat/matroskadec.c .read_header = webm_dash_manifest_read_header, .read_packet = webm_dash_manifest_read_packet, diff -urN a/libavformat/utils.c b/libavformat/utils.c ---- a/libavformat/utils.c 2018-04-20 04:02:58.000000000 -0600 -+++ b/libavformat/utils.c 2018-04-24 11:02:20.780232001 -0600 -@@ -2471,6 +2471,13 @@ +--- a/libavformat/utils.c 2018-11-05 16:22:26.000000000 -0700 ++++ b/libavformat/utils.c 2018-11-08 07:25:17.069799908 -0700 +@@ -2472,6 +2472,13 @@ return seek_frame_byte(s, stream_index, timestamp, flags); }