mirror of
https://github.com/brl/mutter.git
synced 2025-01-03 08:12:15 +00:00
2006-08-30 Jorn Baayen <jorn@openedhand.com>
* clutter/Makefile.am: * clutter/clutter-audio.c: * clutter/clutter-audio.h: * clutter/clutter.h: Added ClutterAudio audio playback object.
This commit is contained in:
parent
3afb377c3e
commit
f907bcee1e
@ -1,3 +1,12 @@
|
|||||||
|
2006-08-30 Jorn Baayen <jorn@openedhand.com>
|
||||||
|
|
||||||
|
* clutter/Makefile.am:
|
||||||
|
* clutter/clutter-audio.c:
|
||||||
|
* clutter/clutter-audio.h:
|
||||||
|
* clutter/clutter.h:
|
||||||
|
|
||||||
|
Added ClutterAudio audio playback object.
|
||||||
|
|
||||||
2006-08-29 Matthew Allum <mallum@openedhand.com>
|
2006-08-29 Matthew Allum <mallum@openedhand.com>
|
||||||
|
|
||||||
* clutter/clutter-fixed.h:
|
* clutter/clutter-fixed.h:
|
||||||
|
@ -25,6 +25,7 @@ source_h = \
|
|||||||
$(srcdir)/clutter-behaviour.h \
|
$(srcdir)/clutter-behaviour.h \
|
||||||
$(srcdir)/clutter-behaviours.h \
|
$(srcdir)/clutter-behaviours.h \
|
||||||
$(srcdir)/clutter-alpha.h \
|
$(srcdir)/clutter-alpha.h \
|
||||||
|
$(srcdir)/clutter-audio.h \
|
||||||
$(srcdir)/clutter-main.h
|
$(srcdir)/clutter-main.h
|
||||||
|
|
||||||
clutter-marshal.h: clutter-marshal.list
|
clutter-marshal.h: clutter-marshal.list
|
||||||
@ -90,6 +91,7 @@ source_c = clutter-main.c \
|
|||||||
clutter-behaviour.c \
|
clutter-behaviour.c \
|
||||||
clutter-behaviours.c \
|
clutter-behaviours.c \
|
||||||
clutter-alpha.c \
|
clutter-alpha.c \
|
||||||
|
clutter-audio.c \
|
||||||
clutter-enum-types.c
|
clutter-enum-types.c
|
||||||
|
|
||||||
source_h_priv = clutter-private.h
|
source_h_priv = clutter-private.h
|
||||||
|
818
clutter/clutter-audio.c
Normal file
818
clutter/clutter-audio.c
Normal file
@ -0,0 +1,818 @@
|
|||||||
|
/*
|
||||||
|
* Clutter.
|
||||||
|
*
|
||||||
|
* An OpenGL based 'interactive canvas' library.
|
||||||
|
*
|
||||||
|
* Authored By Matthew Allum <mallum@openedhand.com>
|
||||||
|
* Jorn Baayen <jorn@openedhand.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006 OpenedHand
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SECTION:clutter-audio
|
||||||
|
* @short_description: Object for playback of audio files.
|
||||||
|
*
|
||||||
|
* #ClutterAudio is an object that plays audio files.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "clutter-audio.h"
|
||||||
|
#include "clutter-main.h"
|
||||||
|
#include "clutter-private.h" /* for DBG */
|
||||||
|
#include "clutter-marshal.h"
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <gst/audio/gstbaseaudiosink.h>
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
struct _ClutterAudioPrivate
|
||||||
|
{
|
||||||
|
GstElement *playbin;
|
||||||
|
char *uri;
|
||||||
|
gboolean can_seek;
|
||||||
|
int buffer_percent;
|
||||||
|
int duration;
|
||||||
|
guint tick_timeout_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROP_0,
|
||||||
|
/* ClutterMedia proprs */
|
||||||
|
PROP_URI,
|
||||||
|
PROP_PLAYING,
|
||||||
|
PROP_POSITION,
|
||||||
|
PROP_VOLUME,
|
||||||
|
PROP_CAN_SEEK,
|
||||||
|
PROP_BUFFER_PERCENT,
|
||||||
|
PROP_DURATION
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#define TICK_TIMEOUT 0.5
|
||||||
|
|
||||||
|
static void clutter_media_init (ClutterMediaInterface *iface);
|
||||||
|
|
||||||
|
static gboolean tick_timeout (ClutterAudio *audio);
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_EXTENDED (ClutterAudio, \
|
||||||
|
clutter_audio, \
|
||||||
|
G_TYPE_OBJECT, \
|
||||||
|
0, \
|
||||||
|
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_MEDIA, \
|
||||||
|
clutter_media_init));
|
||||||
|
|
||||||
|
/* Interface implementation */
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_uri (ClutterMedia *media,
|
||||||
|
const char *uri)
|
||||||
|
{
|
||||||
|
ClutterAudio *audio = CLUTTER_AUDIO(media);
|
||||||
|
ClutterAudioPrivate *priv;
|
||||||
|
GstState state, pending;
|
||||||
|
|
||||||
|
g_return_if_fail (CLUTTER_IS_AUDIO (audio));
|
||||||
|
|
||||||
|
priv = audio->priv;
|
||||||
|
|
||||||
|
if (!priv->playbin)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_free (priv->uri);
|
||||||
|
|
||||||
|
if (uri)
|
||||||
|
{
|
||||||
|
priv->uri = g_strdup (uri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure the tick timeout is installed.
|
||||||
|
*
|
||||||
|
* We also have it installed in PAUSED state, because
|
||||||
|
* seeks etc may have a delayed effect on the position.
|
||||||
|
**/
|
||||||
|
if (priv->tick_timeout_id == 0)
|
||||||
|
{
|
||||||
|
priv->tick_timeout_id = g_timeout_add (TICK_TIMEOUT * 1000,
|
||||||
|
(GSourceFunc) tick_timeout,
|
||||||
|
audio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
priv->uri = NULL;
|
||||||
|
|
||||||
|
if (priv->tick_timeout_id > 0)
|
||||||
|
{
|
||||||
|
g_source_remove (priv->tick_timeout_id);
|
||||||
|
priv->tick_timeout_id = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->can_seek = FALSE;
|
||||||
|
priv->duration = 0;
|
||||||
|
|
||||||
|
gst_element_get_state (priv->playbin, &state, &pending, 0);
|
||||||
|
|
||||||
|
if (pending)
|
||||||
|
state = pending;
|
||||||
|
|
||||||
|
gst_element_set_state (priv->playbin, GST_STATE_NULL);
|
||||||
|
|
||||||
|
g_object_set (priv->playbin,
|
||||||
|
"uri", uri,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore state.
|
||||||
|
**/
|
||||||
|
if (uri)
|
||||||
|
gst_element_set_state (priv->playbin, state);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Emit notififications for all these to make sure UI is not showing
|
||||||
|
* any properties of the old URI.
|
||||||
|
*/
|
||||||
|
g_object_notify (G_OBJECT (audio), "uri");
|
||||||
|
g_object_notify (G_OBJECT (audio), "can-seek");
|
||||||
|
g_object_notify (G_OBJECT (audio), "duration");
|
||||||
|
g_object_notify (G_OBJECT (audio), "position");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
get_uri (ClutterMedia *media)
|
||||||
|
{
|
||||||
|
ClutterAudio *audio = CLUTTER_AUDIO(media);
|
||||||
|
ClutterAudioPrivate *priv;
|
||||||
|
|
||||||
|
g_return_val_if_fail (CLUTTER_IS_AUDIO (audio), NULL);
|
||||||
|
|
||||||
|
priv = audio->priv;
|
||||||
|
|
||||||
|
return priv->uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_playing (ClutterMedia *media,
|
||||||
|
gboolean playing)
|
||||||
|
{
|
||||||
|
ClutterAudio *audio = CLUTTER_AUDIO(media);
|
||||||
|
ClutterAudioPrivate *priv;
|
||||||
|
|
||||||
|
g_return_if_fail (CLUTTER_IS_AUDIO (audio));
|
||||||
|
|
||||||
|
priv = audio->priv;
|
||||||
|
|
||||||
|
if (!priv->playbin)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (priv->uri)
|
||||||
|
{
|
||||||
|
GstState state;
|
||||||
|
|
||||||
|
if (playing)
|
||||||
|
state = GST_STATE_PLAYING;
|
||||||
|
else
|
||||||
|
state = GST_STATE_PAUSED;
|
||||||
|
|
||||||
|
gst_element_set_state (audio->priv->playbin, state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (playing)
|
||||||
|
g_warning ("Tried to play, but no URI is loaded.");
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_notify (G_OBJECT (audio), "playing");
|
||||||
|
g_object_notify (G_OBJECT (audio), "position");
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
get_playing (ClutterMedia *media)
|
||||||
|
{
|
||||||
|
ClutterAudio *audio = CLUTTER_AUDIO(media);
|
||||||
|
ClutterAudioPrivate *priv;
|
||||||
|
GstState state, pending;
|
||||||
|
|
||||||
|
g_return_val_if_fail (CLUTTER_IS_AUDIO (audio), FALSE);
|
||||||
|
|
||||||
|
priv = audio->priv;
|
||||||
|
|
||||||
|
if (!priv->playbin)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
gst_element_get_state (priv->playbin, &state, &pending, 0);
|
||||||
|
|
||||||
|
if (pending)
|
||||||
|
return (pending == GST_STATE_PLAYING);
|
||||||
|
else
|
||||||
|
return (state == GST_STATE_PLAYING);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_position (ClutterMedia *media,
|
||||||
|
int position) /* seconds */
|
||||||
|
{
|
||||||
|
ClutterAudio *audio = CLUTTER_AUDIO(media);
|
||||||
|
ClutterAudioPrivate *priv;
|
||||||
|
GstState state, pending;
|
||||||
|
|
||||||
|
g_return_if_fail (CLUTTER_IS_AUDIO (audio));
|
||||||
|
|
||||||
|
priv = audio->priv;
|
||||||
|
|
||||||
|
if (!priv->playbin)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gst_element_get_state (priv->playbin, &state, &pending, 0);
|
||||||
|
|
||||||
|
if (pending)
|
||||||
|
state = pending;
|
||||||
|
|
||||||
|
gst_element_set_state (priv->playbin, GST_STATE_PAUSED);
|
||||||
|
|
||||||
|
gst_element_seek (priv->playbin,
|
||||||
|
1.0,
|
||||||
|
GST_FORMAT_TIME,
|
||||||
|
GST_SEEK_FLAG_FLUSH,
|
||||||
|
GST_SEEK_TYPE_SET,
|
||||||
|
position * GST_SECOND,
|
||||||
|
0, 0);
|
||||||
|
|
||||||
|
gst_element_set_state (priv->playbin, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_position (ClutterMedia *media)
|
||||||
|
{
|
||||||
|
ClutterAudio *audio = CLUTTER_AUDIO(media);
|
||||||
|
ClutterAudioPrivate *priv;
|
||||||
|
GstQuery *query;
|
||||||
|
gint64 position;
|
||||||
|
|
||||||
|
g_return_val_if_fail (CLUTTER_IS_AUDIO (audio), -1);
|
||||||
|
|
||||||
|
priv = audio->priv;
|
||||||
|
|
||||||
|
if (!priv->playbin)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
query = gst_query_new_position (GST_FORMAT_TIME);
|
||||||
|
|
||||||
|
if (gst_element_query (priv->playbin, query))
|
||||||
|
gst_query_parse_position (query, NULL, &position);
|
||||||
|
else
|
||||||
|
position = 0;
|
||||||
|
|
||||||
|
gst_query_unref (query);
|
||||||
|
|
||||||
|
return (position / GST_SECOND);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_volume (ClutterMedia *media,
|
||||||
|
double volume)
|
||||||
|
{
|
||||||
|
ClutterAudio *audio = CLUTTER_AUDIO(media);
|
||||||
|
ClutterAudioPrivate *priv;
|
||||||
|
|
||||||
|
g_return_if_fail (CLUTTER_IS_AUDIO (audio));
|
||||||
|
// g_return_if_fail (volume >= 0.0 && volume <= GST_VOL_MAX);
|
||||||
|
|
||||||
|
priv = audio->priv;
|
||||||
|
|
||||||
|
if (!priv->playbin)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_object_set (G_OBJECT (audio->priv->playbin),
|
||||||
|
"volume", volume,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
g_object_notify (G_OBJECT (audio), "volume");
|
||||||
|
}
|
||||||
|
|
||||||
|
static double
|
||||||
|
get_volume (ClutterMedia *media)
|
||||||
|
{
|
||||||
|
ClutterAudio *audio = CLUTTER_AUDIO(media);
|
||||||
|
ClutterAudioPrivate *priv;
|
||||||
|
double volume;
|
||||||
|
|
||||||
|
g_return_val_if_fail (CLUTTER_IS_AUDIO (audio), 0.0);
|
||||||
|
|
||||||
|
priv = audio->priv;
|
||||||
|
|
||||||
|
if (!priv->playbin)
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
g_object_get (priv->playbin,
|
||||||
|
"volume", &volume,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
can_seek (ClutterMedia *media)
|
||||||
|
{
|
||||||
|
ClutterAudio *audio = CLUTTER_AUDIO(media);
|
||||||
|
|
||||||
|
g_return_val_if_fail (CLUTTER_IS_AUDIO (audio), FALSE);
|
||||||
|
|
||||||
|
return audio->priv->can_seek;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_buffer_percent (ClutterMedia *media)
|
||||||
|
{
|
||||||
|
ClutterAudio *audio = CLUTTER_AUDIO(media);
|
||||||
|
|
||||||
|
g_return_val_if_fail (CLUTTER_IS_AUDIO (audio), -1);
|
||||||
|
|
||||||
|
return audio->priv->buffer_percent;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_duration (ClutterMedia *media)
|
||||||
|
{
|
||||||
|
ClutterAudio *audio = CLUTTER_AUDIO(media);
|
||||||
|
|
||||||
|
g_return_val_if_fail (CLUTTER_IS_AUDIO (audio), -1);
|
||||||
|
|
||||||
|
return audio->priv->duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clutter_media_init (ClutterMediaInterface *iface)
|
||||||
|
{
|
||||||
|
iface->set_uri = set_uri;
|
||||||
|
iface->get_uri = get_uri;
|
||||||
|
iface->set_playing = set_playing;
|
||||||
|
iface->get_playing = get_playing;
|
||||||
|
iface->set_position = set_position;
|
||||||
|
iface->get_position = get_position;
|
||||||
|
iface->set_volume = set_volume;
|
||||||
|
iface->get_volume = get_volume;
|
||||||
|
iface->can_seek = can_seek;
|
||||||
|
iface->get_buffer_percent = get_buffer_percent;
|
||||||
|
iface->get_duration = get_duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clutter_audio_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
ClutterAudio *self;
|
||||||
|
ClutterAudioPrivate *priv;
|
||||||
|
|
||||||
|
self = CLUTTER_AUDIO(object);
|
||||||
|
priv = self->priv;
|
||||||
|
|
||||||
|
/* FIXME: flush an errors off bus ? */
|
||||||
|
/* gst_bus_set_flushing (priv->bus, TRUE); */
|
||||||
|
|
||||||
|
if (priv->playbin)
|
||||||
|
{
|
||||||
|
gst_element_set_state (priv->playbin, GST_STATE_NULL);
|
||||||
|
gst_object_unref (GST_OBJECT (priv->playbin));
|
||||||
|
priv->playbin = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priv->tick_timeout_id > 0)
|
||||||
|
{
|
||||||
|
g_source_remove (priv->tick_timeout_id);
|
||||||
|
priv->tick_timeout_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (clutter_audio_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clutter_audio_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
ClutterAudio *self;
|
||||||
|
ClutterAudioPrivate *priv;
|
||||||
|
|
||||||
|
self = CLUTTER_AUDIO(object);
|
||||||
|
priv = self->priv;
|
||||||
|
|
||||||
|
if (priv->uri)
|
||||||
|
g_free(priv->uri);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (clutter_audio_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clutter_audio_set_property (GObject *object,
|
||||||
|
guint property_id,
|
||||||
|
const GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
ClutterAudio *audio;
|
||||||
|
|
||||||
|
audio = CLUTTER_AUDIO(object);
|
||||||
|
|
||||||
|
switch (property_id)
|
||||||
|
{
|
||||||
|
case PROP_URI:
|
||||||
|
clutter_media_set_uri (CLUTTER_MEDIA(audio),
|
||||||
|
g_value_get_string (value));
|
||||||
|
break;
|
||||||
|
case PROP_PLAYING:
|
||||||
|
clutter_media_set_playing (CLUTTER_MEDIA(audio),
|
||||||
|
g_value_get_boolean (value));
|
||||||
|
break;
|
||||||
|
case PROP_POSITION:
|
||||||
|
clutter_media_set_position (CLUTTER_MEDIA(audio),
|
||||||
|
g_value_get_int (value));
|
||||||
|
break;
|
||||||
|
case PROP_VOLUME:
|
||||||
|
clutter_media_set_volume (CLUTTER_MEDIA(audio),
|
||||||
|
g_value_get_double (value));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clutter_audio_get_property (GObject *object,
|
||||||
|
guint property_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
ClutterAudio *audio;
|
||||||
|
ClutterMedia *media;
|
||||||
|
|
||||||
|
audio = CLUTTER_AUDIO (object);
|
||||||
|
media = CLUTTER_MEDIA (audio);
|
||||||
|
|
||||||
|
switch (property_id)
|
||||||
|
{
|
||||||
|
case PROP_URI:
|
||||||
|
g_value_set_string (value, clutter_media_get_uri (media));
|
||||||
|
break;
|
||||||
|
case PROP_PLAYING:
|
||||||
|
g_value_set_boolean (value, clutter_media_get_playing (media));
|
||||||
|
break;
|
||||||
|
case PROP_POSITION:
|
||||||
|
g_value_set_int (value, clutter_media_get_position (media));
|
||||||
|
break;
|
||||||
|
case PROP_VOLUME:
|
||||||
|
g_value_set_double (value, clutter_media_get_volume (media));
|
||||||
|
break;
|
||||||
|
case PROP_CAN_SEEK:
|
||||||
|
g_value_set_boolean (value, clutter_media_get_can_seek (media));
|
||||||
|
break;
|
||||||
|
case PROP_BUFFER_PERCENT:
|
||||||
|
g_value_set_int (value, clutter_media_get_buffer_percent (media));
|
||||||
|
break;
|
||||||
|
case PROP_DURATION:
|
||||||
|
g_value_set_int (value, clutter_media_get_duration (media));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clutter_audio_class_init (ClutterAudioClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class;
|
||||||
|
|
||||||
|
object_class = (GObjectClass*)klass;
|
||||||
|
|
||||||
|
object_class->dispose = clutter_audio_dispose;
|
||||||
|
object_class->finalize = clutter_audio_finalize;
|
||||||
|
object_class->set_property = clutter_audio_set_property;
|
||||||
|
object_class->get_property = clutter_audio_get_property;
|
||||||
|
|
||||||
|
/* Interface props */
|
||||||
|
|
||||||
|
g_object_class_override_property (object_class, PROP_URI, "uri");
|
||||||
|
g_object_class_override_property (object_class, PROP_PLAYING, "playing");
|
||||||
|
g_object_class_override_property (object_class, PROP_POSITION, "position");
|
||||||
|
g_object_class_override_property (object_class, PROP_VOLUME, "volume");
|
||||||
|
g_object_class_override_property (object_class, PROP_CAN_SEEK, "can-seek");
|
||||||
|
g_object_class_override_property (object_class, PROP_DURATION, "duration");
|
||||||
|
g_object_class_override_property (object_class, PROP_BUFFER_PERCENT,
|
||||||
|
"buffer-percent" );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bus_message_error_cb (GstBus *bus,
|
||||||
|
GstMessage *message,
|
||||||
|
ClutterAudio *audio)
|
||||||
|
{
|
||||||
|
GError *error;
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
|
gst_message_parse_error (message, &error, NULL);
|
||||||
|
|
||||||
|
g_signal_emit_by_name (CLUTTER_MEDIA(audio), "error", error);
|
||||||
|
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bus_message_eos_cb (GstBus *bus,
|
||||||
|
GstMessage *message,
|
||||||
|
ClutterAudio *audio)
|
||||||
|
{
|
||||||
|
g_object_notify (G_OBJECT (audio), "position");
|
||||||
|
|
||||||
|
g_signal_emit_by_name (CLUTTER_MEDIA(audio), "eos");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bus_message_tag_cb (GstBus *bus,
|
||||||
|
GstMessage *message,
|
||||||
|
ClutterAudio *audio)
|
||||||
|
{
|
||||||
|
GstTagList *tag_list;
|
||||||
|
|
||||||
|
gst_message_parse_tag (message, &tag_list);
|
||||||
|
|
||||||
|
g_signal_emit_by_name (CLUTTER_MEDIA(audio),
|
||||||
|
"metadata-available",
|
||||||
|
tag_list);
|
||||||
|
|
||||||
|
gst_tag_list_free (tag_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bus_message_buffering_cb (GstBus *bus,
|
||||||
|
GstMessage *message,
|
||||||
|
ClutterAudio *audio)
|
||||||
|
{
|
||||||
|
const GstStructure *str;
|
||||||
|
|
||||||
|
str = gst_message_get_structure (message);
|
||||||
|
if (!str)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!gst_structure_get_int (str,
|
||||||
|
"buffer-percent",
|
||||||
|
&audio->priv->buffer_percent))
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_object_notify (G_OBJECT (audio), "buffer-percent");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bus_message_duration_cb (GstBus *bus,
|
||||||
|
GstMessage *message,
|
||||||
|
ClutterAudio *audio)
|
||||||
|
{
|
||||||
|
GstFormat format;
|
||||||
|
gint64 duration;
|
||||||
|
|
||||||
|
gst_message_parse_duration (message,
|
||||||
|
&format,
|
||||||
|
&duration);
|
||||||
|
|
||||||
|
if (format != GST_FORMAT_TIME)
|
||||||
|
return;
|
||||||
|
|
||||||
|
audio->priv->duration = duration / GST_SECOND;
|
||||||
|
|
||||||
|
g_object_notify (G_OBJECT (audio), "duration");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bus_message_state_change_cb (GstBus *bus,
|
||||||
|
GstMessage *message,
|
||||||
|
ClutterAudio *audio)
|
||||||
|
{
|
||||||
|
gpointer src;
|
||||||
|
GstState old_state, new_state;
|
||||||
|
|
||||||
|
src = GST_MESSAGE_SRC (message);
|
||||||
|
|
||||||
|
if (src != audio->priv->playbin)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gst_message_parse_state_changed (message, &old_state, &new_state, NULL);
|
||||||
|
|
||||||
|
if (old_state == GST_STATE_READY &&
|
||||||
|
new_state == GST_STATE_PAUSED)
|
||||||
|
{
|
||||||
|
GstQuery *query;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether we can seek.
|
||||||
|
**/
|
||||||
|
query = gst_query_new_seeking (GST_FORMAT_TIME);
|
||||||
|
|
||||||
|
if (gst_element_query (audio->priv->playbin, query)) {
|
||||||
|
gst_query_parse_seeking (query,
|
||||||
|
NULL,
|
||||||
|
&audio->priv->can_seek,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Could not query for ability to seek. Determine
|
||||||
|
* using URI.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (g_str_has_prefix (audio->priv->uri,
|
||||||
|
"http://")) {
|
||||||
|
audio->priv->can_seek = FALSE;
|
||||||
|
} else {
|
||||||
|
audio->priv->can_seek = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_query_unref (query);
|
||||||
|
|
||||||
|
g_object_notify (G_OBJECT (audio), "can-seek");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the duration.
|
||||||
|
**/
|
||||||
|
query = gst_query_new_duration (GST_FORMAT_TIME);
|
||||||
|
|
||||||
|
if (gst_element_query (audio->priv->playbin, query))
|
||||||
|
{
|
||||||
|
gint64 duration;
|
||||||
|
|
||||||
|
gst_query_parse_duration (query, NULL, &duration);
|
||||||
|
|
||||||
|
audio->priv->duration = duration / GST_SECOND;
|
||||||
|
|
||||||
|
g_object_notify (G_OBJECT (audio), "duration");
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_query_unref (query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
tick_timeout (ClutterAudio *audio)
|
||||||
|
{
|
||||||
|
g_object_notify (G_OBJECT (audio), "position");
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fakesink_handoff_cb (GstElement *fakesrc,
|
||||||
|
GstBuffer *buffer,
|
||||||
|
GstPad *pad,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GstStructure *structure;
|
||||||
|
int width, height;
|
||||||
|
GdkPixbuf *pixb;
|
||||||
|
|
||||||
|
structure = gst_caps_get_structure(GST_CAPS(buffer->caps), 0);
|
||||||
|
gst_structure_get_int(structure, "width", &width);
|
||||||
|
gst_structure_get_int(structure, "height", &height);
|
||||||
|
|
||||||
|
/* FIXME: We really dont want to do this every time as gobject creation
|
||||||
|
* really need a clutter_texture_set_from_data call ?
|
||||||
|
*/
|
||||||
|
pixb = gdk_pixbuf_new_from_data (GST_BUFFER_DATA (buffer),
|
||||||
|
GDK_COLORSPACE_RGB,
|
||||||
|
FALSE,
|
||||||
|
8,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
(3 * width + 3) &~ 3,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (pixb)
|
||||||
|
{
|
||||||
|
clutter_texture_set_pixbuf (CLUTTER_TEXTURE(user_data), pixb);
|
||||||
|
g_object_unref(G_OBJECT(pixb));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
lay_pipeline (ClutterAudio *audio)
|
||||||
|
{
|
||||||
|
ClutterAudioPrivate *priv;
|
||||||
|
GstElement *audio_sink = NULL;
|
||||||
|
|
||||||
|
priv = audio->priv;
|
||||||
|
|
||||||
|
priv->playbin = gst_element_factory_make ("playbin", "playbin");
|
||||||
|
|
||||||
|
if (!priv->playbin)
|
||||||
|
{
|
||||||
|
g_warning ("Unable to create playbin GST element.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_sink = gst_element_factory_make ("gconfaudiosink", "audio-sink");
|
||||||
|
if (!audio_sink)
|
||||||
|
{
|
||||||
|
audio_sink = gst_element_factory_make ("autoaudiosink", "audio-sink");
|
||||||
|
if (!audio_sink)
|
||||||
|
{
|
||||||
|
audio_sink = gst_element_factory_make ("alsasink", "audio-sink");
|
||||||
|
g_warning ("Could not create a GST audio_sink. "
|
||||||
|
"Audio unavailable.");
|
||||||
|
|
||||||
|
if (!audio_sink) /* Need to bother ? */
|
||||||
|
audio_sink = gst_element_factory_make ("fakesink", "audio-sink");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_set (G_OBJECT (priv->playbin),
|
||||||
|
"audio-sink", audio_sink,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clutter_audio_init (ClutterAudio *audio)
|
||||||
|
{
|
||||||
|
ClutterAudioPrivate *priv;
|
||||||
|
GstBus *bus;
|
||||||
|
|
||||||
|
priv = g_new0 (ClutterAudioPrivate, 1);
|
||||||
|
audio->priv = priv;
|
||||||
|
|
||||||
|
if (!lay_pipeline(audio))
|
||||||
|
{
|
||||||
|
g_warning("Failed to initiate suitable playback pipeline.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bus = gst_pipeline_get_bus (GST_PIPELINE (priv->playbin));
|
||||||
|
|
||||||
|
gst_bus_add_signal_watch (bus);
|
||||||
|
|
||||||
|
g_signal_connect_object (bus,
|
||||||
|
"message::error",
|
||||||
|
G_CALLBACK (bus_message_error_cb),
|
||||||
|
audio,
|
||||||
|
0);
|
||||||
|
|
||||||
|
g_signal_connect_object (bus,
|
||||||
|
"message::eos",
|
||||||
|
G_CALLBACK (bus_message_eos_cb),
|
||||||
|
audio,
|
||||||
|
0);
|
||||||
|
|
||||||
|
g_signal_connect_object (bus,
|
||||||
|
"message::tag",
|
||||||
|
G_CALLBACK (bus_message_tag_cb),
|
||||||
|
audio,
|
||||||
|
0);
|
||||||
|
|
||||||
|
g_signal_connect_object (bus,
|
||||||
|
"message::buffering",
|
||||||
|
G_CALLBACK (bus_message_buffering_cb),
|
||||||
|
audio,
|
||||||
|
0);
|
||||||
|
|
||||||
|
g_signal_connect_object (bus,
|
||||||
|
"message::duration",
|
||||||
|
G_CALLBACK (bus_message_duration_cb),
|
||||||
|
audio,
|
||||||
|
0);
|
||||||
|
|
||||||
|
g_signal_connect_object (bus,
|
||||||
|
"message::state-changed",
|
||||||
|
G_CALLBACK (bus_message_state_change_cb),
|
||||||
|
audio,
|
||||||
|
0);
|
||||||
|
|
||||||
|
gst_object_unref (GST_OBJECT (bus));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clutter_audio_new:
|
||||||
|
*
|
||||||
|
* Creates #ClutterAudio object.
|
||||||
|
*
|
||||||
|
* Return value: A newly allocated #ClutterAudio object.
|
||||||
|
*/
|
||||||
|
ClutterAudio*
|
||||||
|
clutter_audio_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (CLUTTER_TYPE_AUDIO, NULL);
|
||||||
|
}
|
||||||
|
|
87
clutter/clutter-audio.h
Normal file
87
clutter/clutter-audio.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Clutter.
|
||||||
|
*
|
||||||
|
* An OpenGL based 'interactive canvas' library.
|
||||||
|
*
|
||||||
|
* Authored By Matthew Allum <mallum@openedhand.com>
|
||||||
|
* Jorn Baayen <jorn@openedhand.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006 OpenedHand
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HAVE_CLUTTER_AUDIO_H
|
||||||
|
#define _HAVE_CLUTTER_AUDIO_H
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <clutter/clutter-media.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define CLUTTER_TYPE_AUDIO clutter_audio_get_type()
|
||||||
|
|
||||||
|
#define CLUTTER_AUDIO(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
|
||||||
|
CLUTTER_TYPE_AUDIO, ClutterAudio))
|
||||||
|
|
||||||
|
#define CLUTTER_AUDIO_CLASS(klass) \
|
||||||
|
(G_TYPE_CHECK_CLASS_CAST ((klass), \
|
||||||
|
CLUTTER_TYPE_AUDIO, ClutterAudioClass))
|
||||||
|
|
||||||
|
#define CLUTTER_IS_AUDIO(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
|
||||||
|
CLUTTER_TYPE_AUDIO))
|
||||||
|
|
||||||
|
#define CLUTTER_IS_AUDIO_CLASS(klass) \
|
||||||
|
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
|
||||||
|
CLUTTER_TYPE_AUDIO))
|
||||||
|
|
||||||
|
#define CLUTTER_AUDIO_GET_CLASS(obj) \
|
||||||
|
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
|
||||||
|
CLUTTER_TYPE_AUDIO, ClutterAudioClass))
|
||||||
|
|
||||||
|
typedef struct _ClutterAudio ClutterAudio;
|
||||||
|
typedef struct _ClutterAudioClass ClutterAudioClass;
|
||||||
|
typedef struct _ClutterAudioPrivate ClutterAudioPrivate;
|
||||||
|
|
||||||
|
/* #define CLUTTER_AUDIO_ERROR clutter_audio_error_quark() */
|
||||||
|
|
||||||
|
struct _ClutterAudio
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
ClutterAudioPrivate *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _ClutterAudioClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
|
||||||
|
/* Future padding */
|
||||||
|
void (* _clutter_reserved1) (void);
|
||||||
|
void (* _clutter_reserved2) (void);
|
||||||
|
void (* _clutter_reserved3) (void);
|
||||||
|
void (* _clutter_reserved4) (void);
|
||||||
|
void (* _clutter_reserved5) (void);
|
||||||
|
void (* _clutter_reserved6) (void);
|
||||||
|
};
|
||||||
|
|
||||||
|
GType clutter_audio_get_type (void) G_GNUC_CONST;
|
||||||
|
ClutterAudio *clutter_audio_new (void);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif
|
@ -19,6 +19,7 @@
|
|||||||
#include "clutter-video-texture.h"
|
#include "clutter-video-texture.h"
|
||||||
#include "clutter-label.h"
|
#include "clutter-label.h"
|
||||||
#include "clutter-alpha.h"
|
#include "clutter-alpha.h"
|
||||||
|
#include "clutter-audio.h"
|
||||||
#include "clutter-enum-types.h"
|
#include "clutter-enum-types.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user