X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fexport_video_dialog.cc;h=c1589909b2c0f38ec7591138f90f5be8bed7011e;hb=cf52d6e4b40111eb04b244ec054055a4ec15dbe0;hp=df05eed8073f1d192a0ad91c279060f550b91a3c;hpb=8381c10b599f54d2be92fc00d5b5f009b079cb1a;p=ardour.git diff --git a/gtk2_ardour/export_video_dialog.cc b/gtk2_ardour/export_video_dialog.cc index df05eed807..c1589909b2 100644 --- a/gtk2_ardour/export_video_dialog.cc +++ b/gtk2_ardour/export_video_dialog.cc @@ -29,10 +29,11 @@ #include -#include +#include "pbd/gstdio_compat.h" #include "pbd/error.h" #include "pbd/convert.h" +#include "gtkmm2ext/keyboard.h" #include "gtkmm2ext/utils.h" #include "ardour/session_directory.h" #include "ardour/profile.h" @@ -54,7 +55,7 @@ #include "opts.h" #include "export_video_dialog.h" #include "utils_videotl.h" -#include "i18n.h" +#include "pbd/i18n.h" using namespace Gtk; using namespace std; @@ -62,16 +63,25 @@ using namespace PBD; using namespace ARDOUR; using namespace VideoUtils; -ExportVideoDialog::ExportVideoDialog (Session* s, TimeSelection &tme, bool range) +ExportVideoDialog::ExportVideoDialog () : ArdourDialog (_("Export Video File ")) - , export_range (tme) + , _aborted(false) + , _twopass(false) + , _firstpass(false) + , _normalize(false) + , _previous_progress(0) + , _transcoder(0) + , _video_source_aspect_ratio(-1) + , _suspend_signals(false) , outfn_path_label (_("File:"), Gtk::ALIGN_LEFT) , outfn_browse_button (_("Browse")) , invid_path_label (_("Video:"), Gtk::ALIGN_LEFT) , invid_browse_button (_("Browse")) , transcode_button (_("Export")) , abort_button (_("Abort")) + , progress_box (0) , scale_checkbox (_("Scale Video (W x H):")) + , scale_aspect (_("Retain Aspect")) , width_adjustment (768, 128, 1920, 1, 16, 0) , width_spinner (width_adjustment) , height_adjustment (576, 128, 1920, 1, 16, 0) @@ -89,8 +99,6 @@ ExportVideoDialog::ExportVideoDialog (Session* s, TimeSelection &tme, bool range , debug_checkbox (_("Debug Mode: Print ffmpeg command and output to stdout.")) #endif { - set_session (s); - set_name ("ExportVideoDialog"); set_modal (true); set_skip_taskbar_hint (true); @@ -102,19 +110,20 @@ ExportVideoDialog::ExportVideoDialog (Session* s, TimeSelection &tme, bool range HBox* path_hbox; /* check if ffmpeg can be found */ - transcoder = new TranscodeFfmpeg(""); - if (!transcoder->ffexec_ok()) { - l = manage (new Label (_("No ffprobe or ffmpeg executables could be found on this system. Video Export is not possible until you install those tools. See the Log window for more information."), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false)); + _transcoder = new TranscodeFfmpeg(X_("")); + if (!_transcoder->ffexec_ok()) { + 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)); l->set_line_wrap(); vbox->pack_start (*l, false, false, 8); get_vbox()->pack_start (*vbox, false, false); add_button (Stock::OK, RESPONSE_CANCEL); show_all_children (); + delete _transcoder; _transcoder = 0; return; } - delete transcoder; transcoder = 0; + delete _transcoder; _transcoder = 0; - l = manage (new Label (_("Output:"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false)); + l = manage (new Label (_("Output: (file extension defines format)"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false)); l->set_use_markup (); vbox->pack_start (*l, false, false, 4); @@ -124,7 +133,7 @@ ExportVideoDialog::ExportVideoDialog (Session* s, TimeSelection &tme, bool range path_hbox->pack_start (outfn_browse_button, false, false, 3); vbox->pack_start (*path_hbox, false, false, 2); - l = manage (new Label (_("Input:"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false)); + l = manage (new Label (_("Input Video:"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false)); l->set_use_markup (); vbox->pack_start (*l, false, false, 4); @@ -141,55 +150,8 @@ ExportVideoDialog::ExportVideoDialog (Session* s, TimeSelection &tme, bool range path_hbox->pack_start (*l, false, false, 2); vbox->pack_start (*path_hbox, false, false, 2); - insnd_combo.set_name ("PaddedButton"); insnd_combo.append_text (string_compose (_("from the %1 session's start to the session's end"), PROGRAM_NAME)); - - frameoffset_t av_offset = ARDOUR_UI::instance()->video_timeline->get_offset(); - if (av_offset < 0 ) { - insnd_combo.append_text (_("from 00:00:00:00 to the video's end")); - } else { - insnd_combo.append_text (_("from the video's start to the video's end")); - } - if (!export_range.empty()) { - insnd_combo.append_text (_("Selected range")); // TODO show export_range.start() -> export_range.end_frame() - } - if (range) { - insnd_combo.set_active(2); - } else { - insnd_combo.set_active(0); - } - outfn_path_entry.set_width_chars(38); - outfn_path_entry.set_text (_session->session_directory().export_path() + G_DIR_SEPARATOR +"export.avi"); - - XMLNode* node = _session->extra_xml (X_("Videotimeline")); - if (node) { - bool filenameset = false; - if (node->property(X_("OriginalVideoFile"))) { - std::string filename = node->property(X_("OriginalVideoFile"))->value(); - if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) { - invid_path_entry.set_text (filename); - filenameset = true; - } - } - if (!filenameset - && node->property(X_("Filename")) - && node->property(X_("LocalFile")) - && node->property(X_("LocalFile"))->value() == X_("1") - ) { - std::string filename = node->property(X_("Filename"))->value(); - if (filename.at(0) != G_DIR_SEPARATOR) { - filename = Glib::build_filename (_session->session_directory().video_path(), filename); - } - if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) { - invid_path_entry.set_text (filename); - filenameset = true; - } - } - if (!filenameset) { - invid_path_entry.set_text (X_("")); - } - } l = manage (new Label (_("Settings:"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false)); l->set_use_markup (); @@ -221,7 +183,8 @@ ExportVideoDialog::ExportVideoDialog (Session* s, TimeSelection &tme, bool range t->attach (*l, 0, 1, ty, ty+1); t->attach (audio_samplerate_combo, 1, 2, ty, ty+1); t->attach (normalize_checkbox, 2, 4, ty, ty+1); ty++; - t->attach (scale_checkbox, 0, 2, ty, ty+1); + t->attach (scale_checkbox, 0, 1, ty, ty+1); + t->attach (scale_aspect, 1, 2, ty, ty+1); t->attach (width_spinner, 2, 3, ty, ty+1); t->attach (height_spinner, 3, 4, ty, ty+1); ty++; t->attach (fps_checkbox, 0, 3, ty, ty+1); @@ -238,27 +201,26 @@ ExportVideoDialog::ExportVideoDialog (Session* s, TimeSelection &tme, bool range t->attach (debug_checkbox, 0, 4, ty, ty+1); ty++; #endif - preset_combo.set_name ("PaddedButton"); preset_combo.append_text("none"); preset_combo.append_text("dvd-mp2"); preset_combo.append_text("dvd-NTSC"); preset_combo.append_text("dvd-PAL"); preset_combo.append_text("flv"); preset_combo.append_text("mpeg4"); + preset_combo.append_text("mp4/h264/aac"); preset_combo.append_text("ogg"); + preset_combo.append_text("webm"); preset_combo.append_text("you-tube"); - preset_combo.set_active(0); - audio_codec_combo.set_name ("PaddedButton"); + audio_codec_combo.append_text(_("(default for format)")); audio_codec_combo.append_text("ac3"); audio_codec_combo.append_text("aac"); audio_codec_combo.append_text("libmp3lame"); audio_codec_combo.append_text("libvorbis"); audio_codec_combo.append_text("mp2"); audio_codec_combo.append_text("pcm_s16le"); - audio_codec_combo.set_active(2); - video_codec_combo.set_name ("PaddedButton"); + video_codec_combo.append_text(_("(default for format)")); video_codec_combo.append_text("flv"); video_codec_combo.append_text("libtheora"); video_codec_combo.append_text("mjpeg"); @@ -267,32 +229,26 @@ ExportVideoDialog::ExportVideoDialog (Session* s, TimeSelection &tme, bool range video_codec_combo.append_text("h264"); video_codec_combo.append_text("vpx (webm)"); video_codec_combo.append_text("copy"); - video_codec_combo.set_active(4); - audio_bitrate_combo.set_name ("PaddedButton"); + audio_bitrate_combo.append_text(_("(default)")); audio_bitrate_combo.append_text("64k"); audio_bitrate_combo.append_text("128k"); audio_bitrate_combo.append_text("192k"); audio_bitrate_combo.append_text("256k"); audio_bitrate_combo.append_text("320k"); - audio_bitrate_combo.set_active(2); - audio_samplerate_combo.set_name ("PaddedButton"); audio_samplerate_combo.append_text("22050"); audio_samplerate_combo.append_text("44100"); audio_samplerate_combo.append_text("48000"); - audio_samplerate_combo.set_active(2); - video_bitrate_combo.set_name ("PaddedButton"); + video_bitrate_combo.append_text(_("(default)")); + video_bitrate_combo.append_text(_("(retain)")); video_bitrate_combo.append_text("200k"); video_bitrate_combo.append_text("800k"); video_bitrate_combo.append_text("2000k"); video_bitrate_combo.append_text("5000k"); video_bitrate_combo.append_text("8000k"); - video_bitrate_combo.append_text("retain"); - video_bitrate_combo.set_active(3); - fps_combo.set_name ("PaddedButton"); fps_combo.append_text("23.976"); fps_combo.append_text("24"); fps_combo.append_text("24.976"); @@ -301,27 +257,9 @@ ExportVideoDialog::ExportVideoDialog (Session* s, TimeSelection &tme, bool range fps_combo.append_text("30"); fps_combo.append_text("59.94"); fps_combo.append_text("60"); - float tcfps = _session->timecode_frames_per_second(); - if (fabs(tcfps - 23.976) < 0.01) { fps_combo.set_active(0); } - else if (fabs(tcfps - 24.0 ) < 0.01) { fps_combo.set_active(1); } - else if (fabs(tcfps - 24.976) < 0.01) { fps_combo.set_active(2); } - else if (fabs(tcfps - 25.0 ) < 0.01) { fps_combo.set_active(3); } - else if (fabs(tcfps - 29.97 ) < 0.01) { fps_combo.set_active(4); } - else if (fabs(tcfps - 30.0 ) < 0.01) { fps_combo.set_active(5); } - else if (fabs(tcfps - 59.94 ) < 0.01) { fps_combo.set_active(6); } - else if (fabs(tcfps - 60.0 ) < 0.01) { fps_combo.set_active(7); } - else { fps_combo.set_active(5); } - aspect_combo.set_name ("PaddedButton"); aspect_combo.append_text("4:3"); aspect_combo.append_text("16:9"); - aspect_combo.set_active(1); - - optimizations_checkbox.set_sensitive(false); - scale_checkbox_toggled(); - aspect_checkbox_toggled(); - fps_checkbox_toggled(); - video_codec_combo_changed(); vbox->pack_start (*options_box, false, true, 4); get_vbox()->set_spacing (4); @@ -342,6 +280,10 @@ ExportVideoDialog::ExportVideoDialog (Session* s, TimeSelection &tme, bool range transcode_button.signal_clicked().connect (sigc::mem_fun (*this, &ExportVideoDialog::launch_export)); abort_button.signal_clicked().connect (sigc::mem_fun (*this, &ExportVideoDialog::abort_clicked)); + invid_path_entry.signal_changed().connect (sigc::mem_fun (*this, &ExportVideoDialog::set_original_file_information)); + width_spinner.signal_value_changed().connect (sigc::mem_fun (*this, &ExportVideoDialog::width_value_changed)); + height_spinner.signal_value_changed().connect (sigc::mem_fun (*this, &ExportVideoDialog::height_value_changed)); + cancel_button = add_button (Stock::CANCEL, RESPONSE_CANCEL); get_action_area()->pack_start (transcode_button, false, false); show_all_children (); @@ -350,7 +292,229 @@ ExportVideoDialog::ExportVideoDialog (Session* s, TimeSelection &tme, bool range ExportVideoDialog::~ExportVideoDialog () { - if (transcoder) { delete transcoder; transcoder = 0;} + if (_transcoder) { delete _transcoder; _transcoder = 0;} +} + +void +ExportVideoDialog::set_original_file_information() +{ + assert(_transcoder == 0); + std::string infile = invid_path_entry.get_text(); + + if (scale_checkbox.get_active()) { + // user may have set custom values already, don't touch. + return; + } + if (infile == "" || !Glib::file_test(infile, Glib::FILE_TEST_EXISTS)) { + return; + } + + _transcoder = new TranscodeFfmpeg(infile); + if (_transcoder->probe_ok()) { + _video_source_aspect_ratio = -1; + width_spinner.set_value(_transcoder->get_width()); + height_spinner.set_value(_transcoder->get_height()); + _video_source_aspect_ratio = _transcoder->get_aspect(); + } + + delete _transcoder; _transcoder = 0; +} +void +ExportVideoDialog::apply_state (TimeSelection &tme, bool range) +{ + _suspend_dirty = true; // TODO really just queue 'dirty' and mark session dirty on "Export" + + export_range = tme; + _video_source_aspect_ratio = -1; + + outfn_path_entry.set_text (_session->session_directory().export_path() + G_DIR_SEPARATOR +"export.avi"); + + // TODO remember setting for export-range.. somehow, (let explicit range override) + frameoffset_t av_offset = ARDOUR_UI::instance()->video_timeline->get_offset(); + if (av_offset < 0 ) { + insnd_combo.append_text (_("from 00:00:00:00 to the video's end")); + } else { + insnd_combo.append_text (_("from the video's start to the video's end")); + } + if (!export_range.empty()) { + insnd_combo.append_text (_("Selected range")); // TODO show export_range.start() -> export_range.end_frame() + } + if (range) { + insnd_combo.set_active(2); + } else { + insnd_combo.set_active(0); + } + + preset_combo.set_active(0); + audio_codec_combo.set_active(0); + video_codec_combo.set_active(0); + audio_bitrate_combo.set_active(0); + audio_samplerate_combo.set_active(2); + video_bitrate_combo.set_active(0); + aspect_combo.set_active(1); + + scale_checkbox.set_active(false); + scale_aspect.set_active(true); + aspect_checkbox.set_active(false); + normalize_checkbox.set_active(false); + twopass_checkbox.set_active(false); + optimizations_checkbox.set_active(false); + deinterlace_checkbox.set_active(false); + bframes_checkbox.set_active(false); + fps_checkbox.set_active(false); + meta_checkbox.set_active(false); + + float tcfps = _session->timecode_frames_per_second(); + + LocaleGuard lg; + + XMLNode* node = _session->extra_xml (X_("Videotimeline")); + bool filenameset = false; + if (node) { + if (node->property(X_("OriginalVideoFile"))) { + std::string filename = node->property(X_("OriginalVideoFile"))->value(); + if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) { + invid_path_entry.set_text (filename); + filenameset = true; + } + } + if (!filenameset + && node->property(X_("Filename")) + && node->property(X_("LocalFile")) + && node->property(X_("LocalFile"))->value() == X_("1") + ) + { + std::string filename = node->property(X_("Filename"))->value(); + if (filename.at(0) != G_DIR_SEPARATOR) + { + filename = Glib::build_filename (_session->session_directory().video_path(), filename); + } + if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) + { + invid_path_entry.set_text (filename); + filenameset = true; + } + } + } + if (!filenameset) { + invid_path_entry.set_text (X_("")); + } + + node = _session->extra_xml (X_("Videoexport")); + if (node) { + XMLProperty const * prop; + prop = node->property (X_("ChangeGeometry")); + if (prop) { scale_checkbox.set_active(atoi(prop->value())?true:false); } + prop = node->property (X_("KeepAspect")); + if (prop) { scale_aspect.set_active(atoi(prop->value())?true:false); } + prop = node->property (X_("ChangeAspect")); + if (prop) { aspect_checkbox.set_active(atoi(prop->value())?true:false); } + prop = node->property (X_("NormalizeAudio")); + if (prop) { normalize_checkbox.set_active(atoi(prop->value())?true:false); } + prop = node->property (X_("TwoPassEncode")); + if (prop) { twopass_checkbox.set_active(atoi(prop->value())?true:false); } + prop = node->property (X_("CodecOptimzations")); + if (prop) { optimizations_checkbox.set_active(atoi(prop->value())?true:false); } + prop = node->property (X_("Deinterlace")); + if (prop) { deinterlace_checkbox.set_active(atoi(prop->value())?true:false); } + prop = node->property (X_("BFrames")); + if (prop) { bframes_checkbox.set_active(atoi(prop->value())?true:false); } + prop = node->property (X_("ChangeFPS")); + if (prop) { fps_checkbox.set_active(atoi(prop->value())?true:false); } + prop = node->property (X_("Metadata")); + if (prop) { meta_checkbox.set_active(atoi(prop->value())?true:false); } + + prop = node->property (X_("Format")); + if (prop && !prop->value().empty()) { change_file_extension( "." + prop->value()); } + + _suspend_signals = true; + prop = node->property (X_("Width")); + if (prop) { width_spinner.set_value(atoi(prop->value())); } + prop = node->property (X_("Height")); + if (prop) { height_spinner.set_value(atoi(prop->value())); } + _suspend_signals = false; + + prop = node->property (X_("FPS")); + if (prop && fps_checkbox.get_active()) { tcfps = atof(prop->value()); } + + prop = node->property (X_("Preset")); + if (prop) { preset_combo.set_active_text(prop->value()); } + prop = node->property (X_("VCodec")); + if (prop) { video_codec_combo.set_active_text(prop->value()); } + prop = node->property (X_("ACodec")); + if (prop) { audio_codec_combo.set_active_text(prop->value()); } + prop = node->property (X_("VBitrate")); + if (prop) { video_bitrate_combo.set_active_text(prop->value()); } + prop = node->property (X_("ABitrate")); + if (prop) { audio_bitrate_combo.set_active_text(prop->value()); } + prop = node->property (X_("AspectRatio")); + if (prop) { aspect_combo.set_active_text(prop->value()); } + prop = node->property (X_("SampleRate")); + if (prop) { audio_samplerate_combo.set_active_text(prop->value()); } + } + + if (fabs(tcfps - 23.976) < 0.01) { fps_combo.set_active(0); } + else if (fabs(tcfps - 24.0 ) < 0.01) { fps_combo.set_active(1); } + else if (fabs(tcfps - 24.976) < 0.01) { fps_combo.set_active(2); } + else if (fabs(tcfps - 25.0 ) < 0.01) { fps_combo.set_active(3); } + else if (fabs(tcfps - 29.97 ) < 0.01) { fps_combo.set_active(4); } + else if (fabs(tcfps - 30.0 ) < 0.01) { fps_combo.set_active(5); } + else if (fabs(tcfps - 59.94 ) < 0.01) { fps_combo.set_active(6); } + else if (fabs(tcfps - 60.0 ) < 0.01) { fps_combo.set_active(7); } + else { fps_combo.set_active(5); } + + set_original_file_information(); + + /* update sensitivity */ + scale_checkbox_toggled(); + aspect_checkbox_toggled(); + fps_checkbox_toggled(); + video_codec_combo_changed(); + + _suspend_dirty = false; + + show_all_children (); + if (progress_box) { + progress_box->hide(); + } +} + +XMLNode& +ExportVideoDialog::get_state () +{ + LocaleGuard lg; + XMLNode* node = new XMLNode (X_("Videoexport")); + node->add_property (X_("ChangeGeometry"), scale_checkbox.get_active() ? X_("1") : X_("0")); + node->add_property (X_("KeepAspect"), scale_aspect.get_active() ? X_("1") : X_("0")); + node->add_property (X_("ChangeAspect"), aspect_checkbox.get_active() ? X_("1") : X_("0")); + node->add_property (X_("NormalizeAudio"), normalize_checkbox.get_active() ? X_("1") : X_("0")); + node->add_property (X_("TwoPassEncode"), twopass_checkbox.get_active() ? X_("1") : X_("0")); + node->add_property (X_("CodecOptimzations"), optimizations_checkbox.get_active() ? X_("1") : X_("0")); + node->add_property (X_("Deinterlace"), deinterlace_checkbox.get_active() ? X_("1") : X_("0")); + node->add_property (X_("BFrames"), bframes_checkbox.get_active() ? X_("1") : X_("0")); + node->add_property (X_("ChangeFPS"), fps_checkbox.get_active() ? X_("1") : X_("0")); + node->add_property (X_("Metadata"), meta_checkbox.get_active() ? X_("1") : X_("0")); + + node->add_property (X_("Format"), get_file_extension(outfn_path_entry.get_text())); + + node->add_property (X_("Width"), width_spinner.get_value()); + node->add_property (X_("Height"), height_spinner.get_value()); + + node->add_property (X_("Preset"), preset_combo.get_active_text()); + node->add_property (X_("VCodec"), video_codec_combo.get_active_text()); + node->add_property (X_("ACodec"), audio_codec_combo.get_active_text()); + node->add_property (X_("VBitrate"), video_bitrate_combo.get_active_text()); + node->add_property (X_("ABitrate"), audio_bitrate_combo.get_active_text()); + node->add_property (X_("AspectRatio"), aspect_combo.get_active_text()); + node->add_property (X_("SampleRate"), audio_samplerate_combo.get_active_text()); + node->add_property (X_("FPS"), fps_combo.get_active_text()); + + return *node; +} + +void +ExportVideoDialog::set_state (const XMLNode &) +{ } void @@ -362,9 +526,9 @@ ExportVideoDialog::on_show () void ExportVideoDialog::abort_clicked () { - aborted = true; - if (transcoder) { - transcoder->cancel(); + _aborted = true; + if (_transcoder) { + _transcoder->cancel(); } } @@ -376,10 +540,10 @@ ExportVideoDialog::update_progress (framecnt_t c, framecnt_t a) pbar.pulse(); } else { double progress = (double)c / (double) a; - progress = progress / ((twopass ? 2.0 : 1.0) + (normalize ? 2.0 : 1.0)); - if (normalize && twopass) progress += (firstpass ? .5 : .75); - else if (normalize) progress += 2.0/3.0; - else if (twopass) progress += (firstpass ? 1.0/3.0 : 2.0/3.0); + progress = progress / ((_twopass ? 2.0 : 1.0) + (_normalize ? 2.0 : 1.0)); + if (_normalize && _twopass) progress += (_firstpass ? .5 : .75); + else if (_normalize) progress += 2.0/3.0; + else if (_twopass) progress += (_firstpass ? 1.0/3.0 : 2.0/3.0); else progress += .5; pbar.set_fraction (progress); @@ -391,36 +555,50 @@ gint ExportVideoDialog::audio_progress_display () { std::string status_text; - double progress = 0.0; - if (status->normalizing) { + double progress = -1.0; + switch (status->active_job) { + case ExportStatus::Normalizing: pbar.set_text (_("Normalizing audio")); progress = ((float) status->current_normalize_cycle) / status->total_normalize_cycles; - progress = progress / (twopass ? 4.0 : 3.0) + (twopass ? .25 : 1.0/3.0); - } else { + progress = progress / (_twopass ? 4.0 : 3.0) + (_twopass ? .25 : 1.0 / 3.0); + break; + case ExportStatus::Exporting: pbar.set_text (_("Exporting audio")); progress = ((float) status->processed_frames_current_timespan) / status->total_frames_current_timespan; - progress = progress / ((twopass ? 2.0 : 1.0) + (normalize ? 2.0 : 1.0)); - } - if (progress < previous_progress) { - // Work around gtk bug - pbar.hide(); - pbar.show(); - } - previous_progress = progress; + progress = progress / ((_twopass ? 2.0 : 1.0) + (_normalize ? 2.0 : 1.0)); + break; + default: + pbar.set_text (_("Exporting audio")); + break; + } + + if (progress < _previous_progress) { + // Work around gtk bug + pbar.hide(); + pbar.show(); + } + _previous_progress = progress; + + if (progress >= 0) { pbar.set_fraction (progress); + } else { + pbar.set_pulse_step(.1); + pbar.pulse(); + } return TRUE; } void ExportVideoDialog::finished () { - if (aborted) { + if (_aborted) { ::g_unlink(outfn_path_entry.get_text().c_str()); - ::g_unlink (insnd.c_str()); + ::g_unlink (_insnd.c_str()); + delete _transcoder; _transcoder = 0; Gtk::Dialog::response(RESPONSE_CANCEL); - } else if (twopass && firstpass) { - firstpass = false; - if (transcoder) { delete transcoder; transcoder = 0;} + } else if (_twopass && _firstpass) { + _firstpass = false; + if (_transcoder) { delete _transcoder; _transcoder = 0;} encode_pass(2); } else { if (twopass_checkbox.get_active()) { @@ -428,7 +606,8 @@ ExportVideoDialog::finished () std::string p2log = Glib::path_get_dirname (outfn) + G_DIR_SEPARATOR + "ffmpeg2pass"; ::g_unlink (p2log.c_str()); } - ::g_unlink (insnd.c_str()); + ::g_unlink (_insnd.c_str()); + delete _transcoder; _transcoder = 0; Gtk::Dialog::response(RESPONSE_ACCEPT); } } @@ -436,8 +615,15 @@ ExportVideoDialog::finished () void ExportVideoDialog::launch_export () { + /* remember current settings. + * needed because apply_state() acts on both: + * "Videotimeline" and "Video Export" extra XML + * as well as current _session settings + */ + _session->add_extra_xml (get_state()); + std::string outfn = outfn_path_entry.get_text(); - if (!confirm_video_outfn(outfn)) { return; } + if (!confirm_video_outfn(*this, outfn)) { return; } vbox->hide(); cancel_button->hide(); @@ -445,10 +631,10 @@ ExportVideoDialog::launch_export () pbar.set_size_request(300,-1); pbar.set_text(_("Exporting Audio...")); progress_box->show(); - aborted = false; - twopass = twopass_checkbox.get_active(); - firstpass = true; - normalize = normalize_checkbox.get_active(); + _aborted = false; + _twopass = twopass_checkbox.get_active(); + _firstpass = true; + _normalize = normalize_checkbox.get_active(); /* export audio track */ ExportTimespanPtr tsp = _session->get_export_handler()->add_timespan(); @@ -457,7 +643,7 @@ ExportVideoDialog::launch_export () boost::shared_ptr b; XMLTree tree; std::string vtl_samplerate = audio_samplerate_combo.get_active_text(); - std::string vtl_normalize = normalize ? "true" : "false"; + std::string vtl_normalize = _normalize ? "true" : "false"; tree.read_buffer(std::string( "" "" @@ -496,14 +682,14 @@ ExportVideoDialog::launch_export () framepos_t start, end; start = end = 0; if (insnd_combo.get_active_row_number() == 1) { - transcoder = new TranscodeFfmpeg(invid_path_entry.get_text()); - if (transcoder->probe_ok() && transcoder->get_fps() > 0) { - end = transcoder->get_duration() * _session->nominal_frame_rate() / transcoder->get_fps(); + _transcoder = new TranscodeFfmpeg(invid_path_entry.get_text()); + if (_transcoder->probe_ok() && _transcoder->get_fps() > 0) { + end = _transcoder->get_duration() * _session->nominal_frame_rate() / _transcoder->get_fps(); } else { warning << _("Export Video: Cannot query duration of video-file, using duration from timeline instead.") << endmsg; end = ARDOUR_UI::instance()->video_timeline->get_duration(); } - if (transcoder) {delete transcoder; transcoder = 0;} + if (_transcoder) {delete _transcoder; _transcoder = 0;} frameoffset_t av_offset = ARDOUR_UI::instance()->video_timeline->get_offset(); #if 0 /* DEBUG */ @@ -531,6 +717,7 @@ ExportVideoDialog::launch_export () if ( (start >= end) || (end < vstart) || (start > vend)) { warning << _("Export Video: export-range does not include video.") << endmsg; + delete _transcoder; _transcoder = 0; Gtk::Dialog::response(RESPONSE_CANCEL); return; } @@ -543,6 +730,7 @@ ExportVideoDialog::launch_export () IO* master_out = _session->master_out()->output().get(); if (!master_out) { warning << _("Export Video: No Master Out Ports to Connect for Audio Export") << endmsg; + delete _transcoder; _transcoder = 0; Gtk::Dialog::response(RESPONSE_CANCEL); return; } @@ -557,7 +745,7 @@ ExportVideoDialog::launch_export () fnp->set_timespan(tsp); fnp->set_label("vtl"); fnp->include_label = true; - insnd = fnp->get_path(fmp); + _insnd = fnp->get_path(fmp); /* do sound export */ fmp->set_soundcloud_upload(false); @@ -566,9 +754,9 @@ ExportVideoDialog::launch_export () status = _session->get_export_status (); audio_progress_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ExportVideoDialog::audio_progress_display), 100); - previous_progress = 0.0; - while (status->running) { - if (aborted) { status->abort(); } + _previous_progress = 0.0; + while (status->running ()) { + if (_aborted) { status->abort(); } if (gtk_events_pending()) { gtk_main_iteration (); } else { @@ -578,7 +766,8 @@ ExportVideoDialog::launch_export () audio_progress_connection.disconnect(); status->finish (); if (status->aborted()) { - ::g_unlink (insnd.c_str()); + ::g_unlink (_insnd.c_str()); + delete _transcoder; _transcoder = 0; Gtk::Dialog::response(RESPONSE_CANCEL); return; } @@ -592,19 +781,21 @@ ExportVideoDialog::encode_pass (int pass) std::string outfn = outfn_path_entry.get_text(); std::string invid = invid_path_entry.get_text(); - transcoder = new TranscodeFfmpeg(invid); - if (!transcoder->ffexec_ok()) { + _transcoder = new TranscodeFfmpeg(invid); + if (!_transcoder->ffexec_ok()) { /* ffmpeg binary was not found. TranscodeFfmpeg prints a warning */ - ::g_unlink (insnd.c_str()); + ::g_unlink (_insnd.c_str()); + delete _transcoder; _transcoder = 0; Gtk::Dialog::response(RESPONSE_CANCEL); return; } - if (!transcoder->probe_ok()) { + if (!_transcoder->probe_ok()) { /* video input file can not be read */ warning << _("Export Video: Video input file cannot be read.") << endmsg; - ::g_unlink (insnd.c_str()); - Gtk::Dialog::response(RESPONSE_CANCEL); - return; + ::g_unlink (_insnd.c_str()); + delete _transcoder; _transcoder = 0; + Gtk::Dialog::response(RESPONSE_CANCEL); + return; } std::string preset = preset_combo.get_active_text(); @@ -613,21 +804,32 @@ ExportVideoDialog::encode_pass (int pass) if (fps_checkbox.get_active()) { ffs["-r"] = fps_combo.get_active_text(); - transcoder->set_fps(atof(fps_combo.get_active_text())); + _transcoder->set_fps(atof(fps_combo.get_active_text())); } if (scale_checkbox.get_active()) { ffs["-s"] = string_compose("%1x%2", width_spinner.get_value(), height_spinner.get_value()); } - ffs["-vcodec"] = video_codec_combo.get_active_text(); - ffs["-acodec"] = audio_codec_combo.get_active_text(); - if (video_bitrate_combo.get_active_text() == "retain" ) { + if (video_codec_combo.get_active_text() != _("(default for format)")) { + ffs["-vcodec"] = video_codec_combo.get_active_text(); + } + if (audio_codec_combo.get_active_text() != _("(default for format)")) { + ffs["-acodec"] = audio_codec_combo.get_active_text(); + } + + if (video_bitrate_combo.get_active_text() == _("(default)") ) { + ; + } + else if (video_bitrate_combo.get_active_text() == _("(retain)") ) { ffs["-qscale"] = "0"; } else { ffs["-b:v"] = video_bitrate_combo.get_active_text(); } - ffs["-b:a"] = audio_bitrate_combo.get_active_text(); + + if (audio_bitrate_combo.get_active_text() != _("(default)") ) { + ffs["-b:a"] = audio_bitrate_combo.get_active_text(); + } if (audio_codec_combo.get_active_text() == "aac" ) { ffs["-strict"] = "-2"; @@ -694,7 +896,7 @@ ExportVideoDialog::encode_pass (int pass) } bool map = true; - if (pass == 1 && twopass) { + if (pass == 1 && _twopass) { pbar.set_text (_("Encoding Video.. Pass 1/2")); map = false; ffs["-pass"] = "1"; @@ -733,7 +935,7 @@ ExportVideoDialog::encode_pass (int pass) std::ostringstream osstream; osstream << duration_s; ffs["-t"] = osstream.str(); - transcoder->set_duration(duration_s * transcoder->get_fps()); + _transcoder->set_duration(duration_s * _transcoder->get_fps()); if (insnd_combo.get_active_row_number() == 0 || insnd_combo.get_active_row_number() == 2) { framepos_t start, snend; @@ -752,13 +954,13 @@ ExportVideoDialog::encode_pass (int pass) #endif if (av_offset > start && av_offset + vid_duration < snend) { - transcoder->set_leadinout((av_offset - start) / (double)_session->nominal_frame_rate(), + _transcoder->set_leadinout((av_offset - start) / (double)_session->nominal_frame_rate(), (snend - (av_offset + vid_duration)) / (double)_session->nominal_frame_rate()); } else if (av_offset > start) { - transcoder->set_leadinout((av_offset - start) / (double)_session->nominal_frame_rate(), 0); + _transcoder->set_leadinout((av_offset - start) / (double)_session->nominal_frame_rate(), 0); } else if (av_offset + vid_duration < snend) { - transcoder->set_leadinout(0, (snend - (av_offset + vid_duration)) / (double)_session->nominal_frame_rate()); - transcoder->set_avoffset((av_offset - start) / (double)_session->nominal_frame_rate()); + _transcoder->set_leadinout(0, (snend - (av_offset + vid_duration)) / (double)_session->nominal_frame_rate()); + _transcoder->set_avoffset((av_offset - start) / (double)_session->nominal_frame_rate()); } #if 0 else if (start > av_offset) { @@ -767,15 +969,15 @@ ExportVideoDialog::encode_pass (int pass) } #endif else { - transcoder->set_avoffset((av_offset - start) / (double)_session->nominal_frame_rate()); + _transcoder->set_avoffset((av_offset - start) / (double)_session->nominal_frame_rate()); } } else if (av_offset < 0) { /* from 00:00:00:00 to video-end */ - transcoder->set_avoffset(av_offset / (double)_session->nominal_frame_rate()); + _transcoder->set_avoffset(av_offset / (double)_session->nominal_frame_rate()); } - TranscodeFfmpeg::FFSettings meta = transcoder->default_meta_data(); + TranscodeFfmpeg::FFSettings meta = _transcoder->default_meta_data(); if (meta_checkbox.get_active()) { ARDOUR::SessionMetadata * session_data = ARDOUR::SessionMetadata::Metadata(); if (session_data->year() > 0 ) { @@ -803,14 +1005,15 @@ ExportVideoDialog::encode_pass (int pass) #if 1 /* tentative debug mode */ if (debug_checkbox.get_active()) { - transcoder->set_debug(true); + _transcoder->set_debug(true); } #endif - transcoder->Progress.connect(*this, invalidator (*this), boost::bind (&ExportVideoDialog::update_progress , this, _1, _2), gui_context()); - transcoder->Finished.connect(*this, invalidator (*this), boost::bind (&ExportVideoDialog::finished, this), gui_context()); - if (!transcoder->encode(outfn, insnd, invid, ffs, meta, map)) { + _transcoder->Progress.connect(*this, invalidator (*this), boost::bind (&ExportVideoDialog::update_progress , this, _1, _2), gui_context()); + _transcoder->Finished.connect(*this, invalidator (*this), boost::bind (&ExportVideoDialog::finished, this), gui_context()); + if (!_transcoder->encode(outfn, _insnd, invid, ffs, meta, map)) { ARDOUR_UI::instance()->popup_error(_("Transcoding failed.")); + delete _transcoder; _transcoder = 0; Gtk::Dialog::response(RESPONSE_CANCEL); return; } @@ -819,28 +1022,69 @@ ExportVideoDialog::encode_pass (int pass) void ExportVideoDialog::change_file_extension (std::string ext) { + if (ext == "") return; outfn_path_entry.set_text ( strip_file_extension(outfn_path_entry.get_text()) + ext ); } +void +ExportVideoDialog::width_value_changed () +{ + if (_suspend_signals) { + return; + } + if (_session && !_suspend_dirty) _session->set_dirty (); + if (!scale_checkbox.get_active() || !scale_aspect.get_active()) { + return; + } + if (_video_source_aspect_ratio <= 0) { + return; + } + _suspend_signals = true; + height_spinner.set_value(rintf(width_spinner.get_value() / _video_source_aspect_ratio)); + _suspend_signals = false; +} + +void +ExportVideoDialog::height_value_changed () +{ + if (_suspend_signals) { + return; + } + if (_session && !_suspend_dirty) _session->set_dirty (); + if (!scale_checkbox.get_active() || !scale_aspect.get_active()) { + return; + } + if (_video_source_aspect_ratio <= 0) { + return; + } + _suspend_signals = true; + width_spinner.set_value(rintf(height_spinner.get_value() * _video_source_aspect_ratio)); + _suspend_signals = false; +} + void ExportVideoDialog::scale_checkbox_toggled () { + scale_aspect.set_sensitive(scale_checkbox.get_active()); width_spinner.set_sensitive(scale_checkbox.get_active()); height_spinner.set_sensitive(scale_checkbox.get_active()); + if (_session && !_suspend_dirty) _session->set_dirty (); } void ExportVideoDialog::fps_checkbox_toggled () { fps_combo.set_sensitive(fps_checkbox.get_active()); + if (_session && !_suspend_dirty) _session->set_dirty (); } void ExportVideoDialog::aspect_checkbox_toggled () { aspect_combo.set_sensitive(aspect_checkbox.get_active()); + if (_session && !_suspend_dirty) _session->set_dirty (); } void @@ -868,6 +1112,7 @@ ExportVideoDialog::video_codec_combo_changed () optimizations_checkbox.set_active(false); optimizations_label.set_text("-"); } + if (_session && !_suspend_dirty) _session->set_dirty (); } void @@ -878,18 +1123,18 @@ ExportVideoDialog::preset_combo_changed () if (p == "flv") { change_file_extension(".flv"); - audio_codec_combo.set_active(1); - video_codec_combo.set_active(0); - audio_bitrate_combo.set_active(1); - video_bitrate_combo.set_active(1); + audio_codec_combo.set_active(2); + video_codec_combo.set_active(1); + audio_bitrate_combo.set_active(2); + video_bitrate_combo.set_active(3); audio_samplerate_combo.set_active(1); } else if (p == "you-tube") { change_file_extension(".avi"); - audio_codec_combo.set_active(2); - video_codec_combo.set_active(5); - audio_bitrate_combo.set_active(1); - video_bitrate_combo.set_active(2); + audio_codec_combo.set_active(3); + video_codec_combo.set_active(6); + audio_bitrate_combo.set_active(2); + video_bitrate_combo.set_active(4); if (_session->nominal_frame_rate() == 48000 || _session->nominal_frame_rate() == 96000) { audio_samplerate_combo.set_active(2); } else { @@ -898,30 +1143,42 @@ ExportVideoDialog::preset_combo_changed () } else if (p == "ogg") { change_file_extension(".ogv"); - audio_codec_combo.set_active(3); - video_codec_combo.set_active(1); - audio_bitrate_combo.set_active(2); - video_bitrate_combo.set_active(2); + audio_codec_combo.set_active(4); + video_codec_combo.set_active(2); + audio_bitrate_combo.set_active(3); + video_bitrate_combo.set_active(4); if (_session->nominal_frame_rate() == 48000 || _session->nominal_frame_rate() == 96000) { audio_samplerate_combo.set_active(2); } else { audio_samplerate_combo.set_active(1); } } - else if (p == "dvd-mp2") { - change_file_extension(".mpg"); + else if (p == "webm") { + change_file_extension(".webm"); audio_codec_combo.set_active(4); - video_codec_combo.set_active(3); + video_codec_combo.set_active(7); audio_bitrate_combo.set_active(3); - video_bitrate_combo.set_active(3); + video_bitrate_combo.set_active(4); + if (_session->nominal_frame_rate() == 48000 || _session->nominal_frame_rate() == 96000) { + audio_samplerate_combo.set_active(2); + } else { + audio_samplerate_combo.set_active(1); + } + } + else if (p == "dvd-mp2") { + change_file_extension(".mpg"); + audio_codec_combo.set_active(5); + video_codec_combo.set_active(4); + audio_bitrate_combo.set_active(4); + video_bitrate_combo.set_active(5); audio_samplerate_combo.set_active(2); } else if (p == "dvd-NTSC" || p == "dvd-PAL") { change_file_extension(".mpg"); - audio_codec_combo.set_active(5); - video_codec_combo.set_active(3); - audio_bitrate_combo.set_active(3); - video_bitrate_combo.set_active(3); + audio_codec_combo.set_active(6); + video_codec_combo.set_active(4); + audio_bitrate_combo.set_active(4); + video_bitrate_combo.set_active(5); audio_samplerate_combo.set_active(2); scale_checkbox.set_active(false); @@ -929,10 +1186,22 @@ ExportVideoDialog::preset_combo_changed () } else if (p == "mpeg4") { change_file_extension(".mp4"); - audio_codec_combo.set_active(0); - video_codec_combo.set_active(4); - audio_bitrate_combo.set_active(3); - video_bitrate_combo.set_active(3); + audio_codec_combo.set_active(1); + video_codec_combo.set_active(5); + audio_bitrate_combo.set_active(4); + video_bitrate_combo.set_active(5); + if (_session->nominal_frame_rate() == 48000 || _session->nominal_frame_rate() == 96000) { + audio_samplerate_combo.set_active(2); + } else { + audio_samplerate_combo.set_active(1); + } + } + else if (p == "mp4/h264/aac") { + change_file_extension(".mp4"); + audio_codec_combo.set_active(2); + video_codec_combo.set_active(6); + audio_bitrate_combo.set_active(0); + video_bitrate_combo.set_active(0); if (_session->nominal_frame_rate() == 48000 || _session->nominal_frame_rate() == 96000) { audio_samplerate_combo.set_active(2); } else {