add finite state machine to control/manage transport state
[ardour.git] / gtk2_ardour / export_video_dialog.cc
1 /*
2  * Copyright (C) 2013-2015 John Emmas <john@creativepost.co.uk>
3  * Copyright (C) 2013-2018 Paul Davis <paul@linuxaudiosystems.com>
4  * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
5  * Copyright (C) 2015 AndrĂ© Nusser <andre.nusser@googlemail.com>
6  * Copyright (C) 2016 Tim Mayberry <mojofunk@gmail.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 #include <cstdio>
23 #include <string>
24 #include <sstream>
25 #include <iomanip>
26
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31
32 #include <sigc++/bind.h>
33
34 #include <gtkmm/filechooserdialog.h>
35 #include <gtkmm/stock.h>
36 #include <gtkmm/table.h>
37
38 #include "pbd/gstdio_compat.h"
39
40 #include "pbd/error.h"
41 #include "pbd/convert.h"
42 #include "gtkmm2ext/keyboard.h"
43 #include "gtkmm2ext/utils.h"
44 #include "ardour/session_directory.h"
45 #include "ardour/profile.h"
46 #include "ardour/template_utils.h"
47 #include "ardour/session.h"
48 #include "ardour_ui.h"
49 #include "gui_thread.h"
50
51 #include "ardour/export_handler.h"
52 #include "ardour/export_status.h"
53 #include "ardour/export_timespan.h"
54 #include "ardour/export_channel_configuration.h"
55 #include "ardour/export_format_specification.h"
56 #include "ardour/export_filename.h"
57 #include "ardour/route.h"
58 #include "ardour/session_metadata.h"
59 #include "ardour/broadcast_info.h"
60
61 #include "opts.h"
62 #include "export_video_dialog.h"
63 #include "utils_videotl.h"
64 #include "pbd/i18n.h"
65
66 using namespace Gtk;
67 using namespace std;
68 using namespace PBD;
69 using namespace ARDOUR;
70 using namespace VideoUtils;
71
72 ExportVideoDialog::ExportVideoDialog ()
73         : ArdourDialog (_("Export Video File "))
74         , _aborted(false)
75         , _twopass(false)
76         , _firstpass(false)
77         , _normalize(false)
78         , _previous_progress(0)
79         , _transcoder(0)
80         , _video_source_aspect_ratio(-1)
81         , _suspend_signals(false)
82         , outfn_path_label (_("File:"), Gtk::ALIGN_LEFT)
83         , outfn_browse_button (_("Browse"))
84         , invid_path_label (_("Video:"), Gtk::ALIGN_LEFT)
85         , invid_browse_button (_("Browse"))
86         , transcode_button (_("Export"))
87         , abort_button (_("Abort"))
88         , progress_box (0)
89         , scale_checkbox (_("Scale Video (W x H):"))
90         , scale_aspect (_("Retain Aspect"))
91         , width_adjustment (768, 128, 1920, 1, 16, 0)
92         , width_spinner (width_adjustment)
93         , height_adjustment (576, 128, 1920, 1, 16, 0)
94         , height_spinner (height_adjustment)
95         , aspect_checkbox (_("Set Aspect Ratio:"))
96         , normalize_checkbox (_("Normalize Audio"))
97         , twopass_checkbox (_("2 Pass Encoding"))
98         , optimizations_checkbox (_("Codec Optimizations:"))
99         , optimizations_label ("-")
100         , deinterlace_checkbox (_("Deinterlace"))
101         , bframes_checkbox (_("Use [2] B-frames (MPEG 2 or 4 only)"))
102         , fps_checkbox (_("Override FPS (Default is to retain FPS from the input video file):"))
103         , meta_checkbox (_("Include Session Metadata"))
104 #if 1 /* tentative debug mode */
105         , debug_checkbox (_("Debug Mode: Print ffmpeg command and output to stdout."))
106 #endif
107 {
108         set_name ("ExportVideoDialog");
109         set_modal (true);
110         set_skip_taskbar_hint (true);
111         set_resizable (false);
112
113         Gtk::Label* l;
114         vbox = manage (new VBox);
115         VBox* options_box = manage (new VBox);
116         HBox* path_hbox;
117
118         /* check if ffmpeg can be found */
119         _transcoder = new TranscodeFfmpeg(X_(""));
120         if (!_transcoder->ffexec_ok()) {
121                 l = manage (new Label (_("ffmpeg installation was not found. Video Export is not possible. See the Log window for more information."), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
122                 l->set_line_wrap();
123                 vbox->pack_start (*l, false, false, 8);
124                 get_vbox()->pack_start (*vbox, false, false);
125                 add_button (Stock::OK, RESPONSE_CANCEL);
126                 show_all_children ();
127                 delete _transcoder; _transcoder = 0;
128                 return;
129         }
130         delete _transcoder; _transcoder = 0;
131
132         l = manage (new Label (_("<b>Output:</b> (file extension defines format)"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
133         l->set_use_markup ();
134         vbox->pack_start (*l, false, false, 4);
135
136         path_hbox = manage (new HBox);
137         path_hbox->pack_start (outfn_path_label, false, false, 3);
138         path_hbox->pack_start (outfn_path_entry, true, true, 3);
139         path_hbox->pack_start (outfn_browse_button, false, false, 3);
140         vbox->pack_start (*path_hbox, false, false, 2);
141
142         l = manage (new Label (_("<b>Input Video:</b>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
143         l->set_use_markup ();
144         vbox->pack_start (*l, false, false, 4);
145
146         path_hbox = manage (new HBox);
147         path_hbox->pack_start (invid_path_label, false, false, 3);
148         path_hbox->pack_start (invid_path_entry, true, true, 3);
149         path_hbox->pack_start (invid_browse_button, false, false, 3);
150         vbox->pack_start (*path_hbox, false, false, 2);
151
152         path_hbox = manage (new HBox);
153         l = manage (new Label (_("Audio:"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
154         path_hbox->pack_start (*l, false, false, 3);
155         l = manage (new Label (_("Master Bus"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
156         path_hbox->pack_start (*l, false, false, 2);
157         vbox->pack_start (*path_hbox, false, false, 2);
158
159         outfn_path_entry.set_width_chars(38);
160
161         l = manage (new Label (_("<b>Settings:</b>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
162         l->set_use_markup ();
163         options_box->pack_start (*l, false, true, 4);
164
165         Table* t = manage (new Table (4, 12));
166         t->set_spacings (4);
167         int ty = 0;
168         options_box->pack_start (*t, true, true, 4);
169         l = manage (new Label (_("Range:"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
170         t->attach (*l, 0, 1, ty, ty+1);
171         t->attach (insnd_combo, 1, 4, ty, ty+1); ty++;
172         l = manage (new Label (_("Preset:"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
173         t->attach (*l, 0, 1, ty, ty+1);
174         t->attach (preset_combo, 1, 4, ty, ty+1); ty++;
175         l = manage (new Label (_("Video Codec:"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
176         t->attach (*l, 0, 1, ty, ty+1);
177         t->attach (video_codec_combo, 1, 2, ty, ty+1);
178         l = manage (new Label (_("Video KBit/s:"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
179         t->attach (*l, 2, 3, ty, ty+1);
180         t->attach (video_bitrate_combo, 3, 4, ty, ty+1); ty++;
181         l = manage (new Label (_("Audio Codec:"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
182         t->attach (*l, 0, 1, ty, ty+1);
183         t->attach (audio_codec_combo, 1, 2, ty, ty+1);
184         l = manage (new Label (_("Audio KBit/s:"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
185         t->attach (*l, 2, 3, ty, ty+1);
186         t->attach (audio_bitrate_combo, 3, 4, ty, ty+1); ty++;
187         l = manage (new Label (_("Audio Samplerate:"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
188         t->attach (*l, 0, 1, ty, ty+1);
189         t->attach (audio_samplerate_combo, 1, 2, ty, ty+1);
190         t->attach (normalize_checkbox, 2, 4, ty, ty+1); ty++;
191         t->attach (scale_checkbox, 0, 1, ty, ty+1);
192         t->attach (scale_aspect, 1, 2, ty, ty+1);
193         t->attach (width_spinner, 2, 3, ty, ty+1);
194         t->attach (height_spinner, 3, 4, ty, ty+1); ty++;
195         t->attach (fps_checkbox, 0, 3, ty, ty+1);
196         t->attach (fps_combo, 3, 4, ty, ty+1); ty++;
197         t->attach (twopass_checkbox, 0, 2, ty, ty+1);
198         t->attach (aspect_checkbox, 2, 3, ty, ty+1);
199         t->attach (aspect_combo, 3, 4, ty, ty+1); ty++;
200         t->attach (bframes_checkbox, 0, 2, ty, ty+1);
201         t->attach (deinterlace_checkbox, 2, 4, ty, ty+1); ty++;
202         t->attach (meta_checkbox, 2, 4, ty, ty+1); ty++;
203         t->attach (optimizations_checkbox, 0, 1, ty, ty+1);
204         t->attach (optimizations_label, 1, 4, ty, ty+1); ty++;
205 #if 1 /* tentative debug mode */
206         t->attach (debug_checkbox, 0, 4, ty, ty+1); ty++;
207 #endif
208
209         preset_combo.append_text("none");
210         preset_combo.append_text("dvd-mp2");
211         preset_combo.append_text("dvd-NTSC");
212         preset_combo.append_text("dvd-PAL");
213         preset_combo.append_text("flv");
214         preset_combo.append_text("mpeg4");
215         preset_combo.append_text("mp4/h264/aac");
216         preset_combo.append_text("ogg");
217         preset_combo.append_text("webm");
218         preset_combo.append_text("you-tube");
219
220         audio_codec_combo.append_text(_("(default for format)"));
221         audio_codec_combo.append_text("ac3");
222         audio_codec_combo.append_text("aac");
223         audio_codec_combo.append_text("libmp3lame");
224         audio_codec_combo.append_text("libvorbis");
225         audio_codec_combo.append_text("mp2");
226         audio_codec_combo.append_text("pcm_s16le");
227
228         video_codec_combo.append_text(_("(default for format)"));
229         video_codec_combo.append_text("flv");
230         video_codec_combo.append_text("libtheora");
231         video_codec_combo.append_text("mjpeg");
232         video_codec_combo.append_text("mpeg2video");
233         video_codec_combo.append_text("mpeg4");
234         video_codec_combo.append_text("h264");
235         video_codec_combo.append_text("vpx (webm)");
236         video_codec_combo.append_text("copy");
237
238         audio_bitrate_combo.append_text(_("(default)"));
239         audio_bitrate_combo.append_text("64k");
240         audio_bitrate_combo.append_text("128k");
241         audio_bitrate_combo.append_text("192k");
242         audio_bitrate_combo.append_text("256k");
243         audio_bitrate_combo.append_text("320k");
244
245         audio_samplerate_combo.append_text("22050");
246         audio_samplerate_combo.append_text("44100");
247         audio_samplerate_combo.append_text("48000");
248
249         video_bitrate_combo.append_text(_("(default)"));
250         video_bitrate_combo.append_text(_("(retain)"));
251         video_bitrate_combo.append_text("200k");
252         video_bitrate_combo.append_text("800k");
253         video_bitrate_combo.append_text("2000k");
254         video_bitrate_combo.append_text("5000k");
255         video_bitrate_combo.append_text("8000k");
256
257         fps_combo.append_text("23.976");
258         fps_combo.append_text("24");
259         fps_combo.append_text("24.976");
260         fps_combo.append_text("25");
261         fps_combo.append_text("29.97");
262         fps_combo.append_text("30");
263         fps_combo.append_text("59.94");
264         fps_combo.append_text("60");
265
266         aspect_combo.append_text("4:3");
267         aspect_combo.append_text("16:9");
268
269         vbox->pack_start (*options_box, false, true, 4);
270         get_vbox()->set_spacing (4);
271         get_vbox()->pack_start (*vbox, false, false);
272
273         progress_box = manage (new VBox);
274         progress_box->pack_start (pbar, false, false);
275         progress_box->pack_start (abort_button, false, false);
276         get_vbox()->pack_start (*progress_box, false, false);
277
278         scale_checkbox.signal_toggled().connect (sigc::mem_fun (*this, &ExportVideoDialog::scale_checkbox_toggled));
279         aspect_checkbox.signal_toggled().connect (sigc::mem_fun (*this, &ExportVideoDialog::aspect_checkbox_toggled));
280         fps_checkbox.signal_toggled().connect (sigc::mem_fun (*this, &ExportVideoDialog::fps_checkbox_toggled));
281         preset_combo.signal_changed().connect (sigc::mem_fun (*this, &ExportVideoDialog::preset_combo_changed));
282         video_codec_combo.signal_changed().connect (sigc::mem_fun (*this, &ExportVideoDialog::video_codec_combo_changed));
283         outfn_browse_button.signal_clicked().connect (sigc::mem_fun (*this, &ExportVideoDialog::open_outfn_dialog));
284         invid_browse_button.signal_clicked().connect (sigc::mem_fun (*this, &ExportVideoDialog::open_invid_dialog));
285         transcode_button.signal_clicked().connect (sigc::mem_fun (*this, &ExportVideoDialog::launch_export));
286         abort_button.signal_clicked().connect (sigc::mem_fun (*this, &ExportVideoDialog::abort_clicked));
287
288         invid_path_entry.signal_changed().connect (sigc::mem_fun (*this, &ExportVideoDialog::set_original_file_information));
289         width_spinner.signal_value_changed().connect (sigc::mem_fun (*this, &ExportVideoDialog::width_value_changed));
290         height_spinner.signal_value_changed().connect (sigc::mem_fun (*this, &ExportVideoDialog::height_value_changed));
291
292         cancel_button = add_button (Stock::CANCEL, RESPONSE_CANCEL);
293         get_action_area()->pack_start (transcode_button, false, false);
294         show_all_children ();
295         progress_box->hide();
296 }
297
298 ExportVideoDialog::~ExportVideoDialog ()
299 {
300         if (_transcoder) { delete _transcoder; _transcoder = 0;}
301 }
302
303 void
304 ExportVideoDialog::set_original_file_information()
305 {
306         assert(_transcoder == 0);
307         std::string infile = invid_path_entry.get_text();
308
309         if (scale_checkbox.get_active()) {
310                 // user may have set custom values already, don't touch.
311                 return;
312         }
313         if (infile == "" || !Glib::file_test(infile, Glib::FILE_TEST_EXISTS)) {
314                 return;
315         }
316
317         _transcoder = new TranscodeFfmpeg(infile);
318         if (_transcoder->probe_ok()) {
319                 _video_source_aspect_ratio = -1;
320                 width_spinner.set_value(_transcoder->get_width());
321                 height_spinner.set_value(_transcoder->get_height());
322                 _video_source_aspect_ratio = _transcoder->get_aspect();
323         }
324
325         delete _transcoder; _transcoder = 0;
326 }
327 void
328 ExportVideoDialog::apply_state (TimeSelection &tme, bool range)
329 {
330         _suspend_dirty = true; // TODO really just queue 'dirty' and mark session dirty on "Export"
331
332         export_range = tme;
333         _video_source_aspect_ratio = -1;
334
335         outfn_path_entry.set_text (_session->session_directory().export_path() + G_DIR_SEPARATOR +"export.avi");
336
337         // TODO remember setting for export-range.. somehow, (let explicit range override)
338         sampleoffset_t av_offset = ARDOUR_UI::instance()->video_timeline->get_offset();
339
340         insnd_combo.remove_all ();
341
342         insnd_combo.append_text (_("from session start marker to session end marker"));
343
344         if (av_offset < 0 ) {
345                 insnd_combo.append_text (_("from 00:00:00:00 to the video end"));
346         } else {
347                 insnd_combo.append_text (_("from video start to video end"));
348         }
349         if (!export_range.empty()) {
350                 insnd_combo.append_text (_("Selected range"));  // TODO show export_range.start() -> export_range.end_sample()
351         }
352         if (range) {
353                 insnd_combo.set_active(2);
354         } else {
355                 insnd_combo.set_active(0);
356         }
357
358         preset_combo.set_active(0);
359         audio_codec_combo.set_active(0);
360         video_codec_combo.set_active(0);
361         audio_bitrate_combo.set_active(0);
362         audio_samplerate_combo.set_active(2);
363         video_bitrate_combo.set_active(0);
364         aspect_combo.set_active(1);
365
366         scale_checkbox.set_active(false);
367         scale_aspect.set_active(true);
368         aspect_checkbox.set_active(false);
369         normalize_checkbox.set_active(false);
370         twopass_checkbox.set_active(false);
371         optimizations_checkbox.set_active(false);
372         deinterlace_checkbox.set_active(false);
373         bframes_checkbox.set_active(false);
374         fps_checkbox.set_active(false);
375         meta_checkbox.set_active(false);
376
377         float tcfps = _session->timecode_frames_per_second();
378
379         XMLNode* node = _session->extra_xml (X_("Videotimeline"));
380         bool filenameset = false;
381         if (node) {
382                 std::string filename;
383                 if (node->get_property(X_("OriginalVideoFile"), filename)) {
384                         if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) {
385                                 invid_path_entry.set_text (filename);
386                                 filenameset = true;
387                         }
388                 }
389
390                 bool local_file;
391
392                 if (!filenameset && node->get_property (X_("Filename"), filename) &&
393                     node->get_property (X_("LocalFile"), local_file) && local_file) {
394                         if (filename.at(0) != G_DIR_SEPARATOR)
395                         {
396                                 filename = Glib::build_filename (_session->session_directory().video_path(), filename);
397                         }
398                         if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS))
399                         {
400                                 invid_path_entry.set_text (filename);
401                                 filenameset = true;
402                         }
403                 }
404         }
405         if (!filenameset) {
406                 invid_path_entry.set_text (X_(""));
407         }
408
409         node = _session->extra_xml (X_("Videoexport"));
410         if (node) {
411                 bool yn;
412                 if (node->get_property (X_("ChangeGeometry"), yn)) {
413                         scale_checkbox.set_active (yn);
414                 }
415                 if (node->get_property (X_("KeepAspect"), yn)) {
416                         scale_aspect.set_active (yn);
417                 }
418                 if (node->get_property (X_("ChangeAspect"), yn)) {
419                         aspect_checkbox.set_active (yn);
420                 }
421                 if (node->get_property (X_("NormalizeAudio"), yn)) {
422                         normalize_checkbox.set_active (yn);
423                 }
424                 if (node->get_property (X_("TwoPassEncode"), yn)) {
425                         twopass_checkbox.set_active (yn);
426                 }
427                 if (node->get_property (X_("CodecOptimzations"), yn)) {
428                         optimizations_checkbox.set_active (yn);
429                 }
430                 if (node->get_property (X_("Deinterlace"), yn)) {
431                         deinterlace_checkbox.set_active (yn);
432                 }
433                 if (node->get_property (X_("BSamples"), yn)) {
434                         bframes_checkbox.set_active (yn);
435                 }
436                 if (node->get_property (X_("ChangeFPS"), yn)) {
437                         fps_checkbox.set_active (yn);
438                 }
439                 if (node->get_property (X_("Metadata"), yn)) {
440                         meta_checkbox.set_active (yn);
441                 }
442
443                 std::string str;
444                 if (node->get_property (X_("Format"), str) && !str.empty()) {
445                         change_file_extension ("." + str);
446                 }
447
448                 _suspend_signals = true;
449                 double val;
450                 if (node->get_property (X_("Width"), val)) {
451                         width_spinner.set_value (val);
452                 }
453                 if (node->get_property (X_("Height"), val)) {
454                         height_spinner.set_value (val);
455                 }
456                 _suspend_signals = false;
457
458                 if (fps_checkbox.get_active () && node->get_property (X_("FPS"), val)) {
459                         tcfps = val;
460                 }
461
462                 if (node->get_property (X_("Preset"), str)) {
463                         preset_combo.set_active_text (str);
464                 }
465                 if (node->get_property (X_("VCodec"), str)) {
466                         video_codec_combo.set_active_text (str);
467                 }
468                 if (node->get_property (X_("ACodec"), str)) {
469                         audio_codec_combo.set_active_text (str);
470                 }
471                 if (node->get_property (X_("VBitrate"), str)) {
472                         video_bitrate_combo.set_active_text (str);
473                 }
474                 if (node->get_property (X_("ABitrate"), str)) {
475                         audio_bitrate_combo.set_active_text (str);
476                 }
477                 if (node->get_property (X_("AspectRatio"), str)) {
478                         aspect_combo.set_active_text (str);
479                 }
480                 if (node->get_property (X_("SampleRate"), str)) {
481                         audio_samplerate_combo.set_active_text (str);
482                 }
483         }
484
485         if      (fabs(tcfps - 23.976) < 0.01) { fps_combo.set_active(0); }
486         else if (fabs(tcfps - 24.0  ) < 0.01) { fps_combo.set_active(1); }
487         else if (fabs(tcfps - 24.976) < 0.01) { fps_combo.set_active(2); }
488         else if (fabs(tcfps - 25.0  ) < 0.01) { fps_combo.set_active(3); }
489         else if (fabs(tcfps - 29.97 ) < 0.01) { fps_combo.set_active(4); }
490         else if (fabs(tcfps - 30.0  ) < 0.01) { fps_combo.set_active(5); }
491         else if (fabs(tcfps - 59.94 ) < 0.01) { fps_combo.set_active(6); }
492         else if (fabs(tcfps - 60.0  ) < 0.01) { fps_combo.set_active(7); }
493         else { fps_combo.set_active(5); }
494
495         set_original_file_information();
496
497         /* update sensitivity */
498         scale_checkbox_toggled();
499         aspect_checkbox_toggled();
500         fps_checkbox_toggled();
501         video_codec_combo_changed();
502
503         _suspend_dirty = false;
504
505         show_all_children ();
506         if (progress_box) {
507                 progress_box->hide();
508         }
509 }
510
511 XMLNode&
512 ExportVideoDialog::get_state ()
513 {
514         XMLNode* node = new XMLNode (X_("Videoexport"));
515         node->set_property (X_("ChangeGeometry"), scale_checkbox.get_active());
516         node->set_property (X_("KeepAspect"), scale_aspect.get_active());
517         node->set_property (X_("ChangeAspect"), aspect_checkbox.get_active());
518         node->set_property (X_("NormalizeAudio"), normalize_checkbox.get_active());
519         node->set_property (X_("TwoPassEncode"), twopass_checkbox.get_active());
520         node->set_property (X_("CodecOptimzations"), optimizations_checkbox.get_active());
521         node->set_property (X_("Deinterlace"), deinterlace_checkbox.get_active());
522         node->set_property (X_("BSamples"), bframes_checkbox.get_active());
523         node->set_property (X_("ChangeFPS"), fps_checkbox.get_active());
524         node->set_property (X_("Metadata"), meta_checkbox.get_active());
525
526         node->set_property (X_("Format"), get_file_extension(outfn_path_entry.get_text()));
527
528         node->set_property (X_("Width"), width_spinner.get_value());
529         node->set_property (X_("Height"), height_spinner.get_value());
530
531         node->set_property (X_("Preset"), preset_combo.get_active_text());
532         node->set_property (X_("VCodec"), video_codec_combo.get_active_text());
533         node->set_property (X_("ACodec"), audio_codec_combo.get_active_text());
534         node->set_property (X_("VBitrate"), video_bitrate_combo.get_active_text());
535         node->set_property (X_("ABitrate"), audio_bitrate_combo.get_active_text());
536         node->set_property (X_("AspectRatio"), aspect_combo.get_active_text());
537         node->set_property (X_("SampleRate"), audio_samplerate_combo.get_active_text());
538         node->set_property (X_("FPS"), fps_combo.get_active_text());
539
540         return *node;
541 }
542
543 void
544 ExportVideoDialog::set_state (const XMLNode &)
545 {
546 }
547
548 void
549 ExportVideoDialog::on_show ()
550 {
551         Dialog::on_show ();
552 }
553
554 void
555 ExportVideoDialog::abort_clicked ()
556 {
557         _aborted = true;
558         if (_transcoder) {
559                 _transcoder->cancel();
560         }
561 }
562
563 void
564 ExportVideoDialog::update_progress (samplecnt_t c, samplecnt_t a)
565 {
566         if (a == 0 || c > a) {
567                 pbar.set_pulse_step(.1);
568                 pbar.pulse();
569         } else {
570                 double progress = (double)c / (double) a;
571                 progress = progress / ((_twopass ? 2.0 : 1.0) + (_normalize ? 2.0 : 1.0));
572                 if (_normalize && _twopass) progress += (_firstpass ? .5 : .75);
573                 else if (_normalize) progress += 2.0/3.0;
574                 else if (_twopass) progress += (_firstpass ? 1.0/3.0 : 2.0/3.0);
575                 else progress += .5;
576
577                 pbar.set_fraction (progress);
578         }
579 }
580
581
582 gint
583 ExportVideoDialog::audio_progress_display ()
584 {
585         std::string status_text;
586         double progress = -1.0;
587         switch (status->active_job) {
588                 case ExportStatus::Normalizing:
589                         pbar.set_text (_("Normalizing audio"));
590                         progress = ((float) status->current_postprocessing_cycle) / status->total_postprocessing_cycles;
591                         progress = progress / (_twopass ? 4.0 : 3.0) + (_twopass ? .25 : 1.0 / 3.0);
592                         break;
593                 case ExportStatus::Exporting:
594                         pbar.set_text (_("Exporting audio"));
595                         progress = ((float) status->processed_samples_current_timespan) / status->total_samples_current_timespan;
596                         progress = progress / ((_twopass ? 2.0 : 1.0) + (_normalize ? 2.0 : 1.0));
597                         break;
598                 default:
599                         pbar.set_text (_("Exporting audio"));
600                         break;
601         }
602
603         if (progress < _previous_progress) {
604                 // Work around gtk bug
605                 pbar.hide();
606                 pbar.show();
607         }
608         _previous_progress = progress;
609
610         if (progress >= 0) {
611                 pbar.set_fraction (progress);
612         } else {
613                 pbar.set_pulse_step(.1);
614                 pbar.pulse();
615         }
616         return TRUE;
617 }
618
619 void
620 ExportVideoDialog::finished ()
621 {
622         if (_aborted) {
623                 ::g_unlink(outfn_path_entry.get_text().c_str());
624                 ::g_unlink (_insnd.c_str());
625                 delete _transcoder; _transcoder = 0;
626                 Gtk::Dialog::response(RESPONSE_CANCEL);
627         } else if (_twopass && _firstpass) {
628                 _firstpass = false;
629                 if (_transcoder) { delete _transcoder; _transcoder = 0;}
630                 encode_pass(2);
631         } else {
632                 if (twopass_checkbox.get_active()) {
633                         std::string outfn = outfn_path_entry.get_text();
634                         std::string p2log = Glib::path_get_dirname (outfn) + G_DIR_SEPARATOR + "ffmpeg2pass";
635                         ::g_unlink (p2log.c_str());
636                 }
637                 ::g_unlink (_insnd.c_str());
638                 delete _transcoder; _transcoder = 0;
639                 Gtk::Dialog::response(RESPONSE_ACCEPT);
640         }
641 }
642
643 void
644 ExportVideoDialog::launch_export ()
645 {
646         /* remember current settings.
647          * needed because apply_state() acts on both:
648          * "Videotimeline" and "Video Export" extra XML
649          * as well as current _session settings
650          */
651         _session->add_extra_xml (get_state());
652
653         std::string outfn = outfn_path_entry.get_text();
654         if (!confirm_video_outfn(*this, outfn)) { return; }
655
656         vbox->hide();
657         cancel_button->hide();
658         transcode_button.hide();
659         pbar.set_size_request(300,-1);
660         pbar.set_text(_("Exporting Audio..."));
661         progress_box->show();
662         _aborted = false;
663         _twopass = twopass_checkbox.get_active();
664         _firstpass = true;
665         _normalize = normalize_checkbox.get_active();
666
667         /* export audio track */
668         ExportTimespanPtr tsp = _session->get_export_handler()->add_timespan();
669         boost::shared_ptr<ExportChannelConfiguration> ccp = _session->get_export_handler()->add_channel_config();
670         boost::shared_ptr<ARDOUR::ExportFilename> fnp = _session->get_export_handler()->add_filename();
671         boost::shared_ptr<AudioGrapher::BroadcastInfo> b;
672         XMLTree tree;
673         std::string vtl_samplerate = audio_samplerate_combo.get_active_text();
674         std::string vtl_normalize = _normalize ? "true" : "false";
675         tree.read_buffer(std::string(
676 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
677 "<ExportFormatSpecification name=\"VTL-WAV-16\" id=\"3094591e-ccb9-4385-a93f-c9955ffeb1f0\">"
678 "  <Encoding id=\"F_WAV\" type=\"T_Sndfile\" extension=\"wav\" name=\"WAV\" has-sample-format=\"true\" channel-limit=\"256\"/>"
679 "  <SampleRate rate=\""+ vtl_samplerate +"\"/>"
680 "  <SRCQuality quality=\"SRC_SincBest\"/>"
681 "  <EncodingOptions>"
682 "    <Option name=\"sample-format\" value=\"SF_16\"/>"
683 "    <Option name=\"dithering\" value=\"D_None\"/>"
684 "    <Option name=\"tag-metadata\" value=\"true\"/>"
685 "    <Option name=\"tag-support\" value=\"false\"/>"
686 "    <Option name=\"broadcast-info\" value=\"false\"/>"
687 "  </EncodingOptions>"
688 "  <Processing>"
689 "    <Normalize enabled=\""+ vtl_normalize +"\" target=\"0\"/>"
690 "    <Silence>"
691 "      <Start>"
692 "        <Trim enabled=\"false\"/>"
693 "        <Add enabled=\"false\">"
694 "          <Duration format=\"Timecode\" hours=\"0\" minutes=\"0\" seconds=\"0\" frames=\"0\"/>"
695 "        </Add>"
696 "      </Start>"
697 "      <End>"
698 "        <Trim enabled=\"false\"/>"
699 "        <Add enabled=\"false\">"
700 "          <Duration format=\"Timecode\" hours=\"0\" minutes=\"0\" seconds=\"0\" frames=\"0\"/>"
701 "        </Add>"
702 "      </End>"
703 "    </Silence>"
704 "  </Processing>"
705 "</ExportFormatSpecification>"
706 ));
707         boost::shared_ptr<ExportFormatSpecification> fmp = _session->get_export_handler()->add_format(*tree.root());
708
709         /* set up range */
710         samplepos_t start, end;
711         start = end = 0;
712         if (insnd_combo.get_active_row_number() == 1) {
713                 _transcoder = new TranscodeFfmpeg(invid_path_entry.get_text());
714                 if (_transcoder->probe_ok() && _transcoder->get_fps() > 0) {
715                         end = _transcoder->get_duration() * _session->nominal_sample_rate() / _transcoder->get_fps();
716                 } else {
717                         warning << _("Export Video: Cannot query duration of video-file, using duration from timeline instead.") << endmsg;
718                         end = ARDOUR_UI::instance()->video_timeline->get_duration();
719                 }
720                 if (_transcoder) {delete _transcoder; _transcoder = 0;}
721
722                 sampleoffset_t av_offset = ARDOUR_UI::instance()->video_timeline->get_offset();
723 #if 0 /* DEBUG */
724                 printf("audio-range -- AV offset: %lld\n", av_offset);
725 #endif
726                 if (av_offset > 0) {
727                         start = av_offset;
728                 }
729                 end += av_offset;
730         }
731         else if (insnd_combo.get_active_row_number() == 2) {
732                 start = ARDOUR_UI::instance()->video_timeline->quantify_samples_to_apv(export_range.start());
733                 end   = ARDOUR_UI::instance()->video_timeline->quantify_samples_to_apv(export_range.end_sample());
734         }
735         if (end <= 0) {
736                 start = _session->current_start_sample();
737                 end   = _session->current_end_sample();
738         }
739 #if 0 /* DEBUG */
740         printf("audio export-range %lld -> %lld\n", start, end);
741 #endif
742
743         const sampleoffset_t vstart = ARDOUR_UI::instance()->video_timeline->get_offset();
744         const sampleoffset_t vend   = vstart + ARDOUR_UI::instance()->video_timeline->get_duration();
745
746         if ( (start >= end) || (end < vstart) || (start > vend)) {
747                 warning << _("Export Video: export-range does not include video.") << endmsg;
748                 delete _transcoder; _transcoder = 0;
749                 Gtk::Dialog::response(RESPONSE_CANCEL);
750                 return;
751         }
752
753         tsp->set_range (start, end);
754         tsp->set_name ("mysession");
755         tsp->set_range_id ("session");
756
757         /* add master outs as default */
758         IO* master_out = _session->master_out()->output().get();
759         if (!master_out) {
760                 warning << _("Export Video: No Master Out Ports to Connect for Audio Export") << endmsg;
761                 delete _transcoder; _transcoder = 0;
762                 Gtk::Dialog::response(RESPONSE_CANCEL);
763                 return;
764         }
765         for (uint32_t n = 0; n < master_out->n_ports().n_audio(); ++n) {
766                 PortExportChannel * channel = new PortExportChannel ();
767                 channel->add_port (master_out->audio (n));
768                 ExportChannelPtr chan_ptr (channel);
769                 ccp->register_channel (chan_ptr);
770         }
771
772         /* outfile */
773         fnp->set_timespan(tsp);
774         fnp->set_label("vtl");
775         fnp->include_label = true;
776         _insnd = fnp->get_path(fmp);
777
778         /* do sound export */
779         fmp->set_soundcloud_upload(false);
780         _session->get_export_handler()->reset ();
781         _session->get_export_handler()->add_export_config (tsp, ccp, fmp, fnp, b);
782         _session->get_export_handler()->do_export();
783         status = _session->get_export_status ();
784
785         audio_progress_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ExportVideoDialog::audio_progress_display), 100);
786         _previous_progress = 0.0;
787         while (status->running ()) {
788                 if (_aborted) { status->abort(); }
789                 if (gtk_events_pending()) {
790                         gtk_main_iteration ();
791                 } else {
792                         Glib::usleep (10000);
793                 }
794         }
795         audio_progress_connection.disconnect();
796         status->finish (TRS_UI);
797         if (status->aborted()) {
798                 ::g_unlink (_insnd.c_str());
799                 delete _transcoder; _transcoder = 0;
800                 Gtk::Dialog::response(RESPONSE_CANCEL);
801                 return;
802         }
803         pbar.set_text (_("Encoding Video..."));
804         encode_pass(1);
805 }
806
807 void
808 ExportVideoDialog::encode_pass (int pass)
809 {
810         std::string outfn = outfn_path_entry.get_text();
811         std::string invid = invid_path_entry.get_text();
812
813         _transcoder = new TranscodeFfmpeg(invid);
814         if (!_transcoder->ffexec_ok()) {
815                 /* ffmpeg binary was not found. TranscodeFfmpeg prints a warning */
816                 ::g_unlink (_insnd.c_str());
817                 delete _transcoder; _transcoder = 0;
818                 Gtk::Dialog::response(RESPONSE_CANCEL);
819                 return;
820         }
821         if (!_transcoder->probe_ok()) {
822                 /* video input file can not be read */
823                 warning << _("Export Video: Video input file cannot be read.") << endmsg;
824                 ::g_unlink (_insnd.c_str());
825                 delete _transcoder; _transcoder = 0;
826                 Gtk::Dialog::response(RESPONSE_CANCEL);
827                 return;
828         }
829
830         std::string preset = preset_combo.get_active_text();
831         TranscodeFfmpeg::FFSettings ffs ; /* = transcoder->default_encoder_settings(); */
832         ffs.clear();
833
834         if (fps_checkbox.get_active()) {
835                 ffs["-r"] = fps_combo.get_active_text();
836                 _transcoder->set_fps(atof(fps_combo.get_active_text()));
837         }
838
839         if (scale_checkbox.get_active()) {
840                 ffs["-s"] = string_compose("%1x%2", width_spinner.get_value(), height_spinner.get_value());
841         }
842
843         if (video_codec_combo.get_active_text() != _("(default for format)")) {
844                 ffs["-vcodec"] = video_codec_combo.get_active_text();
845         }
846         if (audio_codec_combo.get_active_text() != _("(default for format)")) {
847                 ffs["-acodec"] = audio_codec_combo.get_active_text();
848         }
849
850         if (video_bitrate_combo.get_active_text() == _("(default)") ) {
851                 ;
852         }
853         else if (video_bitrate_combo.get_active_text() == _("(retain)") ) {
854                 ffs["-qscale"]  = "0";
855         } else {
856                 ffs["-b:v"]  = video_bitrate_combo.get_active_text();
857         }
858
859         if (audio_bitrate_combo.get_active_text() != _("(default)") ) {
860                 ffs["-b:a"] = audio_bitrate_combo.get_active_text();
861         }
862
863         if (audio_codec_combo.get_active_text() == "aac" ) {
864                 ffs["-strict"] = "-2";
865         }
866
867         if (video_codec_combo.get_active_text() == "h264" ) {
868                 ffs["-vcodec"] = "libx264";
869         }
870         else if (video_codec_combo.get_active_text() == "vpx (webm)" ) {
871                 ffs["-vcodec"] = "libvpx";
872                 ffs["-g"] = "120";
873                 ffs["-qmin"] = "11";
874                 ffs["-qmax"] = "51";
875         }
876
877         if (optimizations_checkbox.get_active()) {
878           if (video_codec_combo.get_active_text() == "mpeg2video") {
879                         ffs["-mbd"] = "rd";
880                         ffs["-trellis"] = "2";
881                         ffs["-cmp"] = "2";
882                         ffs["-subcmp"] = "2";
883                 }
884                 else if (video_codec_combo.get_active_text() == "mpeg4") {
885                         ffs["-mbd"] = "rd";
886                         ffs["-flags"] = "+mv4+aic";
887                         ffs["-trellis"] = "2";
888                         ffs["-cmp"] = "2";
889                         ffs["-subcmp"] = "2";
890                         ffs["-g"] = "300";
891                 }
892                 else if (video_codec_combo.get_active_text() == "flv") {
893                         ffs["-mbd"] = "2";
894                         ffs["-cmp"] = "2";
895                         ffs["-subcmp"] = "2";
896                         ffs["-trellis"] = "2";
897                         ffs["-flags"] = "+aic+mv0+mv4";
898                         ffs["-g"] = "160";
899                 }
900         }
901
902         if (bframes_checkbox.get_active() && (
903                    video_codec_combo.get_active_text() == "mpeg2video"
904                 || video_codec_combo.get_active_text() == "mpeg4"
905                 )) {
906                 ffs["-bf"] = "2";
907         }
908
909         if (preset == "dvd-PAL") {
910                 ffs.clear(); /* ignore all prev settings */
911                 ffs["-target"] = "pal-dvd";
912                 ffs["-aspect"] = "4:3"; /* required for DVD - may be overridden below */
913         }
914         else if (preset == "dvd-NTSC") {
915                 ffs.clear(); /* ignore all prev settings */
916                 ffs["-target"] = "ntsc-dvd";
917                 ffs["-aspect"] = "4:3"; /* required for DVD - may be overridden below */
918         }
919
920         if (aspect_checkbox.get_active()) {
921                 ffs["-aspect"] = aspect_combo.get_active_text();
922         }
923         if (deinterlace_checkbox.get_active()) {
924                 ffs["-deinterlace"] = "-y"; // we use '-y' as dummy parameter for non key/value options
925         }
926
927         bool map = true;
928         if (pass == 1 && _twopass) {
929                 pbar.set_text (_("Encoding Video.. Pass 1/2"));
930                 map = false;
931                 ffs["-pass"] = "1";
932                 ffs["-an"] = "-y";
933                 ffs["-passlogfile"] =  Glib::path_get_dirname (outfn) + G_DIR_SEPARATOR + "ffmpeg2pass";
934                 ffs["-f"] = get_file_extension(invid).empty()?"mov":get_file_extension(invid);
935 #ifdef PLATFORM_WINDOWS
936                 outfn = "NUL";
937 #else
938                 outfn = "/dev/null";
939 #endif
940         } else if (pass == 2) {
941                 pbar.set_text (_("Encoding Video.. Pass 2/2"));
942                 ffs["-pass"] = "2";
943                 ffs["-passlogfile"] =  Glib::path_get_dirname (outfn) + G_DIR_SEPARATOR + "ffmpeg2pass";
944         }
945
946         sampleoffset_t av_offset = ARDOUR_UI::instance()->video_timeline->get_offset();
947         double duration_s  = 0;
948
949         if (insnd_combo.get_active_row_number() == 0) {
950                 /* session start to session end */
951                 samplecnt_t duration_f = _session->current_end_sample() - _session->current_start_sample();
952                 duration_s = (double)duration_f / (double)_session->nominal_sample_rate();
953         } else if (insnd_combo.get_active_row_number() == 2) {
954                 /* selected range */
955                 duration_s = export_range.length() / (double)_session->nominal_sample_rate();
956         } else {
957                 /* video start to end */
958                 samplecnt_t duration_f = ARDOUR_UI::instance()->video_timeline->get_duration();
959                 if (av_offset < 0 ) {
960                         duration_f += av_offset;
961                 }
962                 duration_s = (double)duration_f / (double)_session->nominal_sample_rate();
963         }
964
965         std::ostringstream osstream; osstream << duration_s;
966         ffs["-t"] = osstream.str();
967         _transcoder->set_duration(duration_s * _transcoder->get_fps());
968
969         if (insnd_combo.get_active_row_number() == 0 || insnd_combo.get_active_row_number() == 2) {
970                 samplepos_t start, snend;
971                 const sampleoffset_t vid_duration = ARDOUR_UI::instance()->video_timeline->get_duration();
972                 if (insnd_combo.get_active_row_number() == 0) {
973                         start = _session->current_start_sample();
974                         snend = _session->current_end_sample();
975                 } else {
976                         start = export_range.start();
977                         snend = export_range.end_sample();
978                 }
979
980 #if 0 /* DEBUG */
981                 printf("AV offset: %lld Vid-len: %lld Vid-end: %lld || start:%lld || end:%lld\n",
982                                 av_offset, vid_duration, av_offset+vid_duration, start, snend); // XXX
983 #endif
984
985                 if (av_offset > start && av_offset + vid_duration < snend) {
986                         _transcoder->set_leadinout((av_offset - start) / (double)_session->nominal_sample_rate(),
987                                 (snend - (av_offset + vid_duration)) / (double)_session->nominal_sample_rate());
988                 } else if (av_offset > start) {
989                         _transcoder->set_leadinout((av_offset - start) / (double)_session->nominal_sample_rate(), 0);
990                 } else if (av_offset + vid_duration < snend) {
991                         _transcoder->set_leadinout(0, (snend - (av_offset + vid_duration)) / (double)_session->nominal_sample_rate());
992                         _transcoder->set_avoffset((av_offset - start) / (double)_session->nominal_sample_rate());
993                 }
994 #if 0
995                 else if (start > av_offset) {
996                         std::ostringstream osstream; osstream << ((start - av_offset) / (double)_session->nominal_sample_rate());
997                         ffs["-ss"] = osstream.str();
998                 }
999 #endif
1000                 else {
1001                         _transcoder->set_avoffset((av_offset - start) / (double)_session->nominal_sample_rate());
1002                 }
1003
1004         } else if (av_offset < 0) {
1005                 /* from 00:00:00:00 to video-end */
1006                 _transcoder->set_avoffset(av_offset / (double)_session->nominal_sample_rate());
1007         }
1008
1009         /* NOTE: type (MetaDataMap) == type (FFSettings) == map<string, string> */
1010         ARDOUR::SessionMetadata::MetaDataMap meta = _transcoder->default_meta_data();
1011         if (meta_checkbox.get_active()) {
1012                 ARDOUR::SessionMetadata * session_data = ARDOUR::SessionMetadata::Metadata();
1013                 session_data->av_export_tag (meta);
1014         }
1015
1016 #if 1 /* tentative debug mode */
1017         if (debug_checkbox.get_active()) {
1018                 _transcoder->set_debug(true);
1019         }
1020 #endif
1021
1022         _transcoder->Progress.connect(*this, invalidator (*this), boost::bind (&ExportVideoDialog::update_progress , this, _1, _2), gui_context());
1023         _transcoder->Finished.connect(*this, invalidator (*this), boost::bind (&ExportVideoDialog::finished, this), gui_context());
1024         if (!_transcoder->encode(outfn, _insnd, invid, ffs, meta, map)) {
1025                 ARDOUR_UI::instance()->popup_error(_("Transcoding failed."));
1026                 delete _transcoder; _transcoder = 0;
1027                 Gtk::Dialog::response(RESPONSE_CANCEL);
1028                 return;
1029         }
1030 }
1031
1032 void
1033 ExportVideoDialog::change_file_extension (std::string ext)
1034 {
1035         if (ext == "") return;
1036         outfn_path_entry.set_text (
1037                 strip_file_extension(outfn_path_entry.get_text()) + ext
1038         );
1039 }
1040
1041 void
1042 ExportVideoDialog::width_value_changed ()
1043 {
1044         if (_suspend_signals) {
1045                 return;
1046         }
1047         if (_session && !_suspend_dirty) _session->set_dirty ();
1048         if (!scale_checkbox.get_active() || !scale_aspect.get_active()) {
1049                 return;
1050         }
1051         if (_video_source_aspect_ratio <= 0) {
1052                 return;
1053         }
1054         _suspend_signals = true;
1055         height_spinner.set_value(rintf(width_spinner.get_value() / _video_source_aspect_ratio));
1056         _suspend_signals = false;
1057 }
1058
1059 void
1060 ExportVideoDialog::height_value_changed ()
1061 {
1062         if (_suspend_signals) {
1063                 return;
1064         }
1065         if (_session && !_suspend_dirty) _session->set_dirty ();
1066         if (!scale_checkbox.get_active() || !scale_aspect.get_active()) {
1067                 return;
1068         }
1069         if (_video_source_aspect_ratio <= 0) {
1070                 return;
1071         }
1072         _suspend_signals = true;
1073         width_spinner.set_value(rintf(height_spinner.get_value() * _video_source_aspect_ratio));
1074         _suspend_signals = false;
1075 }
1076
1077 void
1078 ExportVideoDialog::scale_checkbox_toggled ()
1079 {
1080         scale_aspect.set_sensitive(scale_checkbox.get_active());
1081         width_spinner.set_sensitive(scale_checkbox.get_active());
1082         height_spinner.set_sensitive(scale_checkbox.get_active());
1083         if (_session && !_suspend_dirty) _session->set_dirty ();
1084 }
1085
1086 void
1087 ExportVideoDialog::fps_checkbox_toggled ()
1088 {
1089         fps_combo.set_sensitive(fps_checkbox.get_active());
1090         if (_session && !_suspend_dirty) _session->set_dirty ();
1091 }
1092
1093 void
1094 ExportVideoDialog::aspect_checkbox_toggled ()
1095 {
1096         aspect_combo.set_sensitive(aspect_checkbox.get_active());
1097         if (_session && !_suspend_dirty) _session->set_dirty ();
1098 }
1099
1100 void
1101 ExportVideoDialog::video_codec_combo_changed ()
1102 {
1103         if ((  video_codec_combo.get_active_text() == "mpeg4"
1104              ||video_codec_combo.get_active_text() == "mpeg2video"
1105                         ) && !(
1106                preset_combo.get_active_text() == "dvd-PAL"
1107              ||preset_combo.get_active_text() == "dvd-NTSC"
1108            )) {
1109                 bframes_checkbox.set_sensitive(true);
1110                 optimizations_checkbox.set_sensitive(true);
1111                 if (video_codec_combo.get_active_text() == "mpeg2video") {
1112                         optimizations_label.set_text("-mbd rd -trellis 2 -cmp 2 -subcmp 2"); // mpeg2
1113                 } else if (video_codec_combo.get_active_text() == "mpeg4") {
1114                         optimizations_label.set_text("-mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -g 300"); // mpeg4
1115                 } else {
1116                         optimizations_label.set_text("-mbd 2 -cmp 2 -subcmp 2 -trellis 2 -flags +aic+mv0+mv4 -g 160"); // flv
1117                 }
1118         } else {
1119                 bframes_checkbox.set_sensitive(false);
1120                 bframes_checkbox.set_active(false);
1121                 optimizations_checkbox.set_sensitive(false);
1122                 optimizations_checkbox.set_active(false);
1123                 optimizations_label.set_text("-");
1124         }
1125         if (_session && !_suspend_dirty) _session->set_dirty ();
1126 }
1127
1128 void
1129 ExportVideoDialog::preset_combo_changed ()
1130 {
1131         std::string p = preset_combo.get_active_text();
1132         scale_checkbox.set_sensitive(true);
1133
1134         if (p == "flv") {
1135                 change_file_extension(".flv");
1136                 audio_codec_combo.set_active(2);
1137                 video_codec_combo.set_active(1);
1138                 audio_bitrate_combo.set_active(2);
1139                 video_bitrate_combo.set_active(3);
1140                 audio_samplerate_combo.set_active(1);
1141         }
1142         else if (p == "you-tube") {
1143                 change_file_extension(".avi");
1144                 audio_codec_combo.set_active(3);
1145                 video_codec_combo.set_active(6);
1146                 audio_bitrate_combo.set_active(2);
1147                 video_bitrate_combo.set_active(4);
1148                 if (_session->nominal_sample_rate() == 48000 || _session->nominal_sample_rate() == 96000) {
1149                         audio_samplerate_combo.set_active(2);
1150                 } else {
1151                         audio_samplerate_combo.set_active(1);
1152                 }
1153         }
1154         else if (p == "ogg") {
1155                 change_file_extension(".ogv");
1156                 audio_codec_combo.set_active(4);
1157                 video_codec_combo.set_active(2);
1158                 audio_bitrate_combo.set_active(3);
1159                 video_bitrate_combo.set_active(4);
1160                 if (_session->nominal_sample_rate() == 48000 || _session->nominal_sample_rate() == 96000) {
1161                         audio_samplerate_combo.set_active(2);
1162                 } else {
1163                         audio_samplerate_combo.set_active(1);
1164                 }
1165         }
1166         else if (p == "webm") {
1167                 change_file_extension(".webm");
1168                 audio_codec_combo.set_active(4);
1169                 video_codec_combo.set_active(7);
1170                 audio_bitrate_combo.set_active(3);
1171                 video_bitrate_combo.set_active(4);
1172                 if (_session->nominal_sample_rate() == 48000 || _session->nominal_sample_rate() == 96000) {
1173                         audio_samplerate_combo.set_active(2);
1174                 } else {
1175                         audio_samplerate_combo.set_active(1);
1176                 }
1177         }
1178         else if (p == "dvd-mp2") {
1179                 change_file_extension(".mpg");
1180                 audio_codec_combo.set_active(5);
1181                 video_codec_combo.set_active(4);
1182                 audio_bitrate_combo.set_active(4);
1183                 video_bitrate_combo.set_active(5);
1184                 audio_samplerate_combo.set_active(2);
1185         }
1186         else if (p == "dvd-NTSC" || p == "dvd-PAL") {
1187                 change_file_extension(".mpg");
1188                 audio_codec_combo.set_active(6);
1189                 video_codec_combo.set_active(4);
1190                 audio_bitrate_combo.set_active(4);
1191                 video_bitrate_combo.set_active(5);
1192                 audio_samplerate_combo.set_active(2);
1193
1194                 scale_checkbox.set_active(false);
1195                 scale_checkbox.set_sensitive(false);
1196         }
1197         else if (p == "mpeg4") {
1198                 change_file_extension(".mp4");
1199                 audio_codec_combo.set_active(1);
1200                 video_codec_combo.set_active(5);
1201                 audio_bitrate_combo.set_active(4);
1202                 video_bitrate_combo.set_active(5);
1203                 if (_session->nominal_sample_rate() == 48000 || _session->nominal_sample_rate() == 96000) {
1204                         audio_samplerate_combo.set_active(2);
1205                 } else {
1206                         audio_samplerate_combo.set_active(1);
1207                 }
1208         }
1209         else if (p == "mp4/h264/aac") {
1210                 change_file_extension(".mp4");
1211                 audio_codec_combo.set_active(2);
1212                 video_codec_combo.set_active(6);
1213                 audio_bitrate_combo.set_active(0);
1214                 video_bitrate_combo.set_active(0);
1215                 if (_session->nominal_sample_rate() == 48000 || _session->nominal_sample_rate() == 96000) {
1216                         audio_samplerate_combo.set_active(2);
1217                 } else {
1218                         audio_samplerate_combo.set_active(1);
1219                 }
1220         }
1221
1222         if (p == "none") {
1223                 audio_codec_combo.set_sensitive(true);
1224                 video_codec_combo.set_sensitive(true);
1225                 audio_bitrate_combo.set_sensitive(true);
1226                 video_bitrate_combo.set_sensitive(true);
1227                 audio_samplerate_combo.set_sensitive(true);
1228         } else {
1229                 audio_codec_combo.set_sensitive(false);
1230                 video_codec_combo.set_sensitive(false);
1231                 audio_bitrate_combo.set_sensitive(false);
1232                 video_bitrate_combo.set_sensitive(false);
1233                 audio_samplerate_combo.set_sensitive(false);
1234         }
1235
1236         Gtk::Table *t = (Gtk::Table*) preset_combo.get_parent();
1237         Gtk::Table_Helpers::TableList c = t->children();
1238         Gtk::Table_Helpers::TableList::iterator it;
1239         if (p == "dvd-PAL" || p == "dvd-NTSC") {
1240                 for (it = c.begin(); it != c.end(); ++it) {
1241                         int row = it->get_top_attach();
1242                         if (row == 2 || row == 3 || row== 5 || row== 6 || row == 9) {
1243                                 it->get_widget()->hide();
1244                         }
1245                 }
1246         } else {
1247                 for (it = c.begin(); it != c.end(); ++it) {
1248                         int row = it->get_top_attach();
1249                         if (row == 2 || row == 3 || row== 5 || row== 6 || row == 9) {
1250                                 it->get_widget()->show();
1251                         }
1252                 }
1253         }
1254
1255         video_codec_combo_changed();
1256 }
1257
1258 void
1259 ExportVideoDialog::open_outfn_dialog ()
1260 {
1261         Gtk::FileChooserDialog dialog(_("Save Exported Video File"), Gtk::FILE_CHOOSER_ACTION_SAVE);
1262         Gtkmm2ext::add_volume_shortcuts (dialog);
1263         dialog.set_filename (outfn_path_entry.get_text());
1264
1265         dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1266         dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
1267
1268         int result = dialog.run();
1269
1270         if (result == Gtk::RESPONSE_OK) {
1271                 std::string filename = dialog.get_filename();
1272
1273                 if (filename.length()) {
1274                         outfn_path_entry.set_text (filename);
1275                 }
1276         }
1277 }
1278
1279 void
1280 ExportVideoDialog::open_invid_dialog ()
1281 {
1282         Gtk::FileChooserDialog dialog(_("Save Exported Video File"), Gtk::FILE_CHOOSER_ACTION_SAVE);
1283         Gtkmm2ext::add_volume_shortcuts (dialog);
1284         dialog.set_filename (invid_path_entry.get_text());
1285
1286         dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1287         dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
1288
1289         int result = dialog.run();
1290
1291         if (result == Gtk::RESPONSE_OK) {
1292                 std::string filename = dialog.get_filename();
1293
1294                 if (filename.length()) {
1295                         invid_path_entry.set_text (filename);
1296                 }
1297         }
1298 }