upgrade to ffmpeg 4.2, rework mask for speedup
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / edit.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "asset.h"
23 #include "assets.h"
24 #include "bcsignals.h"
25 #include "clip.h"
26 #include "edit.h"
27 #include "edits.h"
28 #include "edl.h"
29 #include "edlsession.h"
30 #include "filexml.h"
31 #include "filesystem.h"
32 #include "labels.h"
33 #include "localsession.h"
34 #include "plugin.h"
35 #include "mainsession.h"
36 #include "strack.h"
37 #include "trackcanvas.h"
38 #include "tracks.h"
39 #include "transition.h"
40 #include <string.h>
41
42
43 Edit::Edit()
44 {
45         reset();
46 }
47
48 Edit::Edit(EDL *edl, Track *track)
49 {
50         reset();
51         this->edl = edl;
52         this->track = track;
53         if(track) this->edits = track->edits;
54         id = EDL::next_id();
55 }
56
57 Edit::Edit(EDL *edl, Edits *edits)
58 {
59         reset();
60         this->edl = edl;
61         this->edits = edits;
62         if(edits) this->track = edits->track;
63         id = EDL::next_id();
64 }
65
66 Edit::~Edit()
67 {
68 //printf("Edit::~Edit 1\n");
69         if(transition) delete transition;
70 //printf("Edit::~Edit 2\n");
71 }
72
73 void Edit::reset()
74 {
75         edl = 0;
76         track = 0;
77         edits = 0;
78         startsource = 0;
79         startproject = 0;
80         length = 0;
81         asset = 0;
82         transition = 0;
83         channel = 0;
84         user_title[0] = 0;
85         nested_edl = 0;
86         is_plugin = 0;
87         is_selected = 0;
88         hard_left = 0;
89         hard_right = 0;
90         color = 0;
91         group_id = 0;
92 }
93
94 Indexable* Edit::get_source()
95 {
96         if(asset) return asset;
97         if(nested_edl) return nested_edl;
98         return 0;
99 }
100
101 int Edit::copy(int64_t start,
102         int64_t end,
103         FileXML *file,
104         const char *output_path)
105 {
106 // variables
107 //printf("Edit::copy 1\n");
108
109         int64_t endproject = startproject + length;
110         int result;
111
112         if((startproject >= start && startproject <= end) ||  // startproject in range
113                  (endproject <= end && endproject >= start) ||     // endproject in range
114                  (startproject <= start && endproject >= end))    // range in project
115         {
116 // edit is in range
117                 int64_t startproject_in_selection = startproject; // start of edit in selection in project
118                 int64_t startsource_in_selection = startsource; // start of source in selection in source
119                 //int64_t endsource_in_selection = startsource + length; // end of source in selection
120                 int64_t length_in_selection = length;             // length of edit in selection
121 //printf("Edit::copy 2\n");
122
123                 if(startproject < start)
124                 {         // start is after start of edit in project
125                         int64_t length_difference = start - startproject;
126
127                         startsource_in_selection += length_difference;
128                         startproject_in_selection += length_difference;
129                         length_in_selection -= length_difference;
130                 }
131 //printf("Edit::copy 3\n");
132
133                 if(endproject > end)
134                 {         // end is before end of edit in project
135                         length_in_selection = end - startproject_in_selection;
136                 }
137
138 //printf("Edit::copy 4\n");
139                 if(file)    // only if not counting
140                 {
141                         file->tag.set_title("EDIT");
142                         file->tag.set_property("STARTSOURCE", startsource_in_selection);
143                         file->tag.set_property("CHANNEL", (int64_t)channel);
144                         file->tag.set_property("LENGTH", length_in_selection);
145                         file->tag.set_property("HARD_LEFT", hard_left);
146                         file->tag.set_property("HARD_RIGHT", hard_right);
147                         file->tag.set_property("COLOR", color);
148                         file->tag.set_property("GROUP_ID", group_id);
149                         if(user_title[0]) file->tag.set_property("USER_TITLE", user_title);
150 //printf("Edit::copy 5\n");
151
152                         copy_properties_derived(file, length_in_selection);
153
154                         file->append_tag();
155 //                      file->append_newline();
156 //printf("Edit::copy 6\n");
157
158                         if(nested_edl)
159                         {
160                                 file->tag.set_title("NESTED_EDL");
161                                 file->tag.set_property("SRC", nested_edl->path);
162                                 file->append_tag();
163                                 file->tag.set_title("/NESTED_EDL");
164                                 file->append_tag();
165                                 file->append_newline();
166                         }
167
168                         if(asset)
169                         {
170 //printf("Edit::copy 6 %s\n", asset->path);
171                                 char stored_path[BCTEXTLEN];
172                                 char asset_directory[BCTEXTLEN];
173                                 char output_directory[BCTEXTLEN];
174                                 FileSystem fs;
175
176 //printf("Edit::copy %d %s\n", __LINE__, asset->path);
177                                 fs.extract_dir(asset_directory, asset->path);
178 //printf("Edit::copy %d %s\n", __LINE__, asset->path);
179
180                                 if(output_path)
181                                         fs.extract_dir(output_directory, output_path);
182                                 else
183                                         output_directory[0] = 0;
184 //printf("Edit::copy %s, %s %s, %s\n", asset->path, asset_directory, output_path, output_directory);
185
186                                 if(output_path && !strcmp(asset_directory, output_directory))
187                                         fs.extract_name(stored_path, asset->path);
188                                 else
189                                         strcpy(stored_path, asset->path);
190
191                                 file->tag.set_title("FILE");
192                                 file->tag.set_property("SRC", stored_path);
193                                 file->append_tag();
194                                 file->tag.set_title("/FILE");
195                                 file->append_tag();
196                         }
197
198                         if(transition && startsource_in_selection == startsource)
199                         {
200                                 transition->save_xml(file);
201                         }
202
203 //printf("Edit::copy 7\n");
204                         file->tag.set_title("/EDIT");
205                         file->append_tag();
206                         file->append_newline();
207 //printf("Edit::copy 8\n");
208                 }
209 //printf("Edit::copy 9\n");
210                 result = 1;
211         }
212         else
213         {
214                 result = 0;
215         }
216 //printf("Edit::copy 10\n");
217         return result;
218 }
219
220
221 int64_t Edit::get_source_end(int64_t default_)
222 {
223         return default_;
224 }
225
226 void Edit::insert_transition(char *title)
227 {
228 //printf("Edit::insert_transition this=%p title=%p title=%s\n", this, title, title);
229         delete transition;
230         transition = new Transition(edl, this, title,
231                 track->to_units(edl->session->default_transition_length, 1));
232 }
233
234 void Edit::detach_transition()
235 {
236         if(transition) delete transition;
237         transition = 0;
238 }
239
240 int Edit::silence()
241 {
242         return (track->data_type != TRACK_SUBTITLE ?
243                 asset || nested_edl :
244                 *((SEdit *)this)->get_text()) ? 0 : 1;
245 }
246
247 void Edit::set_selected(int v)
248 {
249         if( group_id )
250                 edl->tracks->set_group_selected(group_id, v);
251         else
252                 is_selected = v >= 0 ? v : !is_selected ? 1 : 0;
253 }
254
255 void Edit::copy_from(Edit *edit)
256 {
257         this->nested_edl = edl->nested_edls.get_nested(edit->nested_edl);
258         this->asset = edl->assets->update(edit->asset);
259         this->startsource = edit->startsource;
260         this->startproject = edit->startproject;
261         this->length = edit->length;
262         this->hard_left = edit->hard_left;
263         this->hard_right = edit->hard_right;
264         this->color = edit->color;
265         this->group_id = edit->group_id;
266         strcpy (this->user_title, edit->user_title);
267
268         if(edit->transition)
269         {
270                 if(!transition) transition = new Transition(edl,
271                         this,
272                         edit->transition->title,
273                         edit->transition->length);
274                 *this->transition = *edit->transition;
275         }
276         this->channel = edit->channel;
277 }
278
279 void Edit::equivalent_output(Edit *edit, int64_t *result)
280 {
281 // End of edit changed
282         if(startproject + length != edit->startproject + edit->length)
283         {
284                 int64_t new_length = MIN(startproject + length,
285                         edit->startproject + edit->length);
286                 if(*result < 0 || new_length < *result)
287                         *result = new_length;
288         }
289
290         if(
291 // Different nested EDLs
292                 (edit->nested_edl && !nested_edl) ||
293                 (!edit->nested_edl && nested_edl) ||
294 // Different assets
295                 (edit->asset == 0 && asset != 0) ||
296                 (edit->asset != 0 && asset == 0) ||
297 // different transitions
298                 (edit->transition == 0 && transition != 0) ||
299                 (edit->transition != 0 && transition == 0) ||
300 // Position changed
301                 (startproject != edit->startproject) ||
302                 (startsource != edit->startsource) ||
303 // Transition changed
304                 (transition && edit->transition &&
305                         !transition->identical(edit->transition)) ||
306 // Asset changed
307                 (asset && edit->asset &&
308                         !asset->equivalent(*edit->asset, 1, 1, edl)) ||
309 // Nested EDL changed
310                 (nested_edl && edit->nested_edl &&
311                         strcmp(nested_edl->path, edit->nested_edl->path))
312                 )
313         {
314 // Start of edit changed
315                 if(*result < 0 || startproject < *result) *result = startproject;
316         }
317 }
318
319
320 Edit& Edit::operator=(Edit& edit)
321 {
322 //printf("Edit::operator= called\n");
323         copy_from(&edit);
324         return *this;
325 }
326
327 void Edit::synchronize_params(Edit *edit)
328 {
329         copy_from(edit);
330 }
331
332
333 // Comparison for ResourcePixmap drawing
334 int Edit::identical(Edit &edit)
335 {
336         int result = (this->nested_edl == edit.nested_edl &&
337                 this->asset == edit.asset &&
338                 this->startsource == edit.startsource &&
339                 this->startproject == edit.startproject &&
340                 this->length == edit.length &&
341                 this->transition == edit.transition &&
342                 this->channel == edit.channel);
343         return result;
344 }
345
346 int Edit::operator==(Edit &edit)
347 {
348         return identical(edit);
349 }
350
351 double Edit::frames_per_picon()
352 {
353         return Units::round(picon_w()) / frame_w();
354 }
355
356 double Edit::frame_w()
357 {
358         return track->from_units(1) *
359                 edl->session->sample_rate /
360                 edl->local_session->zoom_sample;
361 }
362
363 double Edit::picon_w()
364 {
365         int w = 0, h = 0;
366         if(asset) {
367                 w = asset->width;
368                 h = asset->height;
369         }
370         else if(nested_edl) {
371                 w = nested_edl->session->output_w;
372                 h = nested_edl->session->output_h;
373         }
374         return w>0 && h>0 ? ((double)edl->local_session->zoom_track*w)/h : 0;
375 }
376
377 int Edit::picon_h()
378 {
379         return edl->local_session->zoom_track;
380 }
381
382
383 int Edit::dump(FILE *fp)
384 {
385         fprintf(fp,"     EDIT %p\n", this); fflush(fp);
386         fprintf(fp,"      nested_edl=%p %s asset=%p %s\n",
387                 nested_edl, nested_edl ? nested_edl->path : "",
388                 asset, asset ? asset->path : "");
389         fflush(fp);
390         fprintf(fp,"      channel %d, color %08x, hard lt/rt %d/%d"
391                 " group_id %d, is_selected %d\n",
392                 channel, color, hard_left, hard_right, group_id, is_selected);
393         if( transition ) {
394                 fprintf(fp,"      TRANSITION %p\n", transition);
395                 transition->dump(fp);
396         }
397         fprintf(fp,"      startsource %jd startproject %jd length %jd\n",
398                 startsource, startproject, length);
399         fflush(fp);
400         return 0;
401 }
402
403 int Edit::load_properties(FileXML *file, int64_t &startproject)
404 {
405         startsource = file->tag.get_property("STARTSOURCE", (int64_t)0);
406         length = file->tag.get_property("LENGTH", (int64_t)0);
407         hard_left = file->tag.get_property("HARD_LEFT", (int64_t)0);
408         hard_right = file->tag.get_property("HARD_RIGHT", (int64_t)0);
409         color = file->tag.get_property("COLOR", 0);
410         group_id = file->tag.get_property("GROUP_ID", group_id);
411         user_title[0] = 0;
412         file->tag.get_property("USER_TITLE", user_title);
413         this->startproject = startproject;
414         load_properties_derived(file);
415         return 0;
416 }
417
418 void Edit::shift(int64_t difference)
419 {
420         startproject += difference;
421 }
422
423 void Edit::trim(int64_t difference)
424 {
425         length += difference;
426         if( startproject < 0 ) {
427                 if( (startsource+=startproject) < 0 ) startsource = 0;
428                 if( (length+=startproject) < 0 ) length = 0;
429                 startproject = 0;
430         }
431         if( startsource < 0 )
432                 startsource = 0;
433         int64_t src_len = get_source_end(INT64_MAX);
434         if( startsource + length > src_len ) {
435                 length = src_len - startsource;
436                 if( length < 0 ) length = 0;
437         }
438         if( length < 0 )
439                 length = 0;
440 }
441
442 int Edit::shift_start(int edit_mode, int64_t newposition, int64_t oldposition,
443         int edit_labels, int edit_autos, int edit_plugins, Edits *trim_edits)
444 {
445         int64_t cut_length = newposition - oldposition;
446         int rest_moved = edit_mode == MOVE_RIPPLE || edit_mode == MOVE_EDGE ? 1 : 0;
447         if( cut_length > length )
448                 cut_length = length;
449         else if( cut_length < -length )
450                 cut_length = -length;
451
452         int64_t start = startproject, end = start + length;
453         Edit *prev = this->previous, *next = this->next;
454         int edits_moved = 0;
455
456         switch( edit_mode ) {
457         case MOVE_RIPPLE:
458                 edits_moved = 1;
459                 startsource += cut_length;
460                 cut_length = -cut_length;
461                 length += cut_length;
462                 for( Edit *edit=next; edit; edit=edit->next )
463                         edit->startproject += cut_length;
464                 break;
465         case MOVE_ROLL:
466                 if( prev && prev->length + cut_length < 0 )
467                         cut_length = -prev->length;
468                 if( prev ) prev->trim(cut_length);
469                 startproject += cut_length;
470                 startsource += cut_length;
471                 length -= cut_length;
472                 break;
473         case MOVE_SLIP:
474                 edits_moved = 1;
475                 startsource -= cut_length;
476                 break;
477         case MOVE_SLIDE:
478                 edits_moved = 1;
479                 if( prev && prev->length + cut_length < 0 )
480                         cut_length = -prev->length;
481                 if( next && next->length - cut_length < 0 )
482                         cut_length = next->length;
483                 if( prev ) prev->trim(cut_length);
484                 startproject += cut_length;
485                 if( next ) {
486                         next->startproject += cut_length;
487                         next->startsource += cut_length;
488                         next->trim(-cut_length);
489                 }
490                 break;
491         case MOVE_EDGE:
492                 edits_moved = 1;
493                 startsource -= cut_length;
494                 length += cut_length;
495                 for( Edit *edit=next; edit; edit=edit->next )
496                         edit->startproject += cut_length;
497                 break;
498         }
499         trim(0);
500         return follow_edits(start, end, cut_length, edits_moved, rest_moved,
501                 edit_labels, edit_autos, edit_plugins, trim_edits);
502 }
503
504 int Edit::shift_end(int edit_mode, int64_t newposition, int64_t oldposition,
505         int edit_labels, int edit_autos, int edit_plugins, Edits *trim_edits)
506 {
507         int64_t cut_length = newposition - oldposition;
508         int rest_moved = edit_mode == MOVE_RIPPLE || edit_mode == MOVE_EDGE ? 1 : 0;
509         if( cut_length > length ) {
510                  if( !rest_moved ) cut_length = length;
511         }
512         else if( cut_length < -length )
513                 cut_length = -length;
514         int64_t start = startproject, end = start + length;
515         Edit *prev = this->previous, *next = this->next;
516         int edits_moved = 0;
517
518         switch( edit_mode ) {
519         case MOVE_RIPPLE:
520         case MOVE_EDGE:
521                 length += cut_length;
522                 for( Edit *edit=next; edit; edit=edit->next )
523                         edit->startproject += cut_length;
524                 break;
525         case MOVE_ROLL:
526                 if( next && next->length - cut_length < 0 )
527                         cut_length = next->length;
528                 length += cut_length;
529                 if( next ) {
530                         next->startproject += cut_length;
531                         next->startsource += cut_length;
532                         next->trim(-cut_length);
533                 }
534                 break;
535         case MOVE_SLIP:
536                 edits_moved = 1;
537                 startsource -= cut_length;
538                 break;
539         case MOVE_SLIDE:
540                 edits_moved = 1;
541                 if( prev && prev->length + cut_length < 0 )
542                         cut_length = -prev->length;
543                 if( next && next->length - cut_length < 0 )
544                         cut_length = next->length;
545                 if( prev ) prev->trim(cut_length);
546                 startproject += cut_length;
547                 if( next ) {
548                         next->startproject += cut_length;
549                         next->startsource += cut_length;
550                         next->trim(-cut_length);
551                 }
552                 break;
553         }
554         trim(0);
555         return follow_edits(start, end, cut_length, edits_moved, rest_moved,
556                 edit_labels, edit_autos, edit_plugins, trim_edits);
557 }
558
559 int Edit::follow_edits(int64_t start, int64_t end, int64_t cut_length,
560                 int edits_moved, int rest_moved, int edit_labels, int edit_autos,
561                 int edit_plugins, Edits *trim_edits)
562 {
563         if( edits_moved && rest_moved ) {
564                 if( edit_labels ) {
565                         double cut_len = track->from_units(cut_length);
566                         double start_pos = edits->track->from_units(start);
567                         if( cut_len < 0 )
568                                 edits->edl->labels->clear(start_pos + cut_len, start_pos, 1);
569                         else if( cut_len > 0 )
570                                 edits->edl->labels->insert(start_pos, cut_len);
571                 }
572                 if( cut_length < 0 )
573                         track->clear(start + cut_length, start,
574                                 0, 0, edit_autos, edit_plugins, trim_edits);
575                 else if( cut_length > 0 ) {
576                         if( edit_autos )
577                                 track->shift_keyframes(start, cut_length);
578                         if( edit_plugins )
579                                 track->shift_effects(start, cut_length, 1, trim_edits);
580                 }
581         }
582         else if( edits_moved ) {
583                 if( edit_labels ) {
584                         double cut_len = track->from_units(cut_length);
585                         double start_pos = edits->track->from_units(start);
586                         edits->edl->labels->insert(start_pos, cut_len);
587                         double end_pos = edits->track->from_units(end);
588                         edits->edl->labels->insert(end_pos + cut_len, -cut_len);
589                 }
590                 if( edit_autos ) {
591                         if( cut_length > 0 ) {
592                                 track->clear(end, end+cut_length, 0, 0, 0, 1, 0);
593                                 track->shift_keyframes(start, cut_length);
594                         }
595                         else if( cut_length < 0 ) {
596                                 track->clear(start+cut_length, start, 0, 0, 0, 1, 0);
597                                 track->shift_keyframes(end+cut_length, -cut_length);
598                         }
599                 }
600                 if( edit_plugins ) {
601                         if( cut_length > 0 ) {
602                                 track->clear(end, end+cut_length, 0, 0, -1, 0, 0);
603                                 track->shift_effects(start, cut_length, 1, 0);
604                         }
605                         else if( cut_length < 0 ) {
606                                 track->clear(start+cut_length, start, 0, 0, -1, 0, 0);
607                                 track->shift_effects(end+cut_length, -cut_length, 1, 0);
608                         }
609                 }
610         }
611         else if( rest_moved ) {
612                 if( edit_labels ) {
613                         double cut_len = track->from_units(cut_length);
614                         double end_pos = edits->track->from_units(end);
615                         if( cut_len < 0 )
616                                 edits->edl->labels->clear(end_pos + cut_len, end_pos, 1);
617                         else if( cut_len > 0 )
618                                 edits->edl->labels->insert(end_pos, cut_len);
619                 }
620                 if( cut_length < 0 )
621                         track->clear(end + cut_length, end,
622                                 0, 0, edit_autos, edit_plugins, trim_edits);
623                 else if( cut_length > 0 ) {
624                         if( edit_autos )
625                                 track->shift_keyframes(end, cut_length);
626                         if( edit_plugins )
627                                 track->shift_effects(end, cut_length, 1, trim_edits);
628                 }
629         }
630         return 0;
631 }
632
633
634
635 int Edit::popup_transition(float view_start, float zoom_units, int cursor_x, int cursor_y)
636 {
637         int64_t left, right, left_unit, right_unit;
638         if(!transition) return 0;
639         get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
640
641         if(cursor_x > left && cursor_x < right)
642         {
643 //              transition->popup_transition(cursor_x, cursor_y);
644                 return 1;
645         }
646         return 0;
647 }
648
649 int Edit::select_handle(float view_start, float zoom_units, int cursor_x, int cursor_y, int64_t &selection)
650 {
651         int64_t left, right, left_unit, right_unit;
652         get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
653
654         int64_t pixel1, pixel2;
655         pixel1 = left;
656         pixel2 = pixel1 + 10;
657
658 // test left edit
659 // cursor_x is faked in acanvas
660         if(cursor_x >= pixel1 && cursor_x <= pixel2)
661         {
662                 selection = left_unit;
663                 return 1;     // left handle
664         }
665
666         //int64_t endproject = startproject + length;
667         pixel2 = right;
668         pixel1 = pixel2 - 10;
669
670 // test right edit
671         if(cursor_x >= pixel1 && cursor_x <= pixel2)
672         {
673                 selection = right_unit;
674                 return 2;     // right handle
675         }
676         return 0;
677 }
678
679 void Edit::get_title(char *title)
680 {
681         if( user_title[0] ) {
682                 strcpy(title, user_title);
683                 return;
684         }
685         Indexable *idxbl = asset ? (Indexable*)asset : (Indexable*)nested_edl;
686         if( !idxbl ) {
687                 title[0] = 0;
688                 return;
689         }
690         FileSystem fs;
691         fs.extract_name(title, idxbl->path);
692         if( asset || track->data_type == TRACK_AUDIO ) {
693                 char number[BCSTRLEN];
694                 sprintf(number, " #%d", channel + 1);
695                 strcat(title, number);
696         }
697 }
698