Clementine-audio-player-Mac.../src/remote/streampipeline.cpp

126 lines
3.7 KiB
C++

#include "streampipeline.h"
#include <gst/app/gstappsink.h>
#include "core/logging.h"
const char* StreamPipeline::kPipeline =
"audioconvert ! vorbisenc ! oggmux";
StreamPipeline::StreamPipeline(QObject* parent)
: QIODevice(parent),
pipeline_(NULL) {
}
StreamPipeline::~StreamPipeline() {
gst_object_unref(pipeline_);
}
void StreamPipeline::Init(const Song& song) {
pipeline_ = gst_pipeline_new("pipeline");
GstElement* uridecodebin = CreateElement("uridecodebin", pipeline_);
qLog(Debug) << "Streaming:" << song.url();
g_object_set(
G_OBJECT(uridecodebin), "uri", song.url().toString().toUtf8().constData(), NULL);
g_signal_connect(
G_OBJECT(uridecodebin), "pad-added", G_CALLBACK(NewPadCallback), this);
GError* error = NULL;
convert_bin_ = gst_parse_bin_from_description(kPipeline, TRUE, &error);
gst_bin_add(GST_BIN(pipeline_), convert_bin_);
gst_element_set_state(uridecodebin, GST_STATE_PLAYING);
app_sink_ = CreateElement("appsink", pipeline_);
g_object_set(G_OBJECT(app_sink_), "emit-signals", TRUE, NULL);
g_signal_connect(
G_OBJECT(app_sink_), "new-buffer", G_CALLBACK(NewBufferCallback), this);
qLog(Debug) << "Linking appsink:" << gst_element_link(convert_bin_, app_sink_);
gst_bus_set_sync_handler(
gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallbackSync, this);
bus_cb_id_ = gst_bus_add_watch(
gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this);
}
qint64 StreamPipeline::readData(char* data, qint64 max_size) {
QByteArray ret = buffer_.left(max_size);
buffer_ = buffer_.mid(ret.size());
memcpy(data, ret.constData(), ret.size());
return ret.size();
}
qint64 StreamPipeline::writeData(const char* data, qint64 max_size) {
return -1;
}
GstElement* StreamPipeline::CreateElement(
const QString& factory_name, GstElement* bin) {
GstElement* ret = gst_element_factory_make(
factory_name.toAscii().constData(),
factory_name.toAscii().constData());
qLog(Debug) << "Created element:" << factory_name << ret;
if (ret && bin) {
gst_bin_add(GST_BIN(bin), ret);
}
return ret;
}
GstBusSyncReply StreamPipeline::BusCallbackSync(
GstBus*, GstMessage* msg, gpointer self) {
reinterpret_cast<StreamPipeline*>(self)->HandleMessage(msg);
return GST_BUS_PASS;
}
gboolean StreamPipeline::BusCallback(GstBus*, GstMessage* msg, gpointer self) {
reinterpret_cast<StreamPipeline*>(self)->HandleMessage(msg);
return FALSE;
}
void StreamPipeline::HandleMessage(GstMessage* msg) {
switch (GST_MESSAGE_TYPE(msg)) {
case GST_MESSAGE_EOS:
qLog(Debug) << "Finished streaming";
break;
case GST_MESSAGE_ERROR: {
GError* error;
gchar* debugs;
gst_message_parse_error(msg, &error, &debugs);
QString message = QString::fromLocal8Bit(error->message);
g_error_free(error);
free(debugs);
qLog(Debug) << message;
break;
}
default:
break;
}
}
void StreamPipeline::NewBufferCallback(GstElement* app_sink, gpointer self) {
StreamPipeline* me = reinterpret_cast<StreamPipeline*>(self);
GstBuffer* buffer = gst_app_sink_pull_buffer((GstAppSink*)app_sink);
me->buffer_.append((const char*)buffer->data, buffer->size);
gst_buffer_unref(buffer);
emit me->readyRead();
}
void StreamPipeline::NewPadCallback(
GstElement* decodebin, GstPad* pad, gpointer self) {
qLog(Debug) << "Linking pads";
StreamPipeline* me = reinterpret_cast<StreamPipeline*>(self);
GstPad* const audiopad = gst_element_get_pad(me->convert_bin_, "sink");
gst_pad_link(pad, audiopad);
gst_object_unref(audiopad);
QMetaObject::invokeMethod(me, "StartPipeline", Qt::QueuedConnection);
}
void StreamPipeline::StartPipeline() {
gst_element_set_state(pipeline_, GST_STATE_PLAYING);
}