From 1d48fb011e5f5eefbc656dc72197a620654ff847 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 2 Apr 2013 12:27:08 +0200 Subject: vtl: update video-export * range: pre/postfix with black video-frames * fix A/V offset - decode and skip source until offset is reached * use spinning progress-bar during decode&skip * use video-stream index in stream-map * fix audio-export abort; stop freewheeling * re-indent code --- gtk2_ardour/export_video_dialog.cc | 162 ++++++++++++++++++++----------------- gtk2_ardour/transcode_ffmpeg.cc | 45 +++++++++-- gtk2_ardour/transcode_ffmpeg.h | 5 ++ 3 files changed, 134 insertions(+), 78 deletions(-) diff --git a/gtk2_ardour/export_video_dialog.cc b/gtk2_ardour/export_video_dialog.cc index 7491640e39..380b8240f7 100644 --- a/gtk2_ardour/export_video_dialog.cc +++ b/gtk2_ardour/export_video_dialog.cc @@ -66,7 +66,7 @@ using namespace ARDOUR; ExportVideoDialog::ExportVideoDialog (PublicEditor& ed, Session* s) : ArdourDialog (_("Export Video File ")) , editor (ed) - , outfn_path_label (_("Output File:"), Gtk::ALIGN_LEFT) + , outfn_path_label (_("Output File:"), Gtk::ALIGN_LEFT) , outfn_browse_button (_("Browse")) , invid_path_label (_("Input Video File:"), Gtk::ALIGN_LEFT) , invid_browse_button (_("Browse")) @@ -152,15 +152,6 @@ ExportVideoDialog::ExportVideoDialog (PublicEditor& ed, Session* s) } insnd_combo.set_active(0); -#if 1 - // TODO probe input file - don't use VTL duration. - framecnt_t duration_v = ARDOUR_UI::instance()->video_timeline->get_duration(); - framecnt_t duration_a = _session->current_end_frame() - _session->current_start_frame(); - if ( duration_a > duration_v ) { - warning << _("Audio is longer than video. This file may not play correctly.") << endmsg; - } -#endif - outfn_path_entry.set_width_chars(38); outfn_path_entry.set_text (_session->session_directory().export_path() + G_DIR_SEPARATOR +"export.avi"); @@ -281,14 +272,14 @@ ExportVideoDialog::ExportVideoDialog (PublicEditor& ed, Session* s) 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); } + 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"); @@ -302,7 +293,7 @@ ExportVideoDialog::ExportVideoDialog (PublicEditor& ed, Session* s) fps_checkbox_toggled(); video_codec_combo_changed(); - vbox->pack_start (*options_box, false, true); + vbox->pack_start (*options_box, false, true); get_vbox()->set_spacing (4); get_vbox()->pack_start (*vbox, false, false); @@ -371,7 +362,7 @@ ExportVideoDialog::finished () if (transcoder) { delete transcoder; transcoder = 0;} encode_pass(2); } else { - if (twopass_checkbox.get_active()) { + if (twopass_checkbox.get_active()) { std::string outfn = outfn_path_entry.get_text(); std::string p2log = Glib::path_get_dirname (outfn) + G_DIR_SEPARATOR + "ffmpeg2pass"; unlink (p2log.c_str()); @@ -401,11 +392,10 @@ ExportVideoDialog::launch_export () boost::shared_ptr ccp = _session->get_export_handler()->add_channel_config(); boost::shared_ptr fnp = _session->get_export_handler()->add_filename(); boost::shared_ptr b; - XMLTree tree; + XMLTree tree; std::string vtl_samplerate = audio_samplerate_combo.get_active_text(); std::string vtl_normalize = normalize_checkbox.get_active()?"true":"false"; tree.read_buffer(std::string( - /* TODO: move this into a header file */ "" "" " " @@ -444,23 +434,29 @@ ExportVideoDialog::launch_export () start = end = 0; if (insnd_combo.get_active_row_number() == 1) { transcoder = new TranscodeFfmpeg(invid_path_entry.get_text()); - if (transcoder->probe_ok()) { - end = transcoder->get_duration() * _session->nominal_frame_rate() / transcoder->get_fps(); + 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 */ + printf("audio-range -- AV offset: %lld\n", av_offset); +#endif if (av_offset > 0) { - start += (av_offset / (double)_session->nominal_frame_rate()); + start = av_offset; } - end += (av_offset / (double)_session->nominal_frame_rate()); + end += av_offset; } if (end <= 0) { start = _session->current_start_frame(); end = _session->current_end_frame(); } #if 0 /* DEBUG */ - printf("export-range %ld -> %ld\n", start, end); + printf("audio export-range %lld -> %lld\n", start, end); #endif tsp->set_range (start, end); @@ -490,11 +486,11 @@ ExportVideoDialog::launch_export () /* do sound export */ _session->get_export_handler()->add_export_config (tsp, ccp, fmp, fnp, b); _session->get_export_handler()->do_export(); - boost::shared_ptr status = _session->get_export_status (); + boost::shared_ptr status = _session->get_export_status (); //status->running = true; float previous_progress = 0.0; - while (status->running) { + while (status->running) { if (aborted) { status->abort(); } float progress = 0.0; @@ -512,18 +508,18 @@ ExportVideoDialog::launch_export () } previous_progress = progress; pbar.set_fraction (progress); - if (gtk_events_pending()) { - gtk_main_iteration (); - } else { - usleep (10000); - } - } - if (status->aborted()) { + if (gtk_events_pending()) { + gtk_main_iteration (); + } else { + usleep (10000); + } + } + status->finish (); + if (status->aborted()) { unlink (insnd.c_str()); Gtk::Dialog::response(RESPONSE_CANCEL); return; } - status->finish (); pbar.set_text (_("Encoding Video..")); encode_pass(1); } @@ -534,18 +530,20 @@ ExportVideoDialog::encode_pass (int pass) std::string outfn = outfn_path_entry.get_text(); std::string invid = invid_path_entry.get_text(); - /* TODO: optimize - * if (insnd_combo.get_active_row_number() == 0) - * we can skip parsing the input video-file and use - * new TranscodeFfmpeg(""); - */ transcoder = new TranscodeFfmpeg(invid); if (!transcoder->ffexec_ok()) { - /* ffmpeg binary was not found. TranscodeFfmpeg prints a warning */ + /* ffmpeg binary was not found. TranscodeFfmpeg prints a warning */ unlink (insnd.c_str()); Gtk::Dialog::response(RESPONSE_CANCEL); return; } + if (!transcoder->probe_ok()) { + /* video input file can not be read */ + warning << _("Export Video: Video input file cannot be read.") << endmsg; + unlink (insnd.c_str()); + Gtk::Dialog::response(RESPONSE_CANCEL); + return; + } std::string preset = preset_combo.get_active_text(); FFSettings ffs ; /* = transcoder->default_encoder_settings(); */ @@ -634,7 +632,7 @@ ExportVideoDialog::encode_pass (int pass) ffs["-aspect"] = aspect_combo.get_active_text(); } if (deinterlace_checkbox.get_active()) { - ffs["-deinterlace"] = "-y"; // we use '-y' as dummy parameter for non key/value options + ffs["-deinterlace"] = "-y"; // we use '-y' as dummy parameter for non key/value options } bool map = true; @@ -657,34 +655,52 @@ ExportVideoDialog::encode_pass (int pass) } frameoffset_t av_offset = ARDOUR_UI::instance()->video_timeline->get_offset(); + double duration_s = 0; if (insnd_combo.get_active_row_number() == 0) { framecnt_t duration_f = _session->current_end_frame() - _session->current_start_frame(); - double duration_s = (double)duration_f / (double)_session->nominal_frame_rate(); - std::ostringstream osstream; osstream << duration_s; - ffs["-t"] = osstream.str(); - if (fps_checkbox.get_active()) { - transcoder->set_duration(duration_s * atof(fps_combo.get_active_text())); - } else { - transcoder->set_duration(duration_s * transcoder->get_fps()); + duration_s = (double)duration_f / (double)_session->nominal_frame_rate(); + } else { + framecnt_t duration_f = ARDOUR_UI::instance()->video_timeline->get_duration(); + if (av_offset < 0 ) { + duration_f += av_offset; } + duration_s = (double)duration_f / (double)_session->nominal_frame_rate(); + } + + std::ostringstream osstream; osstream << duration_s; + ffs["-t"] = osstream.str(); + if (fps_checkbox.get_active()) { + transcoder->set_duration(duration_s * atof(fps_combo.get_active_text())); } else { - ; - // TODO use (end-start) from above - // in case offset is negative, video will be shorter.. - // CHECK; maybe this is not needed -itsoffset may take care of it + transcoder->set_duration(duration_s * transcoder->get_fps()); } if (insnd_combo.get_active_row_number() == 0) { - framepos_t start = _session->current_start_frame(); -#if 0 - double start_s = ((double)start / (double)_session->nominal_frame_rate()); - std::ostringstream osstream; osstream << start_s; - ffs["-ss"] = osstream.str(); + const framepos_t start = _session->current_start_frame(); + const framepos_t snend = _session->current_end_frame(); + const frameoffset_t vid_duration = ARDOUR_UI::instance()->video_timeline->get_duration(); + +#if 0 /* DEBUG */ + printf("AV offset: %lld Vid-len: %lld Vid-end: %lld || start:%lld || end:%lld\n", + av_offset, vid_duration, av_offset+vid_duration, start, snend); // XXX #endif - av_offset -= start; + + if (av_offset > start && av_offset + vid_duration < snend) { + 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); + } 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()); + } else { + 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()); FFSettings meta = transcoder->default_meta_data(); if (meta_checkbox.get_active()) { @@ -701,11 +717,11 @@ ExportVideoDialog::encode_pass (int pass) std::ostringstream osstream; osstream << session_data->disc_number(); meta["disc"] = osstream.str(); } - if (!session_data->title().empty()) {meta["title"] = session_data->title();} - if (!session_data->artist().empty()) {meta["author"] = session_data->artist();} + if (!session_data->title().empty()) {meta["title"] = session_data->title();} + if (!session_data->artist().empty()) {meta["author"] = session_data->artist();} if (!session_data->album_artist().empty()) {meta["album_artist"] = session_data->album_artist();} - if (!session_data->album().empty()) {meta["album"] = session_data->album();} - if (!session_data->genre().empty()) {meta["genre"] = session_data->genre();} + if (!session_data->album().empty()) {meta["album"] = session_data->album();} + if (!session_data->genre().empty()) {meta["genre"] = session_data->genre();} if (!session_data->composer().empty()) {meta["composer"] = session_data->composer();} if (!session_data->comment().empty()) {meta["comment"] = session_data->comment();} if (!session_data->copyright().empty()) {meta["copyright"] = session_data->copyright();} @@ -731,7 +747,7 @@ void ExportVideoDialog::change_file_extension (std::string ext) { outfn_path_entry.set_text ( - strip_file_extension(outfn_path_entry.get_text()) + ext + strip_file_extension(outfn_path_entry.get_text()) + ext ); } @@ -766,7 +782,7 @@ ExportVideoDialog::video_codec_combo_changed () )) { bframes_checkbox.set_sensitive(true); optimizations_checkbox.set_sensitive(true); - if (video_codec_combo.get_active_text() == "mpeg2video") { + if (video_codec_combo.get_active_text() == "mpeg2video") { optimizations_label.set_text("-mbd rd -trellis 2 -cmp 2 -subcmp 2"); // mpeg2 } else if (video_codec_combo.get_active_text() == "mpeg4") { optimizations_label.set_text("-mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -g 300"); // mpeg4 @@ -836,8 +852,8 @@ ExportVideoDialog::preset_combo_changed () video_bitrate_combo.set_active(3); audio_samplerate_combo.set_active(2); - scale_checkbox.set_active(false); - scale_checkbox.set_sensitive(false); + scale_checkbox.set_active(false); + scale_checkbox.set_sensitive(false); } else if (p == "mpeg4") { change_file_extension(".mp4"); @@ -873,14 +889,14 @@ ExportVideoDialog::preset_combo_changed () for (it = c.begin(); it != c.end(); ++it) { int row = it->get_top_attach(); if (row ==1 || row ==2 || row==4 || row==5 || row == 8) { - it->get_widget()->hide(); + it->get_widget()->hide(); } } } else { for (it = c.begin(); it != c.end(); ++it) { int row = it->get_top_attach(); if (row ==1 || row ==2 || row==4 || row==5 || row == 8) { - it->get_widget()->show(); + it->get_widget()->show(); } } } diff --git a/gtk2_ardour/transcode_ffmpeg.cc b/gtk2_ardour/transcode_ffmpeg.cc index 5ae9ae1c20..7780c4f02b 100644 --- a/gtk2_ardour/transcode_ffmpeg.cc +++ b/gtk2_ardour/transcode_ffmpeg.cc @@ -42,6 +42,9 @@ TranscodeFfmpeg::TranscodeFfmpeg (std::string f) ffmpeg_exe = ""; ffprobe_exe = ""; m_duration = 0; + m_avoffset = m_lead_in = m_lead_out = 0; + m_width = m_height = 0; + m_aspect = m_fps = 0; #if 1 /* tentative debug mode */ debug_enable = false; #endif @@ -147,7 +150,9 @@ TranscodeFfmpeg::probe () std::string key = kv->substr(0, kvsep); std::string value = kv->substr(kvsep + 1); - if (key == X_("width")) { + if (key == X_("index")) { + m_videoidx = atoi(value.c_str()); + } else if (key == X_("width")) { m_width = atoi(value.c_str()); } else if (key == X_("height")) { m_height = atoi(value.c_str()); @@ -231,7 +236,7 @@ TranscodeFfmpeg::probe () while (ffcmd && --timeout) usleep (1000); // wait until 'ffprobe' terminated. if (timeout == 0) return false; -#if 1 /* DEBUG */ +#if 0 /* DEBUG */ printf("FPS: %f\n", m_fps); printf("Duration: %lu frames\n",(unsigned long)m_duration); printf("W/H: %ix%i\n",m_width, m_height); @@ -314,6 +319,7 @@ TranscodeFfmpeg::encode (std::string outfile, std::string inf_a, std::string inf argp[a++] = strdup("-i"); argp[a++] = strdup(inf_a.c_str()); + for(FFSettings::const_iterator it = ffs.begin(); it != ffs.end(); ++it) { argp[a++] = strdup(it->first.c_str()); argp[a++] = strdup(it->second.c_str()); @@ -322,12 +328,36 @@ TranscodeFfmpeg::encode (std::string outfile, std::string inf_a, std::string inf argp[a++] = strdup("-metadata"); argp[a++] = format_metadata(it->first.c_str(), it->second.c_str()); } + if (m_lead_in != 0 && m_lead_out != 0) { + std::ostringstream osstream; + argp[a++] = strdup("-vf"); + osstream << X_("color=c=black:s=") << m_width << X_("x") << m_height << X_(":d=") << m_lead_in << X_(" [pre]; "); + osstream << X_("color=c=black:s=") << m_width << X_("x") << m_height << X_(":d=") << m_lead_out << X_(" [post]; "); + osstream << X_("[pre] [in] [post] concat=n=3"); + argp[a++] = strdup(osstream.str().c_str()); + } else if (m_lead_in != 0) { + std::ostringstream osstream; + argp[a++] = strdup("-vf"); + osstream << X_("color=c=black:s=") << m_width << X_("x") << m_height << X_(":d=") << m_lead_in << X_(" [pre]; "); + osstream << X_("[pre] [in] concat=n=2"); + argp[a++] = strdup(osstream.str().c_str()); + } else if (m_lead_out != 0) { + std::ostringstream osstream; + argp[a++] = strdup("-vf"); + osstream << X_("color=c=black:s=") << m_width << X_("x") << m_height << X_(":d=") << m_lead_out << X_(" [post]; "); + osstream << X_("[in] [post] concat=n=2"); + argp[a++] = strdup(osstream.str().c_str()); + } + if (map) { + std::ostringstream osstream; argp[a++] = strdup("-map"); - argp[a++] = strdup("0:0"); + osstream << X_("0:") << m_videoidx; + argp[a++] = strdup(osstream.str().c_str()); argp[a++] = strdup("-map"); argp[a++] = strdup("1:0"); } + argp[a++] = strdup("-y"); argp[a++] = strdup(outfile.c_str()); argp[a] = (char *)0; @@ -366,7 +396,7 @@ TranscodeFfmpeg::extract_audio (std::string outfile, ARDOUR::framecnt_t samplera argp[i++] = strdup(ffmpeg_exe.c_str()); argp[i++] = strdup("-i"); argp[i++] = strdup(infile.c_str()); -#if 0 // native samplerate -- use a3/SRC +#if 0 /* ffmpeg write original samplerate, use a3/SRC to resample */ argp[i++] = strdup("-ar"); argp[i] = (char*) calloc(7,sizeof(char)); snprintf(argp[i++], 7, "%"PRId64, samplerate); #endif @@ -523,10 +553,15 @@ TranscodeFfmpeg::ffmpegparse_v (std::string d, size_t /* s */) printf("ffmpeg: '%s'\n", d.c_str()); } #endif + Progress(0, 0); /* EMIT SIGNAL */ return; } ARDOUR::framecnt_t f = atol(d.substr(6).c_str()); - Progress(f, m_duration); /* EMIT SIGNAL */ + if (f == 0) { + Progress(0, 0); /* EMIT SIGNAL */ + } else { + Progress(f, m_duration); /* EMIT SIGNAL */ + } } #endif /* WITH_VIDEOTIMELINE */ diff --git a/gtk2_ardour/transcode_ffmpeg.h b/gtk2_ardour/transcode_ffmpeg.h index 2fd9f2f3f2..b809719f01 100644 --- a/gtk2_ardour/transcode_ffmpeg.h +++ b/gtk2_ardour/transcode_ffmpeg.h @@ -119,7 +119,9 @@ class TranscodeFfmpeg : public sigc::trackable */ void set_duration(ARDOUR::framecnt_t d) { m_duration = d; } + /* offset, lead-in/out are in seconds */ void set_avoffset(double av_offset) { m_avoffset = av_offset; } + void set_leadinout(double lead_in, double lead_out) { m_lead_in = lead_in; m_lead_out = lead_out; } #if 1 /* tentative debug mode */ @@ -138,7 +140,10 @@ class TranscodeFfmpeg : public sigc::trackable int m_height; std::string m_codec; + int m_videoidx; double m_avoffset; + double m_lead_in; + double m_lead_out; bool ffexecok; bool probeok; -- cgit v1.2.3