diff options
author | Robin Gareus <robin@gareus.org> | 2018-11-19 05:21:17 +0100 |
---|---|---|
committer | Robin Gareus <robin@gareus.org> | 2018-11-19 05:21:17 +0100 |
commit | df72e1ba4f7e7dc0d67ea6c0fbdf157ee3777275 (patch) | |
tree | 035abd922b71e595eca6e466bc65a68578603b9b /libs/ardour | |
parent | e4cbd5115e34ff99aa8c85b70870e6ca00ea14d6 (diff) |
Initial backend support for external export encoder
This adds an experimental pipe to ffmpeg to encode mp3. Currently
quality is hardcoded and various aspects remain to be implemented.
However, it is sufficient for initial testing.
Diffstat (limited to 'libs/ardour')
-rw-r--r-- | libs/ardour/ardour/export_format_base.h | 6 | ||||
-rw-r--r-- | libs/ardour/ardour/export_formats.h | 11 | ||||
-rw-r--r-- | libs/ardour/ardour/export_graph_builder.h | 6 | ||||
-rw-r--r-- | libs/ardour/enums.cc | 2 | ||||
-rw-r--r-- | libs/ardour/export_format_manager.cc | 7 | ||||
-rw-r--r-- | libs/ardour/export_formats.cc | 33 | ||||
-rw-r--r-- | libs/ardour/export_graph_builder.cc | 81 | ||||
-rw-r--r-- | libs/ardour/export_profile_manager.cc | 2 |
8 files changed, 144 insertions, 4 deletions
diff --git a/libs/ardour/ardour/export_format_base.h b/libs/ardour/ardour/export_format_base.h index ee2739eee1..04566f081e 100644 --- a/libs/ardour/ardour/export_format_base.h +++ b/libs/ardour/ardour/export_format_base.h @@ -43,7 +43,8 @@ class LIBARDOUR_API ExportFormatBase { enum Type { T_None = 0, - T_Sndfile + T_Sndfile, + T_FFMPEG }; enum FormatId { @@ -56,7 +57,8 @@ class LIBARDOUR_API ExportFormatBase { F_IRCAM = SF_FORMAT_IRCAM, F_RAW = SF_FORMAT_RAW, F_FLAC = SF_FORMAT_FLAC, - F_Ogg = SF_FORMAT_OGG + F_Ogg = SF_FORMAT_OGG, + F_FFMPEG = 0xF10000 }; enum Endianness { diff --git a/libs/ardour/ardour/export_formats.h b/libs/ardour/ardour/export_formats.h index 4c9c205725..8e51827b89 100644 --- a/libs/ardour/ardour/export_formats.h +++ b/libs/ardour/ardour/export_formats.h @@ -210,6 +210,17 @@ class LIBARDOUR_API ExportFormatBWF : public ExportFormat, public HasSampleForma virtual bool has_broadcast_info () const { return true; } }; + +class LIBARDOUR_API ExportFormatFFMPEG : public ExportFormat { + public: + ExportFormatFFMPEG (std::string const& name, std::string const& ext); + ~ExportFormatFFMPEG () {}; + + bool set_compatibility_state (ExportFormatCompatibility const & compatibility); + Type get_type () const { return T_FFMPEG; } + SampleFormat get_explicit_sample_format () const { return SF_Float; } +}; + } // namespace ARDOUR #endif /* __ardour_export_formats__ */ diff --git a/libs/ardour/ardour/export_graph_builder.h b/libs/ardour/ardour/export_graph_builder.h index ebc3fdadfc..00cf8226be 100644 --- a/libs/ardour/ardour/export_graph_builder.h +++ b/libs/ardour/ardour/export_graph_builder.h @@ -39,6 +39,7 @@ namespace AudioGrapher { template <typename T> class SampleFormatConverter; template <typename T> class Interleaver; template <typename T> class SndfileWriter; + template <typename T> class CmdPipeWriter; template <typename T> class SilenceTrimmer; template <typename T> class TmpFile; template <typename T> class Threader; @@ -102,7 +103,11 @@ class LIBARDOUR_API ExportGraphBuilder typedef boost::shared_ptr<AudioGrapher::SndfileWriter<int> > IntWriterPtr; typedef boost::shared_ptr<AudioGrapher::SndfileWriter<short> > ShortWriterPtr; + typedef boost::shared_ptr<AudioGrapher::CmdPipeWriter<Sample> > FloatPipePtr; + template<typename T> void init_writer (boost::shared_ptr<AudioGrapher::SndfileWriter<T> > & writer); + template<typename T> void init_writer (boost::shared_ptr<AudioGrapher::CmdPipeWriter<T> > & writer); + void copy_files (std::string orig_path); FileSpec config; @@ -115,6 +120,7 @@ class LIBARDOUR_API ExportGraphBuilder FloatWriterPtr float_writer; IntWriterPtr int_writer; ShortWriterPtr short_writer; + FloatPipePtr pipe_writer; }; // sample format converter diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index 853391c3d9..dd7c00f25f 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -577,6 +577,7 @@ setup_enum_writer () REGISTER_CLASS_ENUM (ExportFormatBase, T_None); REGISTER_CLASS_ENUM (ExportFormatBase, T_Sndfile); + REGISTER_CLASS_ENUM (ExportFormatBase, T_FFMPEG); REGISTER (_ExportFormatBase_Type); REGISTER_CLASS_ENUM (ExportFormatBase, F_None); @@ -589,6 +590,7 @@ setup_enum_writer () REGISTER_CLASS_ENUM (ExportFormatBase, F_FLAC); REGISTER_CLASS_ENUM (ExportFormatBase, F_Ogg); REGISTER_CLASS_ENUM (ExportFormatBase, F_CAF); + REGISTER_CLASS_ENUM (ExportFormatBase, F_FFMPEG); REGISTER (_ExportFormatBase_FormatId); REGISTER_CLASS_ENUM (ExportFormatBase, E_FileDefault); diff --git a/libs/ardour/export_format_manager.cc b/libs/ardour/export_format_manager.cc index 2b5fc1868b..3581fb890e 100644 --- a/libs/ardour/export_format_manager.cc +++ b/libs/ardour/export_format_manager.cc @@ -19,6 +19,7 @@ */ #include "ardour/export_format_manager.h" +#include "ardour/filesystem_paths.h" #include "ardour/export_format_specification.h" #include "ardour/export_format_compatibility.h" @@ -210,6 +211,12 @@ ExportFormatManager::init_formats () f_ptr.reset (new ExportFormatFLAC ()); add_format (f_ptr); } catch (ExportFormatIncompatible & e) {} + + std::string unused; + if (ArdourVideoToolPaths::transcoder_exe (unused, unused)) { + f_ptr.reset (new ExportFormatFFMPEG ("MP3", "mp3")); + add_format (f_ptr); + } } void diff --git a/libs/ardour/export_formats.cc b/libs/ardour/export_formats.cc index 7fa30c6b51..0ab02046c3 100644 --- a/libs/ardour/export_formats.cc +++ b/libs/ardour/export_formats.cc @@ -365,4 +365,37 @@ ExportFormatBWF::set_compatibility_state (ExportFormatCompatibility const & comp return compatible; } + +/*** FFMPEG Pipe ***/ + +ExportFormatFFMPEG::ExportFormatFFMPEG (std::string const& name, std::string const& ext) +{ + set_name (name); + set_format_id (F_FFMPEG); + sample_formats.insert (SF_Float); + + add_sample_rate (SR_22_05); + add_sample_rate (SR_44_1); + add_sample_rate (SR_48); + add_sample_rate (SR_88_2); + add_sample_rate (SR_96); + add_sample_rate (SR_176_4); + add_sample_rate (SR_192); + add_sample_rate (SR_Session); + + add_endianness (E_Little); + + set_extension (ext); + set_quality (Q_LossyCompression); +} + +bool +ExportFormatFFMPEG::set_compatibility_state (ExportFormatCompatibility const & compatibility) +{ + bool compatible = compatibility.has_format (F_FFMPEG); + set_compatible (compatible); + return compatible; +} + + }; // namespace ARDOUR diff --git a/libs/ardour/export_graph_builder.cc b/libs/ardour/export_graph_builder.cc index b89e73d0ed..b2eaafce21 100644 --- a/libs/ardour/export_graph_builder.cc +++ b/libs/ardour/export_graph_builder.cc @@ -26,6 +26,7 @@ #include "audiographer/process_context.h" #include "audiographer/general/chunker.h" +#include "audiographer/general/cmdpipe_writer.h" #include "audiographer/general/interleaver.h" #include "audiographer/general/normalizer.h" #include "audiographer/general/analyser.h" @@ -42,11 +43,14 @@ #include "ardour/audioengine.h" #include "ardour/export_channel_configuration.h" +#include "ardour/export_failed.h" #include "ardour/export_filename.h" #include "ardour/export_format_specification.h" #include "ardour/export_timespan.h" +#include "ardour/filesystem_paths.h" #include "ardour/session_directory.h" #include "ardour/sndfile_helpers.h" +#include "ardour/system_exec.h" #include "pbd/file_utils.h" #include "pbd/cpus.h" @@ -212,8 +216,13 @@ boost::shared_ptr<AudioGrapher::Sink<Sample> > ExportGraphBuilder::Encoder::init (FileSpec const & new_config) { config = new_config; - init_writer (float_writer); - return float_writer; + if (config.format->format_id() == ExportFormatBase::F_FFMPEG) { + init_writer (pipe_writer); + return pipe_writer; + } else { + init_writer (float_writer); + return float_writer; + } } template <> @@ -257,6 +266,10 @@ ExportGraphBuilder::Encoder::destroy_writer (bool delete_out_file) short_writer->close (); } + if (pipe_writer) { + pipe_writer->close (); + } + if (std::remove(writer_filename.c_str() ) != 0) { std::cout << "Encoder::destroy_writer () : Error removing file: " << strerror(errno) << std::endl; } @@ -265,6 +278,7 @@ ExportGraphBuilder::Encoder::destroy_writer (bool delete_out_file) float_writer.reset (); int_writer.reset (); short_writer.reset (); + pipe_writer.reset (); } bool @@ -293,6 +307,69 @@ ExportGraphBuilder::Encoder::init_writer (boost::shared_ptr<AudioGrapher::Sndfil writer->FileWritten.connect_same_thread (copy_files_connection, boost::bind (&ExportGraphBuilder::Encoder::copy_files, this, _1)); } +template<typename T> +void +ExportGraphBuilder::Encoder::init_writer (boost::shared_ptr<AudioGrapher::CmdPipeWriter<T> > & writer) +{ + unsigned channels = config.channel_config->get_n_chans(); + config.filename->set_channel_config(config.channel_config); + writer_filename = config.filename->get_path (config.format); + + std::string ffmpeg_exe; + std::string unused; + + if (!ArdourVideoToolPaths::transcoder_exe (ffmpeg_exe, unused)) { + throw ExportFailed ("External encoder (ffmpeg) is not available."); + } + + int quality = 3; // TODO get from config.format + + int a=0; + char **argp = (char**) calloc (100, sizeof(char*)); + char tmp[64]; + argp[a++] = strdup(ffmpeg_exe.c_str()); + argp[a++] = strdup ("-f"); + argp[a++] = strdup ("f32le"); + argp[a++] = strdup ("-acodec"); + argp[a++] = strdup ("pcm_f32le"); + argp[a++] = strdup ("-ac"); + snprintf (tmp, sizeof(tmp), "%d", channels); + argp[a++] = strdup (tmp); + argp[a++] = strdup ("-ar"); + snprintf (tmp, sizeof(tmp), "%d", config.format->sample_rate()); + argp[a++] = strdup (tmp); + argp[a++] = strdup ("-i"); + argp[a++] = strdup ("pipe:0"); + + argp[a++] = strdup ("-y"); + if (quality < 10) { + /* variable rate, lower is better */ + snprintf (tmp, sizeof(tmp), "%d", quality); + argp[a++] = strdup ("-q:a"); argp[a++] = strdup (tmp); + } else { + /* fixed bitrate, higher is better */ + snprintf (tmp, sizeof(tmp), "%dk", quality); // eg. "192k" + argp[a++] = strdup ("-b:a"); argp[a++] = strdup (tmp); + } + + /* TODO: add SessionMetadata::Metadata() + * see gtk2_ardour/export_video_dialog.cc + * and gtk2_ardour/transcode_ffmpeg.cc + */ + + argp[a++] = strdup (writer_filename.c_str()); + argp[a] = (char *)0; + + /* argp is free()d in ~SystemExec, + * SystemExec is deleted when writer is destroyed */ + ARDOUR::SystemExec* exec = new ARDOUR::SystemExec (ffmpeg_exe, argp); + if (exec->start(0)) { + throw ExportFailed ("External encoder (ffmpeg) cannot be started."); + } + writer.reset (new AudioGrapher::CmdPipeWriter<T> (exec, writer_filename)); + writer->FileWritten.connect_same_thread (copy_files_connection, boost::bind (&ExportGraphBuilder::Encoder::copy_files, this, _1)); +} + void ExportGraphBuilder::Encoder::copy_files (std::string orig_path) { diff --git a/libs/ardour/export_profile_manager.cc b/libs/ardour/export_profile_manager.cc index f99755e158..28cccde3cd 100644 --- a/libs/ardour/export_profile_manager.cc +++ b/libs/ardour/export_profile_manager.cc @@ -954,6 +954,8 @@ ExportProfileManager::check_format (ExportFormatSpecPtr format, uint32_t channel switch (format->type()) { case ExportFormatBase::T_Sndfile: return check_sndfile_format (format, channels); + case ExportFormatBase::T_FFMPEG: + return true; default: throw ExportFailed (X_("Invalid format given for ExportFileFactory::check!")); |