mirror of
https://github.com/clementine-player/Clementine
synced 2025-01-19 13:01:32 +01:00
Working transcoder. No error handling yet
This commit is contained in:
parent
d5c52f32d8
commit
6fa0f28ca4
@ -19,8 +19,12 @@
|
|||||||
|
|
||||||
#include <QtConcurrentMap>
|
#include <QtConcurrentMap>
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
|
#include <QEventLoop>
|
||||||
|
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
using boost::shared_ptr;
|
||||||
|
|
||||||
|
|
||||||
GstElement* TranscoderFormat::CreateElement(const QString &factory_name,
|
GstElement* TranscoderFormat::CreateElement(const QString &factory_name,
|
||||||
@ -30,11 +34,8 @@ GstElement* TranscoderFormat::CreateElement(const QString &factory_name,
|
|||||||
factory_name.toAscii().constData(),
|
factory_name.toAscii().constData(),
|
||||||
name.isNull() ? factory_name.toAscii().constData() : name.toAscii().constData());
|
name.isNull() ? factory_name.toAscii().constData() : name.toAscii().constData());
|
||||||
|
|
||||||
if (ret) {
|
if (ret && bin)
|
||||||
if (bin) gst_bin_add(GST_BIN(bin), ret);
|
gst_bin_add(GST_BIN(bin), ret);
|
||||||
} else {
|
|
||||||
gst_object_unref(GST_OBJECT(bin));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -45,7 +46,10 @@ GstElement* TranscoderFormat::CreateBin(const QStringList& elements) const {
|
|||||||
GstElement* last_element = NULL;
|
GstElement* last_element = NULL;
|
||||||
for (int i=0 ; i<elements.count() ; ++i) {
|
for (int i=0 ; i<elements.count() ; ++i) {
|
||||||
GstElement* element = CreateElement(elements[i], bin);
|
GstElement* element = CreateElement(elements[i], bin);
|
||||||
if (!element) return NULL;
|
if (!element) {
|
||||||
|
gst_object_unref(bin);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
// If this is the first element, make it the bin's sink
|
// If this is the first element, make it the bin's sink
|
||||||
@ -120,9 +124,103 @@ void Transcoder::Start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Transcoder::RunJob(const Job& job) {
|
void Transcoder::RunJob(const Job& job) {
|
||||||
// TODO
|
bool success = Transcode(job);
|
||||||
|
|
||||||
emit JobComplete(job.input, true);
|
emit JobComplete(job.input, success);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transcoder::NewPadCallback(GstElement*, GstPad* pad, gboolean, gpointer data) {
|
||||||
|
JobState* state = reinterpret_cast<JobState*>(data);
|
||||||
|
GstPad* const audiopad = gst_element_get_pad(state->convert_element, "sink");
|
||||||
|
|
||||||
|
if (GST_PAD_IS_LINKED(audiopad)) {
|
||||||
|
qDebug() << "audiopad is already linked. Unlinking old pad.";
|
||||||
|
gst_pad_unlink(audiopad, GST_PAD_PEER(audiopad));
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_pad_link(pad, audiopad);
|
||||||
|
gst_object_unref(audiopad);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean Transcoder::BusCallback(GstBus*, GstMessage* msg, gpointer data) {
|
||||||
|
JobState* state = reinterpret_cast<JobState*>(data);
|
||||||
|
|
||||||
|
switch (GST_MESSAGE_TYPE(msg)) {
|
||||||
|
case GST_MESSAGE_ERROR:
|
||||||
|
state->success = false;
|
||||||
|
state->event_loop->exit();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return GST_BUS_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
GstBusSyncReply Transcoder::BusCallbackSync(GstBus*, GstMessage* msg, gpointer data) {
|
||||||
|
JobState* state = reinterpret_cast<JobState*>(data);
|
||||||
|
switch (GST_MESSAGE_TYPE(msg)) {
|
||||||
|
case GST_MESSAGE_EOS:
|
||||||
|
state->event_loop->exit();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GST_MESSAGE_ERROR:
|
||||||
|
state->success = false;
|
||||||
|
state->event_loop->exit();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return GST_BUS_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Transcoder::Transcode(const Job &job) const {
|
||||||
|
// Create the pipeline
|
||||||
|
shared_ptr<GstElement> pipeline(gst_pipeline_new("pipeline"),
|
||||||
|
boost::bind(gst_object_unref, _1));
|
||||||
|
if (!pipeline) return false;
|
||||||
|
|
||||||
|
// Create all the elements
|
||||||
|
const TranscoderFormat* f = job.output_format;
|
||||||
|
GstElement* src = f->CreateElement("filesrc", pipeline.get());
|
||||||
|
GstElement* decode = f->CreateElement("decodebin", pipeline.get());
|
||||||
|
GstElement* convert = f->CreateElement("audioconvert", pipeline.get());
|
||||||
|
GstElement* encode = f->CreateEncodeBin();
|
||||||
|
GstElement* sink = f->CreateElement("filesink", pipeline.get());
|
||||||
|
|
||||||
|
if (!src || !decode || !convert || !encode || !sink)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Join them together
|
||||||
|
gst_bin_add(GST_BIN(pipeline.get()), encode);
|
||||||
|
gst_element_link(src, decode);
|
||||||
|
gst_element_link_many(convert, encode, sink, NULL);
|
||||||
|
|
||||||
|
// Set properties
|
||||||
|
g_object_set(src, "location", job.input.toLocal8Bit().constData(), NULL);
|
||||||
|
g_object_set(sink, "location", job.output.toLocal8Bit().constData(), NULL);
|
||||||
|
|
||||||
|
// Set callbacks
|
||||||
|
JobState state;
|
||||||
|
state.convert_element = convert;
|
||||||
|
state.event_loop.reset(new QEventLoop);
|
||||||
|
state.success = true;
|
||||||
|
|
||||||
|
g_signal_connect(decode, "new-decoded-pad", G_CALLBACK(NewPadCallback), &state);
|
||||||
|
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline.get())), BusCallbackSync, &state);
|
||||||
|
gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(pipeline.get())), BusCallback, &state);
|
||||||
|
|
||||||
|
// Start the pipeline and wait until it finishes
|
||||||
|
gst_element_set_state(pipeline.get(), GST_STATE_PLAYING);
|
||||||
|
|
||||||
|
state.event_loop->exec();
|
||||||
|
|
||||||
|
// Do this explicitly so that it's guaranteed to happen before the event
|
||||||
|
// loop is destroyed.
|
||||||
|
pipeline.reset();
|
||||||
|
|
||||||
|
return state.success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Transcoder::JobsFinished() {
|
void Transcoder::JobsFinished() {
|
||||||
|
@ -24,6 +24,10 @@
|
|||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
|
|
||||||
|
#include <boost/scoped_ptr.hpp>
|
||||||
|
|
||||||
|
class QEventLoop;
|
||||||
|
|
||||||
class Transcoder;
|
class Transcoder;
|
||||||
|
|
||||||
class TranscoderFormat {
|
class TranscoderFormat {
|
||||||
@ -36,7 +40,7 @@ class TranscoderFormat {
|
|||||||
virtual QString file_extension() const = 0;
|
virtual QString file_extension() const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual GstElement* CreateOutputBin() const = 0;
|
virtual GstElement* CreateEncodeBin() const = 0;
|
||||||
|
|
||||||
GstElement* CreateElement(const QString& factory_name, GstElement* bin = NULL,
|
GstElement* CreateElement(const QString& factory_name, GstElement* bin = NULL,
|
||||||
const QString& name = QString()) const;
|
const QString& name = QString()) const;
|
||||||
@ -65,13 +69,27 @@ class Transcoder : public QObject {
|
|||||||
void AllJobsComplete();
|
void AllJobsComplete();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// The description of a file to transcode - lives in the main thread.
|
||||||
struct Job {
|
struct Job {
|
||||||
QString input;
|
QString input;
|
||||||
QString output;
|
QString output;
|
||||||
const TranscoderFormat* output_format;
|
const TranscoderFormat* output_format;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// State held by a job and shared across gstreamer callbacks - lives in the
|
||||||
|
// job's thread.
|
||||||
|
struct JobState {
|
||||||
|
GstElement* convert_element;
|
||||||
|
boost::scoped_ptr<QEventLoop> event_loop;
|
||||||
|
bool success;
|
||||||
|
};
|
||||||
|
|
||||||
void RunJob(const Job& job);
|
void RunJob(const Job& job);
|
||||||
|
bool Transcode(const Job& job) const;
|
||||||
|
|
||||||
|
static void NewPadCallback(GstElement*, GstPad* pad, gboolean, gpointer data);
|
||||||
|
static gboolean BusCallback(GstBus*, GstMessage* msg, gpointer data);
|
||||||
|
static GstBusSyncReply BusCallbackSync(GstBus*, GstMessage* msg, gpointer data);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void JobsFinished();
|
void JobsFinished();
|
||||||
|
@ -16,22 +16,22 @@
|
|||||||
|
|
||||||
#include "transcoderformats.h"
|
#include "transcoderformats.h"
|
||||||
|
|
||||||
GstElement* OggVorbisTranscoder::CreateOutputBin() const {
|
GstElement* OggVorbisTranscoder::CreateEncodeBin() const {
|
||||||
return CreateBin(QStringList() << "vorbisenc" << "oggmux");
|
return CreateBin(QStringList() << "vorbisenc" << "oggmux");
|
||||||
}
|
}
|
||||||
|
|
||||||
GstElement* OggSpeexTranscoder::CreateOutputBin() const {
|
GstElement* OggSpeexTranscoder::CreateEncodeBin() const {
|
||||||
return CreateBin(QStringList() << "speexenc" << "oggmux");
|
return CreateBin(QStringList() << "speexenc" << "oggmux");
|
||||||
}
|
}
|
||||||
|
|
||||||
GstElement* FlacTranscoder::CreateOutputBin() const {
|
GstElement* FlacTranscoder::CreateEncodeBin() const {
|
||||||
return CreateElement("flacenc");
|
return CreateElement("flacenc");
|
||||||
}
|
}
|
||||||
|
|
||||||
GstElement* Mp3Transcoder::CreateOutputBin() const {
|
GstElement* Mp3Transcoder::CreateEncodeBin() const {
|
||||||
return CreateElement("lamemp3enc");
|
return CreateElement("lamemp3enc");
|
||||||
}
|
}
|
||||||
|
|
||||||
GstElement* AacTranscoder::CreateOutputBin() const {
|
GstElement* AacTranscoder::CreateEncodeBin() const {
|
||||||
return CreateElement("faac");
|
return CreateElement("faac");
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ class OggVorbisTranscoder : public TranscoderFormat {
|
|||||||
QString name() const { return "Ogg Vorbis"; }
|
QString name() const { return "Ogg Vorbis"; }
|
||||||
QString file_extension() const { return "ogg"; }
|
QString file_extension() const { return "ogg"; }
|
||||||
|
|
||||||
GstElement* CreateOutputBin() const;
|
GstElement* CreateEncodeBin() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class OggSpeexTranscoder : public TranscoderFormat {
|
class OggSpeexTranscoder : public TranscoderFormat {
|
||||||
@ -32,7 +32,7 @@ class OggSpeexTranscoder : public TranscoderFormat {
|
|||||||
QString name() const { return "Ogg Speex"; }
|
QString name() const { return "Ogg Speex"; }
|
||||||
QString file_extension() const { return "spx"; }
|
QString file_extension() const { return "spx"; }
|
||||||
|
|
||||||
GstElement* CreateOutputBin() const;
|
GstElement* CreateEncodeBin() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FlacTranscoder : public TranscoderFormat {
|
class FlacTranscoder : public TranscoderFormat {
|
||||||
@ -40,7 +40,7 @@ class FlacTranscoder : public TranscoderFormat {
|
|||||||
QString name() const { return "FLAC"; }
|
QString name() const { return "FLAC"; }
|
||||||
QString file_extension() const { return "flac"; }
|
QString file_extension() const { return "flac"; }
|
||||||
|
|
||||||
GstElement* CreateOutputBin() const;
|
GstElement* CreateEncodeBin() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Mp3Transcoder : public TranscoderFormat {
|
class Mp3Transcoder : public TranscoderFormat {
|
||||||
@ -48,7 +48,7 @@ class Mp3Transcoder : public TranscoderFormat {
|
|||||||
QString name() const { return "MP3"; }
|
QString name() const { return "MP3"; }
|
||||||
QString file_extension() const { return "mp3"; }
|
QString file_extension() const { return "mp3"; }
|
||||||
|
|
||||||
GstElement* CreateOutputBin() const;
|
GstElement* CreateEncodeBin() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AacTranscoder : public TranscoderFormat {
|
class AacTranscoder : public TranscoderFormat {
|
||||||
@ -56,7 +56,7 @@ class AacTranscoder : public TranscoderFormat {
|
|||||||
QString name() const { return "AAC"; }
|
QString name() const { return "AAC"; }
|
||||||
QString file_extension() const { return "aac"; }
|
QString file_extension() const { return "aac"; }
|
||||||
|
|
||||||
GstElement* CreateOutputBin() const;
|
GstElement* CreateEncodeBin() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WindowsMediaTranscoder : public TranscoderFormat {
|
class WindowsMediaTranscoder : public TranscoderFormat {
|
||||||
@ -64,7 +64,7 @@ class WindowsMediaTranscoder : public TranscoderFormat {
|
|||||||
QString name() const { return "Windows Media"; }
|
QString name() const { return "Windows Media"; }
|
||||||
QString file_extension() const { return "wma"; }
|
QString file_extension() const { return "wma"; }
|
||||||
|
|
||||||
GstElement* CreateOutputBin() const;
|
GstElement* CreateEncodeBin() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TRANSCODERFORMATS_H
|
#endif // TRANSCODERFORMATS_H
|
||||||
|
Loading…
Reference in New Issue
Block a user