add timecode units/alignment/probe, add prefs auto_rotate,
authorGood Guy <good1.2guy@gmail.com>
Mon, 20 Jul 2020 00:09:46 +0000 (18:09 -0600)
committerGood Guy <good1.2guy@gmail.com>
Mon, 20 Jul 2020 00:09:46 +0000 (18:09 -0600)
ffmpeg ssse3 patchB for flip/rotate, fix assetpopup deadlocks,
renderfarm 1frm render fix, resampleRT segv,
info tweaks, plugin.opts vaapi tweaks

76 files changed:
cinelerra-5.1/cinelerra/appearanceprefs.C
cinelerra-5.1/cinelerra/appearanceprefs.h
cinelerra-5.1/cinelerra/appearanceprefs.inc
cinelerra-5.1/cinelerra/asset.C
cinelerra-5.1/cinelerra/asset.h
cinelerra-5.1/cinelerra/assetedit.C
cinelerra-5.1/cinelerra/assetedit.h
cinelerra-5.1/cinelerra/assetpopup.C
cinelerra-5.1/cinelerra/batchrender.C
cinelerra-5.1/cinelerra/cwindowgui.C
cinelerra-5.1/cinelerra/editpanel.C
cinelerra-5.1/cinelerra/editpanel.h
cinelerra-5.1/cinelerra/editpanel.inc
cinelerra-5.1/cinelerra/editpopup.C
cinelerra-5.1/cinelerra/editpopup.h
cinelerra-5.1/cinelerra/editpopup.inc
cinelerra-5.1/cinelerra/edits.C
cinelerra-5.1/cinelerra/edits.h
cinelerra-5.1/cinelerra/edlsession.C
cinelerra-5.1/cinelerra/edlsession.h
cinelerra-5.1/cinelerra/ffmpeg.C
cinelerra-5.1/cinelerra/ffmpeg.h
cinelerra-5.1/cinelerra/mainclock.C
cinelerra-5.1/cinelerra/mainmenu.C
cinelerra-5.1/cinelerra/mainmenu.h
cinelerra-5.1/cinelerra/mainmenu.inc
cinelerra-5.1/cinelerra/mbuttons.C
cinelerra-5.1/cinelerra/menueffects.C
cinelerra-5.1/cinelerra/mtimebar.C
cinelerra-5.1/cinelerra/mwindow.C
cinelerra-5.1/cinelerra/mwindow.h
cinelerra-5.1/cinelerra/mwindowedit.C
cinelerra-5.1/cinelerra/mwindowmove.C
cinelerra-5.1/cinelerra/pluginclient.h
cinelerra-5.1/cinelerra/preferences.C
cinelerra-5.1/cinelerra/preferences.h
cinelerra-5.1/cinelerra/render.C
cinelerra-5.1/cinelerra/render.h
cinelerra-5.1/cinelerra/tracks.C
cinelerra-5.1/cinelerra/tracks.h
cinelerra-5.1/cinelerra/vwindowgui.C
cinelerra-5.1/cinelerra/zoombar.C
cinelerra-5.1/cinelerra/zoompanel.C
cinelerra-5.1/doc/shortcuts.html
cinelerra-5.1/ffmpeg/plugin.opts
cinelerra-5.1/guicast/bccmodels.C
cinelerra-5.1/guicast/bccmodels.h
cinelerra-5.1/guicast/units.C
cinelerra-5.1/guicast/units.h
cinelerra-5.1/guicast/vframe.C
cinelerra-5.1/info/plugins.txt
cinelerra-5.1/plugins/resamplert/resamplert.C
cinelerra-5.1/plugins/resamplert/resamplert.h
cinelerra-5.1/plugins/theme_blond/blondtheme.C
cinelerra-5.1/plugins/theme_blond/data/clapper.png [new file with mode: 0755]
cinelerra-5.1/plugins/theme_blond_cv/blondcvtheme.C
cinelerra-5.1/plugins/theme_blond_cv/data/clapper.png [new file with mode: 0755]
cinelerra-5.1/plugins/theme_blue/bluetheme.C
cinelerra-5.1/plugins/theme_blue/data/clapper.png [new file with mode: 0755]
cinelerra-5.1/plugins/theme_blue_dot/bluedottheme.C
cinelerra-5.1/plugins/theme_blue_dot/data/clapper.png [new file with mode: 0755]
cinelerra-5.1/plugins/theme_bright/brighttheme.C
cinelerra-5.1/plugins/theme_bright/data/clapper.png [new file with mode: 0755]
cinelerra-5.1/plugins/theme_cakewalk/cakewalk.C
cinelerra-5.1/plugins/theme_cakewalk/data/clapper.png [new file with mode: 0755]
cinelerra-5.1/plugins/theme_hulk/data/clapper.png [new file with mode: 0755]
cinelerra-5.1/plugins/theme_hulk/hulktheme.C
cinelerra-5.1/plugins/theme_neophyte/data/clapper.png [new file with mode: 0755]
cinelerra-5.1/plugins/theme_neophyte/neophyte.C
cinelerra-5.1/plugins/theme_pinklady/data/clapper.png [new file with mode: 0755]
cinelerra-5.1/plugins/theme_pinklady/pinkladytheme.C
cinelerra-5.1/plugins/theme_suv/data/clapper.png [new file with mode: 0755]
cinelerra-5.1/plugins/theme_suv/suv.C
cinelerra-5.1/plugins/theme_unflat/data/clapper.png [new file with mode: 0755]
cinelerra-5.1/plugins/theme_unflat/unflattheme.C
cinelerra-5.1/thirdparty/src/ffmpeg-4.3.patchB [new file with mode: 0644]

index 1c6be93189ed3c0467c92317da382b2d08c5e1fc..943274717c2ceb82248661e4c8ecf4c9b29e00f8 100644 (file)
@@ -38,6 +38,7 @@ AppearancePrefs::AppearancePrefs(MWindow *mwindow, PreferencesWindow *pwindow)
 {
        hms = 0;
        hmsf = 0;
+       timecode = 0;
        samples = 0;
        frames = 0;
        hex = 0;
@@ -52,6 +53,7 @@ AppearancePrefs::~AppearancePrefs()
 {
        delete hms;
        delete hmsf;
+       delete timecode;
        delete samples;
        delete frames;
        delete hex;
@@ -131,6 +133,10 @@ void AppearancePrefs::create_objects()
                pwindow->thread->edl->session->time_format == TIME_HMSF,
                x, y));
        y += ys20;
+       add_subwindow(timecode = new TimeFormatTimecode(pwindow, this,
+               pwindow->thread->edl->session->time_format == TIME_TIMECODE,
+               x, y));
+       y += ys20;
        add_subwindow(samples = new TimeFormatSamples(pwindow, this,
                pwindow->thread->edl->session->time_format == TIME_SAMPLES,
                x, y));
@@ -258,6 +264,9 @@ void AppearancePrefs::create_objects()
        DeactivateFocusPolicy *focus_deactivate = new DeactivateFocusPolicy(pwindow, x, y);
        add_subwindow(focus_deactivate);
        y += focus_deactivate->get_h() + ys5;
+       AutoRotate *auto_rotate = new AutoRotate(pwindow, x, y);
+       add_subwindow(auto_rotate);
+       y += auto_rotate->get_h() + ys5;
 }
 
 int AppearancePrefs::update(int new_value)
@@ -266,6 +275,7 @@ int AppearancePrefs::update(int new_value)
        pwindow->thread->edl->session->time_format = new_value;
        hms->update(new_value == TIME_HMS);
        hmsf->update(new_value == TIME_HMSF);
+       timecode->update(new_value == TIME_TIMECODE);
        samples->update(new_value == TIME_SAMPLES);
        hex->update(new_value == TIME_SAMPLES_HEX);
        frames->update(new_value == TIME_FRAMES);
@@ -295,6 +305,16 @@ int TimeFormatHMSF::handle_event()
        return 1;
 }
 
+TimeFormatTimecode::TimeFormatTimecode(PreferencesWindow *pwindow, AppearancePrefs *tfwindow, int value, int x, int y)
+ : BC_Radial(x, y, value, TIME_TIMECODE_TEXT)
+{ this->pwindow = pwindow; this->tfwindow = tfwindow; }
+
+int TimeFormatTimecode::handle_event()
+{
+       tfwindow->update(TIME_TIMECODE);
+       return 1;
+}
+
 TimeFormatSamples::TimeFormatSamples(PreferencesWindow *pwindow, AppearancePrefs *tfwindow, int value, int x, int y)
  : BC_Radial(x, y, value, TIME_SAMPLES_TEXT)
 { this->pwindow = pwindow; this->tfwindow = tfwindow; }
@@ -702,6 +722,19 @@ int DeactivateFocusPolicy::handle_event()
        return 1;
 }
 
+AutoRotate::AutoRotate(PreferencesWindow *pwindow, int x, int y)
+ : BC_CheckBox(x, y, pwindow->thread->preferences->auto_rotate != 0,
+       _("Auto rotate ffmpeg media"))
+{
+       this->pwindow = pwindow;
+}
+
+int AutoRotate::handle_event()
+{
+       pwindow->thread->preferences->auto_rotate = get_value();
+       return 1;
+}
+
 ForwardRenderDisplacement::ForwardRenderDisplacement(PreferencesWindow *pwindow, int x, int y)
  : BC_CheckBox(x, y, pwindow->thread->preferences->forward_render_displacement,
        _("Always show next frame"))
index e7ef3426f1a5d2071ed0872eeb5fc68dddbf1ad5..96a6a41e85721a9751a0a1e60c2e4387d29fc3ac 100644 (file)
@@ -42,6 +42,7 @@ public:
        int update(int new_value);
        TimeFormatHMS *hms;
        TimeFormatHMSF *hmsf;
+       TimeFormatTimecode *timecode;
        TimeFormatSamples *samples;
        TimeFormatHex *hex;
        TimeFormatFrames *frames;
@@ -76,6 +77,15 @@ public:
        AppearancePrefs *tfwindow;
 };
 
+class TimeFormatTimecode : public BC_Radial
+{
+public:
+       TimeFormatTimecode(PreferencesWindow *pwindow, AppearancePrefs *tfwindow, int value, int x, int y);
+       int handle_event();
+       PreferencesWindow *pwindow;
+       AppearancePrefs *tfwindow;
+};
+
 class TimeFormatSamples : public BC_Radial
 {
 public:
@@ -311,6 +321,14 @@ public:
        PreferencesWindow *pwindow;
 };
 
+class AutoRotate: public BC_CheckBox
+{
+public:
+       AutoRotate(PreferencesWindow *pwindow, int x, int y);
+       int handle_event();
+       PreferencesWindow *pwindow;
+};
+
 class ForwardRenderDisplacement : public BC_CheckBox
 {
 public:
index 6b117a93faf5f89998af7b1fd036f0053814c11a..5515b3fdc0af71938a739ce351ca2bb673f993b0 100644 (file)
@@ -25,6 +25,7 @@
 class AppearancePrefs;
 class TimeFormatHMS;
 class TimeFormatHMSF;
+class TimeFormatTimecode;
 class TimeFormatSamples;
 class TimeFormatFrames;
 class TimeFormatHex;
@@ -50,6 +51,7 @@ class PopupMenuBtnup;
 class GrabFocusPolicy;
 class ActivateFocusPolicy;
 class DeactivateFocusPolicy;
+class AutoRotate;
 class ForwardRenderDisplacement;
 class HighlightInverseColor;
 class YuvColorSpace;
index 751660ad9cc06aec8a70d82f890b2f258d2c59b2..fab58403d7c54cfaefccc06232ffcf45d9f97589 100644 (file)
@@ -167,6 +167,7 @@ void Asset::reset_video()
        proxy_scale = 0; // not a proxy
        proxy_edl = 0; // not proxy from edl
        video_length = 0;
+       timecode = -2; // unknown
        single_frame = 0;
        vmpeg_cmodel = BC_YUV420P;
        frame_rate = 0;
@@ -217,6 +218,7 @@ void Asset::copy_format(Asset *asset, int do_index)
        use_header = asset->use_header;
        aspect_ratio = asset->aspect_ratio;
        interlace_mode = asset->interlace_mode;
+       timecode = asset->timecode;
 
        video_data = asset->video_data;
        layers = asset->layers;
@@ -378,7 +380,8 @@ int Asset::equivalent(Asset &asset, int test_audio, int test_video, EDL *edl)
                result = (layers == asset.layers &&
                        program == asset.program &&
                        frame_rate == asset.frame_rate &&
-                       asset.interlace_mode    == interlace_mode &&
+                       asset.interlace_mode == interlace_mode &&
+                       asset.timecode == timecode &&
                        width == asset.width &&
                        height == asset.height &&
                        !strcmp(vcodec, asset.vcodec) &&
@@ -482,6 +485,7 @@ int Asset::read_video(FileXML *file)
        file->tag.get_property("VCODEC", vcodec);
 
        video_length = file->tag.get_property("VIDEO_LENGTH", (int64_t)0);
+       timecode = file->tag.get_property("TIMECODE", -2);
        mov_sphere = file->tag.get_property("MOV_SPHERE", 0);
        jpeg_sphere = file->tag.get_property("JPEG_SPHERE", 0);
        single_frame = file->tag.get_property("SINGLE_FRAME", (int64_t)0);
@@ -649,6 +653,7 @@ int Asset::write_video(FileXML *file)
                file->tag.set_property("VCODEC", vcodec);
 
        file->tag.set_property("VIDEO_LENGTH", video_length);
+       file->tag.set_property("TIMECODE", timecode);
        file->tag.set_property("MOV_SPHERE", mov_sphere);
        file->tag.set_property("JPEG_SPHERE", jpeg_sphere);
        file->tag.set_property("SINGLE_FRAME", single_frame);
@@ -995,11 +1000,10 @@ int Asset::dump(FILE *fp)
                " height %d vcodec %s aspect_ratio %f ilace_mode %s\n",
                video_data, layers, program, frame_rate, width, height,
                vcodec, aspect_ratio,string);
-       fprintf(fp,"   actual_width %d actual_height %d proxy_scale %d proxy_edl %d"
-               " video_length %jd repeat %d\n",
-               actual_width, actual_height, proxy_scale, proxy_edl, video_length,
-               single_frame);
-       fprintf(fp,"   video_length %jd repeat %d\n", video_length, single_frame);
+       fprintf(fp,"   actual_width %d actual_height %d proxy_scale %d proxy_edl %d\n",
+               actual_width, actual_height, proxy_scale, proxy_edl);
+       fprintf(fp,"   video_length %jd repeat %d timecode %f\n",
+               video_length, single_frame, timecode);
        fprintf(fp,"   mov_sphere=%d jpeg_sphere=%d\n", mov_sphere, jpeg_sphere);
        return 0;
 }
index ce922c22c1e61aaed9c35a73f0a348d975b74527..bb40ed4a32957a3577c2f021e6eff6bde48e346a 100644 (file)
 #include "pluginserver.inc"
 
 
-// Time code formats
-#define TC_DROPFRAME 0
-#define TC_NONDROPFRAME 1
-#define TC_PAL 2
-#define TC_FILM 3
-
 class Asset : public Indexable, public ListItem<Asset>
 {
 public:
@@ -155,8 +149,8 @@ public:
 // Length in frames
 // -1 means a still photo
        int64_t video_length;
-
-
+// timecode, unknown=-2, no timecode=-1, timecode>=0 
+       double timecode;
 
 // mp3 compression
        int mp3_bitrate;
index 6222f1c6913a05a6b686fa36f15f196c77e41abd..e603ad8cabba5eaa0aad4fd5060c86252a70258a 100644 (file)
@@ -104,6 +104,14 @@ void AssetEdit::edit_asset(Indexable *indexable, int x, int y)
 
 void AssetEdit::handle_done_event(int result)
 {
+       if( !result && changed_params->timecode >= 0 ) {
+               double rate = indexable->get_frame_rate();
+               changed_params->timecode =
+                       atoi(window->tc_hrs->get_text()) * 3600 +
+                       atoi(window->tc_mins->get_text()) * 60 +
+                       atoi(window->tc_secs->get_text()) +
+                       atoi(window->tc_rest->get_text()) / rate;
+       }
 }
 
 void AssetEdit::handle_close_event(int result)
@@ -187,14 +195,16 @@ BC_Window* AssetEdit::new_gui()
 
 int AssetEdit::window_height()
 {
-       int h = 128 + 64;
-       if( indexable->have_audio() ) h += 200;
+       int h = yS(128 + 64);
+       if( indexable->have_audio() ) h += yS(200);
        if( indexable->have_video() ) {
-               h += 160;
+               h += yS(160);
                if( indexable->is_asset ) {
                        Asset *asset = (Asset *)indexable;
                        if( File::can_scale_input(asset) )
-                               h += 42;
+                               h += yS(42);
+                       if( asset->timecode >= 0 )
+                               h += yS(32);
                }
        }
        return yS(h);
@@ -516,8 +526,35 @@ void AssetEditWindow::create_objects()
                                &asset_edit->changed_params->interlace_mode,
                                (ArrayList<BC_ListBoxItem*>*)&mwindow->interlace_asset_modes,
                                x2 + edit_ilace_mode->get_w(), y));
+                       y += title->get_h() + yS(15);
                }
        }
+       if( asset && asset->timecode >= 0 ) {
+               char text[BCSTRLEN], *tc = text;
+               Units::totext(tc, asset->timecode, TIME_HMSF,
+                       asset->sample_rate, asset->frame_rate);
+               const char *hrs  = tc;  tc = strchr(tc, ':');  *tc++ = 0;
+               const char *mins = tc;  tc = strchr(tc, ':');  *tc++ = 0;
+               const char *secs = tc;  tc = strchr(tc, ':');  *tc++ = 0;
+               const char *rest = tc;
+               int padw = BC_Title::calculate_w(this, ":", MEDIUMFONT);
+               int fldw = BC_Title::calculate_w(this, "00", MEDIUMFONT) + 5;
+               int hdrw = fldw + padw;  x = x2;
+               add_subwindow(title = new BC_Title(x, y, _("hour"), SMALLFONT));  x += hdrw;
+               add_subwindow(title = new BC_Title(x, y, _("min"),  SMALLFONT));  x += hdrw;
+               add_subwindow(title = new BC_Title(x, y, _("sec"),  SMALLFONT));  x += hdrw;
+               add_subwindow(title = new BC_Title(x, y, _("frms"), SMALLFONT));
+               y += title->get_h() + xS(3);
+               add_subwindow(title = new BC_Title(x1, y, _("Time Code Start:")));
+               add_subwindow(tc_hrs = new BC_TextBox(x=x2, y, fldw, 1, hrs));
+               add_subwindow(new BC_Title(x += tc_hrs->get_w(), y, ":"));
+               add_subwindow(tc_mins = new BC_TextBox(x += padw, y, fldw, 1, mins));
+               add_subwindow(new BC_Title(x += tc_mins->get_w(), y, ":"));
+               add_subwindow(tc_secs = new BC_TextBox(x += padw, y , fldw, 1, secs));
+               add_subwindow(new BC_Title(x += tc_secs->get_w(), y, ":"));
+               add_subwindow(tc_rest = new BC_TextBox(x += 10, y, fldw, 1, rest));
+               y += title->get_h() + ypad5;
+       }
 
        add_subwindow(new BC_OKButton(this));
        add_subwindow(new BC_CancelButton(this));
index d74262938b847f961f836d7be5aa4c093a44ea84..24e0166ff90f31e5b74dad3436b3e533061bb9af 100644 (file)
@@ -80,6 +80,8 @@ public:
        AssetEdit *asset_edit;
        BC_Title *win_width;
        BC_Title *win_height;
+       BC_TextBox *tc_hrs, *tc_mins;
+       BC_TextBox *tc_secs, *tc_rest;
        DetailAssetDialog *detail_dialog;
        void show_info_detail();
 
index bc4281642a3563acd1bfb4c671c72ae77d4a0849..9eda28d72e521d62e515118400463b5377234b1b 100644 (file)
@@ -99,19 +99,15 @@ void AssetPopup::create_objects()
 
 void AssetPopup::paste_assets()
 {
-// Collect items into the drag vectors for temporary storage
-       gui->lock_window("AssetPopup::paste_assets");
-       mwindow->gui->lock_window("AssetPopup::paste_assets");
-       mwindow->cwindow->gui->lock_window("AssetPopup::paste_assets");
-
        int proxy = mwindow->edl->session->awindow_folder == AW_PROXY_FOLDER ? 1 : 0;
        gui->collect_assets(proxy);
+// Collect items into the drag vectors for temporary storage
+       gui->unlock_window();
+       mwindow->gui->lock_window("AssetPopup::paste_assets");
        mwindow->paste_assets(mwindow->edl->local_session->get_selectionstart(1),
                mwindow->edl->tracks->first, 0);   // do not overwrite
-
-       gui->unlock_window();
        mwindow->gui->unlock_window();
-       mwindow->cwindow->gui->unlock_window();
+       gui->lock_window("AssetPopup::paste_assets");
 }
 
 void AssetPopup::match_size()
@@ -302,8 +298,10 @@ AssetPopupBuildIndex::~AssetPopupBuildIndex()
 
 int AssetPopupBuildIndex::handle_event()
 {
+       popup->unlock_window();
 //printf("AssetPopupBuildIndex::handle_event 1\n");
        mwindow->rebuild_indices();
+       popup->lock_window("AssetPopupBuildIndex::handle_event");
        return 1;
 }
 
@@ -394,9 +392,11 @@ AssetPopupOpenMixer::~AssetPopupOpenMixer()
 
 int AssetPopupOpenMixer::handle_event()
 {
+       popup->unlock_window();
        mwindow->gui->lock_window("AssetPopupOpenMixer::handle_event");
        mwindow->create_mixers();
        mwindow->gui->unlock_window();
+       popup->lock_window("AssetPopupOpenMixer::handle_event");
        return 1;
 }
 
@@ -413,9 +413,11 @@ AssetPopupInsertMixer::~AssetPopupInsertMixer()
 
 int AssetPopupInsertMixer::handle_event()
 {
+       popup->unlock_window();
        mwindow->gui->lock_window("AssetPopupInsertMixer::handle_event");
        mwindow->create_mixers(-1);
        mwindow->gui->unlock_window();
+       popup->lock_window("AssetPopupInsertMixer::handle_event");
        return 1;
 }
 
index 0694cc2fd445fc6232625f33ad13177e47f6b102..6dcdbc62f69fd7bf8fcd0015f011de87b2e93786 100644 (file)
@@ -181,7 +181,9 @@ char *BatchRenderJob::create_script(EDL *edl, ArrayList<Indexable *> *idxbls)
 
 int BatchRenderJob::get_strategy()
 {
-       return Render::get_strategy(farmed, labeled);
+       int range = File::is_image_render(asset->format) ?
+               RANGE_1FRAME : RANGE_SELECTION;
+       return Render::get_strategy(farmed, labeled, range);
 }
 
 
index 7f5ddf5fa87ef189fc4d7e05474ce94129e77e78..6ac2035c0bb9956f86b2cd741cda4d476cbf0580 100644 (file)
@@ -761,7 +761,8 @@ CWindowEditing::CWindowEditing(MWindow *mwindow, CWindow *cwindow)
                0, // use_goto
                1, // use_clk2play
                1, // use_scope
-               0) // use_gang_tracks
+               0, // use_gang_tracks
+               0) // use_timecode
 {
        this->mwindow = mwindow;
        this->cwindow = cwindow;
index 41394277fc1de8aa66199a9567005c8e821c5089..1f75414f45a68d668661f96a24e3cbca3af9c4e5 100644 (file)
@@ -76,7 +76,8 @@ EditPanel::EditPanel(MWindow *mwindow,
        int use_goto,
        int use_clk2play,
        int use_scope,
-       int use_gang_tracks)
+       int use_gang_tracks,
+       int use_timecode)
 {
        this->window_id = window_id;
        this->editing_mode = editing_mode;
@@ -100,6 +101,7 @@ EditPanel::EditPanel(MWindow *mwindow,
        this->use_clk2play = use_clk2play;
        this->use_scope = use_scope;
        this->use_gang_tracks = use_gang_tracks;
+       this->use_timecode = use_timecode;
 
        this->x = x;
        this->y = y;
@@ -322,6 +324,12 @@ void EditPanel::create_buttons()
                scope_dialog = new EditPanelScopeDialog(mwindow, this);
        }
 
+       if( use_timecode ) {
+               timecode = new EditPanelTimecode(mwindow, this, x1, y1);
+               subwindow->add_subwindow(timecode);
+               x1 += timecode->get_w();
+       }
+
        if( use_gang_tracks ) {
                gang_tracks = new EditPanelGangTracks(mwindow, this, x1, y1-yS(1));
                subwindow->add_subwindow(gang_tracks);
@@ -448,6 +456,10 @@ void EditPanel::reposition_buttons(int x, int y)
                scope->reposition_window(x1, y1-yS(1));
                x1 += scope->get_w();
        }
+       if( use_timecode ) {
+               timecode->reposition_window(x1, y1);
+               x1 += timecode->get_w();
+       }
 
        if( use_meters ) {
                meters->reposition_window(x1, y1);
@@ -1366,3 +1378,195 @@ int EditPanelGangTracks::handle_event()
        return 1;
 }
 
+
+EditPanelTimecode::EditPanelTimecode(MWindow *mwindow,
+       EditPanel *panel, int x, int y)
+ : BC_Button(x, y, mwindow->theme->get_image_set("clapperbutton"))
+{
+       this->mwindow = mwindow;
+       this->panel = panel;
+       tc_dialog = 0;
+       set_tooltip(_("Set Timecode"));
+}
+
+EditPanelTimecode::~EditPanelTimecode()
+{
+       delete tc_dialog;
+}
+
+int EditPanelTimecode::handle_event()
+{
+       if( !tc_dialog )
+               tc_dialog = new EditPanelTcDialog(mwindow, panel);
+       int px, py;
+       get_pop_cursor(px, py, 0);
+       tc_dialog->start_dialog(px, py);
+       return 1;
+}
+
+EditPanelTcDialog::EditPanelTcDialog(MWindow *mwindow, EditPanel *panel)
+ : BC_DialogThread()
+{
+       this->mwindow = mwindow;
+       this->panel = panel;
+       tc_gui = 0;
+       px = py = 0;
+}
+
+EditPanelTcDialog::~EditPanelTcDialog()
+{
+       close_window();
+}
+
+#define TCW_W xS(200)
+#define TCW_H yS(120)
+
+void EditPanelTcDialog::start_dialog(int px, int py)
+{
+       this->px = px - TCW_W/2;
+       this->py = py - TCW_H/2;
+       start();
+}
+
+BC_Window *EditPanelTcDialog::new_gui()
+{
+       tc_gui = new EditPanelTcWindow(this, px, py);
+       tc_gui->create_objects();
+       double timecode = mwindow->get_timecode_offset();
+       tc_gui->update(timecode);
+       tc_gui->show_window();
+       return tc_gui;
+}
+
+void EditPanelTcDialog::handle_done_event(int result)
+{
+       if( result ) return;
+       double ofs = tc_gui->get_timecode();
+       mwindow->set_timecode_offset(ofs);
+}
+
+EditPanelTcWindow::EditPanelTcWindow(EditPanelTcDialog *tc_dialog, int x, int y)
+ : BC_Window(_(PROGRAM_NAME ": Timecode"), x, y,
+       TCW_W, TCW_H, TCW_W, TCW_H, 0, 0, 1)
+{
+       this->tc_dialog = tc_dialog;
+}
+
+EditPanelTcWindow::~EditPanelTcWindow()
+{
+}
+
+double EditPanelTcWindow::get_timecode()
+{
+       int hrs = atoi(hours->get_text());
+       int mins = atoi(minutes->get_text());
+       int secs = atoi(seconds->get_text());
+       int frms = atoi(frames->get_text());
+       double frame_rate = tc_dialog->mwindow->edl->session->frame_rate;
+       double timecode = hrs*3600 + mins*60 + secs + frms/frame_rate;
+       return timecode;
+}
+
+void EditPanelTcWindow::update(double timecode)
+{
+       if( timecode < 0 ) timecode = 0;
+       int64_t pos = timecode;
+       int hrs = pos/3600;
+       int mins = pos/60 - hrs*60;
+       int secs = pos - hrs*3600 - mins*60;
+       double frame_rate = tc_dialog->mwindow->edl->session->frame_rate;
+       int frms = (timecode-pos) * frame_rate;
+       hours->update(hrs);
+       minutes->update(mins);
+       seconds->update(secs);
+       frames->update(frms);
+}
+
+void EditPanelTcWindow::create_objects()
+{
+       lock_window("EditPanelTcWindow::create_objects");
+       int x = xS(20), y = yS(5);
+       BC_Title *title = new BC_Title(x - 2, y, _("hour  min   sec   frms"), SMALLFONT);
+       add_subwindow(title);  y += title->get_h() + xS(3);
+       hours = new EditPanelTcInt(this, x, y, xS(26), 99, "%02i");
+       add_subwindow(hours);    x += hours->get_w() + xS(4);
+       minutes = new EditPanelTcInt(this, x, y, xS(26), 59, "%02i");
+       add_subwindow(minutes);  x += minutes->get_w() + xS(4);
+       seconds = new EditPanelTcInt(this, x, y, xS(26), 60, "%02i");
+       add_subwindow(seconds);  x += seconds->get_w() + xS(4);
+       frames = new EditPanelTcInt(this, x, y, xS(34), 999, "%03i");
+       add_subwindow(frames);   x += frames->get_w() + xS(16);
+       add_subwindow(new EditPanelTcReset(this, x, y));
+       double timecode = tc_dialog->mwindow->get_timecode_offset();
+       update(timecode);
+       add_subwindow(new BC_OKButton(this));
+       add_subwindow(new BC_CancelButton(this));
+       unlock_window();
+}
+
+EditPanelTcReset::EditPanelTcReset(EditPanelTcWindow *window, int x, int y)
+ : BC_Button(x, y, window->tc_dialog->mwindow->theme->get_image_set("reset_button"))
+{
+       this->window = window;
+}
+
+int EditPanelTcReset::handle_event()
+{
+       window->update(0);
+       return 1;
+}
+
+
+EditPanelTcInt::EditPanelTcInt(EditPanelTcWindow *window, int x, int y, int w,
+       int max, const char *format)
+ : BC_TextBox(x, y, w, 1, "")
+{
+       this->window = window;
+       this->max = max;
+       this->format = format;
+       digits = 1;
+       for( int m=max; (m/=10)>0; ++digits );
+}
+
+EditPanelTcInt::~EditPanelTcInt()
+{
+}
+
+int EditPanelTcInt::handle_event()
+{
+       int v = atoi(get_text());
+       if( v > max ) {
+               v = v % (max+1);
+               char string[BCSTRLEN];
+               sprintf(string, format, v);
+               BC_TextBox::update(string);
+       }
+       return 1;
+}
+
+void EditPanelTcInt::update(int v)
+{
+       char text[BCTEXTLEN];
+       if( v > max ) v = max;
+       sprintf(text, format, v);
+       BC_TextBox::update(text);
+}
+
+int EditPanelTcInt::keypress_event()
+{
+       if( (int)strlen(get_text()) >= digits )
+               BC_TextBox::update("");
+       int key = get_keypress();
+       switch( key ) {
+       case TAB:   case LEFTTAB:
+       case LEFT:  case RIGHT:
+       case HOME:  case END:
+       case BACKSPACE:
+       case DELETE:
+       case '0': case '1': case '2': case '3': case '4':
+       case '5': case '6': case '7': case '8': case '9':
+               return BC_TextBox::keypress_event();
+       }
+       return 1;
+}
+
index 9adbaf62b567be40dc2c015ff9f7ebf45a211b4b..5a2bafe4a23e5953e33481b7fd8528df9c06657a 100644 (file)
@@ -404,6 +404,71 @@ public:
        MWindow *mwindow;
 };
 
+class EditPanelTimecode : public BC_Button
+{
+public:
+       EditPanelTimecode(MWindow *mwindow, EditPanel *panel, int x, int y);
+       ~EditPanelTimecode();
+       int handle_event();
+       MWindow *mwindow;
+       EditPanel *panel;
+       EditPanelTcDialog *tc_dialog;
+};
+
+class EditPanelTcDialog : public BC_DialogThread
+{
+public:
+       EditPanelTcDialog(MWindow *mwindow, EditPanel *panel);
+       ~EditPanelTcDialog();
+       BC_Window *new_gui();
+       void start_dialog(int px, int py);
+       void handle_done_event(int result);
+
+       MWindow *mwindow;
+       EditPanel *panel;
+       EditPanelTcWindow *tc_gui;
+       int px, py;
+};
+
+class EditPanelTcWindow : public BC_Window
+{
+public:
+       EditPanelTcWindow(EditPanelTcDialog *tc_dialog, int x, int y);
+       ~EditPanelTcWindow();
+       void create_objects();
+       double get_timecode();
+       void update(double timecode);
+
+       EditPanelTcDialog *tc_dialog;
+       EditPanelTcInt *hours;
+       EditPanelTcInt *minutes;
+       EditPanelTcInt *seconds;
+       EditPanelTcInt *frames;
+};
+
+class EditPanelTcInt : public BC_TextBox
+{
+public:
+       EditPanelTcInt(EditPanelTcWindow *window, int x, int y, int w,
+               int max, const char *format);
+       ~EditPanelTcInt();
+       int handle_event();
+       int keypress_event();
+       void update(int v);
+
+       EditPanelTcWindow *window;
+       int max, digits;
+       const char *format;
+};
+
+class EditPanelTcReset : public BC_Button
+{
+public:
+       EditPanelTcReset(EditPanelTcWindow *window, int x, int y);
+       int handle_event();
+
+       EditPanelTcWindow *window;
+};
 
 class EditPanel
 {
@@ -428,7 +493,8 @@ public:
                int use_goto,
                int use_clk2play,
                int use_scope,
-               int use_gang_tracks);
+               int use_gang_tracks,
+               int use_timecode);
        ~EditPanel();
 
        void set_meters(MeterPanel *meter_panel);
@@ -493,6 +559,7 @@ public:
        int use_clk2play;
        int use_scope;
        int use_gang_tracks;
+       int use_timecode;
 
        EditFit *fit;
        EditFitAutos *fit_autos;
@@ -508,6 +575,7 @@ public:
        EditManualGoto *mangoto;
        EditClick2Play *click2play;
        EditPanelScope *scope;
+       EditPanelTimecode *timecode;
        EditPanelScopeDialog *scope_dialog;
        EditCopy *copy;
        EditPaste *paste;
index 5b24a831cf6bc7e779015bee45a8a5fefee8a8b0..e6db6bf47f4142ca24b747b6b021a6e328bcd0da 100644 (file)
@@ -57,6 +57,9 @@ class LockLabelsButton;
 class EditPanelScopeGUI;
 class EditPanelScopeDialog;
 class EditPanelScope;
-class EditPanelMashMixers;
+class EditPanelTimecode;
+class EditPanelTcDialog;
+class EditPanelTcWindow;
+class EditPanelTcInt;
 
 #endif
index 45d7d87a0a696e918e9fb337d97de799858c1b27..ff66a88bc2792a4d3f3d223e8a6a8b94cd89ceef 100644 (file)
@@ -79,6 +79,7 @@ void EditPopup::create_objects()
        add_item(new EditPopupOverwritePlugins(mwindow, this));
        add_item(new EditCollectEffects(mwindow, this));
        add_item(new EditPasteEffects(mwindow, this));
+       add_item(new EditPopupTimecode(mwindow, this));
 }
 
 int EditPopup::activate_menu(Track *track, Edit *edit,
@@ -339,4 +340,32 @@ int EditPasteEffects::handle_event()
        return 1;
 }
 
+EditPopupTimecode::EditPopupTimecode(MWindow *mwindow, EditPopup *popup)
+ : BC_MenuItem(_("Timecode"),_("Ctrl-!"),'!')
+{
+       this->mwindow = mwindow;
+       this->popup = popup;
+       set_ctrl(1);
+}
+
+int EditPopupTimecode::handle_event()
+{
+       if( mwindow->session->current_operation != NO_OPERATION ) return 1;
+       Edit *edit = popup->edit;
+       if( !edit || !edit->asset ) return 1;
+       Asset *asset = edit->asset;
+       double timecode = asset->timecode != -2 ? asset->timecode :
+               FFMPEG::get_timecode(asset->path,
+                       edit->track->data_type, edit->channel,
+                       mwindow->edl->session->frame_rate);
+       asset->timecode = timecode;
+       if( timecode >= 0 ) {
+               int64_t pos = edit->startproject + edit->startsource;
+               double position = edit->track->from_units(pos);
+               mwindow->set_timecode_offset(timecode - position);
+       }
+       else
+               mwindow->set_timecode_offset(0);
+       return 1;
+}
 
index 4c19b71779a86f397d5bbb0023a316b0da3c15cb..2c34be62489d2e0864f2db30aedabe26ecc52e4c 100644 (file)
@@ -181,4 +181,13 @@ public:
        EditPopup *popup;
 };
 
+class EditPopupTimecode : public BC_MenuItem
+{
+public:
+       EditPopupTimecode(MWindow *mwindow, EditPopup *popup);
+       int handle_event();
+       MWindow *mwindow;
+       EditPopup *popup;
+};
+
 #endif
index 142afb9616ef2dca5c5c18261495c85a48bb48f9..6620ce3c6d8137e0426d0d761f0b790ed0853800 100644 (file)
@@ -36,5 +36,6 @@ class EditPopupOverwrite;
 class EditPopupOverwritePlugins;
 class EditCollectEffects;
 class EditPasteEffects;
+class EditPopupTimecode;
 
 #endif
index de93e5830d351d41ce3cd68e7a489f999e2c1312..b2bcaf98f1cf40eacdef8f6b19aa5c938781f10e 100644 (file)
@@ -31,6 +31,7 @@
 #include "edits.h"
 #include "edl.h"
 #include "edlsession.h"
+#include "ffmpeg.h"
 #include "file.h"
 #include "filexml.h"
 #include "filesystem.h"
@@ -838,3 +839,58 @@ void Edits::shift_effects_recursive(int64_t position, int64_t length, int edit_a
        track->shift_effects(position, length, edit_autos, 0);
 }
 
+double Edits::early_timecode()
+{
+       double result = -1;
+       for( Edit *edit=first; edit; edit=edit->next ) {
+               Asset *asset = edit->asset;
+               if( !asset ) continue;
+               if( asset->timecode < -1 )
+                       asset->timecode = FFMPEG::get_timecode(asset->path,
+                               track->data_type, edit->channel,
+                               edl->session->frame_rate);
+               if( asset->timecode < 0 ) continue;
+               if( result < 0 ||  result > asset->timecode )
+                       result = asset->timecode;
+       }
+       return result;
+}
+
+void Edits::align_timecodes(double offset)
+{
+       for( Edit *edit=first, *next=0; edit; edit=next ) {
+               next = edit->next;
+               if( edit->silence() ) delete edit;
+       }
+       for( Edit *edit=first, *next=0; edit; edit=next ) {
+               next = edit->next;
+               Asset *asset = edit->asset;
+               if( !asset && asset->timecode < 0 ) continue;
+               double position = asset->timecode - offset;
+               edit->startproject = track->to_units(position, 1) + edit->startsource;
+       }
+       int result = 1;
+       while( result ) {
+               result = 0;
+               for( Edit *edit=first, *next=0; edit; edit=next ) {
+                       next = edit->next;
+                       if( !next || next->startproject >= edit->startproject ) continue;
+                       swap(next, edit);
+                       next = edit;
+                       result = 1;
+               }
+       }
+       int64_t startproject = 0;
+       for( Edit *edit=first; edit; edit=edit->next ) {
+               int64_t length = edit->startproject - startproject;
+               if( length > 0 ) {
+                       Edit *new_edit = create_edit();
+                       insert_before(edit, new_edit);
+                       new_edit->startproject = startproject;
+                       new_edit->length = length;
+                       startproject = edit->startproject;
+               }
+               startproject += edit->length;
+       }
+}
+
index 76c0171da50c69c7b792f35f167ee66cfb1107b1..f38b0767256d0c7759f0e2fe1474000ef96fd7a7 100644 (file)
@@ -84,6 +84,8 @@ public:
        void paste_silence(int64_t start, int64_t end);
 // Returns the newly created edit
        Edit *create_silence(int64_t start, int64_t end);
+       double early_timecode();
+       void align_timecodes(double offset);
 
        void resample(double old_rate, double new_rate);
 // Shift edits on or after position by distance
index 74eb3471d944ea832c0165c19bc98cf2952e2838..673ec070f77bb563f8a89e0c8c6d6600d24a42f8 100644 (file)
@@ -134,6 +134,7 @@ EDLSession::EDLSession(EDL *edl)
        si_duration = 3;
        test_playback_edits = 1;
        time_format = TIME_HMSF;
+       timecode_offset = 0;
        nudge_format = 1;
        tool_window = 0;
        for(int i = 0; i < MAXCHANNELS; i++) {
@@ -328,6 +329,7 @@ int EDLSession::load_defaults(BC_Hash *defaults)
        gang_tracks = defaults->get("GANG_TRACKS", GANG_NONE);
 //     test_playback_edits = defaults->get("TEST_PLAYBACK_EDITS", 1);
        time_format = defaults->get("TIME_FORMAT", TIME_HMSF);
+       timecode_offset = defaults->get("TIMECODE_OFFSET", timecode_offset);
        nudge_format = defaults->get("NUDGE_FORMAT", 1);
        tool_window = defaults->get("TOOL_WINDOW", 0);
        vconfig_in->load_defaults(defaults);
@@ -467,6 +469,7 @@ int EDLSession::save_defaults(BC_Hash *defaults)
        defaults->update("GANG_TRACKS", gang_tracks);
 //     defaults->update("TEST_PLAYBACK_EDITS", test_playback_edits);
        defaults->update("TIME_FORMAT", time_format);
+       defaults->update("TIMECODE_OFFSET", timecode_offset);
        defaults->update("NUDGE_FORMAT", nudge_format);
        defaults->update("TOOL_WINDOW", tool_window);
        vconfig_in->save_defaults(defaults);
@@ -652,6 +655,7 @@ int EDLSession::load_xml(FileXML *file,
                gang_tracks = file->tag.get_property("GANG_TRACKS", GANG_NONE);
 //             test_playback_edits = file->tag.get_property("TEST_PLAYBACK_EDITS", test_playback_edits);
                time_format = file->tag.get_property("TIME_FORMAT", time_format);
+               timecode_offset = file->tag.get_property("TIMECODE_OFFSET", timecode_offset);
                nudge_format = file->tag.get_property("NUDGE_FORMAT", nudge_format);
                tool_window = file->tag.get_property("TOOL_WINDOW", tool_window);
                vwindow_meter = file->tag.get_property("VWINDOW_METER", vwindow_meter);
@@ -722,6 +726,7 @@ int EDLSession::save_xml(FileXML *file)
        file->tag.set_property("GANG_TRACKS", gang_tracks);
        file->tag.set_property("TEST_PLAYBACK_EDITS", test_playback_edits);
        file->tag.set_property("TIME_FORMAT", time_format);
+       file->tag.set_property("TIMECODE_OFFSET", timecode_offset);
        file->tag.set_property("NUDGE_FORMAT", nudge_format);
        file->tag.set_property("TOOL_WINDOW", tool_window);
        file->tag.set_property("VWINDOW_METER", vwindow_meter);
@@ -899,6 +904,7 @@ int EDLSession::copy(EDLSession *session)
        gang_tracks = session->gang_tracks;
        test_playback_edits = session->test_playback_edits;
        time_format = session->time_format;
+       timecode_offset = session->timecode_offset;
        nudge_format = session->nudge_format;
        tool_window = session->tool_window;
        for(int i = 0; i < MAXCHANNELS; i++) {
index b2994f181e602f07d20449b44c2601c37a4994c0..eebe71f2bac092bcfb9a3c3564a8773f7796e69f 100644 (file)
@@ -208,8 +208,8 @@ public:
        int test_playback_edits;
 // Format to display times in
        int time_format;
-// Offset for timecode
-       int timecode_offset[4];
+// Offset for timecode units
+       double timecode_offset;
 // Format to display nudge in, either seconds or track units.
        int nudge_format;
 // Show tool window in CWindow
index 5ff87abc9779a9428d26d69bca3601da3718a13b..ba0e0a34ecdd9260c579297bb81302c134f49056 100644 (file)
@@ -1051,6 +1051,7 @@ FFVideoStream::FFVideoStream(FFMPEG *ffmpeg, AVStream *strm, int idx, int fidx)
 {
        this->idx = idx;
        width = height = 0;
+       transpose = 0;
        frame_rate = 0;
        aspect_ratio = 0;
        length = 0;
@@ -1659,6 +1660,44 @@ IndexMarks *FFVideoStream::get_markers()
        return !index_state ? 0 : index_state->video_markers[idx];
 }
 
+double FFVideoStream::get_rotation_angle()
+{
+       int size = 0;
+       int *matrix = (int*)av_stream_get_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, &size);
+       int len = size/sizeof(*matrix);
+       if( !matrix || len < 5 ) return 0;
+       const double s = 1/65536.;
+       double theta = (!matrix[0] && !matrix[3]) || (!matrix[1] && !matrix[4]) ? 0 :
+                atan2( s*matrix[1] / hypot(s*matrix[1], s*matrix[4]),
+                       s*matrix[0] / hypot(s*matrix[0], s*matrix[3])) * 180/M_PI;
+       return theta;
+}
+
+void FFVideoStream::flip()
+{
+       transpose = 0;
+       if( !ffmpeg->file_base ) return;
+       double theta = get_rotation_angle(), tolerance = 1;
+       if( fabs(theta-0) < tolerance ) return;
+        if( fabs(theta-90) < tolerance ) {
+               create_filter("transpose=clock", st->codecpar);
+               transpose = 1;
+        }
+       else if( fabs(theta-180) < tolerance ) {
+               create_filter("hflip", st->codecpar);
+               create_filter("vflip", st->codecpar);
+        }
+       else if (fabs(theta-270) < tolerance ) {
+               create_filter("transpose=cclock", st->codecpar);
+               transpose = 1;
+        }
+       else {
+               char rotate[BCSTRLEN];
+               sprintf(rotate, "rotate=%f", theta*M_PI/180.);
+               create_filter(rotate, st->codecpar);
+        }
+}
+
 
 FFMPEG::FFMPEG(FileBase *file_base)
 {
@@ -1722,9 +1761,8 @@ static inline AVRational std_frame_rate(int i)
        return (AVRational) { freq, 1001*12 };
 }
 
-AVRational FFMPEG::check_frame_rate(AVCodec *codec, double frame_rate)
+AVRational FFMPEG::check_frame_rate(const AVRational *p, double frame_rate)
 {
-       const AVRational *p = codec->supported_framerates;
        AVRational rate, best_rate = (AVRational) { 0, 0 };
        double max_err = 1.;  int i = 0;
        while( ((p ? (rate=*p++) : (rate=std_frame_rate(i++))), rate.num) != 0 ) {
@@ -2307,6 +2345,9 @@ int FFMPEG::info(char *text, int len)
                int hrs = secs/3600;  secs -= hrs*3600;
                int mins = secs/60;  secs -= mins*60;
                report("  %d:%02d:%05.2f\n", hrs, mins, secs);
+               double theta = vid->get_rotation_angle();
+               if( fabs(theta) > 1 ) 
+                       report("    rotation angle: %0.1f\n", theta);
        }
        if( ffaudio.size() > 0 )
                report("\n%d audio stream%s\n",ffaudio.size(), ffaudio.size()!=1 ? "s" : "");
@@ -2494,6 +2535,8 @@ int FFMPEG::open_decoder()
                        vid->reading = -1;
                        if( opt_video_filter )
                                ret = vid->create_filter(opt_video_filter, avpar);
+                       if( file_base && file_base->file->preferences->auto_rotate )
+                               vid->flip();
                        break; }
                case AVMEDIA_TYPE_AUDIO: {
                        if( avpar->channels < 1 ) continue;
@@ -2753,7 +2796,7 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                        int mask_h = (1<<desc->log2_chroma_h)-1;
                        ctx->height = (vid->height+mask_h) & ~mask_h;
                        ctx->sample_aspect_ratio = to_sample_aspect_ratio(asset);
-                       AVRational frame_rate = check_frame_rate(codec, vid->frame_rate);
+                       AVRational frame_rate = check_frame_rate(codec->supported_framerates, vid->frame_rate);
                        if( !frame_rate.num || !frame_rate.den ) {
                                eprintf(_("check_frame_rate failed %s\n"), filename);
                                ret = 1;
@@ -3310,25 +3353,29 @@ int FFMPEG::ff_total_vstreams()
 
 int FFMPEG::ff_video_width(int stream)
 {
-       return ffvideo[stream]->width;
+       FFVideoStream *vst = ffvideo[stream];
+       return !vst->transpose ? vst->width : vst->height;
 }
 
 int FFMPEG::ff_video_height(int stream)
 {
-       return ffvideo[stream]->height;
+       FFVideoStream *vst = ffvideo[stream];
+       return !vst->transpose ? vst->height : vst->width;
 }
 
 int FFMPEG::ff_set_video_width(int stream, int width)
 {
-       int w = ffvideo[stream]->width;
-       ffvideo[stream]->width = width;
+       FFVideoStream *vst = ffvideo[stream];
+       int *vw = !vst->transpose ? &vst->width : &vst->height, w = *vw;
+       *vw = width;
        return w;
 }
 
 int FFMPEG::ff_set_video_height(int stream, int height)
 {
-       int h = ffvideo[stream]->height;
-       ffvideo[stream]->height = height;
+       FFVideoStream *vst = ffvideo[stream];
+       int *vh = !vst->transpose ? &vst->height : &vst->width, h = *vh;
+       *vh = height;
        return h;
 }
 
@@ -3387,7 +3434,7 @@ int FFMPEG::ff_video_mpeg_color_range(int stream)
 
 int FFMPEG::ff_cpus()
 {
-       return file_base->file->cpus;
+       return !file_base ? 1 : file_base->file->cpus;
 }
 
 const char *FFMPEG::ff_hw_dev()
@@ -3416,14 +3463,17 @@ int FFVideoStream::create_filter(const char *filter_spec, AVCodecParameters *avp
        filter_graph = avfilter_graph_alloc();
        const AVFilter *buffersrc = avfilter_get_by_name("buffer");
        const AVFilter *buffersink = avfilter_get_by_name("buffersink");
+       int sa_num = avpar->sample_aspect_ratio.num;
+       if( !sa_num ) sa_num = 1;
+       int sa_den = avpar->sample_aspect_ratio.den;
+       if( !sa_den ) sa_num = 1;
 
        int ret = 0;  char args[BCTEXTLEN];
        AVPixelFormat pix_fmt = (AVPixelFormat)avpar->format;
        snprintf(args, sizeof(args),
                "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
                avpar->width, avpar->height, (int)pix_fmt,
-               st->time_base.num, st->time_base.den,
-               avpar->sample_aspect_ratio.num, avpar->sample_aspect_ratio.den);
+               st->time_base.num, st->time_base.den, sa_num, sa_den);
        if( ret >= 0 )
                ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
                        args, NULL, filter_graph);
@@ -3520,6 +3570,50 @@ int FFStream::create_filter(const char *filter_spec)
        return ret;
 }
 
+
+AVCodecContext *FFMPEG::activate_decoder(AVStream *st)
+{
+       AVDictionary *copts = 0;
+       av_dict_copy(&copts, opts, 0);
+       AVCodecID codec_id = st->codecpar->codec_id;
+       AVCodec *decoder = 0;
+       switch( st->codecpar->codec_type ) {
+       case AVMEDIA_TYPE_VIDEO:
+               if( opt_video_decoder )
+                       decoder = avcodec_find_decoder_by_name(opt_video_decoder);
+               else
+                       video_codec_remaps.update(codec_id, decoder);
+               break;
+       case AVMEDIA_TYPE_AUDIO:
+               if( opt_audio_decoder )
+                       decoder = avcodec_find_decoder_by_name(opt_audio_decoder);
+               else
+                       audio_codec_remaps.update(codec_id, decoder);
+               break;
+       default:
+               return 0;
+       }
+       if( !decoder && !(decoder = avcodec_find_decoder(codec_id)) ) {
+               eprintf(_("cant find decoder codec %d\n"), (int)codec_id);
+               return 0;
+       }
+       AVCodecContext *avctx = avcodec_alloc_context3(decoder);
+       if( !avctx ) {
+               eprintf(_("cant allocate codec context\n"));
+               return 0;
+       }
+       avcodec_parameters_to_context(avctx, st->codecpar);
+       if( !av_dict_get(copts, "threads", NULL, 0) )
+               avctx->thread_count = ff_cpus();
+       int ret = avcodec_open2(avctx, decoder, &copts);
+       av_dict_free(&copts);
+       if( ret < 0 ) {
+               avcodec_free_context(&avctx);
+               avctx = 0;
+       }
+       return avctx;
+}
+
 int FFMPEG::scan(IndexState *index_state, int64_t *scan_position, int *canceled)
 {
        AVPacket pkt;
@@ -3536,43 +3630,9 @@ int FFMPEG::scan(IndexState *index_state, int64_t *scan_position, int *canceled)
        index_state->add_audio_markers(ffaudio.size());
 
        for( int i=0; i<(int)fmt_ctx->nb_streams; ++i ) {
-               int ret = 0;
-               AVDictionary *copts = 0;
-               av_dict_copy(&copts, opts, 0);
                AVStream *st = fmt_ctx->streams[i];
-               AVCodecID codec_id = st->codecpar->codec_id;
-               AVCodec *decoder = 0;
-               switch( st->codecpar->codec_type ) {
-               case AVMEDIA_TYPE_VIDEO:
-                       if( opt_video_decoder )
-                               decoder = avcodec_find_decoder_by_name(opt_video_decoder);
-                       else
-                               video_codec_remaps.update(codec_id, decoder);
-                       break;
-               case AVMEDIA_TYPE_AUDIO:
-                       if( opt_audio_decoder )
-                               decoder = avcodec_find_decoder_by_name(opt_audio_decoder);
-                       else
-                               audio_codec_remaps.update(codec_id, decoder);
-                       break;
-               default:
-                       continue;
-               }
-               if( !decoder && !(decoder = avcodec_find_decoder(codec_id)) )
-                       continue;
-               AVCodecContext *avctx = avcodec_alloc_context3(decoder);
-               if( !avctx ) {
-                       eprintf(_("cant allocate codec context\n"));
-                       ret = AVERROR(ENOMEM);
-               }
-               if( ret >= 0 ) {
-                       avcodec_parameters_to_context(avctx, st->codecpar);
-                       if( !av_dict_get(copts, "threads", NULL, 0) )
-                               avctx->thread_count = ff_cpus();
-                       ret = avcodec_open2(avctx, decoder, &copts);
-               }
-               av_dict_free(&copts);
-               if( ret >= 0 ) {
+               AVCodecContext *avctx = activate_decoder(st);
+               if( avctx ) {
                        AVCodecParameters *avpar = st->codecpar;
                        switch( avpar->codec_type ) {
                        case AVMEDIA_TYPE_VIDEO: {
@@ -3731,3 +3791,246 @@ void FFStream::load_markers(IndexMarks &marks, double rate)
        }
 }
 
+
+/*
+ * 1) if the format context has a timecode
+ *   return fmt_ctx->timecode - 0
+ * 2) if the layer/channel has a timecode
+ *   return st->timecode - (start_time-nudge)
+ * 3) find the 1st program with stream, find 1st program video stream,
+ *   if video stream has a timecode, return st->timecode - (start_time-nudge)
+ * 4) find timecode in any stream, return st->timecode
+ * 5) read 100 packets, save ofs=pkt.pts*st->time_base - st->nudge:
+ *   decode frame for video stream of 1st program
+ *   if frame->timecode has a timecode, return frame->timecode - ofs
+ *   if side_data has gop timecode, return gop->timecode - ofs
+ *   if side_data has smpte timecode, return smpte->timecode - ofs
+ * 6) if the filename/url scans *date_time.ext, return date_time
+ * 7) if stat works on the filename/url, return mtime
+ * 8) return -1 failure
+*/
+double FFMPEG::get_initial_timecode(int data_type, int channel, double frame_rate)
+{
+       AVRational rate = check_frame_rate(0, frame_rate);
+       if( !rate.num ) return -1;
+// format context timecode
+       AVDictionaryEntry *tc = av_dict_get(fmt_ctx->metadata, "timecode", 0, 0);
+       if( tc ) return ff_get_timecode(tc->value, rate, 0);
+// stream timecode
+       if( open_decoder() ) return -1;
+       AVStream *st = 0;
+       int64_t nudge = 0;
+       int codec_type = -1, fidx = -1;
+       switch( data_type ) {
+       case TRACK_AUDIO: {
+               codec_type = AVMEDIA_TYPE_AUDIO;
+               int aidx = astrm_index[channel].st_idx;
+               FFAudioStream *aud = ffaudio[aidx];
+               fidx = aud->fidx;
+               nudge = aud->nudge;
+               st = aud->st;
+               break; }
+       case TRACK_VIDEO: {
+               codec_type = AVMEDIA_TYPE_VIDEO;
+               int vidx = vstrm_index[channel].st_idx;
+               FFVideoStream *vid = ffvideo[vidx];
+               fidx = vid->fidx;
+               nudge = vid->nudge;
+               st = vid->st;
+               break; }
+       }
+       if( codec_type < 0 ) return -1;
+       if( st )
+               tc = av_dict_get(st->metadata, "timecode", 0, 0);
+       if( !tc ) {
+               st = 0;
+// find first program which references this stream
+               int pidx = -1;
+               for( int i=0, m=fmt_ctx->nb_programs; pidx<0 && i<m; ++i ) {
+                       AVProgram *pgrm = fmt_ctx->programs[i];
+                       for( int j=0, n=pgrm->nb_stream_indexes; j<n; ++j ) {
+                               int st_idx = pgrm->stream_index[j];
+                               if( st_idx == fidx ) { pidx = i;  break; }
+                       }
+               }
+               fidx = -1;
+               if( pidx >= 0 ) {
+                       AVProgram *pgrm = fmt_ctx->programs[pidx];
+                       for( int j=0, n=pgrm->nb_stream_indexes; j<n; ++j ) {
+                               int st_idx = pgrm->stream_index[j];
+                               AVStream *tst = fmt_ctx->streams[st_idx];
+                               if( !tst ) continue;
+                               if( tst->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ) {
+                                       st = tst;  fidx = st_idx;
+                                       break;
+                               }
+                       }
+               }
+               else {
+                       for( int i=0, n=fmt_ctx->nb_streams; i<n; ++i ) {
+                               AVStream *tst = fmt_ctx->streams[i];
+                               if( !tst ) continue;
+                               if( tst->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ) {
+                                       st = tst;  fidx = i;
+                                       break;
+                               }
+                       }
+               }
+               if( st )
+                       tc = av_dict_get(st->metadata, "timecode", 0, 0);
+       }
+
+       if( !tc ) {
+               // any timecode, includes -data- streams
+               for( int i=0, n=fmt_ctx->nb_streams; i<n; ++i ) {
+                       AVStream *tst = fmt_ctx->streams[i];
+                       if( !tst ) continue;
+                       if( (tc = av_dict_get(tst->metadata, "timecode", 0, 0)) ) {
+                               st = tst;  fidx = i;
+                               break;
+                       }
+               }
+       }
+
+       if( st && st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ) {
+               if( st->r_frame_rate.num && st->r_frame_rate.den )
+                       rate = st->r_frame_rate;
+               nudge = st->start_time;
+               for( int i=0; i<ffvideo.size(); ++i ) {
+                       if( ffvideo[i]->st == st ) {
+                               nudge = ffvideo[i]->nudge;
+                               break;
+                       }
+               }
+       }
+
+       if( tc ) { // return timecode
+               double secs = st->start_time == AV_NOPTS_VALUE ? 0 :
+                       to_secs(st->start_time - nudge, st->time_base);
+               return ff_get_timecode(tc->value, rate, secs);
+       }
+       
+       if( !st || fidx < 0 ) return -1;
+
+       decode_activate();
+       AVCodecContext *av_ctx = activate_decoder(st);
+       if( !av_ctx ) {
+               fprintf(stderr,"activate_decoder failed\n");
+               return -1;
+       }
+       avCodecContext avctx(av_ctx); // auto deletes
+       if( avctx->codec_type == AVMEDIA_TYPE_VIDEO &&
+           avctx->framerate.num && avctx->framerate.den )
+               rate = avctx->framerate;
+
+       avPacket pkt;   // auto deletes
+       avFrame frame;  // auto deletes
+       if( !frame ) {
+               fprintf(stderr,"av_frame_alloc failed\n");
+               return -1;
+       }
+       int errs = 0;
+       int64_t max_packets = 100;
+       char tcbuf[AV_TIMECODE_STR_SIZE];
+
+       for( int64_t count=0; count<max_packets; ++count ) {
+               av_packet_unref(pkt);
+               pkt->data = 0; pkt->size = 0;
+
+               int ret = av_read_frame(fmt_ctx, pkt);
+               if( ret < 0 ) {
+                       if( ret == AVERROR_EOF ) break;
+                       if( ++errs > 100 ) {
+                               fprintf(stderr,"over 100 read_frame errs\n");
+                               break;
+                       }
+                       continue;
+               }
+               if( !pkt->data ) continue;
+               int i = pkt->stream_index;
+               if( i != fidx ) continue;
+               int64_t tstmp = pkt->pts;
+               if( tstmp == AV_NOPTS_VALUE ) tstmp = pkt->dts;
+               double secs = to_secs(tstmp - nudge, st->time_base);
+               ret = avcodec_send_packet(avctx, pkt);
+               if( ret < 0 ) return -1;
+
+               while( (ret = avcodec_receive_frame(avctx, frame)) >= 0 ) {
+                       if( (tc = av_dict_get(frame->metadata, "timecode", 0, 0)) )
+                               return ff_get_timecode(tc->value, rate, secs);
+                       int k = frame->nb_side_data;
+                       AVFrameSideData *side_data = 0;
+                       while( --k >= 0 ) {
+                               side_data = frame->side_data[k];
+                               switch( side_data->type ) {
+                               case AV_FRAME_DATA_GOP_TIMECODE: {
+                                       int64_t data = *(int64_t *)side_data->data;
+                                       int sz = sizeof(data);
+                                       if( side_data->size >= sz ) {
+                                               av_timecode_make_mpeg_tc_string(tcbuf, data);
+                                               return ff_get_timecode(tcbuf, rate, secs);
+                                       }
+                                       break; }
+                               case AV_FRAME_DATA_S12M_TIMECODE: {
+                                       uint32_t *data = (uint32_t *)side_data->data;
+                                       int n = data[0], sz = (n+1)*sizeof(*data);
+                                       if( side_data->size >= sz ) {
+                                               av_timecode_make_smpte_tc_string(tcbuf, data[n], 0);
+                                               return ff_get_timecode(tcbuf, rate, secs);
+                                       }
+                                       break; }
+                               default:
+                                       break;
+                               }
+                       }
+               }
+       }
+       char *path = fmt_ctx->url;
+       char *bp = strrchr(path, '/');
+       if( !bp ) bp = path; else ++bp;
+       char *cp = strrchr(bp, '.');
+       if( cp && (cp-=(8+1+6)) >= bp ) {
+               char sep[BCSTRLEN];
+               int year,mon,day, hour,min,sec, frm=0;
+               if( sscanf(cp,"%4d%2d%2d%[_-]%2d%2d%2d",
+                               &year,&mon,&day, sep, &hour,&min,&sec) == 7 ) {
+                       int ch = sep[0];
+                       // year>=1970,mon=1..12,day=1..31, hour=0..23,min=0..59,sec=0..60
+                       if( (ch=='_' || ch=='-' ) &&
+                           year >= 1970 && mon>=1 && mon<=12 && day>=1 && day<=31 &&
+                           hour>=0 && hour<24 && min>=0 && min<60 && sec>=0 && sec<=60 ) {
+                               sprintf(tcbuf,"%d:%02d:%02d:%02d", hour,min,sec, frm);
+                               return ff_get_timecode(tcbuf, rate, 0);
+                       }
+               }
+       }
+       struct stat tst;
+       if( stat(path, &tst) >= 0 ) {
+               time_t t = (time_t)tst.st_mtim.tv_sec;
+               struct tm tm;
+               localtime_r(&t, &tm);
+               int64_t us = tst.st_mtim.tv_nsec / 1000;
+               int frm = us/1000000. * frame_rate;
+               sprintf(tcbuf,"%d:%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec, frm);
+               return ff_get_timecode(tcbuf, rate, 0);
+       }
+       return -1;
+}
+
+double FFMPEG::ff_get_timecode(char *str, AVRational rate, double pos)
+{
+       AVTimecode tc;
+       if( av_timecode_init_from_string(&tc, rate, str, fmt_ctx) )
+               return -1;
+       double secs = (double)tc.start / tc.fps - pos;
+       if( secs < 0 ) secs = 0;
+       return secs;
+}
+
+double FFMPEG::get_timecode(const char *path, int data_type, int channel, double rate)
+{
+       FFMPEG ffmpeg(0);
+       if( ffmpeg.init_decoder(path) ) return -1;
+       return ffmpeg.get_initial_timecode(data_type, channel, rate);
+}
+
index 56ce4e5b22d512a09d2f97863e5d321987f24b63..18516d3611c1bd5a6116698a290622ca8e477f75 100644 (file)
@@ -37,6 +37,8 @@ extern "C" {
 #include "libavutil/pixdesc.h"
 #include "libswresample/swresample.h"
 #include "libswscale/swscale.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/timecode.h"
 }
 
 class FFPacket  {
@@ -254,10 +256,12 @@ public:
        int video_seek(int64_t pos);
        int encode(VFrame *vframe);
        int drain();
+       double get_rotation_angle();
+       void flip();
 
        int idx;
        double frame_rate;
-       int width, height;
+       int width, height, transpose;
        int64_t length;
        float aspect_ratio;
 
@@ -282,6 +286,38 @@ public:
        int update(AVCodecID &codec_id, AVCodec *&decoder);
 };
 
+// for get_initial_timecode auto deletes
+class avFrame {
+       AVFrame *frm;
+public:
+       avFrame() { frm = av_frame_alloc(); }
+       ~avFrame() { av_frame_free(&frm); }
+       operator AVFrame *() { return frm; }
+       AVFrame *operator ->() { return frm; }
+};
+
+class avPacket {
+       AVPacket pkt;
+public:
+       avPacket() {
+               av_init_packet(&pkt);
+               pkt.data = 0; pkt.size = 0;
+       }
+       ~avPacket() { av_packet_unref(&pkt); }
+       operator AVPacket *() { return &pkt; }
+       AVPacket *operator ->() { return &pkt; }
+};
+
+class avCodecContext {
+       AVCodecContext *avctx;
+public:
+       avCodecContext(AVCodecContext *ctx) { avctx = ctx; }
+       ~avCodecContext() { avcodec_free_context(&avctx); }
+       operator AVCodecContext *() { return avctx; }
+       AVCodecContext *operator ->() { return avctx; }
+};
+
+
 class FFMPEG : public Thread {
 public:
        static Mutex fflock;
@@ -289,7 +325,7 @@ public:
        static void ff_unlock() { fflock.unlock(); }
 
        int check_sample_rate(AVCodec *codec, int sample_rate);
-       AVRational check_frame_rate(AVCodec *codec, double frame_rate);
+       AVRational check_frame_rate(const AVRational *p, double frame_rate);
        AVRational to_sample_aspect_ratio(Asset *asset);
        AVRational to_time_base(int sample_rate);
        static int get_fmt_score(AVSampleFormat dst_fmt, AVSampleFormat src_fmt);
@@ -334,6 +370,7 @@ public:
 
        int total_audio_channels();
        int total_video_channels();
+       double get_initial_timecode(int data_type, int channel, double frame_rate);
 
        int audio_seek(int ch, int64_t pos);
        int video_seek(int layer, int64_t pos);
@@ -390,6 +427,7 @@ public:
 
        FFMPEG(FileBase *file_base=0);
        ~FFMPEG();
+       AVCodecContext *activate_decoder(AVStream *st);
        int scan(IndexState *index_state, int64_t *scan_position, int *canceled);
 
        int ff_audio_stream(int channel) { return astrm_index[channel].st_idx; }
@@ -420,6 +458,8 @@ public:
        int64_t ff_video_frames(int stream);
        int ff_video_pid(int stream);
        int ff_video_mpeg_color_range(int stream);
+       double ff_get_timecode(char *str, AVRational rate, double pos);
+       static double get_timecode(const char *path, int data_type, int channel, double rate);
 
        int ff_cpus();
        const char *ff_hw_dev();
index ddee51a23634249d8a28b7f1328f4a6a8230934b..06e23d2b1242c2520a0b258142ad68cf2856b107 100644 (file)
@@ -49,11 +49,15 @@ void MainClock::update(double position)
        lock_window("MainClock::update");
        char string[BCTEXTLEN];
        position += position_offset;
+       double timecode_offset =
+               mwindow->edl->session->time_format == TIME_TIMECODE ?
+                       mwindow->get_timecode_offset() : 0 ;
        Units::totext(string, position,
                mwindow->edl->session->time_format,
                mwindow->edl->session->sample_rate,
                mwindow->edl->session->frame_rate,
-               mwindow->edl->session->frames_per_foot);
+               mwindow->edl->session->frames_per_foot,
+               timecode_offset);
        BC_Title::update(string);
        unlock_window();
 }
index 64e90885255df6e2dea596e1a150d58a6cd1629a..061dd8c19f3c1f4d0e4669910efdf28663b7b4dc 100644 (file)
@@ -282,6 +282,7 @@ void MainMenu::create_objects()
        windowmenu->add_item(split_x = new SplitX(mwindow));
        windowmenu->add_item(split_y = new SplitY(mwindow));
        windowmenu->add_item(mixer_items = new MixerItems(mwindow));
+       windowmenu->add_item(align_timecodes = new AlignTimecodes(mwindow));
        mixer_items->create_objects();
        windowmenu->add_item(new TileWindows(mwindow,_("Tile left"),0));
        windowmenu->add_item(new TileWindows(mwindow,_("Tile right"),1));
@@ -1769,6 +1770,19 @@ int AlignMixers::handle_event()
 }
 
 
+AlignTimecodes::AlignTimecodes(MWindow *mwindow)
+ : BC_MenuItem(_("Align Timecodes"))
+{
+       this->mwindow = mwindow;
+}
+
+int AlignTimecodes::handle_event()
+{
+       mwindow->align_timecodes();
+       return 1;
+}
+
+
 LoadLayoutItem::LoadLayoutItem(LoadLayout *load_layout, const char *text, int idx, int hotkey)
  : BC_MenuItem(text, "", hotkey)
 {
index bf14888f3296ad0d7ab223c5a32339a4e1fc30e9..f0c493b22a735af15ea23dc1f8fcef70c14ae636 100644 (file)
@@ -172,6 +172,7 @@ public:
        SplitX *split_x;
        SplitY *split_y;
        MixerItems *mixer_items;
+       AlignTimecodes *align_timecodes;
        LoadLayout *load_layout;
        LoadLayout *save_layout;
 };
@@ -538,6 +539,14 @@ public:
        int handle_event();
 };
 
+class AlignTimecodes : public BC_MenuItem
+{
+public:
+       AlignTimecodes(MWindow *mwindow);
+       int handle_event();
+       MWindow *mwindow;
+};
+
 // ======================================== audio
 
 class AddAudioTrack : public BC_MenuItem
index dbe513848a55afd24e29dec111dbdc6e6e6e5015..426ab487e1f2e21629a1af45e508dd880a424dfb 100644 (file)
@@ -70,6 +70,7 @@ class MixerItems;
 class MixerViewer;
 class TileMixers;
 class AlignMixers;
+class AlignTimecodes;
 class AddAudioTrack;
 class DeleteAudioTrack;
 class DefaultATransition;
index d40b726df60d970df309e5d4c14e986418c2face..8c3b8a2d0258e2bb533ed28931ac2f107504c668 100644 (file)
@@ -144,7 +144,8 @@ MainEditing::MainEditing(MWindow *mwindow, MButtons *mbuttons, int x, int y)
                1, // use_goto
                0, // use_clk2play
                0, // use_scope
-               1) // use_gang_tracks
+               1, // use_gang_tracks
+               1) // use_timecode
 {
        this->mwindow = mwindow;
        this->mbuttons = mbuttons;
index 1dcbe8a7b8769dcceab4c5c2b3be8eea4bdeba6d..b58d770c224814430b22eb7356a0d2fa5b12aa8e 100644 (file)
@@ -360,7 +360,9 @@ void MenuEffectThread::run()
                default_asset->height = mwindow->edl->session->output_h;
        }
 
-       int strategy = Render::get_strategy(mwindow->preferences->use_renderfarm, use_labels);
+       int range = File::is_image_render(default_asset->format) ?
+               RANGE_1FRAME : RANGE_SELECTION;
+       int strategy = Render::get_strategy(mwindow->preferences->use_renderfarm, use_labels, range);
 // Process the total length in fragments
        ArrayList<MenuEffectPacket*> packets;
        if(!result)
index 7eec4d34d3ee01165347190cd924fe6cdc3d9429..60d71da1b36478d4a818502a73c9e2049f8890e9 100644 (file)
@@ -308,6 +308,7 @@ void MTimeBar::draw_time()
                }
 
                case TIME_FRAMES:
+               case TIME_TIMECODE:
                case TIME_HMSF:
 // One frame per text mark
                        if(frame_seconds >= min_time)
@@ -363,11 +364,14 @@ void MTimeBar::draw_time()
 
 // Set tick interval
        tick_interval = text_interval;
+       double timecode_offset = 0;
 
        switch(mwindow->edl->session->time_format)
        {
-               case TIME_HMSF:
+               case TIME_TIMECODE:
+                       timecode_offset = mwindow->get_timecode_offset(); // fall thru
                case TIME_FEET_FRAMES:
+               case TIME_HMSF:
                case TIME_FRAMES:
                        if(frame_seconds / time_per_pixel > TICK_SPACING)
                                tick_interval = frame_seconds;
@@ -395,7 +399,8 @@ void MTimeBar::draw_time()
                        mwindow->edl->session->time_format,
                        sample_rate,
                        mwindow->edl->session->frame_rate,
-                       mwindow->edl->session->frames_per_foot);
+                       mwindow->edl->session->frames_per_foot,
+                       timecode_offset);
                set_color(get_resources()->default_text_color);
                set_font(MEDIUMFONT);
 
@@ -697,14 +702,16 @@ void TimeBarPopup::create_objects()
        add_item(items[1] = new TimeBarPopupItem(mwindow,
                this, TIME_HMSF_TEXT, TIME_HMSF));
        add_item(items[2] = new TimeBarPopupItem(mwindow,
-               this, TIME_FRAMES_TEXT, TIME_FRAMES));
+               this, TIME_TIMECODE_TEXT, TIME_TIMECODE));
        add_item(items[3] = new TimeBarPopupItem(mwindow,
-               this, TIME_SAMPLES_TEXT, TIME_SAMPLES));
+               this, TIME_FRAMES_TEXT, TIME_FRAMES));
        add_item(items[4] = new TimeBarPopupItem(mwindow,
-               this, TIME_SAMPLES_HEX_TEXT, TIME_SAMPLES_HEX));
+               this, TIME_SAMPLES_TEXT, TIME_SAMPLES));
        add_item(items[5] = new TimeBarPopupItem(mwindow,
-               this, TIME_SECONDS_TEXT, TIME_SECONDS));
+               this, TIME_SAMPLES_HEX_TEXT, TIME_SAMPLES_HEX));
        add_item(items[6] = new TimeBarPopupItem(mwindow,
+               this, TIME_SECONDS_TEXT, TIME_SECONDS));
+       add_item(items[7] = new TimeBarPopupItem(mwindow,
                this, TIME_FEET_FRAMES_TEXT, TIME_FEET_FRAMES));
 }
 
index 1466b4451275c4b0529af43647787a70dae5db74..7d0c140f17a90e1e986283c49f7822ac3041fd19 100644 (file)
@@ -3731,6 +3731,7 @@ void MWindow::update_project(int load_mode)
 
        if( load_mode == LOADMODE_REPLACE ||
            load_mode == LOADMODE_REPLACE_CONCATENATE ) {
+               edl->session->timecode_offset = 0;
                delete gui->keyvalue_popup;
                gui->keyvalue_popup = 0;
                gui->load_panes();
@@ -3952,6 +3953,7 @@ int MWindow::save(int save_as)
        for( int i=stack.size(); --i>=0;  ) {
                StackItem &item = stack[i];
                Indexable *idxbl = item.idxbl;
+               if( !idxbl ) continue;
                if( idxbl->is_asset ) {
                        Asset *asset = (Asset *)idxbl;
                        if( asset->format == FILE_REF ) {
@@ -3959,8 +3961,8 @@ int MWindow::save(int save_as)
                                        return 1;
                        }
                }
-               else if( item.new_edl != item.idxbl )
-                       item.new_edl->overwrite_clip((EDL*)item.idxbl);
+               else if( item.new_edl != idxbl )
+                       item.new_edl->overwrite_clip((EDL*)idxbl);
        }
        EDL *new_edl = stack.size() ? stack[0].edl : edl;
        save(new_edl, path, 1);
@@ -4779,31 +4781,30 @@ int MWindow::interrupt_indexes()
 
 void MWindow::next_time_format()
 {
-       switch(edl->session->time_format)
-       {
-               case TIME_HMS: edl->session->time_format = TIME_HMSF; break;
-               case TIME_HMSF: edl->session->time_format = TIME_SAMPLES; break;
-               case TIME_SAMPLES: edl->session->time_format = TIME_SAMPLES_HEX; break;
-               case TIME_SAMPLES_HEX: edl->session->time_format = TIME_FRAMES; break;
-               case TIME_FRAMES: edl->session->time_format = TIME_FEET_FRAMES; break;
-               case TIME_FEET_FRAMES: edl->session->time_format = TIME_SECONDS; break;
-               case TIME_SECONDS: edl->session->time_format = TIME_HMS; break;
+       switch( edl->session->time_format ) {
+       case TIME_HMS:          edl->session->time_format = TIME_HMSF;         break;
+       case TIME_HMSF:         edl->session->time_format = TIME_TIMECODE;     break;
+       case TIME_TIMECODE:     edl->session->time_format = TIME_FRAMES;       break;
+       case TIME_FRAMES:       edl->session->time_format = TIME_SAMPLES;      break;
+       case TIME_SAMPLES:      edl->session->time_format = TIME_SAMPLES_HEX;  break;
+       case TIME_SAMPLES_HEX:  edl->session->time_format = TIME_SECONDS;      break;
+       case TIME_SECONDS:      edl->session->time_format = TIME_FEET_FRAMES;  break;
+       case TIME_FEET_FRAMES:  edl->session->time_format = TIME_HMS;          break;
        }
-
        time_format_common();
 }
 
 void MWindow::prev_time_format()
 {
-       switch(edl->session->time_format)
-       {
-               case TIME_HMS: edl->session->time_format = TIME_SECONDS; break;
-               case TIME_SECONDS: edl->session->time_format = TIME_FEET_FRAMES; break;
-               case TIME_FEET_FRAMES: edl->session->time_format = TIME_FRAMES; break;
-               case TIME_FRAMES: edl->session->time_format = TIME_SAMPLES_HEX; break;
-               case TIME_SAMPLES_HEX: edl->session->time_format = TIME_SAMPLES; break;
-               case TIME_SAMPLES: edl->session->time_format = TIME_HMSF; break;
-               case TIME_HMSF: edl->session->time_format = TIME_HMS; break;
+       switch( edl->session->time_format ) {
+       case TIME_HMS:          edl->session->time_format = TIME_FEET_FRAMES;  break;
+       case TIME_HMSF:         edl->session->time_format = TIME_HMS;          break;
+       case TIME_TIMECODE:     edl->session->time_format = TIME_HMSF;         break;
+       case TIME_FRAMES:       edl->session->time_format = TIME_TIMECODE;     break;
+       case TIME_SAMPLES:      edl->session->time_format = TIME_FRAMES;       break;
+       case TIME_SAMPLES_HEX:  edl->session->time_format = TIME_SAMPLES;      break;
+       case TIME_SECONDS:      edl->session->time_format = TIME_SAMPLES_HEX;  break;
+       case TIME_FEET_FRAMES:  edl->session->time_format = TIME_SECONDS;      break;
        }
 
        time_format_common();
index 8f80280f13ef45a412969db698e89a0b6e35bf03..cbe172774d8ad3efa9b9d94ff3c1ee0309adc869 100644 (file)
@@ -332,6 +332,9 @@ public:
        void toggle_projector_xyz();
        double get_position();
        void set_position(double position);
+       double get_timecode_offset();
+       void set_timecode_offset(double offset);
+       void align_timecodes();
 
 // seek to labels
 // shift_down must be passed by the caller because different windows call
index 6242b788bc027a81b389e66075b923c27febae55..26aa4cb25054d2f3317c8d1222a16baa7202d48f 100644 (file)
@@ -2710,3 +2710,15 @@ void MWindow::paste_effects()
        group->remove_user();
 }
 
+void MWindow::align_timecodes()
+{
+       undo_before();
+       double offset = edl->tracks->align_timecodes();
+       set_timecode_offset(offset);
+       save_backup();
+       undo_after(_("align timecodes"), LOAD_ALL);
+       restart_brender();
+       cwindow->refresh_frame(CHANGE_EDL);
+       update_plugin_guis();
+}
+
index 2c0479bb947953c8f1383ea1035831ce999498f2..89aaaee1d457e997842141d6e96316a1ce95db33 100644 (file)
@@ -756,21 +756,37 @@ int MWindow::find_selection(double position, int scroll_display)
 
 double MWindow::get_position()
 {
-        return edl->local_session->get_selectionstart(1);
+       return edl->local_session->get_selectionstart(1);
 }
 
 void MWindow::set_position(double position)
 {
-        if( position != get_position() ) {
-                if( position < 0 ) position = 0;
-                edl->local_session->set_selectionstart(position);
-                edl->local_session->set_selectionend(position);
-                gui->lock_window();
-                find_cursor();
-                gui->update(1, NORMAL_DRAW, 1, 1, 1, 1, 0);
-                gui->unlock_window();
-                cwindow->update(1, 0, 0, 0, 0);
-        }
+       if( position != get_position() ) {
+               if( position < 0 ) position = 0;
+               edl->local_session->set_selectionstart(position);
+               edl->local_session->set_selectionend(position);
+               gui->lock_window();
+               find_cursor();
+               gui->update(1, NORMAL_DRAW, 1, 1, 1, 1, 0);
+               gui->unlock_window();
+               cwindow->update(1, 0, 0, 0, 0);
+       }
+}
+
+
+double MWindow::get_timecode_offset()
+{
+       return edl->session->timecode_offset;
+}
+
+void MWindow::set_timecode_offset(double offset)
+{
+       edl->session->time_format = TIME_TIMECODE;
+       edl->session->timecode_offset = offset;
+       gui->lock_window();
+       gui->update(1, NORMAL_DRAW, 1, 1, 1, 1, 0);
+       gui->unlock_window();
+       cwindow->update(1, 0, 0, 0, 0);
 }
 
 
index b338debaf09f5f5037aa9fa7ed60e4dc2185d22f..50d50608f27ab584f75520243fc72b634a28d284 100644 (file)
@@ -107,34 +107,26 @@ PluginClientWindow* plugin_class::new_window() \
 #define LOAD_CONFIGURATION_MACRO(plugin_class, config_class) \
 int plugin_class::load_configuration() \
 { \
-       KeyFrame *prev_keyframe, *next_keyframe; \
-       prev_keyframe = get_prev_keyframe(get_source_position()); \
-       next_keyframe = get_next_keyframe(get_source_position()); \
- \
-       int64_t next_position = edl_to_local(next_keyframe->position); \
+       KeyFrame * prev_keyframe = get_prev_keyframe(get_source_position()); \
        int64_t prev_position = edl_to_local(prev_keyframe->position); \
- \
        config_class old_config, prev_config, next_config; \
        old_config.copy_from(config); \
        read_data(prev_keyframe); \
        prev_config.copy_from(config); \
-       read_data(next_keyframe); \
-       next_config.copy_from(config); \
- \
-       config.interpolate(prev_config,  \
-               next_config,  \
-               (next_position == prev_position) ? \
-                       get_source_position() : \
-                       prev_position, \
-               (next_position == prev_position) ? \
-                       get_source_position() + 1 : \
-                       next_position, \
-               get_source_position()); \
+       KeyFrame * next_keyframe = get_next_keyframe(get_source_position()); \
+       if( next_keyframe ) { \
+               int64_t next_position = edl_to_local(next_keyframe->position); \
+               read_data(next_keyframe); \
+               next_config.copy_from(config); \
  \
-       if(!config.equivalent(old_config)) \
-               return 1; \
-       else \
-               return 0; \
+               config.interpolate(prev_config, next_config,  \
+                       (next_position == prev_position) ? \
+                               get_source_position() : prev_position, \
+                       (next_position == prev_position) ? \
+                               get_source_position() + 1 : next_position, \
+                       get_source_position()); \
+       } \
+       return !config.equivalent(old_config) ? 1 : 0; \
 }
 
 
index dbf14c49597688b74f7028d96815eca5948d527d..d7dbf4d6785697bfb4438816ba8b98abbef34e4a 100644 (file)
@@ -98,6 +98,7 @@ Preferences::Preferences()
        popupmenu_btnup = 1;
        grab_input_focus = 1;
        textbox_focus_policy = 0;
+       auto_rotate = 1;
        forward_render_displacement = 0;
        dvd_yuv420p_interlace = 0;
        highlight_inverse = 0xffffff;
@@ -223,6 +224,7 @@ void Preferences::copy_from(Preferences *that)
        popupmenu_btnup = that->popupmenu_btnup;
        grab_input_focus = that->grab_input_focus;
        textbox_focus_policy = that->textbox_focus_policy;
+       auto_rotate = that->auto_rotate;
        forward_render_displacement = that->forward_render_displacement;
        dvd_yuv420p_interlace = that->dvd_yuv420p_interlace;
        highlight_inverse = that->highlight_inverse;
@@ -372,6 +374,7 @@ int Preferences::load_defaults(BC_Hash *defaults)
        popupmenu_btnup = defaults->get("POPUPMENU_BTNUP", popupmenu_btnup);
        grab_input_focus = defaults->get("GRAB_FOCUS", grab_input_focus);
        textbox_focus_policy = defaults->get("TEXTBOX_FOCUS_POLICY", textbox_focus_policy);
+       auto_rotate = defaults->get("AUTO_ROTATE", auto_rotate);
        forward_render_displacement = defaults->get("FORWARD_RENDER_DISPLACEMENT", forward_render_displacement);
        dvd_yuv420p_interlace = defaults->get("DVD_YUV420P_INTERLACE", dvd_yuv420p_interlace);
        highlight_inverse = defaults->get("HIGHLIGHT_INVERSE", highlight_inverse);
@@ -519,6 +522,7 @@ int Preferences::save_defaults(BC_Hash *defaults)
        defaults->update("POPUPMENU_BTNUP", popupmenu_btnup);
        defaults->update("GRAB_FOCUS", grab_input_focus);
        defaults->update("TEXTBOX_FOCUS_POLICY", textbox_focus_policy);
+       defaults->update("AUTO_ROTATE", auto_rotate);
        defaults->update("FORWARD_RENDER_DISPLACEMENT", forward_render_displacement);
        defaults->update("DVD_YUV420P_INTERLACE", dvd_yuv420p_interlace);
        defaults->update("HIGHLIGHT_INVERSE", highlight_inverse);
index e076457494a576a99e148558d4313318dda8dee5..fd2304692a7039f573b8013e5363809329cdcd0c 100644 (file)
@@ -127,6 +127,8 @@ public:
        int popupmenu_btnup;
 // textbox focus policy: click, leave
        int textbox_focus_policy;
+// apply display_matrix rotation, ffmpeg only
+       int auto_rotate;
 // forward playback starts next frame, not this frame
        int forward_render_displacement;
 // use dvd yuv420p interlace format
index 741d6ff3747c0ef55dbbe5e5aeddf00584c09fe0..323bebf333f4ccf2dc4530355e8353ffeaf0712c 100644 (file)
@@ -446,15 +446,16 @@ int Render::check_asset(EDL *edl, Asset &asset)
        return 0;
 }
 
-int Render::get_strategy(int use_renderfarm, int use_labels)
+int Render::get_strategy(int use_renderfarm, int use_labels, int range_type)
 {
-       return use_renderfarm ?
-               (use_labels ? FILE_PER_LABEL_FARM : SINGLE_PASS_FARM) :
-               (use_labels ? FILE_PER_LABEL      : SINGLE_PASS     ) ;
+       return range_type == RANGE_1FRAME ? SINGLE_PASS :
+                use_renderfarm ?
+                       (use_labels ? FILE_PER_LABEL_FARM : SINGLE_PASS_FARM) :
+                       (use_labels ? FILE_PER_LABEL      : SINGLE_PASS     ) ;
 }
 int Render::get_strategy()
 {
-       return get_strategy(preferences->use_renderfarm, use_labels);
+       return get_strategy(preferences->use_renderfarm, use_labels, range_type);
 }
 
 void Render::start_progress()
index eddbf7c593748541d0f6d1a5141b8d2a7aea5077..ad954335773f762bd5a2305c6e87c7bfc390e8a6 100644 (file)
@@ -137,7 +137,7 @@ public:
 // This should be integrated into the Asset Class.
        static int check_asset(EDL *edl, Asset &asset);
 // strategy to conform with using renderfarm.
-       static int get_strategy(int use_renderfarm, int use_labels);
+       static int get_strategy(int use_renderfarm, int use_labels, int range_type);
        int get_strategy();
 // Force filename to have a 0 padded number if rendering to a list.
        int check_numbering(Asset &asset);
index d904d5d6b330df4d660b61d6d05d24990a736f62..170f2931af52857e1f2374f15e623eff36761bde 100644 (file)
@@ -787,3 +787,21 @@ void Tracks::move_tracks(Track *src, Track *dst, int n)
        }
 }
 
+double Tracks::align_timecodes()
+{
+       double offset = -1;
+       for( Track *track=edl->tracks->first; track; track=track->next ) {
+               if( !track->is_armed() ) continue;
+               double early_offset = track->edits->early_timecode();
+               if( offset < 0 || offset > early_offset )
+                       offset = early_offset;
+       }
+       if( offset >= 0 ) {
+               for( Track *track=edl->tracks->first; track; track=track->next ) {
+                       if( !track->is_armed() ) continue;
+                       track->edits->align_timecodes(offset);
+               }
+       }
+       return offset;
+}
+
index dd1a92f6d4c761d280a1685cd73be39dcd96cf00..449fd6ad6f78d2ab7b1e7f19dd6d5e1b3709684f 100644 (file)
@@ -112,6 +112,7 @@ public:
        void translate_camera(float dx, float dy, int all=0);
        void crop_resize(float x, float y, float z);
        void crop_shrink(float x, float y, float z);
+       double align_timecodes();
 
        int total_of(int type);
        Track* get_track_by_id(int id);
index 09d3e6f52b60cceb83ec8840625754ccaa1ecd14..e789d1f0026dd8273ab208a004c7bbb23e68d0f3 100644 (file)
@@ -560,7 +560,8 @@ VWindowEditing::VWindowEditing(MWindow *mwindow, VWindow *vwindow)
                0, // use_goto
                1, // use_clk2play
                1, // use_scope
-               0) // use_gang_tracks
+               0, // use_gang_tracks
+               0) // use_timecode
 {
        this->mwindow = mwindow;
        this->vwindow = vwindow;
index 5171469d3f1e0a6e9fc77917da01b583cb7b33a8..d8d3ccc35faf001b8869e1965bf6d66db3d8e2ff 100644 (file)
@@ -612,7 +612,8 @@ int FromTextBox::update_position(double new_position)
                mwindow->edl->session->time_format,
                mwindow->edl->session->sample_rate,
                mwindow->edl->session->frame_rate,
-               mwindow->edl->session->frames_per_foot);
+               mwindow->edl->session->frames_per_foot,
+               mwindow->get_timecode_offset());
 //printf("FromTextBox::update_position %f %s\n", new_position, string);
        update(string);
        return 0;
@@ -681,7 +682,8 @@ int ToTextBox::update_position(double new_position)
                mwindow->edl->session->time_format,
                mwindow->edl->session->sample_rate,
                mwindow->edl->session->frame_rate,
-               mwindow->edl->session->frames_per_foot);
+               mwindow->edl->session->frames_per_foot,
+               mwindow->get_timecode_offset());
        update(string);
        return 0;
 }
index 940774694333cb1e1a066cb3e2851c307f61c593..5e9dcd5bd32b239294792ff13c5eebd0b2432535 100644 (file)
@@ -244,7 +244,8 @@ double ZoomPanel::text_to_zoom(char *text, int use_table)
                                mwindow->edl->session->sample_rate,
                                mwindow->edl->session->time_format,
                                mwindow->edl->session->frame_rate,
-                               mwindow->edl->session->frames_per_foot);
+                               mwindow->edl->session->frames_per_foot,
+                               mwindow->get_timecode_offset());
                        total_samples /= mwindow->theme->mcanvas_w -
                                mwindow->theme->patchbay_w -
                                BC_ScrollBar::get_span(SCROLL_VERT);
index 343f7f5c57efdc1228f659818ac2300307587a9d..9f54f5688efd74fc697cc2607b9a12bf603bf69d 100644 (file)
                 <td height="26" align="left"><b><font face="Liberation Serif" size=4>  Drag/Drop</font></b></td>
                 <td align="left"><font face="Liberation Serif" size=4>Clear Select</font></td>
                 <td align="left"><font face="Liberation Serif" size=4>Ctrl-Shift-A</font></td>
-                <td align="left"><font face="Liberation Serif" size=4>Delselect all selected edits</font></td>
+                <td align="left"><font face="Liberation Serif" size=4>De-select all selected edits</font></td>
         </tr>
         <tr>
                 <td height="26" align="left"><b><font face="Liberation Serif" size=4>     Edits </font></b></td>
index ae5822d82b27aa4ffda04d4c391f149c1c277e3b..7c1176ebd1584ad182ff1795801bf9e59cc06af1 100644 (file)
@@ -387,6 +387,8 @@ vibrance
 #xfade ###Input/output error
 #arnndn ###Invalid argument
 #dnn_processing ###Invalid argument
+#tonemap_vaapi ###Operation not permitted
+#afirsrc ###Operation not permitted
 #; the ffmpeg system library on Arch has these errors:
 #ladspa
 #ass
index 13df1c31ad11ee0ab7c7b5de7177dc45520a59bb..38c2c4b05d80664991cc93731d6329a9b7fa29ee 100644 (file)
@@ -183,7 +183,7 @@ int BC_CModels::calculate_datasize(int w, int h, int bytes_per_line, int color_m
        }
        if( bytes_per_line < 0 )
                bytes_per_line = w * calculate_pixelsize(color_model);
-       return h * bytes_per_line + 4;
+       return h * bytes_per_line + BC_COLOR_ALIGN;
 }
 
 int BC_CModels::bc_to_x(int color_model)
index 28b58459fb74eabb72e3fbf74f371ea51786cd18..30dd190287b3982d149e9eb48f3ff9a441766769 100644 (file)
@@ -81,7 +81,7 @@ enum BC_CModel {
 #define FOURCC_YUV2 0x32595559  /* YUV2   YUV422 */
 #define FOURCC_UYVY 0x59565955  /* UYVY   UVY422 */
 #define FOURCC_I420 0x30323449  /* I420   Intel Indeo 4 */
-
+#define BC_COLOR_ALIGN 64      /* overwrite padding, ssse3 */
 
 #endif // !BC_TRANSPARENCY
 
index e0a170dcc88711911d48ed9643f64a9a29ed3cc1..cf48af1722b85a9733f56ba89f4dec9d9297ed3b 100644 (file)
@@ -166,7 +166,8 @@ void Units::finit()
 
 // give text representation as time
 char* Units::totext(char *text, double seconds, int time_format,
-                       int sample_rate, float frame_rate, float frames_per_foot)
+                       int sample_rate, float frame_rate, float frames_per_foot,
+                       double timecode_offset)
 {
        int64_t hour, feet, frame;
        int minute, second, thousandths;
@@ -208,6 +209,8 @@ char* Units::totext(char *text, double seconds, int time_format,
                sprintf(text, "%02d:%02d:%02d", (int)hour, minute, second);
                break; }
 
+       case TIME_TIMECODE:
+               seconds += timecode_offset; // fall thru
        case TIME_HMSF: {
                seconds = fabs(seconds) + 1.0e-6;
                hour = seconds/3600;
@@ -263,10 +266,11 @@ char* Units::totext(char *text, double seconds, int time_format,
 
 // give text representation as time
 char* Units::totext(char *text, int64_t samples, int samplerate,
-               int time_format, float frame_rate, float frames_per_foot)
+               int time_format, float frame_rate, float frames_per_foot,
+               double timecode_offset)
 {
        return totext(text, (double)samples/samplerate, time_format,
-                       samplerate, frame_rate, frames_per_foot);
+               samplerate, frame_rate, frames_per_foot, timecode_offset);
 }
 
 int64_t Units::get_int64(const char *&bp)
@@ -299,7 +303,8 @@ void Units::skip_seperators(const char *&bp)
 }
 
 int64_t Units::fromtext(const char *text, int samplerate, int time_format,
-                       float frame_rate, float frames_per_foot)
+                       float frame_rate, float frames_per_foot,
+                       double timecode_offset)
 {
        int64_t hours, total_samples;
        int minutes, frames, feet;
@@ -320,12 +325,15 @@ int64_t Units::fromtext(const char *text, int samplerate, int time_format,
                total_seconds = seconds + minutes*60 + hours*3600;
                break; }
 
+       case TIME_TIMECODE:
        case TIME_HMSF: {
                hours = get_int64(text);    skip_seperators(text);
                minutes = get_int64(text);  skip_seperators(text);
                seconds = get_int64(text);  skip_seperators(text);
                frames = get_int64(text);
                total_seconds = frames/frame_rate + seconds + minutes*60 + hours*3600;
+               if( time_format == TIME_TIMECODE )
+                       total_seconds -= timecode_offset;
                break; }
 
        case TIME_SAMPLES: {
@@ -365,10 +373,11 @@ int64_t Units::fromtext(const char *text, int samplerate, int time_format,
 }
 
 double Units::text_to_seconds(const char *text, int samplerate, int time_format,
-                               float frame_rate, float frames_per_foot)
+                               float frame_rate, float frames_per_foot,
+                               double timecode_offset)
 {
        return (double)fromtext(text, samplerate, time_format,
-                               frame_rate, frames_per_foot) / samplerate;
+               frame_rate, frames_per_foot, timecode_offset) / samplerate;
 }
 
 
@@ -381,6 +390,7 @@ int Units::timeformat_totype(char *tcf)
        if (!strcmp(tcf,TIME_HMS2__STR)) return(TIME_HMS2);
        if (!strcmp(tcf,TIME_HMS3__STR)) return(TIME_HMS3);
        if (!strcmp(tcf,TIME_HMSF__STR)) return(TIME_HMSF);
+       if (!strcmp(tcf,TIME_TIMECODE__STR)) return(TIME_TIMECODE);
        if (!strcmp(tcf,TIME_SAMPLES__STR)) return(TIME_SAMPLES);
        if (!strcmp(tcf,TIME_SAMPLES_HEX__STR)) return(TIME_SAMPLES_HEX);
        if (!strcmp(tcf,TIME_FRAMES__STR)) return(TIME_FRAMES);
@@ -498,6 +508,7 @@ const char* Units::print_time_format(int time_format, char *string)
        case TIME_SECONDS:     fmt = TIME_SECONDS_TEXT;               break;
        case TIME_MS1:
        case TIME_MS2:         fmt = TIME_MS2_TEXT;                   break;
+       case TIME_TIMECODE:    fmt = TIME_TIMECODE_TEXT;              break;
        }
        return strcpy(string,fmt);
 }
@@ -513,6 +524,7 @@ int Units::text_to_format(const char *string)
        if(!strcmp(string, TIME_HMS3_TEXT)) return TIME_HMS3;
        if(!strcmp(string, TIME_SECONDS_TEXT)) return TIME_SECONDS;
        if(!strcmp(string, TIME_MS2_TEXT)) return TIME_MS2;
+       if(!strcmp(string, TIME_TIMECODE_TEXT)) return TIME_TIMECODE;
        return TIME_HMS;
 }
 
@@ -617,6 +629,7 @@ const char* Units::format_to_separators(int time_format)
                case TIME_HMS:         return "0:00:00.000";
                case TIME_HMS2:        return "0:00:00";
                case TIME_HMS3:        return "00:00:00";
+               case TIME_TIMECODE:
                case TIME_HMSF:        return "0:00:00:00";
                case TIME_SAMPLES:     return 0;
                case TIME_SAMPLES_HEX: return 0;
index 7e2243ef498b673f29798760756a55a1f0be38fb..6de33118da06cf24fa46b256645da2bad41ad766 100644 (file)
@@ -34,7 +34,7 @@
 #define TOTALFREQS 1024
 // slots per octave
 #define OCTAVE 105
-#define TOTAL_TIMEFORMATS 7
+#define TOTAL_TIMEFORMATS 8
 
 // h:mm:ss.sss
 #define TIME_HMS 0
@@ -55,6 +55,7 @@
 #define TIME_HMS2__STR         "h:mm:ss"
 #define TIME_HMS3__STR         "hh:mm:ss"
 #define TIME_HMSF__STR         "h:mm:ss:ff"
+#define TIME_TIMECODE__STR     "timecode"
 #define TIME_SAMPLES__STR      "audio samples"
 #define TIME_SAMPLES_HEX__STR  "audio samples (hex)"
 #define TIME_FRAMES__STR       "video frames"
@@ -77,6 +78,9 @@
 #define TIME_MS2 10
 #define TIME_MS2_TEXT _("Minutes:Seconds")
 
+#define TIME_TIMECODE 11
+#define TIME_TIMECODE_TEXT _("Timecode")
+
 class Units;
 
 class DB
@@ -178,16 +182,20 @@ public:
        static int64_t tosamples(double frames, int sample_rate, float framerate);
 // give text representation as time
        static char* totext(char *text, int64_t samples, int time_format,
-               int samplerate, float frame_rate = 0, float frames_per_foot = 0);
+               int samplerate, float frame_rate = 0, float frames_per_foot = 0,
+                       double timecode_offset = 0);
 // give text representation as time
        static char* totext(char *text, double seconds, int time_format,
-               int sample_rate = 0, float frame_rate = 0, float frames_per_foot = 0);
+               int sample_rate = 0, float frame_rate = 0, float frames_per_foot = 0,
+                       double timecode_offset = 0);
 // convert time to samples
        static int64_t fromtext(const char *text, int samplerate,
-               int time_format, float frame_rate, float frames_per_foot);
+               int time_format, float frame_rate, float frames_per_foot,
+               double timecode_offset);
 // Convert text to seconds
        static double text_to_seconds(const char *text, int samplerate,
-               int time_format, float frame_rate = 0, float frames_per_foot = 0);
+               int time_format, float frame_rate = 0, float frames_per_foot = 0,
+                       double timecode_offset = 0);
        static char* size_totext(int64_t bytes, char *text);
 
        static float xy_to_polar(int x, int y);
index c1ff05237bfdc65c743d1330fb538d55fa583c87..d85245bd0024fe33e247e4dbeb5990fa926c06ee 100644 (file)
@@ -416,7 +416,7 @@ long VFrame::get_bytes_per_line()
 
 long VFrame::get_data_size()
 {
-       return calculate_data_size(w, h, bytes_per_line, color_model) - 4;
+       return calculate_data_size(w, h, bytes_per_line, color_model) - BC_COLOR_ALIGN;
 }
 
 long VFrame::calculate_data_size(int w, int h, int bytes_per_line, int color_model)
index c8cabb8b677983ce774497a7a024724fe98c77ea..9c5e8a1be05f57f1de3564f865af574114bf50a2 100644 (file)
@@ -243,11 +243,14 @@ Zoom Blur:        Blur the video and use a zoom effect.
 #
 #  Description of FFmpeg Video Plugins
 #
+F_addroi:      Mark a region of interest in a video frame.
 F_amplify:     Amplify changes between successive video frames.
 F_atadenoise:  Apply an Adaptive Temporal Averaging Denoiser.
 F_avgblur:     Apply average blur filter.
 F_bbox:                Compute bounding box for each frame.
 F_bench:       Benchmarks part of a filtergraph.
+F_bilateral:   Apply bilateral filter, spatial smoothing while
+               preserving edges.
 F_bitplanenoise: Measure bit plane noise.
 F_blackdetect: Detect video intervals that are (almost) black.
 F_blackframe:  Detect frames that are (almost) black.
@@ -255,6 +258,7 @@ F_boxblur:  Blurs the input video. Through the settings you are
                able to change the power and the radius of the
                boxblur applied to luma, chroma and alpha.
 F_bwdif:       Deinterlaces the input image.
+F_cas:         Apply Contrast Adaptive Sharpen filter to video. 
 F_chromahold:  Turns a certain color range into gray.
 F_chromakey:   Turns a certain color into transparency. Operates on
                YUV colors.
@@ -273,6 +277,7 @@ F_crop:             Crops the input video.
 F_cropdetect:  Auto-detect crop size.
 F_curves:      Adjust components curves.
 F_datascope:   Video data analysis.
+F_dblur:       Apply Directional blur filter.
 F_dctdnoiz:    Denoise frames using 2D DCT.
 F_deband:      Debands video.
 F_deblock:     Deblock video.
@@ -323,6 +328,7 @@ F_framestep:        Select one frame every N frames.
 F_fspp:                Applies Fast Simple Post-processing filter.
 F_gblur:       Apply Gaussian Blur filter.
 F_gradfun:     Debands video quickly using gradients.
+F_gradients:   Draws a transparent gradient.
 F_graphmonitor:        Show various filtergraph stats.
 F_greyedge:    Estimates scene illumination by grey edge
                assumption.
@@ -354,6 +360,7 @@ F_lutyuv:   Compute and apply a lookup table to YUV input.
 F_mandelbrot:  Render a Mandelbrot fractal.
 F_maskfun:     Create Mask.
 F_mcdeint:     Applies motion compensating deinterlacing.
+F_median:      Pick median pixel from rectangle defined by radius.
 F_mestimate:   Generate motion vectors.
 F_metadata:    Manipulate video frame metadata.
 F_mpdecimate:  Remove near-duplicate frames.
@@ -377,6 +384,8 @@ F_pal75bars:        Generate PAL 75% color bars. This only works with
 F_perms:       Set permissions for the output video frame.
 F_perspective: Corrects the perspective of video.
 F_phase:       Phase shift fields.
+F_photosensitivity:     Filter out photosensitive epilepsy seizure-inducing
+               flashes.
 F_pixscope:    Pixel data analysis for checking color and levels.
                It will display sample values of color channels.
 F_pp:          Filters video using libpostproc.
@@ -402,6 +411,9 @@ F_sab:              Applies shape adaptive blur.
 F_scale:       Scale the input video size and/or convert the image
                format.
 F_scale_cuda:  GPU accelerated video resizer.
+F_scdet:       Detect video scene change.
+F_scroll:      Scroll input video horizontally and/or vertically
+               by constant speed.
 F_separatefields: Split input video frames into fields.
 F_setparams:   Force field, or color property for the output video
                frame.
@@ -410,6 +422,8 @@ F_showinfo: Show textual information for each video frame.
 F_showpalette: Display frame palette.
 F_shuffleframes: Shuffles video frames.
 F_shuffleplanes: Shuffles video planes.
+F_sierpinski:  Generate a Sierpinski carpet/triangle fractal, and
+               randomly pan around.
 F_signalstats: Separates statistics from video analysis.
 F_smartblur:   Blurs the input video without impacting the outlines.
                Through the settings you can select the radius, the
@@ -427,15 +441,20 @@ F_tpad:           Temporarily pad video frames.
 F_tblend:      Blend successive frames.
 F_testsrc:     Generate test pattern.
 F_testsrc2:    Generate another test pattern.
+F_thistogram:  Compute and draw a color distribution histogram for
+               the input video across time.
 F_tile:                Tile several successive frames together.
 F_tinterlace:  Performs temporal field interlacing.
 F_tlut2:       Compute and apply a lookup table from two
                successive frames.
+F_tmedian:     Pick median pixels from successive frames.
 F_tmix:                Mix successive video frames.
 F_transpose:   Transposes input video.
 F_transpose_vaapi: VAAPI VPP for transpose.
 F_unsharp:     Sharpen or blur the input video.
+F_untile:      Untile a frame into a sequence of frames.
 F_uspp:                Applies Ultra Simple/Slow Post-processing filter.
+F_v360:                Convert 360 videos between various formats.
 F_vaguedenoiser: Applies a Wavelet based Denoiser.
 F_vectorscope: Video vectorscope.
 F_vflip:       Flips the input video vertically.
@@ -451,6 +470,7 @@ F_weave:    Weaves input video fields into frames.
 F_xbr:         Scales the input using xBR algorithm.
 F_yadif:       Deinterlaces the input image.
 F_yadif_cuda:  Deinterlace CUDA frames.
+F_yaepblur:    Yet another edge preserving blur filter.
 F_yuvtestsrc:  Generate YUV test pattern.
 F_zoompan:     Applies Zoom & Pan effect.
 #
@@ -488,6 +508,7 @@ F_asoftclip:        Apply audio soft clipping - a type of distortion effect
                where signal amplitude is saturated along a smooth
                curve.
 F_astats:      Shows time domain statistics about audio frames.
+F_asubboost:   Boost subwoofer frequencies.
 F_atempo:      Adjusts audio tempo.
 F_atrim:       Pick one continuous section from the input and drop
                the rest.
index f0c75d8d5daad29102f40bd6ca3f570c7e98bc6c..2b3b98d5fdc61b0ca967c76c88cc65e96ed1f3f1 100644 (file)
@@ -182,8 +182,7 @@ ResampleRTResample::ResampleRTResample(ResampleRT *plugin)
 // To get the keyframes to work, resampling is always done in the forward
 // direction with the plugin converting to reverse.
 int ResampleRTResample::read_samples(Samples *buffer,
-       int64_t start,
-       int64_t len)
+       int64_t start, int64_t len, int direction)
 {
        int result = plugin->read_samples(buffer,
                0,
index 4bada326c373fcac719fd9ff3de1dfda4585f6dd..63baf08db2429d1a0397c2de6d00c45a746b7807 100644 (file)
@@ -89,7 +89,7 @@ class ResampleRTResample : public Resample
 {
 public:
        ResampleRTResample(ResampleRT *plugin);
-       int read_samples(Samples *buffer, int64_t start, int64_t len);
+       int read_samples(Samples *buffer, int64_t start, int64_t len, int direction);
        ResampleRT *plugin;
 };
 
index 52434590766849483c677e731520787c307452a7..3619d43953f75a1c8245c47604aae43c26bd3492 100644 (file)
@@ -841,6 +841,7 @@ void BlondTheme::initialize()
        splice_data = new_button("splice.png", editpanel_up, editpanel_hi, editpanel_dn, "slicebutton");
        new_button("toclip.png", editpanel_up, editpanel_hi, editpanel_dn, "toclip");
        new_button("goto.png", editpanel_up, editpanel_hi, editpanel_dn, "goto");
+       new_button("clapper.png", editpanel_up, editpanel_hi, editpanel_dn, "clapperbutton");
        new_button("top_justify.png", editpanel_up, editpanel_hi, editpanel_dn, "top_justify");
        new_button("undo.png", editpanel_up, editpanel_hi, editpanel_dn, "undo");
        new_button("wrench.png", editpanel_up, editpanel_hi, editpanel_dn, "wrench");
diff --git a/cinelerra-5.1/plugins/theme_blond/data/clapper.png b/cinelerra-5.1/plugins/theme_blond/data/clapper.png
new file mode 100755 (executable)
index 0000000..ec4656c
Binary files /dev/null and b/cinelerra-5.1/plugins/theme_blond/data/clapper.png differ
index 6db8b722f4ee49a75d771acde1ddb32a193b4bcc..993f75ad3436e962b94467d8a2097c7bb986bf32 100644 (file)
@@ -854,6 +854,7 @@ void BlondCVTheme::initialize()
        splice_data = new_button("splice.png", editpanel_up, editpanel_hi, editpanel_dn, "slicebutton");
        new_button("toclip.png", editpanel_up, editpanel_hi, editpanel_dn, "toclip");
        new_button("goto.png", editpanel_up, editpanel_hi, editpanel_dn, "goto");
+       new_button("clapper.png", editpanel_up, editpanel_hi, editpanel_dn, "clapperbutton");
        new_button("top_justify.png", editpanel_up, editpanel_hi, editpanel_dn, "top_justify");
        new_button("undo.png", editpanel_up, editpanel_hi, editpanel_dn, "undo");
        //new_button("wrench.png", editpanel_up, editpanel_hi, editpanel_dn, "wrench");
diff --git a/cinelerra-5.1/plugins/theme_blond_cv/data/clapper.png b/cinelerra-5.1/plugins/theme_blond_cv/data/clapper.png
new file mode 100755 (executable)
index 0000000..ec4656c
Binary files /dev/null and b/cinelerra-5.1/plugins/theme_blond_cv/data/clapper.png differ
index b589e0e325af001b08a69d252ffe459df689d99f..f83fc3a8722c32da3ceb7c14493a499f73482cc5 100644 (file)
@@ -839,6 +839,7 @@ void BlueDotTheme::initialize()
        splice_data = new_button("splice.png", editpanel_up, editpanel_hi, editpanel_dn, "slicebutton");
        new_button("toclip.png", editpanel_up, editpanel_hi, editpanel_dn, "toclip");
        new_button("goto.png", editpanel_up, editpanel_hi, editpanel_dn, "goto");
+       new_button("clapper.png", editpanel_up, editpanel_hi, editpanel_dn, "clapperbutton");
        new_button("top_justify.png", editpanel_up, editpanel_hi, editpanel_dn, "top_justify");
        new_button("undo.png", editpanel_up, editpanel_hi, editpanel_dn, "undo");
        new_button("wrench.png", editpanel_up, editpanel_hi, editpanel_dn, "wrench");
diff --git a/cinelerra-5.1/plugins/theme_blue/data/clapper.png b/cinelerra-5.1/plugins/theme_blue/data/clapper.png
new file mode 100755 (executable)
index 0000000..ec4656c
Binary files /dev/null and b/cinelerra-5.1/plugins/theme_blue/data/clapper.png differ
index 3d000749d7d74c877c008a3faf0c97f1a6ba4c99..dd42972e44c194f74d3a53b0d8bb969b4fa9deea 100644 (file)
@@ -872,6 +872,7 @@ void BlueDotTheme::initialize()
        splice_data = new_button("splice.png", editpanel_up, editpanel_hi, editpanel_dn, "slicebutton");
        new_button("toclip.png", editpanel_up, editpanel_hi, editpanel_dn, "toclip");
        new_button("goto.png", editpanel_up, editpanel_hi, editpanel_dn, "goto");
+       new_button("clapper.png", editpanel_up, editpanel_hi, editpanel_dn, "clapperbutton");
        new_button("top_justify.png", editpanel_up, editpanel_hi, editpanel_dn, "top_justify");
        new_button("undo.png", editpanel_up, editpanel_hi, editpanel_dn, "undo");
        new_button("wrench.png", "editpanelW_up.png", "editpanelW_hi.png", "editpanelW_dn.png", "wrench");
diff --git a/cinelerra-5.1/plugins/theme_blue_dot/data/clapper.png b/cinelerra-5.1/plugins/theme_blue_dot/data/clapper.png
new file mode 100755 (executable)
index 0000000..ec4656c
Binary files /dev/null and b/cinelerra-5.1/plugins/theme_blue_dot/data/clapper.png differ
index 6c8afa05e0358118d136c11fa42ca17337286452..2ca4541de55d3b0dc627d72872a74413efff9e47 100644 (file)
@@ -843,6 +843,7 @@ void BrightTheme::initialize()
        splice_data = new_button("splice.png", editpanel_up, editpanel_hi, editpanel_dn, "slicebutton");
        new_button("toclip.png", editpanel_up, editpanel_hi, editpanel_dn, "toclip");
        new_button("goto.png", editpanel_up, editpanel_hi, editpanel_dn, "goto");
+       new_button("clapper.png", editpanel_up, editpanel_hi, editpanel_dn, "clapperbutton");
        new_button("top_justify.png", editpanel_up, editpanel_hi, editpanel_dn, "top_justify");
        new_button("undo.png", editpanel_up, editpanel_hi, editpanel_dn, "undo");
        new_button("wrench.png", editpanel_up, editpanel_hi, editpanel_dn, "wrench");
diff --git a/cinelerra-5.1/plugins/theme_bright/data/clapper.png b/cinelerra-5.1/plugins/theme_bright/data/clapper.png
new file mode 100755 (executable)
index 0000000..ec4656c
Binary files /dev/null and b/cinelerra-5.1/plugins/theme_bright/data/clapper.png differ
index 22d2d554cb76893cbb7187c3755ad958588d735c..9c0cdd975c0b0c46c94949ba1e17d21822afa493 100644 (file)
@@ -898,6 +898,9 @@ void CAKEWALKTHEME::initialize()
  new_button("goto.png",
       editpanel_up, editpanel_hi, editpanel_dn,
       "goto");
+ new_button("clapper.png",
+      editpanel_up, editpanel_hi, editpanel_dn,
+      "clapperbutton");
  new_button("top_justify.png",
       editpanel_up, editpanel_hi, editpanel_dn,
       "top_justify");
diff --git a/cinelerra-5.1/plugins/theme_cakewalk/data/clapper.png b/cinelerra-5.1/plugins/theme_cakewalk/data/clapper.png
new file mode 100755 (executable)
index 0000000..ec4656c
Binary files /dev/null and b/cinelerra-5.1/plugins/theme_cakewalk/data/clapper.png differ
diff --git a/cinelerra-5.1/plugins/theme_hulk/data/clapper.png b/cinelerra-5.1/plugins/theme_hulk/data/clapper.png
new file mode 100755 (executable)
index 0000000..ec4656c
Binary files /dev/null and b/cinelerra-5.1/plugins/theme_hulk/data/clapper.png differ
index e9b216fe4ed6d0da57c4ce727d650289b3ea2579..2a93f8abcc973b02d278474aad2ca825979fd6f4 100644 (file)
@@ -838,6 +838,7 @@ void HULKTHEME::initialize()
        splice_data = new_button("splice.png", editpanel_up, editpanel_hi, editpanel_dn, "slicebutton");
        new_button("toclip.png", editpanel_up, editpanel_hi, editpanel_dn, "toclip");
        new_button("goto.png", editpanel_up, editpanel_hi, editpanel_dn, "goto");
+       new_button("clapper.png", editpanel_up, editpanel_hi, editpanel_dn, "clapperbutton");
        new_button("top_justify.png", editpanel_up, editpanel_hi, editpanel_dn, "top_justify");
        new_button("undo.png", editpanel_up, editpanel_hi, editpanel_dn, "undo");
        new_button("wrench.png", editpanel_up, editpanel_hi, editpanel_dn, "wrench");
diff --git a/cinelerra-5.1/plugins/theme_neophyte/data/clapper.png b/cinelerra-5.1/plugins/theme_neophyte/data/clapper.png
new file mode 100755 (executable)
index 0000000..ec4656c
Binary files /dev/null and b/cinelerra-5.1/plugins/theme_neophyte/data/clapper.png differ
index e58d2c049f2f92f4fd30b968ee15c7b0854a8613..744f5dcfaed4fccff08d5f7c3c940d94ef7308d3 100644 (file)
@@ -1042,6 +1042,9 @@ void NEOPHYTETHEME::initialize()
        new_button("goto.png",
                           editpanel_up, editpanel_hi, editpanel_dn,
                           "goto");
+       new_button("clapper.png",
+                          editpanel_up, editpanel_hi, editpanel_dn,
+                          "clapperbutton");
        new_button("top_justify.png",
                           editpanel_up, editpanel_hi, editpanel_dn,
                           "top_justify");
diff --git a/cinelerra-5.1/plugins/theme_pinklady/data/clapper.png b/cinelerra-5.1/plugins/theme_pinklady/data/clapper.png
new file mode 100755 (executable)
index 0000000..ec4656c
Binary files /dev/null and b/cinelerra-5.1/plugins/theme_pinklady/data/clapper.png differ
index db4ed973b6d8fb121b0ad3290e9bbdf1b91e953d..7de528a7ea6faf6aedaa07bd7ce33f5d1c017f1b 100644 (file)
@@ -838,6 +838,7 @@ void PINKLADY::initialize()
        splice_data = new_button("splice.png", editpanel_up, editpanel_hi, editpanel_dn, "slicebutton");
        new_button("toclip.png", editpanel_up, editpanel_hi, editpanel_dn, "toclip");
        new_button("goto.png", editpanel_up, editpanel_hi, editpanel_dn, "goto");
+       new_button("clapper.png", editpanel_up, editpanel_hi, editpanel_dn, "clapperbutton");
        new_button("top_justify.png", editpanel_up, editpanel_hi, editpanel_dn, "top_justify");
        new_button("undo.png", editpanel_up, editpanel_hi, editpanel_dn, "undo");
        new_button("wrench.png", editpanel_up, editpanel_hi, editpanel_dn, "wrench");
diff --git a/cinelerra-5.1/plugins/theme_suv/data/clapper.png b/cinelerra-5.1/plugins/theme_suv/data/clapper.png
new file mode 100755 (executable)
index 0000000..ec4656c
Binary files /dev/null and b/cinelerra-5.1/plugins/theme_suv/data/clapper.png differ
index 2b20a62ec42b1e0229c3519a7ead8047f37173dd..8f0ed26a67dd8430627bd9413f59611abff79c1d 100644 (file)
@@ -825,6 +825,7 @@ void SUV::initialize()
        splice_data = new_button("splice.png", editpanel_up, editpanel_hi, editpanel_dn, "slicebutton");
        new_button("toclip.png", editpanel_up, editpanel_hi, editpanel_dn, "toclip");
        new_button("goto.png", editpanel_up, editpanel_hi, editpanel_dn, "goto");
+       new_button("clapper.png", editpanel_up, editpanel_hi, editpanel_dn, "clapperbutton");
        new_button("top_justify.png", editpanel_up, editpanel_hi, editpanel_dn, "top_justify");
        new_button("undo.png", editpanel_up, editpanel_hi, editpanel_dn, "undo");
        new_button("wrench.png", editpanel_up, editpanel_hi, editpanel_dn, "wrench");
diff --git a/cinelerra-5.1/plugins/theme_unflat/data/clapper.png b/cinelerra-5.1/plugins/theme_unflat/data/clapper.png
new file mode 100755 (executable)
index 0000000..ec4656c
Binary files /dev/null and b/cinelerra-5.1/plugins/theme_unflat/data/clapper.png differ
index 4a09eb82573242086f84acad14f32403deca3ea0..7529c0de7196ec0af8ff744f945abbaf00a5f645 100644 (file)
@@ -839,6 +839,7 @@ void UNFLATTHEME::initialize()
        splice_data = new_button("splice.png", editpanel_up, editpanel_hi, editpanel_dn,"splicebutton");
        new_button("toclip.png", editpanel_up, editpanel_hi, editpanel_dn, "toclip");
        new_button("goto.png", editpanel_up, editpanel_hi, editpanel_dn, "goto");
+       new_button("clapper.png", editpanel_up, editpanel_hi, editpanel_dn, "clapperbutton");
        new_button("top_justify.png", editpanel_up, editpanel_hi, editpanel_dn, "top_justify");
        new_button("undo.png", editpanel_up, editpanel_hi, editpanel_dn, "undo");
        new_button("wrench.png", editpanel_up, editpanel_hi, editpanel_dn, "wrench");
diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-4.3.patchB b/cinelerra-5.1/thirdparty/src/ffmpeg-4.3.patchB
new file mode 100644 (file)
index 0000000..cac742d
--- /dev/null
@@ -0,0 +1,43 @@
+diff --git a/libswscale/x86/yuv_2_rgb.asm b/libswscale/x86/yuv_2_rgb.asm
+index 575a84d921..003dff1f25 100644
+--- a/libswscale/x86/yuv_2_rgb.asm
++++ b/libswscale/x86/yuv_2_rgb.asm
+@@ -268,9 +268,9 @@ cglobal %1_420_%2%3, GPR_num, GPR_num, reg_num, parameters
+     por    m2, m7
+     por    m1, m6          ; g5  b5  r6  g6  b6  r7  g7  b7  r8  g8  b8  r9  g9  b9  r10 g10
+     por    m2, m3          ; b10 r11 g11 b11 r12 g12 b12 r13 g13 b13 r14 g14 b14 r15 g15 b15
+-    mova [imageq], m0
+-    mova [imageq + 16], m1
+-    mova [imageq + 32], m2
++    movu [imageq], m0
++    movu [imageq + 16], m1
++    movu [imageq + 32], m2
+ %endif ; mmsize = 16
+ %else ; PACK RGB15/16/32
+     packuswb m0, m1
+@@ -300,10 +300,10 @@ cglobal %1_420_%2%3, GPR_num, GPR_num, reg_num, parameters
+     punpckhwd m_green, m_red
+     punpcklwd m5, m6
+     punpckhwd m_alpha, m6
+-    mova [imageq + 0], m_blue
+-    mova [imageq + 8  * time_num], m_green
+-    mova [imageq + 16 * time_num], m5
+-    mova [imageq + 24 * time_num], m_alpha
++    movu [imageq + 0], m_blue
++    movu [imageq + 8  * time_num], m_green
++    movu [imageq + 16 * time_num], m5
++    movu [imageq + 24 * time_num], m_alpha
+ %else ; PACK RGB15/16
+ %define depth 2
+ %if cpuflag(ssse3)
+@@ -342,8 +342,8 @@ cglobal %1_420_%2%3, GPR_num, GPR_num, reg_num, parameters
+     mova m2, m0
+     punpcklbw m0, m1
+     punpckhbw m2, m1
+-    mova [imageq], m0
+-    mova [imageq + 8 * time_num], m2
++    movu [imageq], m0
++    movu [imageq + 8 * time_num], m2
+ %endif ; PACK RGB15/16
+ %endif ; PACK RGB15/16/32