68dd6afd24670fd9804481d603909b54308095fe
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / formattools.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2010-2013 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 "bcsignals.h"
24 #include "clip.h"
25 #include "guicast.h"
26 #include "file.h"
27 #include "filesystem.h"
28 #include "formattools.h"
29 #include "language.h"
30 #ifdef HAVE_DV
31 #include "libdv.h"
32 #endif
33 #include "libmjpeg.h"
34 #include "maxchannels.h"
35 #include "mwindow.h"
36 #include "preferences.h"
37 #include "theme.h"
38 #include "videodevice.inc"
39 #include <string.h>
40 #include <unistd.h>
41 #include <ctype.h>
42
43
44 FormatTools::FormatTools(MWindow *mwindow,
45                                 BC_WindowBase *window,
46                                 Asset *asset)
47 {
48         this->mwindow = mwindow;
49         this->window = window;
50         this->asset = asset;
51
52         aparams_button = 0;
53         vparams_button = 0;
54         aparams_thread = 0;
55         vparams_thread = 0;
56         audio_switch = 0;
57         video_switch = 0;
58         path_textbox = 0;
59         path_button = 0;
60         path_recent = 0;
61         format_title = 0;
62         format_button = 0;
63         format_text = 0;
64         audio_title = 0;
65         video_title = 0;
66         labeled_files = 0;
67         w = window->get_w();
68
69         recording = 0;
70         use_brender = 0;
71         do_audio = 0;
72         do_video = 0;
73         prompt_audio = 0;
74         prompt_audio_channels = 0;
75         prompt_video = 0;
76         prompt_video_compression = 0;
77         file_per_label = 0;
78         locked_compressor = 0;
79         video_driver = 0;
80 }
81
82 FormatTools::~FormatTools()
83 {
84         delete path_recent;
85 SET_TRACE
86         delete path_button;
87 SET_TRACE
88         delete path_textbox;
89 SET_TRACE
90         delete format_button;
91 SET_TRACE
92
93         if(aparams_button) delete aparams_button;
94 SET_TRACE
95         if(vparams_button) delete vparams_button;
96 SET_TRACE
97         if(aparams_thread) delete aparams_thread;
98 SET_TRACE
99         if(vparams_thread) delete vparams_thread;
100 SET_TRACE
101 }
102
103 void FormatTools::create_objects(
104                 int &init_x, int &init_y,
105                 int do_audio, int do_video,   // Include support for audio, video
106                 int prompt_audio, int prompt_video, // Include checkbox for audio, video
107                 int prompt_audio_channels,
108                 int prompt_video_compression,
109                 const char *locked_compressor,
110                 int recording,
111                 int *file_per_label,
112                 int brender,
113                 int horizontal_layout)
114 {
115         int ys10 = yS(10);
116         int x = init_x;
117         int y = init_y;
118         int ylev = init_y;
119         int margin = mwindow->theme->widget_border;
120
121         this->locked_compressor = locked_compressor;
122         this->recording = recording;
123         this->use_brender = brender;
124         this->do_audio = do_audio;
125         this->do_video = do_video;
126         this->prompt_audio = prompt_audio;
127         this->prompt_audio_channels = prompt_audio_channels;
128         this->prompt_video = prompt_video;
129         this->prompt_video_compression = prompt_video_compression;
130         this->file_per_label = file_per_label;
131
132
133         if(asset->format == FILE_UNKNOWN)
134                 asset->format = FILE_FFMPEG;
135 //printf("FormatTools::create_objects 1\n");
136
137         if(!recording)
138         {
139                 int px = x;
140                 window->add_subwindow(path_textbox = new FormatPathText(px, y, this));
141                 px += path_textbox->get_w() + 5;
142                 path_recent = new BC_RecentList("PATH", mwindow->defaults,
143                                         path_textbox, 10, px, y, xS(300), yS(100));
144                 window->add_subwindow(path_recent);
145                 path_recent->load_items(File::formattostr(asset->format));
146                 px += path_recent->get_w();
147                 window->add_subwindow(path_button = new BrowseButton(
148                         mwindow->theme, window, path_textbox, px, y, asset->path,
149                         _("Output to file"), _("Select a file to write to:"), 0));
150
151 // Set w for user.
152                 w = MAX(w, xS(305));
153                 y += path_textbox->get_h() + ys10;
154         }
155         else
156         {
157 //              w = x + xS(305);
158                 w = xS(305);
159         }
160
161         x = init_x;
162         window->add_subwindow(format_title = new BC_Title(x, y, _("File Format:")));
163         x += format_title->get_w() + margin;
164         window->add_subwindow(format_text = new BC_TextBox(x, y, xS(160), 1,
165                 File::formattostr(asset->format)));
166         x += format_text->get_w() + margin;
167 //printf("FormatTools::create_objects %d %p\n", __LINE__, window);
168         window->add_subwindow(format_button = new FormatFormat(x, y, this));
169         format_button->create_objects();
170         x += format_button->get_w() + 5;
171         window->add_subwindow(ffmpeg_type = new FFMpegType(x, y, xS(70), 1, asset->fformat));
172         FFMPEG::set_asset_format(asset, mwindow->edl, asset->fformat);
173         x += ffmpeg_type->get_w();
174         window->add_subwindow(format_ffmpeg = new FormatFFMPEG(x, y, this));
175         format_ffmpeg->create_objects();
176         x = init_x;
177         y += format_button->get_h() + ys10;
178         if( do_audio ) {
179                 window->add_subwindow(audio_title = new BC_Title(x, y, _("Audio:"), LARGEFONT,
180                         BC_WindowBase::get_resources()->audiovideo_color));
181                 x += audio_title->get_w() + margin;
182                 window->add_subwindow(aparams_button = new FormatAParams(mwindow, this, x, y));
183                 x += aparams_button->get_w() + margin;
184                 if(prompt_audio) {
185                         window->add_subwindow(audio_switch = new FormatAudio(x, y, this, asset->audio_data));
186                 }
187                 x = init_x;
188                 ylev = y;
189                 y += aparams_button->get_h() + ys10;
190
191 //printf("FormatTools::create_objects 6\n");
192                 aparams_thread = new FormatAThread(this);
193         }
194
195 //printf("FormatTools::create_objects 7\n");
196         if( do_video ) {
197                 if( horizontal_layout && do_audio ) {
198                         x += xS(370);
199                         y = ylev;
200                 }
201
202 //printf("FormatTools::create_objects 8\n");
203                 window->add_subwindow(video_title = new BC_Title(x, y, _("Video:"), LARGEFONT,
204                         BC_WindowBase::get_resources()->audiovideo_color));
205                 x += video_title->get_w() + margin;
206                 if(prompt_video_compression) {
207                         window->add_subwindow(vparams_button = new FormatVParams(mwindow, this, x, y));
208                         x += vparams_button->get_w() + margin;
209                 }
210
211 //printf("FormatTools::create_objects 9\n");
212                 if(prompt_video) {
213                         window->add_subwindow(video_switch = new FormatVideo(x, y, this, asset->video_data));
214                         y += video_switch->get_h();
215                 }
216                 else {
217                         y += vparams_button->get_h();
218                 }
219
220 //printf("FormatTools::create_objects 10\n");
221                 y += ys10;
222                 vparams_thread = new FormatVThread(this);
223         }
224
225 //printf("FormatTools::create_objects 11\n");
226
227         x = init_x;
228         if( file_per_label ) {
229                 labeled_files = new FormatFilePerLabel(this, x, y, file_per_label);
230                 window->add_subwindow(labeled_files);
231                 y += labeled_files->get_h() + ys10;
232         }
233
234 //printf("FormatTools::create_objects 12\n");
235
236         init_y = y;
237         update_format();
238 }
239
240 void FormatTools::update_driver(int driver)
241 {
242         this->video_driver = driver;
243
244         locked_compressor = 0;
245         switch(driver)
246         {
247                 case CAPTURE_DVB:
248                 case VIDEO4LINUX2MPEG:
249 // Just give the user information about how the stream is going to be
250 // stored but don't change the asset.
251 // Want to be able to revert to user settings.
252                         if(asset->format == FILE_MPEG) break;
253                         asset->format = FILE_MPEG;
254                         format_text->update(File::formattostr(asset->format));
255                         asset->audio_data = 1;
256                         asset->video_data = 1;
257                         audio_switch->update(1);
258                         video_switch->update(1);
259                         break;
260
261                 case CAPTURE_IEC61883:
262                 case CAPTURE_FIREWIRE:
263                 case VIDEO4LINUX2JPEG:
264                 case CAPTURE_JPEG_WEBCAM:
265                         asset->format = FILE_FFMPEG;
266                         format_text->update(File::formattostr(asset->format));
267
268                         switch(driver) {
269 #ifdef HAVE_DV
270                         case CAPTURE_IEC61883:
271                         case CAPTURE_FIREWIRE:
272                                 locked_compressor = (char*)CODEC_TAG_DVSD;
273                                 break;
274 #endif
275                         case VIDEO4LINUX2JPEG:
276                                 locked_compressor = (char*)CODEC_TAG_MJPEG;
277                                 break;
278
279                         case CAPTURE_JPEG_WEBCAM:
280                                 locked_compressor = (char*)CODEC_TAG_JPEG;
281                                 break;
282                         }
283                         if( locked_compressor )
284                                 strcpy(asset->vcodec, locked_compressor);
285
286                         audio_switch->update(asset->audio_data);
287                         video_switch->update(asset->video_data);
288                         break;
289
290                 default:
291                         format_text->update(File::formattostr(asset->format));
292                         audio_switch->update(asset->audio_data);
293                         video_switch->update(asset->video_data);
294                         break;
295         }
296         close_format_windows();
297         update_format();
298 }
299
300 void FormatTools::update_format()
301 {
302         if( do_audio && prompt_audio && audio_switch ) {
303                 audio_switch->update(asset->audio_data);
304                 if( File::renders_audio(asset) )
305                         audio_switch->enable();
306                 else
307                         audio_switch->disable();
308         }
309         if( do_video && prompt_video && video_switch ) {
310                 video_switch->update(asset->video_data);
311                 if( File::renders_video(asset) )
312                         video_switch->enable();
313                 else
314                         video_switch->disable();
315         }
316         if( asset->format == FILE_FFMPEG ) {
317                 ffmpeg_type->show();
318                 format_ffmpeg->show();
319         }
320         else {
321                 ffmpeg_type->hide();
322                 format_ffmpeg->hide();
323         }
324 }
325
326 int FormatTools::handle_event()
327 {
328         return 0;
329 }
330
331 Asset* FormatTools::get_asset()
332 {
333         return asset;
334 }
335
336 void FormatTools::update_extension()
337 {
338         const char *extension = File::get_tag(asset->format);
339 // split multiple extensions
340         ArrayList<const char*> extensions;
341         int len = !extension ? -1 : strlen(extension);
342         const char *extension_ptr = extension;
343         for(int i = 0; i <= len; i++)
344         {
345                 if(extension[i] == '/' || extension[i] == 0)
346                 {
347                         extensions.append(extension_ptr);
348                         extension_ptr = extension + i + 1;
349                 }
350         }
351
352         if(extensions.size())
353         {
354                 char *ptr = strrchr(asset->path, '.');
355                 if(!ptr)
356                 {
357                         ptr = asset->path + strlen(asset->path);
358                         *ptr = '.';
359                 }
360                 ptr++;
361
362 // test for equivalent extension
363                 int need_extension = 1;
364                 //int extension_len = 0;
365                 for(int i = 0; i < extensions.size() && need_extension; i++)
366                 {
367                         char *ptr1 = ptr;
368                         extension_ptr = extensions.get(i);
369 // test an extension
370                         need_extension = 0;
371                         while(*ptr1 != 0 && *extension_ptr != 0 && *extension_ptr != '/')
372                         {
373                                 if(tolower(*ptr1) != tolower(*extension_ptr))
374                                 {
375                                         need_extension = 1;
376                                         break;
377                                 }
378                                 ptr1++;
379                                 extension_ptr++;
380                         }
381
382                         if( (!*ptr1 && (*extension_ptr && *extension_ptr != '/')) ||
383                             (*ptr1 && (!*extension_ptr || *extension_ptr == '/')) )
384                                 need_extension = 1;
385                 }
386
387 //printf("FormatTools::update_extension %d %d\n", __LINE__, need_extension);
388 // copy extension
389                 if(need_extension)
390                 {
391                         char *ptr1 = ptr;
392 // change "qt" to "mov" since ffmpeg does not know qt
393                         extension_ptr = asset->format != FILE_FFMPEG ? extensions.get(0) :
394                                 !strcmp(asset->fformat, "qt" ) ||
395                                 !strcmp(asset->fformat, "pro" ) ? "mov" : asset->fformat ;
396                         while(*extension_ptr != 0 && *extension_ptr != '/')
397                                 *ptr1++ = *extension_ptr++;
398                         *ptr1 = 0;
399                 }
400
401                 int character1 = ptr - asset->path;
402                 int character2 = strlen(asset->path);
403 //              *(asset->path + character2) = 0;
404                 if(path_textbox)
405                 {
406                         path_textbox->update(asset->path);
407                         path_textbox->set_selection(character1, character2, character2);
408                 }
409         }
410 }
411
412 void FormatTools::update(Asset *asset, int *file_per_label)
413 {
414         this->asset = asset;
415         this->file_per_label = file_per_label;
416         if( file_per_label ) labeled_files->update(file_per_label);
417         if( path_textbox ) path_textbox->update(asset->path);
418         format_text->update(File::formattostr(asset->format));
419         update_format();
420         close_format_windows();
421 }
422
423 void FormatTools::close_format_windows()
424 {
425 // This is done in ~file
426         if( aparams_thread ) {
427                 if( aparams_thread->running() )
428                         aparams_thread->file->close_window();
429                 aparams_thread->join();
430         }
431         if( vparams_thread ) {
432                 if( vparams_thread->running() )
433                         vparams_thread->file->close_window();
434                 vparams_thread->join();
435         }
436 }
437
438 int FormatTools::get_w()
439 {
440         return asset->format != FILE_FFMPEG ? w :
441                 format_ffmpeg->get_x() + format_ffmpeg->get_w();
442 }
443
444 void FormatTools::set_w(int w)
445 {
446         this->w = w;
447 }
448
449 void FormatTools::reposition_window(int &init_x, int &init_y)
450 {
451         int xs10 = xS(10), xs80 = xS(80);
452         int ys10 = yS(10);
453         int x = init_x;
454         int y = init_y;
455
456         if(path_textbox)
457         {
458                 int px = x;
459                 path_textbox->reposition_window(px, y);
460                 px += path_textbox->get_w() + xS(5);
461                 path_recent->reposition_window(px, y);
462                 px += path_recent->get_w() + xS(8);
463                 path_button->reposition_window(px, y);
464                 y += path_textbox->get_h() + ys10;
465         }
466
467         format_title->reposition_window(x, y);
468         x += xS(90);
469         format_text->reposition_window(x, y);
470         x += format_text->get_w();
471         format_button->reposition_window(x, y);
472         x += format_button->get_w() + 5;
473         ffmpeg_type->reposition_window(x, y);
474         x += ffmpeg_type->get_w();
475         format_ffmpeg->reposition_window(x, y);
476
477         x = init_x;
478         y += format_button->get_h() + ys10;
479
480         if(do_audio)
481         {
482                 audio_title->reposition_window(x, y);
483                 x += xs80;
484                 aparams_button->reposition_window(x, y);
485                 x += aparams_button->get_w() + xs10;
486                 if(prompt_audio) audio_switch->reposition_window(x, y);
487
488                 x = init_x;
489                 y += aparams_button->get_h() + ys10;
490         }
491
492
493         if(do_video)
494         {
495                 video_title->reposition_window(x, y);
496                 x += xs80;
497                 if(prompt_video_compression)
498                 {
499                         vparams_button->reposition_window(x, y);
500                         x += vparams_button->get_w() + xs10;
501                 }
502
503                 if(prompt_video)
504                 {
505                         video_switch->reposition_window(x, y);
506                         y += video_switch->get_h();
507                 }
508                 else
509                 {
510                         y += vparams_button->get_h();
511                 }
512
513                 y += ys10;
514                 x = init_x;
515         }
516
517         if( file_per_label ) {
518                 labeled_files->reposition_window(x, y);
519                 y += labeled_files->get_h() + ys10;
520         }
521
522         init_y = y;
523 }
524
525
526 int FormatTools::set_audio_options()
527 {
528 //      if(video_driver == CAPTURE_DVB)
529 //      {
530 //              return 0;
531 //      }
532
533         if(!aparams_thread->running())
534         {
535                 aparams_thread->start();
536         }
537         else
538         {
539                 aparams_thread->file->raise_window();
540         }
541         return 0;
542 }
543
544 int FormatTools::set_video_options()
545 {
546 //      if(video_driver == CAPTURE_DVB)
547 //      {
548 //              return 0;
549 //      }
550
551         if(!vparams_thread->running())
552         {
553                 vparams_thread->start();
554         }
555         else
556         {
557                 vparams_thread->file->raise_window();
558         }
559
560         return 0;
561 }
562
563
564
565
566
567 FormatAParams::FormatAParams(MWindow *mwindow, FormatTools *format, int x, int y)
568  : BC_Button(x, y, mwindow->theme->get_image_set("wrench"))
569 {
570         this->format = format;
571         set_tooltip(_("Configure audio compression"));
572 }
573
574 FormatAParams::~FormatAParams()
575 {
576 }
577
578 int FormatAParams::handle_event()
579 {
580         format->set_audio_options();
581         format->handle_event();
582         return 1;
583 }
584
585
586
587
588
589 FormatVParams::FormatVParams(MWindow *mwindow, FormatTools *format, int x, int y)
590  : BC_Button(x, y, mwindow->theme->get_image_set("wrench"))
591 {
592         this->format = format;
593         set_tooltip(_("Configure video compression"));
594 }
595
596 FormatVParams::~FormatVParams()
597 {
598 }
599
600 int FormatVParams::handle_event()
601 {
602         format->set_video_options();
603         format->handle_event();
604         return 1;
605 }
606
607
608
609
610
611 FormatAThread::FormatAThread(FormatTools *format)
612  : Thread(1, 0, 0)
613 {
614         this->format = format;
615         file = new File;
616         joined = 1;
617 }
618
619 FormatAThread::~FormatAThread()
620 {
621         delete file;  file = 0;
622         join();
623 }
624
625 void FormatAThread::start()
626 {
627         join();
628         joined = 0;
629         Thread::start();
630 }
631
632
633 void FormatAThread::run()
634 {
635         file->get_options(format, 1, 0);
636 }
637
638
639
640
641 FormatVThread::FormatVThread(FormatTools *format)
642  : Thread(1, 0, 0)
643 {
644         this->format = format;
645         file = new File;
646         joined = 1;
647 }
648
649 FormatVThread::~FormatVThread()
650 {
651         delete file;  file = 0;
652         join();
653 }
654
655 void FormatVThread::start()
656 {
657         join();
658         joined = 0;
659         Thread::start();
660 }
661
662 void FormatVThread::run()
663 {
664         file->get_options(format, 0, 1);
665 }
666
667
668
669
670
671 FormatPathText::FormatPathText(int x, int y, FormatTools *format)
672  : BC_TextBox(x, y, format->w - x -
673                 2*format->mwindow->theme->get_image_set("wrench")[0]->get_w() - xS(20), 1,
674         format->asset->path)
675 {
676         this->format = format;
677 }
678
679 FormatPathText::~FormatPathText()
680 {
681 }
682 int FormatPathText::handle_event()
683 {
684         calculate_suggestions();
685         strcpy(format->asset->path, get_text());
686         format->handle_event();
687         return 1;
688 }
689
690
691
692
693 FormatAudio::FormatAudio(int x, int y, FormatTools *format, int default_)
694  : BC_CheckBox(x,
695         y,
696         default_,
697         (char*)(format->recording ? _("Record audio tracks") : _("Render audio tracks")))
698 {
699         this->format = format;
700 }
701
702 FormatAudio::~FormatAudio() {}
703 int FormatAudio::handle_event()
704 {
705         format->asset->audio_data = get_value();
706         format->handle_event();
707         return 1;
708 }
709
710
711 FormatVideo::FormatVideo(int x, int y, FormatTools *format, int default_)
712  : BC_CheckBox(x,
713         y,
714         default_,
715         (char*)(format->recording ? _("Record video tracks") : _("Render video tracks")))
716 {
717 this->format = format;
718 }
719
720 FormatVideo::~FormatVideo() {}
721 int FormatVideo::handle_event()
722 {
723         format->asset->video_data = get_value();
724         format->handle_event();
725         return 1;
726 }
727
728
729
730
731 FormatFormat::FormatFormat(int x, int y, FormatTools *format)
732  : FormatPopup(x, y, format->do_audio, format->do_video, format->use_brender)
733 {
734         this->format = format;
735 }
736
737 FormatFormat::~FormatFormat()
738 {
739 }
740
741 int FormatFormat::handle_event()
742 {
743         BC_ListBoxItem *selection = get_selection(0, 0);
744         if( selection ) {
745                 int new_format = File::strtoformat(get_selection(0, 0)->get_text());
746 //              if(new_format != format->asset->format)
747                 {
748                         Asset *asset = format->asset;
749                         asset->format = new_format;
750                         asset->audio_data = File::renders_audio(asset);
751                         asset->video_data = File::renders_video(asset);
752                         asset->ff_audio_options[0] = 0;
753                         asset->ff_video_options[0] = 0;
754                         asset->ff_format_options[0] = 0;
755                         format->format_text->update(selection->get_text());
756                         if( !format->use_brender )
757                                 format->update_extension();
758                         format->close_format_windows();
759                         if (format->path_recent) format->path_recent->
760                                 load_items(File::formattostr(format->asset->format));
761                         format->update_format();
762                 }
763                 format->handle_event();
764         }
765         return 1;
766 }
767
768
769 FormatFFMPEG::FormatFFMPEG(int x, int y, FormatTools *format)
770  : FFMPEGPopup(x, y)
771 {
772         this->format = format;
773 }
774
775 FormatFFMPEG::~FormatFFMPEG()
776 {
777 }
778
779 int FormatFFMPEG::handle_event()
780 {
781         BC_ListBoxItem *selection = get_selection(0, 0);
782         if( selection ) {
783                 const char *text = get_selection(0, 0)->get_text();
784                 format->ffmpeg_type->update(text);
785 // forces options load defaults
786                 format->asset->ff_audio_options[0] = 0;
787                 format->asset->ff_video_options[0] = 0;
788                 format->asset->ff_format_options[0] = 0;
789                 FFMPEG::set_asset_format(format->asset, format->mwindow->edl, text);
790                 format->update_extension();
791                 format->close_format_windows();
792                 format->update_format();
793                 format->handle_event();
794         }
795         return 1;
796 }
797
798
799 FormatFilePerLabel::FormatFilePerLabel(FormatTools *format,
800         int x, int y, int *output)
801  : BC_CheckBox(x, y, *output, _("Create new file at each label"))
802 {
803         this->format = format;
804         this->output = output;
805 }
806
807 FormatFilePerLabel::~FormatFilePerLabel()
808 {
809 }
810
811 int FormatFilePerLabel::handle_event()
812 {
813         *output = get_value();
814         format->handle_event();
815         return 1;
816 }
817
818 void FormatFilePerLabel::update(int *output)
819 {
820         this->output = output;
821         set_value(*output ? 1 : 0);
822 }
823
824