mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-11 00:56:33 +01:00
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.
This commit is contained in:
parent
e4cbd5115e
commit
df72e1ba4f
8 changed files with 144 additions and 4 deletions
|
|
@ -43,7 +43,8 @@ class LIBARDOUR_API ExportFormatBase {
|
||||||
|
|
||||||
enum Type {
|
enum Type {
|
||||||
T_None = 0,
|
T_None = 0,
|
||||||
T_Sndfile
|
T_Sndfile,
|
||||||
|
T_FFMPEG
|
||||||
};
|
};
|
||||||
|
|
||||||
enum FormatId {
|
enum FormatId {
|
||||||
|
|
@ -56,7 +57,8 @@ class LIBARDOUR_API ExportFormatBase {
|
||||||
F_IRCAM = SF_FORMAT_IRCAM,
|
F_IRCAM = SF_FORMAT_IRCAM,
|
||||||
F_RAW = SF_FORMAT_RAW,
|
F_RAW = SF_FORMAT_RAW,
|
||||||
F_FLAC = SF_FORMAT_FLAC,
|
F_FLAC = SF_FORMAT_FLAC,
|
||||||
F_Ogg = SF_FORMAT_OGG
|
F_Ogg = SF_FORMAT_OGG,
|
||||||
|
F_FFMPEG = 0xF10000
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Endianness {
|
enum Endianness {
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,17 @@ class LIBARDOUR_API ExportFormatBWF : public ExportFormat, public HasSampleForma
|
||||||
virtual bool has_broadcast_info () const { return true; }
|
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
|
} // namespace ARDOUR
|
||||||
|
|
||||||
#endif /* __ardour_export_formats__ */
|
#endif /* __ardour_export_formats__ */
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ namespace AudioGrapher {
|
||||||
template <typename T> class SampleFormatConverter;
|
template <typename T> class SampleFormatConverter;
|
||||||
template <typename T> class Interleaver;
|
template <typename T> class Interleaver;
|
||||||
template <typename T> class SndfileWriter;
|
template <typename T> class SndfileWriter;
|
||||||
|
template <typename T> class CmdPipeWriter;
|
||||||
template <typename T> class SilenceTrimmer;
|
template <typename T> class SilenceTrimmer;
|
||||||
template <typename T> class TmpFile;
|
template <typename T> class TmpFile;
|
||||||
template <typename T> class Threader;
|
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<int> > IntWriterPtr;
|
||||||
typedef boost::shared_ptr<AudioGrapher::SndfileWriter<short> > ShortWriterPtr;
|
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::SndfileWriter<T> > & writer);
|
||||||
|
template<typename T> void init_writer (boost::shared_ptr<AudioGrapher::CmdPipeWriter<T> > & writer);
|
||||||
|
|
||||||
void copy_files (std::string orig_path);
|
void copy_files (std::string orig_path);
|
||||||
|
|
||||||
FileSpec config;
|
FileSpec config;
|
||||||
|
|
@ -115,6 +120,7 @@ class LIBARDOUR_API ExportGraphBuilder
|
||||||
FloatWriterPtr float_writer;
|
FloatWriterPtr float_writer;
|
||||||
IntWriterPtr int_writer;
|
IntWriterPtr int_writer;
|
||||||
ShortWriterPtr short_writer;
|
ShortWriterPtr short_writer;
|
||||||
|
FloatPipePtr pipe_writer;
|
||||||
};
|
};
|
||||||
|
|
||||||
// sample format converter
|
// sample format converter
|
||||||
|
|
|
||||||
|
|
@ -577,6 +577,7 @@ setup_enum_writer ()
|
||||||
|
|
||||||
REGISTER_CLASS_ENUM (ExportFormatBase, T_None);
|
REGISTER_CLASS_ENUM (ExportFormatBase, T_None);
|
||||||
REGISTER_CLASS_ENUM (ExportFormatBase, T_Sndfile);
|
REGISTER_CLASS_ENUM (ExportFormatBase, T_Sndfile);
|
||||||
|
REGISTER_CLASS_ENUM (ExportFormatBase, T_FFMPEG);
|
||||||
REGISTER (_ExportFormatBase_Type);
|
REGISTER (_ExportFormatBase_Type);
|
||||||
|
|
||||||
REGISTER_CLASS_ENUM (ExportFormatBase, F_None);
|
REGISTER_CLASS_ENUM (ExportFormatBase, F_None);
|
||||||
|
|
@ -589,6 +590,7 @@ setup_enum_writer ()
|
||||||
REGISTER_CLASS_ENUM (ExportFormatBase, F_FLAC);
|
REGISTER_CLASS_ENUM (ExportFormatBase, F_FLAC);
|
||||||
REGISTER_CLASS_ENUM (ExportFormatBase, F_Ogg);
|
REGISTER_CLASS_ENUM (ExportFormatBase, F_Ogg);
|
||||||
REGISTER_CLASS_ENUM (ExportFormatBase, F_CAF);
|
REGISTER_CLASS_ENUM (ExportFormatBase, F_CAF);
|
||||||
|
REGISTER_CLASS_ENUM (ExportFormatBase, F_FFMPEG);
|
||||||
REGISTER (_ExportFormatBase_FormatId);
|
REGISTER (_ExportFormatBase_FormatId);
|
||||||
|
|
||||||
REGISTER_CLASS_ENUM (ExportFormatBase, E_FileDefault);
|
REGISTER_CLASS_ENUM (ExportFormatBase, E_FileDefault);
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ardour/export_format_manager.h"
|
#include "ardour/export_format_manager.h"
|
||||||
|
#include "ardour/filesystem_paths.h"
|
||||||
|
|
||||||
#include "ardour/export_format_specification.h"
|
#include "ardour/export_format_specification.h"
|
||||||
#include "ardour/export_format_compatibility.h"
|
#include "ardour/export_format_compatibility.h"
|
||||||
|
|
@ -210,6 +211,12 @@ ExportFormatManager::init_formats ()
|
||||||
f_ptr.reset (new ExportFormatFLAC ());
|
f_ptr.reset (new ExportFormatFLAC ());
|
||||||
add_format (f_ptr);
|
add_format (f_ptr);
|
||||||
} catch (ExportFormatIncompatible & e) {}
|
} catch (ExportFormatIncompatible & e) {}
|
||||||
|
|
||||||
|
std::string unused;
|
||||||
|
if (ArdourVideoToolPaths::transcoder_exe (unused, unused)) {
|
||||||
|
f_ptr.reset (new ExportFormatFFMPEG ("MP3", "mp3"));
|
||||||
|
add_format (f_ptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -365,4 +365,37 @@ ExportFormatBWF::set_compatibility_state (ExportFormatCompatibility const & comp
|
||||||
return compatible;
|
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
|
}; // namespace ARDOUR
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
#include "audiographer/process_context.h"
|
#include "audiographer/process_context.h"
|
||||||
#include "audiographer/general/chunker.h"
|
#include "audiographer/general/chunker.h"
|
||||||
|
#include "audiographer/general/cmdpipe_writer.h"
|
||||||
#include "audiographer/general/interleaver.h"
|
#include "audiographer/general/interleaver.h"
|
||||||
#include "audiographer/general/normalizer.h"
|
#include "audiographer/general/normalizer.h"
|
||||||
#include "audiographer/general/analyser.h"
|
#include "audiographer/general/analyser.h"
|
||||||
|
|
@ -42,11 +43,14 @@
|
||||||
|
|
||||||
#include "ardour/audioengine.h"
|
#include "ardour/audioengine.h"
|
||||||
#include "ardour/export_channel_configuration.h"
|
#include "ardour/export_channel_configuration.h"
|
||||||
|
#include "ardour/export_failed.h"
|
||||||
#include "ardour/export_filename.h"
|
#include "ardour/export_filename.h"
|
||||||
#include "ardour/export_format_specification.h"
|
#include "ardour/export_format_specification.h"
|
||||||
#include "ardour/export_timespan.h"
|
#include "ardour/export_timespan.h"
|
||||||
|
#include "ardour/filesystem_paths.h"
|
||||||
#include "ardour/session_directory.h"
|
#include "ardour/session_directory.h"
|
||||||
#include "ardour/sndfile_helpers.h"
|
#include "ardour/sndfile_helpers.h"
|
||||||
|
#include "ardour/system_exec.h"
|
||||||
|
|
||||||
#include "pbd/file_utils.h"
|
#include "pbd/file_utils.h"
|
||||||
#include "pbd/cpus.h"
|
#include "pbd/cpus.h"
|
||||||
|
|
@ -212,8 +216,13 @@ boost::shared_ptr<AudioGrapher::Sink<Sample> >
|
||||||
ExportGraphBuilder::Encoder::init (FileSpec const & new_config)
|
ExportGraphBuilder::Encoder::init (FileSpec const & new_config)
|
||||||
{
|
{
|
||||||
config = new_config;
|
config = new_config;
|
||||||
init_writer (float_writer);
|
if (config.format->format_id() == ExportFormatBase::F_FFMPEG) {
|
||||||
return float_writer;
|
init_writer (pipe_writer);
|
||||||
|
return pipe_writer;
|
||||||
|
} else {
|
||||||
|
init_writer (float_writer);
|
||||||
|
return float_writer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
|
@ -257,6 +266,10 @@ ExportGraphBuilder::Encoder::destroy_writer (bool delete_out_file)
|
||||||
short_writer->close ();
|
short_writer->close ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pipe_writer) {
|
||||||
|
pipe_writer->close ();
|
||||||
|
}
|
||||||
|
|
||||||
if (std::remove(writer_filename.c_str() ) != 0) {
|
if (std::remove(writer_filename.c_str() ) != 0) {
|
||||||
std::cout << "Encoder::destroy_writer () : Error removing file: " << strerror(errno) << std::endl;
|
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 ();
|
float_writer.reset ();
|
||||||
int_writer.reset ();
|
int_writer.reset ();
|
||||||
short_writer.reset ();
|
short_writer.reset ();
|
||||||
|
pipe_writer.reset ();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
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));
|
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
|
void
|
||||||
ExportGraphBuilder::Encoder::copy_files (std::string orig_path)
|
ExportGraphBuilder::Encoder::copy_files (std::string orig_path)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -954,6 +954,8 @@ ExportProfileManager::check_format (ExportFormatSpecPtr format, uint32_t channel
|
||||||
switch (format->type()) {
|
switch (format->type()) {
|
||||||
case ExportFormatBase::T_Sndfile:
|
case ExportFormatBase::T_Sndfile:
|
||||||
return check_sndfile_format (format, channels);
|
return check_sndfile_format (format, channels);
|
||||||
|
case ExportFormatBase::T_FFMPEG:
|
||||||
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw ExportFailed (X_("Invalid format given for ExportFileFactory::check!"));
|
throw ExportFailed (X_("Invalid format given for ExportFileFactory::check!"));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue