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 
ExportVideoDialog()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 
296 	progress_box->set_no_show_all();
297 	progress_box->hide();
298 }
299 
~ExportVideoDialog()300 ExportVideoDialog::~ExportVideoDialog ()
301 {
302 	if (_transcoder) { delete _transcoder; _transcoder = 0;}
303 }
304 
305 void
set_original_file_information()306 ExportVideoDialog::set_original_file_information()
307 {
308 	assert(_transcoder == 0);
309 	std::string infile = invid_path_entry.get_text();
310 
311 	if (scale_checkbox.get_active()) {
312 		// user may have set custom values already, don't touch.
313 		return;
314 	}
315 	if (infile == "" || !Glib::file_test(infile, Glib::FILE_TEST_EXISTS)) {
316 		return;
317 	}
318 
319 	_transcoder = new TranscodeFfmpeg(infile);
320 	if (_transcoder->probe_ok()) {
321 		_video_source_aspect_ratio = -1;
322 		width_spinner.set_value(_transcoder->get_width());
323 		height_spinner.set_value(_transcoder->get_height());
324 		_video_source_aspect_ratio = _transcoder->get_aspect();
325 	}
326 
327 	delete _transcoder; _transcoder = 0;
328 }
329 void
apply_state(TimeSelection & tme,bool range)330 ExportVideoDialog::apply_state (TimeSelection &tme, bool range)
331 {
332 	_suspend_dirty = true; // TODO really just queue 'dirty' and mark session dirty on "Export"
333 
334 	export_range = tme;
335 	_video_source_aspect_ratio = -1;
336 
337 	outfn_path_entry.set_text (_session->session_directory().export_path() + G_DIR_SEPARATOR +"export.avi");
338 
339 	// TODO remember setting for export-range.. somehow, (let explicit range override)
340 	sampleoffset_t av_offset = ARDOUR_UI::instance()->video_timeline->get_offset();
341 
342 	insnd_combo.remove_all ();
343 
344 	insnd_combo.append_text (_("from session start marker to session end marker"));
345 
346 	if (av_offset < 0 ) {
347 		insnd_combo.append_text (_("from 00:00:00:00 to the video end"));
348 	} else {
349 		insnd_combo.append_text (_("from video start to video end"));
350 	}
351 	if (!export_range.empty()) {
352 		insnd_combo.append_text (_("Selected range"));  // TODO show export_range.start() -> export_range.end_sample()
353 	}
354 	if (range) {
355 		insnd_combo.set_active(2);
356 	} else {
357 		insnd_combo.set_active(0);
358 	}
359 
360 	preset_combo.set_active(0);
361 	audio_codec_combo.set_active(0);
362 	video_codec_combo.set_active(0);
363 	audio_bitrate_combo.set_active(0);
364 	audio_samplerate_combo.set_active(2);
365 	video_bitrate_combo.set_active(0);
366 	aspect_combo.set_active(1);
367 
368 	scale_checkbox.set_active(false);
369 	scale_aspect.set_active(true);
370 	aspect_checkbox.set_active(false);
371 	normalize_checkbox.set_active(false);
372 	twopass_checkbox.set_active(false);
373 	optimizations_checkbox.set_active(false);
374 	deinterlace_checkbox.set_active(false);
375 	bframes_checkbox.set_active(false);
376 	fps_checkbox.set_active(false);
377 	meta_checkbox.set_active(false);
378 
379 	float tcfps = _session->timecode_frames_per_second();
380 
381 	XMLNode* node = _session->extra_xml (X_("Videotimeline"));
382 	bool filenameset = false;
383 	if (node) {
384 		std::string filename;
385 		if (node->get_property(X_("OriginalVideoFile"), filename)) {
386 			if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) {
387 				invid_path_entry.set_text (filename);
388 				filenameset = true;
389 			}
390 		}
391 
392 		bool local_file;
393 
394 		if (!filenameset && node->get_property (X_("Filename"), filename) &&
395 		    node->get_property (X_("LocalFile"), local_file) && local_file) {
396 			if (filename.at(0) != G_DIR_SEPARATOR)
397 			{
398 				filename = Glib::build_filename (_session->session_directory().video_path(), filename);
399 			}
400 			if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS))
401 			{
402 				invid_path_entry.set_text (filename);
403 				filenameset = true;
404 			}
405 		}
406 	}
407 	if (!filenameset) {
408 		invid_path_entry.set_text (X_(""));
409 	}
410 
411 	node = _session->extra_xml (X_("Videoexport"));
412 	if (node) {
413 		bool yn;
414 		if (node->get_property (X_("ChangeGeometry"), yn)) {
415 			scale_checkbox.set_active (yn);
416 		}
417 		if (node->get_property (X_("KeepAspect"), yn)) {
418 			scale_aspect.set_active (yn);
419 		}
420 		if (node->get_property (X_("ChangeAspect"), yn)) {
421 			aspect_checkbox.set_active (yn);
422 		}
423 		if (node->get_property (X_("NormalizeAudio"), yn)) {
424 			normalize_checkbox.set_active (yn);
425 		}
426 		if (node->get_property (X_("TwoPassEncode"), yn)) {
427 			twopass_checkbox.set_active (yn);
428 		}
429 		if (node->get_property (X_("CodecOptimzations"), yn)) {
430 			optimizations_checkbox.set_active (yn);
431 		}
432 		if (node->get_property (X_("Deinterlace"), yn)) {
433 			deinterlace_checkbox.set_active (yn);
434 		}
435 		if (node->get_property (X_("BSamples"), yn)) {
436 			bframes_checkbox.set_active (yn);
437 		}
438 		if (node->get_property (X_("ChangeFPS"), yn)) {
439 			fps_checkbox.set_active (yn);
440 		}
441 		if (node->get_property (X_("Metadata"), yn)) {
442 			meta_checkbox.set_active (yn);
443 		}
444 
445 		std::string str;
446 		if (node->get_property (X_("Format"), str) && !str.empty()) {
447 			change_file_extension ("." + str);
448 		}
449 
450 		_suspend_signals = true;
451 		double val;
452 		if (node->get_property (X_("Width"), val)) {
453 			width_spinner.set_value (val);
454 		}
455 		if (node->get_property (X_("Height"), val)) {
456 			height_spinner.set_value (val);
457 		}
458 		_suspend_signals = false;
459 
460 		if (fps_checkbox.get_active () && node->get_property (X_("FPS"), val)) {
461 			tcfps = val;
462 		}
463 
464 		if (node->get_property (X_("Preset"), str)) {
465 			preset_combo.set_active_text (str);
466 		}
467 		if (node->get_property (X_("VCodec"), str)) {
468 			video_codec_combo.set_active_text (str);
469 		}
470 		if (node->get_property (X_("ACodec"), str)) {
471 			audio_codec_combo.set_active_text (str);
472 		}
473 		if (node->get_property (X_("VBitrate"), str)) {
474 			video_bitrate_combo.set_active_text (str);
475 		}
476 		if (node->get_property (X_("ABitrate"), str)) {
477 			audio_bitrate_combo.set_active_text (str);
478 		}
479 		if (node->get_property (X_("AspectRatio"), str)) {
480 			aspect_combo.set_active_text (str);
481 		}
482 		if (node->get_property (X_("SampleRate"), str)) {
483 			audio_samplerate_combo.set_active_text (str);
484 		}
485 	}
486 
487 	if      (fabs(tcfps - 23.976) < 0.01) { fps_combo.set_active(0); }
488 	else if (fabs(tcfps - 24.0  ) < 0.01) { fps_combo.set_active(1); }
489 	else if (fabs(tcfps - 24.976) < 0.01) { fps_combo.set_active(2); }
490 	else if (fabs(tcfps - 25.0  ) < 0.01) { fps_combo.set_active(3); }
491 	else if (fabs(tcfps - 29.97 ) < 0.01) { fps_combo.set_active(4); }
492 	else if (fabs(tcfps - 30.0  ) < 0.01) { fps_combo.set_active(5); }
493 	else if (fabs(tcfps - 59.94 ) < 0.01) { fps_combo.set_active(6); }
494 	else if (fabs(tcfps - 60.0  ) < 0.01) { fps_combo.set_active(7); }
495 	else { fps_combo.set_active(5); }
496 
497 	set_original_file_information();
498 
499 	/* update sensitivity */
500 	scale_checkbox_toggled();
501 	aspect_checkbox_toggled();
502 	fps_checkbox_toggled();
503 	video_codec_combo_changed();
504 
505 	_suspend_dirty = false;
506 
507 	show_all_children ();
508 	if (progress_box) {
509 		progress_box->hide();
510 	}
511 }
512 
513 XMLNode&
get_state()514 ExportVideoDialog::get_state ()
515 {
516 	XMLNode* node = new XMLNode (X_("Videoexport"));
517 	node->set_property (X_("ChangeGeometry"), scale_checkbox.get_active());
518 	node->set_property (X_("KeepAspect"), scale_aspect.get_active());
519 	node->set_property (X_("ChangeAspect"), aspect_checkbox.get_active());
520 	node->set_property (X_("NormalizeAudio"), normalize_checkbox.get_active());
521 	node->set_property (X_("TwoPassEncode"), twopass_checkbox.get_active());
522 	node->set_property (X_("CodecOptimzations"), optimizations_checkbox.get_active());
523 	node->set_property (X_("Deinterlace"), deinterlace_checkbox.get_active());
524 	node->set_property (X_("BSamples"), bframes_checkbox.get_active());
525 	node->set_property (X_("ChangeFPS"), fps_checkbox.get_active());
526 	node->set_property (X_("Metadata"), meta_checkbox.get_active());
527 
528 	node->set_property (X_("Format"), get_file_extension(outfn_path_entry.get_text()));
529 
530 	node->set_property (X_("Width"), width_spinner.get_value());
531 	node->set_property (X_("Height"), height_spinner.get_value());
532 
533 	node->set_property (X_("Preset"), preset_combo.get_active_text());
534 	node->set_property (X_("VCodec"), video_codec_combo.get_active_text());
535 	node->set_property (X_("ACodec"), audio_codec_combo.get_active_text());
536 	node->set_property (X_("VBitrate"), video_bitrate_combo.get_active_text());
537 	node->set_property (X_("ABitrate"), audio_bitrate_combo.get_active_text());
538 	node->set_property (X_("AspectRatio"), aspect_combo.get_active_text());
539 	node->set_property (X_("SampleRate"), audio_samplerate_combo.get_active_text());
540 	node->set_property (X_("FPS"), fps_combo.get_active_text());
541 
542 	return *node;
543 }
544 
545 void
set_state(const XMLNode &)546 ExportVideoDialog::set_state (const XMLNode &)
547 {
548 }
549 
550 void
on_show()551 ExportVideoDialog::on_show ()
552 {
553 	Dialog::on_show ();
554 }
555 
556 void
abort_clicked()557 ExportVideoDialog::abort_clicked ()
558 {
559 	_aborted = true;
560 	if (_transcoder) {
561 		_transcoder->cancel();
562 	}
563 }
564 
565 void
update_progress(samplecnt_t c,samplecnt_t a)566 ExportVideoDialog::update_progress (samplecnt_t c, samplecnt_t a)
567 {
568 	if (a == 0 || c > a) {
569 		pbar.set_pulse_step(.1);
570 		pbar.pulse();
571 	} else {
572 		double progress = (double)c / (double) a;
573 		progress = progress / ((_twopass ? 2.0 : 1.0) + (_normalize ? 2.0 : 1.0));
574 		if (_normalize && _twopass) progress += (_firstpass ? .5 : .75);
575 		else if (_normalize) progress += 2.0/3.0;
576 		else if (_twopass) progress += (_firstpass ? 1.0/3.0 : 2.0/3.0);
577 		else progress += .5;
578 
579 		pbar.set_fraction (progress);
580 	}
581 }
582 
583 
584 gint
audio_progress_display()585 ExportVideoDialog::audio_progress_display ()
586 {
587 	std::string status_text;
588 	double progress = -1.0;
589 	switch (status->active_job) {
590 		case ExportStatus::Normalizing:
591 			pbar.set_text (_("Normalizing audio"));
592 			progress = ((float) status->current_postprocessing_cycle) / status->total_postprocessing_cycles;
593 			progress = progress / (_twopass ? 4.0 : 3.0) + (_twopass ? .25 : 1.0 / 3.0);
594 			break;
595 		case ExportStatus::Exporting:
596 			pbar.set_text (_("Exporting audio"));
597 			progress = ((float) status->processed_samples_current_timespan) / status->total_samples_current_timespan;
598 			progress = progress / ((_twopass ? 2.0 : 1.0) + (_normalize ? 2.0 : 1.0));
599 			break;
600 		default:
601 			pbar.set_text (_("Exporting audio"));
602 			break;
603 	}
604 
605 	if (progress < _previous_progress) {
606 		// Work around gtk bug
607 		pbar.hide();
608 		pbar.show();
609 	}
610 	_previous_progress = progress;
611 
612 	if (progress >= 0) {
613 		pbar.set_fraction (progress);
614 	} else {
615 		pbar.set_pulse_step(.1);
616 		pbar.pulse();
617 	}
618 	return TRUE;
619 }
620 
621 void
finished()622 ExportVideoDialog::finished ()
623 {
624 	if (_aborted) {
625 		::g_unlink(outfn_path_entry.get_text().c_str());
626 		::g_unlink (_insnd.c_str());
627 		delete _transcoder; _transcoder = 0;
628 		Gtk::Dialog::response(RESPONSE_CANCEL);
629 	} else if (_twopass && _firstpass) {
630 		_firstpass = false;
631 		if (_transcoder) { delete _transcoder; _transcoder = 0;}
632 		encode_pass(2);
633 	} else {
634 		if (twopass_checkbox.get_active()) {
635 			std::string outfn = outfn_path_entry.get_text();
636 			std::string p2log = Glib::path_get_dirname (outfn) + G_DIR_SEPARATOR + "ffmpeg2pass";
637 			::g_unlink (p2log.c_str());
638 		}
639 		::g_unlink (_insnd.c_str());
640 		delete _transcoder; _transcoder = 0;
641 		Gtk::Dialog::response(RESPONSE_ACCEPT);
642 	}
643 }
644 
645 void
launch_export()646 ExportVideoDialog::launch_export ()
647 {
648 	/* remember current settings.
649 	 * needed because apply_state() acts on both:
650 	 * "Videotimeline" and "Video Export" extra XML
651 	 * as well as current _session settings
652 	 */
653 	_session->add_extra_xml (get_state());
654 
655 	std::string outfn = outfn_path_entry.get_text();
656 	if (!confirm_video_outfn(*this, outfn)) { return; }
657 
658 	vbox->hide();
659 	cancel_button->hide();
660 	transcode_button.hide();
661 	pbar.set_size_request(300,-1);
662 	pbar.set_text(_("Exporting Audio..."));
663 	progress_box->show();
664 	_aborted = false;
665 	_twopass = twopass_checkbox.get_active();
666 	_firstpass = true;
667 	_normalize = normalize_checkbox.get_active();
668 
669 	/* export audio track */
670 	ExportTimespanPtr tsp = _session->get_export_handler()->add_timespan();
671 	boost::shared_ptr<ExportChannelConfiguration> ccp = _session->get_export_handler()->add_channel_config();
672 	boost::shared_ptr<ARDOUR::ExportFilename> fnp = _session->get_export_handler()->add_filename();
673 	boost::shared_ptr<AudioGrapher::BroadcastInfo> b;
674 	XMLTree tree;
675 	std::string vtl_samplerate = audio_samplerate_combo.get_active_text();
676 	std::string vtl_normalize = _normalize ? "true" : "false";
677 	tree.read_buffer(std::string (
678 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
679 "<ExportFormatSpecification name=\"VTL-WAV-16\" id=\"3094591e-ccb9-4385-a93f-c9955ffeb1f0\">"
680 "  <Encoding id=\"F_WAV\" type=\"T_Sndfile\" extension=\"wav\" name=\"WAV\" has-sample-format=\"true\" channel-limit=\"256\"/>"
681 "  <SampleRate rate=\""+ vtl_samplerate +"\"/>"
682 "  <SRCQuality quality=\"SRC_SincBest\"/>"
683 "  <EncodingOptions>"
684 "    <Option name=\"sample-format\" value=\"SF_16\"/>"
685 "    <Option name=\"dithering\" value=\"D_None\"/>"
686 "    <Option name=\"tag-metadata\" value=\"true\"/>"
687 "    <Option name=\"tag-support\" value=\"false\"/>"
688 "    <Option name=\"broadcast-info\" value=\"false\"/>"
689 "  </EncodingOptions>"
690 "  <Processing>"
691 "    <Normalize enabled=\""+ vtl_normalize +"\" target=\"0\"/>"
692 "    <Silence>"
693 "      <Start>"
694 "        <Trim enabled=\"false\"/>"
695 "        <Add enabled=\"false\">"
696 "          <Duration format=\"Timecode\" hours=\"0\" minutes=\"0\" seconds=\"0\" frames=\"0\"/>"
697 "        </Add>"
698 "      </Start>"
699 "      <End>"
700 "        <Trim enabled=\"false\"/>"
701 "        <Add enabled=\"false\">"
702 "          <Duration format=\"Timecode\" hours=\"0\" minutes=\"0\" seconds=\"0\" frames=\"0\"/>"
703 "        </Add>"
704 "      </End>"
705 "    </Silence>"
706 "  </Processing>"
707 "</ExportFormatSpecification>"
708 	).c_str());
709 	boost::shared_ptr<ExportFormatSpecification> fmp = _session->get_export_handler()->add_format(*tree.root());
710 
711 	/* set up range */
712 	samplepos_t start, end;
713 	start = end = 0;
714 	if (insnd_combo.get_active_row_number() == 1) {
715 		_transcoder = new TranscodeFfmpeg(invid_path_entry.get_text());
716 		if (_transcoder->probe_ok() && _transcoder->get_fps() > 0) {
717 			end = _transcoder->get_duration() * _session->nominal_sample_rate() / _transcoder->get_fps();
718 		} else {
719 			warning << _("Export Video: Cannot query duration of video-file, using duration from timeline instead.") << endmsg;
720 			end = ARDOUR_UI::instance()->video_timeline->get_duration();
721 		}
722 		if (_transcoder) {delete _transcoder; _transcoder = 0;}
723 
724 		sampleoffset_t av_offset = ARDOUR_UI::instance()->video_timeline->get_offset();
725 #if 0 /* DEBUG */
726 		printf("audio-range -- AV offset: %lld\n", av_offset);
727 #endif
728 		if (av_offset > 0) {
729 			start = av_offset;
730 		}
731 		end += av_offset;
732 	}
733 	else if (insnd_combo.get_active_row_number() == 2) {
734 		start = ARDOUR_UI::instance()->video_timeline->quantify_samples_to_apv(export_range.start());
735 		end   = ARDOUR_UI::instance()->video_timeline->quantify_samples_to_apv(export_range.end_sample());
736 	}
737 	if (end <= 0) {
738 		start = _session->current_start_sample();
739 		end   = _session->current_end_sample();
740 	}
741 #if 0 /* DEBUG */
742 	printf("audio export-range %lld -> %lld\n", start, end);
743 #endif
744 
745 	const sampleoffset_t vstart = ARDOUR_UI::instance()->video_timeline->get_offset();
746 	const sampleoffset_t vend   = vstart + ARDOUR_UI::instance()->video_timeline->get_duration();
747 
748 	if ( (start >= end) || (end < vstart) || (start > vend)) {
749 		warning << _("Export Video: export-range does not include video.") << endmsg;
750 		delete _transcoder; _transcoder = 0;
751 		Gtk::Dialog::response(RESPONSE_CANCEL);
752 		return;
753 	}
754 
755 	tsp->set_range (start, end);
756 	tsp->set_name ("mysession");
757 	tsp->set_range_id ("session");
758 
759 	/* add master outs as default */
760 	IO* master_out = _session->master_out()->output().get();
761 	if (!master_out) {
762 		warning << _("Export Video: No Master Out Ports to Connect for Audio Export") << endmsg;
763 		delete _transcoder; _transcoder = 0;
764 		Gtk::Dialog::response(RESPONSE_CANCEL);
765 		return;
766 	}
767 	for (uint32_t n = 0; n < master_out->n_ports().n_audio(); ++n) {
768 		PortExportChannel * channel = new PortExportChannel ();
769 		channel->add_port (master_out->audio (n));
770 		ExportChannelPtr chan_ptr (channel);
771 		ccp->register_channel (chan_ptr);
772 	}
773 
774 	/* outfile */
775 	fnp->set_timespan(tsp);
776 	fnp->set_label("vtl");
777 	fnp->include_label = true;
778 	_insnd = fnp->get_path(fmp);
779 
780 	/* do sound export */
781 	fmp->set_soundcloud_upload(false);
782 	_session->get_export_handler()->reset ();
783 	_session->get_export_handler()->add_export_config (tsp, ccp, fmp, fnp, b);
784 	_session->get_export_handler()->do_export();
785 	status = _session->get_export_status ();
786 
787 	audio_progress_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ExportVideoDialog::audio_progress_display), 100);
788 	_previous_progress = 0.0;
789 	while (status->running ()) {
790 		if (_aborted) { status->abort(); }
791 		if (gtk_events_pending()) {
792 			gtk_main_iteration ();
793 		} else {
794 			Glib::usleep (10000);
795 		}
796 	}
797 	audio_progress_connection.disconnect();
798 	status->finish (TRS_UI);
799 	if (status->aborted()) {
800 		::g_unlink (_insnd.c_str());
801 		delete _transcoder; _transcoder = 0;
802 		Gtk::Dialog::response(RESPONSE_CANCEL);
803 		return;
804 	}
805 	pbar.set_text (_("Encoding Video..."));
806 	encode_pass(1);
807 }
808 
809 void
encode_pass(int pass)810 ExportVideoDialog::encode_pass (int pass)
811 {
812 	std::string outfn = outfn_path_entry.get_text();
813 	std::string invid = invid_path_entry.get_text();
814 
815 	_transcoder = new TranscodeFfmpeg(invid);
816 	if (!_transcoder->ffexec_ok()) {
817 		/* ffmpeg binary was not found. TranscodeFfmpeg prints a warning */
818 		::g_unlink (_insnd.c_str());
819 		delete _transcoder; _transcoder = 0;
820 		Gtk::Dialog::response(RESPONSE_CANCEL);
821 		return;
822 	}
823 	if (!_transcoder->probe_ok()) {
824 		/* video input file can not be read */
825 		warning << _("Export Video: Video input file cannot be read.") << endmsg;
826 		::g_unlink (_insnd.c_str());
827 		delete _transcoder; _transcoder = 0;
828 		Gtk::Dialog::response(RESPONSE_CANCEL);
829 		return;
830 	}
831 
832 	std::string preset = preset_combo.get_active_text();
833 	TranscodeFfmpeg::FFSettings ffs ; /* = transcoder->default_encoder_settings(); */
834 	ffs.clear();
835 
836 	if (fps_checkbox.get_active()) {
837 		ffs["-r"] = fps_combo.get_active_text();
838 		_transcoder->set_fps(atof(fps_combo.get_active_text()));
839 	}
840 
841 	if (scale_checkbox.get_active()) {
842 		ffs["-s"] = string_compose("%1x%2", width_spinner.get_value(), height_spinner.get_value());
843 	}
844 
845 	if (video_codec_combo.get_active_text() != _("(default for format)")) {
846 		ffs["-vcodec"] = video_codec_combo.get_active_text();
847 	}
848 	if (audio_codec_combo.get_active_text() != _("(default for format)")) {
849 		ffs["-acodec"] = audio_codec_combo.get_active_text();
850 	}
851 
852 	if (video_bitrate_combo.get_active_text() == _("(default)") ) {
853 		;
854 	}
855 	else if (video_bitrate_combo.get_active_text() == _("(retain)") ) {
856 		ffs["-qscale"]  = "0";
857 	} else {
858 		ffs["-b:v"]  = video_bitrate_combo.get_active_text();
859 	}
860 
861 	if (audio_bitrate_combo.get_active_text() != _("(default)") ) {
862 		ffs["-b:a"] = audio_bitrate_combo.get_active_text();
863 	}
864 
865 	if (audio_codec_combo.get_active_text() == "aac" ) {
866 		ffs["-strict"] = "-2";
867 	}
868 
869 	if (video_codec_combo.get_active_text() == "h264" ) {
870 		ffs["-vcodec"] = "libx264";
871 	}
872 	else if (video_codec_combo.get_active_text() == "vpx (webm)" ) {
873 		ffs["-vcodec"] = "libvpx";
874 		ffs["-g"] = "120";
875 		ffs["-qmin"] = "11";
876 		ffs["-qmax"] = "51";
877 	}
878 
879 	if (optimizations_checkbox.get_active()) {
880 	  if (video_codec_combo.get_active_text() == "mpeg2video") {
881 			ffs["-mbd"] = "rd";
882 			ffs["-trellis"] = "2";
883 			ffs["-cmp"] = "2";
884 			ffs["-subcmp"] = "2";
885 		}
886 		else if (video_codec_combo.get_active_text() == "mpeg4") {
887 			ffs["-mbd"] = "rd";
888 			ffs["-flags"] = "+mv4+aic";
889 			ffs["-trellis"] = "2";
890 			ffs["-cmp"] = "2";
891 			ffs["-subcmp"] = "2";
892 			ffs["-g"] = "300";
893 		}
894 		else if (video_codec_combo.get_active_text() == "flv") {
895 			ffs["-mbd"] = "2";
896 			ffs["-cmp"] = "2";
897 			ffs["-subcmp"] = "2";
898 			ffs["-trellis"] = "2";
899 			ffs["-flags"] = "+aic+mv0+mv4";
900 			ffs["-g"] = "160";
901 		}
902 	}
903 
904 	if (bframes_checkbox.get_active() && (
905 		   video_codec_combo.get_active_text() == "mpeg2video"
906 		|| video_codec_combo.get_active_text() == "mpeg4"
907 		)) {
908 		ffs["-bf"] = "2";
909 	}
910 
911 	if (preset == "dvd-PAL") {
912 		ffs.clear(); /* ignore all prev settings */
913 		ffs["-target"] = "pal-dvd";
914 		ffs["-aspect"] = "4:3"; /* required for DVD - may be overridden below */
915 	}
916 	else if (preset == "dvd-NTSC") {
917 		ffs.clear(); /* ignore all prev settings */
918 		ffs["-target"] = "ntsc-dvd";
919 		ffs["-aspect"] = "4:3"; /* required for DVD - may be overridden below */
920 	}
921 
922 	if (aspect_checkbox.get_active()) {
923 		ffs["-aspect"] = aspect_combo.get_active_text();
924 	}
925 	if (deinterlace_checkbox.get_active()) {
926 		ffs["-deinterlace"] = "-y"; // we use '-y' as dummy parameter for non key/value options
927 	}
928 
929 	bool map = true;
930 	if (pass == 1 && _twopass) {
931 		pbar.set_text (_("Encoding Video.. Pass 1/2"));
932 		map = false;
933 		ffs["-pass"] = "1";
934 		ffs["-an"] = "-y";
935 		ffs["-passlogfile"] =  Glib::path_get_dirname (outfn) + G_DIR_SEPARATOR + "ffmpeg2pass";
936 		ffs["-f"] = get_file_extension(invid).empty()?"mov":get_file_extension(invid);
937 #ifdef PLATFORM_WINDOWS
938 		outfn = "NUL";
939 #else
940 		outfn = "/dev/null";
941 #endif
942 	} else if (pass == 2) {
943 		pbar.set_text (_("Encoding Video.. Pass 2/2"));
944 		ffs["-pass"] = "2";
945 		ffs["-passlogfile"] =  Glib::path_get_dirname (outfn) + G_DIR_SEPARATOR + "ffmpeg2pass";
946 	}
947 
948 	sampleoffset_t av_offset = ARDOUR_UI::instance()->video_timeline->get_offset();
949 	double duration_s  = 0;
950 
951 	if (insnd_combo.get_active_row_number() == 0) {
952 		/* session start to session end */
953 		samplecnt_t duration_f = _session->current_end_sample() - _session->current_start_sample();
954 		duration_s = (double)duration_f / (double)_session->nominal_sample_rate();
955 	} else if (insnd_combo.get_active_row_number() == 2) {
956 		/* selected range */
957 		duration_s = export_range.length() / (double)_session->nominal_sample_rate();
958 	} else {
959 		/* video start to end */
960 		samplecnt_t duration_f = ARDOUR_UI::instance()->video_timeline->get_duration();
961 		if (av_offset < 0 ) {
962 			duration_f += av_offset;
963 		}
964 		duration_s = (double)duration_f / (double)_session->nominal_sample_rate();
965 	}
966 
967 	std::ostringstream osstream; osstream << duration_s;
968 	ffs["-t"] = osstream.str();
969 	_transcoder->set_duration(duration_s * _transcoder->get_fps());
970 
971 	if (insnd_combo.get_active_row_number() == 0 || insnd_combo.get_active_row_number() == 2) {
972 		samplepos_t start, snend;
973 		const sampleoffset_t vid_duration = ARDOUR_UI::instance()->video_timeline->get_duration();
974 		if (insnd_combo.get_active_row_number() == 0) {
975 			start = _session->current_start_sample();
976 			snend = _session->current_end_sample();
977 		} else {
978 			start = export_range.start();
979 			snend = export_range.end_sample();
980 		}
981 
982 #if 0 /* DEBUG */
983 		printf("AV offset: %lld Vid-len: %lld Vid-end: %lld || start:%lld || end:%lld\n",
984 				av_offset, vid_duration, av_offset+vid_duration, start, snend); // XXX
985 #endif
986 
987 		if (av_offset > start && av_offset + vid_duration < snend) {
988 			_transcoder->set_leadinout((av_offset - start) / (double)_session->nominal_sample_rate(),
989 				(snend - (av_offset + vid_duration)) / (double)_session->nominal_sample_rate());
990 		} else if (av_offset > start) {
991 			_transcoder->set_leadinout((av_offset - start) / (double)_session->nominal_sample_rate(), 0);
992 		} else if (av_offset + vid_duration < snend) {
993 			_transcoder->set_leadinout(0, (snend - (av_offset + vid_duration)) / (double)_session->nominal_sample_rate());
994 			_transcoder->set_avoffset((av_offset - start) / (double)_session->nominal_sample_rate());
995 		}
996 #if 0
997 		else if (start > av_offset) {
998 			std::ostringstream osstream; osstream << ((start - av_offset) / (double)_session->nominal_sample_rate());
999 			ffs["-ss"] = osstream.str();
1000 		}
1001 #endif
1002 		else {
1003 			_transcoder->set_avoffset((av_offset - start) / (double)_session->nominal_sample_rate());
1004 		}
1005 
1006 	} else if (av_offset < 0) {
1007 		/* from 00:00:00:00 to video-end */
1008 		_transcoder->set_avoffset(av_offset / (double)_session->nominal_sample_rate());
1009 	}
1010 
1011 	/* NOTE: type (MetaDataMap) == type (FFSettings) == map<string, string> */
1012 	ARDOUR::SessionMetadata::MetaDataMap meta = _transcoder->default_meta_data();
1013 	if (meta_checkbox.get_active()) {
1014 		ARDOUR::SessionMetadata * session_data = ARDOUR::SessionMetadata::Metadata();
1015 		session_data->av_export_tag (meta);
1016 	}
1017 
1018 #if 1 /* tentative debug mode */
1019 	if (debug_checkbox.get_active()) {
1020 		_transcoder->set_debug(true);
1021 	}
1022 #endif
1023 
1024 	_transcoder->Progress.connect(*this, invalidator (*this), boost::bind (&ExportVideoDialog::update_progress , this, _1, _2), gui_context());
1025 	_transcoder->Finished.connect(*this, invalidator (*this), boost::bind (&ExportVideoDialog::finished, this), gui_context());
1026 	if (!_transcoder->encode(outfn, _insnd, invid, ffs, meta, map)) {
1027 		ARDOUR_UI::instance()->popup_error(_("Transcoding failed."));
1028 		delete _transcoder; _transcoder = 0;
1029 		Gtk::Dialog::response(RESPONSE_CANCEL);
1030 		return;
1031 	}
1032 }
1033 
1034 void
change_file_extension(std::string ext)1035 ExportVideoDialog::change_file_extension (std::string ext)
1036 {
1037 	if (ext == "") return;
1038 	outfn_path_entry.set_text (
1039 		strip_file_extension(outfn_path_entry.get_text()) + ext
1040 	);
1041 }
1042 
1043 void
width_value_changed()1044 ExportVideoDialog::width_value_changed ()
1045 {
1046 	if (_suspend_signals) {
1047 		return;
1048 	}
1049 	if (_session && !_suspend_dirty) _session->set_dirty ();
1050 	if (!scale_checkbox.get_active() || !scale_aspect.get_active()) {
1051 		return;
1052 	}
1053 	if (_video_source_aspect_ratio <= 0) {
1054 		return;
1055 	}
1056 	_suspend_signals = true;
1057 	height_spinner.set_value(rintf(width_spinner.get_value() / _video_source_aspect_ratio));
1058 	_suspend_signals = false;
1059 }
1060 
1061 void
height_value_changed()1062 ExportVideoDialog::height_value_changed ()
1063 {
1064 	if (_suspend_signals) {
1065 		return;
1066 	}
1067 	if (_session && !_suspend_dirty) _session->set_dirty ();
1068 	if (!scale_checkbox.get_active() || !scale_aspect.get_active()) {
1069 		return;
1070 	}
1071 	if (_video_source_aspect_ratio <= 0) {
1072 		return;
1073 	}
1074 	_suspend_signals = true;
1075 	width_spinner.set_value(rintf(height_spinner.get_value() * _video_source_aspect_ratio));
1076 	_suspend_signals = false;
1077 }
1078 
1079 void
scale_checkbox_toggled()1080 ExportVideoDialog::scale_checkbox_toggled ()
1081 {
1082 	scale_aspect.set_sensitive(scale_checkbox.get_active());
1083 	width_spinner.set_sensitive(scale_checkbox.get_active());
1084 	height_spinner.set_sensitive(scale_checkbox.get_active());
1085 	if (_session && !_suspend_dirty) _session->set_dirty ();
1086 }
1087 
1088 void
fps_checkbox_toggled()1089 ExportVideoDialog::fps_checkbox_toggled ()
1090 {
1091 	fps_combo.set_sensitive(fps_checkbox.get_active());
1092 	if (_session && !_suspend_dirty) _session->set_dirty ();
1093 }
1094 
1095 void
aspect_checkbox_toggled()1096 ExportVideoDialog::aspect_checkbox_toggled ()
1097 {
1098 	aspect_combo.set_sensitive(aspect_checkbox.get_active());
1099 	if (_session && !_suspend_dirty) _session->set_dirty ();
1100 }
1101 
1102 void
video_codec_combo_changed()1103 ExportVideoDialog::video_codec_combo_changed ()
1104 {
1105 	if ((  video_codec_combo.get_active_text() == "mpeg4"
1106 	     ||video_codec_combo.get_active_text() == "mpeg2video"
1107 			) && !(
1108 	       preset_combo.get_active_text() == "dvd-PAL"
1109 	     ||preset_combo.get_active_text() == "dvd-NTSC"
1110 	   )) {
1111 		bframes_checkbox.set_sensitive(true);
1112 		optimizations_checkbox.set_sensitive(true);
1113 		if (video_codec_combo.get_active_text() == "mpeg2video") {
1114 			optimizations_label.set_text("-mbd rd -trellis 2 -cmp 2 -subcmp 2"); // mpeg2
1115 		} else if (video_codec_combo.get_active_text() == "mpeg4") {
1116 			optimizations_label.set_text("-mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -g 300"); // mpeg4
1117 		} else {
1118 			optimizations_label.set_text("-mbd 2 -cmp 2 -subcmp 2 -trellis 2 -flags +aic+mv0+mv4 -g 160"); // flv
1119 		}
1120 	} else {
1121 		bframes_checkbox.set_sensitive(false);
1122 		bframes_checkbox.set_active(false);
1123 		optimizations_checkbox.set_sensitive(false);
1124 		optimizations_checkbox.set_active(false);
1125 		optimizations_label.set_text("-");
1126 	}
1127 	if (_session && !_suspend_dirty) _session->set_dirty ();
1128 }
1129 
1130 void
preset_combo_changed()1131 ExportVideoDialog::preset_combo_changed ()
1132 {
1133 	std::string p = preset_combo.get_active_text();
1134 	scale_checkbox.set_sensitive(true);
1135 
1136 	if (p == "flv") {
1137 		change_file_extension(".flv");
1138 		audio_codec_combo.set_active(2);
1139 		video_codec_combo.set_active(1);
1140 		audio_bitrate_combo.set_active(2);
1141 		video_bitrate_combo.set_active(3);
1142 		audio_samplerate_combo.set_active(1);
1143 	}
1144 	else if (p == "you-tube") {
1145 		change_file_extension(".avi");
1146 		audio_codec_combo.set_active(3);
1147 		video_codec_combo.set_active(6);
1148 		audio_bitrate_combo.set_active(2);
1149 		video_bitrate_combo.set_active(4);
1150 		if (_session->nominal_sample_rate() == 48000 || _session->nominal_sample_rate() == 96000) {
1151 			audio_samplerate_combo.set_active(2);
1152 		} else {
1153 			audio_samplerate_combo.set_active(1);
1154 		}
1155 	}
1156 	else if (p == "ogg") {
1157 		change_file_extension(".ogv");
1158 		audio_codec_combo.set_active(4);
1159 		video_codec_combo.set_active(2);
1160 		audio_bitrate_combo.set_active(3);
1161 		video_bitrate_combo.set_active(4);
1162 		if (_session->nominal_sample_rate() == 48000 || _session->nominal_sample_rate() == 96000) {
1163 			audio_samplerate_combo.set_active(2);
1164 		} else {
1165 			audio_samplerate_combo.set_active(1);
1166 		}
1167 	}
1168 	else if (p == "webm") {
1169 		change_file_extension(".webm");
1170 		audio_codec_combo.set_active(4);
1171 		video_codec_combo.set_active(7);
1172 		audio_bitrate_combo.set_active(3);
1173 		video_bitrate_combo.set_active(4);
1174 		if (_session->nominal_sample_rate() == 48000 || _session->nominal_sample_rate() == 96000) {
1175 			audio_samplerate_combo.set_active(2);
1176 		} else {
1177 			audio_samplerate_combo.set_active(1);
1178 		}
1179 	}
1180 	else if (p == "dvd-mp2") {
1181 		change_file_extension(".mpg");
1182 		audio_codec_combo.set_active(5);
1183 		video_codec_combo.set_active(4);
1184 		audio_bitrate_combo.set_active(4);
1185 		video_bitrate_combo.set_active(5);
1186 		audio_samplerate_combo.set_active(2);
1187 	}
1188 	else if (p == "dvd-NTSC" || p == "dvd-PAL") {
1189 		change_file_extension(".mpg");
1190 		audio_codec_combo.set_active(6);
1191 		video_codec_combo.set_active(4);
1192 		audio_bitrate_combo.set_active(4);
1193 		video_bitrate_combo.set_active(5);
1194 		audio_samplerate_combo.set_active(2);
1195 
1196 		scale_checkbox.set_active(false);
1197 		scale_checkbox.set_sensitive(false);
1198 	}
1199 	else if (p == "mpeg4") {
1200 		change_file_extension(".mp4");
1201 		audio_codec_combo.set_active(1);
1202 		video_codec_combo.set_active(5);
1203 		audio_bitrate_combo.set_active(4);
1204 		video_bitrate_combo.set_active(5);
1205 		if (_session->nominal_sample_rate() == 48000 || _session->nominal_sample_rate() == 96000) {
1206 			audio_samplerate_combo.set_active(2);
1207 		} else {
1208 			audio_samplerate_combo.set_active(1);
1209 		}
1210 	}
1211 	else if (p == "mp4/h264/aac") {
1212 		change_file_extension(".mp4");
1213 		audio_codec_combo.set_active(2);
1214 		video_codec_combo.set_active(6);
1215 		audio_bitrate_combo.set_active(0);
1216 		video_bitrate_combo.set_active(0);
1217 		if (_session->nominal_sample_rate() == 48000 || _session->nominal_sample_rate() == 96000) {
1218 			audio_samplerate_combo.set_active(2);
1219 		} else {
1220 			audio_samplerate_combo.set_active(1);
1221 		}
1222 	}
1223 
1224 	if (p == "none") {
1225 		audio_codec_combo.set_sensitive(true);
1226 		video_codec_combo.set_sensitive(true);
1227 		audio_bitrate_combo.set_sensitive(true);
1228 		video_bitrate_combo.set_sensitive(true);
1229 		audio_samplerate_combo.set_sensitive(true);
1230 	} else {
1231 		audio_codec_combo.set_sensitive(false);
1232 		video_codec_combo.set_sensitive(false);
1233 		audio_bitrate_combo.set_sensitive(false);
1234 		video_bitrate_combo.set_sensitive(false);
1235 		audio_samplerate_combo.set_sensitive(false);
1236 	}
1237 
1238 	Gtk::Table *t = (Gtk::Table*) preset_combo.get_parent();
1239 	Gtk::Table_Helpers::TableList c = t->children();
1240 	Gtk::Table_Helpers::TableList::iterator it;
1241 	if (p == "dvd-PAL" || p == "dvd-NTSC") {
1242 		for (it = c.begin(); it != c.end(); ++it) {
1243 			int row = it->get_top_attach();
1244 			if (row == 2 || row == 3 || row== 5 || row== 6 || row == 9) {
1245 				it->get_widget()->hide();
1246 			}
1247 		}
1248 	} else {
1249 		for (it = c.begin(); it != c.end(); ++it) {
1250 			int row = it->get_top_attach();
1251 			if (row == 2 || row == 3 || row== 5 || row== 6 || row == 9) {
1252 				it->get_widget()->show();
1253 			}
1254 		}
1255 	}
1256 
1257 	video_codec_combo_changed();
1258 }
1259 
1260 void
open_outfn_dialog()1261 ExportVideoDialog::open_outfn_dialog ()
1262 {
1263 	Gtk::FileChooserDialog dialog(_("Save Exported Video File"), Gtk::FILE_CHOOSER_ACTION_SAVE);
1264 	Gtkmm2ext::add_volume_shortcuts (dialog);
1265 	dialog.set_filename (outfn_path_entry.get_text());
1266 
1267 	dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1268 	dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
1269 
1270 	int result = dialog.run();
1271 
1272 	if (result == Gtk::RESPONSE_OK) {
1273 		std::string filename = dialog.get_filename();
1274 
1275 		if (filename.length()) {
1276 			outfn_path_entry.set_text (filename);
1277 		}
1278 	}
1279 }
1280 
1281 void
open_invid_dialog()1282 ExportVideoDialog::open_invid_dialog ()
1283 {
1284 	Gtk::FileChooserDialog dialog(_("Save Exported Video File"), Gtk::FILE_CHOOSER_ACTION_SAVE);
1285 	Gtkmm2ext::add_volume_shortcuts (dialog);
1286 	dialog.set_filename (invid_path_entry.get_text());
1287 
1288 	dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1289 	dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
1290 
1291 	int result = dialog.run();
1292 
1293 	if (result == Gtk::RESPONSE_OK) {
1294 		std::string filename = dialog.get_filename();
1295 
1296 		if (filename.length()) {
1297 			invid_path_entry.set_text (filename);
1298 		}
1299 	}
1300 }
1301