First pass at Gstreamer 1.0 porting.
This at least compiles against gstreamer 1.2. Things that work: * Playing audio * Automatically completing tags Things that don't work * Spotify * Moodbar Things I haven't tested * Transcoding
This commit is contained in:
parent
88bb606082
commit
56c949815b
|
@ -66,11 +66,11 @@ pkg_check_modules(CHROMAPRINT libchromaprint)
|
||||||
pkg_check_modules(GIO gio-2.0)
|
pkg_check_modules(GIO gio-2.0)
|
||||||
pkg_check_modules(GLIB glib-2.0)
|
pkg_check_modules(GLIB glib-2.0)
|
||||||
pkg_check_modules(GOBJECT gobject-2.0)
|
pkg_check_modules(GOBJECT gobject-2.0)
|
||||||
pkg_check_modules(GSTREAMER gstreamer-0.10)
|
pkg_check_modules(GSTREAMER gstreamer-1.0)
|
||||||
pkg_check_modules(GSTREAMER_APP gstreamer-app-0.10)
|
pkg_check_modules(GSTREAMER_APP gstreamer-app-1.0)
|
||||||
pkg_check_modules(GSTREAMER_BASE gstreamer-base-0.10)
|
pkg_check_modules(GSTREAMER_BASE gstreamer-base-1.0)
|
||||||
pkg_check_modules(GSTREAMER_CDDA gstreamer-cdda-0.10)
|
#pkg_check_modules(GSTREAMER_CDDA gstreamer-cdda-0.10)
|
||||||
pkg_check_modules(GSTREAMER_TAG gstreamer-tag-0.10)
|
pkg_check_modules(GSTREAMER_TAG gstreamer-tag-1.0)
|
||||||
pkg_check_modules(INDICATEQT indicate-qt)
|
pkg_check_modules(INDICATEQT indicate-qt)
|
||||||
pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92)
|
pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92)
|
||||||
pkg_check_modules(LIBMTP libmtp>=1.0)
|
pkg_check_modules(LIBMTP libmtp>=1.0)
|
||||||
|
|
|
@ -91,17 +91,14 @@ bool MediaPipeline::Init(int sample_rate, int channels) {
|
||||||
gst_app_src_set_callbacks(appsrc_, &callbacks, this, NULL);
|
gst_app_src_set_callbacks(appsrc_, &callbacks, this, NULL);
|
||||||
|
|
||||||
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
|
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
|
||||||
const int endianness = G_BIG_ENDIAN;
|
static const char* format = "S16BE";
|
||||||
#elif Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
#elif Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||||
const int endianness = G_LITTLE_ENDIAN;
|
static const char* format = "S16LE";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Set caps
|
// Set caps
|
||||||
GstCaps* caps = gst_caps_new_simple("audio/x-raw-int",
|
GstCaps* caps = gst_caps_new_simple("audio/x-raw",
|
||||||
"endianness", G_TYPE_INT, endianness,
|
"format", G_TYPE_STRING, format,
|
||||||
"signed", G_TYPE_BOOLEAN, TRUE,
|
|
||||||
"width", G_TYPE_INT, 16,
|
|
||||||
"depth", G_TYPE_INT, 16,
|
|
||||||
"rate", G_TYPE_INT, sample_rate,
|
"rate", G_TYPE_INT, sample_rate,
|
||||||
"channels", G_TYPE_INT, channels,
|
"channels", G_TYPE_INT, channels,
|
||||||
NULL);
|
NULL);
|
||||||
|
@ -123,8 +120,12 @@ void MediaPipeline::WriteData(const char* data, qint64 length) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
GstBuffer* buffer = gst_buffer_new_and_alloc(length);
|
GstBuffer* buffer = gst_buffer_new_and_alloc(length);
|
||||||
|
GstMapInfo map_info;
|
||||||
|
gst_buffer_map(buffer, &map_info, GST_MAP_WRITE);
|
||||||
|
|
||||||
memcpy(GST_BUFFER_DATA(buffer), data, length);
|
memcpy(map_info.data, data, length);
|
||||||
|
|
||||||
|
gst_buffer_unmap(buffer, &map_info);
|
||||||
|
|
||||||
GST_BUFFER_OFFSET(buffer) = offset_bytes_;
|
GST_BUFFER_OFFSET(buffer) = offset_bytes_;
|
||||||
GST_BUFFER_TIMESTAMP(buffer) = offset_bytes_ * kNsecPerSec / byte_rate_;
|
GST_BUFFER_TIMESTAMP(buffer) = offset_bytes_ * kNsecPerSec / byte_rate_;
|
||||||
|
|
|
@ -40,7 +40,8 @@ static Level sDefaultLevel = Level_Debug;
|
||||||
static QMap<QString, Level>* sClassLevels = NULL;
|
static QMap<QString, Level>* sClassLevels = NULL;
|
||||||
static QIODevice* sNullDevice = NULL;
|
static QIODevice* sNullDevice = NULL;
|
||||||
|
|
||||||
const char* kDefaultLogLevels = "GstEnginePipeline:2,*:3";
|
//const char* kDefaultLogLevels = "GstEnginePipeline:2,*:3";
|
||||||
|
const char* kDefaultLogLevels = "*:3";
|
||||||
|
|
||||||
static const char* kMessageHandlerMagic = "__logging_message__";
|
static const char* kMessageHandlerMagic = "__logging_message__";
|
||||||
static const int kMessageHandlerMagicLength = strlen(kMessageHandlerMagic);
|
static const int kMessageHandlerMagicLength = strlen(kMessageHandlerMagic);
|
||||||
|
|
|
@ -89,20 +89,28 @@ static GstStaticPadTemplate src_factory
|
||||||
( SPECTRUM_FREQ_CAPS )
|
( SPECTRUM_FREQ_CAPS )
|
||||||
);
|
);
|
||||||
|
|
||||||
GST_BOILERPLATE (GstFFTWSpectrum, gst_fftwspectrum, GstElement,
|
G_DEFINE_TYPE(GstFFTWSpectrum, gst_fftwspectrum, GST_TYPE_ELEMENT);
|
||||||
GST_TYPE_ELEMENT);
|
|
||||||
|
|
||||||
static void gst_fftwspectrum_set_property (GObject *object, guint prop_id,
|
static void gst_fftwspectrum_set_property (GObject *object, guint prop_id,
|
||||||
const GValue *value, GParamSpec *pspec);
|
const GValue *value, GParamSpec *pspec);
|
||||||
static void gst_fftwspectrum_get_property (GObject *object, guint prop_id,
|
static void gst_fftwspectrum_get_property (GObject *object, guint prop_id,
|
||||||
GValue *value, GParamSpec *pspec);
|
GValue *value, GParamSpec *pspec);
|
||||||
|
|
||||||
|
static gboolean gst_fftwspectrum_sink_event(
|
||||||
|
GstPad* pad, GstObject* parent, GstEvent* event);
|
||||||
|
static gboolean gst_fftwspectrum_src_event(
|
||||||
|
GstPad* pad, GstObject* parent, GstEvent* event);
|
||||||
|
static gboolean gst_fftwspectrum_src_query(
|
||||||
|
GstPad* pad, GstObject* parent, GstQuery* query);
|
||||||
|
static gboolean gst_fftwspectrum_sink_query(
|
||||||
|
GstPad* pad, GstObject* parent, GstQuery* query);
|
||||||
static gboolean gst_fftwspectrum_set_sink_caps (GstPad *pad, GstCaps *caps);
|
static gboolean gst_fftwspectrum_set_sink_caps (GstPad *pad, GstCaps *caps);
|
||||||
static gboolean gst_fftwspectrum_set_src_caps (GstPad *pad, GstCaps *caps);
|
static gboolean gst_fftwspectrum_set_src_caps (GstPad *pad, GstCaps *caps);
|
||||||
static void gst_fftwspectrum_fixatecaps (GstPad *pad, GstCaps *caps);
|
static void gst_fftwspectrum_fixatecaps (GstPad *pad, GstCaps *caps);
|
||||||
static GstCaps *gst_fftwspectrum_getcaps (GstPad *pad);
|
static GstCaps *gst_fftwspectrum_getcaps (GstPad *pad);
|
||||||
|
|
||||||
static GstFlowReturn gst_fftwspectrum_chain (GstPad *pad, GstBuffer *buf);
|
static GstFlowReturn gst_fftwspectrum_chain(
|
||||||
|
GstPad *pad, GstObject* object, GstBuffer *buf);
|
||||||
static GstStateChangeReturn gst_fftwspectrum_change_state (GstElement *element,
|
static GstStateChangeReturn gst_fftwspectrum_change_state (GstElement *element,
|
||||||
GstStateChange transition);
|
GstStateChange transition);
|
||||||
|
|
||||||
|
@ -115,25 +123,6 @@ static GstStateChangeReturn gst_fftwspectrum_change_state (GstElement *element,
|
||||||
/***************************************************************/
|
/***************************************************************/
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_fftwspectrum_base_init (gpointer gclass)
|
|
||||||
{
|
|
||||||
static GstElementDetails element_details =
|
|
||||||
{
|
|
||||||
"FFTW-based Fourier transform",
|
|
||||||
"Filter/Converter/Spectrum",
|
|
||||||
"Convert a raw audio stream into a frequency spectrum",
|
|
||||||
"Joe Rabinoff <bobqwatson@yahoo.com>"
|
|
||||||
};
|
|
||||||
GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
|
|
||||||
|
|
||||||
gst_element_class_add_pad_template (element_class,
|
|
||||||
gst_static_pad_template_get (&src_factory));
|
|
||||||
gst_element_class_add_pad_template (element_class,
|
|
||||||
gst_static_pad_template_get (&sink_factory));
|
|
||||||
gst_element_class_set_details (element_class, &element_details);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* initialize the plugin's class */
|
/* initialize the plugin's class */
|
||||||
static void
|
static void
|
||||||
gst_fftwspectrum_class_init (GstFFTWSpectrumClass * klass)
|
gst_fftwspectrum_class_init (GstFFTWSpectrumClass * klass)
|
||||||
|
@ -164,6 +153,18 @@ gst_fftwspectrum_class_init (GstFFTWSpectrumClass * klass)
|
||||||
|
|
||||||
gstelement_class->change_state
|
gstelement_class->change_state
|
||||||
= GST_DEBUG_FUNCPTR (gst_fftwspectrum_change_state);
|
= GST_DEBUG_FUNCPTR (gst_fftwspectrum_change_state);
|
||||||
|
|
||||||
|
gst_element_class_add_pad_template (GST_ELEMENT_CLASS(klass),
|
||||||
|
gst_static_pad_template_get (&src_factory));
|
||||||
|
gst_element_class_add_pad_template (GST_ELEMENT_CLASS(klass),
|
||||||
|
gst_static_pad_template_get (&sink_factory));
|
||||||
|
|
||||||
|
gst_element_class_set_static_metadata(
|
||||||
|
GST_ELEMENT_CLASS(klass),
|
||||||
|
"FFTW-based Fourier transform",
|
||||||
|
"Filter/Converter/Spectrum",
|
||||||
|
"Convert a raw audio stream into a frequency spectrum",
|
||||||
|
"Joe Rabinoff <bobqwatson@yahoo.com>");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialize the new element
|
/* initialize the new element
|
||||||
|
@ -172,30 +173,28 @@ gst_fftwspectrum_class_init (GstFFTWSpectrumClass * klass)
|
||||||
* initialize structure
|
* initialize structure
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
gst_fftwspectrum_init (GstFFTWSpectrum * conv,
|
gst_fftwspectrum_init (GstFFTWSpectrum * conv)
|
||||||
GstFFTWSpectrumClass * gclass)
|
|
||||||
{
|
{
|
||||||
GstElementClass *klass = GST_ELEMENT_GET_CLASS (conv);
|
GstElementClass* klass =
|
||||||
|
G_TYPE_INSTANCE_GET_CLASS(conv, GST_ELEMENT_TYPE, GstElementClass);
|
||||||
|
|
||||||
conv->sinkpad =
|
conv->sinkpad =
|
||||||
gst_pad_new_from_template
|
gst_pad_new_from_template
|
||||||
(gst_element_class_get_pad_template (klass, "sink"), "sink");
|
(gst_element_class_get_pad_template (klass, "sink"), "sink");
|
||||||
gst_pad_set_setcaps_function (conv->sinkpad,
|
gst_pad_set_event_function(conv->sinkpad, gst_fftwspectrum_sink_event);
|
||||||
GST_DEBUG_FUNCPTR (gst_fftwspectrum_set_sink_caps));
|
gst_pad_set_query_function(conv->srcpad, gst_fftwspectrum_src_query);
|
||||||
gst_pad_set_getcaps_function (conv->sinkpad,
|
|
||||||
GST_DEBUG_FUNCPTR (gst_fftwspectrum_getcaps));
|
gst_pad_set_chain_function (conv->sinkpad,
|
||||||
gst_pad_set_chain_function (conv->sinkpad,
|
|
||||||
GST_DEBUG_FUNCPTR (gst_fftwspectrum_chain));
|
GST_DEBUG_FUNCPTR (gst_fftwspectrum_chain));
|
||||||
|
|
||||||
conv->srcpad =
|
conv->srcpad =
|
||||||
gst_pad_new_from_template
|
gst_pad_new_from_template
|
||||||
(gst_element_class_get_pad_template (klass, "src"), "src");
|
(gst_element_class_get_pad_template (klass, "src"), "src");
|
||||||
gst_pad_set_setcaps_function (conv->srcpad,
|
gst_pad_set_event_function(conv->srcpad, gst_fftwspectrum_src_event);
|
||||||
GST_DEBUG_FUNCPTR (gst_fftwspectrum_set_src_caps));
|
gst_pad_set_query_function(conv->sinkpad, gst_fftwspectrum_sink_query);
|
||||||
gst_pad_set_getcaps_function (conv->srcpad,
|
|
||||||
GST_DEBUG_FUNCPTR (gst_fftwspectrum_getcaps));
|
//gst_pad_set_fixatecaps_function (conv->srcpad,
|
||||||
gst_pad_set_fixatecaps_function (conv->srcpad,
|
// GST_DEBUG_FUNCPTR (gst_fftwspectrum_fixatecaps));
|
||||||
GST_DEBUG_FUNCPTR (gst_fftwspectrum_fixatecaps));
|
|
||||||
|
|
||||||
|
|
||||||
gst_element_add_pad (GST_ELEMENT (conv), conv->sinkpad);
|
gst_element_add_pad (GST_ELEMENT (conv), conv->sinkpad);
|
||||||
|
@ -205,7 +204,7 @@ gst_fftwspectrum_init (GstFFTWSpectrum * conv,
|
||||||
conv->rate = 0;
|
conv->rate = 0;
|
||||||
conv->size = 0;
|
conv->size = 0;
|
||||||
conv->step = 0;
|
conv->step = 0;
|
||||||
|
|
||||||
/* These are set when we change to READY */
|
/* These are set when we change to READY */
|
||||||
conv->fftw_in = NULL;
|
conv->fftw_in = NULL;
|
||||||
conv->fftw_out = NULL;
|
conv->fftw_out = NULL;
|
||||||
|
@ -223,6 +222,65 @@ gst_fftwspectrum_init (GstFFTWSpectrum * conv,
|
||||||
conv->hi_q = HIQUALITY_DEFAULT;
|
conv->hi_q = HIQUALITY_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_fftwspectrum_sink_event(GstPad* pad, GstObject* parent, GstEvent* event)
|
||||||
|
{
|
||||||
|
GstFFTWSpectrum* conv = GST_FFTWSPECTRUM(parent);
|
||||||
|
switch (GST_EVENT_TYPE(event)) {
|
||||||
|
case GST_EVENT_CAPS: {
|
||||||
|
GstCaps* caps = NULL;
|
||||||
|
gst_event_parse_caps(event, &caps);
|
||||||
|
gst_fftwspectrum_set_sink_caps(pad, caps);
|
||||||
|
return gst_pad_push_event(conv->srcpad, event);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return gst_pad_event_default(pad, parent, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_fftwspectrum_src_event(GstPad* pad, GstObject* parent, GstEvent* event)
|
||||||
|
{
|
||||||
|
switch (GST_EVENT_TYPE(event)) {
|
||||||
|
case GST_EVENT_CAPS: {
|
||||||
|
GstCaps* caps = NULL;
|
||||||
|
gst_event_parse_caps(event, &caps);
|
||||||
|
gst_fftwspectrum_set_src_caps(pad, caps);
|
||||||
|
}
|
||||||
|
// FALLTHROUGH
|
||||||
|
default:
|
||||||
|
return gst_pad_event_default(pad, parent, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_fftwspectrum_src_query(GstPad* pad, GstObject* parent, GstQuery* query)
|
||||||
|
{
|
||||||
|
switch (GST_QUERY_TYPE(query)) {
|
||||||
|
case GST_QUERY_CAPS: {
|
||||||
|
GstCaps* caps = gst_fftwspectrum_getcaps(pad);
|
||||||
|
gst_pad_set_caps(pad, caps);
|
||||||
|
}
|
||||||
|
// FALLTHROUGH
|
||||||
|
default:
|
||||||
|
return gst_pad_query_default(pad, parent, query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_fftwspectrum_sink_query(GstPad* pad, GstObject* parent, GstQuery* query)
|
||||||
|
{
|
||||||
|
switch (GST_QUERY_TYPE(query)) {
|
||||||
|
case GST_QUERY_CAPS: {
|
||||||
|
GstCaps* caps = gst_fftwspectrum_getcaps(pad);
|
||||||
|
gst_pad_set_caps(pad, caps);
|
||||||
|
}
|
||||||
|
// FALLTHROUGH
|
||||||
|
default:
|
||||||
|
return gst_pad_query_default(pad, parent, query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_fftwspectrum_set_property (GObject * object, guint prop_id,
|
gst_fftwspectrum_set_property (GObject * object, guint prop_id,
|
||||||
const GValue * value, GParamSpec * pspec)
|
const GValue * value, GParamSpec * pspec)
|
||||||
|
@ -362,7 +420,7 @@ gst_fftwspectrum_set_sink_caps (GstPad * pad, GstCaps * caps)
|
||||||
|
|
||||||
/* Fixate the source caps with the given rate */
|
/* Fixate the source caps with the given rate */
|
||||||
gst_caps_set_simple (newsrccaps, "rate", G_TYPE_INT, rate, NULL);
|
gst_caps_set_simple (newsrccaps, "rate", G_TYPE_INT, rate, NULL);
|
||||||
gst_pad_fixate_caps (conv->srcpad, newsrccaps);
|
//gst_pad_fixate_caps (conv->srcpad, newsrccaps);
|
||||||
conv->rate = rate;
|
conv->rate = rate;
|
||||||
res = gst_pad_set_caps (conv->srcpad, newsrccaps);
|
res = gst_pad_set_caps (conv->srcpad, newsrccaps);
|
||||||
if (!res)
|
if (!res)
|
||||||
|
@ -515,9 +573,10 @@ gst_fftwspectrum_change_state (GstElement * element,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = parent_class->change_state (element, transition);
|
res = GST_ELEMENT_CLASS(gst_fftwspectrum_parent_class)
|
||||||
|
->change_state(element, transition);
|
||||||
|
|
||||||
switch (transition)
|
switch (transition)
|
||||||
{
|
{
|
||||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
||||||
break;
|
break;
|
||||||
|
@ -545,15 +604,20 @@ gst_fftwspectrum_change_state (GstElement * element,
|
||||||
static void
|
static void
|
||||||
push_samples (GstFFTWSpectrum *conv, GstBuffer *buf)
|
push_samples (GstFFTWSpectrum *conv, GstBuffer *buf)
|
||||||
{
|
{
|
||||||
gint newsamples = GST_BUFFER_SIZE (buf) / sizeof (gdouble);
|
GstMapInfo map;
|
||||||
|
gst_buffer_map(buf, &map, GST_MAP_READ);
|
||||||
|
|
||||||
|
gint newsamples = map.size / sizeof (gdouble);
|
||||||
gint oldsamples = conv->numsamples;
|
gint oldsamples = conv->numsamples;
|
||||||
|
|
||||||
conv->numsamples += newsamples;
|
conv->numsamples += newsamples;
|
||||||
conv->samples = g_realloc (conv->samples, conv->numsamples * sizeof (gdouble));
|
conv->samples = g_realloc (conv->samples, conv->numsamples * sizeof (gdouble));
|
||||||
memcpy (&conv->samples[oldsamples], GST_BUFFER_DATA (buf),
|
memcpy (&conv->samples[oldsamples], map.data,
|
||||||
newsamples * sizeof (gdouble));
|
newsamples * sizeof (gdouble));
|
||||||
|
|
||||||
/* GST_LOG ("Added %d samples", newsamples); */
|
/* GST_LOG ("Added %d samples", newsamples); */
|
||||||
|
|
||||||
|
gst_buffer_unmap(buf, &map);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This basically does the opposite of push_samples, but takes samples
|
/* This basically does the opposite of push_samples, but takes samples
|
||||||
|
@ -586,45 +650,78 @@ shift_samples (GstFFTWSpectrum *conv, gint toshift)
|
||||||
* by conv->step.
|
* by conv->step.
|
||||||
*/
|
*/
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_fftwspectrum_chain (GstPad * pad, GstBuffer * buf)
|
gst_fftwspectrum_chain(GstPad* pad, GstObject* object, GstBuffer* buf)
|
||||||
{
|
{
|
||||||
GstFFTWSpectrum *conv;
|
GstFFTWSpectrum *conv = GST_FFTWSPECTRUM(object);
|
||||||
GstBuffer *outbuf;
|
GstBuffer *outbuf;
|
||||||
GstFlowReturn res = GST_FLOW_OK;
|
GstFlowReturn res = GST_FLOW_OK;
|
||||||
|
|
||||||
conv = GST_FFTWSPECTRUM (gst_pad_get_parent (pad));
|
|
||||||
|
|
||||||
push_samples (conv, buf);
|
push_samples (conv, buf);
|
||||||
gst_buffer_unref (buf);
|
gst_buffer_unref (buf);
|
||||||
|
|
||||||
|
GstQuery* query = gst_query_new_allocation(gst_pad_get_current_caps(pad), TRUE);
|
||||||
|
if (!gst_pad_peer_query(pad, query)) {
|
||||||
|
// Query failed, not a problem, we use the query defaults.
|
||||||
|
}
|
||||||
|
|
||||||
|
GstBufferPool* pool = NULL;
|
||||||
|
guint size = 0;
|
||||||
|
guint min = 0;
|
||||||
|
guint max = 0;
|
||||||
|
if (gst_query_get_n_allocation_pools(query) > 0) {
|
||||||
|
gst_query_parse_nth_allocation_pool(query, 0, &pool, &size, &min, &max);
|
||||||
|
}
|
||||||
|
if (pool == NULL) {
|
||||||
|
pool = gst_buffer_pool_new();
|
||||||
|
}
|
||||||
|
|
||||||
|
GstStructure* config = gst_buffer_pool_get_config(pool);
|
||||||
|
gst_buffer_pool_config_set_params(
|
||||||
|
config, gst_pad_get_current_caps(pad), size, min, max);
|
||||||
|
gst_buffer_pool_set_config(pool, config);
|
||||||
|
|
||||||
|
gst_buffer_pool_set_active(pool, TRUE);
|
||||||
|
|
||||||
while (conv->numsamples >= MAX (conv->size, conv->step))
|
while (conv->numsamples >= MAX (conv->size, conv->step))
|
||||||
{
|
{
|
||||||
res = gst_pad_alloc_buffer_and_set_caps
|
/*
|
||||||
(conv->srcpad, conv->offset, OUTPUT_SIZE (conv),
|
res = gst_pad_alloc_buffer_and_set_caps(
|
||||||
GST_PAD_CAPS(conv->srcpad), &outbuf);
|
conv->srcpad,
|
||||||
|
conv->offset,
|
||||||
|
OUTPUT_SIZE(conv),
|
||||||
|
GST_PAD_CAPS(conv->srcpad),
|
||||||
|
&outbuf);
|
||||||
|
*/
|
||||||
|
|
||||||
|
res = gst_buffer_pool_acquire_buffer(pool, &outbuf, NULL);
|
||||||
|
|
||||||
if (res != GST_FLOW_OK)
|
if (res != GST_FLOW_OK)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
GST_BUFFER_SIZE (outbuf) = OUTPUT_SIZE (conv);
|
gst_buffer_set_size(outbuf, OUTPUT_SIZE(conv));
|
||||||
GST_BUFFER_OFFSET (outbuf) = conv->offset;
|
GST_BUFFER_OFFSET (outbuf) = conv->offset;
|
||||||
GST_BUFFER_OFFSET_END (outbuf) = conv->offset + conv->step;
|
GST_BUFFER_OFFSET_END (outbuf) = conv->offset + conv->step;
|
||||||
GST_BUFFER_TIMESTAMP (outbuf) = conv->timestamp;
|
GST_BUFFER_TIMESTAMP (outbuf) = conv->timestamp;
|
||||||
GST_BUFFER_DURATION (outbuf)
|
GST_BUFFER_DURATION (outbuf) =
|
||||||
= gst_util_uint64_scale_int (GST_SECOND, conv->step, conv->rate);
|
gst_util_uint64_scale_int (GST_SECOND, conv->step, conv->rate);
|
||||||
|
|
||||||
/* Do the Fourier transform */
|
/* Do the Fourier transform */
|
||||||
memcpy (conv->fftw_in, conv->samples, conv->size * sizeof (double));
|
memcpy (conv->fftw_in, conv->samples, conv->size * sizeof (double));
|
||||||
fftw_execute (conv->fftw_plan);
|
fftw_execute (conv->fftw_plan);
|
||||||
{ /* Normalize */
|
{ /* Normalize */
|
||||||
gint i;
|
gint i;
|
||||||
gfloat root = sqrtf (conv->size);
|
gfloat root = sqrtf (conv->size);
|
||||||
for (i = 0; i < 2*(conv->size/2+1); ++i)
|
for (i = 0; i < 2*(conv->size/2+1); ++i) {
|
||||||
conv->fftw_out[i] /= root;
|
conv->fftw_out[i] /= root;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
memcpy (GST_BUFFER_DATA (outbuf), conv->fftw_out, OUTPUT_SIZE (conv));
|
GstMapInfo map;
|
||||||
|
gst_buffer_map(outbuf, &map, GST_MAP_WRITE);
|
||||||
|
memcpy (map.data, conv->fftw_out, OUTPUT_SIZE (conv));
|
||||||
|
gst_buffer_unmap(outbuf, &map);
|
||||||
|
|
||||||
res = gst_pad_push (conv->srcpad, outbuf);
|
res = gst_pad_push (conv->srcpad, outbuf);
|
||||||
|
|
||||||
shift_samples (conv, conv->step);
|
shift_samples (conv, conv->step);
|
||||||
|
|
||||||
if (res != GST_FLOW_OK)
|
if (res != GST_FLOW_OK)
|
||||||
|
|
|
@ -100,8 +100,7 @@ static GstStaticPadTemplate src_factory
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
GST_BOILERPLATE (GstMoodbar, gst_moodbar, GstElement,
|
G_DEFINE_TYPE(GstMoodbar, gst_moodbar, GST_TYPE_ELEMENT);
|
||||||
GST_TYPE_ELEMENT);
|
|
||||||
|
|
||||||
static void gst_moodbar_set_property (GObject *object, guint prop_id,
|
static void gst_moodbar_set_property (GObject *object, guint prop_id,
|
||||||
const GValue *value, GParamSpec *pspec);
|
const GValue *value, GParamSpec *pspec);
|
||||||
|
@ -109,9 +108,11 @@ static void gst_moodbar_get_property (GObject *object, guint prop_id,
|
||||||
GValue *value, GParamSpec *pspec);
|
GValue *value, GParamSpec *pspec);
|
||||||
|
|
||||||
static gboolean gst_moodbar_set_sink_caps (GstPad *pad, GstCaps *caps);
|
static gboolean gst_moodbar_set_sink_caps (GstPad *pad, GstCaps *caps);
|
||||||
static gboolean gst_moodbar_sink_event (GstPad *pad, GstEvent *event);
|
static gboolean gst_moodbar_sink_event (
|
||||||
|
GstPad *pad, GstObject *parent, GstEvent *event);
|
||||||
|
|
||||||
static GstFlowReturn gst_moodbar_chain (GstPad *pad, GstBuffer *buf);
|
static GstFlowReturn gst_moodbar_chain (
|
||||||
|
GstPad *pad, GstObject *object, GstBuffer *buf);
|
||||||
static GstStateChangeReturn gst_moodbar_change_state (GstElement *element,
|
static GstStateChangeReturn gst_moodbar_change_state (GstElement *element,
|
||||||
GstStateChange transition);
|
GstStateChange transition);
|
||||||
|
|
||||||
|
@ -143,26 +144,6 @@ static const guint bark_bands[24]
|
||||||
/* GObject boilerplate stuff */
|
/* GObject boilerplate stuff */
|
||||||
/***************************************************************/
|
/***************************************************************/
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_moodbar_base_init (gpointer gclass)
|
|
||||||
{
|
|
||||||
static GstElementDetails element_details =
|
|
||||||
{
|
|
||||||
"Moodbar analyzer",
|
|
||||||
"Filter/Converter/Moodbar",
|
|
||||||
"Convert a spectrum into a stream of (uchar) rgb triples representing its \"mood\"",
|
|
||||||
"Joe Rabinoff <bobqwatson@yahoo.com>"
|
|
||||||
};
|
|
||||||
GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
|
|
||||||
|
|
||||||
gst_element_class_add_pad_template (element_class,
|
|
||||||
gst_static_pad_template_get (&src_factory));
|
|
||||||
gst_element_class_add_pad_template (element_class,
|
|
||||||
gst_static_pad_template_get (&sink_factory));
|
|
||||||
gst_element_class_set_details (element_class, &element_details);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* initialize the plugin's class */
|
/* initialize the plugin's class */
|
||||||
static void
|
static void
|
||||||
gst_moodbar_class_init (GstMoodbarClass * klass)
|
gst_moodbar_class_init (GstMoodbarClass * klass)
|
||||||
|
@ -178,16 +159,28 @@ gst_moodbar_class_init (GstMoodbarClass * klass)
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class, ARG_HEIGHT,
|
g_object_class_install_property (gobject_class, ARG_HEIGHT,
|
||||||
g_param_spec_int ("height", "Image height",
|
g_param_spec_int ("height", "Image height",
|
||||||
"The height of the resulting raw image",
|
"The height of the resulting raw image",
|
||||||
1, G_MAXINT32, HEIGHT_DEFAULT, G_PARAM_READWRITE));
|
1, G_MAXINT32, HEIGHT_DEFAULT, G_PARAM_READWRITE));
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class, ARG_MAX_WIDTH,
|
g_object_class_install_property (gobject_class, ARG_MAX_WIDTH,
|
||||||
g_param_spec_int ("max-width", "Image maximum width",
|
g_param_spec_int ("max-width", "Image maximum width",
|
||||||
"The maximum width of the resulting raw image, or 0 for no rescaling",
|
"The maximum width of the resulting raw image, or 0 for no rescaling",
|
||||||
0, G_MAXINT32, MAX_WIDTH_DEFAULT, G_PARAM_READWRITE));
|
0, G_MAXINT32, MAX_WIDTH_DEFAULT, G_PARAM_READWRITE));
|
||||||
|
|
||||||
gstelement_class->change_state
|
gstelement_class->change_state
|
||||||
= GST_DEBUG_FUNCPTR (gst_moodbar_change_state);
|
= GST_DEBUG_FUNCPTR (gst_moodbar_change_state);
|
||||||
|
|
||||||
|
gst_element_class_add_pad_template (gstelement_class,
|
||||||
|
gst_static_pad_template_get (&src_factory));
|
||||||
|
gst_element_class_add_pad_template (gstelement_class,
|
||||||
|
gst_static_pad_template_get (&sink_factory));
|
||||||
|
|
||||||
|
gst_element_class_set_static_metadata(
|
||||||
|
gstelement_class,
|
||||||
|
"Moodbar analyzer",
|
||||||
|
"Filter/Converter/Moodbar",
|
||||||
|
"Convert a spectrum into a stream of (uchar) rgb triples representing its \"mood\"",
|
||||||
|
"Joe Rabinoff <bobqwatson@yahoo.com>");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialize the new element
|
/* initialize the new element
|
||||||
|
@ -196,19 +189,17 @@ gst_moodbar_class_init (GstMoodbarClass * klass)
|
||||||
* initialize structure
|
* initialize structure
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
gst_moodbar_init (GstMoodbar *mood, GstMoodbarClass *gclass)
|
gst_moodbar_init (GstMoodbar *mood)
|
||||||
{
|
{
|
||||||
GstElementClass *klass = GST_ELEMENT_GET_CLASS (mood);
|
GstElementClass *klass = GST_ELEMENT_GET_CLASS (mood);
|
||||||
|
|
||||||
mood->sinkpad =
|
mood->sinkpad =
|
||||||
gst_pad_new_from_template
|
gst_pad_new_from_template
|
||||||
(gst_element_class_get_pad_template (klass, "sink"), "sink");
|
(gst_element_class_get_pad_template (klass, "sink"), "sink");
|
||||||
gst_pad_set_setcaps_function (mood->sinkpad,
|
|
||||||
GST_DEBUG_FUNCPTR (gst_moodbar_set_sink_caps));
|
|
||||||
gst_pad_set_event_function (mood->sinkpad,
|
gst_pad_set_event_function (mood->sinkpad,
|
||||||
GST_DEBUG_FUNCPTR (gst_moodbar_sink_event));
|
GST_DEBUG_FUNCPTR (gst_moodbar_sink_event));
|
||||||
gst_pad_set_chain_function (mood->sinkpad,
|
gst_pad_set_chain_function (mood->sinkpad,
|
||||||
GST_DEBUG_FUNCPTR (gst_moodbar_chain));
|
GST_DEBUG_FUNCPTR (gst_moodbar_chain));
|
||||||
|
|
||||||
mood->srcpad =
|
mood->srcpad =
|
||||||
gst_pad_new_from_template
|
gst_pad_new_from_template
|
||||||
|
@ -222,7 +213,7 @@ gst_moodbar_init (GstMoodbar *mood, GstMoodbarClass *gclass)
|
||||||
mood->rate = 0;
|
mood->rate = 0;
|
||||||
mood->size = 0;
|
mood->size = 0;
|
||||||
mood->barkband_table = NULL;
|
mood->barkband_table = NULL;
|
||||||
|
|
||||||
/* These are allocated when we change to PAUSED */
|
/* These are allocated when we change to PAUSED */
|
||||||
mood->r = NULL;
|
mood->r = NULL;
|
||||||
mood->g = NULL;
|
mood->g = NULL;
|
||||||
|
@ -240,7 +231,7 @@ static void gst_moodbar_set_property (GObject *object, guint prop_id,
|
||||||
{
|
{
|
||||||
GstMoodbar *mood = GST_MOODBAR (object);
|
GstMoodbar *mood = GST_MOODBAR (object);
|
||||||
|
|
||||||
switch (prop_id)
|
switch (prop_id)
|
||||||
{
|
{
|
||||||
case ARG_HEIGHT:
|
case ARG_HEIGHT:
|
||||||
mood->height = (guint) g_value_get_int (value);
|
mood->height = (guint) g_value_get_int (value);
|
||||||
|
@ -252,7 +243,7 @@ static void gst_moodbar_set_property (GObject *object, guint prop_id,
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -352,16 +343,19 @@ gst_moodbar_set_sink_caps (GstPad *pad, GstCaps *caps)
|
||||||
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_moodbar_sink_event (GstPad *pad, GstEvent *event)
|
gst_moodbar_sink_event (GstPad *pad, GstObject *parent, GstEvent *event)
|
||||||
{
|
{
|
||||||
GstMoodbar *mood;
|
GstMoodbar *mood = GST_MOODBAR(parent);
|
||||||
gboolean res = TRUE;
|
gboolean res = TRUE;
|
||||||
|
|
||||||
mood = GST_MOODBAR (gst_pad_get_parent (pad));
|
if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
|
||||||
|
|
||||||
if (GST_EVENT_TYPE (event) == GST_EVENT_EOS)
|
|
||||||
gst_moodbar_finish (mood);
|
gst_moodbar_finish (mood);
|
||||||
|
} else if (GST_EVENT_TYPE(event) == GST_EVENT_CAPS) {
|
||||||
|
GstCaps* caps = NULL;
|
||||||
|
gst_event_parse_caps(event, &caps);
|
||||||
|
gst_moodbar_set_sink_caps(pad, caps);
|
||||||
|
}
|
||||||
|
|
||||||
res = gst_pad_push_event (mood->srcpad, event);
|
res = gst_pad_push_event (mood->srcpad, event);
|
||||||
gst_object_unref (mood);
|
gst_object_unref (mood);
|
||||||
|
|
||||||
|
@ -397,7 +391,8 @@ gst_moodbar_change_state (GstElement *element, GstStateChange transition)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = parent_class->change_state (element, transition);
|
res = GST_ELEMENT_CLASS(gst_moodbar_parent_class)
|
||||||
|
->change_state (element, transition);
|
||||||
|
|
||||||
switch (transition)
|
switch (transition)
|
||||||
{
|
{
|
||||||
|
@ -457,27 +452,31 @@ allocate_another_frame (GstMoodbar *mood)
|
||||||
* once we receive an EOS signal.
|
* once we receive an EOS signal.
|
||||||
*/
|
*/
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_moodbar_chain (GstPad *pad, GstBuffer *buf)
|
gst_moodbar_chain (GstPad *pad, GstObject *parent, GstBuffer *buf)
|
||||||
{
|
{
|
||||||
GstMoodbar *mood = GST_MOODBAR (gst_pad_get_parent (pad));
|
GstMoodbar *mood = GST_MOODBAR (parent);
|
||||||
guint i;
|
guint i;
|
||||||
gdouble amplitudes[24], rgb[3] = {0.f, 0.f, 0.f};
|
gdouble amplitudes[24], rgb[3] = {0.f, 0.f, 0.f};
|
||||||
gdouble *out, real, imag;
|
gdouble *out, real, imag;
|
||||||
guint numfreqs = NUMFREQS (mood);
|
guint numfreqs = NUMFREQS (mood);
|
||||||
|
|
||||||
if (GST_BUFFER_SIZE (buf) != numfreqs * sizeof (gdouble) * 2)
|
GstMapInfo map;
|
||||||
|
gst_buffer_map(buf, &map, GST_MAP_READ);
|
||||||
|
|
||||||
|
if (map.size != numfreqs * sizeof (gdouble) * 2)
|
||||||
{
|
{
|
||||||
|
gst_buffer_unmap(buf, &map);
|
||||||
gst_object_unref (mood);
|
gst_object_unref (mood);
|
||||||
return GST_FLOW_ERROR;
|
return GST_FLOW_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
out = (gdouble *) GST_BUFFER_DATA (buf);
|
out = (gdouble *) map.data;
|
||||||
|
|
||||||
if (!allocate_another_frame (mood))
|
if (!allocate_another_frame (mood))
|
||||||
return GST_FLOW_ERROR;
|
return GST_FLOW_ERROR;
|
||||||
|
|
||||||
/* Calculate total amplitudes for the different bark bands */
|
/* Calculate total amplitudes for the different bark bands */
|
||||||
|
|
||||||
for (i = 0; i < 24; ++i)
|
for (i = 0; i < 24; ++i)
|
||||||
amplitudes[i] = 0.f;
|
amplitudes[i] = 0.f;
|
||||||
|
|
||||||
|
@ -501,6 +500,7 @@ gst_moodbar_chain (GstPad *pad, GstBuffer *buf)
|
||||||
mood->g[mood->numframes] = rgb[1];
|
mood->g[mood->numframes] = rgb[1];
|
||||||
mood->b[mood->numframes] = rgb[2];
|
mood->b[mood->numframes] = rgb[2];
|
||||||
|
|
||||||
|
gst_buffer_unmap (buf, &map);
|
||||||
gst_buffer_unref (buf);
|
gst_buffer_unref (buf);
|
||||||
gst_object_unref (mood);
|
gst_object_unref (mood);
|
||||||
|
|
||||||
|
@ -624,9 +624,12 @@ gst_moodbar_finish (GstMoodbar *mood)
|
||||||
(output_width * mood->height * 3 * sizeof (guchar));
|
(output_width * mood->height * 3 * sizeof (guchar));
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
GstMapInfo map;
|
||||||
|
gst_buffer_map(buf, &map, GST_MAP_READ);
|
||||||
/* Don't set the timestamp, duration, etc. since it's irrelevant */
|
/* Don't set the timestamp, duration, etc. since it's irrelevant */
|
||||||
GST_BUFFER_OFFSET (buf) = 0;
|
map.memory->offset = 0;
|
||||||
data = (guchar *) GST_BUFFER_DATA (buf);
|
data = (guchar *) map.data;
|
||||||
|
|
||||||
gdouble r, g, b;
|
gdouble r, g, b;
|
||||||
guint i, j, n;
|
guint i, j, n;
|
||||||
|
@ -634,40 +637,43 @@ gst_moodbar_finish (GstMoodbar *mood)
|
||||||
for (line = 0; line < mood->height; ++line)
|
for (line = 0; line < mood->height; ++line)
|
||||||
{
|
{
|
||||||
for (i = 0; i < output_width; ++i)
|
for (i = 0; i < output_width; ++i)
|
||||||
{
|
{
|
||||||
r = 0.f; g = 0.f; b = 0.f;
|
r = 0.f; g = 0.f; b = 0.f;
|
||||||
start = i * mood->numframes / output_width;
|
start = i * mood->numframes / output_width;
|
||||||
end = (i + 1) * mood->numframes / output_width;
|
end = (i + 1) * mood->numframes / output_width;
|
||||||
if ( start == end )
|
if ( start == end )
|
||||||
end = start + 1;
|
end = start + 1;
|
||||||
|
|
||||||
for( j = start; j < end; j++ )
|
for( j = start; j < end; j++ )
|
||||||
{
|
{
|
||||||
r += mood->r[j] * 255.f;
|
r += mood->r[j] * 255.f;
|
||||||
g += mood->g[j] * 255.f;
|
g += mood->g[j] * 255.f;
|
||||||
b += mood->b[j] * 255.f;
|
b += mood->b[j] * 255.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = end - start;
|
n = end - start;
|
||||||
|
|
||||||
*(data++) = (guchar) (r / ((gdouble) n));
|
*(data++) = (guchar) (r / ((gdouble) n));
|
||||||
*(data++) = (guchar) (g / ((gdouble) n));
|
*(data++) = (guchar) (g / ((gdouble) n));
|
||||||
*(data++) = (guchar) (b / ((gdouble) n));
|
*(data++) = (guchar) (b / ((gdouble) n));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{ /* Now we (finally) know the width of the image we're pushing */
|
{ /* Now we (finally) know the width of the image we're pushing */
|
||||||
GstCaps *caps = gst_caps_copy (gst_pad_get_caps (mood->srcpad));
|
GstCaps *caps = gst_caps_copy (gst_pad_get_current_caps (mood->srcpad));
|
||||||
gboolean res;
|
gboolean res;
|
||||||
gst_caps_set_simple (caps, "width", G_TYPE_INT, output_width, NULL);
|
gst_caps_set_simple (caps, "width", G_TYPE_INT, output_width, NULL);
|
||||||
gst_caps_set_simple (caps, "height", G_TYPE_INT, mood->height, NULL);
|
gst_caps_set_simple (caps, "height", G_TYPE_INT, mood->height, NULL);
|
||||||
res = gst_pad_set_caps (mood->srcpad, caps);
|
res = gst_pad_set_caps (mood->srcpad, caps);
|
||||||
|
/*
|
||||||
if (res)
|
if (res)
|
||||||
gst_buffer_set_caps (buf, caps);
|
gst_buffer_set_caps (buf, caps);
|
||||||
|
*/
|
||||||
gst_caps_unref (caps);
|
gst_caps_unref (caps);
|
||||||
if (!res)
|
if (!res)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_pad_push (mood->srcpad, buf);
|
gst_pad_push (mood->srcpad, buf);
|
||||||
|
gst_buffer_unmap(buf, &map);
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,7 +122,7 @@ SongLoader::Result SongLoader::LoadLocalPartial(const QString& filename) {
|
||||||
SongLoader::Result SongLoader::LoadAudioCD() {
|
SongLoader::Result SongLoader::LoadAudioCD() {
|
||||||
#ifdef HAVE_AUDIOCD
|
#ifdef HAVE_AUDIOCD
|
||||||
// Create gstreamer cdda element
|
// Create gstreamer cdda element
|
||||||
GstElement* cdda = gst_element_make_from_uri (GST_URI_SRC, "cdda://", NULL);
|
GstElement* cdda = gst_element_make_from_uri (GST_URI_SRC, "cdda://", NULL, NULL);
|
||||||
if (cdda == NULL) {
|
if (cdda == NULL) {
|
||||||
qLog(Error) << "Error while creating CDDA GstElement";
|
qLog(Error) << "Error while creating CDDA GstElement";
|
||||||
return Error;
|
return Error;
|
||||||
|
@ -460,7 +460,7 @@ SongLoader::Result SongLoader::LoadRemote() {
|
||||||
|
|
||||||
// Create the source element automatically based on the URL
|
// Create the source element automatically based on the URL
|
||||||
GstElement* source = gst_element_make_from_uri(
|
GstElement* source = gst_element_make_from_uri(
|
||||||
GST_URI_SRC, url_.toEncoded().constData(), NULL);
|
GST_URI_SRC, url_.toEncoded().constData(), NULL, NULL);
|
||||||
if (!source) {
|
if (!source) {
|
||||||
qLog(Warning) << "Couldn't create gstreamer source element for" << url_.toString();
|
qLog(Warning) << "Couldn't create gstreamer source element for" << url_.toString();
|
||||||
return Error;
|
return Error;
|
||||||
|
@ -476,12 +476,13 @@ SongLoader::Result SongLoader::LoadRemote() {
|
||||||
// Connect callbacks
|
// Connect callbacks
|
||||||
GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline.get()));
|
GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline.get()));
|
||||||
CHECKED_GCONNECT(typefind, "have-type", &TypeFound, this);
|
CHECKED_GCONNECT(typefind, "have-type", &TypeFound, this);
|
||||||
gst_bus_set_sync_handler(bus, BusCallbackSync, this);
|
gst_bus_set_sync_handler(bus, BusCallbackSync, this, NULL);
|
||||||
gst_bus_add_watch(bus, BusCallback, this);
|
gst_bus_add_watch(bus, BusCallback, this);
|
||||||
|
|
||||||
// Add a probe to the sink so we can capture the data if it's a playlist
|
// Add a probe to the sink so we can capture the data if it's a playlist
|
||||||
GstPad* pad = gst_element_get_static_pad(fakesink, "sink");
|
GstPad* pad = gst_element_get_static_pad(fakesink, "sink");
|
||||||
gst_pad_add_buffer_probe(pad, G_CALLBACK(DataReady), this);
|
gst_pad_add_probe(
|
||||||
|
pad, GST_PAD_PROBE_TYPE_BUFFER, &DataReady, this, NULL);
|
||||||
gst_object_unref(pad);
|
gst_object_unref(pad);
|
||||||
|
|
||||||
// Start "playing"
|
// Start "playing"
|
||||||
|
@ -511,16 +512,21 @@ void SongLoader::TypeFound(GstElement*, uint, GstCaps* caps, void* self) {
|
||||||
instance->StopTypefindAsync(true);
|
instance->StopTypefindAsync(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean SongLoader::DataReady(GstPad*, GstBuffer* buf, void* self) {
|
GstPadProbeReturn SongLoader::DataReady(
|
||||||
SongLoader* instance = static_cast<SongLoader*>(self);
|
GstPad*, GstPadProbeInfo* info, gpointer self) {
|
||||||
|
SongLoader* instance = reinterpret_cast<SongLoader*>(self);
|
||||||
|
|
||||||
if (instance->state_ == Finished)
|
if (instance->state_ == Finished)
|
||||||
return true;
|
return GST_PAD_PROBE_OK;
|
||||||
|
|
||||||
|
GstBuffer* buffer = gst_pad_probe_info_get_buffer(info);
|
||||||
|
GstMapInfo map;
|
||||||
|
gst_buffer_map(buffer, &map, GST_MAP_READ);
|
||||||
|
|
||||||
// Append the data to the buffer
|
// Append the data to the buffer
|
||||||
instance->buffer_.append(reinterpret_cast<const char*>(GST_BUFFER_DATA(buf)),
|
instance->buffer_.append(reinterpret_cast<const char*>(map.data), map.size);
|
||||||
GST_BUFFER_SIZE(buf));
|
|
||||||
qLog(Debug) << "Received total" << instance->buffer_.size() << "bytes";
|
qLog(Debug) << "Received total" << instance->buffer_.size() << "bytes";
|
||||||
|
gst_buffer_unmap(buffer, &map);
|
||||||
|
|
||||||
if (instance->state_ == WaitingForMagic &&
|
if (instance->state_ == WaitingForMagic &&
|
||||||
(instance->buffer_.size() >= PlaylistParser::kMagicSize ||
|
(instance->buffer_.size() >= PlaylistParser::kMagicSize ||
|
||||||
|
@ -529,7 +535,7 @@ gboolean SongLoader::DataReady(GstPad*, GstBuffer* buf, void* self) {
|
||||||
instance->MagicReady();
|
instance->MagicReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return GST_PAD_PROBE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean SongLoader::BusCallback(GstBus*, GstMessage* msg, gpointer self) {
|
gboolean SongLoader::BusCallback(GstBus*, GstMessage* msg, gpointer self) {
|
||||||
|
|
|
@ -100,7 +100,7 @@ private:
|
||||||
|
|
||||||
// GStreamer callbacks
|
// GStreamer callbacks
|
||||||
static void TypeFound(GstElement* typefind, uint probability, GstCaps* caps, void* self);
|
static void TypeFound(GstElement* typefind, uint probability, GstCaps* caps, void* self);
|
||||||
static gboolean DataReady(GstPad*, GstBuffer* buf, void* self);
|
static GstPadProbeReturn DataReady(GstPad*, GstPadProbeInfo* buf, gpointer self);
|
||||||
static GstBusSyncReply BusCallbackSync(GstBus*, GstMessage*, gpointer);
|
static GstBusSyncReply BusCallbackSync(GstBus*, GstMessage*, gpointer);
|
||||||
static gboolean BusCallback(GstBus*, GstMessage*, gpointer);
|
static gboolean BusCallback(GstBus*, GstMessage*, gpointer);
|
||||||
|
|
||||||
|
|
|
@ -200,24 +200,28 @@ void GstEngine::UpdateScope() {
|
||||||
typedef Engine::Scope::value_type sample_type;
|
typedef Engine::Scope::value_type sample_type;
|
||||||
|
|
||||||
// determine the number of channels
|
// determine the number of channels
|
||||||
|
/*
|
||||||
GstStructure* structure = gst_caps_get_structure(
|
GstStructure* structure = gst_caps_get_structure(
|
||||||
GST_BUFFER_CAPS(latest_buffer_), 0);
|
GST_BUFFER_CAPS(latest_buffer_), 0);
|
||||||
|
*/
|
||||||
int channels = 2;
|
int channels = 2;
|
||||||
gst_structure_get_int(structure, "channels", &channels);
|
//gst_structure_get_int(structure, "channels", &channels);
|
||||||
|
|
||||||
// scope does not support >2 channels
|
// scope does not support >2 channels
|
||||||
if (channels > 2)
|
if (channels > 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const sample_type* source = reinterpret_cast<sample_type*>(
|
GstMapInfo map;
|
||||||
GST_BUFFER_DATA(latest_buffer_));
|
gst_buffer_map(latest_buffer_, &map, GST_MAP_READ);
|
||||||
|
const sample_type* source = reinterpret_cast<sample_type*>(map.data);
|
||||||
sample_type* dest = scope_.data();
|
sample_type* dest = scope_.data();
|
||||||
const int bytes = qMin(
|
const int bytes = qMin(
|
||||||
static_cast<Engine::Scope::size_type>(GST_BUFFER_SIZE(latest_buffer_)),
|
static_cast<Engine::Scope::size_type>(map.size),
|
||||||
scope_.size() * sizeof(sample_type));
|
scope_.size() * sizeof(sample_type));
|
||||||
|
|
||||||
memcpy(dest, source, bytes);
|
memcpy(dest, source, bytes);
|
||||||
|
|
||||||
|
gst_buffer_unmap(latest_buffer_, &map);
|
||||||
gst_buffer_unref(latest_buffer_);
|
gst_buffer_unref(latest_buffer_);
|
||||||
latest_buffer_ = NULL;
|
latest_buffer_ = NULL;
|
||||||
}
|
}
|
||||||
|
@ -636,19 +640,21 @@ GstEngine::PluginDetailsList
|
||||||
|
|
||||||
PluginDetailsList ret;
|
PluginDetailsList ret;
|
||||||
|
|
||||||
GstRegistry* registry = gst_registry_get_default();
|
GstRegistry* registry = gst_registry_get();
|
||||||
GList* const features =
|
GList* const features =
|
||||||
gst_registry_get_feature_list(registry, GST_TYPE_ELEMENT_FACTORY);
|
gst_registry_get_feature_list(registry, GST_TYPE_ELEMENT_FACTORY);
|
||||||
|
|
||||||
GList* p = features;
|
GList* p = features;
|
||||||
while (p) {
|
while (p) {
|
||||||
GstElementFactory* factory = GST_ELEMENT_FACTORY(p->data);
|
GstElementFactory* factory = GST_ELEMENT_FACTORY(p->data);
|
||||||
if (QString(factory->details.klass).contains(classname)) {
|
if (QString(gst_element_factory_get_klass(factory)).contains(classname)) {
|
||||||
PluginDetails details;
|
PluginDetails details;
|
||||||
details.name = QString::fromUtf8(GST_PLUGIN_FEATURE_NAME(p->data));
|
details.name = QString::fromUtf8(GST_OBJECT_NAME(p->data));
|
||||||
details.long_name = QString::fromUtf8(factory->details.longname);
|
details.long_name =
|
||||||
details.description = QString::fromUtf8(factory->details.description);
|
QString::fromUtf8(gst_element_factory_get_longname(factory));
|
||||||
details.author = QString::fromUtf8(factory->details.author);
|
details.description =
|
||||||
|
QString::fromUtf8(gst_element_factory_get_description(factory));
|
||||||
|
details.author = QString::fromUtf8(gst_element_factory_get_author(factory));
|
||||||
ret << details;
|
ret << details;
|
||||||
}
|
}
|
||||||
p = g_list_next(p);
|
p = g_list_next(p);
|
||||||
|
|
|
@ -285,7 +285,8 @@ bool GstEnginePipeline::Init() {
|
||||||
// We do it here because we want pre-equalized and pre-volume samples
|
// We do it here because we want pre-equalized and pre-volume samples
|
||||||
// so that our visualization are not be affected by them.
|
// so that our visualization are not be affected by them.
|
||||||
pad = gst_element_get_static_pad(event_probe, "src");
|
pad = gst_element_get_static_pad(event_probe, "src");
|
||||||
gst_pad_add_event_probe(pad, G_CALLBACK(EventHandoffCallback), this);
|
gst_pad_add_probe(
|
||||||
|
pad, GST_PAD_PROBE_TYPE_EVENT_UPSTREAM, &EventHandoffCallback, this, NULL);
|
||||||
gst_object_unref(pad);
|
gst_object_unref(pad);
|
||||||
|
|
||||||
// Configure the fakesink properly
|
// Configure the fakesink properly
|
||||||
|
@ -296,7 +297,7 @@ bool GstEnginePipeline::Init() {
|
||||||
|
|
||||||
int last_band_frequency = 0;
|
int last_band_frequency = 0;
|
||||||
for (int i=0 ; i<kEqBandCount ; ++i) {
|
for (int i=0 ; i<kEqBandCount ; ++i) {
|
||||||
GstObject* band = gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(equalizer_), i);
|
GstObject* band = GST_OBJECT(gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(equalizer_), i));
|
||||||
|
|
||||||
const float frequency = kEqBandFrequencies[i];
|
const float frequency = kEqBandFrequencies[i];
|
||||||
const float bandwidth = frequency - last_band_frequency;
|
const float bandwidth = frequency - last_band_frequency;
|
||||||
|
@ -329,12 +330,14 @@ bool GstEnginePipeline::Init() {
|
||||||
|
|
||||||
// Create the caps to put in each path in the tee. The scope path gets 16-bit
|
// Create the caps to put in each path in the tee. The scope path gets 16-bit
|
||||||
// ints and the audiosink path gets float32.
|
// ints and the audiosink path gets float32.
|
||||||
GstCaps* caps16 = gst_caps_new_simple ("audio/x-raw-int",
|
GstCaps* caps16 = gst_caps_new_simple (
|
||||||
"width", G_TYPE_INT, 16,
|
"audio/x-raw",
|
||||||
"signed", G_TYPE_BOOLEAN, true,
|
"format", G_TYPE_STRING, "S16LE",
|
||||||
NULL);
|
NULL);
|
||||||
GstCaps* caps32 = gst_caps_new_simple ("audio/x-raw-float",
|
|
||||||
"width", G_TYPE_INT, 32,
|
GstCaps* caps32 = gst_caps_new_simple (
|
||||||
|
"audio/x-raw",
|
||||||
|
"format", G_TYPE_STRING, "F32LE",
|
||||||
NULL);
|
NULL);
|
||||||
if (mono_playback_) {
|
if (mono_playback_) {
|
||||||
gst_caps_set_simple(caps32, "channels", G_TYPE_INT, 1, NULL);
|
gst_caps_set_simple(caps32, "channels", G_TYPE_INT, 1, NULL);
|
||||||
|
@ -347,8 +350,8 @@ bool GstEnginePipeline::Init() {
|
||||||
gst_caps_unref(caps32);
|
gst_caps_unref(caps32);
|
||||||
|
|
||||||
// Link the outputs of tee to the queues on each path.
|
// Link the outputs of tee to the queues on each path.
|
||||||
gst_pad_link(gst_element_get_request_pad(tee, "src%d"), gst_element_get_static_pad(probe_queue, "sink"));
|
gst_pad_link(gst_element_get_request_pad(tee, "src_%u"), gst_element_get_static_pad(probe_queue, "sink"));
|
||||||
gst_pad_link(gst_element_get_request_pad(tee, "src%d"), gst_element_get_static_pad(audio_queue, "sink"));
|
gst_pad_link(gst_element_get_request_pad(tee, "src_%u"), gst_element_get_static_pad(audio_queue, "sink"));
|
||||||
|
|
||||||
// Link replaygain elements if enabled.
|
// Link replaygain elements if enabled.
|
||||||
if (rg_enabled_) {
|
if (rg_enabled_) {
|
||||||
|
@ -361,8 +364,9 @@ bool GstEnginePipeline::Init() {
|
||||||
volume_, audioscale_, convert, audiosink_, NULL);
|
volume_, audioscale_, convert, audiosink_, NULL);
|
||||||
|
|
||||||
// Add probes and handlers.
|
// Add probes and handlers.
|
||||||
gst_pad_add_buffer_probe(gst_element_get_static_pad(probe_converter, "src"), G_CALLBACK(HandoffCallback), this);
|
gst_pad_add_probe(gst_element_get_static_pad(probe_converter, "src"), GST_PAD_PROBE_TYPE_BUFFER, &HandoffCallback, this, NULL);
|
||||||
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallbackSync, this);
|
gst_bus_set_sync_handler(
|
||||||
|
gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallbackSync, this, NULL);
|
||||||
bus_cb_id_ = gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this);
|
bus_cb_id_ = gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this);
|
||||||
|
|
||||||
MaybeLinkDecodeToAudio();
|
MaybeLinkDecodeToAudio();
|
||||||
|
@ -421,7 +425,8 @@ bool GstEnginePipeline::InitFromUrl(const QUrl &url, qint64 end_nanosec) {
|
||||||
|
|
||||||
GstEnginePipeline::~GstEnginePipeline() {
|
GstEnginePipeline::~GstEnginePipeline() {
|
||||||
if (pipeline_) {
|
if (pipeline_) {
|
||||||
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), NULL, NULL);
|
gst_bus_set_sync_handler(
|
||||||
|
gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), NULL, NULL, NULL);
|
||||||
g_source_remove(bus_cb_id_);
|
g_source_remove(bus_cb_id_);
|
||||||
gst_element_set_state(pipeline_, GST_STATE_NULL);
|
gst_element_set_state(pipeline_, GST_STATE_NULL);
|
||||||
gst_object_unref(GST_OBJECT(pipeline_));
|
gst_object_unref(GST_OBJECT(pipeline_));
|
||||||
|
@ -505,12 +510,7 @@ void GstEnginePipeline::StreamStatusMessageReceived(GstMessage* msg) {
|
||||||
const GValue* val = gst_message_get_stream_status_object(msg);
|
const GValue* val = gst_message_get_stream_status_object(msg);
|
||||||
if (G_VALUE_TYPE(val) == GST_TYPE_TASK) {
|
if (G_VALUE_TYPE(val) == GST_TYPE_TASK) {
|
||||||
GstTask* task = static_cast<GstTask*>(g_value_get_object(val));
|
GstTask* task = static_cast<GstTask*>(g_value_get_object(val));
|
||||||
|
gst_task_set_enter_callback(task, &TaskEnterCallback, this, NULL);
|
||||||
GstTaskThreadCallbacks callbacks;
|
|
||||||
memset(&callbacks, 0, sizeof(callbacks));
|
|
||||||
callbacks.enter_thread = TaskEnterCallback;
|
|
||||||
|
|
||||||
gst_task_set_thread_callbacks(task, &callbacks, this, NULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -666,8 +666,10 @@ void GstEnginePipeline::NewPadCallback(GstElement*, GstPad* pad, gpointer self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GstEnginePipeline::HandoffCallback(GstPad*, GstBuffer* buf, gpointer self) {
|
GstPadProbeReturn GstEnginePipeline::HandoffCallback(
|
||||||
|
GstPad*, GstPadProbeInfo* info, gpointer self) {
|
||||||
GstEnginePipeline* instance = reinterpret_cast<GstEnginePipeline*>(self);
|
GstEnginePipeline* instance = reinterpret_cast<GstEnginePipeline*>(self);
|
||||||
|
GstBuffer* buf = gst_pad_probe_info_get_buffer(info);
|
||||||
|
|
||||||
QList<BufferConsumer*> consumers;
|
QList<BufferConsumer*> consumers;
|
||||||
{
|
{
|
||||||
|
@ -724,20 +726,22 @@ bool GstEnginePipeline::HandoffCallback(GstPad*, GstBuffer* buf, gpointer self)
|
||||||
|
|
||||||
instance->last_buffer_offset_ = GST_BUFFER_OFFSET(buf);
|
instance->last_buffer_offset_ = GST_BUFFER_OFFSET(buf);
|
||||||
|
|
||||||
return true;
|
return GST_PAD_PROBE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GstEnginePipeline::EventHandoffCallback(GstPad*, GstEvent* e, gpointer self) {
|
GstPadProbeReturn GstEnginePipeline::EventHandoffCallback(
|
||||||
|
GstPad*, GstPadProbeInfo* info, gpointer self) {
|
||||||
GstEnginePipeline* instance = reinterpret_cast<GstEnginePipeline*>(self);
|
GstEnginePipeline* instance = reinterpret_cast<GstEnginePipeline*>(self);
|
||||||
|
GstEvent* e = gst_pad_probe_info_get_event(info);
|
||||||
|
|
||||||
qLog(Debug) << instance->id() << "event" << GST_EVENT_TYPE_NAME(e);
|
qLog(Debug) << instance->id() << "event" << GST_EVENT_TYPE_NAME(e);
|
||||||
|
|
||||||
if (GST_EVENT_TYPE(e) == GST_EVENT_NEWSEGMENT && !instance->segment_start_received_) {
|
if (GST_EVENT_TYPE(e) == GST_EVENT_SEGMENT && !instance->segment_start_received_) {
|
||||||
// The segment start time is used to calculate the proper offset of data
|
// The segment start time is used to calculate the proper offset of data
|
||||||
// buffers from the start of the stream
|
// buffers from the start of the stream
|
||||||
gint64 start = 0;
|
const GstSegment* segment = NULL;
|
||||||
gst_event_parse_new_segment(e, NULL, NULL, NULL, &start, NULL, NULL);
|
gst_event_parse_segment(e, &segment);
|
||||||
instance->segment_start_ = start;
|
instance->segment_start_ = segment->start;
|
||||||
instance->segment_start_received_ = true;
|
instance->segment_start_received_ = true;
|
||||||
|
|
||||||
if (instance->emit_track_ended_on_segment_start_) {
|
if (instance->emit_track_ended_on_segment_start_) {
|
||||||
|
@ -748,7 +752,7 @@ bool GstEnginePipeline::EventHandoffCallback(GstPad*, GstEvent* e, gpointer self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return GST_PAD_PROBE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GstEnginePipeline::SourceDrainedCallback(GstURIDecodeBin* bin, gpointer self) {
|
void GstEnginePipeline::SourceDrainedCallback(GstURIDecodeBin* bin, gpointer self) {
|
||||||
|
@ -836,17 +840,15 @@ void GstEnginePipeline::TransitionToNext() {
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 GstEnginePipeline::position() const {
|
qint64 GstEnginePipeline::position() const {
|
||||||
GstFormat fmt = GST_FORMAT_TIME;
|
|
||||||
gint64 value = 0;
|
gint64 value = 0;
|
||||||
gst_element_query_position(pipeline_, &fmt, &value);
|
gst_element_query_position(pipeline_, GST_FORMAT_TIME, &value);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 GstEnginePipeline::length() const {
|
qint64 GstEnginePipeline::length() const {
|
||||||
GstFormat fmt = GST_FORMAT_TIME;
|
|
||||||
gint64 value = 0;
|
gint64 value = 0;
|
||||||
gst_element_query_duration(pipeline_, &fmt, &value);
|
gst_element_query_duration(pipeline_, GST_FORMAT_TIME, &value);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -906,7 +908,8 @@ void GstEnginePipeline::UpdateEqualizer() {
|
||||||
else
|
else
|
||||||
gain *= 0.12;
|
gain *= 0.12;
|
||||||
|
|
||||||
GstObject* band = gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(equalizer_), i);
|
GstObject* band = GST_OBJECT(
|
||||||
|
gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(equalizer_), i));
|
||||||
g_object_set(G_OBJECT(band), "gain", gain, NULL);
|
g_object_set(G_OBJECT(band), "gain", gain, NULL);
|
||||||
g_object_unref(G_OBJECT(band));
|
g_object_unref(G_OBJECT(band));
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,8 +126,8 @@ class GstEnginePipeline : public QObject {
|
||||||
static GstBusSyncReply BusCallbackSync(GstBus*, GstMessage*, gpointer);
|
static GstBusSyncReply BusCallbackSync(GstBus*, GstMessage*, gpointer);
|
||||||
static gboolean BusCallback(GstBus*, GstMessage*, gpointer);
|
static gboolean BusCallback(GstBus*, GstMessage*, gpointer);
|
||||||
static void NewPadCallback(GstElement*, GstPad*, gpointer);
|
static void NewPadCallback(GstElement*, GstPad*, gpointer);
|
||||||
static bool HandoffCallback(GstPad*, GstBuffer*, gpointer);
|
static GstPadProbeReturn HandoffCallback(GstPad*, GstPadProbeInfo*, gpointer);
|
||||||
static bool EventHandoffCallback(GstPad*, GstEvent*, gpointer);
|
static GstPadProbeReturn EventHandoffCallback(GstPad*, GstPadProbeInfo*, gpointer);
|
||||||
static void SourceDrainedCallback(GstURIDecodeBin*, gpointer);
|
static void SourceDrainedCallback(GstURIDecodeBin*, gpointer);
|
||||||
static void SourceSetupCallback(GstURIDecodeBin*, GParamSpec *pspec, gpointer);
|
static void SourceSetupCallback(GstURIDecodeBin*, GParamSpec *pspec, gpointer);
|
||||||
static void TaskEnterCallback(GstTask*, GThread*, gpointer);
|
static void TaskEnterCallback(GstTask*, GThread*, gpointer);
|
||||||
|
|
|
@ -108,12 +108,13 @@ void MoodbarPipeline::Start() {
|
||||||
|
|
||||||
// Connect signals
|
// Connect signals
|
||||||
CHECKED_GCONNECT(decodebin, "pad-added", &NewPadCallback, this);
|
CHECKED_GCONNECT(decodebin, "pad-added", &NewPadCallback, this);
|
||||||
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallbackSync, this);
|
gst_bus_set_sync_handler(
|
||||||
|
gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallbackSync, this, NULL);
|
||||||
|
|
||||||
// Set appsink callbacks
|
// Set appsink callbacks
|
||||||
GstAppSinkCallbacks callbacks;
|
GstAppSinkCallbacks callbacks;
|
||||||
memset(&callbacks, 0, sizeof(callbacks));
|
memset(&callbacks, 0, sizeof(callbacks));
|
||||||
callbacks.new_buffer = NewBufferCallback;
|
callbacks.new_sample = NewBufferCallback;
|
||||||
|
|
||||||
gst_app_sink_set_callbacks(reinterpret_cast<GstAppSink*>(appsink), &callbacks, this, NULL);
|
gst_app_sink_set_callbacks(reinterpret_cast<GstAppSink*>(appsink), &callbacks, this, NULL);
|
||||||
|
|
||||||
|
@ -151,8 +152,12 @@ void MoodbarPipeline::NewPadCallback(GstElement*, GstPad* pad, gpointer data) {
|
||||||
GstFlowReturn MoodbarPipeline::NewBufferCallback(GstAppSink* app_sink, gpointer data) {
|
GstFlowReturn MoodbarPipeline::NewBufferCallback(GstAppSink* app_sink, gpointer data) {
|
||||||
MoodbarPipeline* self = reinterpret_cast<MoodbarPipeline*>(data);
|
MoodbarPipeline* self = reinterpret_cast<MoodbarPipeline*>(data);
|
||||||
|
|
||||||
GstBuffer* buffer = gst_app_sink_pull_buffer(app_sink);
|
GstSample* sample = gst_app_sink_pull_sample(app_sink);
|
||||||
self->data_.append(reinterpret_cast<const char*>(buffer->data), buffer->size);
|
GstBuffer* buffer = gst_sample_get_buffer(sample);
|
||||||
|
GstMapInfo map;
|
||||||
|
gst_buffer_map(buffer, &map, GST_MAP_READ);
|
||||||
|
self->data_.append(reinterpret_cast<const char*>(map.data), map.size);
|
||||||
|
gst_buffer_unmap(buffer, &map);
|
||||||
gst_buffer_unref(buffer);
|
gst_buffer_unref(buffer);
|
||||||
|
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
|
@ -187,7 +192,8 @@ void MoodbarPipeline::Cleanup() {
|
||||||
Q_ASSERT(QThread::currentThread() != qApp->thread());
|
Q_ASSERT(QThread::currentThread() != qApp->thread());
|
||||||
|
|
||||||
if (pipeline_) {
|
if (pipeline_) {
|
||||||
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), NULL, NULL);
|
gst_bus_set_sync_handler(
|
||||||
|
gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), NULL, NULL, NULL);
|
||||||
gst_element_set_state(pipeline_, GST_STATE_NULL);
|
gst_element_set_state(pipeline_, GST_STATE_NULL);
|
||||||
gst_object_unref(pipeline_);
|
gst_object_unref(pipeline_);
|
||||||
pipeline_ = NULL;
|
pipeline_ = NULL;
|
||||||
|
|
|
@ -70,7 +70,7 @@ QString Chromaprinter::CreateFingerprint() {
|
||||||
|
|
||||||
pipeline_ = gst_pipeline_new("pipeline");
|
pipeline_ = gst_pipeline_new("pipeline");
|
||||||
GstElement* src = CreateElement("filesrc", pipeline_);
|
GstElement* src = CreateElement("filesrc", pipeline_);
|
||||||
GstElement* decode = CreateElement("decodebin2", pipeline_);
|
GstElement* decode = CreateElement("decodebin", pipeline_);
|
||||||
GstElement* convert = CreateElement("audioconvert", pipeline_);
|
GstElement* convert = CreateElement("audioconvert", pipeline_);
|
||||||
GstElement* resample = CreateElement("audioresample", pipeline_);
|
GstElement* resample = CreateElement("audioresample", pipeline_);
|
||||||
GstElement* sink = CreateElement("appsink", pipeline_);
|
GstElement* sink = CreateElement("appsink", pipeline_);
|
||||||
|
@ -85,10 +85,10 @@ QString Chromaprinter::CreateFingerprint() {
|
||||||
gst_element_link_many(src, decode, NULL);
|
gst_element_link_many(src, decode, NULL);
|
||||||
gst_element_link_many(convert, resample, NULL);
|
gst_element_link_many(convert, resample, NULL);
|
||||||
|
|
||||||
// Chromaprint expects mono floats at a sample rate of 11025Hz.
|
// Chromaprint expects mono 16-bit ints at a sample rate of 11025Hz.
|
||||||
GstCaps* caps = gst_caps_new_simple(
|
GstCaps* caps = gst_caps_new_simple(
|
||||||
"audio/x-raw-int",
|
"audio/x-raw",
|
||||||
"width", G_TYPE_INT, 16,
|
"format", G_TYPE_STRING, "S16LE",
|
||||||
"channels", G_TYPE_INT, kDecodeChannels,
|
"channels", G_TYPE_INT, kDecodeChannels,
|
||||||
"rate", G_TYPE_INT, kDecodeRate,
|
"rate", G_TYPE_INT, kDecodeRate,
|
||||||
NULL);
|
NULL);
|
||||||
|
@ -97,7 +97,7 @@ QString Chromaprinter::CreateFingerprint() {
|
||||||
|
|
||||||
GstAppSinkCallbacks callbacks;
|
GstAppSinkCallbacks callbacks;
|
||||||
memset(&callbacks, 0, sizeof(callbacks));
|
memset(&callbacks, 0, sizeof(callbacks));
|
||||||
callbacks.new_buffer = NewBufferCallback;
|
callbacks.new_sample = NewBufferCallback;
|
||||||
gst_app_sink_set_callbacks(reinterpret_cast<GstAppSink*>(sink), &callbacks, this, NULL);
|
gst_app_sink_set_callbacks(reinterpret_cast<GstAppSink*>(sink), &callbacks, this, NULL);
|
||||||
g_object_set(G_OBJECT(sink), "sync", FALSE, NULL);
|
g_object_set(G_OBJECT(sink), "sync", FALSE, NULL);
|
||||||
g_object_set(G_OBJECT(sink), "emit-signals", TRUE, NULL);
|
g_object_set(G_OBJECT(sink), "emit-signals", TRUE, NULL);
|
||||||
|
@ -106,8 +106,9 @@ QString Chromaprinter::CreateFingerprint() {
|
||||||
g_object_set(src, "location", filename_.toUtf8().constData(), NULL);
|
g_object_set(src, "location", filename_.toUtf8().constData(), NULL);
|
||||||
|
|
||||||
// Connect signals
|
// Connect signals
|
||||||
CHECKED_GCONNECT(decode, "new-decoded-pad", &NewPadCallback, this);
|
CHECKED_GCONNECT(decode, "pad-added", &NewPadCallback, this);
|
||||||
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallbackSync, this);
|
gst_bus_set_sync_handler(
|
||||||
|
gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallbackSync, this, NULL);
|
||||||
guint bus_callback_id = gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this);
|
guint bus_callback_id = gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this);
|
||||||
|
|
||||||
QTime time;
|
QTime time;
|
||||||
|
@ -151,9 +152,10 @@ QString Chromaprinter::CreateFingerprint() {
|
||||||
qLog(Debug) << "Decode time:" << decode_time << "Codegen time:" << codegen_time;
|
qLog(Debug) << "Decode time:" << decode_time << "Codegen time:" << codegen_time;
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
callbacks.new_buffer = NULL;
|
callbacks.new_sample = NULL;
|
||||||
gst_app_sink_set_callbacks(reinterpret_cast<GstAppSink*>(sink), &callbacks, this, NULL);
|
gst_app_sink_set_callbacks(reinterpret_cast<GstAppSink*>(sink), &callbacks, this, NULL);
|
||||||
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), NULL, NULL);
|
gst_bus_set_sync_handler(
|
||||||
|
gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), NULL, NULL, NULL);
|
||||||
g_source_remove(bus_callback_id);
|
g_source_remove(bus_callback_id);
|
||||||
gst_element_set_state(pipeline_, GST_STATE_NULL);
|
gst_element_set_state(pipeline_, GST_STATE_NULL);
|
||||||
gst_object_unref(pipeline_);
|
gst_object_unref(pipeline_);
|
||||||
|
@ -161,7 +163,7 @@ QString Chromaprinter::CreateFingerprint() {
|
||||||
return fingerprint;
|
return fingerprint;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chromaprinter::NewPadCallback(GstElement*, GstPad* pad, gboolean, gpointer data) {
|
void Chromaprinter::NewPadCallback(GstElement*, GstPad* pad, gpointer data) {
|
||||||
Chromaprinter* instance = reinterpret_cast<Chromaprinter*>(data);
|
Chromaprinter* instance = reinterpret_cast<Chromaprinter*>(data);
|
||||||
GstPad* const audiopad = gst_element_get_static_pad(
|
GstPad* const audiopad = gst_element_get_static_pad(
|
||||||
instance->convert_element_, "sink");
|
instance->convert_element_, "sink");
|
||||||
|
@ -228,13 +230,16 @@ GstFlowReturn Chromaprinter::NewBufferCallback(GstAppSink* app_sink, gpointer se
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
GstBuffer* buffer = gst_app_sink_pull_buffer(app_sink);
|
GstSample* sample = gst_app_sink_pull_sample(app_sink);
|
||||||
me->buffer_.write(reinterpret_cast<const char*>(buffer->data), buffer->size);
|
GstBuffer* buffer = gst_sample_get_buffer(sample);
|
||||||
|
GstMapInfo map;
|
||||||
|
gst_buffer_map(buffer, &map, GST_MAP_READ);
|
||||||
|
me->buffer_.write(reinterpret_cast<const char*>(map.data), map.size);
|
||||||
|
gst_buffer_unmap(buffer, &map);
|
||||||
gst_buffer_unref(buffer);
|
gst_buffer_unref(buffer);
|
||||||
|
|
||||||
gint64 pos = 0;
|
gint64 pos = 0;
|
||||||
GstFormat format = GST_FORMAT_TIME;
|
gboolean ret = gst_element_query_position(me->pipeline_, GST_FORMAT_TIME, &pos);
|
||||||
gboolean ret = gst_element_query_position(me->pipeline_, &format, &pos);
|
|
||||||
if (ret && pos > 30 * kNsecPerSec) {
|
if (ret && pos > 30 * kNsecPerSec) {
|
||||||
me->finishing_ = true;
|
me->finishing_ = true;
|
||||||
g_main_loop_quit(me->event_loop_);
|
g_main_loop_quit(me->event_loop_);
|
||||||
|
|
|
@ -49,7 +49,7 @@ private:
|
||||||
|
|
||||||
void ReportError(GstMessage* message);
|
void ReportError(GstMessage* message);
|
||||||
|
|
||||||
static void NewPadCallback(GstElement*, GstPad* pad, gboolean, gpointer data);
|
static void NewPadCallback(GstElement*, GstPad* pad, gpointer data);
|
||||||
static gboolean BusCallback(GstBus*, GstMessage* msg, gpointer data);
|
static gboolean BusCallback(GstBus*, GstMessage* msg, gpointer data);
|
||||||
static GstBusSyncReply BusCallbackSync(GstBus*, GstMessage* msg, gpointer data);
|
static GstBusSyncReply BusCallbackSync(GstBus*, GstMessage* msg, gpointer data);
|
||||||
static GstFlowReturn NewBufferCallback(GstAppSink* app_sink, gpointer self);
|
static GstFlowReturn NewBufferCallback(GstAppSink* app_sink, gpointer self);
|
||||||
|
|
|
@ -100,7 +100,7 @@ GstElement* Transcoder::CreateElementForMimeType(const QString& element_type,
|
||||||
// The caps we're trying to find
|
// The caps we're trying to find
|
||||||
GstCaps* target_caps = gst_caps_from_string(mime_type.toUtf8().constData());
|
GstCaps* target_caps = gst_caps_from_string(mime_type.toUtf8().constData());
|
||||||
|
|
||||||
GstRegistry* registry = gst_registry_get_default();
|
GstRegistry* registry = gst_registry_get();
|
||||||
GList* const features =
|
GList* const features =
|
||||||
gst_registry_get_feature_list(registry, GST_TYPE_ELEMENT_FACTORY);
|
gst_registry_get_feature_list(registry, GST_TYPE_ELEMENT_FACTORY);
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ GstElement* Transcoder::CreateElementForMimeType(const QString& element_type,
|
||||||
GstElementFactory* factory = GST_ELEMENT_FACTORY(p->data);
|
GstElementFactory* factory = GST_ELEMENT_FACTORY(p->data);
|
||||||
|
|
||||||
// Is this the right type of plugin?
|
// Is this the right type of plugin?
|
||||||
if (QString(factory->details.klass).contains(element_type)) {
|
if (QString(gst_element_factory_get_klass(factory)).contains(element_type)) {
|
||||||
const GList* const templates =
|
const GList* const templates =
|
||||||
gst_element_factory_get_static_pad_templates(factory);
|
gst_element_factory_get_static_pad_templates(factory);
|
||||||
for (const GList* p = templates ; p ; p = g_list_next(p)) {
|
for (const GList* p = templates ; p ; p = g_list_next(p)) {
|
||||||
|
@ -124,7 +124,7 @@ GstElement* Transcoder::CreateElementForMimeType(const QString& element_type,
|
||||||
if (intersection) {
|
if (intersection) {
|
||||||
if (!gst_caps_is_empty(intersection)) {
|
if (!gst_caps_is_empty(intersection)) {
|
||||||
int rank = gst_plugin_feature_get_rank(GST_PLUGIN_FEATURE(factory));
|
int rank = gst_plugin_feature_get_rank(GST_PLUGIN_FEATURE(factory));
|
||||||
QString name = GST_PLUGIN_FEATURE_NAME(factory);
|
QString name = GST_OBJECT_NAME(factory);
|
||||||
|
|
||||||
if (name.startsWith("ffmux") || name.startsWith("ffenc"))
|
if (name.startsWith("ffmux") || name.startsWith("ffenc"))
|
||||||
rank = -1; // ffmpeg usually sucks
|
rank = -1; // ffmpeg usually sucks
|
||||||
|
@ -451,7 +451,8 @@ bool Transcoder::StartJob(const Job &job) {
|
||||||
state->convert_element_ = convert;
|
state->convert_element_ = convert;
|
||||||
|
|
||||||
CHECKED_GCONNECT(decode, "new-decoded-pad", &NewPadCallback, state.get());
|
CHECKED_GCONNECT(decode, "new-decoded-pad", &NewPadCallback, state.get());
|
||||||
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(state->pipeline_)), BusCallbackSync, state.get());
|
gst_bus_set_sync_handler(
|
||||||
|
gst_pipeline_get_bus(GST_PIPELINE(state->pipeline_)), BusCallbackSync, state.get(), NULL);
|
||||||
state->bus_callback_id_ = gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(state->pipeline_)), BusCallback, state.get());
|
state->bus_callback_id_ = gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(state->pipeline_)), BusCallback, state.get());
|
||||||
|
|
||||||
// Start the pipeline
|
// Start the pipeline
|
||||||
|
@ -494,7 +495,7 @@ bool Transcoder::event(QEvent* e) {
|
||||||
// Remove event handlers from the gstreamer pipeline so they don't get
|
// Remove event handlers from the gstreamer pipeline so they don't get
|
||||||
// called after the pipeline is shutting down
|
// called after the pipeline is shutting down
|
||||||
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(
|
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(
|
||||||
finished_event->state_->pipeline_)), NULL, NULL);
|
finished_event->state_->pipeline_)), NULL, NULL, NULL);
|
||||||
g_source_remove(finished_event->state_->bus_callback_id_);
|
g_source_remove(finished_event->state_->bus_callback_id_);
|
||||||
|
|
||||||
// Remove it from the list - this will also destroy the GStreamer pipeline
|
// Remove it from the list - this will also destroy the GStreamer pipeline
|
||||||
|
@ -524,7 +525,7 @@ void Transcoder::Cancel() {
|
||||||
// Remove event handlers from the gstreamer pipeline so they don't get
|
// Remove event handlers from the gstreamer pipeline so they don't get
|
||||||
// called after the pipeline is shutting down
|
// called after the pipeline is shutting down
|
||||||
gst_bus_set_sync_handler(gst_pipeline_get_bus(
|
gst_bus_set_sync_handler(gst_pipeline_get_bus(
|
||||||
GST_PIPELINE(state->pipeline_)), NULL, NULL);
|
GST_PIPELINE(state->pipeline_)), NULL, NULL, NULL);
|
||||||
g_source_remove(state->bus_callback_id_);
|
g_source_remove(state->bus_callback_id_);
|
||||||
|
|
||||||
// Stop the pipeline
|
// Stop the pipeline
|
||||||
|
@ -547,10 +548,9 @@ QMap<QString, float> Transcoder::GetProgress() const {
|
||||||
|
|
||||||
gint64 position = 0;
|
gint64 position = 0;
|
||||||
gint64 duration = 0;
|
gint64 duration = 0;
|
||||||
GstFormat format = GST_FORMAT_TIME;
|
|
||||||
|
|
||||||
gst_element_query_position(state->pipeline_, &format, &position);
|
gst_element_query_position(state->pipeline_, GST_FORMAT_TIME, &position);
|
||||||
gst_element_query_duration(state->pipeline_, &format, &duration);
|
gst_element_query_duration(state->pipeline_, GST_FORMAT_TIME, &duration);
|
||||||
|
|
||||||
ret[state->job_.input] = float(position) / duration;
|
ret[state->job_.input] = float(position) / duration;
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,7 +140,9 @@
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
#ifdef HAVE_AUDIOCD
|
||||||
#include <gst/cdda/gstcddabasesrc.h>
|
#include <gst/cdda/gstcddabasesrc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
using boost::shared_ptr;
|
using boost::shared_ptr;
|
||||||
using boost::scoped_ptr;
|
using boost::scoped_ptr;
|
||||||
|
|
|
@ -162,11 +162,15 @@ void ProjectMVisualisation::SetDuration(int seconds) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectMVisualisation::ConsumeBuffer(GstBuffer* buffer, int) {
|
void ProjectMVisualisation::ConsumeBuffer(GstBuffer* buffer, int) {
|
||||||
const int samples_per_channel = GST_BUFFER_SIZE(buffer) / sizeof(short) / 2;
|
GstMapInfo map;
|
||||||
const short* data = reinterpret_cast<short*>(GST_BUFFER_DATA(buffer));
|
gst_buffer_map(buffer, &map, GST_MAP_READ);
|
||||||
|
const int samples_per_channel = map.size / sizeof(short) / 2;
|
||||||
|
const short* data = reinterpret_cast<short*>(map.data);
|
||||||
|
|
||||||
if (projectm_)
|
if (projectm_)
|
||||||
projectm_->pcm()->addPCM16Data(data, samples_per_channel);
|
projectm_->pcm()->addPCM16Data(data, samples_per_channel);
|
||||||
|
|
||||||
|
gst_buffer_unmap(buffer, &map);
|
||||||
gst_buffer_unref(buffer);
|
gst_buffer_unref(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue