diff options
Diffstat (limited to 'ext')
96 files changed, 0 insertions, 33997 deletions
diff --git a/ext/Makefile.am b/ext/Makefile.am deleted file mode 100644 index 7c006ff7..00000000 --- a/ext/Makefile.am +++ /dev/null @@ -1,83 +0,0 @@ -if USE_ALSA -ALSA_DIR=alsa -else -ALSA_DIR= -endif - -if USE_CDPARANOIA -CDPARANOIA_DIR=cdparanoia -else -CDPARANOIA_DIR= -endif - -if USE_GIO -GIO_DIR=gio -else -GIO_DIR= -endif - -if USE_GNOME_VFS -GNOMEVFS_DIR=gnomevfs -else -GNOMEVFS_DIR= -endif - -if USE_LIBVISUAL -LIBVISUAL_DIR=libvisual -else -LIBVISUAL_DIR= -endif - -if USE_OGG -OGG_DIR=ogg -else -OGG_DIR= -endif - -if USE_PANGO -PANGO_DIR = pango -else -PANGO_DIR = -endif - -if USE_VORBIS -VORBIS_DIR=vorbis -endif - -if USE_IVORBIS -VORBIS_DIR=vorbis -endif - -if !USE_VORBIS -if !USE_IVORBIS -VORBIS_DIR= -endif -endif - -if USE_THEORA -THEORA_DIR=theora -else -THEORA_DIR= -endif - -SUBDIRS = \ - $(ALSA_DIR) \ - $(CDPARANOIA_DIR) \ - $(GIO_DIR) \ - $(GNOMEVFS_DIR) \ - $(LIBVISUAL_DIR) \ - $(OGG_DIR) \ - $(PANGO_DIR) \ - $(THEORA_DIR) \ - $(VORBIS_DIR) - -DIST_SUBDIRS = \ - alsa \ - cdparanoia \ - gio \ - gnomevfs \ - libvisual \ - ogg \ - pango \ - theora \ - vorbis diff --git a/ext/alsa/Makefile.am b/ext/alsa/Makefile.am deleted file mode 100644 index 38cde48a..00000000 --- a/ext/alsa/Makefile.am +++ /dev/null @@ -1,36 +0,0 @@ -plugin_LTLIBRARIES = libgstalsa.la - -libgstalsa_la_SOURCES = \ - gstalsadeviceprobe.c \ - gstalsamixer.c \ - gstalsamixerelement.c \ - gstalsamixertrack.c \ - gstalsamixeroptions.c \ - gstalsaplugin.c \ - gstalsasink.c \ - gstalsasrc.c \ - gstalsa.c - -libgstalsa_la_CFLAGS = \ - $(GST_PLUGINS_BASE_CFLAGS) \ - $(GST_BASE_CFLAGS) \ - $(GST_CFLAGS) \ - $(ALSA_CFLAGS) -libgstalsa_la_LIBADD = \ - $(top_builddir)/gst-libs/gst/interfaces/libgstinterfaces-$(GST_MAJORMINOR).la \ - $(top_builddir)/gst-libs/gst/audio/libgstaudio-$(GST_MAJORMINOR).la \ - $(GST_BASE_LIBS) \ - $(ALSA_LIBS) - -libgstalsa_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstalsa_la_LIBTOOLFLAGS = --tag=disable-static - -noinst_HEADERS = \ - gstalsa.h \ - gstalsadeviceprobe.h \ - gstalsamixer.h \ - gstalsamixerelement.h \ - gstalsamixertrack.h \ - gstalsamixeroptions.h \ - gstalsasrc.h \ - gstalsasink.h diff --git a/ext/alsa/gstalsa.c b/ext/alsa/gstalsa.c deleted file mode 100644 index 2cc37ab1..00000000 --- a/ext/alsa/gstalsa.c +++ /dev/null @@ -1,561 +0,0 @@ -/* Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "gstalsa.h" - -#include <gst/audio/multichannel.h> - -static GstCaps * -gst_alsa_detect_rates (GstObject * obj, snd_pcm_hw_params_t * hw_params, - GstCaps * in_caps) -{ - GstCaps *caps; - guint min, max; - gint err, dir, min_rate, max_rate, i; - - GST_LOG_OBJECT (obj, "probing sample rates ..."); - - if ((err = snd_pcm_hw_params_get_rate_min (hw_params, &min, &dir)) < 0) - goto min_rate_err; - - if ((err = snd_pcm_hw_params_get_rate_max (hw_params, &max, &dir)) < 0) - goto max_rate_err; - - min_rate = min; - max_rate = max; - - if (min_rate < 4000) - min_rate = 4000; /* random 'sensible minimum' */ - - if (max_rate <= 0) - max_rate = G_MAXINT; /* or maybe just use 192400 or so? */ - else if (max_rate > 0 && max_rate < 4000) - max_rate = MAX (4000, min_rate); - - GST_DEBUG_OBJECT (obj, "Min. rate = %u (%d)", min_rate, min); - GST_DEBUG_OBJECT (obj, "Max. rate = %u (%d)", max_rate, max); - - caps = gst_caps_make_writable (in_caps); - - for (i = 0; i < gst_caps_get_size (caps); ++i) { - GstStructure *s; - - s = gst_caps_get_structure (caps, i); - if (min_rate == max_rate) { - gst_structure_set (s, "rate", G_TYPE_INT, min_rate, NULL); - } else { - gst_structure_set (s, "rate", GST_TYPE_INT_RANGE, - min_rate, max_rate, NULL); - } - } - - return caps; - - /* ERRORS */ -min_rate_err: - { - GST_ERROR_OBJECT (obj, "failed to query minimum sample rate: %s", - snd_strerror (err)); - gst_caps_unref (in_caps); - return NULL; - } -max_rate_err: - { - GST_ERROR_OBJECT (obj, "failed to query maximum sample rate: %s", - snd_strerror (err)); - gst_caps_unref (in_caps); - return NULL; - } -} - -static const struct -{ - const int width; - const int depth; - const int sformat; - const int uformat; -} pcmformats[] = { - { - 8, 8, SND_PCM_FORMAT_S8, SND_PCM_FORMAT_U8}, { - 16, 16, SND_PCM_FORMAT_S16, SND_PCM_FORMAT_U16}, { - 32, 24, SND_PCM_FORMAT_S24, SND_PCM_FORMAT_U24}, { -#if (G_BYTE_ORDER == G_LITTLE_ENDIAN) /* no endian-unspecific enum available */ - 24, 24, SND_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_U24_3LE}, { -#else - 24, 24, SND_PCM_FORMAT_S24_3BE, SND_PCM_FORMAT_U24_3BE}, { -#endif - 32, 32, SND_PCM_FORMAT_S32, SND_PCM_FORMAT_U32} -}; - -static GstCaps * -gst_alsa_detect_formats (GstObject * obj, snd_pcm_hw_params_t * hw_params, - GstCaps * in_caps) -{ - snd_pcm_format_mask_t *mask; - GstStructure *s; - GstCaps *caps; - gint i; - - snd_pcm_format_mask_malloc (&mask); - snd_pcm_hw_params_get_format_mask (hw_params, mask); - - caps = gst_caps_new_empty (); - - for (i = 0; i < gst_caps_get_size (in_caps); ++i) { - GstStructure *scopy; - gint w, width = 0, depth = 0; - - s = gst_caps_get_structure (in_caps, i); - if (!gst_structure_has_name (s, "audio/x-raw-int")) { - GST_WARNING_OBJECT (obj, "skipping non-int format"); - continue; - } - if (!gst_structure_get_int (s, "width", &width) || - !gst_structure_get_int (s, "depth", &depth)) - continue; - if (width == 0 || (width % 8) != 0) - continue; /* Only full byte widths are valid */ - for (w = 0; w < G_N_ELEMENTS (pcmformats); w++) - if (pcmformats[w].width == width && pcmformats[w].depth == depth) - break; - if (w == G_N_ELEMENTS (pcmformats)) - continue; /* Unknown format */ - - if (snd_pcm_format_mask_test (mask, pcmformats[w].sformat) && - snd_pcm_format_mask_test (mask, pcmformats[w].uformat)) { - /* template contains { true, false } or just one, leave it as it is */ - scopy = gst_structure_copy (s); - } else if (snd_pcm_format_mask_test (mask, pcmformats[w].sformat)) { - scopy = gst_structure_copy (s); - gst_structure_set (scopy, "signed", G_TYPE_BOOLEAN, TRUE, NULL); - } else if (snd_pcm_format_mask_test (mask, pcmformats[w].uformat)) { - scopy = gst_structure_copy (s); - gst_structure_set (scopy, "signed", G_TYPE_BOOLEAN, FALSE, NULL); - } else { - scopy = NULL; - } - if (scopy) { - if (width > 8) { - /* TODO: proper endianness detection, for now it's CPU endianness only */ - gst_structure_set (scopy, "endianness", G_TYPE_INT, G_BYTE_ORDER, NULL); - } - gst_caps_append_structure (caps, scopy); - } - } - - snd_pcm_format_mask_free (mask); - gst_caps_unref (in_caps); - return caps; -} - -/* we don't have channel mappings for more than this many channels */ -#define GST_ALSA_MAX_CHANNELS 8 - -static GstStructure * -get_channel_free_structure (const GstStructure * in_structure) -{ - GstStructure *s = gst_structure_copy (in_structure); - - gst_structure_remove_field (s, "channels"); - return s; -} - -static void -caps_add_channel_configuration (GstCaps * caps, - const GstStructure * in_structure, gint min_chans, gint max_chans) -{ - GstAudioChannelPosition pos[8] = { - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, - GST_AUDIO_CHANNEL_POSITION_LFE, - GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, - GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT - }; - GstStructure *s = NULL; - gint c; - - if (min_chans == max_chans && max_chans <= 2) { - s = get_channel_free_structure (in_structure); - gst_structure_set (s, "channels", G_TYPE_INT, max_chans, NULL); - gst_caps_append_structure (caps, s); - return; - } - - g_assert (min_chans >= 1); - - /* mono and stereo don't need channel configurations */ - if (min_chans == 2) { - s = get_channel_free_structure (in_structure); - gst_structure_set (s, "channels", G_TYPE_INT, 2, NULL); - gst_caps_append_structure (caps, s); - } else if (min_chans == 1 && max_chans >= 2) { - s = get_channel_free_structure (in_structure); - gst_structure_set (s, "channels", GST_TYPE_INT_RANGE, 1, 2, NULL); - gst_caps_append_structure (caps, s); - } - - /* don't know whether to use 2.1 or 3.0 here - but I suspect - * alsa might work around that/fix it somehow. Can we tell alsa - * what our channel layout is like? */ - if (max_chans >= 3 && min_chans <= 3) { - GstAudioChannelPosition pos_21[3] = { - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_LFE - }; - - s = get_channel_free_structure (in_structure); - gst_structure_set (s, "channels", G_TYPE_INT, 3, NULL); - gst_audio_set_channel_positions (s, pos_21); - gst_caps_append_structure (caps, s); - } - - /* everything else (4, 6, 8 channels) needs a channel layout */ - for (c = MAX (4, min_chans); c <= 8; c += 2) { - if (max_chans >= c) { - s = get_channel_free_structure (in_structure); - gst_structure_set (s, "channels", G_TYPE_INT, c, NULL); - gst_audio_set_channel_positions (s, pos); - gst_caps_append_structure (caps, s); - } - } - - for (c = MAX (9, min_chans); c <= max_chans; ++c) { - GstAudioChannelPosition *ch_layout; - guint i; - - ch_layout = g_new (GstAudioChannelPosition, c); - for (i = 0; i < c; ++i) { - ch_layout[i] = GST_AUDIO_CHANNEL_POSITION_NONE; - } - s = get_channel_free_structure (in_structure); - gst_structure_set (s, "channels", G_TYPE_INT, c, NULL); - gst_audio_set_channel_positions (s, ch_layout); - gst_caps_append_structure (caps, s); - g_free (ch_layout); - } -} - -static GstCaps * -gst_alsa_detect_channels (GstObject * obj, snd_pcm_hw_params_t * hw_params, - GstCaps * in_caps) -{ - GstCaps *caps; - guint min, max; - gint min_chans, max_chans; - gint err, i; - - GST_LOG_OBJECT (obj, "probing channels ..."); - - if ((err = snd_pcm_hw_params_get_channels_min (hw_params, &min)) < 0) - goto min_chan_error; - - if ((err = snd_pcm_hw_params_get_channels_max (hw_params, &max)) < 0) - goto max_chan_error; - - /* note: the above functions may return (guint) -1 */ - min_chans = min; - max_chans = max; - - if (min_chans < 0) { - min_chans = 1; - max_chans = GST_ALSA_MAX_CHANNELS; - } else if (max_chans < 0) { - max_chans = GST_ALSA_MAX_CHANNELS; - } - - if (min_chans > max_chans) { - gint temp; - - GST_WARNING_OBJECT (obj, "minimum channels > maximum channels (%d > %d), " - "please fix your soundcard drivers", min, max); - temp = min_chans; - min_chans = max_chans; - max_chans = temp; - } - - /* pro cards seem to return large numbers for min_channels */ - if (min_chans > GST_ALSA_MAX_CHANNELS) { - GST_DEBUG_OBJECT (obj, "min_chans = %u, looks like a pro card", min_chans); - if (max_chans < min_chans) { - max_chans = min_chans; - } else { - /* only support [max_chans; max_chans] for these cards for now - * to avoid inflating the source caps with loads of structures ... */ - min_chans = max_chans; - } - } else { - min_chans = MAX (min_chans, 1); - max_chans = MIN (GST_ALSA_MAX_CHANNELS, max_chans); - } - - GST_DEBUG_OBJECT (obj, "Min. channels = %d (%d)", min_chans, min); - GST_DEBUG_OBJECT (obj, "Max. channels = %d (%d)", max_chans, max); - - caps = gst_caps_new_empty (); - - for (i = 0; i < gst_caps_get_size (in_caps); ++i) { - GstStructure *s; - GType field_type; - gint c_min = min_chans; - gint c_max = max_chans; - - s = gst_caps_get_structure (in_caps, i); - /* the template caps might limit the number of channels (like alsasrc), - * in which case we don't want to return a superset, so hack around this - * for the two common cases where the channels are either a fixed number - * or a min/max range). Example: alsasrc template has channels = [1,2] and - * the detection will claim to support 8 channels for device 'plughw:0' */ - field_type = gst_structure_get_field_type (s, "channels"); - if (field_type == G_TYPE_INT) { - gst_structure_get_int (s, "channels", &c_min); - gst_structure_get_int (s, "channels", &c_max); - } else if (field_type == GST_TYPE_INT_RANGE) { - const GValue *val; - - val = gst_structure_get_value (s, "channels"); - c_min = CLAMP (gst_value_get_int_range_min (val), min_chans, max_chans); - c_max = CLAMP (gst_value_get_int_range_max (val), min_chans, max_chans); - } else { - c_min = min_chans; - c_max = max_chans; - } - - caps_add_channel_configuration (caps, s, c_min, c_max); - } - - gst_caps_unref (in_caps); - - return caps; - - /* ERRORS */ -min_chan_error: - { - GST_ERROR_OBJECT (obj, "failed to query minimum channel count: %s", - snd_strerror (err)); - return NULL; - } -max_chan_error: - { - GST_ERROR_OBJECT (obj, "failed to query maximum channel count: %s", - snd_strerror (err)); - return NULL; - } -} - -snd_pcm_t * -gst_alsa_open_iec958_pcm (GstObject * obj) -{ - char *iec958_pcm_name = NULL; - snd_pcm_t *pcm = NULL; - int res; - char devstr[256]; /* Storage for local 'default' device string */ - - /* - * Try and open our default iec958 device. Fall back to searching on card x - * if this fails, which should only happen on older alsa setups - */ - - /* The string will be one of these: - * SPDIF_CON: Non-audio flag not set: - * spdif:{AES0 0x0 AES1 0x82 AES2 0x0 AES3 0x2} - * SPDIF_CON: Non-audio flag set: - * spdif:{AES0 0x2 AES1 0x82 AES2 0x0 AES3 0x2} - */ - sprintf (devstr, - "iec958:{AES0 0x%02x AES1 0x%02x AES2 0x%02x AES3 0x%02x}", - IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO, - IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER, - 0, IEC958_AES3_CON_FS_48000); - - GST_DEBUG_OBJECT (obj, "Generated device string \"%s\"", devstr); - iec958_pcm_name = devstr; - - res = snd_pcm_open (&pcm, iec958_pcm_name, SND_PCM_STREAM_PLAYBACK, 0); - if (G_UNLIKELY (res < 0)) { - GST_DEBUG_OBJECT (obj, "failed opening IEC958 device: %s", - snd_strerror (res)); - pcm = NULL; - } - - return pcm; -} - - -/* - * gst_alsa_probe_supported_formats: - * - * Takes the template caps and returns the subset which is actually - * supported by this device. - * - */ - -GstCaps * -gst_alsa_probe_supported_formats (GstObject * obj, snd_pcm_t * handle, - const GstCaps * template_caps) -{ - snd_pcm_hw_params_t *hw_params; - snd_pcm_stream_t stream_type; - GstCaps *caps; - gint err; - - snd_pcm_hw_params_malloc (&hw_params); - if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) - goto error; - - stream_type = snd_pcm_stream (handle); - - caps = gst_caps_copy (template_caps); - - if (!(caps = gst_alsa_detect_formats (obj, hw_params, caps))) - goto subroutine_error; - - if (!(caps = gst_alsa_detect_rates (obj, hw_params, caps))) - goto subroutine_error; - - if (!(caps = gst_alsa_detect_channels (obj, hw_params, caps))) - goto subroutine_error; - - /* Try opening IEC958 device to see if we can support that format (playback - * only for now but we could add SPDIF capture later) */ - if (stream_type == SND_PCM_STREAM_PLAYBACK) { - snd_pcm_t *pcm = gst_alsa_open_iec958_pcm (obj); - - if (G_LIKELY (pcm)) { - gst_caps_append (caps, gst_caps_new_simple ("audio/x-iec958", NULL)); - snd_pcm_close (pcm); - } - } - - snd_pcm_hw_params_free (hw_params); - return caps; - - /* ERRORS */ -error: - { - GST_ERROR_OBJECT (obj, "failed to query formats: %s", snd_strerror (err)); - snd_pcm_hw_params_free (hw_params); - return NULL; - } -subroutine_error: - { - GST_ERROR_OBJECT (obj, "failed to query formats"); - snd_pcm_hw_params_free (hw_params); - return NULL; - } -} - -static gchar * -gst_alsa_find_device_name_no_handle (GstObject * obj, const gchar * devcard, - gint device_num, snd_pcm_stream_t stream) -{ - snd_ctl_card_info_t *info = NULL; - snd_ctl_t *ctl = NULL; - gchar *ret = NULL; - gint dev = -1; - - GST_LOG_OBJECT (obj, "[%s] device=%d", devcard, device_num); - - if (snd_ctl_open (&ctl, devcard, 0) < 0) - return NULL; - - snd_ctl_card_info_malloc (&info); - if (snd_ctl_card_info (ctl, info) < 0) - goto done; - - while (snd_ctl_pcm_next_device (ctl, &dev) == 0 && dev >= 0) { - if (dev == device_num) { - snd_pcm_info_t *pcminfo; - - snd_pcm_info_malloc (&pcminfo); - snd_pcm_info_set_device (pcminfo, dev); - snd_pcm_info_set_subdevice (pcminfo, 0); - snd_pcm_info_set_stream (pcminfo, stream); - if (snd_ctl_pcm_info (ctl, pcminfo) < 0) { - snd_pcm_info_free (pcminfo); - break; - } - - ret = (gchar *) snd_pcm_info_get_name (pcminfo); - if (ret) { - ret = g_strdup (ret); - GST_LOG_OBJECT (obj, "name from pcminfo: %s", ret); - } - snd_pcm_info_free (pcminfo); - if (ret) - break; - } - } - - if (ret == NULL) { - char *name = NULL; - gint card; - - GST_LOG_OBJECT (obj, "no luck so far, trying backup"); - card = snd_ctl_card_info_get_card (info); - snd_card_get_name (card, &name); - ret = g_strdup (name); - free (name); - } - -done: - snd_ctl_card_info_free (info); - snd_ctl_close (ctl); - - return ret; -} - -gchar * -gst_alsa_find_device_name (GstObject * obj, const gchar * device, - snd_pcm_t * handle, snd_pcm_stream_t stream) -{ - gchar *ret = NULL; - - if (device != NULL) { - gchar *dev, *comma; - gint devnum; - - GST_LOG_OBJECT (obj, "Trying to get device name from string '%s'", device); - - /* only want name:card bit, but not devices and subdevices */ - dev = g_strdup (device); - if ((comma = strchr (dev, ','))) { - *comma = '\0'; - devnum = atoi (comma + 1); - ret = gst_alsa_find_device_name_no_handle (obj, dev, devnum, stream); - } - g_free (dev); - } - - if (ret == NULL && handle != NULL) { - snd_pcm_info_t *info; - - GST_LOG_OBJECT (obj, "Trying to get device name from open handle"); - snd_pcm_info_malloc (&info); - snd_pcm_info (handle, info); - ret = g_strdup (snd_pcm_info_get_name (info)); - snd_pcm_info_free (info); - } - - GST_LOG_OBJECT (obj, "Device name for device '%s': %s", - GST_STR_NULL (device), GST_STR_NULL (ret)); - - return ret; -} diff --git a/ext/alsa/gstalsa.h b/ext/alsa/gstalsa.h deleted file mode 100644 index e7af889f..00000000 --- a/ext/alsa/gstalsa.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2001 CodeFactory AB - * Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se> - * Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu> - * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - -#ifndef __GST_ALSA_H__ -#define __GST_ALSA_H__ - - -#define ALSA_PCM_NEW_HW_PARAMS_API -#define ALSA_PCM_NEW_SW_PARAMS_API - -#include <alsa/asoundlib.h> -#include <alsa/control.h> -#include <alsa/error.h> -#include <gst/gst.h> - -#define GST_CHECK_ALSA_VERSION(major,minor,micro) \ - (SND_LIB_MAJOR > (major) || \ - (SND_LIB_MAJOR == (major) && SND_LIB_MINOR > (minor)) || \ - (SND_LIB_MAJOR == (major) && SND_LIB_MINOR == (minor) && \ - SND_LIB_SUBMINOR >= (micro))) - -GST_DEBUG_CATEGORY_EXTERN (alsa_debug); -#define GST_CAT_DEFAULT alsa_debug - -snd_pcm_t * gst_alsa_open_iec958_pcm (GstObject * obj); - -GstCaps * gst_alsa_probe_supported_formats (GstObject * obj, - snd_pcm_t * handle, - const GstCaps * template_caps); - -gchar * gst_alsa_find_device_name (GstObject * obj, - const gchar * device, - snd_pcm_t * handle, - snd_pcm_stream_t stream); - -#endif /* __GST_ALSA_H__ */ diff --git a/ext/alsa/gstalsadeviceprobe.c b/ext/alsa/gstalsadeviceprobe.c deleted file mode 100644 index 83596a3f..00000000 --- a/ext/alsa/gstalsadeviceprobe.c +++ /dev/null @@ -1,210 +0,0 @@ -/* Copyright (C) 2001 CodeFactory AB - * Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se> - * Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu> - * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de> - * Copyright (C) 2005 Tim-Philipp Müller <tim centricular net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstalsadeviceprobe.h" -#include "gst/interfaces/propertyprobe.h" - -static const GList * -gst_alsa_device_property_probe_get_properties (GstPropertyProbe * probe) -{ - GObjectClass *klass = G_OBJECT_GET_CLASS (probe); - static GList *list = NULL; - - /* well, not perfect, but better than no locking at all. - * In the worst case we leak a list node, so who cares? */ - GST_CLASS_LOCK (GST_OBJECT_CLASS (klass)); - - if (!list) { - GParamSpec *pspec; - - pspec = g_object_class_find_property (klass, "device"); - list = g_list_append (NULL, pspec); - } - - GST_CLASS_UNLOCK (GST_OBJECT_CLASS (klass)); - - return list; -} - -static GList * -gst_alsa_get_device_list (snd_pcm_stream_t stream) -{ - snd_ctl_t *handle; - int card, dev; - snd_ctl_card_info_t *info; - snd_pcm_info_t *pcminfo; - gboolean mixer = (stream == -1); - GList *list = NULL; - - if (stream == -1) - stream = 0; - - snd_ctl_card_info_malloc (&info); - snd_pcm_info_malloc (&pcminfo); - card = -1; - - if (snd_card_next (&card) < 0 || card < 0) { - /* no soundcard found */ - GST_WARNING ("No soundcard found"); - goto beach; - } - - while (card >= 0) { - gchar name[32]; - - g_snprintf (name, sizeof (name), "hw:%d", card); - if (snd_ctl_open (&handle, name, 0) < 0) { - goto next_card; - } - if (snd_ctl_card_info (handle, info) < 0) { - snd_ctl_close (handle); - goto next_card; - } - - if (mixer) { - list = g_list_append (list, g_strdup (name)); - } else { - dev = -1; - while (1) { - gchar *gst_device; - - snd_ctl_pcm_next_device (handle, &dev); - - if (dev < 0) - break; - snd_pcm_info_set_device (pcminfo, dev); - snd_pcm_info_set_subdevice (pcminfo, 0); - snd_pcm_info_set_stream (pcminfo, stream); - if (snd_ctl_pcm_info (handle, pcminfo) < 0) { - continue; - } - - gst_device = g_strdup_printf ("hw:%d,%d", card, dev); - list = g_list_append (list, gst_device); - } - } - snd_ctl_close (handle); - next_card: - if (snd_card_next (&card) < 0) { - break; - } - } - -beach: - snd_ctl_card_info_free (info); - snd_pcm_info_free (pcminfo); - - return list; -} - -static void -gst_alsa_device_property_probe_probe_property (GstPropertyProbe * probe, - guint prop_id, const GParamSpec * pspec) -{ - if (!g_str_equal (pspec->name, "device")) { - G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec); - } -} - -static gboolean -gst_alsa_device_property_probe_needs_probe (GstPropertyProbe * probe, - guint prop_id, const GParamSpec * pspec) -{ - /* don't cache probed data */ - return TRUE; -} - -static GValueArray * -gst_alsa_device_property_probe_get_values (GstPropertyProbe * probe, - guint prop_id, const GParamSpec * pspec) -{ - GstElementClass *klass; - const GList *templates; - snd_pcm_stream_t mode = -1; - GValueArray *array; - GValue value = { 0, }; - GList *l, *list; - - if (!g_str_equal (pspec->name, "device")) { - G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec); - return NULL; - } - - klass = GST_ELEMENT_GET_CLASS (GST_ELEMENT (probe)); - - /* I'm pretty sure ALSA has a good way to do this. However, their cool - * auto-generated documentation is pretty much useless if you try to - * do function-wise look-ups. */ - /* we assume one pad template at max [zero=mixer] */ - templates = gst_element_class_get_pad_template_list (klass); - if (templates) { - if (GST_PAD_TEMPLATE_DIRECTION (templates->data) == GST_PAD_SRC) - mode = SND_PCM_STREAM_CAPTURE; - else - mode = SND_PCM_STREAM_PLAYBACK; - } - - list = gst_alsa_get_device_list (mode); - - if (list == NULL) { - GST_LOG_OBJECT (probe, "No devices found"); - return NULL; - } - - array = g_value_array_new (g_list_length (list)); - g_value_init (&value, G_TYPE_STRING); - for (l = list; l != NULL; l = l->next) { - GST_LOG_OBJECT (probe, "Found device: %s", (gchar *) l->data); - g_value_take_string (&value, (gchar *) l->data); - l->data = NULL; - g_value_array_append (array, &value); - } - g_value_unset (&value); - g_list_free (list); - - return array; -} - -static void -gst_alsa_property_probe_interface_init (GstPropertyProbeInterface * iface) -{ - iface->get_properties = gst_alsa_device_property_probe_get_properties; - iface->probe_property = gst_alsa_device_property_probe_probe_property; - iface->needs_probe = gst_alsa_device_property_probe_needs_probe; - iface->get_values = gst_alsa_device_property_probe_get_values; -} - -void -gst_alsa_type_add_device_property_probe_interface (GType type) -{ - static const GInterfaceInfo probe_iface_info = { - (GInterfaceInitFunc) gst_alsa_property_probe_interface_init, - NULL, - NULL, - }; - - g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE, - &probe_iface_info); -} diff --git a/ext/alsa/gstalsadeviceprobe.h b/ext/alsa/gstalsadeviceprobe.h deleted file mode 100644 index b60b0fef..00000000 --- a/ext/alsa/gstalsadeviceprobe.h +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright (C) 2001 CodeFactory AB - * Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se> - * Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu> - * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef __GST_ALSA_DEVICE_PROBE_H__ -#define __GST_ALSA_DEVICE_PROBE_H__ - -#include "gstalsa.h" - -G_BEGIN_DECLS - -void gst_alsa_type_add_device_property_probe_interface (GType type); - -G_END_DECLS - -#endif /* __GST_ALSA_DEVICE_PROBE_H__ */ - diff --git a/ext/alsa/gstalsamixer.c b/ext/alsa/gstalsamixer.c deleted file mode 100644 index 1c5d8184..00000000 --- a/ext/alsa/gstalsamixer.c +++ /dev/null @@ -1,933 +0,0 @@ -/* ALSA mixer implementation. - * Copyright (C) 2003 Leif Johnson <leif@ambient.2y.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-alsamixer - * @see_also: alsasink, alsasrc - * - * This element controls various aspects such as the volume and balance - * of an audio device using the ALSA api. - * - * The application should query and use the interfaces provided by this - * element to control the device. - * - * Last reviewed on 2006-03-01 (0.10.4) - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstalsamixer.h" - -static void gst_alsa_mixer_update_option (GstAlsaMixer * mixer, - GstAlsaMixerOptions * alsa_opts); -static void gst_alsa_mixer_update_track (GstAlsaMixer * mixer, - GstAlsaMixerTrack * alsa_track); -static int gst_alsa_mixer_handle_callback (snd_mixer_t * handle, - unsigned int mask, snd_mixer_elem_t * elem); - -/* First some utils, then the mixer implementation */ -static gboolean -gst_alsa_mixer_open (GstAlsaMixer * mixer) -{ - gint err; - snd_ctl_t *ctl; - snd_ctl_card_info_t *card_info; - - g_return_val_if_fail (mixer->handle == NULL, FALSE); - - /* open and initialize the mixer device */ - err = snd_mixer_open (&mixer->handle, 0); - if (err < 0 || mixer->handle == NULL) - goto open_failed; - - if ((err = snd_mixer_attach (mixer->handle, mixer->device)) < 0) { - GST_WARNING ("Cannot open mixer for sound device '%s': %s", mixer->device, - snd_strerror (err)); - goto error; - } - - if ((err = snd_mixer_selem_register (mixer->handle, NULL, NULL)) < 0) { - GST_WARNING ("Cannot register mixer elements: %s", snd_strerror (err)); - goto error; - } - - if ((err = snd_mixer_load (mixer->handle)) < 0) { - GST_WARNING ("Cannot load mixer settings: %s", snd_strerror (err)); - goto error; - } - - snd_mixer_set_callback_private (mixer->handle, mixer); - snd_mixer_set_callback (mixer->handle, gst_alsa_mixer_handle_callback); - - /* now get the device name, any of this is not fatal */ - g_free (mixer->cardname); - if ((err = snd_ctl_open (&ctl, mixer->device, 0)) < 0) { - GST_WARNING ("Cannot open CTL: %s", snd_strerror (err)); - goto no_card_name; - } - - snd_ctl_card_info_malloc (&card_info); - if ((err = snd_ctl_card_info (ctl, card_info)) < 0) { - GST_WARNING ("Cannot get card info: %s", snd_strerror (err)); - snd_ctl_close (ctl); - goto no_card_name; - } - - mixer->cardname = g_strdup (snd_ctl_card_info_get_name (card_info)); - GST_DEBUG ("Card name = %s", GST_STR_NULL (mixer->cardname)); - snd_ctl_card_info_free (card_info); - snd_ctl_close (ctl); - -no_card_name: - if (mixer->cardname == NULL) { - mixer->cardname = g_strdup ("Unknown"); - GST_DEBUG ("Cannot find card name"); - } - - GST_INFO ("Successfully opened mixer for device '%s'.", mixer->device); - - return TRUE; - - /* ERROR */ -open_failed: - { - GST_WARNING ("Cannot open mixer: %s", snd_strerror (err)); - mixer->handle = NULL; - return FALSE; - } -error: - { - snd_mixer_close (mixer->handle); - mixer->handle = NULL; - return FALSE; - } -} - -static snd_mixer_elem_t * -gst_alsa_mixer_find_master_mixer (GstAlsaMixer * mixer, snd_mixer_t * handle) -{ - snd_mixer_elem_t *element; - gint i, count; - - count = snd_mixer_get_count (handle); - - g_static_rec_mutex_lock (mixer->rec_mutex); - - /* Check if we have a playback mixer labelled as 'Master' */ - element = snd_mixer_first_elem (handle); - for (i = 0; i < count; i++) { - if (snd_mixer_selem_has_playback_volume (element) && - strcmp (snd_mixer_selem_get_name (element), "Master") == 0) { - g_static_rec_mutex_unlock (mixer->rec_mutex); - return element; - } - element = snd_mixer_elem_next (element); - } - - /* If not, check if we have a playback mixer labelled as 'Front' */ - element = snd_mixer_first_elem (handle); - for (i = 0; i < count; i++) { - if (snd_mixer_selem_has_playback_volume (element) && - strcmp (snd_mixer_selem_get_name (element), "Front") == 0) { - g_static_rec_mutex_unlock (mixer->rec_mutex); - return element; - } - element = snd_mixer_elem_next (element); - } - - /* If not, check if we have a playback mixer labelled as 'PCM' */ - element = snd_mixer_first_elem (handle); - for (i = 0; i < count; i++) { - if (snd_mixer_selem_has_playback_volume (element) && - strcmp (snd_mixer_selem_get_name (element), "PCM") == 0) { - g_static_rec_mutex_unlock (mixer->rec_mutex); - return element; - } - element = snd_mixer_elem_next (element); - } - - /* If not, check if we have a playback mixer labelled as 'Speaker' */ - element = snd_mixer_first_elem (handle); - for (i = 0; i < count; i++) { - if (snd_mixer_selem_has_playback_volume (element) && - strcmp (snd_mixer_selem_get_name (element), "Speaker") == 0) { - g_static_rec_mutex_unlock (mixer->rec_mutex); - return element; - } - element = snd_mixer_elem_next (element); - } - - /* If not, check if we have a playback mixer with both volume and switch that - * is not mono */ - element = snd_mixer_first_elem (handle); - for (i = 0; i < count; i++) { - if (snd_mixer_selem_has_playback_volume (element) && - snd_mixer_selem_has_playback_switch (element) && - !snd_mixer_selem_is_playback_mono (element)) { - g_static_rec_mutex_unlock (mixer->rec_mutex); - return element; - } - element = snd_mixer_elem_next (element); - } - - /* If not, check if we have any playback mixer with both volume and switch */ - element = snd_mixer_first_elem (handle); - for (i = 0; i < count; i++) { - if (snd_mixer_selem_has_playback_volume (element) && - snd_mixer_selem_has_playback_switch (element)) { - g_static_rec_mutex_unlock (mixer->rec_mutex); - return element; - } - element = snd_mixer_elem_next (element); - } - - /* If not, take any playback mixer with a volume control */ - element = snd_mixer_first_elem (handle); - for (i = 0; i < count; i++) { - if (snd_mixer_selem_has_playback_volume (element)) { - g_static_rec_mutex_unlock (mixer->rec_mutex); - return element; - } - element = snd_mixer_elem_next (element); - } - - g_static_rec_mutex_unlock (mixer->rec_mutex); - /* Looks like we're out of luck ... */ - return NULL; -} - -static void -gst_alsa_mixer_update (GstAlsaMixer * mixer, snd_mixer_elem_t * elem) -{ - GList *item; - - g_return_if_fail (mixer != NULL); - - g_static_rec_mutex_lock (mixer->rec_mutex); - - for (item = mixer->tracklist; item != NULL; item = item->next) { - if (GST_IS_ALSA_MIXER_TRACK (item->data)) { - if (elem && (GST_ALSA_MIXER_TRACK (item->data)->element != elem)) - continue; - - gst_alsa_mixer_update_track (mixer, GST_ALSA_MIXER_TRACK (item->data)); - } else if (GST_IS_ALSA_MIXER_OPTIONS (item->data)) { - if (elem && (GST_ALSA_MIXER_OPTIONS (item->data)->element != elem)) - continue; - - gst_alsa_mixer_update_option (mixer, GST_ALSA_MIXER_OPTIONS (item->data)); - } - } - - g_static_rec_mutex_unlock (mixer->rec_mutex); -} - -static int -gst_alsa_mixer_elem_handle_callback (snd_mixer_elem_t * elem, unsigned int mask) -{ - GstAlsaMixer *mixer = - (GstAlsaMixer *) snd_mixer_elem_get_callback_private (elem); - - GST_LOG ("ALSA elem cb"); - - g_return_val_if_fail (mixer != NULL, 1); - - gst_alsa_mixer_update (mixer, elem); - - return 0; -} - -static int -gst_alsa_mixer_handle_callback (snd_mixer_t * handle, unsigned int mask, - snd_mixer_elem_t * elem) -{ - GstAlsaMixer *mixer = - (GstAlsaMixer *) snd_mixer_get_callback_private (handle); - - GST_LOG ("ALSA cb"); - - g_return_val_if_fail (mixer != NULL, 1); - - /* Hopefully won't be call recursively and will handle pending elem events */ - snd_mixer_handle_events (mixer->handle); - - gst_alsa_mixer_update (mixer, elem); - - return 0; -} - -static void -gst_alsa_mixer_ensure_track_list (GstAlsaMixer * mixer) -{ - gint i, count; - snd_mixer_elem_t *element, *master; - GList *item; - - g_return_if_fail (mixer->handle != NULL); - - if (mixer->tracklist) - return; - - g_static_rec_mutex_lock (mixer->rec_mutex); - - master = gst_alsa_mixer_find_master_mixer (mixer, mixer->handle); - - count = snd_mixer_get_count (mixer->handle); - element = snd_mixer_first_elem (mixer->handle); - - /* build track list - * - * Some ALSA tracks may have playback and capture capabilities. - * Here we model them as two separate GStreamer tracks. - */ - - for (i = 0; i < count; i++) { - GstMixerTrack *play_track = NULL; - GstMixerTrack *cap_track = NULL; - const gchar *name; - GList *item; - gint samename = 0; - - name = snd_mixer_selem_get_name (element); - - /* prevent dup names */ - for (item = mixer->tracklist; item != NULL; item = item->next) { - snd_mixer_elem_t *temp; - - if (GST_IS_ALSA_MIXER_OPTIONS (item->data)) - temp = GST_ALSA_MIXER_OPTIONS (item->data)->element; - else - temp = GST_ALSA_MIXER_TRACK (item->data)->element; - - if (strcmp (name, snd_mixer_selem_get_name (temp)) == 0) - samename++; - } - - GST_LOG ("[%s] probing element #%u, mixer->dir=%u", name, i, mixer->dir); - - if (mixer->dir & GST_ALSA_MIXER_PLAYBACK) { - gboolean has_playback_switch, has_playback_volume; - - has_playback_switch = snd_mixer_selem_has_playback_switch (element); - has_playback_volume = snd_mixer_selem_has_playback_volume (element); - - GST_LOG ("[%s] PLAYBACK: has_playback_volume=%d, has_playback_switch=%d" - "%s", name, has_playback_volume, has_playback_switch, - (element == master) ? " MASTER" : ""); - - if (has_playback_volume) { - gint flags = GST_MIXER_TRACK_OUTPUT; - - if (element == master) - flags |= GST_MIXER_TRACK_MASTER; - - play_track = gst_alsa_mixer_track_new (element, samename, i, - flags, FALSE, NULL, FALSE); - - } else if (has_playback_switch) { - /* simple mute switch */ - play_track = gst_alsa_mixer_track_new (element, samename, i, - GST_MIXER_TRACK_OUTPUT, TRUE, NULL, FALSE); - } - - if (snd_mixer_selem_is_enumerated (element)) { - GstMixerOptions *opts = gst_alsa_mixer_options_new (element, i); - - GST_LOG ("[%s] is enumerated (%d)", name, i); - mixer->tracklist = g_list_append (mixer->tracklist, opts); - } - } - - if (mixer->dir & GST_ALSA_MIXER_CAPTURE) { - gboolean has_capture_switch, has_common_switch; - gboolean has_capture_volume, has_common_volume; - - has_capture_switch = snd_mixer_selem_has_capture_switch (element); - has_common_switch = snd_mixer_selem_has_common_switch (element); - has_capture_volume = snd_mixer_selem_has_capture_volume (element); - has_common_volume = snd_mixer_selem_has_common_volume (element); - - GST_LOG ("[%s] CAPTURE: has_capture_volume=%d, has_common_volume=%d, " - "has_capture_switch=%d, has_common_switch=%d, play_track=%p", name, - has_capture_volume, has_common_volume, has_capture_switch, - has_common_switch, play_track); - - if (has_capture_volume && !(play_track && has_common_volume)) { - cap_track = gst_alsa_mixer_track_new (element, samename, i, - GST_MIXER_TRACK_INPUT, FALSE, NULL, play_track != NULL); - } else if (has_capture_switch && !(play_track && has_common_switch)) { - cap_track = gst_alsa_mixer_track_new (element, samename, i, - GST_MIXER_TRACK_INPUT, TRUE, NULL, play_track != NULL); - } - } - - - if (play_track && cap_track) { - GST_ALSA_MIXER_TRACK (play_track)->shared_mute = - GST_ALSA_MIXER_TRACK (cap_track); - GST_ALSA_MIXER_TRACK (cap_track)->shared_mute = - GST_ALSA_MIXER_TRACK (play_track); - } - - if (play_track) - mixer->tracklist = g_list_append (mixer->tracklist, play_track); - - if (cap_track) - mixer->tracklist = g_list_append (mixer->tracklist, cap_track); - - element = snd_mixer_elem_next (element); - } - - for (item = mixer->tracklist; item != NULL; item = item->next) { - snd_mixer_elem_t *temp; - - if (GST_IS_ALSA_MIXER_OPTIONS (item->data)) - temp = GST_ALSA_MIXER_OPTIONS (item->data)->element; - else - temp = GST_ALSA_MIXER_TRACK (item->data)->element; - - snd_mixer_elem_set_callback (temp, gst_alsa_mixer_elem_handle_callback); - snd_mixer_elem_set_callback_private (temp, mixer); - } - - g_static_rec_mutex_unlock (mixer->rec_mutex); -} - -static void -task_monitor_alsa (gpointer data) -{ - struct pollfd *pfds; - unsigned int nfds, rnfds; - unsigned short revents; - GstAlsaMixer *mixer = (GstAlsaMixer *) data; - - g_static_rec_mutex_lock (mixer->rec_mutex); - - nfds = snd_mixer_poll_descriptors_count (mixer->handle); - if (nfds <= 0) { - GST_ERROR ("snd_mixer_poll_descriptors_count <= 0: %d", nfds); - /* FIXME: sleep ? stop monitoring ? */ - g_static_rec_mutex_unlock (mixer->rec_mutex); - return; - } - - pfds = g_newa (struct pollfd, nfds + 1); - rnfds = snd_mixer_poll_descriptors (mixer->handle, pfds, nfds); - g_assert (rnfds <= nfds); - - pfds[rnfds].fd = mixer->pfd[0]; - pfds[rnfds].events = POLLIN | POLLPRI | POLLHUP | POLLERR; - pfds[rnfds].revents = 0; - - g_static_rec_mutex_unlock (mixer->rec_mutex); - - GST_LOG ("task loop"); - poll (pfds, rnfds + 1, -1); - - g_static_rec_mutex_lock (mixer->rec_mutex); - - snd_mixer_poll_descriptors_revents (mixer->handle, pfds, nfds, &revents); - if (revents & POLLIN || revents & POLLPRI) { - GST_DEBUG ("Handling events"); - snd_mixer_handle_events (mixer->handle); - } - - g_static_rec_mutex_unlock (mixer->rec_mutex); -} - -/* API */ - -GstAlsaMixer * -gst_alsa_mixer_new (const char *device, GstAlsaMixerDirection dir) -{ - GstAlsaMixer *ret = NULL; - - g_return_val_if_fail (device != NULL, NULL); - - ret = g_new0 (GstAlsaMixer, 1); - - if (pipe (ret->pfd) == -1) - goto error; - - ret->rec_mutex = g_new (GStaticRecMutex, 1); - g_static_rec_mutex_init (ret->rec_mutex); - - ret->task_mutex = g_new (GStaticRecMutex, 1); - g_static_rec_mutex_init (ret->task_mutex); - - ret->task = gst_task_create (task_monitor_alsa, ret); - gst_task_set_lock (ret->task, ret->task_mutex); - - ret->device = g_strdup (device); - ret->dir = dir; - - if (!gst_alsa_mixer_open (ret)) - goto error; - - if (gst_task_start (ret->task) == FALSE) { - GST_WARNING ("Could not start alsamixer task"); - } - - return ret; - - /* ERRORS */ -error: - { - gst_alsa_mixer_free (ret); - return NULL; - } -} - -void -gst_alsa_mixer_free (GstAlsaMixer * mixer) -{ - g_return_if_fail (mixer != NULL); - - if (mixer->task) { - if (write (mixer->pfd[1], "stop", 5) <= 0) { - GST_ERROR ("Cannot send " "stop" " to alsamixer task"); - close (mixer->pfd[1]); - mixer->pfd[1] = -1; - } - - if (gst_task_join (mixer->task) == FALSE) { - GST_ERROR ("Cannot join alsamixer task"); - } - - gst_object_unref (mixer->task); - mixer->task = NULL; - } - - g_static_rec_mutex_free (mixer->task_mutex); - g_free (mixer->task_mutex); - mixer->task_mutex = NULL; - - if (mixer->pfd[0] > 0) { - close (mixer->pfd[0]); - mixer->pfd[0] = -1; - } - - if (mixer->pfd[1] > 0) { - close (mixer->pfd[1]); - mixer->pfd[1] = -1; - } - - if (mixer->interface) { - g_object_unref (G_OBJECT (mixer->interface)); - mixer->interface = NULL; - } - - if (mixer->device) { - g_free (mixer->device); - mixer->device = NULL; - } - - if (mixer->cardname) { - g_free (mixer->cardname); - mixer->cardname = NULL; - } - - if (mixer->tracklist) { - g_list_foreach (mixer->tracklist, (GFunc) g_object_unref, NULL); - g_list_free (mixer->tracklist); - mixer->tracklist = NULL; - } - - if (mixer->handle) { - snd_mixer_close (mixer->handle); - mixer->handle = NULL; - } - - g_static_rec_mutex_free (mixer->rec_mutex); - g_free (mixer->rec_mutex); - mixer->rec_mutex = NULL; - - g_free (mixer); -} - -const GList * -gst_alsa_mixer_list_tracks (GstAlsaMixer * mixer) -{ - g_return_val_if_fail (mixer->handle != NULL, NULL); - - gst_alsa_mixer_ensure_track_list (mixer); - - return (const GList *) mixer->tracklist; -} - -void -gst_alsa_mixer_get_volume (GstAlsaMixer * mixer, GstMixerTrack * track, - gint * volumes) -{ - gint i; - GstAlsaMixerTrack *alsa_track = GST_ALSA_MIXER_TRACK (track); - - g_return_if_fail (mixer->handle != NULL); - - g_static_rec_mutex_lock (mixer->rec_mutex); - - gst_alsa_mixer_track_update (alsa_track); - - if (track->flags & GST_MIXER_TRACK_OUTPUT) { /* return playback volume */ - - /* Is emulated mute flag activated? */ - if (track->flags & GST_MIXER_TRACK_MUTE && - !(alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_PSWITCH)) { - for (i = 0; i < track->num_channels; i++) - volumes[i] = alsa_track->volumes[i]; - } else { - for (i = 0; i < track->num_channels; i++) { - long tmp = 0; - - snd_mixer_selem_get_playback_volume (alsa_track->element, i, &tmp); - alsa_track->volumes[i] = volumes[i] = (gint) tmp; - } - } - - } else if (track->flags & GST_MIXER_TRACK_INPUT) { /* return capture volume */ - - /* Is emulated record flag activated? */ - if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH || - track->flags & GST_MIXER_TRACK_RECORD) { - for (i = 0; i < track->num_channels; i++) { - long tmp = 0; - - snd_mixer_selem_get_capture_volume (alsa_track->element, i, &tmp); - alsa_track->volumes[i] = volumes[i] = (gint) tmp; - } - } else { - for (i = 0; i < track->num_channels; i++) - volumes[i] = alsa_track->volumes[i]; - } - } - g_static_rec_mutex_unlock (mixer->rec_mutex); -} - -static gboolean -check_if_volumes_are_the_same (guint num_channels, gint * volumes) -{ - guint i; - - if (num_channels <= 1) - return TRUE; - - for (i = 1; i < num_channels; i++) { - if (volumes[i] != volumes[0]) - return FALSE; - } - - return TRUE; -} - -void -gst_alsa_mixer_set_volume (GstAlsaMixer * mixer, GstMixerTrack * track, - gint * volumes) -{ - GstAlsaMixerTrack *alsa_track = GST_ALSA_MIXER_TRACK (track); - gint i; - - g_return_if_fail (mixer->handle != NULL); - - g_static_rec_mutex_lock (mixer->rec_mutex); - - gst_alsa_mixer_track_update (alsa_track); - - if (track->flags & GST_MIXER_TRACK_OUTPUT) { - - /* Is emulated mute flag activated? */ - if (track->flags & GST_MIXER_TRACK_MUTE && - !(alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_PSWITCH)) { - for (i = 0; i < track->num_channels; i++) - alsa_track->volumes[i] = volumes[i]; - } else { - if (check_if_volumes_are_the_same (track->num_channels, volumes)) { - snd_mixer_selem_set_playback_volume_all (alsa_track->element, - volumes[0]); - for (i = 0; i < track->num_channels; i++) - alsa_track->volumes[i] = volumes[0]; - } else { - for (i = 0; i < track->num_channels; i++) { - alsa_track->volumes[i] = volumes[i]; - snd_mixer_selem_set_playback_volume (alsa_track->element, i, - volumes[i]); - } - } - } - - } else if (track->flags & GST_MIXER_TRACK_INPUT) { - - /* Is emulated record flag activated? */ - if (track->flags & GST_MIXER_TRACK_RECORD || - alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH) { - if (check_if_volumes_are_the_same (track->num_channels, volumes)) { - snd_mixer_selem_set_capture_volume_all (alsa_track->element, - volumes[0]); - for (i = 0; i < track->num_channels; i++) - alsa_track->volumes[i] = volumes[0]; - } else { - for (i = 0; i < track->num_channels; i++) { - alsa_track->volumes[i] = volumes[i]; - snd_mixer_selem_set_capture_volume (alsa_track->element, i, - volumes[i]); - } - } - } else { - for (i = 0; i < track->num_channels; i++) - alsa_track->volumes[i] = volumes[i]; - } - } - g_static_rec_mutex_unlock (mixer->rec_mutex); -} - -void -gst_alsa_mixer_set_mute (GstAlsaMixer * mixer, GstMixerTrack * track, - gboolean mute) -{ - GstAlsaMixerTrack *alsa_track = GST_ALSA_MIXER_TRACK (track); - - g_return_if_fail (mixer->handle != NULL); - - g_static_rec_mutex_lock (mixer->rec_mutex); - - gst_alsa_mixer_track_update (alsa_track); - - if (!!(mute) == !!(track->flags & GST_MIXER_TRACK_MUTE)) { - g_static_rec_mutex_unlock (mixer->rec_mutex); - return; - } - if (mute) { - track->flags |= GST_MIXER_TRACK_MUTE; - - if (alsa_track->shared_mute) - ((GstMixerTrack *) (alsa_track->shared_mute))->flags |= - GST_MIXER_TRACK_MUTE; - } else { - track->flags &= ~GST_MIXER_TRACK_MUTE; - - if (alsa_track->shared_mute) - ((GstMixerTrack *) (alsa_track->shared_mute))->flags &= - ~GST_MIXER_TRACK_MUTE; - } - - if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_PSWITCH) { - snd_mixer_selem_set_playback_switch_all (alsa_track->element, mute ? 0 : 1); - } else { - gint i; - GstAlsaMixerTrack *ctrl_track; - - if ((track->flags & GST_MIXER_TRACK_INPUT) - && alsa_track->shared_mute != NULL) - ctrl_track = alsa_track->shared_mute; - else - ctrl_track = alsa_track; - - for (i = 0; i < ((GstMixerTrack *) ctrl_track)->num_channels; i++) { - long vol = - mute ? ((GstMixerTrack *) ctrl_track)->min_volume : ctrl_track-> - volumes[i]; - snd_mixer_selem_set_playback_volume (ctrl_track->element, i, vol); - } - } - g_static_rec_mutex_unlock (mixer->rec_mutex); -} - -void -gst_alsa_mixer_set_record (GstAlsaMixer * mixer, - GstMixerTrack * track, gboolean record) -{ - GstAlsaMixerTrack *alsa_track = GST_ALSA_MIXER_TRACK (track); - - g_return_if_fail (mixer->handle != NULL); - - g_static_rec_mutex_lock (mixer->rec_mutex); - - gst_alsa_mixer_track_update (alsa_track); - - if (!!(record) == !!(track->flags & GST_MIXER_TRACK_RECORD)) { - g_static_rec_mutex_unlock (mixer->rec_mutex); - return; - } - - if (record) { - track->flags |= GST_MIXER_TRACK_RECORD; - } else { - track->flags &= ~GST_MIXER_TRACK_RECORD; - } - - if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH) { - snd_mixer_selem_set_capture_switch_all (alsa_track->element, - record ? 1 : 0); - - /* update all tracks in same exlusive cswitch group */ - if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH_EXCL) { - GList *item; - - for (item = mixer->tracklist; item != NULL; item = item->next) { - - if (GST_IS_ALSA_MIXER_TRACK (item->data)) { - GstAlsaMixerTrack *item_alsa_track = - GST_ALSA_MIXER_TRACK (item->data); - - if (item_alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH_EXCL && - item_alsa_track->capture_group == alsa_track->capture_group) { - gst_alsa_mixer_track_update (item_alsa_track); - } - } - } - } - } else { - gint i; - - for (i = 0; i < track->num_channels; i++) { - long vol = record ? alsa_track->volumes[i] : track->min_volume; - - snd_mixer_selem_set_capture_volume (alsa_track->element, i, vol); - } - } - g_static_rec_mutex_unlock (mixer->rec_mutex); -} - -void -gst_alsa_mixer_set_option (GstAlsaMixer * mixer, - GstMixerOptions * opts, gchar * value) -{ - gint idx = -1, n = 0; - GList *item; - GstAlsaMixerOptions *alsa_opts = GST_ALSA_MIXER_OPTIONS (opts); - - g_return_if_fail (mixer->handle != NULL); - - for (item = opts->values; item != NULL; item = item->next, n++) { - if (!strcmp (item->data, value)) { - idx = n; - break; - } - } - if (idx == -1) - return; - - g_static_rec_mutex_lock (mixer->rec_mutex); - snd_mixer_selem_set_enum_item (alsa_opts->element, 0, idx); - g_static_rec_mutex_unlock (mixer->rec_mutex); -} - -const gchar * -gst_alsa_mixer_get_option (GstAlsaMixer * mixer, GstMixerOptions * opts) -{ - gint ret; - guint idx; - GstAlsaMixerOptions *alsa_opts = GST_ALSA_MIXER_OPTIONS (opts); - - g_return_val_if_fail (mixer->handle != NULL, NULL); - g_static_rec_mutex_lock (mixer->rec_mutex); - ret = snd_mixer_selem_get_enum_item (alsa_opts->element, 0, &idx); - g_static_rec_mutex_unlock (mixer->rec_mutex); - if (ret == 0) - return g_list_nth_data (opts->values, idx); - else - return snd_strerror (ret); /* feeble attempt at error handling */ -} - -GstMixerFlags -gst_alsa_mixer_get_mixer_flags (GstAlsaMixer * mixer) -{ - g_return_val_if_fail (mixer != NULL, GST_MIXER_FLAG_NONE); - - return GST_MIXER_FLAG_AUTO_NOTIFICATIONS; -} - -static void -gst_alsa_mixer_update_option (GstAlsaMixer * mixer, - GstAlsaMixerOptions * alsa_opts) -{ - gint ret; - guint idx; - /* const */ gchar *option; - - if (mixer->interface == NULL) { - GST_WARNING ("Cannot send update notifications, no GstMixer * given"); - return; - } - g_static_rec_mutex_lock (mixer->rec_mutex); - ret = snd_mixer_selem_get_enum_item (alsa_opts->element, 0, &idx); - g_static_rec_mutex_unlock (mixer->rec_mutex); - if (ret == 0) { - option = g_list_nth_data (GST_MIXER_OPTIONS (alsa_opts)->values, idx); - gst_mixer_option_changed (mixer->interface, GST_MIXER_OPTIONS (alsa_opts), - option); - } -} - -static void -gst_alsa_mixer_update_track (GstAlsaMixer * mixer, - GstAlsaMixerTrack * alsa_track) -{ - GstMixerTrack *track = (GstMixerTrack *) alsa_track; - gboolean old_mute; - gboolean old_record; - gint i, n_channels; - gint *old_volumes; - - GST_DEBUG ("Updating track %" GST_PTR_FORMAT, alsa_track); - - if (mixer->interface == NULL) { - GST_WARNING ("Cannot send update notifications, no GstMixer * given"); - return; - } - - old_mute = !!(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE)); - old_record = !!(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD)); - old_volumes = g_new (gint, track->num_channels); - n_channels = track->num_channels; - memcpy (old_volumes, alsa_track->volumes, - sizeof (gint) * track->num_channels); - - gst_alsa_mixer_track_update (alsa_track); - - if (old_record != - !!(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD))) { - gst_mixer_record_toggled (mixer->interface, track, - !!GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD)); - } - if (old_mute != !!(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE))) { - gst_mixer_mute_toggled (mixer->interface, track, - !!GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE)); - } - - n_channels = MIN (n_channels, track->num_channels); - for (i = 0; i < n_channels; i++) { - if (old_volumes[i] != alsa_track->volumes[i]) { - gst_mixer_volume_changed (mixer->interface, track, alsa_track->volumes); - break; - } - } - g_free (old_volumes); -} - -/* utility function for gstalsamixerelement to set the interface */ -void -_gst_alsa_mixer_set_interface (GstAlsaMixer * mixer, GstMixer * interface) -{ - g_return_if_fail (mixer != NULL && mixer->interface == NULL); - g_return_if_fail (interface != NULL); - - mixer->interface = g_object_ref (G_OBJECT (interface)); -} diff --git a/ext/alsa/gstalsamixer.h b/ext/alsa/gstalsamixer.h deleted file mode 100644 index ccc37845..00000000 --- a/ext/alsa/gstalsamixer.h +++ /dev/null @@ -1,217 +0,0 @@ -/* ALSA mixer interface implementation. - * Copyright (C) 2003 Leif Johnson <leif@ambient.2y.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - -#ifndef __GST_ALSA_MIXER_H__ -#define __GST_ALSA_MIXER_H__ - - -#include "gstalsa.h" - -#include <gst/interfaces/mixer.h> -#include "gstalsamixeroptions.h" -#include "gstalsamixertrack.h" - - -G_BEGIN_DECLS - -/* This does not get you what you think it does, use obj->mixer */ -/* #define GST_ALSA_MIXER(obj) ((GstAlsaMixer*)(obj)) */ - -typedef struct _GstAlsaMixer GstAlsaMixer; - -typedef enum { - GST_ALSA_MIXER_CAPTURE = 1<<0, - GST_ALSA_MIXER_PLAYBACK = 1<<1, - GST_ALSA_MIXER_ALL = GST_ALSA_MIXER_CAPTURE | GST_ALSA_MIXER_PLAYBACK -} GstAlsaMixerDirection; - -/** - * GstAlsaMixer: - * - * Opaque data structure - */ -struct _GstAlsaMixer -{ - GList * tracklist; /* list of available tracks */ - - snd_mixer_t * handle; - - GstTask * task; - GStaticRecMutex * task_mutex; - GStaticRecMutex * rec_mutex; - - int pfd[2]; - - GstMixer * interface; - gchar * device; - gchar * cardname; - - GstAlsaMixerDirection dir; -}; - - -GstAlsaMixer* gst_alsa_mixer_new (const gchar *device, - GstAlsaMixerDirection dir); -void gst_alsa_mixer_free (GstAlsaMixer *mixer); - -const GList* gst_alsa_mixer_list_tracks (GstAlsaMixer * mixer); -void gst_alsa_mixer_set_volume (GstAlsaMixer * mixer, - GstMixerTrack * track, - gint * volumes); -void gst_alsa_mixer_get_volume (GstAlsaMixer * mixer, - GstMixerTrack * track, - gint * volumes); -void gst_alsa_mixer_set_record (GstAlsaMixer * mixer, - GstMixerTrack * track, - gboolean record); -void gst_alsa_mixer_set_mute (GstAlsaMixer * mixer, - GstMixerTrack * track, - gboolean mute); -void gst_alsa_mixer_set_option (GstAlsaMixer * mixer, - GstMixerOptions * opts, - gchar * value); -const gchar* gst_alsa_mixer_get_option (GstAlsaMixer * mixer, - GstMixerOptions * opts); -void _gst_alsa_mixer_set_interface (GstAlsaMixer * mixer, - GstMixer * interface); -GstMixerFlags gst_alsa_mixer_get_mixer_flags (GstAlsaMixer *mixer); - -#define GST_IMPLEMENT_ALSA_MIXER_METHODS(Type, interface_as_function) \ -static gboolean \ -interface_as_function ## _supported (Type *this, GType iface_type) \ -{ \ - g_assert (iface_type == GST_TYPE_MIXER); \ - \ - return (this->mixer != NULL); \ -} \ - \ -static const GList* \ -interface_as_function ## _list_tracks (GstMixer * mixer) \ -{ \ - Type *this = (Type*) mixer; \ - \ - g_return_val_if_fail (this != NULL, NULL); \ - g_return_val_if_fail (this->mixer != NULL, NULL); \ - \ - return gst_alsa_mixer_list_tracks (this->mixer); \ -} \ - \ -static void \ -interface_as_function ## _set_volume (GstMixer * mixer, GstMixerTrack * track, \ - gint * volumes) \ -{ \ - Type *this = (Type*) mixer; \ - \ - g_return_if_fail (this != NULL); \ - g_return_if_fail (this->mixer != NULL); \ - \ - gst_alsa_mixer_set_volume (this->mixer, track, volumes); \ -} \ - \ -static void \ -interface_as_function ## _get_volume (GstMixer * mixer, GstMixerTrack * track, \ - gint * volumes) \ -{ \ - Type *this = (Type*) mixer; \ - \ - g_return_if_fail (this != NULL); \ - g_return_if_fail (this->mixer != NULL); \ - \ - gst_alsa_mixer_get_volume (this->mixer, track, volumes); \ -} \ - \ -static void \ -interface_as_function ## _set_record (GstMixer * mixer, GstMixerTrack * track, \ - gboolean record) \ -{ \ - Type *this = (Type*) mixer; \ - \ - g_return_if_fail (this != NULL); \ - g_return_if_fail (this->mixer != NULL); \ - \ - gst_alsa_mixer_set_record (this->mixer, track, record); \ -} \ - \ -static void \ -interface_as_function ## _set_mute (GstMixer * mixer, GstMixerTrack * track, \ - gboolean mute) \ -{ \ - Type *this = (Type*) mixer; \ - \ - g_return_if_fail (this != NULL); \ - g_return_if_fail (this->mixer != NULL); \ - \ - gst_alsa_mixer_set_mute (this->mixer, track, mute); \ -} \ - \ -static void \ -interface_as_function ## _set_option (GstMixer * mixer, GstMixerOptions * opts, \ - gchar * value) \ -{ \ - Type *this = (Type*) mixer; \ - \ - g_return_if_fail (this != NULL); \ - g_return_if_fail (this->mixer != NULL); \ - \ - gst_alsa_mixer_set_option (this->mixer, opts, value); \ -} \ - \ -static const gchar* \ -interface_as_function ## _get_option (GstMixer * mixer, GstMixerOptions * opts) \ -{ \ - Type *this = (Type*) mixer; \ - \ - g_return_val_if_fail (this != NULL, NULL); \ - g_return_val_if_fail (this->mixer != NULL, NULL); \ - \ - return gst_alsa_mixer_get_option (this->mixer, opts); \ -} \ - \ -static GstMixerFlags \ -interface_as_function ## _get_mixer_flags (GstMixer * mixer) \ -{ \ - Type *this = (Type*) mixer; \ - \ - g_return_val_if_fail (this != NULL, GST_MIXER_FLAG_NONE); \ - g_return_val_if_fail (this->mixer != NULL, GST_MIXER_FLAG_NONE); \ - \ - return gst_alsa_mixer_get_mixer_flags (this->mixer); \ -} \ - \ -static void \ -interface_as_function ## _interface_init (GstMixerClass * klass) \ -{ \ - GST_MIXER_TYPE (klass) = GST_MIXER_HARDWARE; \ - \ - /* set up the interface hooks */ \ - klass->list_tracks = interface_as_function ## _list_tracks; \ - klass->set_volume = interface_as_function ## _set_volume; \ - klass->get_volume = interface_as_function ## _get_volume; \ - klass->set_mute = interface_as_function ## _set_mute; \ - klass->set_record = interface_as_function ## _set_record; \ - klass->set_option = interface_as_function ## _set_option; \ - klass->get_option = interface_as_function ## _get_option; \ - klass->get_mixer_flags = interface_as_function ## _get_mixer_flags; \ -} - - -G_END_DECLS - - -#endif /* __GST_ALSA_MIXER_H__ */ diff --git a/ext/alsa/gstalsamixerelement.c b/ext/alsa/gstalsamixerelement.c deleted file mode 100644 index 57e35263..00000000 --- a/ext/alsa/gstalsamixerelement.c +++ /dev/null @@ -1,248 +0,0 @@ -/* ALSA mixer implementation. - * Copyright (C) 2003 Leif Johnson <leif@ambient.2y.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstalsamixerelement.h" -#include "gstalsadeviceprobe.h" - -#define DEFAULT_PROP_DEVICE "default" -#define DEFAULT_PROP_DEVICE_NAME "" - -enum -{ - PROP_0, - PROP_DEVICE, - PROP_DEVICE_NAME -}; - -static const GstElementDetails gst_alsa_mixer_element_details = -GST_ELEMENT_DETAILS ("Alsa mixer", - "Generic/Audio", - "Control sound input and output levels with ALSA", - "Leif Johnson <leif@ambient.2y.net>"); - -static void gst_alsa_mixer_element_init_interfaces (GType type); - -GST_BOILERPLATE_FULL (GstAlsaMixerElement, gst_alsa_mixer_element, - GstElement, GST_TYPE_ELEMENT, gst_alsa_mixer_element_init_interfaces); - -/* massive macro that takes care of all the GstMixer stuff */ -GST_IMPLEMENT_ALSA_MIXER_METHODS (GstAlsaMixerElement, gst_alsa_mixer_element); - -static void gst_alsa_mixer_element_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); -static void gst_alsa_mixer_element_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_alsa_mixer_element_finalize (GObject * object); - -static GstStateChangeReturn gst_alsa_mixer_element_change_state (GstElement - * element, GstStateChange transition); - -static gboolean -gst_alsa_mixer_element_interface_supported (GstAlsaMixerElement * this, - GType interface_type) -{ - if (interface_type == GST_TYPE_MIXER) { - return gst_alsa_mixer_element_supported (this, interface_type); - } - - g_return_val_if_reached (FALSE); -} - -static void -gst_implements_interface_init (GstImplementsInterfaceClass * klass) -{ - klass->supported = (gpointer) gst_alsa_mixer_element_interface_supported; -} - -static void -gst_alsa_mixer_element_init_interfaces (GType type) -{ - static const GInterfaceInfo implements_iface_info = { - (GInterfaceInitFunc) gst_implements_interface_init, - NULL, - NULL, - }; - static const GInterfaceInfo mixer_iface_info = { - (GInterfaceInitFunc) gst_alsa_mixer_element_interface_init, - NULL, - NULL, - }; - - g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, - &implements_iface_info); - g_type_add_interface_static (type, GST_TYPE_MIXER, &mixer_iface_info); - - gst_alsa_type_add_device_property_probe_interface (type); -} - -static void -gst_alsa_mixer_element_base_init (gpointer klass) -{ - gst_element_class_set_details (GST_ELEMENT_CLASS (klass), - &gst_alsa_mixer_element_details); -} - -static void -gst_alsa_mixer_element_class_init (GstAlsaMixerElementClass * klass) -{ - GstElementClass *element_class; - GObjectClass *gobject_class; - - element_class = (GstElementClass *) klass; - gobject_class = (GObjectClass *) klass; - - gobject_class->finalize = gst_alsa_mixer_element_finalize; - gobject_class->get_property = gst_alsa_mixer_element_get_property; - gobject_class->set_property = gst_alsa_mixer_element_set_property; - - g_object_class_install_property (gobject_class, PROP_DEVICE, - g_param_spec_string ("device", "Device", - "ALSA device, as defined in an asound configuration file", - DEFAULT_PROP_DEVICE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_DEVICE_NAME, - g_param_spec_string ("device-name", "Device name", - "Human-readable name of the sound device", - DEFAULT_PROP_DEVICE_NAME, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - element_class->change_state = - GST_DEBUG_FUNCPTR (gst_alsa_mixer_element_change_state); -} - -static void -gst_alsa_mixer_element_finalize (GObject * obj) -{ - GstAlsaMixerElement *this = GST_ALSA_MIXER_ELEMENT (obj); - - g_free (this->device); - - G_OBJECT_CLASS (parent_class)->finalize (obj); -} - -static void -gst_alsa_mixer_element_init (GstAlsaMixerElement * this, - GstAlsaMixerElementClass * klass) -{ - this->mixer = NULL; - this->device = g_strdup (DEFAULT_PROP_DEVICE); -} - -static void -gst_alsa_mixer_element_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstAlsaMixerElement *this = GST_ALSA_MIXER_ELEMENT (object); - - switch (prop_id) { - case PROP_DEVICE:{ - GST_OBJECT_LOCK (this); - g_free (this->device); - this->device = g_value_dup_string (value); - /* make sure we never set NULL, this is nice when we want to open the - * device. */ - if (this->device == NULL) - this->device = g_strdup (DEFAULT_PROP_DEVICE); - GST_OBJECT_UNLOCK (this); - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_alsa_mixer_element_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstAlsaMixerElement *this = GST_ALSA_MIXER_ELEMENT (object); - - switch (prop_id) { - case PROP_DEVICE:{ - GST_OBJECT_LOCK (this); - g_value_set_string (value, this->device); - GST_OBJECT_UNLOCK (this); - break; - } - case PROP_DEVICE_NAME:{ - GST_OBJECT_LOCK (this); - if (this->mixer) { - g_value_set_string (value, this->mixer->cardname); - } else { - g_value_set_string (value, NULL); - } - GST_OBJECT_UNLOCK (this); - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static GstStateChangeReturn -gst_alsa_mixer_element_change_state (GstElement * element, - GstStateChange transition) -{ - GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - GstAlsaMixerElement *this = GST_ALSA_MIXER_ELEMENT (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - if (!this->mixer) { - this->mixer = gst_alsa_mixer_new (this->device, GST_ALSA_MIXER_ALL); - if (!this->mixer) - goto open_failed; - _gst_alsa_mixer_set_interface (this->mixer, GST_MIXER (element)); - } - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (ret == GST_STATE_CHANGE_FAILURE) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_NULL: - if (this->mixer) { - gst_alsa_mixer_free (this->mixer); - this->mixer = NULL; - } - break; - default: - break; - } - - return ret; - - /* ERRORS */ -open_failed: - { - GST_ELEMENT_ERROR (element, RESOURCE, OPEN_READ_WRITE, (NULL), - ("Failed to open alsa mixer device '%s'", this->device)); - return GST_STATE_CHANGE_FAILURE; - } -} diff --git a/ext/alsa/gstalsamixerelement.h b/ext/alsa/gstalsamixerelement.h deleted file mode 100644 index 16cb2458..00000000 --- a/ext/alsa/gstalsamixerelement.h +++ /dev/null @@ -1,61 +0,0 @@ -/* ALSA mixer interface implementation. - * Copyright (C) 2003 Leif Johnson <leif@ambient.2y.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - -#ifndef __GST_ALSA_MIXER_ELEMENT_H__ -#define __GST_ALSA_MIXER_ELEMENT_H__ - - -#include "gstalsa.h" -#include "gstalsamixer.h" - -G_BEGIN_DECLS - -#define GST_ALSA_MIXER_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALSA_MIXER_ELEMENT,GstAlsaMixerElement)) -#define GST_ALSA_MIXER_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALSA_MIXER_ELEMENT,GstAlsaMixerElementClass)) -#define GST_IS_ALSA_MIXER_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALSA_MIXER_ELEMENT)) -#define GST_IS_ALSA_MIXER_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALSA_MIXER_ELEMENT)) -#define GST_TYPE_ALSA_MIXER_ELEMENT (gst_alsa_mixer_element_get_type()) - -typedef struct _GstAlsaMixerElement GstAlsaMixerElement; -typedef struct _GstAlsaMixerElementClass GstAlsaMixerElementClass; - -/** - * GstAlsaMixerElement - * - * Opaque datastructure. - */ -struct _GstAlsaMixerElement { - GstElement parent; - - GstAlsaMixer *mixer; - gchar *device; -}; - -struct _GstAlsaMixerElementClass { - GstElementClass parent; -}; - - -GType gst_alsa_mixer_element_get_type (void); - - -G_END_DECLS - - -#endif /* __GST_ALSA_MIXER_ELEMENT_H__ */ diff --git a/ext/alsa/gstalsamixeroptions.c b/ext/alsa/gstalsamixeroptions.c deleted file mode 100644 index 20bb89d7..00000000 --- a/ext/alsa/gstalsamixeroptions.c +++ /dev/null @@ -1,109 +0,0 @@ -/* ALSA mixer object implementation. - * Copyright (C) 2003 Leif Johnson <leif@ambient.2y.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstalsamixeroptions.h" - -static void gst_alsa_mixer_options_init (GstAlsaMixerOptions * alsa_opts); -static void gst_alsa_mixer_options_class_init (gpointer g_class, - gpointer class_data); - -static GstMixerOptionsClass *parent_class = NULL; - -GType -gst_alsa_mixer_options_get_type (void) -{ - static GType opts_type = 0; - - if (!opts_type) { - static const GTypeInfo opts_info = { - sizeof (GstAlsaMixerOptionsClass), - NULL, - NULL, - gst_alsa_mixer_options_class_init, - NULL, - NULL, - sizeof (GstAlsaMixerOptions), - 0, - (GInstanceInitFunc) gst_alsa_mixer_options_init, - }; - - opts_type = - g_type_register_static (GST_TYPE_MIXER_OPTIONS, "GstAlsaMixerOptions", - &opts_info, 0); - } - - return opts_type; -} - -static void -gst_alsa_mixer_options_class_init (gpointer g_class, gpointer class_data) -{ - parent_class = g_type_class_peek_parent (g_class); -} - -static void -gst_alsa_mixer_options_init (GstAlsaMixerOptions * alsa_opts) -{ -} - -GstMixerOptions * -gst_alsa_mixer_options_new (snd_mixer_elem_t * element, gint track_num) -{ - GstMixerOptions *opts; - GstAlsaMixerOptions *alsa_opts; - GstMixerTrack *track; - const gchar *label; - guint index; - gint num, i; - gchar str[256]; - - label = snd_mixer_selem_get_name (element); - index = snd_mixer_selem_get_index (element); - - GST_LOG ("[%s,%u]", label, index); - - opts = g_object_new (GST_ALSA_MIXER_OPTIONS_TYPE, - "untranslated-label", label, "index", index, NULL); - alsa_opts = (GstAlsaMixerOptions *) opts; - track = (GstMixerTrack *) opts; - - /* set basic information */ - track->label = g_strdup (label); /* FIXME: translate this? */ - track->num_channels = 0; - track->flags = 0; - alsa_opts->element = element; - alsa_opts->track_num = track_num; - - /* get enumerations for switch/options object */ - num = snd_mixer_selem_get_enum_items (element); - for (i = 0; i < num; i++) { - if (snd_mixer_selem_get_enum_item_name (element, i, 255, str) < 0) { - g_object_unref (G_OBJECT (alsa_opts)); - return NULL; - } - - opts->values = g_list_append (opts->values, g_strdup (str)); - } - - return opts; -} diff --git a/ext/alsa/gstalsamixeroptions.h b/ext/alsa/gstalsamixeroptions.h deleted file mode 100644 index b3c36c0d..00000000 --- a/ext/alsa/gstalsamixeroptions.h +++ /dev/null @@ -1,62 +0,0 @@ -/* ALSA mixer options object. - * Copyright (C) 2003 Leif Johnson <leif@ambient.2y.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - -#ifndef __GST_ALSA_MIXER_OPTIONS_H__ -#define __GST_ALSA_MIXER_OPTIONS_H__ - - -#include "gstalsa.h" -#include <gst/interfaces/mixeroptions.h> - - -G_BEGIN_DECLS - - -#define GST_ALSA_MIXER_OPTIONS_TYPE (gst_alsa_mixer_options_get_type ()) -#define GST_ALSA_MIXER_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALSA_MIXER_OPTIONS,GstAlsaMixerOptions)) -#define GST_ALSA_MIXER_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALSA_MIXER_OPTIONS,GstAlsaMixerOptionsClass)) -#define GST_IS_ALSA_MIXER_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALSA_MIXER_OPTIONS)) -#define GST_IS_ALSA_MIXER_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALSA_MIXER_OPTIONS)) -#define GST_TYPE_ALSA_MIXER_OPTIONS (gst_alsa_mixer_options_get_type()) - - -typedef struct _GstAlsaMixerOptions GstAlsaMixerOptions; -typedef struct _GstAlsaMixerOptionsClass GstAlsaMixerOptionsClass; - - -struct _GstAlsaMixerOptions { - GstMixerOptions parent; - snd_mixer_elem_t *element; /* the ALSA mixer element for this track */ - gint track_num; -}; - -struct _GstAlsaMixerOptionsClass { - GstMixerOptionsClass parent; -}; - - -GType gst_alsa_mixer_options_get_type (void); -GstMixerOptions *gst_alsa_mixer_options_new (snd_mixer_elem_t * element, - gint track_num); - - -G_END_DECLS - - -#endif /* __GST_ALSA_MIXER_OPTIONS_H__ */ diff --git a/ext/alsa/gstalsamixertrack.c b/ext/alsa/gstalsamixertrack.c deleted file mode 100644 index a1fdb7f3..00000000 --- a/ext/alsa/gstalsamixertrack.c +++ /dev/null @@ -1,355 +0,0 @@ -/* ALSA mixer track implementation. - * Copyright (C) 2003 Leif Johnson <leif@ambient.2y.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <gst/gst-i18n-plugin.h> - -#include "gstalsamixertrack.h" - -static void gst_alsa_mixer_track_init (GstAlsaMixerTrack * alsa_track); -static void gst_alsa_mixer_track_class_init (gpointer g_class, - gpointer class_data); - -static GstMixerTrackClass *parent_class = NULL; - -GType -gst_alsa_mixer_track_get_type (void) -{ - static GType track_type = 0; - - if (!track_type) { - static const GTypeInfo track_info = { - sizeof (GstAlsaMixerTrackClass), - NULL, - NULL, - gst_alsa_mixer_track_class_init, - NULL, - NULL, - sizeof (GstAlsaMixerTrack), - 0, - (GInstanceInitFunc) gst_alsa_mixer_track_init, - NULL - }; - - track_type = - g_type_register_static (GST_TYPE_MIXER_TRACK, "GstAlsaMixerTrack", - &track_info, 0); - } - - return track_type; -} - -static void -gst_alsa_mixer_track_class_init (gpointer g_class, gpointer class_data) -{ - parent_class = g_type_class_peek_parent (g_class); -} - -static void -gst_alsa_mixer_track_init (GstAlsaMixerTrack * alsa_track) -{ -} - -static void -gst_alsa_mixer_track_update_alsa_capabilities (GstAlsaMixerTrack * alsa_track) -{ - alsa_track->alsa_flags = 0; - alsa_track->capture_group = -1; - - /* common flags */ - if (snd_mixer_selem_has_common_volume (alsa_track->element)) - alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_VOLUME; - - if (snd_mixer_selem_has_common_switch (alsa_track->element)) - alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_SWITCH; - - /* Since we create two separate mixer track objects for alsa elements that - * support both playback and capture, we're going to 'hide' the alsa flags - * that don't pertain to this mixer track from alsa_flags, otherwise - * gst_alsa_mixer_track_update() is going to do things we don't want */ - - /* playback flags */ - if ((GST_MIXER_TRACK (alsa_track)->flags & GST_MIXER_TRACK_OUTPUT)) { - if (snd_mixer_selem_has_playback_volume (alsa_track->element)) - alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_PVOLUME; - - if (snd_mixer_selem_has_playback_switch (alsa_track->element)) - alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_PSWITCH; - } - - /* capture flags */ - if ((GST_MIXER_TRACK (alsa_track)->flags & GST_MIXER_TRACK_INPUT)) { - if (snd_mixer_selem_has_capture_volume (alsa_track->element)) - alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_CVOLUME; - - if (snd_mixer_selem_has_capture_switch (alsa_track->element)) { - alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_CSWITCH; - - if (snd_mixer_selem_has_capture_switch_exclusive (alsa_track->element)) { - alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_CSWITCH_EXCL; - alsa_track->capture_group = - snd_mixer_selem_get_capture_group (alsa_track->element); - } - } - } - - GST_LOG ("[%s] alsa_flags=0x%08x, capture_group=%d", - snd_mixer_selem_get_name (alsa_track->element), - alsa_track->alsa_flags, alsa_track->capture_group); -} - -inline static gboolean -alsa_track_has_cap (GstAlsaMixerTrack * alsa_track, guint32 flag) -{ - return ((alsa_track->alsa_flags & flag) != 0); -} - -GstMixerTrack * -gst_alsa_mixer_track_new (snd_mixer_elem_t * element, - gint num, gint track_num, gint flags, gboolean sw, - GstAlsaMixerTrack * shared_mute_track, gboolean append_capture) -{ - GstAlsaMixerTrack *alsa_track; - GstMixerTrack *track; - const gchar *name; - guint index; - const gchar *label; - gint i; - long min = 0, max = 0; - const struct - { - const gchar orig[12]; - const gchar trans[12]; - } alsa_track_labels[] = { - { - "Master", N_("Master")}, { - "Bass", N_("Bass")}, { - "Treble", N_("Treble")}, { - "PCM", N_("PCM")}, { - "Synth", N_("Synth")}, { - "Line", N_("Line-in")}, { - "CD", N_("CD")}, { - "Mic", N_("Microphone")}, { - "PC Speaker", N_("PC Speaker")}, { - "Playback", N_("Playback")}, { - "Capture", N_("Capture")} - }; - - name = snd_mixer_selem_get_name (element); - index = snd_mixer_selem_get_index (element); - - GST_LOG - ("[%s,%u] num=%d,track_num=%d,flags=0x%08x,sw=%s,shared_mute_track=%p", - name, index, num, track_num, flags, (sw) ? "true" : "false", - shared_mute_track); - - track = (GstMixerTrack *) g_object_new (GST_ALSA_MIXER_TRACK_TYPE, - "untranslated-label", name, "index", index, NULL); - - alsa_track = (GstAlsaMixerTrack *) track; - - GST_LOG ("[%s] created new mixer track %p", name, track); - - /* This reflects the assumptions used for GstAlsaMixerTrack */ - if (!(!!(flags & GST_MIXER_TRACK_OUTPUT) ^ !!(flags & GST_MIXER_TRACK_INPUT))) { - GST_ERROR ("Mixer track must be either output or input!"); - g_return_val_if_reached (NULL); - } - - track->flags = flags; - alsa_track->element = element; - alsa_track->shared_mute = shared_mute_track; - alsa_track->track_num = track_num; - alsa_track->alsa_channels = 0; - - gst_alsa_mixer_track_update_alsa_capabilities (alsa_track); - - if (flags & GST_MIXER_TRACK_OUTPUT) { - while (alsa_track->alsa_channels < GST_ALSA_MAX_CHANNELS && - snd_mixer_selem_has_playback_channel (element, - alsa_track->alsa_channels)) { - alsa_track->alsa_channels++; - } - GST_LOG ("[%s] %d output channels", name, alsa_track->alsa_channels); - } else if (flags & GST_MIXER_TRACK_INPUT) { - while (alsa_track->alsa_channels < GST_ALSA_MAX_CHANNELS && - snd_mixer_selem_has_capture_channel (element, - alsa_track->alsa_channels)) { - alsa_track->alsa_channels++; - } - GST_LOG ("[%s] %d input channels", name, alsa_track->alsa_channels); - } else { - g_assert_not_reached (); - } - - if (sw) - track->num_channels = 0; - else - track->num_channels = alsa_track->alsa_channels; - - /* translate the name if we can */ - label = name; - for (i = 0; i < G_N_ELEMENTS (alsa_track_labels); ++i) { - if (g_utf8_collate (label, alsa_track_labels[i].orig) == 0) { - label = _(alsa_track_labels[i].trans); - break; - } - } - - if (num == 0) { - track->label = g_strdup_printf ("%s%s%s", label, - append_capture ? " " : "", append_capture ? _("Capture") : ""); - } else { - track->label = g_strdup_printf ("%s%s%s %d", label, - append_capture ? " " : "", append_capture ? _("Capture") : "", num); - } - - /* set volume information */ - if (track->num_channels > 0) { - if ((flags & GST_MIXER_TRACK_OUTPUT)) - snd_mixer_selem_get_playback_volume_range (element, &min, &max); - else - snd_mixer_selem_get_capture_volume_range (element, &min, &max); - } - track->min_volume = (gint) min; - track->max_volume = (gint) max; - - for (i = 0; i < track->num_channels; i++) { - long tmp = 0; - - if (flags & GST_MIXER_TRACK_OUTPUT) - snd_mixer_selem_get_playback_volume (element, i, &tmp); - else - snd_mixer_selem_get_capture_volume (element, i, &tmp); - - alsa_track->volumes[i] = (gint) tmp; - } - - gst_alsa_mixer_track_update (alsa_track); - - return track; -} - -void -gst_alsa_mixer_track_update (GstAlsaMixerTrack * alsa_track) -{ - GstMixerTrack *track = (GstMixerTrack *) alsa_track; - gint i; - gint audible = !(track->flags & GST_MIXER_TRACK_MUTE); - - if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_PVOLUME)) { - /* update playback volume */ - for (i = 0; i < track->num_channels; i++) { - long vol = 0; - - snd_mixer_selem_get_playback_volume (alsa_track->element, i, &vol); - alsa_track->volumes[i] = (gint) vol; - } - } - - if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_CVOLUME)) { - /* update capture volume */ - for (i = 0; i < track->num_channels; i++) { - long vol = 0; - - snd_mixer_selem_get_capture_volume (alsa_track->element, i, &vol); - alsa_track->volumes[i] = (gint) vol; - } - } - - /* Any updates in flags? */ - if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_PSWITCH)) { - int v = 0; - - audible = 0; - for (i = 0; i < alsa_track->alsa_channels; ++i) { - snd_mixer_selem_get_playback_switch (alsa_track->element, i, &v); - audible += v; - } - - } else if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_PVOLUME) && - track->flags & GST_MIXER_TRACK_MUTE) { - /* check if user has raised volume with a parallel running application */ - - for (i = 0; i < track->num_channels; i++) { - long vol = 0; - - snd_mixer_selem_get_playback_volume (alsa_track->element, i, &vol); - - if (vol > track->min_volume) { - audible = 1; - break; - } - } - } - - if (!!(audible) != !(track->flags & GST_MIXER_TRACK_MUTE)) { - if (audible) { - track->flags &= ~GST_MIXER_TRACK_MUTE; - - if (alsa_track->shared_mute) - ((GstMixerTrack *) (alsa_track->shared_mute))->flags &= - ~GST_MIXER_TRACK_MUTE; - } else { - track->flags |= GST_MIXER_TRACK_MUTE; - - if (alsa_track->shared_mute) - ((GstMixerTrack *) (alsa_track->shared_mute))->flags |= - GST_MIXER_TRACK_MUTE; - } - } - - if (track->flags & GST_MIXER_TRACK_INPUT) { - gint recording = track->flags & GST_MIXER_TRACK_RECORD; - - if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_CSWITCH)) { - int v = 0; - - recording = 0; - for (i = 0; i < alsa_track->alsa_channels; ++i) { - snd_mixer_selem_get_capture_switch (alsa_track->element, i, &v); - recording += v; - } - - } else if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_CVOLUME) && - !(track->flags & GST_MIXER_TRACK_RECORD)) { - /* check if user has raised volume with a parallel running application */ - - for (i = 0; i < track->num_channels; i++) { - long vol = 0; - - snd_mixer_selem_get_capture_volume (alsa_track->element, i, &vol); - - if (vol > track->min_volume) { - recording = 1; - break; - } - } - } - - if (recording) - track->flags |= GST_MIXER_TRACK_RECORD; - else - track->flags &= ~GST_MIXER_TRACK_RECORD; - } - -} diff --git a/ext/alsa/gstalsamixertrack.h b/ext/alsa/gstalsamixertrack.h deleted file mode 100644 index acc64cb3..00000000 --- a/ext/alsa/gstalsamixertrack.h +++ /dev/null @@ -1,79 +0,0 @@ -/* ALSA mixer track object. - * Copyright (C) 2003 Leif Johnson <leif@ambient.2y.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - -#ifndef __GST_ALSA_MIXER_TRACK_H__ -#define __GST_ALSA_MIXER_TRACK_H__ - - -#include "gstalsa.h" -#include <gst/interfaces/mixertrack.h> - - -G_BEGIN_DECLS - - -#define GST_ALSA_MIXER_TRACK_TYPE (gst_alsa_mixer_track_get_type ()) -#define GST_ALSA_MIXER_TRACK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALSA_MIXER_TRACK,GstAlsaMixerTrack)) -#define GST_ALSA_MIXER_TRACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALSA_MIXER_TRACK,GstAlsaMixerTrackClass)) -#define GST_IS_ALSA_MIXER_TRACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALSA_MIXER_TRACK)) -#define GST_IS_ALSA_MIXER_TRACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALSA_MIXER_TRACK)) -#define GST_TYPE_ALSA_MIXER_TRACK (gst_alsa_mixer_track_get_type()) - -typedef struct _GstAlsaMixerTrack GstAlsaMixerTrack; -typedef struct _GstAlsaMixerTrackClass GstAlsaMixerTrackClass; - -#define GST_ALSA_MIXER_TRACK_VOLUME (1<<0) /* common volume */ -#define GST_ALSA_MIXER_TRACK_PVOLUME (1<<1) -#define GST_ALSA_MIXER_TRACK_CVOLUME (1<<2) -#define GST_ALSA_MIXER_TRACK_SWITCH (1<<3) /* common switch */ -#define GST_ALSA_MIXER_TRACK_PSWITCH (1<<4) -#define GST_ALSA_MIXER_TRACK_CSWITCH (1<<5) -#define GST_ALSA_MIXER_TRACK_CSWITCH_EXCL (1<<6) - -#define GST_ALSA_MAX_CHANNELS (SND_MIXER_SCHN_LAST+1) - -struct _GstAlsaMixerTrack { - GstMixerTrack parent; - snd_mixer_elem_t *element; /* the ALSA mixer element for this track */ - GstAlsaMixerTrack *shared_mute; - gint track_num; - guint32 alsa_flags; /* alsa track capabilities */ - gint alsa_channels; - gint capture_group; - gint volumes[GST_ALSA_MAX_CHANNELS]; -}; - -struct _GstAlsaMixerTrackClass { - GstMixerTrackClass parent; -}; - -GType gst_alsa_mixer_track_get_type (void); -GstMixerTrack * gst_alsa_mixer_track_new (snd_mixer_elem_t * element, - gint num, - gint track_num, - gint flags, - gboolean sw, /* is simple switch? */ - GstAlsaMixerTrack * shared_mute_track, - gboolean label_append_capture); -void gst_alsa_mixer_track_update (GstAlsaMixerTrack * alsa_track); - -G_END_DECLS - - -#endif /* __GST_ALSA_MIXER_TRACK_H__ */ diff --git a/ext/alsa/gstalsaplugin.c b/ext/alsa/gstalsaplugin.c deleted file mode 100644 index 38f16e20..00000000 --- a/ext/alsa/gstalsaplugin.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2001 CodeFactory AB - * Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se> - * Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu> - * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstalsasink.h" -#include "gstalsasrc.h" -#include "gstalsamixerelement.h" - -#include <gst/gst-i18n-plugin.h> - -GST_DEBUG_CATEGORY (alsa_debug); - -/* ALSA debugging wrapper */ -static void -gst_alsa_error_wrapper (const char *file, int line, const char *function, - int err, const char *fmt, ...) -{ -#ifndef GST_DISABLE_GST_DEBUG - va_list args; - gchar *str; - - va_start (args, fmt); - str = g_strdup_vprintf (fmt, args); - va_end (args); - /* FIXME: use GST_LEVEL_ERROR here? Currently warning is used because we're - * able to catch enough of the errors that would be printed otherwise - */ - gst_debug_log (alsa_debug, GST_LEVEL_WARNING, file, function, line, NULL, - "alsalib error: %s%s%s", str, err ? ": " : "", - err ? snd_strerror (err) : ""); - g_free (str); -#endif -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - int err; - - if (!gst_element_register (plugin, "alsamixer", GST_RANK_NONE, - GST_TYPE_ALSA_MIXER_ELEMENT)) - return FALSE; - if (!gst_element_register (plugin, "alsasrc", GST_RANK_PRIMARY, - GST_TYPE_ALSA_SRC)) - return FALSE; - if (!gst_element_register (plugin, "alsasink", GST_RANK_PRIMARY, - GST_TYPE_ALSA_SINK)) - return FALSE; - - GST_DEBUG_CATEGORY_INIT (alsa_debug, "alsa", 0, "alsa plugins"); - -#if ENABLE_NLS - GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE, - LOCALEDIR); - bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); -#endif - - err = snd_lib_error_set_handler (gst_alsa_error_wrapper); - if (err != 0) - GST_WARNING ("failed to set alsa error handler"); - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "alsa", - "ALSA plugin library", - plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/alsa/gstalsasink.c b/ext/alsa/gstalsasink.c deleted file mode 100644 index bc75ca6b..00000000 --- a/ext/alsa/gstalsasink.c +++ /dev/null @@ -1,968 +0,0 @@ -/* GStreamer - * Copyright (C) 2005 Wim Taymans <wim@fluendo.com> - * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> - * - * gstalsasink.c: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-alsasink - * @see_also: alsasrc, alsamixer - * - * This element renders raw audio samples using the ALSA api. - * - * <refsect2> - * <title>Example pipelines</title> - * |[ - * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! audioresample ! alsasink - * ]| Play an Ogg/Vorbis file. - * </refsect2> - * - * Last reviewed on 2006-03-01 (0.10.4) - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include <sys/ioctl.h> -#include <fcntl.h> -#include <errno.h> -#include <unistd.h> -#include <string.h> -#include <getopt.h> -#include <alsa/asoundlib.h> - -#include "gstalsa.h" -#include "gstalsasink.h" -#include "gstalsadeviceprobe.h" - -#include <gst/gst-i18n-plugin.h> - -/* elementfactory information */ -static const GstElementDetails gst_alsasink_details = -GST_ELEMENT_DETAILS ("Audio sink (ALSA)", - "Sink/Audio", - "Output to a sound card via ALSA", - "Wim Taymans <wim@fluendo.com>"); - -#define DEFAULT_DEVICE "default" -#define DEFAULT_DEVICE_NAME "" -#define SPDIF_PERIOD_SIZE 1536 -#define SPDIF_BUFFER_SIZE 15360 - -enum -{ - PROP_0, - PROP_DEVICE, - PROP_DEVICE_NAME -}; - -static void gst_alsasink_init_interfaces (GType type); - -GST_BOILERPLATE_FULL (GstAlsaSink, gst_alsasink, GstAudioSink, - GST_TYPE_AUDIO_SINK, gst_alsasink_init_interfaces); - -static void gst_alsasink_finalise (GObject * object); -static void gst_alsasink_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_alsasink_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); - -static GstCaps *gst_alsasink_getcaps (GstBaseSink * bsink); - -static gboolean gst_alsasink_open (GstAudioSink * asink); -static gboolean gst_alsasink_prepare (GstAudioSink * asink, - GstRingBufferSpec * spec); -static gboolean gst_alsasink_unprepare (GstAudioSink * asink); -static gboolean gst_alsasink_close (GstAudioSink * asink); -static guint gst_alsasink_write (GstAudioSink * asink, gpointer data, - guint length); -static guint gst_alsasink_delay (GstAudioSink * asink); -static void gst_alsasink_reset (GstAudioSink * asink); - -static gint output_ref; /* 0 */ -static snd_output_t *output; /* NULL */ -static GStaticMutex output_mutex = G_STATIC_MUTEX_INIT; - - -#if (G_BYTE_ORDER == G_LITTLE_ENDIAN) -# define ALSA_SINK_FACTORY_ENDIANNESS "LITTLE_ENDIAN, BIG_ENDIAN" -#else -# define ALSA_SINK_FACTORY_ENDIANNESS "BIG_ENDIAN, LITTLE_ENDIAN" -#endif - -static GstStaticPadTemplate alsasink_sink_factory = - GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-int, " - "endianness = (int) { " ALSA_SINK_FACTORY_ENDIANNESS " }, " - "signed = (boolean) { TRUE, FALSE }, " - "width = (int) 32, " - "depth = (int) 32, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; " - "audio/x-raw-int, " - "endianness = (int) { " ALSA_SINK_FACTORY_ENDIANNESS " }, " - "signed = (boolean) { TRUE, FALSE }, " - "width = (int) 24, " - "depth = (int) 24, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; " - "audio/x-raw-int, " - "endianness = (int) { " ALSA_SINK_FACTORY_ENDIANNESS " }, " - "signed = (boolean) { TRUE, FALSE }, " - "width = (int) 32, " - "depth = (int) 24, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; " - "audio/x-raw-int, " - "endianness = (int) { " ALSA_SINK_FACTORY_ENDIANNESS " }, " - "signed = (boolean) { TRUE, FALSE }, " - "width = (int) 16, " - "depth = (int) 16, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; " - "audio/x-raw-int, " - "signed = (boolean) { TRUE, FALSE }, " - "width = (int) 8, " - "depth = (int) 8, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ];" - "audio/x-iec958") - ); - -static void -gst_alsasink_finalise (GObject * object) -{ - GstAlsaSink *sink = GST_ALSA_SINK (object); - - g_free (sink->device); - g_mutex_free (sink->alsa_lock); - - g_static_mutex_lock (&output_mutex); - --output_ref; - if (output_ref == 0) { - snd_output_close (output); - output = NULL; - } - g_static_mutex_unlock (&output_mutex); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_alsasink_init_interfaces (GType type) -{ - gst_alsa_type_add_device_property_probe_interface (type); -} - -static void -gst_alsasink_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_details (element_class, &gst_alsasink_details); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&alsasink_sink_factory)); -} - -static void -gst_alsasink_class_init (GstAlsaSinkClass * klass) -{ - GObjectClass *gobject_class; - GstBaseSinkClass *gstbasesink_class; - GstAudioSinkClass *gstaudiosink_class; - - gobject_class = (GObjectClass *) klass; - gstbasesink_class = (GstBaseSinkClass *) klass; - gstaudiosink_class = (GstAudioSinkClass *) klass; - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->finalize = gst_alsasink_finalise; - gobject_class->get_property = gst_alsasink_get_property; - gobject_class->set_property = gst_alsasink_set_property; - - gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_alsasink_getcaps); - - gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_alsasink_open); - gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_alsasink_prepare); - gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_alsasink_unprepare); - gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_alsasink_close); - gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_alsasink_write); - gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_alsasink_delay); - gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_alsasink_reset); - - g_object_class_install_property (gobject_class, PROP_DEVICE, - g_param_spec_string ("device", "Device", - "ALSA device, as defined in an asound configuration file", - DEFAULT_DEVICE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_DEVICE_NAME, - g_param_spec_string ("device-name", "Device name", - "Human-readable name of the sound device", DEFAULT_DEVICE_NAME, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); -} - -static void -gst_alsasink_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstAlsaSink *sink; - - sink = GST_ALSA_SINK (object); - - switch (prop_id) { - case PROP_DEVICE: - g_free (sink->device); - sink->device = g_value_dup_string (value); - /* setting NULL restores the default device */ - if (sink->device == NULL) { - sink->device = g_strdup (DEFAULT_DEVICE); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_alsasink_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstAlsaSink *sink; - - sink = GST_ALSA_SINK (object); - - switch (prop_id) { - case PROP_DEVICE: - g_value_set_string (value, sink->device); - break; - case PROP_DEVICE_NAME: - g_value_take_string (value, - gst_alsa_find_device_name (GST_OBJECT_CAST (sink), - sink->device, sink->handle, SND_PCM_STREAM_PLAYBACK)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_alsasink_init (GstAlsaSink * alsasink, GstAlsaSinkClass * g_class) -{ - GST_DEBUG_OBJECT (alsasink, "initializing alsasink"); - - alsasink->device = g_strdup (DEFAULT_DEVICE); - alsasink->handle = NULL; - alsasink->cached_caps = NULL; - alsasink->alsa_lock = g_mutex_new (); - - g_static_mutex_lock (&output_mutex); - if (output_ref == 0) { - snd_output_stdio_attach (&output, stdout, 0); - ++output_ref; - } - g_static_mutex_unlock (&output_mutex); -} - -#define CHECK(call, error) \ -G_STMT_START { \ -if ((err = call) < 0) \ - goto error; \ -} G_STMT_END; - -static GstCaps * -gst_alsasink_getcaps (GstBaseSink * bsink) -{ - GstElementClass *element_class; - GstPadTemplate *pad_template; - GstAlsaSink *sink = GST_ALSA_SINK (bsink); - GstCaps *caps; - - if (sink->handle == NULL) { - GST_DEBUG_OBJECT (sink, "device not open, using template caps"); - return NULL; /* base class will get template caps for us */ - } - - if (sink->cached_caps) { - GST_LOG_OBJECT (sink, "Returning cached caps"); - return gst_caps_ref (sink->cached_caps); - } - - element_class = GST_ELEMENT_GET_CLASS (sink); - pad_template = gst_element_class_get_pad_template (element_class, "sink"); - g_return_val_if_fail (pad_template != NULL, NULL); - - caps = gst_alsa_probe_supported_formats (GST_OBJECT (sink), sink->handle, - gst_pad_template_get_caps (pad_template)); - - if (caps) { - sink->cached_caps = gst_caps_ref (caps); - } - - GST_INFO_OBJECT (sink, "returning caps %" GST_PTR_FORMAT, caps); - - return caps; -} - -static int -set_hwparams (GstAlsaSink * alsa) -{ - guint rrate; - gint err, dir; - snd_pcm_hw_params_t *params; - guint period_time, buffer_time; - - snd_pcm_hw_params_malloc (¶ms); - - GST_DEBUG_OBJECT (alsa, "Negotiating to %d channels @ %d Hz (format = %s) " - "SPDIF (%d)", alsa->channels, alsa->rate, - snd_pcm_format_name (alsa->format), alsa->iec958); - - /* start with requested values, if we cannot configure alsa for those values, - * we set these values to -1, which will leave the default alsa values */ - buffer_time = alsa->buffer_time; - period_time = alsa->period_time; - -retry: - /* choose all parameters */ - CHECK (snd_pcm_hw_params_any (alsa->handle, params), no_config); - /* set the interleaved read/write format */ - CHECK (snd_pcm_hw_params_set_access (alsa->handle, params, alsa->access), - wrong_access); - /* set the sample format */ - if (alsa->iec958) { - /* Try to use big endian first else fallback to le and swap bytes */ - if (snd_pcm_hw_params_set_format (alsa->handle, params, alsa->format) < 0) { - alsa->format = SND_PCM_FORMAT_S16_LE; - alsa->need_swap = TRUE; - GST_DEBUG_OBJECT (alsa, "falling back to little endian with swapping"); - } else { - alsa->need_swap = FALSE; - } - } - CHECK (snd_pcm_hw_params_set_format (alsa->handle, params, alsa->format), - no_sample_format); - /* set the count of channels */ - CHECK (snd_pcm_hw_params_set_channels (alsa->handle, params, alsa->channels), - no_channels); - /* set the stream rate */ - rrate = alsa->rate; - CHECK (snd_pcm_hw_params_set_rate_near (alsa->handle, params, &rrate, NULL), - no_rate); - if (rrate != alsa->rate) - goto rate_match; - - /* get and dump some limits */ - { - guint min, max; - - snd_pcm_hw_params_get_buffer_time_min (params, &min, &dir); - snd_pcm_hw_params_get_buffer_time_max (params, &max, &dir); - - GST_DEBUG_OBJECT (alsa, "buffer time %u, min %u, max %u", - alsa->buffer_time, min, max); - - snd_pcm_hw_params_get_period_time_min (params, &min, &dir); - snd_pcm_hw_params_get_period_time_max (params, &max, &dir); - - GST_DEBUG_OBJECT (alsa, "period time %u, min %u, max %u", - alsa->period_time, min, max); - - snd_pcm_hw_params_get_periods_min (params, &min, &dir); - snd_pcm_hw_params_get_periods_max (params, &max, &dir); - - GST_DEBUG_OBJECT (alsa, "periods min %u, max %u", min, max); - } - - /* now try to configure the buffer time and period time, if one - * of those fail, we fall back to the defaults and emit a warning. */ - if (buffer_time != -1 && !alsa->iec958) { - /* set the buffer time */ - if ((err = snd_pcm_hw_params_set_buffer_time_near (alsa->handle, params, - &buffer_time, &dir)) < 0) { - GST_ELEMENT_WARNING (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to set buffer time %i for playback: %s", - buffer_time, snd_strerror (err))); - /* disable buffer_time the next round */ - buffer_time = -1; - goto retry; - } - GST_DEBUG_OBJECT (alsa, "buffer time %u", buffer_time); - } - if (period_time != -1 && !alsa->iec958) { - /* set the period time */ - if ((err = snd_pcm_hw_params_set_period_time_near (alsa->handle, params, - &period_time, &dir)) < 0) { - GST_ELEMENT_WARNING (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to set period time %i for playback: %s", - period_time, snd_strerror (err))); - /* disable period_time the next round */ - period_time = -1; - goto retry; - } - GST_DEBUG_OBJECT (alsa, "period time %u", period_time); - } - - /* Set buffer size and period size manually for SPDIF */ - if (G_UNLIKELY (alsa->iec958)) { - snd_pcm_uframes_t buffer_size = SPDIF_BUFFER_SIZE; - snd_pcm_uframes_t period_size = SPDIF_PERIOD_SIZE; - - CHECK (snd_pcm_hw_params_set_buffer_size_near (alsa->handle, params, - &buffer_size), buffer_size); - CHECK (snd_pcm_hw_params_set_period_size_near (alsa->handle, params, - &period_size, NULL), period_size); - } - - /* write the parameters to device */ - CHECK (snd_pcm_hw_params (alsa->handle, params), set_hw_params); - - /* now get the configured values */ - CHECK (snd_pcm_hw_params_get_buffer_size (params, &alsa->buffer_size), - buffer_size); - CHECK (snd_pcm_hw_params_get_period_size (params, &alsa->period_size, &dir), - period_size); - - GST_DEBUG_OBJECT (alsa, "buffer size %lu, period size %lu", alsa->buffer_size, - alsa->period_size); - - snd_pcm_hw_params_free (params); - return 0; - - /* ERRORS */ -no_config: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Broken configuration for playback: no configurations available: %s", - snd_strerror (err))); - snd_pcm_hw_params_free (params); - return err; - } -wrong_access: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Access type not available for playback: %s", snd_strerror (err))); - snd_pcm_hw_params_free (params); - return err; - } -no_sample_format: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Sample format not available for playback: %s", snd_strerror (err))); - snd_pcm_hw_params_free (params); - return err; - } -no_channels: - { - gchar *msg = NULL; - - if ((alsa->channels) == 1) - msg = g_strdup (_("Could not open device for playback in mono mode.")); - if ((alsa->channels) == 2) - msg = g_strdup (_("Could not open device for playback in stereo mode.")); - if ((alsa->channels) > 2) - msg = - g_strdup_printf (_ - ("Could not open device for playback in %d-channel mode."), - alsa->channels); - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (msg), (snd_strerror (err))); - g_free (msg); - snd_pcm_hw_params_free (params); - return err; - } -no_rate: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Rate %iHz not available for playback: %s", - alsa->rate, snd_strerror (err))); - return err; - } -rate_match: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Rate doesn't match (requested %iHz, get %iHz)", alsa->rate, err)); - snd_pcm_hw_params_free (params); - return -EINVAL; - } -buffer_size: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to get buffer size for playback: %s", snd_strerror (err))); - snd_pcm_hw_params_free (params); - return err; - } -period_size: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to get period size for playback: %s", snd_strerror (err))); - snd_pcm_hw_params_free (params); - return err; - } -set_hw_params: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to set hw params for playback: %s", snd_strerror (err))); - snd_pcm_hw_params_free (params); - return err; - } -} - -static int -set_swparams (GstAlsaSink * alsa) -{ - int err; - snd_pcm_sw_params_t *params; - - snd_pcm_sw_params_malloc (¶ms); - - /* get the current swparams */ - CHECK (snd_pcm_sw_params_current (alsa->handle, params), no_config); - /* start the transfer when the buffer is almost full: */ - /* (buffer_size / avail_min) * avail_min */ - CHECK (snd_pcm_sw_params_set_start_threshold (alsa->handle, params, - (alsa->buffer_size / alsa->period_size) * alsa->period_size), - start_threshold); - - /* allow the transfer when at least period_size samples can be processed */ - CHECK (snd_pcm_sw_params_set_avail_min (alsa->handle, params, - alsa->period_size), set_avail); - -#if GST_CHECK_ALSA_VERSION(1,0,16) - /* snd_pcm_sw_params_set_xfer_align() is deprecated, alignment is always 1 */ -#else - /* align all transfers to 1 sample */ - CHECK (snd_pcm_sw_params_set_xfer_align (alsa->handle, params, 1), set_align); -#endif - - /* write the parameters to the playback device */ - CHECK (snd_pcm_sw_params (alsa->handle, params), set_sw_params); - - snd_pcm_sw_params_free (params); - return 0; - - /* ERRORS */ -no_config: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to determine current swparams for playback: %s", - snd_strerror (err))); - snd_pcm_sw_params_free (params); - return err; - } -start_threshold: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to set start threshold mode for playback: %s", - snd_strerror (err))); - snd_pcm_sw_params_free (params); - return err; - } -set_avail: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to set avail min for playback: %s", snd_strerror (err))); - snd_pcm_sw_params_free (params); - return err; - } -#if !GST_CHECK_ALSA_VERSION(1,0,16) -set_align: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to set transfer align for playback: %s", snd_strerror (err))); - snd_pcm_sw_params_free (params); - return err; - } -#endif -set_sw_params: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to set sw params for playback: %s", snd_strerror (err))); - snd_pcm_sw_params_free (params); - return err; - } -} - -static gboolean -alsasink_parse_spec (GstAlsaSink * alsa, GstRingBufferSpec * spec) -{ - /* Initialize our boolean */ - alsa->iec958 = FALSE; - - switch (spec->type) { - case GST_BUFTYPE_LINEAR: - GST_DEBUG_OBJECT (alsa, - "Linear format : depth=%d, width=%d, sign=%d, bigend=%d", spec->depth, - spec->width, spec->sign, spec->bigend); - - alsa->format = snd_pcm_build_linear_format (spec->depth, spec->width, - spec->sign ? 0 : 1, spec->bigend ? 1 : 0); - break; - case GST_BUFTYPE_FLOAT: - switch (spec->format) { - case GST_FLOAT32_LE: - alsa->format = SND_PCM_FORMAT_FLOAT_LE; - break; - case GST_FLOAT32_BE: - alsa->format = SND_PCM_FORMAT_FLOAT_BE; - break; - case GST_FLOAT64_LE: - alsa->format = SND_PCM_FORMAT_FLOAT64_LE; - break; - case GST_FLOAT64_BE: - alsa->format = SND_PCM_FORMAT_FLOAT64_BE; - break; - default: - goto error; - } - break; - case GST_BUFTYPE_A_LAW: - alsa->format = SND_PCM_FORMAT_A_LAW; - break; - case GST_BUFTYPE_MU_LAW: - alsa->format = SND_PCM_FORMAT_MU_LAW; - break; - case GST_BUFTYPE_IEC958: - alsa->format = SND_PCM_FORMAT_S16_BE; - alsa->iec958 = TRUE; - break; - default: - goto error; - - } - alsa->rate = spec->rate; - alsa->channels = spec->channels; - alsa->buffer_time = spec->buffer_time; - alsa->period_time = spec->latency_time; - alsa->access = SND_PCM_ACCESS_RW_INTERLEAVED; - - return TRUE; - - /* ERRORS */ -error: - { - return FALSE; - } -} - -static gboolean -gst_alsasink_open (GstAudioSink * asink) -{ - GstAlsaSink *alsa; - gint err; - - alsa = GST_ALSA_SINK (asink); - - /* open in non-blocking mode, we'll use snd_pcm_wait() for space to become - * available. */ - CHECK (snd_pcm_open (&alsa->handle, alsa->device, SND_PCM_STREAM_PLAYBACK, - SND_PCM_NONBLOCK), open_error); - GST_LOG_OBJECT (alsa, "Opened device %s", alsa->device); - - return TRUE; - - /* ERRORS */ -open_error: - { - if (err == -EBUSY) { - GST_ELEMENT_ERROR (alsa, RESOURCE, BUSY, - (_("Could not open audio device for playback. " - "Device is being used by another application.")), - ("Device '%s' is busy", alsa->device)); - } else { - GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_WRITE, - (_("Could not open audio device for playback.")), - ("Playback open error on device '%s': %s", alsa->device, - snd_strerror (err))); - } - return FALSE; - } -} - -static gboolean -gst_alsasink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec) -{ - GstAlsaSink *alsa; - gint err; - - alsa = GST_ALSA_SINK (asink); - - if (spec->format == GST_IEC958) { - snd_pcm_close (alsa->handle); - alsa->handle = gst_alsa_open_iec958_pcm (GST_OBJECT (alsa)); - if (G_UNLIKELY (!alsa->handle)) { - goto no_iec958; - } - } - - if (!alsasink_parse_spec (alsa, spec)) - goto spec_parse; - - CHECK (set_hwparams (alsa), hw_params_failed); - CHECK (set_swparams (alsa), sw_params_failed); - - alsa->bytes_per_sample = spec->bytes_per_sample; - spec->segsize = alsa->period_size * spec->bytes_per_sample; - spec->segtotal = alsa->buffer_size / alsa->period_size; - - { - snd_output_t *out_buf = NULL; - char *msg = NULL; - - snd_output_buffer_open (&out_buf); - snd_pcm_dump_hw_setup (alsa->handle, out_buf); - snd_output_buffer_string (out_buf, &msg); - GST_DEBUG_OBJECT (alsa, "Hardware setup: \n%s", msg); - snd_output_close (out_buf); - snd_output_buffer_open (&out_buf); - snd_pcm_dump_sw_setup (alsa->handle, out_buf); - snd_output_buffer_string (out_buf, &msg); - GST_DEBUG_OBJECT (alsa, "Software setup: \n%s", msg); - snd_output_close (out_buf); - } - - return TRUE; - - /* ERRORS */ -no_iec958: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_WRITE, (NULL), - ("Could not open IEC958 (SPDIF) device for playback")); - return FALSE; - } -spec_parse: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Error parsing spec")); - return FALSE; - } -hw_params_failed: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Setting of hwparams failed: %s", snd_strerror (err))); - return FALSE; - } -sw_params_failed: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Setting of swparams failed: %s", snd_strerror (err))); - return FALSE; - } -} - -static gboolean -gst_alsasink_unprepare (GstAudioSink * asink) -{ - GstAlsaSink *alsa; - gint err; - - alsa = GST_ALSA_SINK (asink); - - CHECK (snd_pcm_drop (alsa->handle), drop); - - CHECK (snd_pcm_hw_free (alsa->handle), hw_free); - - return TRUE; - - /* ERRORS */ -drop: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Could not drop samples: %s", snd_strerror (err))); - return FALSE; - } -hw_free: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Could not free hw params: %s", snd_strerror (err))); - return FALSE; - } -} - -static gboolean -gst_alsasink_close (GstAudioSink * asink) -{ - GstAlsaSink *alsa = GST_ALSA_SINK (asink); - gint err; - - if (alsa->handle) { - CHECK (snd_pcm_close (alsa->handle), close_error); - alsa->handle = NULL; - } - gst_caps_replace (&alsa->cached_caps, NULL); - - return TRUE; - - /* ERRORS */ -close_error: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, CLOSE, (NULL), - ("Playback close error: %s", snd_strerror (err))); - return FALSE; - } -} - - -/* - * Underrun and suspend recovery - */ -static gint -xrun_recovery (GstAlsaSink * alsa, snd_pcm_t * handle, gint err) -{ - GST_DEBUG_OBJECT (alsa, "xrun recovery %d", err); - - if (err == -EPIPE) { /* under-run */ - err = snd_pcm_prepare (handle); - if (err < 0) - GST_WARNING_OBJECT (alsa, - "Can't recovery from underrun, prepare failed: %s", - snd_strerror (err)); - return 0; - } else if (err == -ESTRPIPE) { - while ((err = snd_pcm_resume (handle)) == -EAGAIN) - g_usleep (100); /* wait until the suspend flag is released */ - - if (err < 0) { - err = snd_pcm_prepare (handle); - if (err < 0) - GST_WARNING_OBJECT (alsa, - "Can't recovery from suspend, prepare failed: %s", - snd_strerror (err)); - } - return 0; - } - return err; -} - -static guint -gst_alsasink_write (GstAudioSink * asink, gpointer data, guint length) -{ - GstAlsaSink *alsa; - gint err; - gint cptr; - gint16 *ptr = data; - - alsa = GST_ALSA_SINK (asink); - - if (alsa->iec958 && alsa->need_swap) { - guint i; - - GST_DEBUG_OBJECT (asink, "swapping bytes"); - for (i = 0; i < length / 2; i++) { - ptr[i] = GUINT16_SWAP_LE_BE (ptr[i]); - } - } - - GST_LOG_OBJECT (asink, "received audio samples buffer of %u bytes", length); - - cptr = length / alsa->bytes_per_sample; - - GST_ALSA_SINK_LOCK (asink); - while (cptr > 0) { - /* start by doing a blocking wait for free space. Set the timeout - * to 4 times the period time */ - err = snd_pcm_wait (alsa->handle, (4 * alsa->period_time / 1000)); - if (err < 0) { - GST_DEBUG_OBJECT (asink, "wait error, %d", err); - } else { - err = snd_pcm_writei (alsa->handle, ptr, cptr); - } - - GST_DEBUG_OBJECT (asink, "written %d frames out of %d", err, cptr); - if (err < 0) { - GST_DEBUG_OBJECT (asink, "Write error: %s", snd_strerror (err)); - if (err == -EAGAIN) { - continue; - } else if (xrun_recovery (alsa, alsa->handle, err) < 0) { - goto write_error; - } - continue; - } - - ptr += snd_pcm_frames_to_bytes (alsa->handle, err); - cptr -= err; - } - GST_ALSA_SINK_UNLOCK (asink); - - return length - (cptr * alsa->bytes_per_sample); - -write_error: - { - GST_ALSA_SINK_UNLOCK (asink); - return length; /* skip one period */ - } -} - -static guint -gst_alsasink_delay (GstAudioSink * asink) -{ - GstAlsaSink *alsa; - snd_pcm_sframes_t delay; - int res; - - alsa = GST_ALSA_SINK (asink); - - res = snd_pcm_delay (alsa->handle, &delay); - if (G_UNLIKELY (res < 0)) { - /* on errors, report 0 delay */ - GST_DEBUG_OBJECT (alsa, "snd_pcm_delay returned %d", res); - delay = 0; - } - if (G_UNLIKELY (delay < 0)) { - /* make sure we never return a negative delay */ - GST_WARNING_OBJECT (alsa, "snd_pcm_delay returned negative delay"); - delay = 0; - } - - return delay; -} - -static void -gst_alsasink_reset (GstAudioSink * asink) -{ - GstAlsaSink *alsa; - gint err; - - alsa = GST_ALSA_SINK (asink); - - GST_ALSA_SINK_LOCK (asink); - GST_DEBUG_OBJECT (alsa, "drop"); - CHECK (snd_pcm_drop (alsa->handle), drop_error); - GST_DEBUG_OBJECT (alsa, "prepare"); - CHECK (snd_pcm_prepare (alsa->handle), prepare_error); - GST_DEBUG_OBJECT (alsa, "reset done"); - GST_ALSA_SINK_UNLOCK (asink); - - return; - - /* ERRORS */ -drop_error: - { - GST_ERROR_OBJECT (alsa, "alsa-reset: pcm drop error: %s", - snd_strerror (err)); - GST_ALSA_SINK_UNLOCK (asink); - return; - } -prepare_error: - { - GST_ERROR_OBJECT (alsa, "alsa-reset: pcm prepare error: %s", - snd_strerror (err)); - GST_ALSA_SINK_UNLOCK (asink); - return; - } -} diff --git a/ext/alsa/gstalsasink.h b/ext/alsa/gstalsasink.h deleted file mode 100644 index 902dbf77..00000000 --- a/ext/alsa/gstalsasink.h +++ /dev/null @@ -1,86 +0,0 @@ -/* GStreamer - * Copyright (C) 2005 Wim Taymans <wim@fluendo.com> - * - * gstalsasink.h: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_ALSASINK_H__ -#define __GST_ALSASINK_H__ - -#include <gst/gst.h> -#include <gst/audio/gstaudiosink.h> -#include <alsa/asoundlib.h> - -G_BEGIN_DECLS - -#define GST_TYPE_ALSA_SINK (gst_alsasink_get_type()) -#define GST_ALSA_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALSA_SINK,GstAlsaSink)) -#define GST_ALSA_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALSA_SINK,GstAlsaSinkClass)) -#define GST_IS_ALSA_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALSA_SINK)) -#define GST_IS_ALSA_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALSA_SINK)) -#define GST_ALSA_SINK_CAST(obj) ((GstAlsaSink *) (obj)) - -typedef struct _GstAlsaSink GstAlsaSink; -typedef struct _GstAlsaSinkClass GstAlsaSinkClass; - -#define GST_ALSA_SINK_GET_LOCK(obj) (GST_ALSA_SINK_CAST (obj)->alsa_lock) -#define GST_ALSA_SINK_LOCK(obj) (g_mutex_lock (GST_ALSA_SINK_GET_LOCK (obj))) -#define GST_ALSA_SINK_UNLOCK(obj) (g_mutex_unlock (GST_ALSA_SINK_GET_LOCK (obj))) - -/** - * GstAlsaSink: - * - * Opaque data structure - */ -struct _GstAlsaSink { - GstAudioSink sink; - - gchar *device; - - snd_pcm_t *handle; - snd_pcm_hw_params_t *hwparams; - snd_pcm_sw_params_t *swparams; - - snd_pcm_access_t access; - snd_pcm_format_t format; - guint rate; - guint channels; - gint bytes_per_sample; - gboolean iec958; - gboolean need_swap; - - guint buffer_time; - guint period_time; - snd_pcm_uframes_t buffer_size; - snd_pcm_uframes_t period_size; - - GstCaps *cached_caps; - - GMutex *alsa_lock; -}; - -struct _GstAlsaSinkClass { - GstAudioSinkClass parent_class; -}; - -GType gst_alsasink_get_type(void); - -G_END_DECLS - -#endif /* __GST_ALSASINK_H__ */ diff --git a/ext/alsa/gstalsasrc.c b/ext/alsa/gstalsasrc.c deleted file mode 100644 index db9c3a60..00000000 --- a/ext/alsa/gstalsasrc.c +++ /dev/null @@ -1,873 +0,0 @@ -/* GStreamer - * Copyright (C) 2005 Wim Taymans <wim@fluendo.com> - * - * gstalsasrc.c: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-alsasrc - * @see_also: alsasink, alsamixer - * - * This element reads data from an audio card using the ALSA API. - * - * <refsect2> - * <title>Example pipelines</title> - * |[ - * gst-launch -v alsasrc ! audioconvert ! vorbisenc ! oggmux ! filesink location=alsasrc.ogg - * ]| Record from a sound card using ALSA and encode to Ogg/Vorbis. - * </refsect2> - * - * Last reviewed on 2006-03-01 (0.10.4) - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include <sys/ioctl.h> -#include <fcntl.h> -#include <errno.h> -#include <unistd.h> -#include <string.h> -#include <getopt.h> -#include <alsa/asoundlib.h> - -#include "gstalsasrc.h" -#include "gstalsadeviceprobe.h" - -#include <gst/gst-i18n-plugin.h> - -/* elementfactory information */ -static const GstElementDetails gst_alsasrc_details = -GST_ELEMENT_DETAILS ("Audio source (ALSA)", - "Source/Audio", - "Read from a sound card via ALSA", - "Wim Taymans <wim@fluendo.com>"); - -#define DEFAULT_PROP_DEVICE "default" -#define DEFAULT_PROP_DEVICE_NAME "" - -enum -{ - PROP_0, - PROP_DEVICE, - PROP_DEVICE_NAME, -}; - -static void gst_alsasrc_init_interfaces (GType type); - -GST_BOILERPLATE_FULL (GstAlsaSrc, gst_alsasrc, GstAudioSrc, - GST_TYPE_AUDIO_SRC, gst_alsasrc_init_interfaces); - -GST_IMPLEMENT_ALSA_MIXER_METHODS (GstAlsaSrc, gst_alsasrc_mixer); - -static void gst_alsasrc_finalize (GObject * object); -static void gst_alsasrc_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_alsasrc_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); - -static GstCaps *gst_alsasrc_getcaps (GstBaseSrc * bsrc); - -static gboolean gst_alsasrc_open (GstAudioSrc * asrc); -static gboolean gst_alsasrc_prepare (GstAudioSrc * asrc, - GstRingBufferSpec * spec); -static gboolean gst_alsasrc_unprepare (GstAudioSrc * asrc); -static gboolean gst_alsasrc_close (GstAudioSrc * asrc); -static guint gst_alsasrc_read (GstAudioSrc * asrc, gpointer data, guint length); -static guint gst_alsasrc_delay (GstAudioSrc * asrc); -static void gst_alsasrc_reset (GstAudioSrc * asrc); - -/* AlsaSrc signals and args */ -enum -{ - LAST_SIGNAL -}; - -#if (G_BYTE_ORDER == G_LITTLE_ENDIAN) -# define ALSA_SRC_FACTORY_ENDIANNESS "LITTLE_ENDIAN, BIG_ENDIAN" -#else -# define ALSA_SRC_FACTORY_ENDIANNESS "BIG_ENDIAN, LITTLE_ENDIAN" -#endif - -static GstStaticPadTemplate alsasrc_src_factory = - GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-int, " - "endianness = (int) { " ALSA_SRC_FACTORY_ENDIANNESS " }, " - "signed = (boolean) { TRUE, FALSE }, " - "width = (int) 32, " - "depth = (int) 32, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; " - "audio/x-raw-int, " - "endianness = (int) { " ALSA_SRC_FACTORY_ENDIANNESS " }, " - "signed = (boolean) { TRUE, FALSE }, " - "width = (int) 32, " - "depth = (int) 24, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; " - "audio/x-raw-int, " - "endianness = (int) { " ALSA_SRC_FACTORY_ENDIANNESS " }, " - "signed = (boolean) { TRUE, FALSE }, " - "width = (int) 24, " - "depth = (int) 24, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; " - "audio/x-raw-int, " - "endianness = (int) { " ALSA_SRC_FACTORY_ENDIANNESS " }, " - "signed = (boolean) { TRUE, FALSE }, " - "width = (int) 16, " - "depth = (int) 16, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; " - "audio/x-raw-int, " - "signed = (boolean) { TRUE, FALSE }, " - "width = (int) 8, " - "depth = (int) 8, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]") - ); - -static void -gst_alsasrc_finalize (GObject * object) -{ - GstAlsaSrc *src = GST_ALSA_SRC (object); - - g_free (src->device); - g_mutex_free (src->alsa_lock); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static gboolean -gst_alsasrc_interface_supported (GstAlsaSrc * this, GType interface_type) -{ - /* only support this one interface (wrapped by GstImplementsInterface) */ - g_assert (interface_type == GST_TYPE_MIXER); - - return gst_alsasrc_mixer_supported (this, interface_type); -} - -static void -gst_implements_interface_init (GstImplementsInterfaceClass * klass) -{ - klass->supported = (gpointer) gst_alsasrc_interface_supported; -} - -static void -gst_alsasrc_init_interfaces (GType type) -{ - static const GInterfaceInfo implements_iface_info = { - (GInterfaceInitFunc) gst_implements_interface_init, - NULL, - NULL, - }; - static const GInterfaceInfo mixer_iface_info = { - (GInterfaceInitFunc) gst_alsasrc_mixer_interface_init, - NULL, - NULL, - }; - - g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, - &implements_iface_info); - g_type_add_interface_static (type, GST_TYPE_MIXER, &mixer_iface_info); - - gst_alsa_type_add_device_property_probe_interface (type); -} - -static void -gst_alsasrc_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_details (element_class, &gst_alsasrc_details); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&alsasrc_src_factory)); -} - -static void -gst_alsasrc_class_init (GstAlsaSrcClass * klass) -{ - GObjectClass *gobject_class; - GstBaseSrcClass *gstbasesrc_class; - GstAudioSrcClass *gstaudiosrc_class; - - gobject_class = (GObjectClass *) klass; - gstbasesrc_class = (GstBaseSrcClass *) klass; - gstaudiosrc_class = (GstAudioSrcClass *) klass; - - gobject_class->finalize = gst_alsasrc_finalize; - gobject_class->get_property = gst_alsasrc_get_property; - gobject_class->set_property = gst_alsasrc_set_property; - - gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_alsasrc_getcaps); - - gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_alsasrc_open); - gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_alsasrc_prepare); - gstaudiosrc_class->unprepare = GST_DEBUG_FUNCPTR (gst_alsasrc_unprepare); - gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_alsasrc_close); - gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_alsasrc_read); - gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_alsasrc_delay); - gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_alsasrc_reset); - - g_object_class_install_property (gobject_class, PROP_DEVICE, - g_param_spec_string ("device", "Device", - "ALSA device, as defined in an asound configuration file", - DEFAULT_PROP_DEVICE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_DEVICE_NAME, - g_param_spec_string ("device-name", "Device name", - "Human-readable name of the sound device", - DEFAULT_PROP_DEVICE_NAME, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); -} - -static void -gst_alsasrc_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstAlsaSrc *src; - - src = GST_ALSA_SRC (object); - - switch (prop_id) { - case PROP_DEVICE: - g_free (src->device); - src->device = g_value_dup_string (value); - if (src->device == NULL) { - src->device = g_strdup (DEFAULT_PROP_DEVICE); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_alsasrc_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstAlsaSrc *src; - - src = GST_ALSA_SRC (object); - - switch (prop_id) { - case PROP_DEVICE: - g_value_set_string (value, src->device); - break; - case PROP_DEVICE_NAME: - g_value_take_string (value, - gst_alsa_find_device_name (GST_OBJECT_CAST (src), - src->device, src->handle, SND_PCM_STREAM_CAPTURE)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_alsasrc_init (GstAlsaSrc * alsasrc, GstAlsaSrcClass * g_class) -{ - GST_DEBUG_OBJECT (alsasrc, "initializing"); - - alsasrc->device = g_strdup (DEFAULT_PROP_DEVICE); - alsasrc->cached_caps = NULL; - - alsasrc->alsa_lock = g_mutex_new (); -} - -#define CHECK(call, error) \ -G_STMT_START { \ -if ((err = call) < 0) \ - goto error; \ -} G_STMT_END; - - -static GstCaps * -gst_alsasrc_getcaps (GstBaseSrc * bsrc) -{ - GstElementClass *element_class; - GstPadTemplate *pad_template; - GstAlsaSrc *src; - GstCaps *caps; - - src = GST_ALSA_SRC (bsrc); - - if (src->handle == NULL) { - GST_DEBUG_OBJECT (src, "device not open, using template caps"); - return NULL; /* base class will get template caps for us */ - } - - if (src->cached_caps) { - GST_LOG_OBJECT (src, "Returning cached caps"); - return gst_caps_ref (src->cached_caps); - } - - element_class = GST_ELEMENT_GET_CLASS (src); - pad_template = gst_element_class_get_pad_template (element_class, "src"); - g_return_val_if_fail (pad_template != NULL, NULL); - - caps = gst_alsa_probe_supported_formats (GST_OBJECT (src), src->handle, - gst_pad_template_get_caps (pad_template)); - - if (caps) { - src->cached_caps = gst_caps_ref (caps); - } - - GST_INFO_OBJECT (src, "returning caps %" GST_PTR_FORMAT, caps); - - return caps; -} - -static int -set_hwparams (GstAlsaSrc * alsa) -{ - guint rrate; - gint err, dir; - snd_pcm_hw_params_t *params; - - snd_pcm_hw_params_malloc (¶ms); - - /* choose all parameters */ - CHECK (snd_pcm_hw_params_any (alsa->handle, params), no_config); - /* set the interleaved read/write format */ - CHECK (snd_pcm_hw_params_set_access (alsa->handle, params, alsa->access), - wrong_access); - /* set the sample format */ - CHECK (snd_pcm_hw_params_set_format (alsa->handle, params, alsa->format), - no_sample_format); - /* set the count of channels */ - CHECK (snd_pcm_hw_params_set_channels (alsa->handle, params, alsa->channels), - no_channels); - /* set the stream rate */ - rrate = alsa->rate; - CHECK (snd_pcm_hw_params_set_rate_near (alsa->handle, params, &rrate, NULL), - no_rate); - if (rrate != alsa->rate) - goto rate_match; - - if (alsa->buffer_time != -1) { - /* set the buffer time */ - CHECK (snd_pcm_hw_params_set_buffer_time_near (alsa->handle, params, - &alsa->buffer_time, &dir), buffer_time); - } - if (alsa->period_time != -1) { - /* set the period time */ - CHECK (snd_pcm_hw_params_set_period_time_near (alsa->handle, params, - &alsa->period_time, &dir), period_time); - } - - /* write the parameters to device */ - CHECK (snd_pcm_hw_params (alsa->handle, params), set_hw_params); - - CHECK (snd_pcm_hw_params_get_buffer_size (params, &alsa->buffer_size), - buffer_size); - - CHECK (snd_pcm_hw_params_get_period_size (params, &alsa->period_size, &dir), - period_size); - - snd_pcm_hw_params_free (params); - return 0; - - /* ERRORS */ -no_config: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Broken configuration for recording: no configurations available: %s", - snd_strerror (err))); - snd_pcm_hw_params_free (params); - return err; - } -wrong_access: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Access type not available for recording: %s", snd_strerror (err))); - snd_pcm_hw_params_free (params); - return err; - } -no_sample_format: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Sample format not available for recording: %s", snd_strerror (err))); - snd_pcm_hw_params_free (params); - return err; - } -no_channels: - { - gchar *msg = NULL; - - if ((alsa->channels) == 1) - msg = g_strdup (_("Could not open device for recording in mono mode.")); - if ((alsa->channels) == 2) - msg = g_strdup (_("Could not open device for recording in stereo mode.")); - if ((alsa->channels) > 2) - msg = - g_strdup_printf (_ - ("Could not open device for recording in %d-channel mode"), - alsa->channels); - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (msg), (snd_strerror (err))); - g_free (msg); - snd_pcm_hw_params_free (params); - return err; - } -no_rate: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Rate %iHz not available for recording: %s", - alsa->rate, snd_strerror (err))); - snd_pcm_hw_params_free (params); - return err; - } -rate_match: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Rate doesn't match (requested %iHz, get %iHz)", alsa->rate, err)); - snd_pcm_hw_params_free (params); - return -EINVAL; - } -buffer_time: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to set buffer time %i for recording: %s", - alsa->buffer_time, snd_strerror (err))); - snd_pcm_hw_params_free (params); - return err; - } -buffer_size: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to get buffer size for recording: %s", snd_strerror (err))); - snd_pcm_hw_params_free (params); - return err; - } -period_time: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to set period time %i for recording: %s", alsa->period_time, - snd_strerror (err))); - snd_pcm_hw_params_free (params); - return err; - } -period_size: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to get period size for recording: %s", snd_strerror (err))); - snd_pcm_hw_params_free (params); - return err; - } -set_hw_params: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to set hw params for recording: %s", snd_strerror (err))); - snd_pcm_hw_params_free (params); - return err; - } -} - -static int -set_swparams (GstAlsaSrc * alsa) -{ - int err; - snd_pcm_sw_params_t *params; - - snd_pcm_sw_params_malloc (¶ms); - - /* get the current swparams */ - CHECK (snd_pcm_sw_params_current (alsa->handle, params), no_config); - /* allow the transfer when at least period_size samples can be processed */ - CHECK (snd_pcm_sw_params_set_avail_min (alsa->handle, params, - alsa->period_size), set_avail); - /* start the transfer on first read */ - CHECK (snd_pcm_sw_params_set_start_threshold (alsa->handle, params, - 0), start_threshold); - -#if GST_CHECK_ALSA_VERSION(1,0,16) - /* snd_pcm_sw_params_set_xfer_align() is deprecated, alignment is always 1 */ -#else - /* align all transfers to 1 sample */ - CHECK (snd_pcm_sw_params_set_xfer_align (alsa->handle, params, 1), set_align); -#endif - - /* write the parameters to the recording device */ - CHECK (snd_pcm_sw_params (alsa->handle, params), set_sw_params); - - snd_pcm_sw_params_free (params); - return 0; - - /* ERRORS */ -no_config: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to determine current swparams for playback: %s", - snd_strerror (err))); - snd_pcm_sw_params_free (params); - return err; - } -start_threshold: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to set start threshold mode for playback: %s", - snd_strerror (err))); - snd_pcm_sw_params_free (params); - return err; - } -set_avail: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to set avail min for playback: %s", snd_strerror (err))); - snd_pcm_sw_params_free (params); - return err; - } -#if !GST_CHECK_ALSA_VERSION(1,0,16) -set_align: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to set transfer align for playback: %s", snd_strerror (err))); - snd_pcm_sw_params_free (params); - return err; - } -#endif -set_sw_params: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Unable to set sw params for playback: %s", snd_strerror (err))); - snd_pcm_sw_params_free (params); - return err; - } -} - -static gboolean -alsasrc_parse_spec (GstAlsaSrc * alsa, GstRingBufferSpec * spec) -{ - switch (spec->type) { - case GST_BUFTYPE_LINEAR: - alsa->format = snd_pcm_build_linear_format (spec->depth, spec->width, - spec->sign ? 0 : 1, spec->bigend ? 1 : 0); - break; - case GST_BUFTYPE_FLOAT: - switch (spec->format) { - case GST_FLOAT32_LE: - alsa->format = SND_PCM_FORMAT_FLOAT_LE; - break; - case GST_FLOAT32_BE: - alsa->format = SND_PCM_FORMAT_FLOAT_BE; - break; - case GST_FLOAT64_LE: - alsa->format = SND_PCM_FORMAT_FLOAT64_LE; - break; - case GST_FLOAT64_BE: - alsa->format = SND_PCM_FORMAT_FLOAT64_BE; - break; - default: - goto error; - } - break; - case GST_BUFTYPE_A_LAW: - alsa->format = SND_PCM_FORMAT_A_LAW; - break; - case GST_BUFTYPE_MU_LAW: - alsa->format = SND_PCM_FORMAT_MU_LAW; - break; - default: - goto error; - - } - alsa->rate = spec->rate; - alsa->channels = spec->channels; - alsa->buffer_time = spec->buffer_time; - alsa->period_time = spec->latency_time; - alsa->access = SND_PCM_ACCESS_RW_INTERLEAVED; - - return TRUE; - - /* ERRORS */ -error: - { - return FALSE; - } -} - -static gboolean -gst_alsasrc_open (GstAudioSrc * asrc) -{ - GstAlsaSrc *alsa; - gint err; - - alsa = GST_ALSA_SRC (asrc); - - CHECK (snd_pcm_open (&alsa->handle, alsa->device, SND_PCM_STREAM_CAPTURE, - SND_PCM_NONBLOCK), open_error); - - if (!alsa->mixer) - alsa->mixer = gst_alsa_mixer_new (alsa->device, GST_ALSA_MIXER_CAPTURE); - - return TRUE; - - /* ERRORS */ -open_error: - { - if (err == -EBUSY) { - GST_ELEMENT_ERROR (alsa, RESOURCE, BUSY, - (_("Could not open audio device for recording. " - "Device is being used by another application.")), - ("Device '%s' is busy", alsa->device)); - } else { - GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, - (_("Could not open audio device for recording.")), - ("Recording open error on device '%s': %s", alsa->device, - snd_strerror (err))); - } - return FALSE; - } -} - -static gboolean -gst_alsasrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec) -{ - GstAlsaSrc *alsa; - gint err; - - alsa = GST_ALSA_SRC (asrc); - - if (!alsasrc_parse_spec (alsa, spec)) - goto spec_parse; - - CHECK (snd_pcm_nonblock (alsa->handle, 0), non_block); - - CHECK (set_hwparams (alsa), hw_params_failed); - CHECK (set_swparams (alsa), sw_params_failed); - CHECK (snd_pcm_prepare (alsa->handle), prepare_failed); - - alsa->bytes_per_sample = spec->bytes_per_sample; - spec->segsize = alsa->period_size * spec->bytes_per_sample; - spec->segtotal = alsa->buffer_size / alsa->period_size; - spec->silence_sample[0] = 0; - spec->silence_sample[1] = 0; - spec->silence_sample[2] = 0; - spec->silence_sample[3] = 0; - - return TRUE; - - /* ERRORS */ -spec_parse: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Error parsing spec")); - return FALSE; - } -non_block: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Could not set device to blocking: %s", snd_strerror (err))); - return FALSE; - } -hw_params_failed: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Setting of hwparams failed: %s", snd_strerror (err))); - return FALSE; - } -sw_params_failed: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Setting of swparams failed: %s", snd_strerror (err))); - return FALSE; - } -prepare_failed: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Prepare failed: %s", snd_strerror (err))); - return FALSE; - } -} - -static gboolean -gst_alsasrc_unprepare (GstAudioSrc * asrc) -{ - GstAlsaSrc *alsa; - gint err; - - alsa = GST_ALSA_SRC (asrc); - - CHECK (snd_pcm_drop (alsa->handle), drop); - - CHECK (snd_pcm_hw_free (alsa->handle), hw_free); - - CHECK (snd_pcm_nonblock (alsa->handle, 1), non_block); - - return TRUE; - - /* ERRORS */ -drop: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Could not drop samples: %s", snd_strerror (err))); - return FALSE; - } -hw_free: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Could not free hw params: %s", snd_strerror (err))); - return FALSE; - } -non_block: - { - GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), - ("Could not set device to nonblocking: %s", snd_strerror (err))); - return FALSE; - } -} - -static gboolean -gst_alsasrc_close (GstAudioSrc * asrc) -{ - GstAlsaSrc *alsa = GST_ALSA_SRC (asrc); - - snd_pcm_close (alsa->handle); - alsa->handle = NULL; - - if (alsa->mixer) { - gst_alsa_mixer_free (alsa->mixer); - alsa->mixer = NULL; - } - - gst_caps_replace (&alsa->cached_caps, NULL); - - return TRUE; -} - -/* - * Underrun and suspend recovery - */ -static gint -xrun_recovery (GstAlsaSrc * alsa, snd_pcm_t * handle, gint err) -{ - GST_DEBUG_OBJECT (alsa, "xrun recovery %d", err); - - if (err == -EPIPE) { /* under-run */ - err = snd_pcm_prepare (handle); - if (err < 0) - GST_WARNING_OBJECT (alsa, - "Can't recovery from underrun, prepare failed: %s", - snd_strerror (err)); - return 0; - } else if (err == -ESTRPIPE) { - while ((err = snd_pcm_resume (handle)) == -EAGAIN) - g_usleep (100); /* wait until the suspend flag is released */ - - if (err < 0) { - err = snd_pcm_prepare (handle); - if (err < 0) - GST_WARNING_OBJECT (alsa, - "Can't recovery from suspend, prepare failed: %s", - snd_strerror (err)); - } - return 0; - } - return err; -} - -static guint -gst_alsasrc_read (GstAudioSrc * asrc, gpointer data, guint length) -{ - GstAlsaSrc *alsa; - gint err; - gint cptr; - gint16 *ptr; - - alsa = GST_ALSA_SRC (asrc); - - cptr = length / alsa->bytes_per_sample; - ptr = data; - - GST_ALSA_SRC_LOCK (asrc); - while (cptr > 0) { - if ((err = snd_pcm_readi (alsa->handle, ptr, cptr)) < 0) { - if (err == -EAGAIN) { - GST_DEBUG_OBJECT (asrc, "Read error: %s", snd_strerror (err)); - continue; - } else if (xrun_recovery (alsa, alsa->handle, err) < 0) { - goto read_error; - } - continue; - } - - ptr += err * alsa->channels; - cptr -= err; - } - GST_ALSA_SRC_UNLOCK (asrc); - - return length - cptr; - -read_error: - { - GST_ALSA_SRC_UNLOCK (asrc); - return length; /* skip one period */ - } -} - -static guint -gst_alsasrc_delay (GstAudioSrc * asrc) -{ - GstAlsaSrc *alsa; - snd_pcm_sframes_t delay; - int res; - - alsa = GST_ALSA_SRC (asrc); - - res = snd_pcm_delay (alsa->handle, &delay); - if (G_UNLIKELY (res < 0)) { - GST_DEBUG_OBJECT (alsa, "snd_pcm_delay returned %d", res); - delay = 0; - } - - return CLAMP (delay, 0, alsa->buffer_size); -} - -static void -gst_alsasrc_reset (GstAudioSrc * asrc) -{ - GstAlsaSrc *alsa; - gint err; - - alsa = GST_ALSA_SRC (asrc); - - GST_ALSA_SRC_LOCK (asrc); - GST_DEBUG_OBJECT (alsa, "drop"); - CHECK (snd_pcm_drop (alsa->handle), drop_error); - GST_DEBUG_OBJECT (alsa, "prepare"); - CHECK (snd_pcm_prepare (alsa->handle), prepare_error); - GST_DEBUG_OBJECT (alsa, "reset done"); - GST_ALSA_SRC_UNLOCK (asrc); - - return; - - /* ERRORS */ -drop_error: - { - GST_ERROR_OBJECT (alsa, "alsa-reset: pcm drop error: %s", - snd_strerror (err)); - GST_ALSA_SRC_UNLOCK (asrc); - return; - } -prepare_error: - { - GST_ERROR_OBJECT (alsa, "alsa-reset: pcm prepare error: %s", - snd_strerror (err)); - GST_ALSA_SRC_UNLOCK (asrc); - return; - } -} diff --git a/ext/alsa/gstalsasrc.h b/ext/alsa/gstalsasrc.h deleted file mode 100644 index d0fcbf07..00000000 --- a/ext/alsa/gstalsasrc.h +++ /dev/null @@ -1,86 +0,0 @@ -/* GStreamer - * Copyright (C) 2005 Wim Taymans <wim@fluendo.com> - * - * gstalsasrc.h: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_ALSASRC_H__ -#define __GST_ALSASRC_H__ - -#include <gst/audio/gstaudiosrc.h> -#include "gstalsa.h" -#include "gstalsamixer.h" - -G_BEGIN_DECLS - -#define GST_TYPE_ALSA_SRC (gst_alsasrc_get_type()) -#define GST_ALSA_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALSA_SRC,GstAlsaSrc)) -#define GST_ALSA_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALSA_SRC,GstAlsaSrcClass)) -#define GST_IS_ALSA_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALSA_SRC)) -#define GST_IS_ALSA_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALSA_SRC)) -#define GST_ALSA_SRC_CAST(obj) ((GstAlsaSrc *)(obj)) - -#define GST_ALSA_SRC_GET_LOCK(obj) (GST_ALSA_SRC_CAST (obj)->alsa_lock) -#define GST_ALSA_SRC_LOCK(obj) (g_mutex_lock (GST_ALSA_SRC_GET_LOCK (obj))) -#define GST_ALSA_SRC_UNLOCK(obj) (g_mutex_unlock (GST_ALSA_SRC_GET_LOCK (obj))) - -typedef struct _GstAlsaSrc GstAlsaSrc; -typedef struct _GstAlsaSrcClass GstAlsaSrcClass; - -/** - * GstAlsaSrc: - * - * Opaque data structure - */ -struct _GstAlsaSrc { - GstAudioSrc src; - - gchar *device; - - snd_pcm_t *handle; - snd_pcm_hw_params_t *hwparams; - snd_pcm_sw_params_t *swparams; - - GstCaps *cached_caps; - - snd_pcm_access_t access; - snd_pcm_format_t format; - guint rate; - guint channels; - gint bytes_per_sample; - - guint buffer_time; - guint period_time; - snd_pcm_uframes_t buffer_size; - snd_pcm_uframes_t period_size; - - GstAlsaMixer *mixer; - - GMutex *alsa_lock; -}; - -struct _GstAlsaSrcClass { - GstAudioSrcClass parent_class; -}; - -GType gst_alsasrc_get_type(void); - -G_END_DECLS - -#endif /* __GST_ALSASRC_H__ */ diff --git a/ext/cdparanoia/Makefile.am b/ext/cdparanoia/Makefile.am deleted file mode 100644 index aef35819..00000000 --- a/ext/cdparanoia/Makefile.am +++ /dev/null @@ -1,13 +0,0 @@ -plugin_LTLIBRARIES = libgstcdparanoia.la - -libgstcdparanoia_la_SOURCES = gstcdparanoiasrc.c -libgstcdparanoia_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) -libgstcdparanoia_la_LIBADD = \ - $(top_builddir)/gst-libs/gst/cdda/libgstcdda-$(GST_MAJORMINOR).la \ - $(GST_BASE_LIBS) \ - $(GST_LIBS) \ - $(CDPARANOIA_LIBS) -libgstcdparanoia_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstcdparanoia_la_LIBTOOLFLAGS = --tag=disable-static - -noinst_HEADERS = gstcdparanoiasrc.h diff --git a/ext/cdparanoia/gstcdparanoiasrc.c b/ext/cdparanoia/gstcdparanoiasrc.c deleted file mode 100644 index fb710e0d..00000000 --- a/ext/cdparanoia/gstcdparanoiasrc.c +++ /dev/null @@ -1,544 +0,0 @@ -/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * <2005> Wim Taymans <wim@fluendo.com> - * <2005> Tim-Philipp Müller <tim centricular net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <string.h> -#include <errno.h> - -#include "gstcdparanoiasrc.h" -#include "gst/gst-i18n-plugin.h" - -enum -{ - TRANSPORT_ERROR, - UNCORRECTED_ERROR, - NUM_SIGNALS -}; - -enum -{ - PROP_0, - PROP_READ_SPEED, - PROP_PARANOIA_MODE, - PROP_SEARCH_OVERLAP, - PROP_GENERIC_DEVICE, - PROP_CACHE_SIZE -}; - -#define DEFAULT_READ_SPEED -1 -#define DEFAULT_SEARCH_OVERLAP -1 -#define DEFAULT_PARANOIA_MODE PARANOIA_MODE_FRAGMENT -#define DEFAULT_GENERIC_DEVICE NULL -#define DEFAULT_CACHE_SIZE -1 - -GST_DEBUG_CATEGORY_STATIC (gst_cd_paranoia_src_debug); -#define GST_CAT_DEFAULT gst_cd_paranoia_src_debug - -GST_BOILERPLATE (GstCdParanoiaSrc, gst_cd_paranoia_src, GstCddaBaseSrc, - GST_TYPE_CDDA_BASE_SRC); - -static void gst_cd_paranoia_src_finalize (GObject * obj); -static void gst_cd_paranoia_src_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static void gst_cd_paranoia_src_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static GstBuffer *gst_cd_paranoia_src_read_sector (GstCddaBaseSrc * src, - gint sector); -static gboolean gst_cd_paranoia_src_open (GstCddaBaseSrc * src, - const gchar * device); -static void gst_cd_paranoia_src_close (GstCddaBaseSrc * src); - -static const GstElementDetails cdparanoia_details = -GST_ELEMENT_DETAILS ("CD Audio (cdda) Source, Paranoia IV", - "Source/File", - "Read audio from CD in paranoid mode", - "Erik Walthinsen <omega@cse.ogi.edu>, " "Wim Taymans <wim@fluendo.com>"); - -/* We use these to serialize calls to paranoia_read() among several - * cdparanoiasrc instances. We do this because it's the only reasonably - * easy way to find out the calling object from within the paranoia - * callback, and we need the object instance in there to emit our signals */ -static GstCdParanoiaSrc *cur_cb_source; -static GStaticMutex cur_cb_mutex = G_STATIC_MUTEX_INIT; - -static gint cdpsrc_signals[NUM_SIGNALS]; /* all 0 */ - -#define GST_TYPE_CD_PARANOIA_MODE (gst_cd_paranoia_mode_get_type()) -static GType -gst_cd_paranoia_mode_get_type (void) -{ - static const GFlagsValue paranoia_modes[] = { - {PARANOIA_MODE_DISABLE, "PARANOIA_MODE_DISABLE", "disable"}, - {PARANOIA_MODE_FRAGMENT, "PARANOIA_MODE_FRAGMENT", "fragment"}, - {PARANOIA_MODE_OVERLAP, "PARANOIA_MODE_OVERLAP", "overlap"}, - {PARANOIA_MODE_SCRATCH, "PARANOIA_MODE_SCRATCH", "scratch"}, - {PARANOIA_MODE_REPAIR, "PARANOIA_MODE_REPAIR", "repair"}, - {PARANOIA_MODE_FULL, "PARANOIA_MODE_FULL", "full"}, - {0, NULL, NULL}, - }; - - static GType type; /* 0 */ - - if (!type) { - type = g_flags_register_static ("GstCdParanoiaMode", paranoia_modes); - } - - return type; -} - -static void -gst_cd_paranoia_src_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_details (element_class, &cdparanoia_details); -} - -static void -gst_cd_paranoia_src_init (GstCdParanoiaSrc * src, GstCdParanoiaSrcClass * klass) -{ - src->d = NULL; - src->p = NULL; - src->next_sector = -1; - - src->search_overlap = DEFAULT_SEARCH_OVERLAP; - src->paranoia_mode = DEFAULT_PARANOIA_MODE; - src->read_speed = DEFAULT_READ_SPEED; - src->generic_device = g_strdup (DEFAULT_GENERIC_DEVICE); - src->cache_size = DEFAULT_CACHE_SIZE; -} - -static void -gst_cd_paranoia_src_class_init (GstCdParanoiaSrcClass * klass) -{ - GstCddaBaseSrcClass *cddabasesrc_class = GST_CDDA_BASE_SRC_CLASS (klass); - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->set_property = gst_cd_paranoia_src_set_property; - gobject_class->get_property = gst_cd_paranoia_src_get_property; - gobject_class->finalize = gst_cd_paranoia_src_finalize; - - cddabasesrc_class->open = gst_cd_paranoia_src_open; - cddabasesrc_class->close = gst_cd_paranoia_src_close; - cddabasesrc_class->read_sector = gst_cd_paranoia_src_read_sector; - - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_GENERIC_DEVICE, - g_param_spec_string ("generic-device", "Generic device", - "Use specified generic scsi device", DEFAULT_GENERIC_DEVICE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_READ_SPEED, - g_param_spec_int ("read-speed", "Read speed", - "Read from device at specified speed", -1, G_MAXINT, - DEFAULT_READ_SPEED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PARANOIA_MODE, - g_param_spec_flags ("paranoia-mode", "Paranoia mode", - "Type of checking to perform", GST_TYPE_CD_PARANOIA_MODE, - DEFAULT_PARANOIA_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SEARCH_OVERLAP, - g_param_spec_int ("search-overlap", "Search overlap", - "Force minimum overlap search during verification to n sectors", -1, - 75, DEFAULT_SEARCH_OVERLAP, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /** - * GstCdParanoiaSrc:cache-size - * - * Set CD cache size to n sectors (-1 = auto) - * - * Since: 0.10.24 - */ - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CACHE_SIZE, - g_param_spec_int ("cache-size", "Cache size", - "Set CD cache size to n sectors (-1 = auto)", -1, - G_MAXINT, DEFAULT_CACHE_SIZE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /* FIXME: we don't really want signals for this, but messages on the bus, - * but then we can't check any longer whether anyone is interested in them */ - /** - * GstCdParanoiaSrc::transport-error - * @cdparanoia: The CdParanoia instance - * @sector: The sector number at which the error was encountered. - * - * This signal is emitted whenever an error occurs while reading. - * CdParanoia will attempt to recover the data. - */ - cdpsrc_signals[TRANSPORT_ERROR] = - g_signal_new ("transport-error", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstCdParanoiaSrcClass, transport_error), - NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); - /** - * GstCdParanoiaSrc::uncorrected-error - * @cdparanoia: The CdParanoia instance - * @sector: The sector number at which the error was encountered. - * - * This signal is emitted whenever an uncorrectable error occurs while - * reading. The data could not be read. - */ - cdpsrc_signals[UNCORRECTED_ERROR] = - g_signal_new ("uncorrected-error", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstCdParanoiaSrcClass, uncorrected_error), - NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); -} - -static gboolean -gst_cd_paranoia_src_open (GstCddaBaseSrc * cddabasesrc, const gchar * device) -{ - GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (cddabasesrc); - gint i, cache_size; - - GST_DEBUG_OBJECT (src, "trying to open device %s (generic-device=%s) ...", - device, GST_STR_NULL (src->generic_device)); - - /* find the device */ - if (src->generic_device != NULL) { - src->d = cdda_identify_scsi (src->generic_device, device, FALSE, NULL); - } else { - if (device != NULL) { - src->d = cdda_identify (device, FALSE, NULL); - } else { - src->d = cdda_identify ("/dev/cdrom", FALSE, NULL); - } - } - - /* fail if the device couldn't be found */ - if (src->d == NULL) - goto no_device; - - /* set verbosity mode */ - cdda_verbose_set (src->d, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT); - - /* open the disc */ - if (cdda_open (src->d)) - goto open_failed; - - if (src->read_speed != -1) { - cdda_speed_set (src->d, src->read_speed); - } - - for (i = 1; i < src->d->tracks + 1; i++) { - GstCddaBaseSrcTrack track = { 0, }; - - track.num = i; - track.is_audio = IS_AUDIO (src->d, i - 1); - track.start = cdda_track_firstsector (src->d, i); - track.end = cdda_track_lastsector (src->d, i); - track.tags = NULL; - - gst_cdda_base_src_add_track (GST_CDDA_BASE_SRC (src), &track); - } - - /* create the paranoia struct and set it up */ - src->p = paranoia_init (src->d); - if (src->p == NULL) - goto init_failed; - - paranoia_modeset (src->p, src->paranoia_mode); - GST_INFO_OBJECT (src, "set paranoia mode to 0x%02x", src->paranoia_mode); - - if (src->search_overlap != -1) { - paranoia_overlapset (src->p, src->search_overlap); - GST_INFO_OBJECT (src, "search overlap set to %u", src->search_overlap); - } - - cache_size = src->cache_size; - if (cache_size == -1) { - /* if paranoia mode is low (the default), assume we're doing playback */ - if (src->paranoia_mode <= PARANOIA_MODE_FRAGMENT) - cache_size = 150; - else - cache_size = paranoia_cachemodel_size (src->p, -1); - } - paranoia_cachemodel_size (src->p, cache_size); - GST_INFO_OBJECT (src, "set cachemodel size to %u", cache_size); - - src->next_sector = -1; - - return TRUE; - - /* ERRORS */ -no_device: - { - GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, - (_("Could not open CD device for reading.")), ("cdda_identify failed")); - return FALSE; - } -open_failed: - { - GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, - (_("Could not open CD device for reading.")), ("cdda_open failed")); - cdda_close (src->d); - src->d = NULL; - return FALSE; - } -init_failed: - { - GST_ELEMENT_ERROR (src, LIBRARY, INIT, - ("failed to initialize paranoia"), ("failed to initialize paranoia")); - return FALSE; - } -} - -static void -gst_cd_paranoia_src_close (GstCddaBaseSrc * cddabasesrc) -{ - GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (cddabasesrc); - - if (src->p) { - paranoia_free (src->p); - src->p = NULL; - } - - if (src->d) { - cdda_close (src->d); - src->d = NULL; - } - - src->next_sector = -1; -} - -static void -gst_cd_paranoia_dummy_callback (long inpos, int function) -{ - /* Used by instanced where no one is interested what's happening here */ -} - -static void -gst_cd_paranoia_paranoia_callback (long inpos, int function) -{ - GstCdParanoiaSrc *src = cur_cb_source; - gint sector = (gint) (inpos / CD_FRAMEWORDS); - - switch (function) { - case PARANOIA_CB_SKIP: - GST_INFO_OBJECT (src, "Skip at sector %d", sector); - g_signal_emit (src, cdpsrc_signals[UNCORRECTED_ERROR], 0, sector); - break; - case PARANOIA_CB_READERR: - GST_INFO_OBJECT (src, "Transport error at sector %d", sector); - g_signal_emit (src, cdpsrc_signals[TRANSPORT_ERROR], 0, sector); - break; - default: - break; - } -} - -static gboolean -gst_cd_paranoia_src_signal_is_being_watched (GstCdParanoiaSrc * src, gint sig) -{ - return g_signal_has_handler_pending (src, cdpsrc_signals[sig], 0, FALSE); -} - -static GstBuffer * -gst_cd_paranoia_src_read_sector (GstCddaBaseSrc * cddabasesrc, gint sector) -{ - GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (cddabasesrc); - GstBuffer *buf; - gboolean do_serialize; - gint16 *cdda_buf; - -#if 0 - /* Do we really need to output this? (tpm) */ - /* Due to possible autocorrections of start sectors of audio tracks on - * multisession cds, we can maybe not compute the correct discid. - * So issue a warning. - * See cdparanoia/interface/common-interface.c:FixupTOC */ - if (src->d && src->d->cd_extra) { - g_message - ("DiscID on multisession discs might be broken. Use at own risk."); - } -#endif - - if (src->next_sector == -1 || src->next_sector != sector) { - if (paranoia_seek (src->p, sector, SEEK_SET) == -1) - goto seek_failed; - - GST_DEBUG_OBJECT (src, "successfully seeked to sector %d", sector); - src->next_sector = sector; - } - - do_serialize = - gst_cd_paranoia_src_signal_is_being_watched (src, TRANSPORT_ERROR) || - gst_cd_paranoia_src_signal_is_being_watched (src, UNCORRECTED_ERROR); - - if (do_serialize) { - GST_LOG_OBJECT (src, "Signal handlers connected, serialising access"); - g_static_mutex_lock (&cur_cb_mutex); - GST_LOG_OBJECT (src, "Got lock"); - cur_cb_source = src; - - cdda_buf = paranoia_read (src->p, gst_cd_paranoia_paranoia_callback); - - cur_cb_source = NULL; - GST_LOG_OBJECT (src, "Releasing lock"); - g_static_mutex_unlock (&cur_cb_mutex); - } else { - cdda_buf = paranoia_read (src->p, gst_cd_paranoia_dummy_callback); - } - - if (cdda_buf == NULL) - goto read_failed; - - buf = gst_buffer_new_and_alloc (CD_FRAMESIZE_RAW); - memcpy (GST_BUFFER_DATA (buf), cdda_buf, CD_FRAMESIZE_RAW); - - /* cdda base class will take care of timestamping etc. */ - ++src->next_sector; - - return buf; - - /* ERRORS */ -seek_failed: - { - GST_WARNING_OBJECT (src, "seek to sector %d failed!", sector); - GST_ELEMENT_ERROR (src, RESOURCE, SEEK, - (_("Could not seek CD.")), - ("paranoia_seek to %d failed: %s", sector, g_strerror (errno))); - return NULL; - } -read_failed: - { - GST_WARNING_OBJECT (src, "read at sector %d failed!", sector); - GST_ELEMENT_ERROR (src, RESOURCE, READ, - (_("Could not read CD.")), - ("paranoia_read at %d failed: %s", sector, g_strerror (errno))); - return NULL; - } -} - -static void -gst_cd_paranoia_src_finalize (GObject * obj) -{ - GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (obj); - - g_free (src->generic_device); - - G_OBJECT_CLASS (parent_class)->finalize (obj); -} - -static void -gst_cd_paranoia_src_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (object); - - GST_OBJECT_LOCK (src); - - switch (prop_id) { - case PROP_GENERIC_DEVICE:{ - g_free (src->generic_device); - src->generic_device = g_value_dup_string (value); - if (src->generic_device && src->generic_device[0] == '\0') { - g_free (src->generic_device); - src->generic_device = NULL; - } - break; - } - case PROP_READ_SPEED:{ - src->read_speed = g_value_get_int (value); - if (src->read_speed == 0) - src->read_speed = -1; - break; - } - case PROP_PARANOIA_MODE:{ - src->paranoia_mode = g_value_get_flags (value) & PARANOIA_MODE_FULL; - break; - } - case PROP_SEARCH_OVERLAP:{ - src->search_overlap = g_value_get_int (value); - break; - } - case PROP_CACHE_SIZE:{ - src->cache_size = g_value_get_int (value); - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - - GST_OBJECT_UNLOCK (src); -} - -static void -gst_cd_paranoia_src_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (object); - - GST_OBJECT_LOCK (src); - - switch (prop_id) { - case PROP_READ_SPEED: - g_value_set_int (value, src->read_speed); - break; - case PROP_PARANOIA_MODE: - g_value_set_flags (value, src->paranoia_mode); - break; - case PROP_GENERIC_DEVICE: - g_value_set_string (value, src->generic_device); - break; - case PROP_SEARCH_OVERLAP: - g_value_set_int (value, src->search_overlap); - break; - case PROP_CACHE_SIZE: - g_value_set_int (value, src->cache_size); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - - GST_OBJECT_UNLOCK (src); -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - GST_DEBUG_CATEGORY_INIT (gst_cd_paranoia_src_debug, "cdparanoiasrc", 0, - "CD Paranoia Source"); - - if (!gst_element_register (plugin, "cdparanoiasrc", GST_RANK_SECONDARY, - GST_TYPE_CD_PARANOIA_SRC)) - return FALSE; - -#ifdef ENABLE_NLS - GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE, - LOCALEDIR); - bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); -#endif - - - return TRUE; -} - - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "cdparanoia", - "Read audio from CD in paranoid mode", - plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/cdparanoia/gstcdparanoiasrc.h b/ext/cdparanoia/gstcdparanoiasrc.h deleted file mode 100644 index 6cec23c4..00000000 --- a/ext/cdparanoia/gstcdparanoiasrc.h +++ /dev/null @@ -1,100 +0,0 @@ -/* GStreamer - * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_CD_PARANOIA_SRC_H__ -#define __GST_CD_PARANOIA_SRC_H__ - -#include "gst/cdda/gstcddabasesrc.h" - -G_BEGIN_DECLS - -#define size16 gint16 -#define size32 gint32 - -#ifdef CDPARANOIA_HEADERS_IN_DIR - #include <cdda/cdda_interface.h> - #include <cdda/cdda_paranoia.h> -#else - #include <cdda_interface.h> - #include <cdda_paranoia.h> -#endif - -#define GST_TYPE_CD_PARANOIA_SRC (gst_cd_paranoia_src_get_type()) -#define GST_CD_PARANOIA_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CD_PARANOIA_SRC,GstCdParanoiaSrc)) -#define GST_CD_PARANOIA_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CD_PARANOIA_SRC,GstCdParanoiaSrcClass)) -#define GST_IS_CD_PARANOIA_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CD_PARANOIA_SRC)) -#define GST_IS_CD_PARANOIA_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CD_PARANOIA_SRC)) -#define GST_CD_PARANOIA_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CDDA_BASAE_SRC, GstCdParanoiaSrcClass)) - -typedef struct _GstCdParanoiaSrc GstCdParanoiaSrc; -typedef struct _GstCdParanoiaSrcClass GstCdParanoiaSrcClass; - -/** - * GstCdParanoiaSrc: - * - * The cdparanoia object structure. - */ -struct _GstCdParanoiaSrc { - GstCddaBaseSrc cddabasesrc; - - /*< private >*/ - cdrom_drive *d; - cdrom_paranoia *p; - - gint next_sector; /* -1 or next sector we expect to - * read, so we know when to do a seek */ - - gint paranoia_mode; - gint read_speed; - gint search_overlap; - gint cache_size; - - gchar *generic_device; -}; - -struct _GstCdParanoiaSrcClass { - GstCddaBaseSrcClass parent_class; - - /* signal callbacks */ - /** - * GstCdParanoiaSrcClass::transport-error: - * @src: the GstCddaBaseSrc source element object - * @sector: the sector at which the error happened - * - * This signal is emitted when a sector could not be read - * because of a transport error. - */ - void (*transport_error) (GstCdParanoiaSrc * src, gint sector); - /** - * GstCdParanoiaSrcClass::uncorrected-error: - * @src: the GstCddaBaseSrc source element object - * @sector: the sector at which the error happened - * - * This signal is emitted when a sector could not be read - * because of a transport error. - */ - void (*uncorrected_error) (GstCdParanoiaSrc * src, gint sector); -}; - -GType gst_cd_paranoia_src_get_type (void); - -G_END_DECLS - -#endif /* __GST_CD_PARANOIA_SRC_H__ */ - diff --git a/ext/gio/Makefile.am b/ext/gio/Makefile.am deleted file mode 100644 index 237cfdd2..00000000 --- a/ext/gio/Makefile.am +++ /dev/null @@ -1,29 +0,0 @@ -# plugindir is set in configure - -plugin_LTLIBRARIES = libgstgio.la - -# sources used to compile this plug-in -libgstgio_la_SOURCES = \ - gstgio.c \ - gstgiobasesink.c \ - gstgiobasesrc.c \ - gstgiosink.c \ - gstgiosrc.c \ - gstgiostreamsink.c \ - gstgiostreamsrc.c - -libgstgio_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(GIO_CFLAGS) -libgstgio_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(GIO_LIBS) -libgstgio_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GIO_LDFLAGS) -libgstgio_la_LIBTOOLFLAGS = --tag=disable-static - -# headers we need but don't want installed -noinst_HEADERS = \ - gstgio.h \ - gstgiobasesink.h \ - gstgiobasesrc.h \ - gstgiosink.h \ - gstgiosrc.h \ - gstgiostreamsink.h \ - gstgiostreamsrc.h - diff --git a/ext/gio/gstgio.c b/ext/gio/gstgio.c deleted file mode 100644 index be0a940b..00000000 --- a/ext/gio/gstgio.c +++ /dev/null @@ -1,259 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Rene Stadler <mail@renestadler.de> - * Copyright (C) 2007 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstgio.h" -#include "gstgiosink.h" -#include "gstgiosrc.h" -#include "gstgiostreamsink.h" -#include "gstgiostreamsrc.h" - -#include <string.h> - -GST_DEBUG_CATEGORY_STATIC (gst_gio_debug); -#define GST_CAT_DEFAULT gst_gio_debug - -/* @func_name: Name of the GIO function, for debugging messages. - * @err: Error location. *err may be NULL, but err must be non-NULL. - * @ret: Flow return location. May be NULL. Is set to either #GST_FLOW_ERROR - * or #GST_FLOW_WRONG_STATE. - * - * Returns: TRUE to indicate a handled error. Error at given location err will - * be freed and *err will be set to NULL. A FALSE return indicates an unhandled - * error: The err location is unchanged and guaranteed to be != NULL. ret, if - * given, is set to GST_FLOW_ERROR. - */ -gboolean -gst_gio_error (gpointer element, const gchar * func_name, GError ** err, - GstFlowReturn * ret) -{ - gboolean handled = TRUE; - - if (ret) - *ret = GST_FLOW_ERROR; - - if (GST_GIO_ERROR_MATCHES (*err, CANCELLED)) { - GST_DEBUG_OBJECT (element, "blocking I/O call cancelled (%s)", func_name); - if (ret) - *ret = GST_FLOW_WRONG_STATE; - } else if (*err != NULL) { - handled = FALSE; - } else { - GST_ELEMENT_ERROR (element, LIBRARY, FAILED, (NULL), - ("%s call failed without error set", func_name)); - } - - if (handled) - g_clear_error (err); - - return handled; -} - -GstFlowReturn -gst_gio_seek (gpointer element, GSeekable * stream, guint64 offset, - GCancellable * cancel) -{ - gboolean success; - GstFlowReturn ret; - GError *err = NULL; - - GST_LOG_OBJECT (element, "seeking to offset %" G_GINT64_FORMAT, offset); - - success = g_seekable_seek (stream, offset, G_SEEK_SET, cancel, &err); - - if (success) - ret = GST_FLOW_OK; - else if (!gst_gio_error (element, "g_seekable_seek", &err, &ret)) { - GST_ELEMENT_ERROR (element, RESOURCE, SEEK, (NULL), - ("Could not seek: %s", err->message)); - g_clear_error (&err); - } - - return ret; -} - -static gpointer -_internal_get_supported_protocols (gpointer data) -{ - const gchar *const *schemes; - gchar **our_schemes; - guint num; - gint i, j; - - schemes = g_vfs_get_supported_uri_schemes (g_vfs_get_default ()); - num = g_strv_length ((gchar **) schemes); - - if (num == 0) { - GST_WARNING ("No GIO supported URI schemes found"); - return NULL; - } - - our_schemes = g_new0 (gchar *, num + 1); - - /* - Filter http/https as we can't support the icy stuff with GIO. - * Use souphttpsrc if you need that. - * - Filter cdda as it doesn't support musicbrainz stuff and everything - * else one expects from a cdda source. Use cdparanoiasrc or cdiosrc - * for cdda. - */ - for (i = 0, j = 0; i < num; i++) { - if (strcmp (schemes[i], "http") == 0 || strcmp (schemes[i], "https") == 0 - || strcmp (schemes[i], "cdda") == 0) - continue; - - our_schemes[j] = g_strdup (schemes[i]); - j++; - } - - return our_schemes; -} - -static gchar ** -gst_gio_get_supported_protocols (void) -{ - static GOnce once = G_ONCE_INIT; - - g_once (&once, _internal_get_supported_protocols, NULL); - return (gchar **) once.retval; -} - -static GstURIType -gst_gio_uri_handler_get_type_sink (void) -{ - return GST_URI_SINK; -} - -static GstURIType -gst_gio_uri_handler_get_type_src (void) -{ - return GST_URI_SRC; -} - -static gchar ** -gst_gio_uri_handler_get_protocols (void) -{ - static gchar **protocols = NULL; - - if (!protocols) - protocols = gst_gio_get_supported_protocols (); - - return protocols; -} - -static const gchar * -gst_gio_uri_handler_get_uri (GstURIHandler * handler) -{ - GstElement *element = GST_ELEMENT (handler); - const gchar *uri; - - g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); - - g_object_get (G_OBJECT (element), "location", &uri, NULL); - - return uri; -} - -static gboolean -gst_gio_uri_handler_set_uri (GstURIHandler * handler, const gchar * uri) -{ - GstElement *element = GST_ELEMENT (handler); - - g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); - - if (GST_STATE (element) == GST_STATE_PLAYING || - GST_STATE (element) == GST_STATE_PAUSED) - return FALSE; - - g_object_set (G_OBJECT (element), "location", uri, NULL); - - return TRUE; -} - -static void -gst_gio_uri_handler_init (gpointer g_iface, gpointer iface_data) -{ - GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; - gboolean sink = GPOINTER_TO_INT (iface_data); /* See in do_init below. */ - - if (sink) - iface->get_type = gst_gio_uri_handler_get_type_sink; - else - iface->get_type = gst_gio_uri_handler_get_type_src; - iface->get_protocols = gst_gio_uri_handler_get_protocols; - iface->get_uri = gst_gio_uri_handler_get_uri; - iface->set_uri = gst_gio_uri_handler_set_uri; -} - -void -gst_gio_uri_handler_do_init (GType type) -{ - GInterfaceInfo uri_handler_info = { - gst_gio_uri_handler_init, - NULL, - NULL - }; - - /* Store information for uri_handler_init to use for distinguishing the - * element types. This lets us use a single interface implementation for both - * classes. */ - uri_handler_info.interface_data = GINT_TO_POINTER (g_type_is_a (type, - GST_TYPE_BASE_SINK)); - - g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &uri_handler_info); -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - gboolean ret = TRUE; - - GST_DEBUG_CATEGORY_INIT (gst_gio_debug, "gio", 0, "GIO elements"); - - gst_plugin_add_dependency_simple (plugin, NULL, GIO_MODULE_DIR, NULL, - GST_PLUGIN_DEPENDENCY_FLAG_NONE); - gst_plugin_add_dependency_simple (plugin, "LD_LIBRARY_PATH", GIO_LIBDIR, - "gvfsd", GST_PLUGIN_DEPENDENCY_FLAG_NONE); - - /* FIXME: Rank is MARGINAL for now, should be at least SECONDARY+1 in the future - * to replace gnomevfssink/src. For testing purposes PRIMARY+1 one makes sense - * so it gets autoplugged and preferred over filesrc/sink. */ - - ret &= gst_element_register (plugin, "giosink", GST_RANK_MARGINAL, - GST_TYPE_GIO_SINK); - - ret &= gst_element_register (plugin, "giosrc", GST_RANK_MARGINAL, - GST_TYPE_GIO_SRC); - - ret &= gst_element_register (plugin, "giostreamsink", GST_RANK_NONE, - GST_TYPE_GIO_STREAM_SINK); - - ret &= gst_element_register (plugin, "giostreamsrc", GST_RANK_NONE, - GST_TYPE_GIO_STREAM_SRC); - - return ret; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, "gio", - "GIO elements", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, - GST_PACKAGE_ORIGIN) diff --git a/ext/gio/gstgio.h b/ext/gio/gstgio.h deleted file mode 100644 index 01183e13..00000000 --- a/ext/gio/gstgio.h +++ /dev/null @@ -1,42 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Rene Stadler <mail@renestadler.de> - * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_GIO_H__ -#define __GST_GIO_H__ - -#include <gio/gio.h> -#include <gst/gst.h> - -G_BEGIN_DECLS - -#define GST_GIO_ERROR_MATCHES(err, code) g_error_matches (err, G_IO_ERROR, G_IO_ERROR_##code) - -#define GST_GIO_STREAM_IS_SEEKABLE(stream) (G_IS_SEEKABLE (stream) && g_seekable_can_seek (G_SEEKABLE (stream))) - -gboolean gst_gio_error (gpointer element, const gchar *func_name, - GError **err, GstFlowReturn *ret); -GstFlowReturn gst_gio_seek (gpointer element, GSeekable *stream, guint64 offset, - GCancellable *cancel); -void gst_gio_uri_handler_do_init (GType type); - -G_END_DECLS - -#endif /* __GST_GIO_H__ */ diff --git a/ext/gio/gstgiobasesink.c b/ext/gio/gstgiobasesink.c deleted file mode 100644 index 2b16909b..00000000 --- a/ext/gio/gstgiobasesink.c +++ /dev/null @@ -1,377 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Rene Stadler <mail@renestadler.de> - * Copyright (C) 2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "gstgiobasesink.h" - -GST_DEBUG_CATEGORY_STATIC (gst_gio_base_sink_debug); -#define GST_CAT_DEFAULT gst_gio_base_sink_debug - -static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -GST_BOILERPLATE (GstGioBaseSink, gst_gio_base_sink, GstBaseSink, - GST_TYPE_BASE_SINK); - -static void gst_gio_base_sink_finalize (GObject * object); -static gboolean gst_gio_base_sink_start (GstBaseSink * base_sink); -static gboolean gst_gio_base_sink_stop (GstBaseSink * base_sink); -static gboolean gst_gio_base_sink_unlock (GstBaseSink * base_sink); -static gboolean gst_gio_base_sink_unlock_stop (GstBaseSink * base_sink); -static gboolean gst_gio_base_sink_event (GstBaseSink * base_sink, - GstEvent * event); -static GstFlowReturn gst_gio_base_sink_render (GstBaseSink * base_sink, - GstBuffer * buffer); -static gboolean gst_gio_base_sink_query (GstPad * pad, GstQuery * query); - -static void -gst_gio_base_sink_base_init (gpointer gclass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); - - GST_DEBUG_CATEGORY_INIT (gst_gio_base_sink_debug, "gio_base_sink", 0, - "GIO base sink"); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_factory)); -} - -static void -gst_gio_base_sink_class_init (GstGioBaseSinkClass * klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - GstBaseSinkClass *gstbasesink_class = (GstBaseSinkClass *) klass; - - gobject_class->finalize = gst_gio_base_sink_finalize; - - gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_gio_base_sink_start); - gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_gio_base_sink_stop); - gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_gio_base_sink_unlock); - gstbasesink_class->unlock_stop = - GST_DEBUG_FUNCPTR (gst_gio_base_sink_unlock_stop); - gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_gio_base_sink_event); - gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_gio_base_sink_render); -} - -static void -gst_gio_base_sink_init (GstGioBaseSink * sink, GstGioBaseSinkClass * gclass) -{ - gst_pad_set_query_function (GST_BASE_SINK_PAD (sink), - GST_DEBUG_FUNCPTR (gst_gio_base_sink_query)); - - gst_base_sink_set_sync (GST_BASE_SINK (sink), FALSE); - - sink->cancel = g_cancellable_new (); -} - -static void -gst_gio_base_sink_finalize (GObject * object) -{ - GstGioBaseSink *sink = GST_GIO_BASE_SINK (object); - - if (sink->cancel) { - g_object_unref (sink->cancel); - sink->cancel = NULL; - } - - if (sink->stream) { - g_object_unref (sink->stream); - sink->stream = NULL; - } - - GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); -} - -static gboolean -gst_gio_base_sink_start (GstBaseSink * base_sink) -{ - GstGioBaseSink *sink = GST_GIO_BASE_SINK (base_sink); - GstGioBaseSinkClass *gbsink_class = GST_GIO_BASE_SINK_GET_CLASS (sink); - - sink->position = 0; - - /* FIXME: This will likely block */ - sink->stream = gbsink_class->get_stream (sink); - if (G_UNLIKELY (!G_IS_OUTPUT_STREAM (sink->stream))) { - GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL), - ("No output stream provided by subclass")); - return FALSE; - } else if (G_UNLIKELY (g_output_stream_is_closed (sink->stream))) { - GST_ELEMENT_ERROR (sink, LIBRARY, FAILED, (NULL), - ("Output stream is already closed")); - return FALSE; - } - - GST_DEBUG_OBJECT (sink, "started sink"); - - return TRUE; -} - -static gboolean -gst_gio_base_sink_stop (GstBaseSink * base_sink) -{ - GstGioBaseSink *sink = GST_GIO_BASE_SINK (base_sink); - GstGioBaseSinkClass *klass = GST_GIO_BASE_SINK_GET_CLASS (sink); - gboolean success; - GError *err = NULL; - - if (klass->close_on_stop && G_IS_OUTPUT_STREAM (sink->stream)) { - GST_DEBUG_OBJECT (sink, "closing stream"); - - /* FIXME: can block but unfortunately we can't use async operations - * here because they require a running main loop */ - success = g_output_stream_close (sink->stream, sink->cancel, &err); - - if (!success && !gst_gio_error (sink, "g_output_stream_close", &err, NULL)) { - GST_ELEMENT_WARNING (sink, RESOURCE, CLOSE, (NULL), - ("gio_output_stream_close failed: %s", err->message)); - g_clear_error (&err); - } else if (!success) { - GST_ELEMENT_WARNING (sink, RESOURCE, CLOSE, (NULL), - ("g_output_stream_close failed")); - } else { - GST_DEBUG_OBJECT (sink, "g_outut_stream_close succeeded"); - } - - g_object_unref (sink->stream); - sink->stream = NULL; - } else { - success = g_output_stream_flush (sink->stream, sink->cancel, &err); - - if (!success && !gst_gio_error (sink, "g_output_stream_flush", &err, NULL)) { - GST_ELEMENT_WARNING (sink, RESOURCE, CLOSE, (NULL), - ("gio_output_stream_flush failed: %s", err->message)); - g_clear_error (&err); - } else if (!success) { - GST_ELEMENT_WARNING (sink, RESOURCE, CLOSE, (NULL), - ("g_output_stream_flush failed")); - } else { - GST_DEBUG_OBJECT (sink, "g_outut_stream_flush succeeded"); - } - - g_object_unref (sink->stream); - sink->stream = NULL; - } - - return TRUE; -} - -static gboolean -gst_gio_base_sink_unlock (GstBaseSink * base_sink) -{ - GstGioBaseSink *sink = GST_GIO_BASE_SINK (base_sink); - - GST_LOG_OBJECT (sink, "triggering cancellation"); - - g_cancellable_cancel (sink->cancel); - - return TRUE; -} - -static gboolean -gst_gio_base_sink_unlock_stop (GstBaseSink * base_sink) -{ - GstGioBaseSink *sink = GST_GIO_BASE_SINK (base_sink); - - GST_LOG_OBJECT (sink, "resetting cancellable"); - - g_cancellable_reset (sink->cancel); - - return TRUE; -} - -static gboolean -gst_gio_base_sink_event (GstBaseSink * base_sink, GstEvent * event) -{ - GstGioBaseSink *sink = GST_GIO_BASE_SINK (base_sink); - GstFlowReturn ret = GST_FLOW_OK; - - if (sink->stream == NULL) - return TRUE; - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_NEWSEGMENT: - if (G_IS_OUTPUT_STREAM (sink->stream)) { - GstFormat format; - gint64 offset; - - gst_event_parse_new_segment (event, NULL, NULL, &format, &offset, NULL, - NULL); - - if (format != GST_FORMAT_BYTES) { - GST_WARNING_OBJECT (sink, "ignored NEWSEGMENT event in %s format", - gst_format_get_name (format)); - break; - } - - if (GST_GIO_STREAM_IS_SEEKABLE (sink->stream)) { - ret = gst_gio_seek (sink, G_SEEKABLE (sink->stream), offset, - sink->cancel); - if (ret == GST_FLOW_OK) - sink->position = offset; - } else { - ret = GST_FLOW_NOT_SUPPORTED; - } - } - break; - - case GST_EVENT_EOS: - case GST_EVENT_FLUSH_START: - if (G_IS_OUTPUT_STREAM (sink->stream)) { - gboolean success; - GError *err = NULL; - - success = g_output_stream_flush (sink->stream, sink->cancel, &err); - - if (!success && !gst_gio_error (sink, "g_output_stream_flush", &err, - &ret)) { - GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL), - ("flush failed: %s", err->message)); - g_clear_error (&err); - } - } - break; - - default: - break; - } - - return (ret == GST_FLOW_OK); -} - -static GstFlowReturn -gst_gio_base_sink_render (GstBaseSink * base_sink, GstBuffer * buffer) -{ - GstGioBaseSink *sink = GST_GIO_BASE_SINK (base_sink); - gssize written; - gboolean success; - GError *err = NULL; - - g_return_val_if_fail (G_IS_OUTPUT_STREAM (sink->stream), GST_FLOW_ERROR); - - GST_LOG_OBJECT (sink, "writing %u bytes to offset %" G_GUINT64_FORMAT, - GST_BUFFER_SIZE (buffer), sink->position); - - written = g_output_stream_write (sink->stream, - GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), sink->cancel, &err); - - success = (written >= 0); - - if (G_UNLIKELY (success && written < GST_BUFFER_SIZE (buffer))) { - /* FIXME: Can this happen? Should we handle it gracefully? gnomevfssink - * doesn't... */ - GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL), - ("Could not write to stream: (short write, only %" - G_GSSIZE_FORMAT " bytes of %d bytes written)", - written, GST_BUFFER_SIZE (buffer))); - return GST_FLOW_ERROR; - } - - if (success) { - sink->position += written; - return GST_FLOW_OK; - - } else { - GstFlowReturn ret; - - if (!gst_gio_error (sink, "g_output_stream_write", &err, &ret)) { - GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL), - ("Could not write to stream: %s", err->message)); - g_clear_error (&err); - } - - return ret; - } -} - -static gboolean -gst_gio_base_sink_query (GstPad * pad, GstQuery * query) -{ - GstGioBaseSink *sink = GST_GIO_BASE_SINK (GST_PAD_PARENT (pad)); - GstFormat format; - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - gst_query_parse_position (query, &format, NULL); - switch (format) { - case GST_FORMAT_BYTES: - case GST_FORMAT_DEFAULT: - gst_query_set_position (query, GST_FORMAT_BYTES, sink->position); - return TRUE; - default: - return FALSE; - } - case GST_QUERY_FORMATS: - gst_query_set_formats (query, 2, GST_FORMAT_DEFAULT, GST_FORMAT_BYTES); - return TRUE; - case GST_QUERY_URI: - if (GST_IS_URI_HANDLER (sink)) { - const gchar *uri; - - uri = gst_uri_handler_get_uri (GST_URI_HANDLER (sink)); - gst_query_set_uri (query, uri); - return TRUE; - } - return FALSE; - default: - return gst_pad_query_default (pad, query); - } -} - -void -gst_gio_base_sink_set_stream (GstGioBaseSink * sink, GOutputStream * stream) -{ - g_return_if_fail (G_IS_OUTPUT_STREAM (stream)); - g_return_if_fail ((GST_STATE (sink) != GST_STATE_PLAYING && - GST_STATE (sink) != GST_STATE_PAUSED)); - - if (G_IS_OUTPUT_STREAM (sink->stream)) { - gboolean success; - GError *err = NULL; - - GST_DEBUG_OBJECT (sink, "closing old stream"); - - /* FIXME: can block but unfortunately we can't use async operations - * here because they require a running main loop */ - success = g_output_stream_close (sink->stream, sink->cancel, &err); - - if (!success && !gst_gio_error (sink, "g_output_stream_close", &err, NULL)) { - GST_ELEMENT_WARNING (sink, RESOURCE, CLOSE, (NULL), - ("g_output_stream_close failed: %s", err->message)); - g_clear_error (&err); - } else if (!success) { - GST_ELEMENT_WARNING (sink, RESOURCE, CLOSE, (NULL), - ("g_output_stream_close failed")); - } else { - GST_DEBUG_OBJECT (sink, "g_output_stream_close succeeded"); - } - - g_object_unref (sink->stream); - sink->stream = NULL; - } - - sink->stream = stream; -} diff --git a/ext/gio/gstgiobasesink.h b/ext/gio/gstgiobasesink.h deleted file mode 100644 index 7f13a854..00000000 --- a/ext/gio/gstgiobasesink.h +++ /dev/null @@ -1,71 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Rene Stadler <mail@renestadler.de> - * Copyright (C) 2007-2009 Sebastian Dröge <slomo@circular-chaos.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_GIO_BASE_SINK_H__ -#define __GST_GIO_BASE_SINK_H__ - -#include "gstgio.h" - -#include <gst/base/gstbasesink.h> - -G_BEGIN_DECLS - -#define GST_TYPE_GIO_BASE_SINK \ - (gst_gio_base_sink_get_type()) -#define GST_GIO_BASE_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GIO_BASE_SINK,GstGioBaseSink)) -#define GST_GIO_BASE_SINK_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_GIO_BASE_SINK, GstGioBaseSinkClass)) -#define GST_GIO_BASE_SINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GIO_BASE_SINK,GstGioBaseSinkClass)) -#define GST_IS_GIO_BASE_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GIO_BASE_SINK)) -#define GST_IS_GIO_BASE_SINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GIO_BASE_SINK)) - -typedef struct _GstGioBaseSink GstGioBaseSink; -typedef struct _GstGioBaseSinkClass GstGioBaseSinkClass; - -struct _GstGioBaseSink -{ - GstBaseSink sink; - - /* < protected > */ - GCancellable *cancel; - guint64 position; - - /* < private > */ - GOutputStream *stream; -}; - -struct _GstGioBaseSinkClass -{ - GstBaseSinkClass parent_class; - - GOutputStream * (*get_stream) (GstGioBaseSink *bsink); - gboolean close_on_stop; -}; - -GType gst_gio_base_sink_get_type (void); - -G_END_DECLS - -#endif /* __GST_GIO_BASE_SINK_H__ */ diff --git a/ext/gio/gstgiobasesrc.c b/ext/gio/gstgiobasesrc.c deleted file mode 100644 index 133e12c2..00000000 --- a/ext/gio/gstgiobasesrc.c +++ /dev/null @@ -1,447 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Rene Stadler <mail@renestadler.de> - * Copyright (C) 2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "gstgiobasesrc.h" - -#include <gst/base/gsttypefindhelper.h> - -GST_DEBUG_CATEGORY_STATIC (gst_gio_base_src_debug); -#define GST_CAT_DEFAULT gst_gio_base_src_debug - -static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -GST_BOILERPLATE (GstGioBaseSrc, gst_gio_base_src, GstBaseSrc, - GST_TYPE_BASE_SRC); - -static void gst_gio_base_src_finalize (GObject * object); - -static gboolean gst_gio_base_src_start (GstBaseSrc * base_src); -static gboolean gst_gio_base_src_stop (GstBaseSrc * base_src); -static gboolean gst_gio_base_src_get_size (GstBaseSrc * base_src, - guint64 * size); -static gboolean gst_gio_base_src_is_seekable (GstBaseSrc * base_src); -static gboolean gst_gio_base_src_unlock (GstBaseSrc * base_src); -static gboolean gst_gio_base_src_unlock_stop (GstBaseSrc * base_src); -static gboolean gst_gio_base_src_check_get_range (GstBaseSrc * base_src); -static GstFlowReturn gst_gio_base_src_create (GstBaseSrc * base_src, - guint64 offset, guint size, GstBuffer ** buf); -static gboolean gst_gio_base_src_query (GstBaseSrc * base_src, - GstQuery * query); - -static void -gst_gio_base_src_base_init (gpointer gclass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); - - GST_DEBUG_CATEGORY_INIT (gst_gio_base_src_debug, "gio_base_src", 0, - "GIO base source"); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_factory)); -} - -static void -gst_gio_base_src_class_init (GstGioBaseSrcClass * klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - GstBaseSrcClass *gstbasesrc_class = (GstBaseSrcClass *) klass; - - gobject_class->finalize = gst_gio_base_src_finalize; - - gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_gio_base_src_start); - gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_gio_base_src_stop); - gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_gio_base_src_get_size); - gstbasesrc_class->is_seekable = - GST_DEBUG_FUNCPTR (gst_gio_base_src_is_seekable); - gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_gio_base_src_unlock); - gstbasesrc_class->unlock_stop = - GST_DEBUG_FUNCPTR (gst_gio_base_src_unlock_stop); - gstbasesrc_class->check_get_range = - GST_DEBUG_FUNCPTR (gst_gio_base_src_check_get_range); - gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_gio_base_src_create); - gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_gio_base_src_query); -} - -static void -gst_gio_base_src_init (GstGioBaseSrc * src, GstGioBaseSrcClass * gclass) -{ - src->cancel = g_cancellable_new (); -} - -static void -gst_gio_base_src_finalize (GObject * object) -{ - GstGioBaseSrc *src = GST_GIO_BASE_SRC (object); - - if (src->cancel) { - g_object_unref (src->cancel); - src->cancel = NULL; - } - - if (src->stream) { - g_object_unref (src->stream); - src->stream = NULL; - } - - if (src->cache) { - gst_buffer_unref (src->cache); - src->cache = NULL; - } - - GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); -} - -static gboolean -gst_gio_base_src_start (GstBaseSrc * base_src) -{ - GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src); - GstGioBaseSrcClass *gbsrc_class = GST_GIO_BASE_SRC_GET_CLASS (src); - - src->position = 0; - - /* FIXME: This will likely block */ - src->stream = gbsrc_class->get_stream (src); - if (G_UNLIKELY (!G_IS_INPUT_STREAM (src->stream))) { - GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), - ("No input stream provided by subclass")); - return FALSE; - } else if (G_UNLIKELY (g_input_stream_is_closed (src->stream))) { - GST_ELEMENT_ERROR (src, LIBRARY, FAILED, (NULL), - ("Input stream is already closed")); - return FALSE; - } - - if (G_IS_SEEKABLE (src->stream)) - src->position = g_seekable_tell (G_SEEKABLE (src->stream)); - - GST_DEBUG_OBJECT (src, "started source"); - - return TRUE; -} - -static gboolean -gst_gio_base_src_stop (GstBaseSrc * base_src) -{ - GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src); - GstGioBaseSrcClass *klass = GST_GIO_BASE_SRC_GET_CLASS (src); - gboolean success; - GError *err = NULL; - - if (klass->close_on_stop && G_IS_INPUT_STREAM (src->stream)) { - GST_DEBUG_OBJECT (src, "closing stream"); - - /* FIXME: can block but unfortunately we can't use async operations - * here because they require a running main loop */ - success = g_input_stream_close (src->stream, src->cancel, &err); - - if (!success && !gst_gio_error (src, "g_input_stream_close", &err, NULL)) { - GST_ELEMENT_WARNING (src, RESOURCE, CLOSE, (NULL), - ("g_input_stream_close failed: %s", err->message)); - g_clear_error (&err); - } else if (!success) { - GST_ELEMENT_WARNING (src, RESOURCE, CLOSE, (NULL), - ("g_input_stream_close failed")); - } else { - GST_DEBUG_OBJECT (src, "g_input_stream_close succeeded"); - } - - g_object_unref (src->stream); - src->stream = NULL; - } else { - g_object_unref (src->stream); - src->stream = NULL; - } - - return TRUE; -} - -static gboolean -gst_gio_base_src_get_size (GstBaseSrc * base_src, guint64 * size) -{ - GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src); - - if (G_IS_FILE_INPUT_STREAM (src->stream)) { - GFileInfo *info; - GError *err = NULL; - - info = g_file_input_stream_query_info (G_FILE_INPUT_STREAM (src->stream), - G_FILE_ATTRIBUTE_STANDARD_SIZE, src->cancel, &err); - - if (info != NULL) { - *size = g_file_info_get_size (info); - g_object_unref (info); - GST_DEBUG_OBJECT (src, "found size: %" G_GUINT64_FORMAT, *size); - return TRUE; - } - - if (!gst_gio_error (src, "g_file_input_stream_query_info", &err, NULL)) { - - if (GST_GIO_ERROR_MATCHES (err, NOT_SUPPORTED)) - GST_DEBUG_OBJECT (src, "size information not available"); - else - GST_WARNING_OBJECT (src, "size information retrieval failed: %s", - err->message); - - g_clear_error (&err); - } - } - - if (GST_GIO_STREAM_IS_SEEKABLE (src->stream)) { - goffset old; - goffset stream_size; - gboolean ret; - GSeekable *seekable = G_SEEKABLE (src->stream); - GError *err = NULL; - - old = g_seekable_tell (seekable); - - ret = g_seekable_seek (seekable, 0, G_SEEK_END, src->cancel, &err); - if (!ret) { - if (!gst_gio_error (src, "g_seekable_seek", &err, NULL)) { - if (GST_GIO_ERROR_MATCHES (err, NOT_SUPPORTED)) - GST_DEBUG_OBJECT (src, - "Seeking to the end of stream is not supported"); - else - GST_WARNING_OBJECT (src, "Seeking to end of stream failed: %s", - err->message); - g_clear_error (&err); - } else { - GST_WARNING_OBJECT (src, "Seeking to end of stream failed"); - } - return FALSE; - } - - stream_size = g_seekable_tell (seekable); - - ret = g_seekable_seek (seekable, old, G_SEEK_SET, src->cancel, &err); - if (!ret) { - if (!gst_gio_error (src, "g_seekable_seek", &err, NULL)) { - if (GST_GIO_ERROR_MATCHES (err, NOT_SUPPORTED)) - GST_ERROR_OBJECT (src, "Seeking to the old position not supported"); - else - GST_ERROR_OBJECT (src, "Seeking to the old position failed: %s", - err->message); - g_clear_error (&err); - } else { - GST_ERROR_OBJECT (src, "Seeking to the old position faile"); - } - return FALSE; - } - - if (stream_size >= 0) { - *size = stream_size; - return TRUE; - } - } - - return FALSE; -} - -static gboolean -gst_gio_base_src_is_seekable (GstBaseSrc * base_src) -{ - GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src); - gboolean seekable; - - seekable = GST_GIO_STREAM_IS_SEEKABLE (src->stream); - - GST_DEBUG_OBJECT (src, "can seek: %d", seekable); - - return seekable; -} - -static gboolean -gst_gio_base_src_unlock (GstBaseSrc * base_src) -{ - GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src); - - GST_LOG_OBJECT (src, "triggering cancellation"); - - g_cancellable_cancel (src->cancel); - - return TRUE; -} - -static gboolean -gst_gio_base_src_unlock_stop (GstBaseSrc * base_src) -{ - GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src); - - GST_LOG_OBJECT (src, "resetting cancellable"); - - g_cancellable_reset (src->cancel); - - return TRUE; -} - -static gboolean -gst_gio_base_src_check_get_range (GstBaseSrc * base_src) -{ - return GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_SRC_CLASS, - check_get_range, (base_src), FALSE); -} - -static GstFlowReturn -gst_gio_base_src_create (GstBaseSrc * base_src, guint64 offset, guint size, - GstBuffer ** buf_return) -{ - GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src); - GstBuffer *buf; - GstFlowReturn ret = GST_FLOW_OK; - - g_return_val_if_fail (G_IS_INPUT_STREAM (src->stream), GST_FLOW_ERROR); - - /* If we have the requested part in our cache take a subbuffer of that, - * otherwise fill the cache again with at least 4096 bytes from the - * requested offset and return a subbuffer of that. - * - * We need caching because every read/seek operation will need to go - * over DBus if our backend is GVfs and this is painfully slow. */ - if (src->cache && offset >= GST_BUFFER_OFFSET (src->cache) && - offset + size <= GST_BUFFER_OFFSET_END (src->cache)) { - GST_DEBUG_OBJECT (src, "Creating subbuffer from cached buffer: offset %" - G_GUINT64_FORMAT " length %u", offset, size); - - buf = gst_buffer_create_sub (src->cache, - offset - GST_BUFFER_OFFSET (src->cache), size); - - GST_BUFFER_OFFSET (buf) = offset; - GST_BUFFER_OFFSET_END (buf) = offset + size; - GST_BUFFER_SIZE (buf) = size; - } else { - guint cachesize = MAX (4096, size); - gssize read, res; - gboolean success, eos; - GError *err = NULL; - - if (src->cache) { - gst_buffer_unref (src->cache); - src->cache = NULL; - } - - if (G_UNLIKELY (offset != src->position)) { - if (!GST_GIO_STREAM_IS_SEEKABLE (src->stream)) - return GST_FLOW_NOT_SUPPORTED; - - GST_DEBUG_OBJECT (src, "Seeking to position %" G_GUINT64_FORMAT, offset); - ret = gst_gio_seek (src, G_SEEKABLE (src->stream), offset, src->cancel); - - if (ret == GST_FLOW_OK) - src->position = offset; - else - return ret; - } - - src->cache = gst_buffer_try_new_and_alloc (cachesize); - if (G_UNLIKELY (src->cache == NULL)) { - GST_ERROR_OBJECT (src, "Failed to allocate %u bytes", cachesize); - return GST_FLOW_ERROR; - } - - GST_LOG_OBJECT (src, "Reading %u bytes from offset %" G_GUINT64_FORMAT, - cachesize, offset); - - /* GIO sometimes gives less bytes than requested although - * it's not at the end of file. SMB for example only - * supports reads up to 64k. So we loop here until we get at - * at least the requested amount of bytes or a read returns - * nothing. */ - read = 0; - while (size - read > 0 && (res = - g_input_stream_read (G_INPUT_STREAM (src->stream), - GST_BUFFER_DATA (src->cache) + read, cachesize - read, - src->cancel, &err)) > 0) { - read += res; - } - - success = (read >= 0); - eos = (cachesize > 0 && read == 0); - - if (!success && !gst_gio_error (src, "g_input_stream_read", &err, &ret)) { - GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), - ("Could not read from stream: %s", err->message)); - g_clear_error (&err); - } - - if (success && !eos) { - src->position += read; - GST_BUFFER_SIZE (src->cache) = read; - - GST_BUFFER_OFFSET (src->cache) = offset; - GST_BUFFER_OFFSET_END (src->cache) = offset + read; - - GST_DEBUG_OBJECT (src, "Read successful"); - GST_DEBUG_OBJECT (src, "Creating subbuffer from new " - "cached buffer: offset %" G_GUINT64_FORMAT " length %u", offset, - size); - - buf = gst_buffer_create_sub (src->cache, 0, MIN (size, read)); - - GST_BUFFER_OFFSET (buf) = offset; - GST_BUFFER_OFFSET_END (buf) = offset + MIN (size, read); - GST_BUFFER_SIZE (buf) = MIN (size, read); - } else { - GST_DEBUG_OBJECT (src, "Read not successful"); - gst_buffer_unref (src->cache); - src->cache = NULL; - buf = NULL; - } - - if (eos) - ret = GST_FLOW_UNEXPECTED; - } - - *buf_return = buf; - - return ret; -} - -static gboolean -gst_gio_base_src_query (GstBaseSrc * base_src, GstQuery * query) -{ - gboolean ret = FALSE; - GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_URI: - if (GST_IS_URI_HANDLER (src)) { - const gchar *uri = gst_uri_handler_get_uri (GST_URI_HANDLER (src)); - gst_query_set_uri (query, uri); - ret = TRUE; - } - break; - default: - ret = FALSE; - break; - } - - if (!ret) - ret = GST_BASE_SRC_CLASS (parent_class)->query (base_src, query); - - return ret; -} diff --git a/ext/gio/gstgiobasesrc.h b/ext/gio/gstgiobasesrc.h deleted file mode 100644 index 7a14859c..00000000 --- a/ext/gio/gstgiobasesrc.h +++ /dev/null @@ -1,72 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Rene Stadler <mail@renestadler.de> - * Copyright (C) 2007-2009 Sebastian Dröge <slomo@circular-chaos.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_GIO_BASE_SRC_H__ -#define __GST_GIO_BASE_SRC_H__ - -#include "gstgio.h" - -#include <gst/base/gstbasesrc.h> - -G_BEGIN_DECLS - -#define GST_TYPE_GIO_BASE_SRC \ - (gst_gio_base_src_get_type()) -#define GST_GIO_BASE_SRC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GIO_BASE_SRC,GstGioBaseSrc)) -#define GST_GIO_BASE_SRC_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_GIO_BASE_SRC, GstGioBaseSrcClass)) -#define GST_GIO_BASE_SRC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GIO_BASE_SRC,GstGioBaseSrcClass)) -#define GST_IS_GIO_BASE_SRC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GIO_BASE_SRC)) -#define GST_IS_GIO_BASE_SRC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GIO_BASE_SRC)) - -typedef struct _GstGioBaseSrc GstGioBaseSrc; -typedef struct _GstGioBaseSrcClass GstGioBaseSrcClass; - -struct _GstGioBaseSrc -{ - GstBaseSrc src; - - /* < protected > */ - GCancellable *cancel; - guint64 position; - - /* < private > */ - GInputStream *stream; - GstBuffer *cache; -}; - -struct _GstGioBaseSrcClass -{ - GstBaseSrcClass parent_class; - - GInputStream * (*get_stream) (GstGioBaseSrc *bsrc); - gboolean close_on_stop; -}; - -GType gst_gio_base_src_get_type (void); - -G_END_DECLS - -#endif /* __GST_GIO_BASE_SRC_H__ */ diff --git a/ext/gio/gstgiosink.c b/ext/gio/gstgiosink.c deleted file mode 100644 index 69e05e50..00000000 --- a/ext/gio/gstgiosink.c +++ /dev/null @@ -1,319 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Rene Stadler <mail@renestadler.de> - * Copyright (C) 2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-giosink - * @see_also: #GstFileSink, #GstGnomeVFSSink, #GstGioSrc - * - * This plugin writes incoming data to a local or remote location specified - * by an URI. This location can be specified using any protocol supported by - * the GIO library or it's VFS backends. Common protocols are 'file', 'ftp', - * or 'smb'. - * - * If the URI or #GFile already exists giosink will post a message of - * type %GST_MESSAGE_ELEMENT with name "file-exists" on the bus. The message - * also contains the #GFile and the corresponding URI. - * Applications can use the "file-exists" message to notify the user about - * the problem and to set a different target location or to remove the - * existing file. Note that right after the "file-exists" message a normal - * error message is posted on the bus which should be ignored if "file-exists" - * is handled by the application, for example by calling - * gst_bus_set_flushing(bus, TRUE) after the "file-exists" message was - * received and gst_bus_set_flushing(bus, FALSE) after the problem is - * resolved. - * - * Similar to the "file-exist" message a "not-mounted" message is posted - * on the bus if the target location is not mounted yet and needs to be - * mounted. This message can be used by application to mount the location - * and retry after the location was mounted successfully. - * - * <refsect2> - * <title>Example pipelines</title> - * |[ - * gst-launch -v filesrc location=input.xyz ! giosink location=file:///home/joe/out.xyz - * ]| The above pipeline will simply copy a local file. Instead of giosink, - * we could just as well have used the filesink element here. - * |[ - * gst-launch -v filesrc location=foo.mp3 ! mad ! flacenc ! giosink location=smb://othercomputer/foo.flac - * ]| The above pipeline will re-encode an mp3 file into FLAC format and store - * it on a remote host using the Samba protocol. - * |[ - * gst-launch -v audiotestsrc num-buffers=100 ! vorbisenc ! oggmux ! giosink location=file:///home/foo/bar.ogg - * ]| The above pipeline will encode a 440Hz sine wave to Ogg Vorbis and stores - * it in the home directory of user foo. - * </refsect2> - */ - -/* FIXME: We would like to mount the enclosing volume of an URL - * if it isn't mounted yet but this is possible async-only. - * Unfortunately this requires a running main loop from the - * default context and we can't guarantuee this! - * - * We would also like to do authentication while mounting. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "gstgiosink.h" - -GST_DEBUG_CATEGORY_STATIC (gst_gio_sink_debug); -#define GST_CAT_DEFAULT gst_gio_sink_debug - -/* Filter signals and args */ -enum -{ - LAST_SIGNAL -}; - -enum -{ - PROP_0, - PROP_LOCATION, - PROP_FILE -}; - -GST_BOILERPLATE_FULL (GstGioSink, gst_gio_sink, GstGioBaseSink, - GST_TYPE_GIO_BASE_SINK, gst_gio_uri_handler_do_init); - -static void gst_gio_sink_finalize (GObject * object); -static void gst_gio_sink_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_gio_sink_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static GOutputStream *gst_gio_sink_get_stream (GstGioBaseSink * base_sink); - -static void -gst_gio_sink_base_init (gpointer gclass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); - - GST_DEBUG_CATEGORY_INIT (gst_gio_sink_debug, "gio_sink", 0, "GIO sink"); - - gst_element_class_set_details_simple (element_class, "GIO sink", - "Sink/File", - "Write to any GIO-supported location", - "Ren\xc3\xa9 Stadler <mail@renestadler.de>, " - "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); -} - -static void -gst_gio_sink_class_init (GstGioSinkClass * klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - GstGioBaseSinkClass *gstgiobasesink_class = (GstGioBaseSinkClass *) klass; - - gobject_class->finalize = gst_gio_sink_finalize; - gobject_class->set_property = gst_gio_sink_set_property; - gobject_class->get_property = gst_gio_sink_get_property; - - g_object_class_install_property (gobject_class, PROP_LOCATION, - g_param_spec_string ("location", "Location", "URI location to write to", - NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /** - * GstGioSink:file - * - * %GFile to write to. - * - * Since: 0.10.20 - **/ - g_object_class_install_property (gobject_class, PROP_FILE, - g_param_spec_object ("file", "File", "GFile to write to", - G_TYPE_FILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - gstgiobasesink_class->get_stream = - GST_DEBUG_FUNCPTR (gst_gio_sink_get_stream); - gstgiobasesink_class->close_on_stop = TRUE; -} - -static void -gst_gio_sink_init (GstGioSink * sink, GstGioSinkClass * gclass) -{ -} - -static void -gst_gio_sink_finalize (GObject * object) -{ - GstGioSink *sink = GST_GIO_SINK (object); - - if (sink->file) { - g_object_unref (sink->file); - sink->file = NULL; - } - - GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); -} - -static void -gst_gio_sink_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstGioSink *sink = GST_GIO_SINK (object); - - switch (prop_id) { - case PROP_LOCATION:{ - const gchar *uri = NULL; - - if (GST_STATE (sink) == GST_STATE_PLAYING || - GST_STATE (sink) == GST_STATE_PAUSED) { - GST_WARNING - ("Setting a new location or GFile not supported in PLAYING or PAUSED state"); - break; - } - - GST_OBJECT_LOCK (GST_OBJECT (sink)); - if (sink->file) - g_object_unref (sink->file); - - uri = g_value_get_string (value); - - if (uri) { - sink->file = g_file_new_for_uri (uri); - - if (!sink->file) { - GST_ERROR ("Could not create GFile for URI '%s'", uri); - } - } else { - sink->file = NULL; - } - GST_OBJECT_UNLOCK (GST_OBJECT (sink)); - break; - } - case PROP_FILE: - if (GST_STATE (sink) == GST_STATE_PLAYING || - GST_STATE (sink) == GST_STATE_PAUSED) { - GST_WARNING - ("Setting a new location or GFile not supported in PLAYING or PAUSED state"); - break; - } - - GST_OBJECT_LOCK (GST_OBJECT (sink)); - if (sink->file) - g_object_unref (sink->file); - - sink->file = g_value_dup_object (value); - - GST_OBJECT_UNLOCK (GST_OBJECT (sink)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_gio_sink_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstGioSink *sink = GST_GIO_SINK (object); - - switch (prop_id) { - case PROP_LOCATION:{ - gchar *uri; - - GST_OBJECT_LOCK (GST_OBJECT (sink)); - if (sink->file) { - uri = g_file_get_uri (sink->file); - g_value_set_string (value, uri); - g_free (uri); - } else { - g_value_set_string (value, NULL); - } - GST_OBJECT_UNLOCK (GST_OBJECT (sink)); - break; - } - case PROP_FILE: - GST_OBJECT_LOCK (GST_OBJECT (sink)); - g_value_set_object (value, sink->file); - GST_OBJECT_UNLOCK (GST_OBJECT (sink)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static GOutputStream * -gst_gio_sink_get_stream (GstGioBaseSink * bsink) -{ - GstGioSink *sink = GST_GIO_SINK (bsink); - GOutputStream *stream; - GCancellable *cancel = GST_GIO_BASE_SINK (sink)->cancel; - GError *err = NULL; - gchar *uri; - - if (sink->file == NULL) { - GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL), - ("No location or GFile given")); - return NULL; - } - - uri = g_file_get_uri (sink->file); - if (!uri) - uri = g_strdup ("(null)"); - - stream = - G_OUTPUT_STREAM (g_file_create (sink->file, G_FILE_CREATE_NONE, cancel, - &err)); - - if (!stream) { - if (!gst_gio_error (sink, "g_file_create", &err, NULL)) { - /*if (GST_GIO_ERROR_MATCHES (err, EXISTS)) */ - /* FIXME: Retry with replace if overwrite == TRUE! */ - - if (GST_GIO_ERROR_MATCHES (err, NOT_FOUND)) { - GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, (NULL), - ("Could not open location %s for writing: %s", uri, err->message)); - } else if (GST_GIO_ERROR_MATCHES (err, EXISTS)) { - gst_element_post_message (GST_ELEMENT_CAST (sink), - gst_message_new_element (GST_OBJECT_CAST (sink), - gst_structure_new ("file-exists", "file", G_TYPE_FILE, - sink->file, "uri", G_TYPE_STRING, uri, NULL))); - - GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL), - ("Location %s already exists: %s", uri, err->message)); - } else if (GST_GIO_ERROR_MATCHES (err, NOT_MOUNTED)) { - gst_element_post_message (GST_ELEMENT_CAST (sink), - gst_message_new_element (GST_OBJECT_CAST (sink), - gst_structure_new ("not-mounted", "file", G_TYPE_FILE, - sink->file, "uri", G_TYPE_STRING, uri, NULL))); - - GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL), - ("Location %s not mounted: %s", uri, err->message)); - } else { - GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL), - ("Could not open location %s for writing: %s", uri, err->message)); - } - - g_clear_error (&err); - } - g_free (uri); - return NULL; - } - - GST_DEBUG_OBJECT (sink, "opened location %s", uri); - - g_free (uri); - - return stream; -} diff --git a/ext/gio/gstgiosink.h b/ext/gio/gstgiosink.h deleted file mode 100644 index 494d5db3..00000000 --- a/ext/gio/gstgiosink.h +++ /dev/null @@ -1,68 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Rene Stadler <mail@renestadler.de> - * Copyright (C) 2007-2009 Sebastian Dröge <slomo@circular-chaos.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_GIO_SINK_H__ -#define __GST_GIO_SINK_H__ - -#include "gstgio.h" -#include "gstgiobasesink.h" - -#include <gst/base/gstbasesink.h> - -G_BEGIN_DECLS - -#define GST_TYPE_GIO_SINK \ - (gst_gio_sink_get_type()) -#define GST_GIO_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GIO_SINK,GstGioSink)) -#define GST_GIO_SINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GIO_SINK,GstGioSinkClass)) -#define GST_IS_GIO_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GIO_SINK)) -#define GST_IS_GIO_SINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GIO_SINK)) - -typedef struct _GstGioSink GstGioSink; -typedef struct _GstGioSinkClass GstGioSinkClass; - -/** - * GstGioSink: - * - * Opaque data structure. - */ -struct _GstGioSink -{ - GstGioBaseSink sink; - - /*< private >*/ - GFile *file; -}; - -struct _GstGioSinkClass -{ - GstGioBaseSinkClass parent_class; -}; - -GType gst_gio_sink_get_type (void); - -G_END_DECLS - -#endif /* __GST_GIO_SINK_H__ */ diff --git a/ext/gio/gstgiosrc.c b/ext/gio/gstgiosrc.c deleted file mode 100644 index 5fd810e7..00000000 --- a/ext/gio/gstgiosrc.c +++ /dev/null @@ -1,339 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Rene Stadler <mail@renestadler.de> - * Copyright (C) 2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-giosrc - * @see_also: #GstFileSrc, #GstGnomeVFSSrc, #GstGioSink - * - * This plugin reads data from a local or remote location specified - * by an URI. This location can be specified using any protocol supported by - * the GIO library or it's VFS backends. Common protocols are 'file', 'http', - * 'ftp', or 'smb'. - * - * If an URI or #GFile is not mounted giosrc will post a message of type - * %GST_MESSAGE_ELEMENT with name "not-mounted" on the bus. The message - * also contains the #GFile and the corresponding URI. - * Applications can use the "not-mounted" message to mount the #GFile - * by calling g_file_mount_enclosing_volume() and then restart the - * pipeline after the mounting has succeeded. Note that right after the - * "not-mounted" message a normal error message is posted on the bus which - * should be ignored if "not-mounted" is handled by the application, for - * example by calling gst_bus_set_flushing(bus, TRUE) after the "not-mounted" - * message was received and gst_bus_set_flushing(bus, FALSE) after the - * mounting was successful. - * - * <refsect2> - * <title>Example launch lines</title> - * |[ - * gst-launch -v giosrc location=file:///home/joe/foo.xyz ! fakesink - * ]| The above pipeline will simply read a local file and do nothing with the - * data read. Instead of giosrc, we could just as well have used the - * filesrc element here. - * |[ - * gst-launch -v giosrc location=smb://othercomputer/foo.xyz ! filesink location=/home/joe/foo.xyz - * ]| The above pipeline will copy a file from a remote host to the local file - * system using the Samba protocol. - * |[ - * gst-launch -v giosrc location=http://music.foobar.com/demo.mp3 ! mad ! audioconvert ! audioresample ! alsasink - * ]| The above pipeline will read and decode and play an mp3 file from a - * web server using the http protocol. - * </refsect2> - */ - -/* FIXME: We would like to mount the enclosing volume of an URL - * if it isn't mounted yet but this is possible async-only. - * Unfortunately this requires a running main loop from the - * default context and we can't guarantuee this! - * - * We would also like to do authentication while mounting. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "gstgiosrc.h" -#include <string.h> - -GST_DEBUG_CATEGORY_STATIC (gst_gio_src_debug); -#define GST_CAT_DEFAULT gst_gio_src_debug - -enum -{ - PROP_0, - PROP_LOCATION, - PROP_FILE -}; - -GST_BOILERPLATE_FULL (GstGioSrc, gst_gio_src, GstGioBaseSrc, - GST_TYPE_GIO_BASE_SRC, gst_gio_uri_handler_do_init); - -static void gst_gio_src_finalize (GObject * object); - -static void gst_gio_src_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_gio_src_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static GInputStream *gst_gio_src_get_stream (GstGioBaseSrc * bsrc); - -static gboolean gst_gio_src_check_get_range (GstBaseSrc * base_src); - -static void -gst_gio_src_base_init (gpointer gclass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); - - GST_DEBUG_CATEGORY_INIT (gst_gio_src_debug, "gio_src", 0, "GIO source"); - - gst_element_class_set_details_simple (element_class, "GIO source", - "Source/File", - "Read from any GIO-supported location", - "Ren\xc3\xa9 Stadler <mail@renestadler.de>, " - "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); -} - -static void -gst_gio_src_class_init (GstGioSrcClass * klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - GstBaseSrcClass *gstbasesrc_class = (GstBaseSrcClass *) klass; - GstGioBaseSrcClass *gstgiobasesrc_class = (GstGioBaseSrcClass *) klass; - - gobject_class->finalize = gst_gio_src_finalize; - gobject_class->set_property = gst_gio_src_set_property; - gobject_class->get_property = gst_gio_src_get_property; - - g_object_class_install_property (gobject_class, PROP_LOCATION, - g_param_spec_string ("location", "Location", "URI location to read from", - NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /** - * GstGioSrc:file - * - * %GFile to read from. - * - * Since: 0.10.20 - **/ - g_object_class_install_property (gobject_class, PROP_FILE, - g_param_spec_object ("file", "File", "GFile to read from", - G_TYPE_FILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - gstbasesrc_class->check_get_range = - GST_DEBUG_FUNCPTR (gst_gio_src_check_get_range); - - gstgiobasesrc_class->get_stream = GST_DEBUG_FUNCPTR (gst_gio_src_get_stream); - gstgiobasesrc_class->close_on_stop = TRUE; -} - -static void -gst_gio_src_init (GstGioSrc * src, GstGioSrcClass * gclass) -{ -} - -static void -gst_gio_src_finalize (GObject * object) -{ - GstGioSrc *src = GST_GIO_SRC (object); - - if (src->file) { - g_object_unref (src->file); - src->file = NULL; - } - - GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); -} - -static void -gst_gio_src_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstGioSrc *src = GST_GIO_SRC (object); - - switch (prop_id) { - case PROP_LOCATION:{ - const gchar *uri = NULL; - - if (GST_STATE (src) == GST_STATE_PLAYING || - GST_STATE (src) == GST_STATE_PAUSED) { - GST_WARNING - ("Setting a new location or GFile not supported in PLAYING or PAUSED state"); - break; - } - - GST_OBJECT_LOCK (GST_OBJECT (src)); - if (src->file) - g_object_unref (src->file); - - uri = g_value_get_string (value); - - if (uri) { - src->file = g_file_new_for_uri (uri); - - if (!src->file) { - GST_ERROR ("Could not create GFile for URI '%s'", uri); - } - } else { - src->file = NULL; - } - GST_OBJECT_UNLOCK (GST_OBJECT (src)); - break; - } - case PROP_FILE: - if (GST_STATE (src) == GST_STATE_PLAYING || - GST_STATE (src) == GST_STATE_PAUSED) { - GST_WARNING - ("Setting a new location or GFile not supported in PLAYING or PAUSED state"); - break; - } - - GST_OBJECT_LOCK (GST_OBJECT (src)); - if (src->file) - g_object_unref (src->file); - - src->file = g_value_dup_object (value); - - GST_OBJECT_UNLOCK (GST_OBJECT (src)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_gio_src_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstGioSrc *src = GST_GIO_SRC (object); - - switch (prop_id) { - case PROP_LOCATION:{ - gchar *uri; - - GST_OBJECT_LOCK (GST_OBJECT (src)); - if (src->file) { - uri = g_file_get_uri (src->file); - g_value_set_string (value, uri); - g_free (uri); - } else { - g_value_set_string (value, NULL); - } - GST_OBJECT_UNLOCK (GST_OBJECT (src)); - break; - } - case PROP_FILE: - GST_OBJECT_LOCK (GST_OBJECT (src)); - g_value_set_object (value, src->file); - GST_OBJECT_UNLOCK (GST_OBJECT (src)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static gboolean -gst_gio_src_check_get_range (GstBaseSrc * base_src) -{ - GstGioSrc *src = GST_GIO_SRC (base_src); - gchar *scheme; - - if (src->file == NULL) - goto done; - - scheme = g_file_get_uri_scheme (src->file); - if (scheme == NULL) - goto done; - - if (strcmp (scheme, "file") == 0) { - GST_LOG_OBJECT (src, "local URI, assuming random access is possible"); - g_free (scheme); - return TRUE; - } else if (strcmp (scheme, "http") == 0 || strcmp (scheme, "https") == 0) { - GST_LOG_OBJECT (src, "blacklisted protocol '%s', " - "no random access possible", scheme); - g_free (scheme); - return FALSE; - } - - g_free (scheme); - -done: - - GST_DEBUG_OBJECT (src, "undecided about random access, asking base class"); - - return GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_SRC_CLASS, - check_get_range, (base_src), FALSE); -} - - -static GInputStream * -gst_gio_src_get_stream (GstGioBaseSrc * bsrc) -{ - GstGioSrc *src = GST_GIO_SRC (bsrc); - GError *err = NULL; - GInputStream *stream; - GCancellable *cancel = bsrc->cancel; - gchar *uri = NULL; - - if (src->file == NULL) { - GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), - ("No location or GFile given")); - return NULL; - } - - uri = g_file_get_uri (src->file); - if (!uri) - uri = g_strdup ("(null)"); - - stream = G_INPUT_STREAM (g_file_read (src->file, cancel, &err)); - - if (stream == NULL && !gst_gio_error (src, "g_file_read", &err, NULL)) { - if (GST_GIO_ERROR_MATCHES (err, NOT_FOUND)) { - GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL), - ("Could not open location %s for reading: %s", uri, err->message)); - } else if (GST_GIO_ERROR_MATCHES (err, NOT_MOUNTED)) { - gst_element_post_message (GST_ELEMENT_CAST (src), - gst_message_new_element (GST_OBJECT_CAST (src), - gst_structure_new ("not-mounted", "file", G_TYPE_FILE, src->file, - "uri", G_TYPE_STRING, uri, NULL))); - - GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), - ("Location %s not mounted: %s", uri, err->message)); - } else { - GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), - ("Could not open location %s for reading: %s", uri, err->message)); - } - - g_free (uri); - g_clear_error (&err); - return NULL; - } else if (stream == NULL) { - g_free (uri); - return NULL; - } - - GST_DEBUG_OBJECT (src, "opened location %s", uri); - g_free (uri); - - return stream; -} diff --git a/ext/gio/gstgiosrc.h b/ext/gio/gstgiosrc.h deleted file mode 100644 index 080d3d8f..00000000 --- a/ext/gio/gstgiosrc.h +++ /dev/null @@ -1,68 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Rene Stadler <mail@renestadler.de> - * Copyright (C) 2007-2009 Sebastian Dröge <slomo@circular-chaos.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_GIO_SRC_H__ -#define __GST_GIO_SRC_H__ - -#include "gstgio.h" -#include "gstgiobasesrc.h" - -#include <gst/base/gstbasesrc.h> - -G_BEGIN_DECLS - -#define GST_TYPE_GIO_SRC \ - (gst_gio_src_get_type()) -#define GST_GIO_SRC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GIO_SRC,GstGioSrc)) -#define GST_GIO_SRC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GIO_SRC,GstGioSrcClass)) -#define GST_IS_GIO_SRC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GIO_SRC)) -#define GST_IS_GIO_SRC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GIO_SRC)) - -typedef struct _GstGioSrc GstGioSrc; -typedef struct _GstGioSrcClass GstGioSrcClass; - -/** - * GstGioSrc: - * - * Opaque data structure. - */ -struct _GstGioSrc -{ - GstGioBaseSrc src; - - /*< private >*/ - GFile *file; -}; - -struct _GstGioSrcClass -{ - GstGioBaseSrcClass parent_class; -}; - -GType gst_gio_src_get_type (void); - -G_END_DECLS - -#endif /* __GST_GIO_SRC_H__ */ diff --git a/ext/gio/gstgiostreamsink.c b/ext/gio/gstgiostreamsink.c deleted file mode 100644 index 02dac71f..00000000 --- a/ext/gio/gstgiostreamsink.c +++ /dev/null @@ -1,198 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Rene Stadler <mail@renestadler.de> - * Copyright (C) 2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-giostreamsink - * - * This plugin writes incoming data to a custom GIO #GOutputStream. - * - * It can, for example, be used to write a stream to memory with a - * #GMemoryOuputStream or to write to a file with a #GFileOuputStream. - * - * <refsect2> - * <title>Example code</title> - * <para> - * The following example writes the received data to a #GMemoryOutputStream. - * |[ - -#include <gst/gst.h> -#include <gio/gio.h> - -... - -GstElement *sink; -GMemoryOuputStream *stream; -// out_data will contain the received data -guint8 *out_data; - -... - -stream = G_MEMORY_OUTPUT_STREAM (g_memory_output_stream_new (NULL, 0, - (GReallocFunc) g_realloc, (GDestroyNotify) g_free)); -sink = gst_element_factory_make ("giostreamsink", "sink"); -g_object_set (G_OBJECT (sink), "stream", stream, NULL); - -... - -// after processing get the written data -out_data = g_memory_ouput_stream_get_data (G_MEMORY_OUTPUT_STREAM (stream)); - -... - - * ]| - * </para> - * </refsect2> - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "gstgiostreamsink.h" - -GST_DEBUG_CATEGORY_STATIC (gst_gio_stream_sink_debug); -#define GST_CAT_DEFAULT gst_gio_stream_sink_debug - -/* Filter signals and args */ -enum -{ - LAST_SIGNAL -}; - -enum -{ - PROP_0, - PROP_STREAM -}; - -GST_BOILERPLATE (GstGioStreamSink, gst_gio_stream_sink, GstGioBaseSink, - GST_TYPE_GIO_BASE_SINK); - -static void gst_gio_stream_sink_finalize (GObject * object); -static void gst_gio_stream_sink_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_gio_stream_sink_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static GOutputStream *gst_gio_stream_sink_get_stream (GstGioBaseSink * bsink); - -static void -gst_gio_stream_sink_base_init (gpointer gclass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); - - GST_DEBUG_CATEGORY_INIT (gst_gio_stream_sink_debug, "gio_stream_sink", 0, - "GIO stream sink"); - - gst_element_class_set_details_simple (element_class, "GIO stream sink", - "Sink", - "Write to any GIO stream", - "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); -} - -static void -gst_gio_stream_sink_class_init (GstGioStreamSinkClass * klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - GstGioBaseSinkClass *ggbsink_class = (GstGioBaseSinkClass *) klass; - - gobject_class->finalize = gst_gio_stream_sink_finalize; - gobject_class->set_property = gst_gio_stream_sink_set_property; - gobject_class->get_property = gst_gio_stream_sink_get_property; - - g_object_class_install_property (gobject_class, PROP_STREAM, - g_param_spec_object ("stream", "Stream", "Stream to write to", - G_TYPE_OUTPUT_STREAM, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - ggbsink_class->get_stream = - GST_DEBUG_FUNCPTR (gst_gio_stream_sink_get_stream); -} - -static void -gst_gio_stream_sink_init (GstGioStreamSink * sink, - GstGioStreamSinkClass * gclass) -{ -} - -static void -gst_gio_stream_sink_finalize (GObject * object) -{ - GstGioStreamSink *sink = GST_GIO_STREAM_SINK (object); - - if (sink->stream) { - g_object_unref (sink->stream); - sink->stream = NULL; - } - - GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); -} - -static void -gst_gio_stream_sink_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstGioStreamSink *sink = GST_GIO_STREAM_SINK (object); - - switch (prop_id) { - case PROP_STREAM:{ - GObject *stream; - - if (GST_STATE (sink) == GST_STATE_PLAYING || - GST_STATE (sink) == GST_STATE_PAUSED) { - GST_WARNING - ("Setting a new stream not supported in PLAYING or PAUSED state"); - break; - } - - stream = g_value_dup_object (value); - if (sink->stream) - g_object_unref (sink->stream); - sink->stream = G_OUTPUT_STREAM (stream); - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_gio_stream_sink_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstGioStreamSink *sink = GST_GIO_STREAM_SINK (object); - - switch (prop_id) { - case PROP_STREAM: - g_value_set_object (value, GST_GIO_BASE_SINK (sink)->stream); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static GOutputStream * -gst_gio_stream_sink_get_stream (GstGioBaseSink * bsink) -{ - GstGioStreamSink *sink = GST_GIO_STREAM_SINK (bsink); - - return (sink->stream) ? g_object_ref (sink->stream) : NULL; -} diff --git a/ext/gio/gstgiostreamsink.h b/ext/gio/gstgiostreamsink.h deleted file mode 100644 index 5e6206b5..00000000 --- a/ext/gio/gstgiostreamsink.h +++ /dev/null @@ -1,68 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Rene Stadler <mail@renestadler.de> - * Copyright (C) 2007-2009 Sebastian Dröge <slomo@circular-chaos.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_GIO_STREAM_SINK_H__ -#define __GST_GIO_STREAM_SINK_H__ - -#include "gstgio.h" -#include "gstgiobasesink.h" - -#include <gst/base/gstbasesink.h> - -G_BEGIN_DECLS - -#define GST_TYPE_GIO_STREAM_SINK \ - (gst_gio_stream_sink_get_type()) -#define GST_GIO_STREAM_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GIO_STREAM_SINK,GstGioStreamSink)) -#define GST_GIO_STREAM_SINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GIO_STREAM_SINK,GstGioStreamSinkClass)) -#define GST_IS_GIO_STREAM_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GIO_STREAM_SINK)) -#define GST_IS_GIO_STREAM_SINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GIO_STREAM_SINK)) - -typedef struct _GstGioStreamSink GstGioStreamSink; -typedef struct _GstGioStreamSinkClass GstGioStreamSinkClass; - -/** - * GstGioStreamSink: - * - * Opaque data structure. - */ -struct _GstGioStreamSink -{ - GstGioBaseSink sink; - - /* < private > */ - GOutputStream *stream; -}; - -struct _GstGioStreamSinkClass -{ - GstGioBaseSinkClass parent_class; -}; - -GType gst_gio_stream_sink_get_type (void); - -G_END_DECLS - -#endif /* __GST_GIO_STREAM_SINK_H__ */ diff --git a/ext/gio/gstgiostreamsrc.c b/ext/gio/gstgiostreamsrc.c deleted file mode 100644 index b031de6b..00000000 --- a/ext/gio/gstgiostreamsrc.c +++ /dev/null @@ -1,191 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Rene Stadler <mail@renestadler.de> - * Copyright (C) 2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-giostreamsrc - * - * This plugin reads data from a custom GIO #GInputStream. - * - * It can, for example, be used to read data from memory with a - * #GMemoryInputStream or to read from a file with a - * #GFileInputStream. - * - * <refsect2> - * <title>Example code</title> - * <para> - * The following example reads data from a #GMemoryOutputStream. - * |[ - -#include <gst/gst.h> -#include <gio/gio.h> - -... - -GstElement *src; -GMemoryInputStream *stream; -// in_data will contain the data to send -guint8 *in_data; -gint i; - -... -in_data = g_new (guint8, 512); -for (i = 0; i < 512; i++) - in_data[i] = i % 256; - -stream = G_MEMORY_INPUT_STREAM (g_memory_input_stream_new_from_data (in_data, 512, - (GDestroyNotify) g_free)); -src = gst_element_factory_make ("giostreamsrc", "src"); -g_object_set (G_OBJECT (src), "stream", stream, NULL); - -... - - * ]| - * </para> - * </refsect2> - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "gstgiostreamsrc.h" - -GST_DEBUG_CATEGORY_STATIC (gst_gio_stream_src_debug); -#define GST_CAT_DEFAULT gst_gio_stream_src_debug - -enum -{ - PROP_0, - PROP_STREAM -}; - -GST_BOILERPLATE (GstGioStreamSrc, gst_gio_stream_src, GstGioBaseSrc, - GST_TYPE_GIO_BASE_SRC); - -static void gst_gio_stream_src_finalize (GObject * object); -static void gst_gio_stream_src_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_gio_stream_src_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static GInputStream *gst_gio_stream_src_get_stream (GstGioBaseSrc * bsrc); - -static void -gst_gio_stream_src_base_init (gpointer gclass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); - - GST_DEBUG_CATEGORY_INIT (gst_gio_stream_src_debug, "gio_stream_src", 0, - "GIO source"); - - gst_element_class_set_details_simple (element_class, "GIO stream source", - "Source", - "Read from any GIO stream", - "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); -} - -static void -gst_gio_stream_src_class_init (GstGioStreamSrcClass * klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - GstGioBaseSrcClass *gstgiobasesrc_class = (GstGioBaseSrcClass *) klass; - - gobject_class->finalize = gst_gio_stream_src_finalize; - gobject_class->set_property = gst_gio_stream_src_set_property; - gobject_class->get_property = gst_gio_stream_src_get_property; - - g_object_class_install_property (gobject_class, PROP_STREAM, - g_param_spec_object ("stream", "Stream", "Stream to read from", - G_TYPE_INPUT_STREAM, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - gstgiobasesrc_class->get_stream = - GST_DEBUG_FUNCPTR (gst_gio_stream_src_get_stream); -} - -static void -gst_gio_stream_src_init (GstGioStreamSrc * src, GstGioStreamSrcClass * gclass) -{ -} - -static void -gst_gio_stream_src_finalize (GObject * object) -{ - GstGioStreamSrc *src = GST_GIO_STREAM_SRC (object); - - if (src->stream) { - g_object_unref (src->stream); - src->stream = NULL; - } - - GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); -} - -static void -gst_gio_stream_src_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstGioStreamSrc *src = GST_GIO_STREAM_SRC (object); - - switch (prop_id) { - case PROP_STREAM:{ - GObject *stream; - - if (GST_STATE (src) == GST_STATE_PLAYING || - GST_STATE (src) == GST_STATE_PAUSED) { - GST_WARNING - ("Setting a new stream not supported in PLAYING or PAUSED state"); - break; - } - - stream = g_value_dup_object (value); - if (src->stream) - g_object_unref (src->stream); - src->stream = G_INPUT_STREAM (stream); - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_gio_stream_src_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstGioStreamSrc *src = GST_GIO_STREAM_SRC (object); - - switch (prop_id) { - case PROP_STREAM: - g_value_set_object (value, src->stream); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static GInputStream * -gst_gio_stream_src_get_stream (GstGioBaseSrc * bsrc) -{ - GstGioStreamSrc *src = GST_GIO_STREAM_SRC (bsrc); - - return (src->stream) ? g_object_ref (src->stream) : NULL; -} diff --git a/ext/gio/gstgiostreamsrc.h b/ext/gio/gstgiostreamsrc.h deleted file mode 100644 index 975a2775..00000000 --- a/ext/gio/gstgiostreamsrc.h +++ /dev/null @@ -1,68 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Rene Stadler <mail@renestadler.de> - * Copyright (C) 2007-2009 Sebastian Dröge <slomo@circular-chaos.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_GIO_STREAM_SRC_H__ -#define __GST_GIO_STREAM_SRC_H__ - -#include "gstgio.h" -#include "gstgiobasesrc.h" - -#include <gst/base/gstbasesrc.h> - -G_BEGIN_DECLS - -#define GST_TYPE_GIO_STREAM_SRC \ - (gst_gio_stream_src_get_type()) -#define GST_GIO_STREAM_SRC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GIO_STREAM_SRC,GstGioStreamSrc)) -#define GST_GIO_STREAM_SRC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GIO_STREAM_SRC,GstGioStreamSrcClass)) -#define GST_IS_GIO_STREAM_SRC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GIO_STREAM_SRC)) -#define GST_IS_GIO_STREAM_SRC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GIO_STREAM_SRC)) - -typedef struct _GstGioStreamSrc GstGioStreamSrc; -typedef struct _GstGioStreamSrcClass GstGioStreamSrcClass; - -/** - * GstGioStreamSrc: - * - * Opaque data structure. - */ -struct _GstGioStreamSrc -{ - GstGioBaseSrc src; - - /* < private > */ - GInputStream *stream; -}; - -struct _GstGioStreamSrcClass -{ - GstGioBaseSrcClass parent_class; -}; - -GType gst_gio_stream_src_get_type (void); - -G_END_DECLS - -#endif /* __GST_GIO_STREAM_SRC_H__ */ diff --git a/ext/gnomevfs/Makefile.am b/ext/gnomevfs/Makefile.am deleted file mode 100644 index f87a94b3..00000000 --- a/ext/gnomevfs/Makefile.am +++ /dev/null @@ -1,21 +0,0 @@ -plugin_LTLIBRARIES = libgstgnomevfs.la - -libgstgnomevfs_la_SOURCES = \ - gstgnomevfs.c \ - gstgnomevfssink.c \ - gstgnomevfssrc.c \ - gstgnomevfsuri.c - -libgstgnomevfs_la_CFLAGS = \ - $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(GNOME_VFS_CFLAGS) -libgstgnomevfs_la_LIBADD = \ - $(top_builddir)/gst-libs/gst/tag/libgsttag-$(GST_MAJORMINOR).la \ - $(GST_BASE_LIBS) $(GNOME_VFS_LIBS) -libgstgnomevfs_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstgnomevfs_la_LIBTOOLFLAGS = --tag=disable-static - -noinst_HEADERS = \ - gstgnomevfs.h \ - gstgnomevfssink.h \ - gstgnomevfssrc.h \ - gstgnomevfsuri.h diff --git a/ext/gnomevfs/gstgnomevfs.c b/ext/gnomevfs/gstgnomevfs.c deleted file mode 100644 index 747f1cf0..00000000 --- a/ext/gnomevfs/gstgnomevfs.c +++ /dev/null @@ -1,143 +0,0 @@ -/* GStreamer - * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de> - * - * gnomevfs.c: register gnomevfs elements - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "gst/gst-i18n-plugin.h" - -#include "gstgnomevfs.h" -#include "gstgnomevfssrc.h" -#include "gstgnomevfssink.h" - -#include <libgnomevfs/gnome-vfs.h> -#include <gst/gst.h> - -#include <string.h> - -gchar * -gst_gnome_vfs_location_to_uri_string (const gchar * location) -{ - gchar *newloc, *ret; - - if (location == NULL) - return NULL; - - /* already an URI string? */ - if (strstr (location, "://")) - return g_strdup (location); - - newloc = gnome_vfs_escape_path_string (location); - - if (newloc && *newloc == '/') { - ret = g_strdup_printf ("file://%s", newloc); - } else { - gchar *curdir; - - curdir = g_get_current_dir (); - ret = g_strdup_printf ("file://%s/%s", curdir, newloc); - g_free (curdir); - } - - g_free (newloc); - return ret; -} - -GType -gst_gnome_vfs_uri_get_type (void) -{ - static GType type; /* 0 */ - - if (type == 0) { - type = g_boxed_type_register_static ("GnomeVFSURI", - (GBoxedCopyFunc) gnome_vfs_uri_ref, - (GBoxedFreeFunc) gnome_vfs_uri_unref); - } - - return type; -} - -static gpointer -gst_gnome_vfs_handle_copy (gpointer handle) -{ - return handle; -} - -static void -gst_gnome_vfs_handle_free (gpointer handle) -{ - return; -} - -GType -gst_gnome_vfs_handle_get_type (void) -{ - static GType type; /* 0 */ - - if (type == 0) { - /* hackish, but makes it show up nicely in gst-inspect */ - type = g_boxed_type_register_static ("GnomeVFSHandle", - (GBoxedCopyFunc) gst_gnome_vfs_handle_copy, - (GBoxedFreeFunc) gst_gnome_vfs_handle_free); - } - - return type; -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - /* gnome vfs engine init */ - if (!gnome_vfs_initialized ()) { - if (!gnome_vfs_init ()) { - GST_WARNING ("Failed to initialize GnomeVFS - not registering plugin!"); - return FALSE; - } - } - - gst_plugin_add_dependency_simple (plugin, NULL, GNOME_VFS_MODULES_DIR, NULL, - GST_PLUGIN_DEPENDENCY_FLAG_NONE); - - if (!gst_element_register (plugin, "gnomevfssrc", GST_RANK_SECONDARY, - gst_gnome_vfs_src_get_type ())) - return FALSE; - - if (!gst_element_register (plugin, "gnomevfssink", GST_RANK_SECONDARY, - gst_gnome_vfs_sink_get_type ())) - return FALSE; - -#ifdef ENABLE_NLS -/* FIXME: add category - GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE, LOCALEDIR); - */ - bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); -#endif /* ENABLE_NLS */ - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "gnomevfs", - "elements to read from and write to Gnome-VFS uri's", - plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/gnomevfs/gstgnomevfs.h b/ext/gnomevfs/gstgnomevfs.h deleted file mode 100644 index f2228bea..00000000 --- a/ext/gnomevfs/gstgnomevfs.h +++ /dev/null @@ -1,38 +0,0 @@ -/* GStreamer - * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_GNOME_VFS_H__ -#define __GST_GNOME_VFS_H__ - -#include <glib-object.h> - -G_BEGIN_DECLS - -#define GST_TYPE_GNOME_VFS_URI (gst_gnome_vfs_uri_get_type ()) -#define GST_TYPE_GNOME_VFS_HANDLE (gst_gnome_vfs_handle_get_type ()) - -GType gst_gnome_vfs_uri_get_type (void); -GType gst_gnome_vfs_handle_get_type (void); - -gchar * gst_gnome_vfs_location_to_uri_string (const gchar * location); - -G_END_DECLS - -#endif /* __GST_GNOME_VFS_H__ */ diff --git a/ext/gnomevfs/gstgnomevfssink.c b/ext/gnomevfs/gstgnomevfssink.c deleted file mode 100644 index 431c9fda..00000000 --- a/ext/gnomevfs/gstgnomevfssink.c +++ /dev/null @@ -1,632 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * 2000 Wim Taymans <wtay@chello.be> - * 2001 Bastien Nocera <hadess@hadess.net> - * 2003 Colin Walters <walters@verbum.org> - * 2005 Tim-Philipp Müller <tim centricular net> - * - * gstgnomevfssink.c: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-gnomevfssink - * @see_also: #GstFileSink, #GstGnomeVFSSrc - * - * This plugin writes incoming data to a local or remote location specified - * by an URI. This location can be specified using any protocol supported by - * the GnomeVFS library. Common protocols are 'file', 'ftp', or 'smb'. - * - * Applications can connect to the #GstGnomeVFSSink::allow-overwrite signal to - * receive a callback when an existing file will be overwritten. The return - * value of the signal will determine if gnomevfssink will overwrite the - * resource or abort with an error. - * - * <refsect2> - * <title>Example launch lines</title> - * |[ - * gst-launch -v filesrc location=input.xyz ! gnomevfssink location=file:///home/joe/out.xyz - * ]| The above pipeline will simply copy a local file. Instead of gnomevfssink, - * we could just as well have used the filesink element here. - * |[ - * gst-launch -v filesrc location=foo.mp3 ! mad ! flacenc ! gnomevfssink location=smb://othercomputer/foo.flac - * ]| The above pipeline will re-encode an mp3 file into FLAC format and store - * it on a remote host using the Samba protocol. - * </refsect2> - * - * Last reviewed on 2006-02-28 (0.10.4) - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstgnomevfssink.h" - -#include "gst/gst-i18n-plugin.h" - -#include <gst/gst.h> -#include <libgnomevfs/gnome-vfs.h> -#include <string.h> -#include <errno.h> - -static const GstElementDetails gst_gnome_vfs_sink_details = -GST_ELEMENT_DETAILS ("GnomeVFS Sink", - "Sink/File", - "Write a stream to a GnomeVFS URI", - "Bastien Nocera <hadess@hadess.net>"); - -enum -{ - SIGNAL_ERASE_ASK, - LAST_SIGNAL -}; - -enum -{ - ARG_0, - ARG_LOCATION, - ARG_URI, - ARG_HANDLE -}; - -static void gst_gnome_vfs_sink_finalize (GObject * obj); - -static void gst_gnome_vfs_sink_uri_handler_init (gpointer g_iface, - gpointer iface_data); - -static void gst_gnome_vfs_sink_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_gnome_vfs_sink_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static gboolean gst_gnome_vfs_sink_open_file (GstGnomeVFSSink * sink); -static void gst_gnome_vfs_sink_close_file (GstGnomeVFSSink * sink); -static gboolean gst_gnome_vfs_sink_start (GstBaseSink * basesink); -static gboolean gst_gnome_vfs_sink_stop (GstBaseSink * basesink); -static GstFlowReturn gst_gnome_vfs_sink_render (GstBaseSink * basesink, - GstBuffer * buffer); -static gboolean gst_gnome_vfs_sink_handle_event (GstBaseSink * basesink, - GstEvent * event); -static gboolean gst_gnome_vfs_sink_query (GstPad * pad, GstQuery * query); - -static guint gst_gnome_vfs_sink_signals[LAST_SIGNAL]; /* all 0 */ - -static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -GST_DEBUG_CATEGORY_STATIC (gst_gnome_vfs_sink_debug); -#define GST_CAT_DEFAULT gst_gnome_vfs_sink_debug - -static void -gst_gnome_vfs_sink_do_init (GType type) -{ - static const GInterfaceInfo urihandler_info = { - gst_gnome_vfs_sink_uri_handler_init, - NULL, - NULL - }; - - g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info); - - GST_DEBUG_CATEGORY_INIT (gst_gnome_vfs_sink_debug, "gnomevfssink", 0, - "Gnome VFS sink element"); -} - -GST_BOILERPLATE_FULL (GstGnomeVFSSink, gst_gnome_vfs_sink, GstBaseSink, - GST_TYPE_BASE_SINK, gst_gnome_vfs_sink_do_init); - -static void -gst_gnome_vfs_sink_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sinktemplate)); - - gst_element_class_set_details (element_class, &gst_gnome_vfs_sink_details); -} - -static gboolean -_gst_boolean_allow_overwrite_accumulator (GSignalInvocationHint * ihint, - GValue * return_accu, const GValue * handler_return, gpointer dummy) -{ - gboolean allow_overwrite; - - allow_overwrite = g_value_get_boolean (handler_return); - if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP)) - g_value_set_boolean (return_accu, allow_overwrite); - - /* stop emission if signal doesn't allow overwriting */ - return allow_overwrite; -} - -static void -gst_gnome_vfs_sink_class_init (GstGnomeVFSSinkClass * klass) -{ - GstBaseSinkClass *basesink_class; - GObjectClass *gobject_class; - - gobject_class = (GObjectClass *) klass; - basesink_class = (GstBaseSinkClass *) klass; - - gobject_class->set_property = gst_gnome_vfs_sink_set_property; - gobject_class->get_property = gst_gnome_vfs_sink_get_property; - gobject_class->finalize = gst_gnome_vfs_sink_finalize; - - g_object_class_install_property (gobject_class, ARG_LOCATION, - g_param_spec_string ("location", "File Location", - "Location of the file to write", NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_URI, - g_param_spec_boxed ("uri", "GnomeVFSURI", "URI for GnomeVFS", - GST_TYPE_GNOME_VFS_URI, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_HANDLE, - g_param_spec_boxed ("handle", "GnomeVFSHandle", "Handle for GnomeVFS", - GST_TYPE_GNOME_VFS_HANDLE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /** - * GstGnomeVFSSink::allow-overwrite - * @sink: the object which received the signal - * @uri: the URI to be overwritten - * - * This signal is fired when gnomevfssink is about to overwrite an - * existing resource. The application can connect to this signal and ask - * the user if the resource may be overwritten. - * - * Returns: A boolean indicating that the resource may be overwritten. - */ - gst_gnome_vfs_sink_signals[SIGNAL_ERASE_ASK] = - g_signal_new ("allow-overwrite", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_CLEANUP, G_STRUCT_OFFSET (GstGnomeVFSSinkClass, erase_ask), - _gst_boolean_allow_overwrite_accumulator, NULL, - gst_marshal_BOOLEAN__POINTER, G_TYPE_BOOLEAN, 1, GST_TYPE_GNOME_VFS_URI); - - basesink_class->stop = GST_DEBUG_FUNCPTR (gst_gnome_vfs_sink_stop); - basesink_class->start = GST_DEBUG_FUNCPTR (gst_gnome_vfs_sink_start); - basesink_class->event = GST_DEBUG_FUNCPTR (gst_gnome_vfs_sink_handle_event); - basesink_class->render = GST_DEBUG_FUNCPTR (gst_gnome_vfs_sink_render); - basesink_class->get_times = NULL; -} - -static void -gst_gnome_vfs_sink_finalize (GObject * obj) -{ - GstGnomeVFSSink *sink = GST_GNOME_VFS_SINK (obj); - - if (sink->uri) { - gnome_vfs_uri_unref (sink->uri); - sink->uri = NULL; - } - - if (sink->uri_name) { - g_free (sink->uri_name); - sink->uri_name = NULL; - } - - G_OBJECT_CLASS (parent_class)->finalize (obj); -} - -static void -gst_gnome_vfs_sink_init (GstGnomeVFSSink * sink, GstGnomeVFSSinkClass * klass) -{ - gst_pad_set_query_function (GST_BASE_SINK_PAD (sink), - GST_DEBUG_FUNCPTR (gst_gnome_vfs_sink_query)); - - sink->uri = NULL; - sink->uri_name = NULL; - sink->handle = NULL; - sink->own_handle = FALSE; - sink->current_pos = 0; - - GST_BASE_SINK (sink)->sync = FALSE; -} - -static void -gst_gnome_vfs_sink_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstGnomeVFSSink *sink; - GstState cur_state; - - sink = GST_GNOME_VFS_SINK (object); - - gst_element_get_state (GST_ELEMENT (sink), &cur_state, NULL, 0); - - if (cur_state == GST_STATE_PLAYING || cur_state == GST_STATE_PAUSED) { - GST_WARNING_OBJECT (sink, "cannot set property when PAUSED or PLAYING"); - return; - } - - GST_OBJECT_LOCK (sink); - - switch (prop_id) { - case ARG_LOCATION:{ - const gchar *new_location; - - if (sink->uri) { - gnome_vfs_uri_unref (sink->uri); - sink->uri = NULL; - } - if (sink->uri_name) { - g_free (sink->uri_name); - sink->uri_name = NULL; - } - - new_location = g_value_get_string (value); - if (new_location) { - sink->uri_name = gst_gnome_vfs_location_to_uri_string (new_location); - sink->uri = gnome_vfs_uri_new (sink->uri_name); - } - break; - } - case ARG_URI:{ - if (sink->uri) { - gnome_vfs_uri_unref (sink->uri); - sink->uri = NULL; - } - if (sink->uri_name) { - g_free (sink->uri_name); - sink->uri_name = NULL; - } - if (g_value_get_boxed (value)) { - sink->uri = (GnomeVFSURI *) g_value_dup_boxed (value); - sink->uri_name = gnome_vfs_uri_to_string (sink->uri, 0); - } - break; - } - case ARG_HANDLE:{ - if (sink->uri) { - gnome_vfs_uri_unref (sink->uri); - sink->uri = NULL; - } - if (sink->uri_name) { - g_free (sink->uri_name); - sink->uri_name = NULL; - } - sink->handle = g_value_get_boxed (value); - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - - GST_OBJECT_UNLOCK (sink); -} - -static void -gst_gnome_vfs_sink_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstGnomeVFSSink *sink; - - sink = GST_GNOME_VFS_SINK (object); - - GST_OBJECT_LOCK (sink); - - switch (prop_id) { - case ARG_LOCATION: - g_value_set_string (value, sink->uri_name); - break; - case ARG_URI: - g_value_set_boxed (value, sink->uri); - break; - case ARG_HANDLE: - g_value_set_boxed (value, sink->handle); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - - GST_OBJECT_UNLOCK (sink); -} - -static gboolean -gst_gnome_vfs_sink_open_file (GstGnomeVFSSink * sink) -{ - GnomeVFSResult result; - - if (sink->uri) { - /* open the file, all permissions, umask will apply */ - result = gnome_vfs_create_uri (&(sink->handle), sink->uri, - GNOME_VFS_OPEN_WRITE, TRUE, - GNOME_VFS_PERM_USER_READ | GNOME_VFS_PERM_USER_WRITE | - GNOME_VFS_PERM_GROUP_READ | GNOME_VFS_PERM_GROUP_WRITE | - GNOME_VFS_PERM_OTHER_READ | GNOME_VFS_PERM_OTHER_WRITE); - - /* if the file existed and the property says to ask, then ask! */ - if (result == GNOME_VFS_ERROR_FILE_EXISTS) { - gboolean erase_anyway = FALSE; - - g_signal_emit (G_OBJECT (sink), - gst_gnome_vfs_sink_signals[SIGNAL_ERASE_ASK], 0, sink->uri, - &erase_anyway); - if (erase_anyway) { - result = gnome_vfs_create_uri (&(sink->handle), sink->uri, - GNOME_VFS_OPEN_WRITE, FALSE, - GNOME_VFS_PERM_USER_READ | GNOME_VFS_PERM_USER_WRITE | - GNOME_VFS_PERM_GROUP_READ | GNOME_VFS_PERM_GROUP_WRITE | - GNOME_VFS_PERM_OTHER_READ | GNOME_VFS_PERM_OTHER_WRITE); - } - } - - GST_DEBUG_OBJECT (sink, "open: %s", gnome_vfs_result_to_string (result)); - - if (result != GNOME_VFS_OK) { - gchar *filename = gnome_vfs_uri_to_string (sink->uri, - GNOME_VFS_URI_HIDE_PASSWORD); - - GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, - (_("Could not open vfs file \"%s\" for writing: %s."), - filename, gnome_vfs_result_to_string (result)), GST_ERROR_SYSTEM); - g_free (filename); - return FALSE; - } - sink->own_handle = TRUE; - } else if (!sink->handle) { - GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, (_("No filename given")), - (NULL)); - return FALSE; - } else { - sink->own_handle = FALSE; - } - - sink->current_pos = 0; - - return TRUE; -} - -static void -gst_gnome_vfs_sink_close_file (GstGnomeVFSSink * sink) -{ - GnomeVFSResult result; - - if (sink->own_handle) { - /* close the file */ - result = gnome_vfs_close (sink->handle); - - if (result != GNOME_VFS_OK) { - gchar *filename = gnome_vfs_uri_to_string (sink->uri, - GNOME_VFS_URI_HIDE_PASSWORD); - - GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE, - (_("Could not close vfs file \"%s\"."), filename), GST_ERROR_SYSTEM); - g_free (filename); - } - - sink->own_handle = FALSE; - sink->handle = NULL; - } -} - -static gboolean -gst_gnome_vfs_sink_start (GstBaseSink * basesink) -{ - gboolean ret; - - ret = gst_gnome_vfs_sink_open_file (GST_GNOME_VFS_SINK (basesink)); - - return ret; -} - -static gboolean -gst_gnome_vfs_sink_stop (GstBaseSink * basesink) -{ - GST_DEBUG_OBJECT (basesink, "closing ..."); - gst_gnome_vfs_sink_close_file (GST_GNOME_VFS_SINK (basesink)); - return TRUE; -} - -static gboolean -gst_gnome_vfs_sink_handle_event (GstBaseSink * basesink, GstEvent * event) -{ - GstGnomeVFSSink *sink; - gboolean ret = TRUE; - - sink = GST_GNOME_VFS_SINK (basesink); - - GST_DEBUG_OBJECT (sink, "processing %s event", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_NEWSEGMENT:{ - GnomeVFSResult res; - GstFormat format; - gint64 offset; - - gst_event_parse_new_segment (event, NULL, NULL, &format, &offset, - NULL, NULL); - - if (format != GST_FORMAT_BYTES) { - GST_WARNING_OBJECT (sink, "ignored NEWSEGMENT event in %s format", - gst_format_get_name (format)); - break; - } - - GST_LOG_OBJECT (sink, "seeking to offset %" G_GINT64_FORMAT, offset); - res = gnome_vfs_seek (sink->handle, GNOME_VFS_SEEK_START, offset); - - if (res != GNOME_VFS_OK) { - GST_ERROR_OBJECT (sink, "Failed to seek to offset %" - G_GINT64_FORMAT ": %s", offset, gnome_vfs_result_to_string (res)); - ret = FALSE; - } else { - sink->current_pos = offset; - } - - break; - } - - case GST_EVENT_FLUSH_START: - case GST_EVENT_EOS:{ - /* No need to flush with GnomeVfs */ - break; - } - default: - break; - } - - return ret; -} - -static gboolean -gst_gnome_vfs_sink_query (GstPad * pad, GstQuery * query) -{ - GstGnomeVFSSink *sink; - GstFormat format; - - sink = GST_GNOME_VFS_SINK (GST_PAD_PARENT (pad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - gst_query_parse_position (query, &format, NULL); - switch (format) { - case GST_FORMAT_DEFAULT: - case GST_FORMAT_BYTES: - gst_query_set_position (query, GST_FORMAT_BYTES, sink->current_pos); - return TRUE; - default: - return FALSE; - } - - case GST_QUERY_FORMATS: - gst_query_set_formats (query, 2, GST_FORMAT_DEFAULT, GST_FORMAT_BYTES); - return TRUE; - - case GST_QUERY_URI: - gst_query_set_uri (query, sink->uri_name); - return TRUE; - - default: - return gst_pad_query_default (pad, query); - } -} - -static GstFlowReturn -gst_gnome_vfs_sink_render (GstBaseSink * basesink, GstBuffer * buf) -{ - GnomeVFSFileSize written, cur_pos; - GstGnomeVFSSink *sink; - GnomeVFSResult result; - GstFlowReturn ret; - - sink = GST_GNOME_VFS_SINK (basesink); - - if (gnome_vfs_tell (sink->handle, &cur_pos) == GNOME_VFS_OK) { - /* bring up to date with current position for proper reporting */ - sink->current_pos = cur_pos; - } - - result = gnome_vfs_write (sink->handle, GST_BUFFER_DATA (buf), - GST_BUFFER_SIZE (buf), &written); - - switch (result) { - case GNOME_VFS_OK:{ - GST_DEBUG_OBJECT (sink, "wrote %" G_GINT64_FORMAT " bytes at %" - G_GINT64_FORMAT, (gint64) written, (gint64) cur_pos); - - if (written < GST_BUFFER_SIZE (buf)) { - /* FIXME: what to do here? (tpm) */ - g_warning ("%s: %d bytes should be written, only %" - G_GUINT64_FORMAT " bytes written", G_STRLOC, - GST_BUFFER_SIZE (buf), written); - } - - sink->current_pos += GST_BUFFER_SIZE (buf); - ret = GST_FLOW_OK; - break; - } - case GNOME_VFS_ERROR_NO_SPACE:{ - /* TODO: emit signal/send msg on out-of-diskspace and - * handle this gracefully (see open bug) (tpm) */ - GST_ELEMENT_ERROR (sink, RESOURCE, NO_SPACE_LEFT, (NULL), - ("bufsize=%u, written=%u", GST_BUFFER_SIZE (buf), (guint) written)); - ret = GST_FLOW_ERROR; - break; - } - default:{ - gchar *filename = gnome_vfs_uri_to_string (sink->uri, - GNOME_VFS_URI_HIDE_PASSWORD); - - GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, - (_("Error while writing to file \"%s\"."), filename), - ("%s, bufsize=%u, written=%u", gnome_vfs_result_to_string (result), - GST_BUFFER_SIZE (buf), (guint) written)); - - g_free (filename); - ret = GST_FLOW_ERROR; - break; - } - } - - return GST_FLOW_OK; -} - -/*** GSTURIHANDLER INTERFACE *************************************************/ - -static GstURIType -gst_gnome_vfs_sink_uri_get_type (void) -{ - return GST_URI_SINK; -} - -static gchar ** -gst_gnome_vfs_sink_uri_get_protocols (void) -{ - return gst_gnomevfs_get_supported_uris (); -} - -static const gchar * -gst_gnome_vfs_sink_uri_get_uri (GstURIHandler * handler) -{ - GstGnomeVFSSink *sink = GST_GNOME_VFS_SINK (handler); - - return sink->uri_name; -} - -static gboolean -gst_gnome_vfs_sink_uri_set_uri (GstURIHandler * handler, const gchar * uri) -{ - GstGnomeVFSSink *sink = GST_GNOME_VFS_SINK (handler); - GstState cur_state; - - gst_element_get_state (GST_ELEMENT (sink), &cur_state, NULL, 0); - - if (cur_state == GST_STATE_PLAYING || cur_state == GST_STATE_PAUSED) { - GST_WARNING_OBJECT (sink, "cannot set uri when PAUSED or PLAYING"); - return FALSE; - } - - g_object_set (sink, "location", uri, NULL); - - return TRUE; -} - -static void -gst_gnome_vfs_sink_uri_handler_init (gpointer g_iface, gpointer iface_data) -{ - GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; - - iface->get_type = gst_gnome_vfs_sink_uri_get_type; - iface->get_protocols = gst_gnome_vfs_sink_uri_get_protocols; - iface->get_uri = gst_gnome_vfs_sink_uri_get_uri; - iface->set_uri = gst_gnome_vfs_sink_uri_set_uri; -} diff --git a/ext/gnomevfs/gstgnomevfssink.h b/ext/gnomevfs/gstgnomevfssink.h deleted file mode 100644 index 409add96..00000000 --- a/ext/gnomevfs/gstgnomevfssink.h +++ /dev/null @@ -1,84 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * 2000 Wim Taymans <wtay@chello.be> - * 2001 Bastien Nocera <hadess@hadess.net> - * 2003 Colin Walters <walters@verbum.org> - * 2005 Tim-Philipp Müller <tim centricular net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_GNOME_VFS_SINK_H__ -#define __GST_GNOME_VFS_SINK_H__ - -#include "gstgnomevfs.h" -#include "gstgnomevfsuri.h" -#include <gst/base/gstbasesink.h> - -G_BEGIN_DECLS - -#define GST_TYPE_GNOME_VFS_SINK \ - (gst_gnome_vfs_sink_get_type()) -#define GST_GNOME_VFS_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GNOME_VFS_SINK,GstGnomeVFSSink)) -#define GST_GNOME_VFS_SINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GNOME_VFS_SINK,GstGnomeVFSSinkClass)) -#define GST_IS_GNOME_VFS_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GNOME_VFS_SINK)) -#define GST_IS_GNOME_VFS_SINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GNOME_VFS_SINK)) - -typedef struct _GstGnomeVFSSink GstGnomeVFSSink; -typedef struct _GstGnomeVFSSinkClass GstGnomeVFSSinkClass; - -/** - * GstGnomeVFSSink: - * - * Opaque data structure. - */ -struct _GstGnomeVFSSink -{ - GstBaseSink basesink; - - /*< private >*/ - - /* uri */ - GnomeVFSURI *uri; - gchar *uri_name; - - /* handle */ - GnomeVFSHandle *handle; - - /* whether we opened the handle ourselves */ - gboolean own_handle; - - guint64 current_pos; -}; - -struct _GstGnomeVFSSinkClass -{ - GstBaseSinkClass basesink_class; - - /* signals */ - gboolean (*erase_ask) (GstElement * element, GnomeVFSURI * uri); -}; - -GType gst_gnome_vfs_sink_get_type (void); - -G_END_DECLS - -#endif /* __GST_GNOME_VFS_SINK_H__ */ - diff --git a/ext/gnomevfs/gstgnomevfssrc.c b/ext/gnomevfs/gstgnomevfssrc.c deleted file mode 100644 index cf26b30e..00000000 --- a/ext/gnomevfs/gstgnomevfssrc.c +++ /dev/null @@ -1,897 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * 2000 Wim Taymans <wtay@chello.be> - * 2001 Bastien Nocera <hadess@hadess.net> - * 2002 Kristian Rietveld <kris@gtk.org> - * 2002,2003 Colin Walters <walters@gnu.org> - * - * gnomevfssrc.c: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-gnomevfssrc - * @see_also: #GstFileSrc, #GstGnomeVFSSink - * - * This plugin reads data from a local or remote location specified - * by an URI. This location can be specified using any protocol supported by - * the GnomeVFS library. Common protocols are 'file', 'http', 'ftp', or 'smb'. - * - * In case the #GstGnomeVFSSrc:iradio-mode property is set and the - * location is a http resource, gnomevfssrc will send special icecast http - * headers to the server to request additional icecast metainformation. If - * the server is not an icecast server, it will display the same behaviour - * as if the #GstGnomeVFSSrc:iradio-mode property was not set. However, - * if the server is in fact an icecast server, gnomevfssrc will output - * data with a media type of application/x-icy, in which case you will - * need to use the #GstICYDemux element as follow-up element to extract - * the icecast meta data and to determine the underlying media type. - * - * <refsect2> - * <title>Example launch lines</title> - * |[ - * gst-launch -v gnomevfssrc location=file:///home/joe/foo.xyz ! fakesink - * ]| The above pipeline will simply read a local file and do nothing with the - * data read. Instead of gnomevfssrc, we could just as well have used the - * filesrc element here. - * |[ - * gst-launch -v gnomevfssrc location=smb://othercomputer/foo.xyz ! filesink location=/home/joe/foo.xyz - * ]| The above pipeline will copy a file from a remote host to the local file - * system using the Samba protocol. - * |[ - * gst-launch -v gnomevfssrc location=http://music.foobar.com/demo.mp3 ! mad ! audioconvert ! audioresample ! alsasink - * ]| The above pipeline will read and decode and play an mp3 file from a - * web server using the http protocol. - * </refsect2> - */ - - -#define BROKEN_SIG 1 -/*#undef BROKEN_SIG */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gst/gst-i18n-plugin.h" - -#include "gstgnomevfssrc.h" - -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/time.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <netdb.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <sys/mman.h> -#include <errno.h> -#include <string.h> - -#include <gst/gst.h> -#include <gst/tag/tag.h> - -/* gnome-vfs.h doesn't include the following header, which we need: */ -#include <libgnomevfs/gnome-vfs-standard-callbacks.h> - -GST_DEBUG_CATEGORY_STATIC (gnomevfssrc_debug); -#define GST_CAT_DEFAULT gnomevfssrc_debug - -static const GstElementDetails gst_gnome_vfs_src_details = -GST_ELEMENT_DETAILS ("GnomeVFS Source", - "Source/File", - "Read from any GnomeVFS-supported file", - "Bastien Nocera <hadess@hadess.net>\n" - "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>"); - -static GStaticMutex count_lock = G_STATIC_MUTEX_INIT; -static gint ref_count = 0; -static gboolean vfs_owner = FALSE; - -static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -enum -{ - ARG_0, - ARG_HANDLE, - ARG_LOCATION, - ARG_IRADIO_MODE, - ARG_IRADIO_NAME, - ARG_IRADIO_GENRE, - ARG_IRADIO_URL, - ARG_IRADIO_TITLE -}; - -static void gst_gnome_vfs_src_base_init (gpointer g_class); -static void gst_gnome_vfs_src_class_init (GstGnomeVFSSrcClass * klass); -static void gst_gnome_vfs_src_init (GstGnomeVFSSrc * gnomevfssrc); -static void gst_gnome_vfs_src_finalize (GObject * object); -static void gst_gnome_vfs_src_uri_handler_init (gpointer g_iface, - gpointer iface_data); - -static void gst_gnome_vfs_src_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_gnome_vfs_src_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static gboolean gst_gnome_vfs_src_stop (GstBaseSrc * src); -static gboolean gst_gnome_vfs_src_start (GstBaseSrc * src); -static gboolean gst_gnome_vfs_src_is_seekable (GstBaseSrc * src); -static gboolean gst_gnome_vfs_src_check_get_range (GstBaseSrc * src); -static gboolean gst_gnome_vfs_src_get_size (GstBaseSrc * src, guint64 * size); -static GstFlowReturn gst_gnome_vfs_src_create (GstBaseSrc * basesrc, - guint64 offset, guint size, GstBuffer ** buffer); -static gboolean gst_gnome_vfs_src_query (GstBaseSrc * src, GstQuery * query); - -static GstElementClass *parent_class = NULL; - -GType -gst_gnome_vfs_src_get_type (void) -{ - static GType gnomevfssrc_type = 0; - - if (!gnomevfssrc_type) { - static const GTypeInfo gnomevfssrc_info = { - sizeof (GstGnomeVFSSrcClass), - gst_gnome_vfs_src_base_init, - NULL, - (GClassInitFunc) gst_gnome_vfs_src_class_init, - NULL, - NULL, - sizeof (GstGnomeVFSSrc), - 0, - (GInstanceInitFunc) gst_gnome_vfs_src_init, - }; - static const GInterfaceInfo urihandler_info = { - gst_gnome_vfs_src_uri_handler_init, - NULL, - NULL - }; - - gnomevfssrc_type = - g_type_register_static (GST_TYPE_BASE_SRC, - "GstGnomeVFSSrc", &gnomevfssrc_info, 0); - g_type_add_interface_static (gnomevfssrc_type, GST_TYPE_URI_HANDLER, - &urihandler_info); - } - return gnomevfssrc_type; -} - -static void -gst_gnome_vfs_src_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&srctemplate)); - gst_element_class_set_details (element_class, &gst_gnome_vfs_src_details); - - GST_DEBUG_CATEGORY_INIT (gnomevfssrc_debug, "gnomevfssrc", 0, - "Gnome-VFS Source"); -} - -static void -gst_gnome_vfs_src_class_init (GstGnomeVFSSrcClass * klass) -{ - GObjectClass *gobject_class; - GstBaseSrcClass *gstbasesrc_class; - - gobject_class = G_OBJECT_CLASS (klass); - gstbasesrc_class = GST_BASE_SRC_CLASS (klass); - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->finalize = gst_gnome_vfs_src_finalize; - gobject_class->set_property = gst_gnome_vfs_src_set_property; - gobject_class->get_property = gst_gnome_vfs_src_get_property; - - /* properties */ - gst_element_class_install_std_props (GST_ELEMENT_CLASS (klass), - "location", ARG_LOCATION, G_PARAM_READWRITE, NULL); - g_object_class_install_property (gobject_class, - ARG_HANDLE, - g_param_spec_boxed ("handle", - "GnomeVFSHandle", "Handle for GnomeVFS", - GST_TYPE_GNOME_VFS_HANDLE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /* icecast stuff */ - g_object_class_install_property (gobject_class, - ARG_IRADIO_MODE, - g_param_spec_boolean ("iradio-mode", - "iradio-mode", - "Enable internet radio mode (extraction of shoutcast/icecast metadata)", - FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, - ARG_IRADIO_NAME, - g_param_spec_string ("iradio-name", - "iradio-name", "Name of the stream", NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_IRADIO_GENRE, - g_param_spec_string ("iradio-genre", "iradio-genre", - "Genre of the stream", NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_IRADIO_URL, - g_param_spec_string ("iradio-url", "iradio-url", - "Homepage URL for radio stream", NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_IRADIO_TITLE, - g_param_spec_string ("iradio-title", "iradio-title", - "Name of currently playing song", NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_start); - gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_stop); - gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_get_size); - gstbasesrc_class->is_seekable = - GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_is_seekable); - gstbasesrc_class->check_get_range = - GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_check_get_range); - gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_create); - gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_query); -} - -static void -gst_gnome_vfs_src_init (GstGnomeVFSSrc * gnomevfssrc) -{ - gnomevfssrc->uri = NULL; - gnomevfssrc->uri_name = NULL; - gnomevfssrc->handle = NULL; - gnomevfssrc->curoffset = 0; - gnomevfssrc->seekable = FALSE; - - gnomevfssrc->iradio_mode = FALSE; - gnomevfssrc->http_callbacks_pushed = FALSE; - gnomevfssrc->iradio_name = NULL; - gnomevfssrc->iradio_genre = NULL; - gnomevfssrc->iradio_url = NULL; - gnomevfssrc->iradio_title = NULL; - - g_static_mutex_lock (&count_lock); - if (ref_count == 0) { - /* gnome vfs engine init */ - if (gnome_vfs_initialized () == FALSE) { - gnome_vfs_init (); - vfs_owner = TRUE; - } - } - ref_count++; - g_static_mutex_unlock (&count_lock); -} - -static void -gst_gnome_vfs_src_finalize (GObject * object) -{ - GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (object); - - g_static_mutex_lock (&count_lock); - ref_count--; - if (ref_count == 0 && vfs_owner) { - if (gnome_vfs_initialized () == TRUE) { - gnome_vfs_shutdown (); - } - } - g_static_mutex_unlock (&count_lock); - - if (src->uri) { - gnome_vfs_uri_unref (src->uri); - src->uri = NULL; - } - - g_free (src->uri_name); - src->uri_name = NULL; - - g_free (src->iradio_name); - src->iradio_name = NULL; - - g_free (src->iradio_genre); - src->iradio_genre = NULL; - - g_free (src->iradio_url); - src->iradio_url = NULL; - - g_free (src->iradio_title); - src->iradio_title = NULL; - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -/* - * URI interface support. - */ - -static GstURIType -gst_gnome_vfs_src_uri_get_type (void) -{ - return GST_URI_SRC; -} - -static gchar ** -gst_gnome_vfs_src_uri_get_protocols (void) -{ - return gst_gnomevfs_get_supported_uris (); -} - -static const gchar * -gst_gnome_vfs_src_uri_get_uri (GstURIHandler * handler) -{ - GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (handler); - - return src->uri_name; -} - -static gboolean -gst_gnome_vfs_src_uri_set_uri (GstURIHandler * handler, const gchar * uri) -{ - GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (handler); - - if (GST_STATE (src) == GST_STATE_PLAYING || - GST_STATE (src) == GST_STATE_PAUSED) - return FALSE; - - g_object_set (G_OBJECT (src), "location", uri, NULL); - - return TRUE; -} - -static void -gst_gnome_vfs_src_uri_handler_init (gpointer g_iface, gpointer iface_data) -{ - GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; - - iface->get_type = gst_gnome_vfs_src_uri_get_type; - iface->get_protocols = gst_gnome_vfs_src_uri_get_protocols; - iface->get_uri = gst_gnome_vfs_src_uri_get_uri; - iface->set_uri = gst_gnome_vfs_src_uri_set_uri; -} - -static void -gst_gnome_vfs_src_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstGnomeVFSSrc *src; - - src = GST_GNOME_VFS_SRC (object); - - switch (prop_id) { - case ARG_LOCATION:{ - const gchar *new_location; - - /* the element must be stopped or paused in order to do this */ - if (GST_STATE (src) == GST_STATE_PLAYING || - GST_STATE (src) == GST_STATE_PAUSED) - break; - - if (src->uri) { - gnome_vfs_uri_unref (src->uri); - src->uri = NULL; - } - if (src->uri_name) { - g_free (src->uri_name); - src->uri_name = NULL; - } - - new_location = g_value_get_string (value); - if (new_location) { - src->uri_name = gst_gnome_vfs_location_to_uri_string (new_location); - src->uri = gnome_vfs_uri_new (src->uri_name); - } - break; - } - case ARG_HANDLE: - if (GST_STATE (src) == GST_STATE_NULL || - GST_STATE (src) == GST_STATE_READY) { - if (src->uri) { - gnome_vfs_uri_unref (src->uri); - src->uri = NULL; - } - if (src->uri_name) { - g_free (src->uri_name); - src->uri_name = NULL; - } - src->handle = g_value_get_boxed (value); - } - break; - case ARG_IRADIO_MODE: - src->iradio_mode = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_gnome_vfs_src_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstGnomeVFSSrc *src; - - src = GST_GNOME_VFS_SRC (object); - - switch (prop_id) { - case ARG_LOCATION: - g_value_set_string (value, src->uri_name); - break; - case ARG_HANDLE: - g_value_set_boxed (value, src->handle); - break; - case ARG_IRADIO_MODE: - g_value_set_boolean (value, src->iradio_mode); - break; - case ARG_IRADIO_NAME: - g_value_set_string (value, src->iradio_name); - break; - case ARG_IRADIO_GENRE: - g_value_set_string (value, src->iradio_genre); - break; - case ARG_IRADIO_URL: - g_value_set_string (value, src->iradio_url); - break; - case ARG_IRADIO_TITLE: - g_value_set_string (value, src->iradio_title); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static char * -gst_gnome_vfs_src_unicodify (const char *str) -{ - const gchar *env_vars[] = { "GST_ICY_TAG_ENCODING", - "GST_TAG_ENCODING", NULL - }; - - return gst_tag_freeform_string_to_utf8 (str, -1, env_vars); -} - -static void -gst_gnome_vfs_src_send_additional_headers_callback (gconstpointer in, - gsize in_size, gpointer out, gsize out_size, gpointer callback_data) -{ - GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (callback_data); - GnomeVFSModuleCallbackAdditionalHeadersOut *out_args = - (GnomeVFSModuleCallbackAdditionalHeadersOut *) out; - - if (!src->iradio_mode) - return; - GST_DEBUG_OBJECT (src, "sending headers\n"); - - out_args->headers = g_list_append (out_args->headers, - g_strdup ("icy-metadata:1\r\n")); -} - -static void -gst_gnome_vfs_src_received_headers_callback (gconstpointer in, - gsize in_size, gpointer out, gsize out_size, gpointer callback_data) -{ - GList *i; - gint icy_metaint; - GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (callback_data); - GnomeVFSModuleCallbackReceivedHeadersIn *in_args = - (GnomeVFSModuleCallbackReceivedHeadersIn *) in; - - /* This is only used for internet radio stuff right now */ - if (!src->iradio_mode) - return; - - GST_DEBUG_OBJECT (src, "receiving internet radio metadata\n"); - - /* FIXME: Could we use "Accept-Ranges: bytes" - * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.5 - * to enable pull-mode? - */ - - for (i = in_args->headers; i; i = i->next) { - char *data = (char *) i->data; - char *key = data; - char *value = strchr (data, ':'); - - if (!value) - continue; - - value++; - g_strstrip (value); - if (!strlen (value)) - continue; - - GST_LOG_OBJECT (src, "data %s", data); - - /* Icecast stuff */ - if (strncmp (data, "icy-metaint:", 12) == 0) { /* ugh */ - if (sscanf (data + 12, "%d", &icy_metaint) == 1) { - if (icy_metaint > 0) { - GstCaps *icy_caps; - - icy_caps = gst_caps_new_simple ("application/x-icy", - "metadata-interval", G_TYPE_INT, icy_metaint, NULL); - gst_pad_set_caps (GST_BASE_SRC_PAD (src), icy_caps); - gst_caps_unref (icy_caps); - } - } - continue; - } - - if (!strncmp (data, "icy-", 4)) - key = data + 4; - else - continue; - - GST_DEBUG_OBJECT (src, "key: %s", key); - if (!strncmp (key, "name", 4)) { - g_free (src->iradio_name); - src->iradio_name = gst_gnome_vfs_src_unicodify (value); - if (src->iradio_name) - g_object_notify (G_OBJECT (src), "iradio-name"); - } else if (!strncmp (key, "genre", 5)) { - g_free (src->iradio_genre); - src->iradio_genre = gst_gnome_vfs_src_unicodify (value); - if (src->iradio_genre) - g_object_notify (G_OBJECT (src), "iradio-genre"); - } else if (!strncmp (key, "url", 3)) { - g_free (src->iradio_url); - src->iradio_url = gst_gnome_vfs_src_unicodify (value); - if (src->iradio_url) - g_object_notify (G_OBJECT (src), "iradio-url"); - } - } -} - -static void -gst_gnome_vfs_src_push_callbacks (GstGnomeVFSSrc * src) -{ - if (src->http_callbacks_pushed) - return; - - GST_DEBUG_OBJECT (src, "pushing callbacks"); - gnome_vfs_module_callback_push - (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS, - gst_gnome_vfs_src_send_additional_headers_callback, src, NULL); - gnome_vfs_module_callback_push - (GNOME_VFS_MODULE_CALLBACK_HTTP_RECEIVED_HEADERS, - gst_gnome_vfs_src_received_headers_callback, src, NULL); - - src->http_callbacks_pushed = TRUE; -} - -static void -gst_gnome_vfs_src_pop_callbacks (GstGnomeVFSSrc * src) -{ - if (!src->http_callbacks_pushed) - return; - - GST_DEBUG_OBJECT (src, "popping callbacks"); - gnome_vfs_module_callback_pop - (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS); - gnome_vfs_module_callback_pop - (GNOME_VFS_MODULE_CALLBACK_HTTP_RECEIVED_HEADERS); - - src->http_callbacks_pushed = FALSE; -} - -/* - * Read a new buffer from src->reqoffset, takes care of events - * and seeking and such. - */ -static GstFlowReturn -gst_gnome_vfs_src_create (GstBaseSrc * basesrc, guint64 offset, guint size, - GstBuffer ** buffer) -{ - GnomeVFSResult res; - GstBuffer *buf; - GnomeVFSFileSize readbytes; - guint8 *data; - guint todo; - GstGnomeVFSSrc *src; - - src = GST_GNOME_VFS_SRC (basesrc); - - GST_DEBUG ("now at %" G_GINT64_FORMAT ", reading from %" G_GUINT64_FORMAT - ", size %u", src->curoffset, offset, size); - - /* seek if required */ - if (G_UNLIKELY (src->curoffset != offset)) { - GST_DEBUG ("need to seek"); - if (src->seekable) { - GST_DEBUG ("seeking to %" G_GUINT64_FORMAT, offset); - res = gnome_vfs_seek (src->handle, GNOME_VFS_SEEK_START, offset); - if (res != GNOME_VFS_OK) - goto seek_failed; - src->curoffset = offset; - } else { - goto cannot_seek; - } - } - - buf = gst_buffer_try_new_and_alloc (size); - if (G_UNLIKELY (buf == NULL && size == 0)) { - GST_ERROR_OBJECT (src, "Failed to allocate %u bytes", size); - return GST_FLOW_ERROR; - } - - data = GST_BUFFER_DATA (buf); - - todo = size; - while (todo > 0) { - /* this can return less that we ask for */ - res = gnome_vfs_read (src->handle, data, todo, &readbytes); - - if (G_UNLIKELY (res == GNOME_VFS_ERROR_EOF || (res == GNOME_VFS_OK - && readbytes == 0))) - goto eos; - - if (G_UNLIKELY (res != GNOME_VFS_OK)) - goto read_failed; - - if (readbytes < todo) { - data = &data[readbytes]; - todo -= readbytes; - } else { - todo = 0; - } - GST_LOG (" got size %" G_GUINT64_FORMAT, readbytes); - } - GST_BUFFER_OFFSET (buf) = src->curoffset; - src->curoffset += size; - - /* we're done, return the buffer */ - *buffer = buf; - - return GST_FLOW_OK; - -seek_failed: - { - GST_ELEMENT_ERROR (src, RESOURCE, SEEK, (NULL), - ("Failed to seek to requested position %" G_GINT64_FORMAT ": %s", - offset, gnome_vfs_result_to_string (res))); - return GST_FLOW_ERROR; - } -cannot_seek: - { - GST_ELEMENT_ERROR (src, RESOURCE, SEEK, (NULL), - ("Requested seek from %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT - " on non-seekable stream", src->curoffset, offset)); - return GST_FLOW_ERROR; - } -read_failed: - { - gst_buffer_unref (buf); - GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), - ("Failed to read data: %s", gnome_vfs_result_to_string (res))); - return GST_FLOW_ERROR; - } -eos: - { - gst_buffer_unref (buf); - GST_DEBUG_OBJECT (src, "Reading data gave EOS"); - return GST_FLOW_UNEXPECTED; - } -} - -static gboolean -gst_gnome_vfs_src_query (GstBaseSrc * basesrc, GstQuery * query) -{ - gboolean ret = FALSE; - GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (basesrc); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_URI: - gst_query_set_uri (query, src->uri_name); - ret = TRUE; - break; - default: - ret = FALSE; - break; - } - - if (!ret) - ret = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query); - - return ret; -} - -static gboolean -gst_gnome_vfs_src_is_seekable (GstBaseSrc * basesrc) -{ - GstGnomeVFSSrc *src; - - src = GST_GNOME_VFS_SRC (basesrc); - - return src->seekable; -} - -static gboolean -gst_gnome_vfs_src_check_get_range (GstBaseSrc * basesrc) -{ - GstGnomeVFSSrc *src; - const gchar *protocol; - - src = GST_GNOME_VFS_SRC (basesrc); - - if (src->uri == NULL) { - GST_WARNING_OBJECT (src, "no URI set yet"); - return FALSE; - } - - if (gnome_vfs_uri_is_local (src->uri)) { - GST_LOG_OBJECT (src, "local URI (%s), assuming random access is possible", - GST_STR_NULL (src->uri_name)); - return TRUE; - } - - /* blacklist certain protocols we know won't work getrange-based */ - protocol = gnome_vfs_uri_get_scheme (src->uri); - if (protocol == NULL) - goto undecided; - - if (strcmp (protocol, "http") == 0 || strcmp (protocol, "https") == 0) { - GST_LOG_OBJECT (src, "blacklisted protocol '%s', no random access possible" - " (URI=%s)", protocol, GST_STR_NULL (src->uri_name)); - return FALSE; - } - - /* fall through to undecided */ - -undecided: - { - /* don't know what to do, let the basesrc class decide for us */ - GST_LOG_OBJECT (src, "undecided about URI '%s', let base class handle it", - GST_STR_NULL (src->uri_name)); - - if (GST_BASE_SRC_CLASS (parent_class)->check_get_range) - return GST_BASE_SRC_CLASS (parent_class)->check_get_range (basesrc); - - return FALSE; - } -} - -static gboolean -gst_gnome_vfs_src_get_size (GstBaseSrc * basesrc, guint64 * size) -{ - GstGnomeVFSSrc *src; - GnomeVFSFileInfo *info; - GnomeVFSFileInfoOptions options; - GnomeVFSResult res; - - src = GST_GNOME_VFS_SRC (basesrc); - - *size = -1; - info = gnome_vfs_file_info_new (); - options = GNOME_VFS_FILE_INFO_DEFAULT | GNOME_VFS_FILE_INFO_FOLLOW_LINKS; - res = gnome_vfs_get_file_info_from_handle (src->handle, info, options); - if (res == GNOME_VFS_OK) { - if ((info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) != 0) { - *size = info->size; - GST_DEBUG_OBJECT (src, "from handle: %" G_GUINT64_FORMAT " bytes", *size); - } else if (src->own_handle && gnome_vfs_uri_is_local (src->uri)) { - GST_DEBUG_OBJECT (src, - "file size not known, file local, trying fallback"); - res = gnome_vfs_get_file_info_uri (src->uri, info, options); - if (res == GNOME_VFS_OK && - (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) != 0) { - *size = info->size; - GST_DEBUG_OBJECT (src, "from uri: %" G_GUINT64_FORMAT " bytes", *size); - } - } - } else { - GST_WARNING_OBJECT (src, "getting info failed: %s", - gnome_vfs_result_to_string (res)); - } - gnome_vfs_file_info_unref (info); - - if (*size == (GnomeVFSFileSize) - 1) - return FALSE; - - GST_DEBUG_OBJECT (src, "return size %" G_GUINT64_FORMAT, *size); - - return TRUE; -} - -/* open the file, do stuff necessary to go to PAUSED state */ -static gboolean -gst_gnome_vfs_src_start (GstBaseSrc * basesrc) -{ - GnomeVFSResult res; - GstGnomeVFSSrc *src; - - src = GST_GNOME_VFS_SRC (basesrc); - - gst_gnome_vfs_src_push_callbacks (src); - - if (src->uri != NULL) { - GnomeVFSOpenMode mode = GNOME_VFS_OPEN_READ; - - /* this can block... */ - res = gnome_vfs_open_uri (&src->handle, src->uri, mode); - if (res != GNOME_VFS_OK) - goto open_failed; - src->own_handle = TRUE; - } else if (!src->handle) { - goto no_filename; - } else { - src->own_handle = FALSE; - } - - if (gnome_vfs_seek (src->handle, GNOME_VFS_SEEK_CURRENT, 0) == GNOME_VFS_OK) { - src->seekable = TRUE; - } else { - src->seekable = FALSE; - } - - return TRUE; - - /* ERRORS */ -open_failed: - { - gchar *filename = gnome_vfs_uri_to_string (src->uri, - GNOME_VFS_URI_HIDE_PASSWORD); - - gst_gnome_vfs_src_pop_callbacks (src); - - if (res == GNOME_VFS_ERROR_NOT_FOUND || - res == GNOME_VFS_ERROR_HOST_NOT_FOUND || - res == GNOME_VFS_ERROR_SERVICE_NOT_AVAILABLE) { - GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL), - ("Could not open vfs file \"%s\" for reading: %s (%d)", - filename, gnome_vfs_result_to_string (res), res)); - } else { - GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), - ("Could not open vfs file \"%s\" for reading: %s (%d)", - filename, gnome_vfs_result_to_string (res), res)); - } - g_free (filename); - return FALSE; - } -no_filename: - { - GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("No filename given")); - return FALSE; - } -} - -static gboolean -gst_gnome_vfs_src_stop (GstBaseSrc * basesrc) -{ - GstGnomeVFSSrc *src; - - src = GST_GNOME_VFS_SRC (basesrc); - - gst_gnome_vfs_src_pop_callbacks (src); - - if (src->own_handle) { - GnomeVFSResult res; - - res = gnome_vfs_close (src->handle); - if (res != GNOME_VFS_OK) { - GST_ELEMENT_ERROR (src, RESOURCE, CLOSE, (NULL), - ("Could not close vfs handle: %s", gnome_vfs_result_to_string (res))); - } - src->handle = NULL; - } - src->curoffset = 0; - - return TRUE; -} diff --git a/ext/gnomevfs/gstgnomevfssrc.h b/ext/gnomevfs/gstgnomevfssrc.h deleted file mode 100644 index 31c1f65c..00000000 --- a/ext/gnomevfs/gstgnomevfssrc.h +++ /dev/null @@ -1,87 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * 2000 Wim Taymans <wtay@chello.be> - * 2001 Bastien Nocera <hadess@hadess.net> - * 2002 Kristian Rietveld <kris@gtk.org> - * 2002,2003 Colin Walters <walters@gnu.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_GNOME_VFS_SRC_H__ -#define __GST_GNOME_VFS_SRC_H__ - -#include <gst/base/gstbasesrc.h> - -#include "gstgnomevfs.h" -#include "gstgnomevfsuri.h" -#include <libgnomevfs/gnome-vfs.h> - -G_BEGIN_DECLS - -#define GST_TYPE_GNOME_VFS_SRC \ - (gst_gnome_vfs_src_get_type()) -#define GST_GNOME_VFS_SRC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GNOME_VFS_SRC,GstGnomeVFSSrc)) -#define GST_GNOME_VFS_SRC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GNOME_VFS_SRC,GstGnomeVFSSrcClass)) -#define GST_IS_GNOME_VFS_SRC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GNOME_VFS_SRC)) -#define GST_IS_GNOME_VFS_SRC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GNOME_VFS_SRC)) - -typedef struct _GstGnomeVFSSrc GstGnomeVFSSrc; -typedef struct _GstGnomeVFSSrcClass GstGnomeVFSSrcClass; - -/** - * GstGnomeVFSSrc: - * - * Opaque data structure. - */ -struct _GstGnomeVFSSrc -{ - GstBaseSrc basesrc; - - /* uri, file, ... */ - GnomeVFSURI *uri; - gchar *uri_name; - GnomeVFSHandle *handle; - gboolean own_handle; - GnomeVFSFileOffset curoffset; /* current offset in file */ - gboolean seekable; - - /* shoutcast/icecast metadata extraction handling */ - gboolean iradio_mode; - gboolean http_callbacks_pushed; - - gchar *iradio_name; - gchar *iradio_genre; - gchar *iradio_url; - gchar *iradio_title; -}; - -struct _GstGnomeVFSSrcClass -{ - GstBaseSrcClass basesrc_class; -}; - -GType gst_gnome_vfs_src_get_type (void); - -G_END_DECLS - -#endif /* __GST_GNOME_VFS_SRC_H__ */ - - diff --git a/ext/gnomevfs/gstgnomevfsuri.c b/ext/gnomevfs/gstgnomevfsuri.c deleted file mode 100644 index e811c31b..00000000 --- a/ext/gnomevfs/gstgnomevfsuri.c +++ /dev/null @@ -1,90 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * 2000 Wim Taymans <wtay@chello.be> - * 2001 Bastien Nocera <hadess@hadess.net> - * 2003 Colin Walters <walters@verbum.org> - * - * gstgnomevfssink.c: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <libgnomevfs/gnome-vfs.h> -#include "gstgnomevfsuri.h" - -#include <gst/gst.h> - -/* FIXME: move this to source and sink and remove this file: - * e.g. sinks cannot save to http:// and src cannot read from burn:// - */ -static gpointer -_internal_get_supported_uris (gpointer data) -{ - /* no dav/davs in the list, because they don't appear to be reliable enough */ - const gchar *uris[] = { - "http://localhost/bla", - "https://localhost/bla", - "file:///bla", - "smb://localhost/bla", - "ftp://localhost/bla", - "sftp://localhost/bla", - "nfs://localhost/bla", - "ssh://localhost/bla", - "burn://" - }; - GnomeVFSURI *uri; - gchar **result; - gint n, r = 0; - - result = g_new0 (gchar *, G_N_ELEMENTS (uris) + 1); - for (n = 0; n < G_N_ELEMENTS (uris); n++) { - uri = gnome_vfs_uri_new (uris[n]); - if (uri != NULL) { - gchar *protocol = g_strdup (uris[n]); - gint n; - - gnome_vfs_uri_unref (uri); - for (n = 0; protocol[n] != '\0'; n++) { - if (protocol[n] == ':') { - protocol[n] = '\0'; - break; - } - } - - GST_DEBUG ("adding protocol '%s'", protocol); - result[r++] = protocol; - } else { - GST_DEBUG ("could not create GnomeVfsUri from '%s'", uris[n]); - } - } - result[r] = NULL; - - return result; -} - -gchar ** -gst_gnomevfs_get_supported_uris (void) -{ - static GOnce once = G_ONCE_INIT; - - g_once (&once, _internal_get_supported_uris, NULL); - return (gchar **) once.retval; -} diff --git a/ext/gnomevfs/gstgnomevfsuri.h b/ext/gnomevfs/gstgnomevfsuri.h deleted file mode 100644 index f99b1f7e..00000000 --- a/ext/gnomevfs/gstgnomevfsuri.h +++ /dev/null @@ -1,32 +0,0 @@ -/* GStreamer - * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_GNOME_VFS_URI_H__ -#define __GST_GNOME_VFS_URI_H__ - -#include <libgnomevfs/gnome-vfs.h> - -G_BEGIN_DECLS - -gchar **gst_gnomevfs_get_supported_uris (void); - -G_END_DECLS - -#endif /* __GST_GNOME_VFS_URI_H__ */ diff --git a/ext/libvisual/Makefile.am b/ext/libvisual/Makefile.am deleted file mode 100644 index d5d02136..00000000 --- a/ext/libvisual/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -plugin_LTLIBRARIES = libgstlibvisual.la - -libgstlibvisual_la_SOURCES = visual.c -libgstlibvisual_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(LIBVISUAL_CFLAGS) -libgstlibvisual_la_LIBADD = $(GST_BASE_LIBS) $(LIBVISUAL_LIBS) -libgstlibvisual_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstlibvisual_la_LIBTOOLFLAGS = --tag=disable-static - diff --git a/ext/libvisual/visual.c b/ext/libvisual/visual.c deleted file mode 100644 index 8e0ef0ea..00000000 --- a/ext/libvisual/visual.c +++ /dev/null @@ -1,960 +0,0 @@ -/* GStreamer - * Copyright (C) 2004 Benjamin Otte <otte@gnome.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <gst/gst.h> -#include <gst/base/gstadapter.h> -#include <gst/video/video.h> -#include <gst/audio/audio.h> -#include <libvisual/libvisual.h> - -#define GST_TYPE_VISUAL (gst_visual_get_type()) -#define GST_IS_VISUAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VISUAL)) -#define GST_VISUAL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VISUAL,GstVisual)) -#define GST_IS_VISUAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VISUAL)) -#define GST_VISUAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VISUAL,GstVisualClass)) -#define GST_VISUAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VISUAL, GstVisualClass)) - -typedef struct _GstVisual GstVisual; -typedef struct _GstVisualClass GstVisualClass; - -GST_DEBUG_CATEGORY_STATIC (libvisual_debug); -#define GST_CAT_DEFAULT (libvisual_debug) - -struct _GstVisual -{ - GstElement element; - - /* pads */ - GstPad *sinkpad; - GstPad *srcpad; - GstSegment segment; - - /* libvisual stuff */ - VisAudio *audio; - VisVideo *video; - VisActor *actor; - - /* audio/video state */ - gint channels; - gint rate; /* Input samplerate */ - gint bps; - - /* framerate numerator & denominator */ - gint fps_n; - gint fps_d; - gint width; - gint height; - GstClockTime duration; - guint outsize; - - /* samples per frame based on caps */ - guint spf; - - /* state stuff */ - GstAdapter *adapter; - guint count; - - /* QoS stuff *//* with LOCK */ - gdouble proportion; - GstClockTime earliest_time; -}; - -struct _GstVisualClass -{ - GstElementClass parent_class; - - VisPluginRef *plugin; -}; - -GType gst_visual_get_type (void); - - -static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN "; " -#if G_BYTE_ORDER == G_BIG_ENDIAN - GST_VIDEO_CAPS_RGB "; " -#else - GST_VIDEO_CAPS_BGR "; " -#endif - GST_VIDEO_CAPS_RGB_16) - ); - -static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-int, " - "width = (int) 16, " - "depth = (int) 16, " - "endianness = (int) BYTE_ORDER, " - "signed = (boolean) TRUE, " "channels = (int) { 1, 2 }, " -#if defined(VISUAL_API_VERSION) && VISUAL_API_VERSION >= 4000 && VISUAL_API_VERSION < 5000 - "rate = (int) { 8000, 11250, 22500, 32000, 44100, 48000, 96000 }" -#else - "rate = (int) [ 1000, MAX ]" -#endif - ) - ); - - -static void gst_visual_class_init (gpointer g_class, gpointer class_data); -static void gst_visual_init (GstVisual * visual); -static void gst_visual_dispose (GObject * object); - -static GstStateChangeReturn gst_visual_change_state (GstElement * element, - GstStateChange transition); -static GstFlowReturn gst_visual_chain (GstPad * pad, GstBuffer * buffer); -static gboolean gst_visual_sink_event (GstPad * pad, GstEvent * event); -static gboolean gst_visual_src_event (GstPad * pad, GstEvent * event); - -static gboolean gst_visual_sink_setcaps (GstPad * pad, GstCaps * caps); -static gboolean gst_visual_src_setcaps (GstPad * pad, GstCaps * caps); -static GstCaps *gst_visual_getcaps (GstPad * pad); -static void libvisual_log_handler (const char *message, const char *funcname, - void *priv); - -static GstElementClass *parent_class = NULL; - -GType -gst_visual_get_type (void) -{ - static GType type = 0; - - if (G_UNLIKELY (type == 0)) { - static const GTypeInfo info = { - sizeof (GstVisualClass), - NULL, - NULL, - gst_visual_class_init, - NULL, - NULL, - sizeof (GstVisual), - 0, - (GInstanceInitFunc) gst_visual_init, - }; - - type = g_type_register_static (GST_TYPE_ELEMENT, "GstVisual", &info, 0); - } - return type; -} - -static void -libvisual_log_handler (const char *message, const char *funcname, void *priv) -{ - GST_CAT_LEVEL_LOG (libvisual_debug, (GstDebugLevel) (priv), NULL, "%s - %s", - funcname, message); -} - -static void -gst_visual_class_init (gpointer g_class, gpointer class_data) -{ - GstVisualClass *klass = GST_VISUAL_CLASS (g_class); - GstElementClass *element = GST_ELEMENT_CLASS (g_class); - GObjectClass *object = G_OBJECT_CLASS (g_class); - - klass->plugin = class_data; - - element->change_state = gst_visual_change_state; - - if (class_data == NULL) { - parent_class = g_type_class_peek_parent (g_class); - } else { - GstElementDetails details = { - NULL, - "Visualization", - klass->plugin->info->about, - "Benjamin Otte <otte@gnome.org>" - }; - - details.longname = g_strdup_printf ("libvisual %s plugin v.%s", - klass->plugin->info->name, klass->plugin->info->version); - - /* FIXME: improve to only register what plugin supports? */ - gst_element_class_add_pad_template (element, - gst_static_pad_template_get (&src_template)); - gst_element_class_add_pad_template (element, - gst_static_pad_template_get (&sink_template)); - gst_element_class_set_details (element, &details); - g_free (details.longname); - } - - object->dispose = gst_visual_dispose; -} - -static void -gst_visual_init (GstVisual * visual) -{ - /* create the sink and src pads */ - visual->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink"); - gst_pad_set_setcaps_function (visual->sinkpad, gst_visual_sink_setcaps); - gst_pad_set_chain_function (visual->sinkpad, gst_visual_chain); - gst_pad_set_event_function (visual->sinkpad, gst_visual_sink_event); - gst_element_add_pad (GST_ELEMENT (visual), visual->sinkpad); - - visual->srcpad = gst_pad_new_from_static_template (&src_template, "src"); - gst_pad_set_setcaps_function (visual->srcpad, gst_visual_src_setcaps); - gst_pad_set_getcaps_function (visual->srcpad, gst_visual_getcaps); - gst_pad_set_event_function (visual->srcpad, gst_visual_src_event); - gst_element_add_pad (GST_ELEMENT (visual), visual->srcpad); - - visual->adapter = gst_adapter_new (); -} - -static void -gst_visual_clear_actors (GstVisual * visual) -{ - if (visual->actor) { - visual_object_unref (VISUAL_OBJECT (visual->actor)); - visual->actor = NULL; - } - if (visual->video) { - visual_object_unref (VISUAL_OBJECT (visual->video)); - visual->video = NULL; - } - if (visual->audio) { - visual_object_unref (VISUAL_OBJECT (visual->audio)); - visual->audio = NULL; - } -} - -static void -gst_visual_dispose (GObject * object) -{ - GstVisual *visual = GST_VISUAL (object); - - if (visual->adapter) { - g_object_unref (visual->adapter); - visual->adapter = NULL; - } - gst_visual_clear_actors (visual); - - GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object)); -} - -static void -gst_visual_reset (GstVisual * visual) -{ - gst_adapter_clear (visual->adapter); - gst_segment_init (&visual->segment, GST_FORMAT_UNDEFINED); - - GST_OBJECT_LOCK (visual); - visual->proportion = 1.0; - visual->earliest_time = -1; - GST_OBJECT_UNLOCK (visual); -} - -static GstCaps * -gst_visual_getcaps (GstPad * pad) -{ - GstCaps *ret; - GstVisual *visual = GST_VISUAL (gst_pad_get_parent (pad)); - int depths; - - if (!visual->actor) { - ret = gst_caps_copy (gst_pad_get_pad_template_caps (visual->srcpad)); - goto beach; - } - - ret = gst_caps_new_empty (); - depths = visual_actor_get_supported_depth (visual->actor); - if (depths < 0) { - /* FIXME: set an error */ - goto beach; - } - if (depths == VISUAL_VIDEO_DEPTH_GL) { - /* We can't handle GL only plugins */ - goto beach; - } - - GST_DEBUG_OBJECT (visual, "libvisual plugin supports depths %u (0x%04x)", - depths, depths); - /* if (depths & VISUAL_VIDEO_DEPTH_32BIT) Always supports 32bit output */ - gst_caps_append (ret, gst_caps_from_string (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN)); - - if (depths & VISUAL_VIDEO_DEPTH_24BIT) { -#if G_BYTE_ORDER == G_BIG_ENDIAN - gst_caps_append (ret, gst_caps_from_string (GST_VIDEO_CAPS_RGB)); -#else - gst_caps_append (ret, gst_caps_from_string (GST_VIDEO_CAPS_BGR)); -#endif - } - if (depths & VISUAL_VIDEO_DEPTH_16BIT) { - gst_caps_append (ret, gst_caps_from_string (GST_VIDEO_CAPS_RGB_16)); - } - -beach: - - GST_DEBUG_OBJECT (visual, "returning caps %" GST_PTR_FORMAT, ret); - gst_object_unref (visual); - return ret; -} - -static gboolean -gst_visual_src_setcaps (GstPad * pad, GstCaps * caps) -{ - GstVisual *visual = GST_VISUAL (gst_pad_get_parent (pad)); - GstStructure *structure; - gint depth, pitch; - - structure = gst_caps_get_structure (caps, 0); - - GST_DEBUG_OBJECT (visual, "src pad got caps %" GST_PTR_FORMAT, caps); - - if (!gst_structure_get_int (structure, "width", &visual->width)) - goto error; - if (!gst_structure_get_int (structure, "height", &visual->height)) - goto error; - if (!gst_structure_get_int (structure, "bpp", &depth)) - goto error; - if (!gst_structure_get_fraction (structure, "framerate", &visual->fps_n, - &visual->fps_d)) - goto error; - - visual_video_set_depth (visual->video, - visual_video_depth_enum_from_value (depth)); - visual_video_set_dimension (visual->video, visual->width, visual->height); - pitch = GST_ROUND_UP_4 (visual->width * visual->video->bpp); - visual_video_set_pitch (visual->video, pitch); - visual_actor_video_negotiate (visual->actor, 0, FALSE, FALSE); - - /* precalc some values */ - visual->outsize = visual->video->height * pitch; - visual->spf = - gst_util_uint64_scale_int (visual->rate, visual->fps_d, visual->fps_n); - visual->duration = - gst_util_uint64_scale_int (GST_SECOND, visual->fps_d, visual->fps_n); - - gst_object_unref (visual); - return TRUE; - - /* ERRORS */ -error: - { - GST_DEBUG_OBJECT (visual, "error parsing caps"); - gst_object_unref (visual); - return FALSE; - } -} - -static gboolean -gst_visual_sink_setcaps (GstPad * pad, GstCaps * caps) -{ - GstVisual *visual = GST_VISUAL (gst_pad_get_parent (pad)); - GstStructure *structure; - - structure = gst_caps_get_structure (caps, 0); - - gst_structure_get_int (structure, "channels", &visual->channels); - gst_structure_get_int (structure, "rate", &visual->rate); - - /* this is how many samples we need to fill one frame at the requested - * framerate. */ - if (visual->fps_n != 0) { - visual->spf = - gst_util_uint64_scale_int (visual->rate, visual->fps_d, visual->fps_n); - } - visual->bps = visual->channels * sizeof (gint16); - - gst_object_unref (visual); - return TRUE; -} - -static gboolean -gst_vis_src_negotiate (GstVisual * visual) -{ - GstCaps *othercaps, *target, *intersect; - GstStructure *structure; - GstCaps *caps; - - caps = gst_pad_get_caps (visual->srcpad); - - /* see what the peer can do */ - othercaps = gst_pad_peer_get_caps (visual->srcpad); - if (othercaps) { - intersect = gst_caps_intersect (othercaps, caps); - gst_caps_unref (othercaps); - gst_caps_unref (caps); - - if (gst_caps_is_empty (intersect)) - goto no_format; - - target = gst_caps_copy_nth (intersect, 0); - gst_caps_unref (intersect); - } else { - /* need a copy, we'll be modifying it when fixating */ - target = gst_caps_copy (caps); - gst_caps_unref (caps); - } - - /* fixate in case something is not fixed. This does nothing if the value is - * already fixed. For video we always try to fixate to something like - * 320x240x30 by convention. */ - structure = gst_caps_get_structure (target, 0); - gst_structure_fixate_field_nearest_int (structure, "width", 320); - gst_structure_fixate_field_nearest_int (structure, "height", 240); - gst_structure_fixate_field_nearest_fraction (structure, "framerate", 30, 1); - - gst_pad_set_caps (visual->srcpad, target); - gst_caps_unref (target); - - return TRUE; - - /* ERRORS */ -no_format: - { - GST_ELEMENT_ERROR (visual, STREAM, FORMAT, (NULL), - ("could not negotiate output format")); - gst_caps_unref (intersect); - return FALSE; - } -} - -static gboolean -gst_visual_sink_event (GstPad * pad, GstEvent * event) -{ - GstVisual *visual; - gboolean res; - - visual = GST_VISUAL (gst_pad_get_parent (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - res = gst_pad_push_event (visual->srcpad, event); - break; - case GST_EVENT_FLUSH_STOP: - /* reset QoS and adapter. */ - gst_visual_reset (visual); - res = gst_pad_push_event (visual->srcpad, event); - break; - case GST_EVENT_NEWSEGMENT: - { - GstFormat format; - gdouble rate, arate; - gint64 start, stop, time; - gboolean update; - - /* the newsegment values are used to clip the input samples - * and to convert the incomming timestamps to running time so - * we can do QoS */ - gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, - &start, &stop, &time); - - /* now configure the values */ - gst_segment_set_newsegment_full (&visual->segment, update, - rate, arate, format, start, stop, time); - - /* and forward */ - res = gst_pad_push_event (visual->srcpad, event); - break; - } - default: - res = gst_pad_push_event (visual->srcpad, event); - break; - } - - gst_object_unref (visual); - return res; -} - -static gboolean -gst_visual_src_event (GstPad * pad, GstEvent * event) -{ - GstVisual *visual; - gboolean res; - - visual = GST_VISUAL (gst_pad_get_parent (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_QOS: - { - gdouble proportion; - GstClockTimeDiff diff; - GstClockTime timestamp; - - gst_event_parse_qos (event, &proportion, &diff, ×tamp); - - /* save stuff for the _chain function */ - GST_OBJECT_LOCK (visual); - visual->proportion = proportion; - if (diff >= 0) - /* we're late, this is a good estimate for next displayable - * frame (see part-qos.txt) */ - visual->earliest_time = timestamp + 2 * diff + visual->duration; - else - visual->earliest_time = timestamp + diff; - - GST_OBJECT_UNLOCK (visual); - - res = gst_pad_push_event (visual->sinkpad, event); - break; - } - default: - res = gst_pad_push_event (visual->sinkpad, event); - break; - } - - gst_object_unref (visual); - return res; -} - -/* allocate and output buffer, if no format was negotiated, this - * function will negotiate one. After calling this function, a - * reverse negotiation could have happened. */ -static GstFlowReturn -get_buffer (GstVisual * visual, GstBuffer ** outbuf) -{ - GstFlowReturn ret; - - /* we don't know an output format yet, pick one */ - if (GST_PAD_CAPS (visual->srcpad) == NULL) { - if (!gst_vis_src_negotiate (visual)) - return GST_FLOW_NOT_NEGOTIATED; - } - - GST_DEBUG_OBJECT (visual, "allocating output buffer with caps %" - GST_PTR_FORMAT, GST_PAD_CAPS (visual->srcpad)); - - /* now allocate a buffer with the last negotiated format. - * Downstream could renegotiate a new format, which will trigger - * our setcaps function on the source pad. */ - ret = - gst_pad_alloc_buffer_and_set_caps (visual->srcpad, - GST_BUFFER_OFFSET_NONE, visual->outsize, - GST_PAD_CAPS (visual->srcpad), outbuf); - - /* no buffer allocated, we don't care why. */ - if (ret != GST_FLOW_OK) - return ret; - - return GST_FLOW_OK; -} - -static GstFlowReturn -gst_visual_chain (GstPad * pad, GstBuffer * buffer) -{ - GstBuffer *outbuf = NULL; - guint i; - GstVisual *visual = GST_VISUAL (gst_pad_get_parent (pad)); - GstFlowReturn ret = GST_FLOW_OK; - guint avail; - - GST_DEBUG_OBJECT (visual, "chain function called"); - - /* If we don't have an output format yet, preallocate a buffer to try and - * set one */ - if (GST_PAD_CAPS (visual->srcpad) == NULL) { - ret = get_buffer (visual, &outbuf); - if (ret != GST_FLOW_OK) { - gst_buffer_unref (buffer); - goto beach; - } - } - - /* resync on DISCONT */ - if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) { - gst_adapter_clear (visual->adapter); - } - - GST_DEBUG_OBJECT (visual, - "Input buffer has %d samples, time=%" G_GUINT64_FORMAT, - GST_BUFFER_SIZE (buffer) / visual->bps, GST_BUFFER_TIMESTAMP (buffer)); - - gst_adapter_push (visual->adapter, buffer); - - while (TRUE) { - gboolean need_skip; - const guint16 *data; - guint64 dist, timestamp; - - GST_DEBUG_OBJECT (visual, "processing buffer"); - - avail = gst_adapter_available (visual->adapter); - GST_DEBUG_OBJECT (visual, "avail now %u", avail); - - /* we need at least 512 samples */ - if (avail < 512 * visual->bps) - break; - - /* we need at least enough samples to make one frame */ - if (avail < visual->spf * visual->bps) - break; - - /* get timestamp of the current adapter byte */ - timestamp = gst_adapter_prev_timestamp (visual->adapter, &dist); - if (GST_CLOCK_TIME_IS_VALID (timestamp)) { - /* convert bytes to time */ - dist /= visual->bps; - timestamp += gst_util_uint64_scale_int (dist, GST_SECOND, visual->rate); - } - - if (timestamp != -1) { - gint64 qostime; - - /* QoS is done on running time */ - qostime = gst_segment_to_running_time (&visual->segment, GST_FORMAT_TIME, - timestamp); - - GST_OBJECT_LOCK (visual); - /* check for QoS, don't compute buffers that are known to be late */ - need_skip = visual->earliest_time != -1 && - qostime <= visual->earliest_time; - GST_OBJECT_UNLOCK (visual); - - if (need_skip) { - GST_WARNING_OBJECT (visual, - "QoS: skip ts: %" GST_TIME_FORMAT ", earliest: %" GST_TIME_FORMAT, - GST_TIME_ARGS (qostime), GST_TIME_ARGS (visual->earliest_time)); - goto skip; - } - } - - /* Read 512 samples per channel */ - data = - (const guint16 *) gst_adapter_peek (visual->adapter, 512 * visual->bps); - -#if defined(VISUAL_API_VERSION) && VISUAL_API_VERSION >= 4000 && VISUAL_API_VERSION < 5000 - { - VisBuffer *lbuf, *rbuf; - guint16 ldata[512], rdata[512]; - VisAudioSampleRateType rate; - - lbuf = visual_buffer_new_with_buffer (ldata, sizeof (ldata), NULL); - rbuf = visual_buffer_new_with_buffer (rdata, sizeof (rdata), NULL); - - if (visual->channels == 2) { - for (i = 0; i < 512; i++) { - ldata[i] = *data++; - rdata[i] = *data++; - } - } else { - for (i = 0; i < 512; i++) { - ldata[i] = *data; - rdata[i] = *data++; - } - } - - switch (visual->rate) { - case 8000: - rate = VISUAL_AUDIO_SAMPLE_RATE_8000; - break; - case 11250: - rate = VISUAL_AUDIO_SAMPLE_RATE_11250; - break; - case 22500: - rate = VISUAL_AUDIO_SAMPLE_RATE_22500; - break; - case 32000: - rate = VISUAL_AUDIO_SAMPLE_RATE_32000; - break; - case 44100: - rate = VISUAL_AUDIO_SAMPLE_RATE_44100; - break; - case 48000: - rate = VISUAL_AUDIO_SAMPLE_RATE_48000; - break; - case 96000: - rate = VISUAL_AUDIO_SAMPLE_RATE_96000; - break; - default: - visual_object_unref (VISUAL_OBJECT (lbuf)); - visual_object_unref (VISUAL_OBJECT (rbuf)); - GST_ERROR_OBJECT (visual, "unsupported rate %d", visual->rate); - ret = GST_FLOW_ERROR; - goto beach; - break; - } - - visual_audio_samplepool_input_channel (visual->audio->samplepool, - lbuf, - rate, VISUAL_AUDIO_SAMPLE_FORMAT_S16, VISUAL_AUDIO_CHANNEL_LEFT); - visual_audio_samplepool_input_channel (visual->audio->samplepool, - rbuf, - rate, VISUAL_AUDIO_SAMPLE_FORMAT_S16, VISUAL_AUDIO_CHANNEL_RIGHT); - - visual_object_unref (VISUAL_OBJECT (lbuf)); - visual_object_unref (VISUAL_OBJECT (rbuf)); - - } -#else - if (visual->channels == 2) { - for (i = 0; i < 512; i++) { - visual->audio->plugpcm[0][i] = *data++; - visual->audio->plugpcm[1][i] = *data++; - } - } else { - for (i = 0; i < 512; i++) { - visual->audio->plugpcm[0][i] = *data; - visual->audio->plugpcm[1][i] = *data++; - } - } -#endif - - /* alloc a buffer if we don't have one yet, this happens - * when we pushed a buffer in this while loop before */ - if (outbuf == NULL) { - ret = get_buffer (visual, &outbuf); - if (ret != GST_FLOW_OK) { - goto beach; - } - } - visual_video_set_buffer (visual->video, GST_BUFFER_DATA (outbuf)); - visual_audio_analyze (visual->audio); - visual_actor_run (visual->actor, visual->audio); - visual_video_set_buffer (visual->video, NULL); - GST_DEBUG_OBJECT (visual, "rendered one frame"); - - GST_BUFFER_TIMESTAMP (outbuf) = timestamp; - GST_BUFFER_DURATION (outbuf) = visual->duration; - - ret = gst_pad_push (visual->srcpad, outbuf); - outbuf = NULL; - - skip: - GST_DEBUG_OBJECT (visual, "finished frame, flushing %u samples from input", - visual->spf); - - /* Flush out the number of samples per frame */ - gst_adapter_flush (visual->adapter, visual->spf * visual->bps); - - /* quit the loop if something was wrong */ - if (ret != GST_FLOW_OK) - break; - } - -beach: - - if (outbuf != NULL) - gst_buffer_unref (outbuf); - - gst_object_unref (visual); - - return ret; -} - -static GstStateChangeReturn -gst_visual_change_state (GstElement * element, GstStateChange transition) -{ - GstVisual *visual = GST_VISUAL (element); - GstStateChangeReturn ret; - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - visual->actor = - visual_actor_new (GST_VISUAL_GET_CLASS (visual)->plugin->info-> - plugname); - visual->video = visual_video_new (); - visual->audio = visual_audio_new (); - /* can't have a play without actors */ - if (!visual->actor || !visual->video) - goto no_actors; - - if (visual_actor_realize (visual->actor) != 0) - goto no_realize; - - visual_actor_set_video (visual->actor, visual->video); - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - gst_visual_reset (visual); - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_NULL: - gst_visual_clear_actors (visual); - break; - default: - break; - } - - return ret; - - /* ERRORS */ -no_actors: - { - GST_ELEMENT_ERROR (visual, LIBRARY, INIT, (NULL), - ("could not create actors")); - gst_visual_clear_actors (visual); - return GST_STATE_CHANGE_FAILURE; - } -no_realize: - { - GST_ELEMENT_ERROR (visual, LIBRARY, INIT, (NULL), - ("could not realize actor")); - gst_visual_clear_actors (visual); - return GST_STATE_CHANGE_FAILURE; - } -} - -static void -make_valid_name (char *name) -{ - /* - * Replace invalid chars with _ in the type name - */ - static const gchar extra_chars[] = "-_+"; - gchar *p = name; - - for (; *p; p++) { - int valid = ((p[0] >= 'A' && p[0] <= 'Z') || - (p[0] >= 'a' && p[0] <= 'z') || - (p[0] >= '0' && p[0] <= '9') || strchr (extra_chars, p[0])); - if (!valid) - *p = '_'; - } -} - -static gboolean -gst_visual_actor_plugin_is_gl (VisObject * plugin, const gchar * name) -{ - gboolean is_gl; - gint depth; - -#if !defined(VISUAL_API_VERSION) - - depth = VISUAL_PLUGIN_ACTOR (plugin)->depth; - is_gl = (depth == VISUAL_VIDEO_DEPTH_GL); - -#elif VISUAL_API_VERSION >= 4000 && VISUAL_API_VERSION < 5000 - - depth = VISUAL_ACTOR_PLUGIN (plugin)->vidoptions.depth; - /* FIXME: how to figure this out correctly in 0.4? */ - is_gl = (depth & VISUAL_VIDEO_DEPTH_GL) == VISUAL_VIDEO_DEPTH_GL; - -#else -# error what libvisual version is this? -#endif - - if (!is_gl) { - GST_DEBUG ("plugin %s is not a GL plugin (%d), registering", name, depth); - } else { - GST_DEBUG ("plugin %s is a GL plugin (%d), ignoring", name, depth); - } - - return is_gl; -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - guint i, count; - VisList *list; - - GST_DEBUG_CATEGORY_INIT (libvisual_debug, "libvisual", 0, - "libvisual audio visualisations"); - -#ifdef LIBVISUAL_PLUGINSBASEDIR - gst_plugin_add_dependency_simple (plugin, "HOME/.libvisual/actor", - LIBVISUAL_PLUGINSBASEDIR "/actor", NULL, GST_PLUGIN_DEPENDENCY_FLAG_NONE); -#endif - - visual_log_set_verboseness (VISUAL_LOG_VERBOSENESS_LOW); - visual_log_set_info_handler (libvisual_log_handler, (void *) GST_LEVEL_INFO); - visual_log_set_warning_handler (libvisual_log_handler, - (void *) GST_LEVEL_WARNING); - visual_log_set_critical_handler (libvisual_log_handler, - (void *) GST_LEVEL_ERROR); - visual_log_set_error_handler (libvisual_log_handler, - (void *) GST_LEVEL_ERROR); - - if (!visual_is_initialized ()) - if (visual_init (NULL, NULL) != 0) - return FALSE; - - list = visual_actor_get_list (); - -#if !defined(VISUAL_API_VERSION) - count = visual_list_count (list); -#elif VISUAL_API_VERSION >= 4000 && VISUAL_API_VERSION < 5000 - count = visual_collection_size (VISUAL_COLLECTION (list)); -#endif - - for (i = 0; i < count; i++) { - VisPluginRef *ref = visual_list_get (list, i); - VisPluginData *visplugin = NULL; - gboolean skip = FALSE; - GType type; - gchar *name; - GTypeInfo info = { - sizeof (GstVisualClass), - NULL, - NULL, - gst_visual_class_init, - NULL, - ref, - sizeof (GstVisual), - 0, - NULL - }; - - visplugin = visual_plugin_load (ref); - - if (ref->info->plugname == NULL) - continue; - - /* Blacklist some plugins */ - if (strcmp (ref->info->plugname, "gstreamer") == 0 || - strcmp (ref->info->plugname, "gdkpixbuf") == 0) { - skip = TRUE; - } else { - /* Ignore plugins that only support GL output for now */ - skip = gst_visual_actor_plugin_is_gl (visplugin->info->plugin, - visplugin->info->plugname); - } - - visual_plugin_unload (visplugin); - - if (!skip) { - name = g_strdup_printf ("GstVisual%s", ref->info->plugname); - make_valid_name (name); - type = g_type_register_static (GST_TYPE_VISUAL, name, &info, 0); - g_free (name); - - name = g_strdup_printf ("libvisual_%s", ref->info->plugname); - make_valid_name (name); - if (!gst_element_register (plugin, name, GST_RANK_NONE, type)) { - g_free (name); - return FALSE; - } - g_free (name); - } - } - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "libvisual", - "libvisual visualization plugins", - plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/ogg/Makefile.am b/ext/ogg/Makefile.am deleted file mode 100644 index 2d152335..00000000 --- a/ext/ogg/Makefile.am +++ /dev/null @@ -1,27 +0,0 @@ -plugin_LTLIBRARIES = libgstogg.la - -libgstogg_la_SOURCES = \ - gstogg.c \ - gstoggdemux.c \ - gstoggmux.c \ - gstogmparse.c \ - gstoggaviparse.c \ - gstoggparse.c \ - gstoggstream.c \ - gstoggstream.h \ - dirac_parse.c \ - dirac_parse.h \ - vorbis_parse.c - -noinst_HEADERS = \ - gstoggdemux.h gstoggmux.h - -libgstogg_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(OGG_CFLAGS) -libgstogg_la_LIBADD = \ - $(top_builddir)/gst-libs/gst/riff/libgstriff-$(GST_MAJORMINOR).la \ - $(top_builddir)/gst-libs/gst/tag/libgsttag-$(GST_MAJORMINOR).la \ - $(GST_BASE_LIBS) \ - $(OGG_LIBS) -libgstogg_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstogg_la_LIBTOOLFLAGS = --tag=disable-static - diff --git a/ext/ogg/README b/ext/ogg/README deleted file mode 100644 index 557e9d50..00000000 --- a/ext/ogg/README +++ /dev/null @@ -1,366 +0,0 @@ -This document describes some things to know about the Ogg format, as well -as implementation details in GStreamer. - -INTRODUCTION -============ - -ogg and the granulepos ----------------------- - -An ogg stream contains pages with a serial number and a granulepos. -The granulepos is a 64 bit signed integer. It is a value that in some way -represents a time since the start of the stream. -The interpretation as such is however both codec-specific and -stream-specific. - -ogg has no notion of time: it only knows about bytes and granulepos values -on pages. - -The granule position is just a number; the only guarantee for a valid ogg -stream is that within a logical stream, this number never decreases. - -While logically a granulepos value can be constructed for every ogg packet, -the page is marked with only one granulepos value: the granulepos of the -last packet to end on that page. - -theora and the granulepos -------------------------- - -The granulepos in theora is an encoding of the frame number of the last -key frame ("i frame"), and the number of frames since the last key frame -("p frame"). The granulepos is constructed as the sum of the first number, -shifted to the left for granuleshift bits, and the second number: -granulepos = pframe << granuleshift + iframe - -(This means that given a framenumber or a timestamp, one cannot generate - the one and only granulepos for that page; several granulepos possibilities - correspond to this frame number. You also need the last keyframe, as well - as the granuleshift. - However, given a granulepos, the theora codec can still map that to a - unique timestamp and frame number for that theora stream) - - Note: currently theora stores the "presentation time" as the granulepos; - ie. a first data page with one packet contains one video frame and - will be marked with 0/0. Changing that to be 1/0 (so that it - represents the number of decodable frames up to that point, like - for Vorbis) is being discussed. - -vorbis and granulepos ---------------------- - -In Vorbis, the granulepos represents the number of samples that can be -decoded from all packets up to that point. - -In GStreamer, the vorbisenc elements produces a stream where: -- OFFSET is the time corresponding to the granulepos - number of bytes produced before -- OFFSET_END is the granulepos of the produced vorbis buffer -- TIMESTAMP is the timestamp matching the begin of the buffer -- DURATION is set to the length in time of the buffer - -Ogg media mapping ------------------ - -Ogg defines a mapping for each media type that it embeds. - -For Vorbis: - - - 3 header pages, with granulepos 0. - - 1 page with 1 packet header identification - - N pages with 2 packets comments and codebooks - - granulepos is samplenumber of next page - - one packet can contain a variable number of samples but one frame - that should be handed to the vorbis decoder. - -For Theora - - - 3 header pages, with granulepos 0. - - 1 page with 1 packet header identification - - N pages with 2 packets comments and codebooks - - granulepos is framenumber of last packet in page, where framenumber - is a combination of keyframe number and p frames since keyframe. - - one packet contains 1 frame - - - - -DEMUXING -======== - -ogg demuxer ------------ - -This ogg demuxer has two modes of operation, which both share a significant -amount of code. The first mode is the streaming mode which is automatically -selected when the demuxer is connected to a non-getrange based element. When -connected to a getrange based element the ogg demuxer can do full seeking -with great efficiency. - -1) the streaming mode. - -In this mode, the ogg demuxer receives buffers in the _chain() function which -are then simply submited to the ogg sync layer. Pages are then processed when -the sync layer detects them, pads are created for new chains and packets are -sent to the peer elements of the pads. - -In this mode, no seeking is possible. This is the typical case when the -stream is read from a network source. - -In this mode, no setup is done at startup, the pages are just read and decoded. -A new logical chain is detected when one of the pages has the BOS flag set. At -this point the existing pads are removed and new pads are created for all the -logical streams in this new chain. - - -2) the random access mode. - - In this mode, the ogg file is first scanned to detect the position and length -of all chains. This scanning is performed using a recursive binary search -algorithm that is explained below. - - find_chains(start, end) - { - ret1 = read_next_pages (start); - ret2 = read_prev_page (end); - - if (WAS_HEADER (ret1)) { - } - else { - } - - } - - a) read first and last pages - - start end - V V - +-----------------------+-------------+--------------------+ - | 111 | 222 | 333 | - BOS BOS BOS EOS - - - after reading start, serial 111, BOS, chain[0] = 111 - after reading end, serial 333, EOS - - start serialno != end serialno, binary search start, (end-start)/2 - - start bisect end - V V V - +-----------------------+-------------+--------------------+ - | 111 | 222 | 333 | - - - after reading start, serial 111, BOS, chain[0] = 111 - after reading end, serial 222, EOS - - while ( - - - -testcases ---------- - - a) stream without BOS - - +----------------------------------------------------------+ - 111 | - EOS - - b) chained stream, first chain without BOS - - +-------------------+--------------------------------------+ - 111 | 222 | - BOS EOS - - - c) chained stream - - +-------------------+--------------------------------------+ - | 111 | 222 | - BOS BOS EOS - - - d) chained stream, second without BOS - - +-------------------+--------------------------------------+ - | 111 | 222 | - BOS EOS - -What can an ogg demuxer do? ---------------------------- - -An ogg demuxer can read pages and get the granulepos from them. -It can ask the decoder elements to convert a granulepos to time. - -An ogg demuxer can also get the granulepos of the first and the last page of a -stream to get the start and end timestamp of that stream. -It can also get the length in bytes of the stream -(when the peer is seekable, that is). - -An ogg demuxer is therefore basically able to seek to any byte position and -timestamp. - -When asked to seek to a given granulepos, the ogg demuxer should always convert -the value to a timestamp using the peer decoder element conversion function. It -can then binary search the file to eventually end up on the page with the given -granule pos or a granulepos with the same timestamp. - -Seeking in ogg currently ------------------------- - -When seeking in an ogg, the decoders can choose to forward the seek event as a -granulepos or a timestamp to the ogg demuxer. - -In the case of a granulepos, the ogg demuxer will seek back to the beginning of -the stream and skip pages until it finds one with the requested timestamp. - -In the case of a timestamp, the ogg demuxer also seeks back to the beginning of -the stream. For each page it reads, it asks the decoder element to convert the -granulepos back to a timestamp. The ogg demuxer keeps on skipping pages until -the page has a timestamp bigger or equal to the requested one. - -It is therefore important that the decoder elements in vorbis can convert a -granulepos into a timestamp or never seek on timestamp on the oggdemuxer. - -The default format on the oggdemuxer source pads is currently defined as a the -granulepos of the packets, it is also the value of the OFFSET field in the -GstBuffer. - -MUXING -====== - -Oggmux ------- - -The ogg muxer's job is to output complete Ogg pages such that the absolute -time represented by the valid (ie, not -1) granulepos values on those pages -never decreases. This has to be true for all logical streams in the group at -the same time. - -To achieve this, encoders are required to pass along the exact time that the -granulepos represents for each ogg packet that it pushes to the ogg muxer. -This is ESSENTIAL: without this exact time representation of the granulepos, -the muxer can not produce valid streams. - -The ogg muxer has a packet queue per sink pad. From this queue a page can -be flushed when: - - total byte size of queued packets exceeds a given value - - total time duration of queued packets exceeds a given value - - total byte size of queued packets exceeds maximum Ogg page size - - eos of the pad - - encoder sent a command to flush out an ogg page after this new packet - (in 0.8, through a flush event; in 0.10, with a GstOggBuffer) - - muxer wants a flush to happen (so it can output pages) - -The ogg muxer also has a page queue per sink pad. This queue collects -Ogg pages from the corresponding packet queue. Each page is also marked -with the timestamp that the granulepos in the header represents. - -A page can be flushed from this collection of page queues when: -- ideally, every page queue has at least one page with a valid granulepos - -> choose the page, from all queues, with the lowest timestamp value -- if not, muxer can wait if the following limits aren't reached: - - total byte size of any page queue exceeds a limit - - total time duration of any page queue exceeds a limit -- if this limit is reached, then: - - request a page flush from packet queue to page queue for each queue - that does not have pages - - now take the page from all queues with the lowest timestamp value - - make sure all later-coming data is marked as old, either to be still - output (but producing an invalid stream, though it can be fixed later) - or dropped (which means it's gone forever) - -The oggmuxer uses the offset fields to fill in the granulepos in the pages. - -GStreamer implementation details --------------------------------- -As said before, the basic rule is that the ogg muxer needs an exact time -representation for each granulepos. This needs to be provided by the encoder. - -Potential problems are: - - initial offsets for a raw stream need to be preserved somehow. Example: - if the first audio sample has time 0.5, the granulepos in the vorbis encoder - needs to be adjusted to take this into account. - - initial offsets may need be on rate boundaries. Example: - if the framerate is 5 fps, and the first video frame has time 0.1 s, the - granulepos cannot correctly represent this timestamp. - This can be handled out-of-band (initial offset in another muxing format, - skeleton track with initial offsets, ...) - -Given that the basic rule for muxing is that the muxer needs an exact timestamp -matching the granulepos, we need some way of communicating this time value -from encoders to the Ogg muxer. So we need a mechanism to communicate -a granulepos and its time representation for each GstBuffer. - -(This is an instance of a more generic problem - having a way to attach - more fields to a GstBuffer) - -Possible ways: -- setting TIMESTAMP to this value: bad - this value represents the end time - of the buffer, and thus conflicts with GStreamer's idea of what TIMESTAMP - is. This would cause problems muxing the encoded stream in other muxing - formats, or for streaming. Note that this is what was done in GStreamer 0.8 -- setting DURATION to GP_TIME - TIMESTAMP: bad - this breaks the concept of - duration for this frame. Take the video example above; each buffer would - have a correct timestamp, but always a 0.1 s duration as opposed to the - correct 0.2 s duration -- subclassing GstBuffer: clean, but requires a common header used between - ogg muxer and all encoders that can be muxed into ogg. Also, what if - a format can be muxed into more than one container, and they each have - their own "extra" info to communicate ? -- adding key/value pairs to GstBuffer: clean, but requires changes to - core. Also, the overhead of allocating e.g. a GstStructure for *each* buffer - may be expensive. -- "cheating": - - abuse OFFSET to store the timestamp matching this granulepos - - abuse OFFSET_END to store the granulepos value - The drawback here is that before, it made sense to use OFFSET and OFFSET_END - to store a byte count. Given that this is not used for anything critical - (you can't store a raw theora or vorbis stream in a file anyway), - this is what's being done for now. - -In practice ------------ -- all encoders of formats that can be muxed into Ogg produce a stream where: - - OFFSET is abused to be the timestamp corresponding exactly to the - granulepos - - OFFSET_END is abused to be the granulepos of the encoded theora buffer - - TIMESTAMP is the timestamp matching the begin of the buffer - - DURATION is the length in time of the buffer - -- initial delays should be handled in the GStreamer encoders by mangling - the granulepos of the encoded packet to take the delay into account as - best as possible and store that in OFFSET; - this then brings TIMESTAMP + DURATION to within less - than a frame period of the granulepos's time representation - The ogg muxer will then create new ogg packets with this OFFSET as - the granulepos. So in effect, the granulepos produced by the encoders - does not get used directly. - -TODO ----- -- decide on a proper mechanism for communicating extra per-buffer fields -- the ogg muxer sets timestamp and duration on outgoing ogg pages based on - timestamp/duration of incoming ogg packets. - Note that: - - since the ogg muxer *has* to output pages sorted by gp time, representing - end time of the page, this means that the buffer's timestamps are not - necessarily monotonically increasing - - timestamp + duration of buffers don't match up; the duration represents - the length of the ogg page *for that stream*. Hence, for a normal - two-stream file, the sum of all durations is twice the length of the - muxed file. - -TESTING -------- -Proper muxing can be tested by generating test files with command lines like: -- video and audio start from 0: -gst-launch -v videotestsrc ! theoraenc ! oggmux audiotestsrc ! audioconvert ! vorbisenc ! identity ! oggmux0. oggmux0. ! filesink location=test.ogg - -- video starts after audio: -gst-launch -v videotestsrc timestamp-offset=500000000 ! theoraenc ! oggmux audiotestsrc ! audioconvert ! vorbisenc ! identity ! oggmux0. oggmux0. ! filesink location=test.ogg - -- audio starts after video: -gst-launch -v videotestsrc ! theoraenc ! oggmux audiotestsrc timestamp-offset=500000000 ! audioconvert ! vorbisenc ! identity ! oggmux0. oggmux0. ! filesink location=test.ogg - -The resulting files can be verified with oggz-validate for correctness. diff --git a/ext/ogg/dirac_parse.c b/ext/ogg/dirac_parse.c deleted file mode 100644 index f7b483dc..00000000 --- a/ext/ogg/dirac_parse.c +++ /dev/null @@ -1,501 +0,0 @@ - -#include "dirac_parse.h" -#include <string.h> - -#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) - -typedef struct _Unpack Unpack; - -struct _Unpack -{ - unsigned char *data; - int n_bits_left; - int index; - int guard_bit; -}; - -static void schro_unpack_init_with_data (Unpack * unpack, unsigned char *data, - int n_bytes, unsigned int guard_bit); - -static unsigned int schro_unpack_decode_bit (Unpack * unpack); -static unsigned int schro_unpack_decode_uint (Unpack * unpack); - - -void schro_video_format_set_std_video_format (DiracSequenceHeader * format, - int index); -void schro_video_format_set_std_frame_rate (DiracSequenceHeader * format, - int index); -void schro_video_format_set_std_aspect_ratio (DiracSequenceHeader * format, - int index); -void schro_video_format_set_std_signal_range (DiracSequenceHeader * format, - int index); -void schro_video_format_set_std_colour_spec (DiracSequenceHeader * format, - int index); - - - - -int -dirac_sequence_header_parse (DiracSequenceHeader * header, - unsigned char *data, int n_bytes) -{ - int bit; - int index; - Unpack _unpack; - Unpack *unpack = &_unpack; - int major_version; - int minor_version; - int profile; - int level; - - memset (header, 0, sizeof (*header)); - - schro_unpack_init_with_data (unpack, data, n_bytes, 1); - - /* parse parameters */ - major_version = schro_unpack_decode_uint (unpack); - minor_version = schro_unpack_decode_uint (unpack); - profile = schro_unpack_decode_uint (unpack); - level = schro_unpack_decode_uint (unpack); - - /* base video header */ - index = schro_unpack_decode_uint (unpack); - schro_video_format_set_std_video_format (header, index); - - header->major_version = major_version; - header->minor_version = minor_version; - header->profile = profile; - header->level = level; - - /* source parameters */ - /* frame dimensions */ - bit = schro_unpack_decode_bit (unpack); - if (bit) { - header->width = schro_unpack_decode_uint (unpack); - header->height = schro_unpack_decode_uint (unpack); - } - - /* chroma header */ - bit = schro_unpack_decode_bit (unpack); - if (bit) { - header->chroma_format = schro_unpack_decode_uint (unpack); - } - - /* scan header */ - bit = schro_unpack_decode_bit (unpack); - if (bit) { - header->interlaced = schro_unpack_decode_bit (unpack); - if (header->interlaced) { - header->top_field_first = schro_unpack_decode_bit (unpack); - } - } - - /* frame rate */ - bit = schro_unpack_decode_bit (unpack); - if (bit) { - index = schro_unpack_decode_uint (unpack); - if (index == 0) { - header->frame_rate_numerator = schro_unpack_decode_uint (unpack); - header->frame_rate_denominator = schro_unpack_decode_uint (unpack); - } else { - schro_video_format_set_std_frame_rate (header, index); - } - } - - /* aspect ratio */ - bit = schro_unpack_decode_bit (unpack); - if (bit) { - index = schro_unpack_decode_uint (unpack); - if (index == 0) { - header->aspect_ratio_numerator = schro_unpack_decode_uint (unpack); - header->aspect_ratio_denominator = schro_unpack_decode_uint (unpack); - } else { - schro_video_format_set_std_aspect_ratio (header, index); - } - } - - /* clean area */ - bit = schro_unpack_decode_bit (unpack); - if (bit) { - header->clean_width = schro_unpack_decode_uint (unpack); - header->clean_height = schro_unpack_decode_uint (unpack); - header->left_offset = schro_unpack_decode_uint (unpack); - header->top_offset = schro_unpack_decode_uint (unpack); - } - - /* signal range */ - bit = schro_unpack_decode_bit (unpack); - if (bit) { - index = schro_unpack_decode_uint (unpack); - if (index == 0) { - header->luma_offset = schro_unpack_decode_uint (unpack); - header->luma_excursion = schro_unpack_decode_uint (unpack); - header->chroma_offset = schro_unpack_decode_uint (unpack); - header->chroma_excursion = schro_unpack_decode_uint (unpack); - } else { - schro_video_format_set_std_signal_range (header, index); - } - } - - /* colour spec */ - bit = schro_unpack_decode_bit (unpack); - if (bit) { - index = schro_unpack_decode_uint (unpack); - schro_video_format_set_std_colour_spec (header, index); - if (index == 0) { - /* colour primaries */ - bit = schro_unpack_decode_bit (unpack); - if (bit) { - header->colour_primaries = schro_unpack_decode_uint (unpack); - } - /* colour matrix */ - bit = schro_unpack_decode_bit (unpack); - if (bit) { - header->colour_matrix = schro_unpack_decode_uint (unpack); - } - /* transfer function */ - bit = schro_unpack_decode_bit (unpack); - if (bit) { - header->transfer_function = schro_unpack_decode_uint (unpack); - } - } - } - - header->interlaced_coding = schro_unpack_decode_uint (unpack); - - return 1; -} - -/* standard stuff */ - -static DiracSequenceHeader schro_video_formats[] = { - {0, 0, 0, 0, - 0, /* custom */ - 640, 480, SCHRO_CHROMA_420, - FALSE, FALSE, - 24000, 1001, 1, 1, - 640, 480, 0, 0, - 0, 255, 128, 255, - 0, 0, 0}, - {0, 0, 0, 0, - 1, /* QSIF525 */ - 176, 120, SCHRO_CHROMA_420, - FALSE, FALSE, - 15000, 1001, 10, 11, - 176, 120, 0, 0, - 0, 255, 128, 255, - 1, 1, 0}, - {0, 0, 0, 0, - 2, /* QCIF */ - 176, 144, SCHRO_CHROMA_420, - FALSE, TRUE, - 25, 2, 12, 11, - 176, 144, 0, 0, - 0, 255, 128, 255, - 2, 1, 0}, - {0, 0, 0, 0, - 3, /* SIF525 */ - 352, 240, SCHRO_CHROMA_420, - FALSE, FALSE, - 15000, 1001, 10, 11, - 352, 240, 0, 0, - 0, 255, 128, 255, - 1, 1, 0}, - {0, 0, 0, 0, - 4, /* CIF */ - 352, 288, SCHRO_CHROMA_420, - FALSE, TRUE, - 25, 2, 12, 11, - 352, 288, 0, 0, - 0, 255, 128, 255, - 2, 1, 0}, - {0, 0, 0, 0, - 5, /* 4SIF525 */ - 704, 480, SCHRO_CHROMA_420, - FALSE, FALSE, - 15000, 1001, 10, 11, - 704, 480, 0, 0, - 0, 255, 128, 255, - 1, 1, 0}, - {0, 0, 0, 0, - 6, /* 4CIF */ - 704, 576, SCHRO_CHROMA_420, - FALSE, TRUE, - 25, 2, 12, 11, - 704, 576, 0, 0, - 0, 255, 128, 255, - 2, 1, 0}, - {0, 0, 0, 0, - 7, /* SD480I-60 */ - 720, 480, SCHRO_CHROMA_422, - TRUE, FALSE, - 30000, 1001, 10, 11, - 704, 480, 8, 0, - 64, 876, 512, 896, - 1, 1, 0}, - {0, 0, 0, 0, - 8, /* SD576I-50 */ - 720, 576, SCHRO_CHROMA_422, - TRUE, TRUE, - 25, 1, 12, 11, - 704, 576, 8, 0, - 64, 876, 512, 896, - 2, 1, 0}, - {0, 0, 0, 0, - 9, /* HD720P-60 */ - 1280, 720, SCHRO_CHROMA_422, - FALSE, TRUE, - 60000, 1001, 1, 1, - 1280, 720, 0, 0, - 64, 876, 512, 896, - 0, 0, 0}, - {0, 0, 0, 0, - 10, /* HD720P-50 */ - 1280, 720, SCHRO_CHROMA_422, - FALSE, TRUE, - 50, 1, 1, 1, - 1280, 720, 0, 0, - 64, 876, 512, 896, - 0, 0, 0}, - {0, 0, 0, 0, - 11, /* HD1080I-60 */ - 1920, 1080, SCHRO_CHROMA_422, - TRUE, TRUE, - 30000, 1001, 1, 1, - 1920, 1080, 0, 0, - 64, 876, 512, 896, - 0, 0, 0}, - {0, 0, 0, 0, - 12, /* HD1080I-50 */ - 1920, 1080, SCHRO_CHROMA_422, - TRUE, TRUE, - 25, 1, 1, 1, - 1920, 1080, 0, 0, - 64, 876, 512, 896, - 0, 0, 0}, - {0, 0, 0, 0, - 13, /* HD1080P-60 */ - 1920, 1080, SCHRO_CHROMA_422, - FALSE, TRUE, - 60000, 1001, 1, 1, - 1920, 1080, 0, 0, - 64, 876, 512, 896, - 0, 0, 0}, - {0, 0, 0, 0, - 14, /* HD1080P-50 */ - 1920, 1080, SCHRO_CHROMA_422, - FALSE, TRUE, - 50, 1, 1, 1, - 1920, 1080, 0, 0, - 64, 876, 512, 896, - 0, 0, 0}, - {0, 0, 0, 0, - 15, /* DC2K */ - 2048, 1080, SCHRO_CHROMA_444, - FALSE, TRUE, - 24, 1, 1, 1, - 2048, 1080, 0, 0, - 256, 3504, 2048, 3584, - 3, 0, 0}, - {0, 0, 0, 0, - 16, /* DC4K */ - 4096, 2160, SCHRO_CHROMA_444, - FALSE, TRUE, - 24, 1, 1, 1, - 2048, 1536, 0, 0, - 256, 3504, 2048, 3584, - 3, 0, 0}, -}; - -void -schro_video_format_set_std_video_format (DiracSequenceHeader * format, - int index) -{ - - if (index < 0 || index >= ARRAY_SIZE (schro_video_formats)) { - return; - } - - memcpy (format, schro_video_formats + index, sizeof (DiracSequenceHeader)); -} - -typedef struct _SchroFrameRate SchroFrameRate; -struct _SchroFrameRate -{ - int numerator; - int denominator; -}; - -static SchroFrameRate schro_frame_rates[] = { - {0, 0}, - {24000, 1001}, - {24, 1}, - {25, 1}, - {30000, 1001}, - {30, 1}, - {50, 1}, - {60000, 1001}, - {60, 1}, - {15000, 1001}, - {25, 2} -}; - -void -schro_video_format_set_std_frame_rate (DiracSequenceHeader * format, int index) -{ - if (index < 1 || index >= ARRAY_SIZE (schro_frame_rates)) { - return; - } - - format->frame_rate_numerator = schro_frame_rates[index].numerator; - format->frame_rate_denominator = schro_frame_rates[index].denominator; -} - -typedef struct _SchroPixelAspectRatio SchroPixelAspectRatio; -struct _SchroPixelAspectRatio -{ - int numerator; - int denominator; -}; - -static const SchroPixelAspectRatio schro_aspect_ratios[] = { - {0, 0}, - {1, 1}, - {10, 11}, - {12, 11}, - {40, 33}, - {16, 11}, - {4, 3} -}; - -void -schro_video_format_set_std_aspect_ratio (DiracSequenceHeader * format, - int index) -{ - if (index < 1 || index >= ARRAY_SIZE (schro_aspect_ratios)) { - return; - } - - format->aspect_ratio_numerator = schro_aspect_ratios[index].numerator; - format->aspect_ratio_denominator = schro_aspect_ratios[index].denominator; - -} - -typedef struct _SchroSignalRangeStruct SchroSignalRangeStruct; -struct _SchroSignalRangeStruct -{ - int luma_offset; - int luma_excursion; - int chroma_offset; - int chroma_excursion; -}; - -static const SchroSignalRangeStruct schro_signal_ranges[] = { - {0, 0, 0, 0}, - {0, 255, 128, 255}, - {16, 219, 128, 224}, - {64, 876, 512, 896}, - {256, 3504, 2048, 3584} -}; - -void -schro_video_format_set_std_signal_range (DiracSequenceHeader * format, int i) -{ - if (i < 1 || i >= ARRAY_SIZE (schro_signal_ranges)) { - return; - } - - format->luma_offset = schro_signal_ranges[i].luma_offset; - format->luma_excursion = schro_signal_ranges[i].luma_excursion; - format->chroma_offset = schro_signal_ranges[i].chroma_offset; - format->chroma_excursion = schro_signal_ranges[i].chroma_excursion; -} - -typedef struct _SchroColourSpecStruct SchroColourSpecStruct; -struct _SchroColourSpecStruct -{ - int colour_primaries; - int colour_matrix; - int transfer_function; -}; - -static const SchroColourSpecStruct schro_colour_specs[] = { - { /* Custom */ - SCHRO_COLOUR_PRIMARY_HDTV, - SCHRO_COLOUR_MATRIX_HDTV, - SCHRO_TRANSFER_CHAR_TV_GAMMA}, - { /* SDTV 525 */ - SCHRO_COLOUR_PRIMARY_SDTV_525, - SCHRO_COLOUR_MATRIX_SDTV, - SCHRO_TRANSFER_CHAR_TV_GAMMA}, - { /* SDTV 625 */ - SCHRO_COLOUR_PRIMARY_SDTV_625, - SCHRO_COLOUR_MATRIX_SDTV, - SCHRO_TRANSFER_CHAR_TV_GAMMA}, - { /* HDTV */ - SCHRO_COLOUR_PRIMARY_HDTV, - SCHRO_COLOUR_MATRIX_HDTV, - SCHRO_TRANSFER_CHAR_TV_GAMMA}, - { /* Cinema */ - SCHRO_COLOUR_PRIMARY_CINEMA, - SCHRO_COLOUR_MATRIX_HDTV, - SCHRO_TRANSFER_CHAR_TV_GAMMA} -}; - -void -schro_video_format_set_std_colour_spec (DiracSequenceHeader * format, int i) -{ - if (i < 0 || i >= ARRAY_SIZE (schro_colour_specs)) { - return; - } - - format->colour_primaries = schro_colour_specs[i].colour_primaries; - format->colour_matrix = schro_colour_specs[i].colour_matrix; - format->transfer_function = schro_colour_specs[i].transfer_function; -} - - -/* unpack */ - -static void -schro_unpack_init_with_data (Unpack * unpack, unsigned char *data, - int n_bytes, unsigned int guard_bit) -{ - memset (unpack, 0, sizeof (Unpack)); - - unpack->data = data; - unpack->n_bits_left = 8 * n_bytes; - unpack->guard_bit = guard_bit; -} - -static unsigned int -schro_unpack_decode_bit (Unpack * unpack) -{ - int bit; - - if (unpack->n_bits_left < 1) { - return unpack->guard_bit; - } - bit = (unpack->data[unpack->index >> 3] >> (7 - (unpack->index & 7))) & 1; - unpack->index++; - unpack->n_bits_left--; - - return bit; -} - -static unsigned int -schro_unpack_decode_uint (Unpack * unpack) -{ - int count; - int value; - - count = 0; - value = 0; - while (!schro_unpack_decode_bit (unpack)) { - count++; - value <<= 1; - value |= schro_unpack_decode_bit (unpack); - } - - return (1 << count) - 1 + value; -} diff --git a/ext/ogg/dirac_parse.h b/ext/ogg/dirac_parse.h deleted file mode 100644 index 9dc4ffec..00000000 --- a/ext/ogg/dirac_parse.h +++ /dev/null @@ -1,178 +0,0 @@ - -#ifndef __DIRAC_PARSE_H__ -#define __DIRAC_PARSE_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef TRUE -#define TRUE 1 -#endif -#ifndef FALSE -#define FALSE 0 -#endif - -typedef enum _SchroParseCode { - SCHRO_PARSE_CODE_SEQUENCE_HEADER = 0x00, - SCHRO_PARSE_CODE_END_OF_SEQUENCE = 0x10, - SCHRO_PARSE_CODE_AUXILIARY_DATA = 0x20, - SCHRO_PARSE_CODE_PADDING = 0x30, - - SCHRO_PARSE_CODE_INTRA_REF = 0x0c, - SCHRO_PARSE_CODE_INTRA_NON_REF = 0x08, - SCHRO_PARSE_CODE_INTRA_REF_NOARITH = 0x4c, - SCHRO_PARSE_CODE_INTRA_NON_REF_NOARITH = 0x48, - - SCHRO_PARSE_CODE_INTER_REF_1 = 0x0d, - SCHRO_PARSE_CODE_INTER_REF_1_NOARITH = 0x4d, - SCHRO_PARSE_CODE_INTER_REF_2 = 0x0e, - SCHRO_PARSE_CODE_INTER_REF_2_NOARITH = 0x4e, - - SCHRO_PARSE_CODE_INTER_NON_REF_1 = 0x09, - SCHRO_PARSE_CODE_INTER_NON_REF_1_NOARITH = 0x49, - SCHRO_PARSE_CODE_INTER_NON_REF_2 = 0x0a, - SCHRO_PARSE_CODE_INTER_NON_REF_2_NOARITH = 0x4a, - - SCHRO_PARSE_CODE_LD_INTRA_REF = 0xcc, - SCHRO_PARSE_CODE_LD_INTRA_NON_REF = 0xc8 -} SchroParseCode; - -#define SCHRO_PARSE_CODE_PICTURE(is_ref,n_refs,is_lowdelay,is_noarith) \ - (8 | ((is_ref)<<2) | (n_refs) | ((is_lowdelay)<<7) | ((is_noarith)<<6)) - -#define SCHRO_PARSE_CODE_IS_SEQ_HEADER(x) ((x) == SCHRO_PARSE_CODE_SEQUENCE_HEADER) -#define SCHRO_PARSE_CODE_IS_END_OF_SEQUENCE(x) ((x) == SCHRO_PARSE_CODE_END_OF_SEQUENCE) -#define SCHRO_PARSE_CODE_IS_AUXILIARY_DATA(x) ((x) == SCHRO_PARSE_CODE_AUXILIARY_DATA) -#define SCHRO_PARSE_CODE_IS_PADDING(x) ((x) == SCHRO_PARSE_CODE_PADDING) -#define SCHRO_PARSE_CODE_IS_PICTURE(x) ((x) & 0x8) -#define SCHRO_PARSE_CODE_IS_LOW_DELAY(x) (((x) & 0x88) == 0x88) -#define SCHRO_PARSE_CODE_IS_CORE_SYNTAX(x) (((x) & 0x88) == 0x08) -#define SCHRO_PARSE_CODE_USING_AC(x) (((x) & 0x48) == 0x08) -#define SCHRO_PARSE_CODE_IS_REFERENCE(x) (((x) & 0xc) == 0x0c) -#define SCHRO_PARSE_CODE_IS_NON_REFERENCE(x) (((x) & 0xc) == 0x08) -#define SCHRO_PARSE_CODE_NUM_REFS(x) ((x) & 0x3) -#define SCHRO_PARSE_CODE_IS_INTRA(x) (SCHRO_PARSE_CODE_IS_PICTURE(x) && SCHRO_PARSE_CODE_NUM_REFS(x) == 0) -#define SCHRO_PARSE_CODE_IS_INTER(x) (SCHRO_PARSE_CODE_IS_PICTURE(x) && SCHRO_PARSE_CODE_NUM_REFS(x) > 0) - -#define SCHRO_PARSE_HEADER_SIZE (4+1+4+4) - -typedef enum _SchroVideoFormatEnum { - SCHRO_VIDEO_FORMAT_CUSTOM = 0, - SCHRO_VIDEO_FORMAT_QSIF, - SCHRO_VIDEO_FORMAT_QCIF, - SCHRO_VIDEO_FORMAT_SIF, - SCHRO_VIDEO_FORMAT_CIF, - SCHRO_VIDEO_FORMAT_4SIF, - SCHRO_VIDEO_FORMAT_4CIF, - SCHRO_VIDEO_FORMAT_SD480I_60, - SCHRO_VIDEO_FORMAT_SD576I_50, - SCHRO_VIDEO_FORMAT_HD720P_60, - SCHRO_VIDEO_FORMAT_HD720P_50, - SCHRO_VIDEO_FORMAT_HD1080I_60, - SCHRO_VIDEO_FORMAT_HD1080I_50, - SCHRO_VIDEO_FORMAT_HD1080P_60, - SCHRO_VIDEO_FORMAT_HD1080P_50, - SCHRO_VIDEO_FORMAT_DC2K_24, - SCHRO_VIDEO_FORMAT_DC4K_24 -} SchroVideoFormatEnum; - -typedef enum _SchroChromaFormat { - SCHRO_CHROMA_444 = 0, - SCHRO_CHROMA_422, - SCHRO_CHROMA_420 -} SchroChromaFormat; - -#define SCHRO_CHROMA_FORMAT_H_SHIFT(format) (((format) == SCHRO_CHROMA_444)?0:1) -#define SCHRO_CHROMA_FORMAT_V_SHIFT(format) (((format) == SCHRO_CHROMA_420)?1:0) - -typedef enum _SchroSignalRange { - SCHRO_SIGNAL_RANGE_CUSTOM = 0, - SCHRO_SIGNAL_RANGE_8BIT_FULL = 1, - SCHRO_SIGNAL_RANGE_8BIT_VIDEO = 2, - SCHRO_SIGNAL_RANGE_10BIT_VIDEO = 3, - SCHRO_SIGNAL_RANGE_12BIT_VIDEO = 4 -} SchroSignalRange; - -typedef enum _SchroColourSpec { - SCHRO_COLOUR_SPEC_CUSTOM = 0, - SCHRO_COLOUR_SPEC_SDTV_525 = 1, - SCHRO_COLOUR_SPEC_SDTV_625 = 2, - SCHRO_COLOUR_SPEC_HDTV = 3, - SCHRO_COLOUR_SPEC_CINEMA = 4 -} SchroColourSpec; - -typedef enum _SchroColourPrimaries { - SCHRO_COLOUR_PRIMARY_HDTV = 0, - SCHRO_COLOUR_PRIMARY_SDTV_525 = 1, - SCHRO_COLOUR_PRIMARY_SDTV_625 = 2, - SCHRO_COLOUR_PRIMARY_CINEMA = 3 -} SchroColourPrimaries; - -typedef enum _SchroColourMatrix { - SCHRO_COLOUR_MATRIX_HDTV = 0, - SCHRO_COLOUR_MATRIX_SDTV = 1, - SCHRO_COLOUR_MATRIX_REVERSIBLE = 2 -}SchroColourMatrix; - -typedef enum _SchroTransferFunction { - SCHRO_TRANSFER_CHAR_TV_GAMMA = 0, - SCHRO_TRANSFER_CHAR_EXTENDED_GAMUT = 1, - SCHRO_TRANSFER_CHAR_LINEAR = 2, - SCHRO_TRANSFER_CHAR_DCI_GAMMA = 3 -} SchroTransferFunction; - - - -typedef struct _DiracSequenceHeader DiracSequenceHeader; - -struct _DiracSequenceHeader { - int major_version; - int minor_version; - int profile; - int level; - - int index; - int width; - int height; - int chroma_format; - - int interlaced; - int top_field_first; - - int frame_rate_numerator; - int frame_rate_denominator; - int aspect_ratio_numerator; - int aspect_ratio_denominator; - - int clean_width; - int clean_height; - int left_offset; - int top_offset; - - int luma_offset; - int luma_excursion; - int chroma_offset; - int chroma_excursion; - - int colour_primaries; - int colour_matrix; - int transfer_function; - - int interlaced_coding; - - int unused0; - int unused1; - int unused2; -}; - - -int dirac_sequence_header_parse (DiracSequenceHeader *header, - unsigned char *data, int length); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/ext/ogg/gstogg.c b/ext/ogg/gstogg.c deleted file mode 100644 index d525aaa4..00000000 --- a/ext/ogg/gstogg.c +++ /dev/null @@ -1,48 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <gst/gst.h> - -extern gboolean gst_ogg_demux_plugin_init (GstPlugin * plugin); -extern gboolean gst_ogg_mux_plugin_init (GstPlugin * plugin); -extern gboolean gst_ogm_parse_plugin_init (GstPlugin * plugin); -extern gboolean gst_ogg_parse_plugin_init (GstPlugin * plugin); -extern gboolean gst_ogg_avi_parse_plugin_init (GstPlugin * plugin); - -static gboolean -plugin_init (GstPlugin * plugin) -{ - gst_ogg_demux_plugin_init (plugin); - gst_ogg_mux_plugin_init (plugin); - gst_ogm_parse_plugin_init (plugin); - gst_ogg_parse_plugin_init (plugin); - gst_ogg_avi_parse_plugin_init (plugin); - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "ogg", - "ogg stream manipulation (info about ogg: http://xiph.org)", - plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/ogg/gstoggaviparse.c b/ext/ogg/gstoggaviparse.c deleted file mode 100644 index 4b51e01e..00000000 --- a/ext/ogg/gstoggaviparse.c +++ /dev/null @@ -1,477 +0,0 @@ -/* GStreamer - * Copyright (C) 2006 Wim Taymans <wim@fluendo.com> - * - * gstoggaviparse.c: ogg avi stream parser - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -/* - * Ogg in AVI is mostly done for vorbis audio. In the codec_data we receive the - * first 3 packets of the raw vorbis data. On the sinkpad we receive full-blown Ogg - * pages. - * Before extracting the packets out of the ogg pages, we push the raw vorbis - * header packets to the decoder. - * We don't use the incomming timestamps but use the ganulepos on the ogg pages - * directly. - * This parser only does ogg/vorbis for now. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include <gst/gst.h> -#include <ogg/ogg.h> -#include <string.h> - -static const GstElementDetails gst_ogg_avi_parse_details = -GST_ELEMENT_DETAILS ("Ogg AVI parser", - "Codec/Parser", - "parse an ogg avi stream into pages (info about ogg: http://xiph.org)", - "Wim Taymans <wim@fluendo.com>"); - -GST_DEBUG_CATEGORY_STATIC (gst_ogg_avi_parse_debug); -#define GST_CAT_DEFAULT gst_ogg_avi_parse_debug - -#define GST_TYPE_OGG_AVI_PARSE (gst_ogg_avi_parse_get_type()) -#define GST_OGG_AVI_PARSE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGG_AVI_PARSE, GstOggAviParse)) -#define GST_OGG_AVI_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGG_AVI_PARSE, GstOggAviParse)) -#define GST_IS_OGG_AVI_PARSE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_AVI_PARSE)) -#define GST_IS_OGG_AVI_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_AVI_PARSE)) - -static GType gst_ogg_avi_parse_get_type (void); - -typedef struct _GstOggAviParse GstOggAviParse; -typedef struct _GstOggAviParseClass GstOggAviParseClass; - -struct _GstOggAviParse -{ - GstElement element; - - GstPad *sinkpad; - GstPad *srcpad; - - gboolean discont; - gint serial; - - ogg_sync_state sync; - ogg_stream_state stream; -}; - -struct _GstOggAviParseClass -{ - GstElementClass parent_class; -}; - -static void gst_ogg_avi_parse_base_init (gpointer g_class); -static void gst_ogg_avi_parse_class_init (GstOggAviParseClass * klass); -static void gst_ogg_avi_parse_init (GstOggAviParse * ogg); -static GstElementClass *parent_class = NULL; - -static GType -gst_ogg_avi_parse_get_type (void) -{ - static GType ogg_avi_parse_type = 0; - - if (!ogg_avi_parse_type) { - static const GTypeInfo ogg_avi_parse_info = { - sizeof (GstOggAviParseClass), - gst_ogg_avi_parse_base_init, - NULL, - (GClassInitFunc) gst_ogg_avi_parse_class_init, - NULL, - NULL, - sizeof (GstOggAviParse), - 0, - (GInstanceInitFunc) gst_ogg_avi_parse_init, - }; - - ogg_avi_parse_type = - g_type_register_static (GST_TYPE_ELEMENT, "GstOggAviParse", - &ogg_avi_parse_info, 0); - } - return ogg_avi_parse_type; -} - -enum -{ - PROP_0 -}; - -static GstStaticPadTemplate ogg_avi_parse_src_template_factory = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-vorbis") - ); - -static GstStaticPadTemplate ogg_avi_parse_sink_template_factory = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("application/x-ogg-avi") - ); - -static void gst_ogg_avi_parse_finalize (GObject * object); -static GstStateChangeReturn gst_ogg_avi_parse_change_state (GstElement * - element, GstStateChange transition); -static gboolean gst_ogg_avi_parse_event (GstPad * pad, GstEvent * event); -static GstFlowReturn gst_ogg_avi_parse_chain (GstPad * pad, GstBuffer * buffer); -static gboolean gst_ogg_avi_parse_setcaps (GstPad * pad, GstCaps * caps); - -static void -gst_ogg_avi_parse_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_details (element_class, &gst_ogg_avi_parse_details); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&ogg_avi_parse_sink_template_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&ogg_avi_parse_src_template_factory)); -} - -static void -gst_ogg_avi_parse_class_init (GstOggAviParseClass * klass) -{ - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - parent_class = g_type_class_peek_parent (klass); - - gstelement_class->change_state = gst_ogg_avi_parse_change_state; - - gobject_class->finalize = gst_ogg_avi_parse_finalize; -} - -static void -gst_ogg_avi_parse_init (GstOggAviParse * ogg) -{ - /* create the sink and source pads */ - ogg->sinkpad = - gst_pad_new_from_static_template (&ogg_avi_parse_sink_template_factory, - "sink"); - gst_pad_set_setcaps_function (ogg->sinkpad, gst_ogg_avi_parse_setcaps); - gst_pad_set_event_function (ogg->sinkpad, gst_ogg_avi_parse_event); - gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_avi_parse_chain); - gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad); - - ogg->srcpad = - gst_pad_new_from_static_template (&ogg_avi_parse_src_template_factory, - "src"); - gst_pad_use_fixed_caps (ogg->srcpad); - gst_element_add_pad (GST_ELEMENT (ogg), ogg->srcpad); -} - -static void -gst_ogg_avi_parse_finalize (GObject * object) -{ - GstOggAviParse *ogg = GST_OGG_AVI_PARSE (object); - - GST_LOG_OBJECT (ogg, "Disposing of object %p", ogg); - - ogg_sync_clear (&ogg->sync); - ogg_stream_clear (&ogg->stream); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static gboolean -gst_ogg_avi_parse_setcaps (GstPad * pad, GstCaps * caps) -{ - GstOggAviParse *ogg; - GstStructure *structure; - const GValue *codec_data; - GstBuffer *buffer; - guint8 *data; - guint size; - guint32 sizes[3]; - GstCaps *outcaps; - gint i, offs; - - ogg = GST_OGG_AVI_PARSE (GST_OBJECT_PARENT (pad)); - - structure = gst_caps_get_structure (caps, 0); - - /* take codec data */ - codec_data = gst_structure_get_value (structure, "codec_data"); - if (codec_data == NULL) - goto no_data; - - /* only buffers are valid */ - if (G_VALUE_TYPE (codec_data) != GST_TYPE_BUFFER) - goto wrong_format; - - /* Now parse the data */ - buffer = gst_value_get_buffer (codec_data); - - /* first 22 bytes are bits_per_sample, channel_mask, GUID - * Then we get 3 LE guint32 with the 3 header sizes - * then we get the bytes of the 3 headers. */ - data = GST_BUFFER_DATA (buffer); - size = GST_BUFFER_SIZE (buffer); - - GST_LOG_OBJECT (ogg, "configuring codec_data of size %u", size); - - /* skip headers */ - data += 22; - size -= 22; - - /* we need at least 12 bytes for the packet sizes of the 3 headers */ - if (size < 12) - goto buffer_too_small; - - /* read sizes of the 3 headers */ - sizes[0] = GST_READ_UINT32_LE (data); - sizes[1] = GST_READ_UINT32_LE (data + 4); - sizes[2] = GST_READ_UINT32_LE (data + 8); - - GST_DEBUG_OBJECT (ogg, "header sizes: %u %u %u", sizes[0], sizes[1], - sizes[2]); - - size -= 12; - - /* and we need at least enough data for all the headers */ - if (size < sizes[0] + sizes[1] + sizes[2]) - goto buffer_too_small; - - /* set caps */ - outcaps = gst_caps_new_simple ("audio/x-vorbis", NULL); - gst_pad_set_caps (ogg->srcpad, outcaps); - - /* copy header data */ - offs = 34; - for (i = 0; i < 3; i++) { - GstBuffer *out; - - /* now output the raw vorbis header packets */ - out = gst_buffer_create_sub (buffer, offs, sizes[i]); - gst_buffer_set_caps (out, outcaps); - gst_pad_push (ogg->srcpad, out); - - offs += sizes[i]; - } - gst_caps_unref (outcaps); - - return TRUE; - - /* ERRORS */ -no_data: - { - GST_DEBUG_OBJECT (ogg, "no codec_data found in caps"); - return FALSE; - } -wrong_format: - { - GST_DEBUG_OBJECT (ogg, "codec_data is not a buffer"); - return FALSE; - } -buffer_too_small: - { - GST_DEBUG_OBJECT (ogg, "codec_data is too small"); - return FALSE; - } -} - -static gboolean -gst_ogg_avi_parse_event (GstPad * pad, GstEvent * event) -{ - GstOggAviParse *ogg; - gboolean ret; - - ogg = GST_OGG_AVI_PARSE (GST_OBJECT_PARENT (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - ret = gst_pad_push_event (ogg->srcpad, event); - break; - case GST_EVENT_FLUSH_STOP: - ogg_sync_reset (&ogg->sync); - ogg_stream_reset (&ogg->stream); - ogg->discont = TRUE; - ret = gst_pad_push_event (ogg->srcpad, event); - break; - default: - ret = gst_pad_push_event (ogg->srcpad, event); - break; - } - return ret; -} - -static GstFlowReturn -gst_ogg_avi_parse_push_packet (GstOggAviParse * ogg, ogg_packet * packet) -{ - GstBuffer *buffer; - GstFlowReturn result; - - /* allocate space for header and body */ - buffer = gst_buffer_new_and_alloc (packet->bytes); - memcpy (GST_BUFFER_DATA (buffer), packet->packet, packet->bytes); - - GST_LOG_OBJECT (ogg, "created buffer %p from page", buffer); - - GST_BUFFER_OFFSET_END (buffer) = packet->granulepos; - - if (ogg->discont) { - GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); - ogg->discont = FALSE; - } - - result = gst_pad_push (ogg->srcpad, buffer); - - return result; -} - -static GstFlowReturn -gst_ogg_avi_parse_chain (GstPad * pad, GstBuffer * buffer) -{ - GstFlowReturn result = GST_FLOW_OK; - GstOggAviParse *ogg; - guint8 *data; - guint size; - gchar *oggbuf; - gint ret = -1; - - ogg = GST_OGG_AVI_PARSE (GST_OBJECT_PARENT (pad)); - - data = GST_BUFFER_DATA (buffer); - size = GST_BUFFER_SIZE (buffer); - - GST_LOG_OBJECT (ogg, "Chain function received buffer of size %d", size); - - if (GST_BUFFER_IS_DISCONT (buffer)) { - ogg_sync_reset (&ogg->sync); - ogg->discont = TRUE; - } - - /* write data to sync layer */ - oggbuf = ogg_sync_buffer (&ogg->sync, size); - memcpy (oggbuf, data, size); - ogg_sync_wrote (&ogg->sync, size); - gst_buffer_unref (buffer); - - /* try to get as many packets out of the stream as possible */ - do { - ogg_page page; - - /* try to swap out a page */ - ret = ogg_sync_pageout (&ogg->sync, &page); - if (ret == 0) { - GST_DEBUG_OBJECT (ogg, "need more data"); - break; - } else if (ret == -1) { - GST_DEBUG_OBJECT (ogg, "discont in pages"); - ogg->discont = TRUE; - } else { - /* new unknown stream, init the ogg stream with the serial number of the - * page. */ - if (ogg->serial == -1) { - ogg->serial = ogg_page_serialno (&page); - ogg_stream_init (&ogg->stream, ogg->serial); - } - - /* submit page */ - if (ogg_stream_pagein (&ogg->stream, &page) != 0) { - GST_WARNING_OBJECT (ogg, "ogg stream choked on page resetting stream"); - ogg_sync_reset (&ogg->sync); - ogg->discont = TRUE; - continue; - } - - /* try to get as many packets as possible out of the page */ - do { - ogg_packet packet; - - ret = ogg_stream_packetout (&ogg->stream, &packet); - GST_LOG_OBJECT (ogg, "packetout gave %d", ret); - switch (ret) { - case 0: - break; - case -1: - /* out of sync, We mark a DISCONT. */ - ogg->discont = TRUE; - break; - case 1: - result = gst_ogg_avi_parse_push_packet (ogg, &packet); - if (GST_FLOW_IS_FATAL (result)) - goto done; - break; - default: - GST_WARNING_OBJECT (ogg, - "invalid return value %d for ogg_stream_packetout, resetting stream", - ret); - break; - } - } - while (ret != 0); - } - } - while (ret != 0); - -done: - return result; -} - -static GstStateChangeReturn -gst_ogg_avi_parse_change_state (GstElement * element, GstStateChange transition) -{ - GstOggAviParse *ogg; - GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE; - - ogg = GST_OGG_AVI_PARSE (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - ogg_sync_init (&ogg->sync); - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - ogg_sync_reset (&ogg->sync); - ogg_stream_reset (&ogg->stream); - ogg->serial = -1; - ogg->discont = TRUE; - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - default: - break; - } - - result = parent_class->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_NULL: - ogg_sync_clear (&ogg->sync); - break; - default: - break; - } - return result; -} - -gboolean -gst_ogg_avi_parse_plugin_init (GstPlugin * plugin) -{ - GST_DEBUG_CATEGORY_INIT (gst_ogg_avi_parse_debug, "oggaviparse", 0, - "ogg avi parser"); - - return gst_element_register (plugin, "oggaviparse", GST_RANK_PRIMARY, - GST_TYPE_OGG_AVI_PARSE); -} diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c deleted file mode 100644 index d8f00efb..00000000 --- a/ext/ogg/gstoggdemux.c +++ /dev/null @@ -1,3366 +0,0 @@ -/* GStreamer - * Copyright (C) 2004 Wim Taymans <wim@fluendo.com> - * - * gstoggdemux.c: ogg stream demuxer - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-oggdemux - * @see_also: <link linkend="gst-plugins-base-plugins-oggmux">oggmux</link> - * - * This element demuxes ogg files into their encoded audio and video components. - * - * <refsect2> - * <title>Example pipelines</title> - * |[ - * gst-launch -v filesrc location=test.ogg ! oggdemux ! vorbisdec ! audioconvert ! alsasink - * ]| Decodes the vorbis audio stored inside an ogg container. - * </refsect2> - * - * Last reviewed on 2006-12-30 (0.10.5) - */ - - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include <string.h> -#include <gst/gst-i18n-plugin.h> -#include <gst/tag/tag.h> - -#include "gstoggdemux.h" - -static const GstElementDetails gst_ogg_demux_details = -GST_ELEMENT_DETAILS ("Ogg demuxer", - "Codec/Demuxer", - "demux ogg streams (info about ogg: http://xiph.org)", - "Wim Taymans <wim@fluendo.com>"); - -#define CHUNKSIZE (8500) /* this is out of vorbisfile */ -#define SKELETON_FISHEAD_SIZE 64 -#define SKELETON_FISBONE_MIN_SIZE 52 - -#define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR - -#define GST_CHAIN_LOCK(ogg) g_mutex_lock((ogg)->chain_lock) -#define GST_CHAIN_UNLOCK(ogg) g_mutex_unlock((ogg)->chain_lock) - -GST_DEBUG_CATEGORY (gst_ogg_demux_debug); -GST_DEBUG_CATEGORY (gst_ogg_demux_setup_debug); -#define GST_CAT_DEFAULT gst_ogg_demux_debug - - -static ogg_packet * -_ogg_packet_copy (const ogg_packet * packet) -{ - ogg_packet *ret = g_new0 (ogg_packet, 1); - - *ret = *packet; - ret->packet = g_memdup (packet->packet, packet->bytes); - - return ret; -} - -static void -_ogg_packet_free (ogg_packet * packet) -{ - g_free (packet->packet); - g_free (packet); -} - -static ogg_page * -gst_ogg_page_copy (ogg_page * page) -{ - ogg_page *p = g_new0 (ogg_page, 1); - - /* make a copy of the page */ - p->header = g_memdup (page->header, page->header_len); - p->header_len = page->header_len; - p->body = g_memdup (page->body, page->body_len); - p->body_len = page->body_len; - - return p; -} - -static void -gst_ogg_page_free (ogg_page * page) -{ - g_free (page->header); - g_free (page->body); - g_free (page); -} - -static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, - GstOggChain * chain); -static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg, - GstOggChain * chain, GstEvent * event); -static void gst_ogg_chain_mark_discont (GstOggChain * chain); - -static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg, - GstEvent * event); -static gboolean gst_ogg_demux_receive_event (GstElement * element, - GstEvent * event); - -static void gst_ogg_pad_class_init (GstOggPadClass * klass); -static void gst_ogg_pad_init (GstOggPad * pad); -static void gst_ogg_pad_dispose (GObject * object); -static void gst_ogg_pad_finalize (GObject * object); - -static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad); -static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query); -static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event); -static GstCaps *gst_ogg_pad_getcaps (GstPad * pad); -static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain, - glong serialno); - -static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg, - GstOggPad * pad, GstFlowReturn ret); -static void gst_ogg_demux_sync_streams (GstOggDemux * ogg); - -G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD); - -static void -gst_ogg_pad_class_init (GstOggPadClass * klass) -{ - GObjectClass *gobject_class; - - gobject_class = (GObjectClass *) klass; - - gobject_class->dispose = gst_ogg_pad_dispose; - gobject_class->finalize = gst_ogg_pad_finalize; -} - -static void -gst_ogg_pad_init (GstOggPad * pad) -{ - gst_pad_set_event_function (GST_PAD (pad), - GST_DEBUG_FUNCPTR (gst_ogg_pad_event)); - gst_pad_set_getcaps_function (GST_PAD (pad), - GST_DEBUG_FUNCPTR (gst_ogg_pad_getcaps)); - gst_pad_set_query_type_function (GST_PAD (pad), - GST_DEBUG_FUNCPTR (gst_ogg_pad_query_types)); - gst_pad_set_query_function (GST_PAD (pad), - GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query)); - - pad->mode = GST_OGG_PAD_MODE_INIT; - - pad->current_granule = -1; - pad->keyframe_granule = -1; - - pad->start_time = GST_CLOCK_TIME_NONE; - - pad->last_stop = GST_CLOCK_TIME_NONE; - - pad->have_type = FALSE; - pad->continued = NULL; - pad->map.headers = NULL; - pad->map.queued = NULL; -} - -static void -gst_ogg_pad_dispose (GObject * object) -{ - GstOggPad *pad = GST_OGG_PAD (object); - - pad->chain = NULL; - pad->ogg = NULL; - - g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL); - g_list_free (pad->map.headers); - pad->map.headers = NULL; - g_list_foreach (pad->map.queued, (GFunc) _ogg_packet_free, NULL); - g_list_free (pad->map.queued); - pad->map.queued = NULL; - - /* clear continued pages */ - g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL); - g_list_free (pad->continued); - pad->continued = NULL; - - ogg_stream_reset (&pad->map.stream); - - G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object); -} - -static void -gst_ogg_pad_finalize (GObject * object) -{ - GstOggPad *pad = GST_OGG_PAD (object); - - ogg_stream_clear (&pad->map.stream); - - G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object); -} - -static const GstQueryType * -gst_ogg_pad_query_types (GstPad * pad) -{ - static const GstQueryType query_types[] = { - GST_QUERY_DURATION, - GST_QUERY_SEEKING, - 0 - }; - - return query_types; -} - -static GstCaps * -gst_ogg_pad_getcaps (GstPad * pad) -{ - return gst_caps_ref (GST_PAD_CAPS (pad)); -} - -static gboolean -gst_ogg_pad_src_query (GstPad * pad, GstQuery * query) -{ - gboolean res = TRUE; - GstOggDemux *ogg; - - ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_DURATION: - { - GstFormat format; - gint64 total_time; - - gst_query_parse_duration (query, &format, NULL); - /* can only get position in time */ - if (format != GST_FORMAT_TIME) - goto wrong_format; - - if (ogg->pullmode) { - /* we must return the total length */ - total_time = ogg->total_time; - } else { - /* in push mode we can answer the query and we must return -1 */ - total_time = -1; - } - - gst_query_set_duration (query, GST_FORMAT_TIME, total_time); - break; - } - case GST_QUERY_SEEKING: - { - GstFormat format; - - gst_query_parse_seeking (query, &format, NULL, NULL, NULL); - if (format == GST_FORMAT_TIME) { - gst_query_set_seeking (query, GST_FORMAT_TIME, ogg->pullmode, - 0, ogg->total_time); - } else { - res = FALSE; - } - break; - } - - default: - res = gst_pad_query_default (pad, query); - break; - } -done: - gst_object_unref (ogg); - - return res; - - /* ERRORS */ -wrong_format: - { - GST_DEBUG_OBJECT (ogg, "only query duration on TIME is supported"); - res = FALSE; - goto done; - } -} - -static gboolean -gst_ogg_demux_receive_event (GstElement * element, GstEvent * event) -{ - gboolean res; - GstOggDemux *ogg; - - ogg = GST_OGG_DEMUX (element); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - /* can't seek if we are not pullmode, FIXME could pass the - * seek query upstream after converting it to bytes using - * the average bitrate of the stream. */ - if (!ogg->pullmode) { - GST_DEBUG_OBJECT (ogg, "seek on pull mode stream not implemented yet"); - goto error; - } - - /* now do the seek */ - res = gst_ogg_demux_perform_seek (ogg, event); - gst_event_unref (event); - break; - default: - GST_DEBUG_OBJECT (ogg, "We only handle seek events here"); - goto error; - } - - return res; - - /* ERRORS */ -error: - { - GST_DEBUG_OBJECT (ogg, "error handling event"); - gst_event_unref (event); - return FALSE; - } -} - -static gboolean -gst_ogg_pad_event (GstPad * pad, GstEvent * event) -{ - gboolean res; - GstOggDemux *ogg; - - ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - /* can't seek if we are not pullmode, FIXME could pass the - * seek query upstream after converting it to bytes using - * the average bitrate of the stream. */ - if (!ogg->pullmode) { - GST_DEBUG_OBJECT (ogg, "seek on pull mode stream not implemented yet"); - goto error; - } - - /* now do the seek */ - res = gst_ogg_demux_perform_seek (ogg, event); - gst_event_unref (event); - break; - default: - res = gst_pad_event_default (pad, event); - break; - } -done: - gst_object_unref (ogg); - - return res; - - /* ERRORS */ -error: - { - GST_DEBUG_OBJECT (ogg, "error handling event"); - gst_event_unref (event); - res = FALSE; - goto done; - } -} - -static void -gst_ogg_pad_reset (GstOggPad * pad) -{ - ogg_stream_reset (&pad->map.stream); - - GST_DEBUG_OBJECT (pad, "doing reset"); - - /* clear continued pages */ - g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL); - g_list_free (pad->continued); - pad->continued = NULL; - - pad->last_ret = GST_FLOW_OK; - pad->last_stop = GST_CLOCK_TIME_NONE; - pad->current_granule = -1; - pad->keyframe_granule = -1; -} - -/* called when the skeleton fishead is found. Caller ensures the packet is - * precisely the correct size; we don't re-check this here. */ -static void -gst_ogg_pad_parse_skeleton_fishead (GstOggPad * pad, ogg_packet * packet) -{ - GstOggDemux *ogg = pad->ogg; - guint8 *data = packet->packet; - guint16 major, minor; - gint64 prestime_n, prestime_d; - gint64 basetime_n, basetime_d; - - /* skip "fishead\0" */ - major = GST_READ_UINT16_LE (data + 8); - minor = GST_READ_UINT16_LE (data + 10); - prestime_n = (gint64) GST_READ_UINT64_LE (data + 12); - prestime_d = (gint64) GST_READ_UINT64_LE (data + 20); - basetime_n = (gint64) GST_READ_UINT64_LE (data + 28); - basetime_d = (gint64) GST_READ_UINT64_LE (data + 36); - - ogg->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d); - ogg->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d); - ogg->have_fishead = TRUE; - pad->map.is_skeleton = TRUE; - pad->start_time = GST_CLOCK_TIME_NONE; - GST_INFO_OBJECT (ogg, "skeleton fishead parsed (basetime: %" - GST_TIME_FORMAT ", prestime: %" GST_TIME_FORMAT ")", - GST_TIME_ARGS (ogg->basetime), GST_TIME_ARGS (ogg->prestime)); -} - -/* function called when a skeleton fisbone is found. Caller ensures that - * the packet length is sufficient */ -static void -gst_ogg_pad_parse_skeleton_fisbone (GstOggPad * pad, ogg_packet * packet) -{ - GstOggPad *fisbone_pad; - gint64 start_granule; - guint32 serialno; - guint8 *data = packet->packet; - - serialno = GST_READ_UINT32_LE (data + 12); - - fisbone_pad = gst_ogg_chain_get_stream (pad->chain, serialno); - if (fisbone_pad) { - if (fisbone_pad->map.have_fisbone) - /* already parsed */ - return; - - fisbone_pad->map.have_fisbone = TRUE; - - fisbone_pad->map.granulerate_n = GST_READ_UINT64_LE (data + 20); - fisbone_pad->map.granulerate_d = GST_READ_UINT64_LE (data + 28); - start_granule = GST_READ_UINT64_LE (data + 36); - fisbone_pad->map.preroll = GST_READ_UINT32_LE (data + 44); - fisbone_pad->map.granuleshift = GST_READ_UINT8 (data + 48); - - GST_INFO_OBJECT (pad->ogg, "skeleton fisbone parsed " - "(serialno: %08x start time: %" GST_TIME_FORMAT - " granulerate_n: %d granulerate_d: %d " - " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)", - serialno, GST_TIME_ARGS (fisbone_pad->start_time), - fisbone_pad->map.granulerate_n, fisbone_pad->map.granulerate_d, - fisbone_pad->map.preroll, fisbone_pad->map.granuleshift); - } else { - GST_WARNING_OBJECT (pad->ogg, - "found skeleton fisbone for an unknown stream %" G_GUINT32_FORMAT, - serialno); - } -} - -/* queue data, basically takes the packet, puts it in a buffer and store the - * buffer in the queued list. */ -static GstFlowReturn -gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet) -{ -#ifndef GST_DISABLE_GST_DEBUG - GstOggDemux *ogg = pad->ogg; -#endif - - GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08lx", pad, - pad->map.serialno); - - pad->map.queued = g_list_append (pad->map.queued, _ogg_packet_copy (packet)); - - /* we are ok now */ - return GST_FLOW_OK; -} - -static GstFlowReturn -gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet, - gboolean push_headers) -{ - GstBuffer *buf = NULL; - GstFlowReturn ret, cret; - GstOggDemux *ogg = pad->ogg; - gint64 current_time; - GstOggChain *chain; - gint64 duration; - gint offset; - gint trim; - GstClockTime out_timestamp, out_duration; - guint64 out_offset, out_offset_end; - gboolean delta_unit = FALSE; - - GST_DEBUG_OBJECT (ogg, - "%p streaming to peer serial %08lx", pad, pad->map.serialno); - - if (pad->map.is_ogm) { - const guint8 *data; - long bytes; - - data = packet->packet; - bytes = packet->bytes; - - if (bytes < 1) - goto empty_packet; - - if (data[0] & 1) { - /* We don't push header packets for OGM */ - cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK); - goto done; - } else if (data[0] & 3 && pad->map.is_ogm_text) { - GstTagList *tags; - - /* We don't push comment packets either for text streams, - * other streams will handle the comment packets in the - * decoder */ - buf = gst_buffer_new (); - - GST_BUFFER_DATA (buf) = (guint8 *) data; - GST_BUFFER_SIZE (buf) = bytes; - - tags = gst_tag_list_from_vorbiscomment_buffer (buf, - (guint8 *) "\003vorbis", 7, NULL); - gst_buffer_unref (buf); - buf = NULL; - - if (tags) { - GST_DEBUG_OBJECT (ogg, "tags = %" GST_PTR_FORMAT, tags); - gst_element_found_tags_for_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad), - tags); - } else { - GST_DEBUG_OBJECT (ogg, "failed to extract tags from vorbis comment"); - } - - cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK); - goto done; - } - - offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1)); - delta_unit = (((data[0] & 0x08) >> 3) == 0); - - trim = 0; - - /* Strip trailing \0 for subtitles */ - if (pad->map.is_ogm_text) { - while (bytes && data[bytes - 1] == 0) { - trim++; - bytes--; - } - } - } else { - offset = 0; - trim = 0; - } - - /* get timing info for the packet */ - duration = gst_ogg_stream_get_packet_duration (&pad->map, packet); - GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration); - - if (packet->b_o_s) { - out_timestamp = GST_CLOCK_TIME_NONE; - out_duration = GST_CLOCK_TIME_NONE; - out_offset = 0; - out_offset_end = -1; - } else { - if (packet->granulepos != -1) { - pad->current_granule = gst_ogg_stream_granulepos_to_granule (&pad->map, - packet->granulepos); - pad->keyframe_granule = - gst_ogg_stream_granulepos_to_key_granule (&pad->map, - packet->granulepos); - GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT, - pad->current_granule); - } else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) { - pad->current_granule += duration; - GST_DEBUG_OBJECT (ogg, "interpollating granule %" G_GUINT64_FORMAT, - pad->current_granule); - } - if (ogg->segment.rate < 0.0 && packet->granulepos == -1) { - /* negative rates, only set timestamp on the packets with a granulepos */ - out_timestamp = -1; - out_duration = -1; - out_offset = -1; - out_offset_end = -1; - } else { - /* we only push buffers after we have a valid granule. This is done so that - * we nicely skip packets without a timestamp after a seek. This is ok - * because we base or seek on the packet after the page with the smaller - * timestamp. */ - if (pad->current_granule == -1) - goto no_timestamp; - - if (pad->map.is_ogm) { - out_timestamp = gst_ogg_stream_granule_to_time (&pad->map, - pad->current_granule); - out_duration = gst_util_uint64_scale (duration, - GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n); - } else if (pad->is_sparse) { - out_timestamp = gst_ogg_stream_granule_to_time (&pad->map, - pad->current_granule); - out_duration = GST_CLOCK_TIME_NONE; - } else { - out_timestamp = gst_ogg_stream_granule_to_time (&pad->map, - pad->current_granule - duration); - out_duration = - gst_ogg_stream_granule_to_time (&pad->map, - pad->current_granule) - out_timestamp; - } - out_offset_end = - gst_ogg_stream_granule_to_granulepos (&pad->map, pad->current_granule, - pad->keyframe_granule); - out_offset = - gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule); - } - } - - /* check for invalid buffer sizes */ - if (G_UNLIKELY (offset + trim >= packet->bytes)) - goto empty_packet; - - ret = - gst_pad_alloc_buffer_and_set_caps (GST_PAD_CAST (pad), - GST_BUFFER_OFFSET_NONE, packet->bytes - offset - trim, - GST_PAD_CAPS (pad), &buf); - - /* combine flows */ - cret = gst_ogg_demux_combine_flows (ogg, pad, ret); - if (ret != GST_FLOW_OK) - goto no_buffer; - - /* set delta flag for OGM content */ - if (delta_unit) - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); - - /* copy packet in buffer */ - memcpy (buf->data, packet->packet + offset, packet->bytes - offset - trim); - - GST_BUFFER_TIMESTAMP (buf) = out_timestamp; - GST_BUFFER_DURATION (buf) = out_duration; - GST_BUFFER_OFFSET (buf) = out_offset; - GST_BUFFER_OFFSET_END (buf) = out_offset_end; - - /* Mark discont on the buffer */ - if (pad->discont) { - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); - pad->discont = FALSE; - } - - pad->last_stop = ogg->segment.last_stop; - - /* don't push the header packets when we are asked to skip them */ - if (!packet->b_o_s || push_headers) { - ret = gst_pad_push (GST_PAD_CAST (pad), buf); - buf = NULL; - - /* combine flows */ - cret = gst_ogg_demux_combine_flows (ogg, pad, ret); - } - - /* we're done with skeleton stuff */ - if (pad->map.is_skeleton) - goto done; - - /* check if valid granulepos, then we can calculate the current - * position. We know the granule for each packet but we only want to update - * the last_stop when we have a valid granulepos on the packet because else - * our time jumps around for the different streams. */ - if (packet->granulepos < 0) - goto done; - - /* convert to time */ - current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, - packet->granulepos); - - /* convert to stream time */ - if ((chain = pad->chain)) { - gint64 chain_start = 0; - - if (chain->segment_start != GST_CLOCK_TIME_NONE) - chain_start = chain->segment_start; - - current_time = current_time - chain_start + chain->begin_time; - } - - /* and store as the current position */ - gst_segment_set_last_stop (&ogg->segment, GST_FORMAT_TIME, current_time); - - GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT, - GST_TIME_ARGS (current_time)); - -done: - if (buf) - gst_buffer_unref (buf); - /* return combined flow result */ - return cret; - - /* special cases */ -empty_packet: - { - GST_DEBUG_OBJECT (ogg, "Skipping empty packet"); - cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK); - goto done; - } -no_timestamp: - { - GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet"); - cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK); - goto done; - } -no_buffer: - { - GST_DEBUG_OBJECT (ogg, - "%p could not get buffer from peer %08lx, %d (%s), combined %d (%s)", - pad, pad->map.serialno, ret, gst_flow_get_name (ret), - cret, gst_flow_get_name (cret)); - goto done; - } -} - -/* submit a packet to the oggpad, this function will run the - * typefind code for the pad if this is the first packet for this - * stream - */ -static GstFlowReturn -gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet) -{ - gint64 granule; - GstFlowReturn ret = GST_FLOW_OK; - - GstOggDemux *ogg = pad->ogg; - - GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08lx", pad, - pad->map.serialno); - - if (!pad->have_type) { - if (!ogg->have_fishead && packet->bytes == SKELETON_FISHEAD_SIZE && - !memcmp (packet->packet, "fishead\0", 8)) { - gst_ogg_pad_parse_skeleton_fishead (pad, packet); - } - pad->have_type = gst_ogg_stream_setup_map (&pad->map, packet); - if (!pad->have_type) { - pad->map.caps = gst_caps_new_simple ("application/x-unknown", NULL); - } - if (pad->map.caps) { - gst_pad_set_caps (GST_PAD (pad), pad->map.caps); - } else { - GST_WARNING_OBJECT (ogg, "stream parser didn't create src pad caps"); - } - } - - if (ogg->have_fishead && packet->bytes >= SKELETON_FISBONE_MIN_SIZE && - !memcmp (packet->packet, "fisbone\0", 8)) { - gst_ogg_pad_parse_skeleton_fisbone (pad, packet); - } - - granule = gst_ogg_stream_granulepos_to_granule (&pad->map, - packet->granulepos); - if (granule != -1) { - GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule); - pad->current_granule = granule; - } - - /* restart header packet count when seeing a b_o_s page; - * particularly useful following a seek or even following chain finding */ - if (packet->b_o_s) { - GST_DEBUG_OBJECT (ogg, "b_o_s packet, resetting header packet count"); - pad->map.n_header_packets_seen = 0; - if (!pad->map.have_headers) { - GST_DEBUG_OBJECT (ogg, "clearing header packets"); - g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL); - g_list_free (pad->map.headers); - pad->map.headers = NULL; - } - } - - /* Overload the value of b_o_s in ogg_packet with a flag whether or - * not this is a header packet. Maybe some day this could be cleaned - * up. */ - packet->b_o_s = gst_ogg_stream_packet_is_header (&pad->map, packet); - if (!packet->b_o_s) { - pad->map.have_headers = TRUE; - if (pad->start_time == GST_CLOCK_TIME_NONE) { - gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet); - GST_DEBUG ("duration %" G_GINT64_FORMAT, duration); - if (duration != -1) { - pad->map.accumulated_granule += duration; - GST_DEBUG ("accumulated granule %" G_GINT64_FORMAT, - pad->map.accumulated_granule); - } - - if (packet->granulepos != -1) { - ogg_int64_t start_granule; - gint64 granule; - - granule = gst_ogg_stream_granulepos_to_granule (&pad->map, - packet->granulepos); - - if (granule > pad->map.accumulated_granule) - start_granule = granule - pad->map.accumulated_granule; - else - start_granule = 0; - - pad->start_time = gst_ogg_stream_granule_to_time (&pad->map, - start_granule); - GST_DEBUG ("start time %" G_GINT64_FORMAT, pad->start_time); - } else { - packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map, - pad->map.accumulated_granule, pad->keyframe_granule); - } - } - } else { - pad->map.n_header_packets_seen++; - if (!pad->map.have_headers) { - pad->map.headers = - g_list_append (pad->map.headers, _ogg_packet_copy (packet)); - GST_DEBUG ("keeping header packet %d", pad->map.n_header_packets_seen); - } - } - - /* we know the start_time of the pad data, see if we - * can activate the complete chain if this is a dynamic - * chain. */ - if (pad->start_time != GST_CLOCK_TIME_NONE) { - GstOggChain *chain = pad->chain; - - /* check if complete chain has start time */ - if (chain == ogg->building_chain) { - - /* see if we have enough info to activate the chain, we have enough info - * when all streams have a valid start time. */ - if (gst_ogg_demux_collect_chain_info (ogg, chain)) { - GstEvent *event; - - GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT, - GST_TIME_ARGS (chain->segment_start)); - GST_DEBUG_OBJECT (ogg, "segment_stop: %" GST_TIME_FORMAT, - GST_TIME_ARGS (chain->segment_stop)); - GST_DEBUG_OBJECT (ogg, "segment_time: %" GST_TIME_FORMAT, - GST_TIME_ARGS (chain->begin_time)); - - /* create the newsegment event we are going to send out */ - event = gst_event_new_new_segment (FALSE, ogg->segment.rate, - GST_FORMAT_TIME, chain->segment_start, chain->segment_stop, - chain->begin_time); - gst_event_set_seqnum (event, ogg->seqnum); - - gst_ogg_demux_activate_chain (ogg, chain, event); - - ogg->building_chain = NULL; - } - } - } - - /* if we are building a chain, store buffer for when we activate - * it. This path is taken if we operate in streaming mode. */ - if (ogg->building_chain) { - /* bos packets where stored in the header list so we can discard - * them here*/ - if (!packet->b_o_s) - ret = gst_ogg_demux_queue_data (pad, packet); - } - /* else we are completely streaming to the peer */ - else { - ret = gst_ogg_demux_chain_peer (pad, packet, !ogg->pullmode); - } - return ret; -} - -/* flush at most @npackets from the stream layer. All packets if - * @npackets is 0; - */ -static GstFlowReturn -gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets) -{ - GstFlowReturn result = GST_FLOW_OK; - gboolean done = FALSE; - GstOggDemux *ogg; - - ogg = pad->ogg; - - while (!done) { - int ret; - ogg_packet packet; - - ret = ogg_stream_packetout (&pad->map.stream, &packet); - switch (ret) { - case 0: - GST_LOG_OBJECT (ogg, "packetout done"); - done = TRUE; - break; - case -1: - GST_LOG_OBJECT (ogg, "packetout discont"); - gst_ogg_chain_mark_discont (pad->chain); - break; - case 1: - GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes); - result = gst_ogg_pad_submit_packet (pad, &packet); - if (GST_FLOW_IS_FATAL (result)) - goto could_not_submit; - break; - default: - GST_WARNING_OBJECT (ogg, - "invalid return value %d for ogg_stream_packetout, resetting stream", - ret); - gst_ogg_pad_reset (pad); - break; - } - if (npackets > 0) { - npackets--; - done = (npackets == 0); - } - } - return result; - - /* ERRORS */ -could_not_submit: - { - GST_WARNING_OBJECT (ogg, - "could not submit packet for stream %08lx, error: %d", - pad->map.serialno, result); - gst_ogg_pad_reset (pad); - return result; - } -} - -/* submit a page to an oggpad, this function will then submit all - * the packets in the page. - */ -static GstFlowReturn -gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page) -{ - GstFlowReturn result = GST_FLOW_OK; - GstOggDemux *ogg; - gboolean continued = FALSE; - - ogg = pad->ogg; - - /* for negative rates we read pages backwards and must therefore be carefull - * with continued pages */ - if (ogg->segment.rate < 0.0) { - gint npackets; - - continued = ogg_page_continued (page); - - /* number of completed packets in the page */ - npackets = ogg_page_packets (page); - if (!continued) { - /* page is not continued so it contains at least one packet start. It's - * possible that no packet ends on this page (npackets == 0). In that - * case, the next (continued) page(s) we kept contain the remainder of the - * packets. We mark npackets=1 to make us start decoding the pages in the - * remainder of the algorithm. */ - if (npackets == 0) - npackets = 1; - } - GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets); - - if (npackets == 0) { - GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page"); - goto done; - } - } - - if (ogg_stream_pagein (&pad->map.stream, page) != 0) - goto choked; - - /* flush all packets in the stream layer, this might not give a packet if - * the page had no packets finishing on the page (npackets == 0). */ - result = gst_ogg_pad_stream_out (pad, 0); - - if (pad->continued) { - ogg_packet packet; - - /* now send the continued pages to the stream layer */ - while (pad->continued) { - ogg_page *p = (ogg_page *) pad->continued->data; - - GST_LOG_OBJECT (ogg, "submitting continued page %p", p); - if (ogg_stream_pagein (&pad->map.stream, p) != 0) - goto choked; - - pad->continued = g_list_delete_link (pad->continued, pad->continued); - - /* free the page */ - gst_ogg_page_free (p); - } - - GST_LOG_OBJECT (ogg, "flushing last continued packet"); - /* flush 1 continued packet in the stream layer */ - result = gst_ogg_pad_stream_out (pad, 1); - - /* flush all remaining packets, we pushed them in the previous round. - * We don't use _reset() because we still want to get the discont when - * we submit a next page. */ - while (ogg_stream_packetout (&pad->map.stream, &packet) != 0); - } - -done: - /* keep continued pages (only in reverse mode) */ - if (continued) { - ogg_page *p = gst_ogg_page_copy (page); - - GST_LOG_OBJECT (ogg, "keeping continued page %p", p); - pad->continued = g_list_prepend (pad->continued, p); - } - - return result; - -choked: - { - GST_WARNING_OBJECT (ogg, - "ogg stream choked on page (serial %08lx), resetting stream", - pad->map.serialno); - gst_ogg_pad_reset (pad); - /* we continue to recover */ - return GST_FLOW_OK; - } -} - - -static GstOggChain * -gst_ogg_chain_new (GstOggDemux * ogg) -{ - GstOggChain *chain = g_new0 (GstOggChain, 1); - - GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain); - chain->ogg = ogg; - chain->offset = -1; - chain->bytes = -1; - chain->have_bos = FALSE; - chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *)); - chain->begin_time = GST_CLOCK_TIME_NONE; - chain->segment_start = GST_CLOCK_TIME_NONE; - chain->segment_stop = GST_CLOCK_TIME_NONE; - chain->total_time = GST_CLOCK_TIME_NONE; - - return chain; -} - -static void -gst_ogg_chain_free (GstOggChain * chain) -{ - gint i; - - for (i = 0; i < chain->streams->len; i++) { - GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); - - gst_object_unref (pad); - } - g_array_free (chain->streams, TRUE); - g_free (chain); -} - -static void -gst_ogg_chain_mark_discont (GstOggChain * chain) -{ - gint i; - - for (i = 0; i < chain->streams->len; i++) { - GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); - - pad->discont = TRUE; - pad->map.last_size = 0; - } -} - -static void -gst_ogg_chain_reset (GstOggChain * chain) -{ - gint i; - - for (i = 0; i < chain->streams->len; i++) { - GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); - - gst_ogg_pad_reset (pad); - } -} - -static GstOggPad * -gst_ogg_chain_new_stream (GstOggChain * chain, glong serialno) -{ - GstOggPad *ret; - GstTagList *list; - gchar *name; - - GST_DEBUG_OBJECT (chain->ogg, "creating new stream %08lx in chain %p", - serialno, chain); - - ret = g_object_new (GST_TYPE_OGG_PAD, NULL); - /* we own this one */ - gst_object_ref (ret); - gst_object_sink (ret); - - GST_PAD_DIRECTION (ret) = GST_PAD_SRC; - ret->discont = TRUE; - ret->map.last_size = 0; - - ret->chain = chain; - ret->ogg = chain->ogg; - - ret->map.serialno = serialno; - if (ogg_stream_init (&ret->map.stream, serialno) != 0) - goto init_failed; - - name = g_strdup_printf ("serial_%08lx", serialno); - gst_object_set_name (GST_OBJECT (ret), name); - g_free (name); - - /* FIXME: either do something with it or remove it */ - list = gst_tag_list_new (); - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serialno, - NULL); - gst_tag_list_free (list); - - GST_DEBUG_OBJECT (chain->ogg, - "created new ogg src %p for stream with serial %08lx", ret, serialno); - - g_array_append_val (chain->streams, ret); - - return ret; - - /* ERRORS */ -init_failed: - { - GST_ERROR ("Could not initialize ogg_stream struct for serial %08lx.", - serialno); - gst_object_unref (ret); - return NULL; - } -} - -static GstOggPad * -gst_ogg_chain_get_stream (GstOggChain * chain, glong serialno) -{ - gint i; - - for (i = 0; i < chain->streams->len; i++) { - GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); - - if (pad->map.serialno == serialno) - return pad; - } - return NULL; -} - -static gboolean -gst_ogg_chain_has_stream (GstOggChain * chain, glong serialno) -{ - return gst_ogg_chain_get_stream (chain, serialno) != NULL; -} - -#define CURRENT_CHAIN(ogg) (&g_array_index ((ogg)->chains, GstOggChain, (ogg)->current_chain)) - -/* signals and args */ -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; - -enum -{ - ARG_0 - /* FILL ME */ -}; - -static GstStaticPadTemplate ogg_demux_src_template_factory = -GST_STATIC_PAD_TEMPLATE ("src_%d", - GST_PAD_SRC, - GST_PAD_SOMETIMES, - GST_STATIC_CAPS_ANY); - -static GstStaticPadTemplate ogg_demux_sink_template_factory = - GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("application/ogg; application/x-annodex") - ); - -static void gst_ogg_demux_finalize (GObject * object); - -static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg, - GstOggChain ** chain); -static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg, - GstOggChain * chain); - -static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event); -static void gst_ogg_demux_loop (GstOggPad * pad); -static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer); -static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad); -static gboolean gst_ogg_demux_sink_activate_pull (GstPad * sinkpad, - gboolean active); -static gboolean gst_ogg_demux_sink_activate_push (GstPad * sinkpad, - gboolean active); -static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element, - GstStateChange transition); -static void gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event); - -static void gst_ogg_print (GstOggDemux * demux); - -GST_BOILERPLATE (GstOggDemux, gst_ogg_demux, GstElement, GST_TYPE_ELEMENT); - -static void -gst_ogg_demux_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_details (element_class, &gst_ogg_demux_details); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&ogg_demux_sink_template_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&ogg_demux_src_template_factory)); -} - -static void -gst_ogg_demux_class_init (GstOggDemuxClass * klass) -{ - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gstelement_class->change_state = gst_ogg_demux_change_state; - gstelement_class->send_event = gst_ogg_demux_receive_event; - - gobject_class->finalize = gst_ogg_demux_finalize; -} - -static void -gst_ogg_demux_init (GstOggDemux * ogg, GstOggDemuxClass * g_class) -{ - /* create the sink pad */ - ogg->sinkpad = - gst_pad_new_from_static_template (&ogg_demux_sink_template_factory, - "sink"); - - gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event); - gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain); - gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate); - gst_pad_set_activatepull_function (ogg->sinkpad, - gst_ogg_demux_sink_activate_pull); - gst_pad_set_activatepush_function (ogg->sinkpad, - gst_ogg_demux_sink_activate_push); - gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad); - - ogg->chain_lock = g_mutex_new (); - ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *)); - - ogg->newsegment = NULL; -} - -static void -gst_ogg_demux_finalize (GObject * object) -{ - GstOggDemux *ogg; - - ogg = GST_OGG_DEMUX (object); - - g_array_free (ogg->chains, TRUE); - g_mutex_free (ogg->chain_lock); - ogg_sync_clear (&ogg->sync); - - if (ogg->newsegment) - gst_event_unref (ogg->newsegment); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static gboolean -gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event) -{ - gboolean res; - GstOggDemux *ogg; - - ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_NEWSEGMENT: - /* FIXME */ - GST_DEBUG_OBJECT (ogg, "got a new segment event"); - ogg_sync_reset (&ogg->sync); - gst_event_unref (event); - res = TRUE; - break; - case GST_EVENT_EOS: - { - GST_DEBUG_OBJECT (ogg, "got an EOS event"); - res = gst_pad_event_default (pad, event); - if (ogg->current_chain == NULL) { - GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), - ("can't get first chain")); - } - break; - } - default: - res = gst_pad_event_default (pad, event); - break; - } - gst_object_unref (ogg); - - return res; -} - -/* submit the given buffer to the ogg sync. - * - * Returns the number of bytes submited. - */ -static GstFlowReturn -gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer) -{ - gint size; - guint8 *data; - gchar *oggbuffer; - GstFlowReturn ret = GST_FLOW_OK; - - size = GST_BUFFER_SIZE (buffer); - data = GST_BUFFER_DATA (buffer); - - GST_DEBUG_OBJECT (ogg, "submitting %u bytes", size); - if (G_UNLIKELY (size == 0)) - goto done; - - oggbuffer = ogg_sync_buffer (&ogg->sync, size); - if (G_UNLIKELY (oggbuffer == NULL)) - goto no_buffer; - - memcpy (oggbuffer, data, size); - if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0)) - goto write_failed; - -done: - gst_buffer_unref (buffer); - - return ret; - - /* ERRORS */ -no_buffer: - { - GST_ELEMENT_ERROR (ogg, STREAM, DECODE, - (NULL), ("failed to get ogg sync buffer")); - ret = GST_FLOW_ERROR; - goto done; - } -write_failed: - { - GST_ELEMENT_ERROR (ogg, STREAM, DECODE, - (NULL), ("failed to write %d bytes to the sync buffer", size)); - ret = GST_FLOW_ERROR; - goto done; - } -} - -/* in random access mode this code updates the current read position - * and resets the ogg sync buffer so that the next read will happen - * from this new location. - */ -static void -gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset) -{ - GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset); - - ogg->offset = offset; - ogg->read_offset = offset; - ogg_sync_reset (&ogg->sync); -} - -/* read more data from the current offset and submit to - * the ogg sync layer. - */ -static GstFlowReturn -gst_ogg_demux_get_data (GstOggDemux * ogg, gint64 end_offset) -{ - GstFlowReturn ret; - GstBuffer *buffer; - - GST_LOG_OBJECT (ogg, - "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT " %" G_GINT64_FORMAT, - ogg->read_offset, ogg->length, end_offset); - - if (end_offset > 0 && ogg->read_offset >= end_offset) - goto boundary_reached; - - if (ogg->read_offset == ogg->length) - goto eos; - - ret = gst_pad_pull_range (ogg->sinkpad, ogg->read_offset, CHUNKSIZE, &buffer); - if (ret != GST_FLOW_OK) - goto error; - - ogg->read_offset += GST_BUFFER_SIZE (buffer); - - ret = gst_ogg_demux_submit_buffer (ogg, buffer); - - return ret; - - /* ERROR */ -boundary_reached: - { - GST_LOG_OBJECT (ogg, "reached boundary"); - return GST_FLOW_LIMIT; - } -eos: - { - GST_LOG_OBJECT (ogg, "reached EOS"); - return GST_FLOW_UNEXPECTED; - } -error: - { - GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret, - gst_flow_get_name (ret)); - return ret; - } -} - -/* Read the next page from the current offset. - * boundary: number of bytes ahead we allow looking for; - * -1 if no boundary - * - * @offset will contain the offset the next page starts at when this function - * returns GST_FLOW_OK. - * - * GST_FLOW_UNEXPECTED is returned on EOS. - * - * GST_FLOW_LIMIT is returned when we did not find a page before the - * boundary. If @boundary is -1, this is never returned. - * - * Any other error returned while retrieving data from the peer is returned as - * is. - */ -static GstFlowReturn -gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary, - gint64 * offset) -{ - gint64 end_offset = -1; - GstFlowReturn ret; - - GST_LOG_OBJECT (ogg, - "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %" - G_GINT64_FORMAT, ogg->offset, boundary); - - if (boundary >= 0) - end_offset = ogg->offset + boundary; - - while (TRUE) { - glong more; - - if (end_offset > 0 && ogg->offset >= end_offset) - goto boundary_reached; - - more = ogg_sync_pageseek (&ogg->sync, og); - - GST_LOG_OBJECT (ogg, "pageseek gave %ld", more); - - if (more < 0) { - /* skipped n bytes */ - ogg->offset -= more; - GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT, more, - ogg->offset); - } else if (more == 0) { - /* we need more data */ - if (boundary == 0) - goto boundary_reached; - - GST_LOG_OBJECT (ogg, "need more data"); - ret = gst_ogg_demux_get_data (ogg, end_offset); - if (ret != GST_FLOW_OK) - break; - } else { - gint64 res_offset = ogg->offset; - - /* got a page. Return the offset at the page beginning, - advance the internal offset past the page end */ - if (offset) - *offset = res_offset; - ret = GST_FLOW_OK; - - ogg->offset += more; - - GST_LOG_OBJECT (ogg, - "got page at %" G_GINT64_FORMAT ", serial %08x, end at %" - G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset, - ogg_page_serialno (og), ogg->offset, - (gint64) ogg_page_granulepos (og)); - break; - } - } - GST_LOG_OBJECT (ogg, "returning %d", ret); - - return ret; - - /* ERRORS */ -boundary_reached: - { - GST_LOG_OBJECT (ogg, - "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT, - ogg->offset, end_offset); - return GST_FLOW_LIMIT; - } -} - -/* from the current offset, find the previous page, seeking backwards - * until we find the page. - */ -static GstFlowReturn -gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset) -{ - GstFlowReturn ret; - gint64 begin = ogg->offset; - gint64 end = begin; - gint64 cur_offset = -1; - - GST_LOG_OBJECT (ogg, "getting page before %" G_GINT64_FORMAT, begin); - - while (cur_offset == -1) { - begin -= CHUNKSIZE; - if (begin < 0) - begin = 0; - - /* seek CHUNKSIZE back */ - gst_ogg_demux_seek (ogg, begin); - - /* now continue reading until we run out of data, if we find a page - * start, we save it. It might not be the final page as there could be - * another page after this one. */ - while (ogg->offset < end) { - gint64 new_offset; - - ret = - gst_ogg_demux_get_next_page (ogg, og, end - ogg->offset, &new_offset); - /* we hit the upper limit, offset contains the last page start */ - if (ret == GST_FLOW_LIMIT) { - GST_LOG_OBJECT (ogg, "hit limit"); - break; - } - /* something went wrong */ - if (ret == GST_FLOW_UNEXPECTED) { - new_offset = 0; - GST_LOG_OBJECT (ogg, "got unexpected"); - } else if (ret != GST_FLOW_OK) { - GST_LOG_OBJECT (ogg, "got error %d", ret); - return ret; - } - - GST_LOG_OBJECT (ogg, "found page at %" G_GINT64_FORMAT, new_offset); - - /* offset is next page start */ - cur_offset = new_offset; - } - } - - GST_LOG_OBJECT (ogg, "found previous page at %" G_GINT64_FORMAT, cur_offset); - - /* we have the offset. Actually snork and hold the page now */ - gst_ogg_demux_seek (ogg, cur_offset); - ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL); - if (ret != GST_FLOW_OK) { - GST_WARNING_OBJECT (ogg, "can't get last page at %" G_GINT64_FORMAT, - cur_offset); - /* this shouldn't be possible */ - return ret; - } - - if (offset) - *offset = cur_offset; - - return ret; -} - -static gboolean -gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg) -{ - gint i; - GstOggChain *chain = ogg->current_chain; - - if (chain == NULL) - return TRUE; - - GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain); - - /* send EOS on all the pads */ - for (i = 0; i < chain->streams->len; i++) { - GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); - GstEvent *event; - - if (!pad->added) - continue; - - event = gst_event_new_eos (); - gst_event_set_seqnum (event, ogg->seqnum); - gst_pad_push_event (GST_PAD_CAST (pad), event); - - GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad); - - /* deactivate first */ - gst_pad_set_active (GST_PAD_CAST (pad), FALSE); - - gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad)); - - pad->added = FALSE; - } - /* if we cannot seek back to the chain, we can destroy the chain - * completely */ - if (!ogg->pullmode) { - gst_ogg_chain_free (chain); - } - ogg->current_chain = NULL; - - return TRUE; -} - -static gboolean -gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain, - GstEvent * event) -{ - gint i; - - if (chain == ogg->current_chain) { - if (event) - gst_event_unref (event); - return TRUE; - } - - /* FIXME, should not be called with NULL */ - if (chain != NULL) { - GST_DEBUG_OBJECT (ogg, "activating chain %p", chain); - - /* first add the pads */ - for (i = 0; i < chain->streams->len; i++) { - GstOggPad *pad; - GstStructure *structure; - - pad = g_array_index (chain->streams, GstOggPad *, i); - - if (pad->map.is_skeleton || pad->added || GST_PAD_CAPS (pad) == NULL) - continue; - - GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad); - - /* mark discont */ - pad->discont = TRUE; - pad->map.last_size = 0; - pad->last_ret = GST_FLOW_OK; - pad->added = TRUE; - - structure = gst_caps_get_structure (GST_PAD_CAPS (pad), 0); - pad->is_sparse = - gst_structure_has_name (structure, "application/x-ogm-text") || - gst_structure_has_name (structure, "text/x-cmml") || - gst_structure_has_name (structure, "subtitle/x-kate") || - gst_structure_has_name (structure, "application/x-kate"); - - /* activate first */ - gst_pad_set_active (GST_PAD_CAST (pad), TRUE); - - gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad)); - } - } - - /* after adding the new pads, remove the old pads */ - gst_ogg_demux_deactivate_current_chain (ogg); - - ogg->current_chain = chain; - - /* we are finished now */ - gst_element_no_more_pads (GST_ELEMENT (ogg)); - - /* FIXME, must be sent from the streaming thread */ - if (event) { - gst_ogg_demux_send_event (ogg, event); - - gst_element_found_tags (GST_ELEMENT_CAST (ogg), - gst_tag_list_new_full (GST_TAG_CONTAINER_FORMAT, "Ogg", NULL)); - } - - GST_DEBUG_OBJECT (ogg, "starting chain"); - - /* then send out any headers and queued packets */ - for (i = 0; i < chain->streams->len; i++) { - GList *walk; - GstOggPad *pad; - - pad = g_array_index (chain->streams, GstOggPad *, i); - - GST_DEBUG_OBJECT (ogg, "pushing headers"); - /* push headers */ - for (walk = pad->map.headers; walk; walk = g_list_next (walk)) { - ogg_packet *p = walk->data; - - gst_ogg_demux_chain_peer (pad, p, TRUE); - } - - GST_DEBUG_OBJECT (ogg, "pushing queued buffers"); - /* push queued packets */ - for (walk = pad->map.queued; walk; walk = g_list_next (walk)) { - ogg_packet *p = walk->data; - - gst_ogg_demux_chain_peer (pad, p, TRUE); - _ogg_packet_free (p); - } - /* and free the queued buffers */ - g_list_free (pad->map.queued); - pad->map.queued = NULL; - } - return TRUE; -} - -static gboolean -do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin, - gint64 end, gint64 begintime, gint64 endtime, gint64 target, - gint64 * offset) -{ - gint64 best; - GstFlowReturn ret; - gint64 result = 0; - - best = begin; - - GST_DEBUG_OBJECT (ogg, - "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT, begin, - end); - GST_DEBUG_OBJECT (ogg, - "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT, - GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime)); - GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target)); - - /* perform the seek */ - while (begin < end) { - gint64 bisect; - - if ((end - begin < CHUNKSIZE) || (endtime == begintime)) { - bisect = begin; - } else { - /* take a (pretty decent) guess, avoiding overflow */ - gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime); - - bisect = (target - begintime) / GST_MSECOND * rate + begin - CHUNKSIZE; - - if (bisect <= begin) - bisect = begin; - GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect); - } - gst_ogg_demux_seek (ogg, bisect); - - while (begin < end) { - ogg_page og; - - GST_DEBUG_OBJECT (ogg, - "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT - ", end %" G_GINT64_FORMAT, bisect, begin, end); - - ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result); - GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT, - result); - - if (ret == GST_FLOW_LIMIT) { - /* we hit the upper limit, go back a bit */ - if (bisect <= begin + 1) { - end = begin; /* found it */ - } else { - if (bisect == 0) - goto seek_error; - - bisect -= CHUNKSIZE; - if (bisect <= begin) - bisect = begin + 1; - - gst_ogg_demux_seek (ogg, bisect); - } - } else if (ret == GST_FLOW_OK) { - /* found offset of next ogg page */ - gint64 granulepos; - GstClockTime granuletime; - GstOggPad *pad; - - /* get the granulepos */ - GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT, - result); - granulepos = ogg_page_granulepos (&og); - if (granulepos == -1) { - GST_LOG_OBJECT (ogg, "granulepos of next page is -1"); - continue; - } - - /* get the stream */ - pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og)); - if (pad == NULL || pad->map.is_skeleton) - continue; - - /* convert granulepos to time */ - granuletime = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, - granulepos); - if (granuletime < pad->start_time) - continue; - - GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %" - GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime)); - - granuletime -= pad->start_time; - granuletime += chain->begin_time; - - GST_DEBUG_OBJECT (ogg, - "found page with granule %" G_GINT64_FORMAT " and time %" - GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime)); - - if (granuletime < target) { - best = result; /* raw offset of packet with granulepos */ - begin = ogg->offset; /* raw offset of next page */ - begintime = granuletime; - - bisect = begin; /* *not* begin + 1 */ - } else { - if (bisect <= begin + 1) { - end = begin; /* found it */ - } else { - if (end == ogg->offset) { /* we're pretty close - we'd be stuck in */ - end = result; - bisect -= CHUNKSIZE; /* an endless loop otherwise. */ - if (bisect <= begin) - bisect = begin + 1; - gst_ogg_demux_seek (ogg, bisect); - } else { - end = result; - endtime = granuletime; - break; - } - } - } - } else - goto seek_error; - } - } - GST_DEBUG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, best); - gst_ogg_demux_seek (ogg, best); - *offset = best; - - return TRUE; - - /* ERRORS */ -seek_error: - { - GST_DEBUG_OBJECT (ogg, "got a seek error"); - return FALSE; - } -} - -/* - * do seek to time @position, return FALSE or chain and TRUE - */ -static gboolean -gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment, - gboolean accurate, gboolean keyframe, GstOggChain ** rchain) -{ - guint64 position; - GstOggChain *chain = NULL; - gint64 begin, end; - gint64 begintime, endtime; - gint64 target, keytarget; - gint64 best; - gint64 total; - gint64 result = 0; - GstFlowReturn ret; - gint i, pending, len; - - position = segment->last_stop; - - /* first find the chain to search in */ - total = ogg->total_time; - if (ogg->chains->len == 0) - goto no_chains; - - for (i = ogg->chains->len - 1; i >= 0; i--) { - chain = g_array_index (ogg->chains, GstOggChain *, i); - total -= chain->total_time; - if (position >= total) - break; - } - - /* first step, locate page containing the required data */ - begin = chain->offset; - end = chain->end_offset; - begintime = chain->begin_time; - endtime = begintime + chain->total_time; - target = position - total + begintime; - - if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target, - &best)) - goto seek_error; - - /* second step: find pages for all streams, we use the keyframe_granule to keep - * track of which ones we saw. If we have seen a page for each stream we can - * calculate the positions of each keyframe. */ - GST_DEBUG_OBJECT (ogg, "find keyframes"); - len = pending = chain->streams->len; - - /* figure out where the keyframes are */ - keytarget = target; - - while (TRUE) { - ogg_page og; - gint64 granulepos; - GstOggPad *pad; - GstClockTime keyframe_time, granule_time; - - ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result); - GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT, - result); - if (ret == GST_FLOW_LIMIT) { - GST_LOG_OBJECT (ogg, "reached limit"); - break; - } - - /* get the stream */ - pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og)); - if (pad == NULL) - continue; - - if (pad->map.is_skeleton) - goto next; - - granulepos = ogg_page_granulepos (&og); - if (granulepos == -1) { - GST_LOG_OBJECT (ogg, "granulepos of next page is -1"); - continue; - } - - /* in reverse we want to go past the page with the lower timestamp */ - if (segment->rate < 0.0) { - /* get time for this pad */ - granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, - granulepos); - - GST_LOG_OBJECT (ogg, - "looking at page with ts %" GST_TIME_FORMAT ", target %" - GST_TIME_FORMAT, GST_TIME_ARGS (granule_time), - GST_TIME_ARGS (target)); - if (granule_time < target) - continue; - } - - /* we've seen this pad before */ - if (pad->keyframe_granule != -1) - continue; - - /* convert granule of this pad to the granule of the keyframe */ - pad->keyframe_granule = gst_ogg_stream_granulepos_to_key_granule (&pad->map, - granulepos); - GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT, - pad->keyframe_granule); - - /* get time of the keyframe */ - keyframe_time = - gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule); - GST_LOG_OBJECT (ogg, "stream %08lx granule time %" GST_TIME_FORMAT, - pad->map.serialno, GST_TIME_ARGS (keyframe_time)); - - /* collect smallest value */ - if (keyframe_time != -1) { - keyframe_time += begintime; - if (keyframe_time < keytarget) - keytarget = keyframe_time; - } - - next: - pending--; - if (pending == 0) - break; - } - - /* for negative rates we will get to the keyframe backwards */ - if (segment->rate < 0.0) - goto done; - - if (keytarget != target) { - GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT, - GST_TIME_ARGS (keytarget)); - - /* last step, seek to the location of the keyframe */ - if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, - keytarget, &best)) - goto seek_error; - } else { - /* seek back to previous position */ - GST_LOG_OBJECT (ogg, "keyframe on target"); - gst_ogg_demux_seek (ogg, best); - } - -done: - if (keyframe) { - if (segment->rate > 0.0) - segment->time = keytarget; - segment->last_stop = keytarget - begintime; - } - - *rchain = chain; - - return TRUE; - -no_chains: - { - GST_DEBUG_OBJECT (ogg, "no chains"); - return FALSE; - } -seek_error: - { - GST_DEBUG_OBJECT (ogg, "got a seek error"); - return FALSE; - } -} - -/* does not take ownership of the event */ -static gboolean -gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event) -{ - GstOggChain *chain = NULL; - gboolean res; - gboolean flush, accurate, keyframe; - GstFormat format; - gdouble rate; - GstSeekFlags flags; - GstSeekType cur_type, stop_type; - gint64 cur, stop; - gboolean update; - guint32 seqnum; - GstEvent *tevent; - - if (event) { - GST_DEBUG_OBJECT (ogg, "seek with event"); - - gst_event_parse_seek (event, &rate, &format, &flags, - &cur_type, &cur, &stop_type, &stop); - - /* we can only seek on time */ - if (format != GST_FORMAT_TIME) { - GST_DEBUG_OBJECT (ogg, "can only seek on TIME"); - goto error; - } - seqnum = gst_event_get_seqnum (event); - } else { - GST_DEBUG_OBJECT (ogg, "seek without event"); - - flags = 0; - rate = 1.0; - seqnum = gst_util_seqnum_next (); - } - - GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate); - - flush = flags & GST_SEEK_FLAG_FLUSH; - accurate = flags & GST_SEEK_FLAG_ACCURATE; - keyframe = flags & GST_SEEK_FLAG_KEY_UNIT; - - /* first step is to unlock the streaming thread if it is - * blocked in a chain call, we do this by starting the flush. because - * we cannot yet hold any streaming lock, we have to protect the chains - * with their own lock. */ - if (flush) { - gint i; - - tevent = gst_event_new_flush_start (); - gst_event_set_seqnum (tevent, seqnum); - - gst_event_ref (tevent); - gst_pad_push_event (ogg->sinkpad, tevent); - - GST_CHAIN_LOCK (ogg); - for (i = 0; i < ogg->chains->len; i++) { - GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i); - gint j; - - for (j = 0; j < chain->streams->len; j++) { - GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j); - - gst_event_ref (tevent); - gst_pad_push_event (GST_PAD (pad), tevent); - } - } - GST_CHAIN_UNLOCK (ogg); - - gst_event_unref (tevent); - } else { - gst_pad_pause_task (ogg->sinkpad); - } - - /* now grab the stream lock so that streaming cannot continue, for - * non flushing seeks when the element is in PAUSED this could block - * forever. */ - GST_PAD_STREAM_LOCK (ogg->sinkpad); - - if (ogg->segment_running && !flush) { - /* create the segment event to close the current segment */ - if ((chain = ogg->current_chain)) { - GstEvent *newseg; - gint64 chain_start = 0; - - if (chain->segment_start != GST_CLOCK_TIME_NONE) - chain_start = chain->segment_start; - - newseg = gst_event_new_new_segment (TRUE, ogg->segment.rate, - GST_FORMAT_TIME, ogg->segment.start + chain_start, - ogg->segment.last_stop + chain_start, ogg->segment.time); - /* set the seqnum of the running segment */ - gst_event_set_seqnum (newseg, ogg->seqnum); - - /* send segment on old chain, FIXME, must be sent from streaming thread. */ - gst_ogg_demux_send_event (ogg, newseg); - } - } - - if (event) { - gst_segment_set_seek (&ogg->segment, rate, format, flags, - cur_type, cur, stop_type, stop, &update); - } - - GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%" - GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start), - GST_TIME_ARGS (ogg->segment.stop)); - - /* we need to stop flushing on the srcpad as we're going to use it - * next. We can do this as we have the STREAM lock now. */ - if (flush) { - tevent = gst_event_new_flush_stop (); - gst_event_set_seqnum (tevent, seqnum); - gst_pad_push_event (ogg->sinkpad, tevent); - } - - { - gint i; - - /* reset all ogg streams now, need to do this from within the lock to - * make sure the streaming thread is not messing with the stream */ - for (i = 0; i < ogg->chains->len; i++) { - GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i); - - gst_ogg_chain_reset (chain); - } - } - - /* for reverse we will already seek accurately */ - res = gst_ogg_demux_do_seek (ogg, &ogg->segment, accurate, keyframe, &chain); - - /* seek failed, make sure we continue the current chain */ - if (!res) { - GST_DEBUG_OBJECT (ogg, "seek failed"); - chain = ogg->current_chain; - } else { - GST_DEBUG_OBJECT (ogg, "seek success"); - } - - if (!chain) - goto no_chain; - - /* now we have a new position, prepare for streaming again */ - { - GstEvent *event; - gint64 stop; - gint64 start; - gint64 last_stop, begin_time; - - /* we have to send the flush to the old chain, not the new one */ - if (flush) { - tevent = gst_event_new_flush_stop (); - gst_event_set_seqnum (tevent, seqnum); - gst_ogg_demux_send_event (ogg, tevent); - } - - /* we need this to see how far inside the chain we need to start */ - if (chain->begin_time != GST_CLOCK_TIME_NONE) - begin_time = chain->begin_time; - else - begin_time = 0; - - /* segment.start gives the start over all chains, we calculate the amount - * of time into this chain we need to start */ - start = ogg->segment.start - begin_time; - if (chain->segment_start != GST_CLOCK_TIME_NONE) - start += chain->segment_start; - - if ((stop = ogg->segment.stop) == -1) - stop = ogg->segment.duration; - - /* segment.stop gives the stop time over all chains, calculate the amount of - * time we need to stop in this chain */ - if (stop != -1) { - if (stop > begin_time) - stop -= begin_time; - else - stop = 0; - stop += chain->segment_start; - /* we must stop when this chain ends and switch to the next chain to play - * the remainder of the segment. */ - stop = MIN (stop, chain->segment_stop); - } - - last_stop = ogg->segment.last_stop; - if (chain->segment_start != GST_CLOCK_TIME_NONE) - last_stop += chain->segment_start; - - /* create the segment event we are going to send out */ - if (ogg->segment.rate >= 0.0) - event = gst_event_new_new_segment (FALSE, ogg->segment.rate, - ogg->segment.format, last_stop, stop, ogg->segment.time); - else - event = gst_event_new_new_segment (FALSE, ogg->segment.rate, - ogg->segment.format, start, last_stop, ogg->segment.time); - - gst_event_set_seqnum (event, seqnum); - - if (chain != ogg->current_chain) { - /* switch to different chain, send segment on new chain */ - gst_ogg_demux_activate_chain (ogg, chain, event); - } else { - /* mark discont and send segment on current chain */ - gst_ogg_chain_mark_discont (chain); - /* This event should be sent from the streaming thread (sink pad task) */ - if (ogg->newsegment) - gst_event_unref (ogg->newsegment); - ogg->newsegment = event; - } - - /* notify start of new segment */ - if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) { - GstMessage *message; - - message = gst_message_new_segment_start (GST_OBJECT (ogg), - GST_FORMAT_TIME, ogg->segment.last_stop); - gst_message_set_seqnum (message, seqnum); - - gst_element_post_message (GST_ELEMENT (ogg), message); - } - - ogg->segment_running = TRUE; - ogg->seqnum = seqnum; - /* restart our task since it might have been stopped when we did the - * flush. */ - gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop, - ogg->sinkpad); - } - - /* streaming can continue now */ - GST_PAD_STREAM_UNLOCK (ogg->sinkpad); - - return res; - -error: - { - GST_DEBUG_OBJECT (ogg, "seek failed"); - return FALSE; - } -no_chain: - { - GST_DEBUG_OBJECT (ogg, "no chain to seek in"); - GST_PAD_STREAM_UNLOCK (ogg->sinkpad); - return FALSE; - } -} - -/* finds each bitstream link one at a time using a bisection search - * (has to begin by knowing the offset of the lb's initial page). - * Recurses for each link so it can alloc the link storage after - * finding them all, then unroll and fill the cache at the same time - */ -static GstFlowReturn -gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg, - gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m) -{ - gint64 endsearched = end; - gint64 next = end; - ogg_page og; - GstFlowReturn ret; - gint64 offset; - GstOggChain *nextchain; - - GST_LOG_OBJECT (ogg, - "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT - ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain); - - /* the below guards against garbage seperating the last and - * first pages of two links. */ - while (searched < endsearched) { - gint64 bisect; - - if (endsearched - searched < CHUNKSIZE) { - bisect = searched; - } else { - bisect = (searched + endsearched) / 2; - } - - gst_ogg_demux_seek (ogg, bisect); - ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset); - - if (ret == GST_FLOW_UNEXPECTED) { - endsearched = bisect; - } else if (ret == GST_FLOW_OK) { - glong serial = ogg_page_serialno (&og); - - if (!gst_ogg_chain_has_stream (chain, serial)) { - endsearched = bisect; - next = offset; - } else { - searched = offset + og.header_len + og.body_len; - } - } else - return ret; - } - - GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched); - - chain->end_offset = searched; - ret = gst_ogg_demux_read_end_chain (ogg, chain); - if (ret != GST_FLOW_OK) - return ret; - - GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next); - - gst_ogg_demux_seek (ogg, next); - ret = gst_ogg_demux_read_chain (ogg, &nextchain); - if (ret == GST_FLOW_UNEXPECTED) { - nextchain = NULL; - ret = GST_FLOW_OK; - GST_LOG_OBJECT (ogg, "no next chain"); - } else if (ret != GST_FLOW_OK) - goto done; - - if (searched < end && nextchain != NULL) { - ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset, - end, nextchain, m + 1); - if (ret != GST_FLOW_OK) - goto done; - } - GST_LOG_OBJECT (ogg, "adding chain %p", chain); - - g_array_insert_val (ogg->chains, 0, chain); - -done: - return ret; -} - -/* read a chain from the ogg file. This code will - * read all BOS pages and will create and return a GstOggChain - * structure with the results. - * - * This function will also read N pages from each stream in the - * chain and submit them to the decoders. When the decoder has - * decoded the first buffer, we know the timestamp of the first - * page in the chain. - */ -static GstFlowReturn -gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain) -{ - GstFlowReturn ret; - GstOggChain *chain = NULL; - gint64 offset = ogg->offset; - ogg_page op; - gboolean done; - gint i; - - GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset); - - /* first read the BOS pages, do typefind on them, create - * the decoders, send data to the decoders. */ - while (TRUE) { - GstOggPad *pad; - glong serial; - - ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL); - if (ret != GST_FLOW_OK) { - GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret); - break; - } - if (!ogg_page_bos (&op)) { - GST_WARNING_OBJECT (ogg, "page is not BOS page"); - /* if we did not find a chain yet, assume this is a bogus stream and - * ignore it */ - if (!chain) - ret = GST_FLOW_UNEXPECTED; - break; - } - - if (chain == NULL) { - chain = gst_ogg_chain_new (ogg); - chain->offset = offset; - } - - serial = ogg_page_serialno (&op); - if (gst_ogg_chain_get_stream (chain, serial) != NULL) { - GST_WARNING_OBJECT (ogg, "found serial %08lx BOS page twice, ignoring", - serial); - continue; - } - - pad = gst_ogg_chain_new_stream (chain, serial); - gst_ogg_pad_submit_page (pad, &op); - } - - if (ret != GST_FLOW_OK || chain == NULL) { - if (ret == GST_FLOW_OK) { - GST_WARNING_OBJECT (ogg, "no chain was found"); - ret = GST_FLOW_ERROR; - } else if (ret != GST_FLOW_UNEXPECTED) { - GST_WARNING_OBJECT (ogg, "failed to read chain"); - } else { - GST_DEBUG_OBJECT (ogg, "done reading chains"); - } - if (chain) { - gst_ogg_chain_free (chain); - } - if (res_chain) - *res_chain = NULL; - return ret; - } - - chain->have_bos = TRUE; - GST_LOG_OBJECT (ogg, "read bos pages, init decoder now"); - - /* now read pages until we receive a buffer from each of the - * stream decoders, this will tell us the timestamp of the - * first packet in the chain then */ - - /* save the offset to the first non bos page in the chain: if searching for - * pad->first_time we read past the end of the chain, we'll seek back to this - * position - */ - offset = ogg->offset; - - done = FALSE; - while (!done) { - glong serial; - gboolean known_serial = FALSE; - GstFlowReturn ret; - - serial = ogg_page_serialno (&op); - done = TRUE; - for (i = 0; i < chain->streams->len; i++) { - GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); - - GST_LOG_OBJECT (ogg, "serial %08lx time %" GST_TIME_FORMAT, - pad->map.serialno, GST_TIME_ARGS (pad->start_time)); - - if (pad->map.serialno == serial) { - known_serial = TRUE; - - /* submit the page now, this will fill in the start_time when the - * internal decoder finds it */ - gst_ogg_pad_submit_page (pad, &op); - - if (!pad->map.is_skeleton && pad->start_time == -1 - && ogg_page_eos (&op)) { - /* got EOS on a pad before we could find its start_time. - * We have no chance of finding a start_time for every pad so - * stop searching for the other start_time(s). - */ - done = TRUE; - break; - } - } - /* the timestamp will be filled in when we submit the pages */ - if (!pad->map.is_skeleton) - done &= (pad->start_time != GST_CLOCK_TIME_NONE); - - GST_LOG_OBJECT (ogg, "done %08lx now %d", pad->map.serialno, done); - } - - /* we read a page not belonging to the current chain: seek back to the - * beginning of the chain - */ - if (!known_serial) { - GST_LOG_OBJECT (ogg, "unknown serial %08lx", serial); - gst_ogg_demux_seek (ogg, offset); - break; - } - - if (!done) { - ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL); - if (ret != GST_FLOW_OK) - break; - } - } - GST_LOG_OBJECT (ogg, "done reading chain"); - /* now we can fill in the missing info using queries */ - for (i = 0; i < chain->streams->len; i++) { - GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); - - if (pad->map.is_skeleton) - continue; - - pad->mode = GST_OGG_PAD_MODE_STREAMING; - } - - if (res_chain) - *res_chain = chain; - - return GST_FLOW_OK; -} - -/* read the last pages from the ogg stream to get the final - * page end_offsets. - */ -static GstFlowReturn -gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain) -{ - gint64 begin = chain->end_offset; - gint64 end = begin; - gint64 last_granule = -1; - GstOggPad *last_pad = NULL; - GstFlowReturn ret; - gboolean done = FALSE; - ogg_page og; - gint i; - - while (!done) { - begin -= CHUNKSIZE; - if (begin < 0) - begin = 0; - - gst_ogg_demux_seek (ogg, begin); - - /* now continue reading until we run out of data, if we find a page - * start, we save it. It might not be the final page as there could be - * another page after this one. */ - while (ogg->offset < end) { - ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL); - - if (ret == GST_FLOW_LIMIT) - break; - if (ret != GST_FLOW_OK) - return ret; - - for (i = 0; i < chain->streams->len; i++) { - GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); - - if (pad->map.is_skeleton) - continue; - - if (pad->map.serialno == ogg_page_serialno (&og)) { - gint64 granulepos = ogg_page_granulepos (&og); - - if (last_granule == -1 || last_granule < granulepos) { - last_granule = granulepos; - last_pad = pad; - } - if (last_granule != -1) { - done = TRUE; - } - break; - } - } - } - } - - if (last_pad) { - chain->segment_stop = - gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map, - last_granule); - } else { - chain->segment_stop = GST_CLOCK_TIME_NONE; - } - - GST_INFO ("segment stop %" G_GUINT64_FORMAT, chain->segment_stop); - - return GST_FLOW_OK; -} - -/* find a pad with a given serial number - */ -static GstOggPad * -gst_ogg_demux_find_pad (GstOggDemux * ogg, glong serialno) -{ - GstOggPad *pad; - gint i; - - /* first look in building chain if any */ - if (ogg->building_chain) { - pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno); - if (pad) - return pad; - } - - /* then look in current chain if any */ - if (ogg->current_chain) { - pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno); - if (pad) - return pad; - } - - for (i = 0; i < ogg->chains->len; i++) { - GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i); - - pad = gst_ogg_chain_get_stream (chain, serialno); - if (pad) - return pad; - } - return NULL; -} - -/* find a chain with a given serial number - */ -static GstOggChain * -gst_ogg_demux_find_chain (GstOggDemux * ogg, glong serialno) -{ - GstOggPad *pad; - - pad = gst_ogg_demux_find_pad (ogg, serialno); - if (pad) { - return pad->chain; - } - return NULL; -} - -/* returns TRUE if all streams have valid start time */ -static gboolean -gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain) -{ - gint i; - gboolean res = TRUE; - - chain->total_time = GST_CLOCK_TIME_NONE; - chain->segment_start = G_MAXUINT64; - - GST_DEBUG_OBJECT (ogg, "trying to collect chain info"); - - for (i = 0; i < chain->streams->len; i++) { - GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); - - if (pad->map.is_skeleton) - continue; - - /* can do this if the pad start time is not defined */ - if (pad->start_time == GST_CLOCK_TIME_NONE) - res = FALSE; - else - chain->segment_start = MIN (chain->segment_start, pad->start_time); - } - - if (chain->segment_stop != GST_CLOCK_TIME_NONE - && chain->segment_start != G_MAXUINT64) - chain->total_time = chain->segment_stop - chain->segment_start; - - GST_DEBUG ("total time %" G_GUINT64_FORMAT, chain->total_time); - - GST_DEBUG_OBJECT (ogg, "return %d", res); - - return res; -} - -static void -gst_ogg_demux_collect_info (GstOggDemux * ogg) -{ - gint i; - - /* collect all info */ - ogg->total_time = 0; - - for (i = 0; i < ogg->chains->len; i++) { - GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i); - - chain->begin_time = ogg->total_time; - - gst_ogg_demux_collect_chain_info (ogg, chain); - - ogg->total_time += chain->total_time; - } - gst_segment_set_duration (&ogg->segment, GST_FORMAT_TIME, ogg->total_time); -} - -/* find all the chains in the ogg file, this reads the first and - * last page of the ogg stream, if they match then the ogg file has - * just one chain, else we do a binary search for all chains. - */ -static GstFlowReturn -gst_ogg_demux_find_chains (GstOggDemux * ogg) -{ - ogg_page og; - GstPad *peer; - GstFormat format; - gboolean res; - gulong serialno; - GstOggChain *chain; - GstFlowReturn ret; - - /* get peer to figure out length */ - if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL) - goto no_peer; - - /* find length to read last page, we store this for later use. */ - format = GST_FORMAT_BYTES; - res = gst_pad_query_duration (peer, &format, &ogg->length); - gst_object_unref (peer); - if (!res || ogg->length <= 0) - goto no_length; - - GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length); - - /* read chain from offset 0, this is the first chain of the - * ogg file. */ - gst_ogg_demux_seek (ogg, 0); - ret = gst_ogg_demux_read_chain (ogg, &chain); - if (ret != GST_FLOW_OK) - goto no_first_chain; - - /* read page from end offset, we use this page to check if its serial - * number is contained in the first chain. If this is the case then - * this ogg is not a chained ogg and we can skip the scanning. */ - gst_ogg_demux_seek (ogg, ogg->length); - ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL); - if (ret != GST_FLOW_OK) - goto no_last_page; - - serialno = ogg_page_serialno (&og); - - if (!gst_ogg_chain_has_stream (chain, serialno)) { - /* the last page is not in the first stream, this means we should - * find all the chains in this chained ogg. */ - ret = - gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain, - 0); - } else { - /* we still call this function here but with an empty range so that - * we can reuse the setup code in this routine. */ - ret = - gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length, ogg->length, - chain, 0); - } - if (ret != GST_FLOW_OK) - goto done; - - /* all fine, collect and print */ - gst_ogg_demux_collect_info (ogg); - - /* dump our chains and streams */ - gst_ogg_print (ogg); - -done: - return ret; - - /*** error cases ***/ -no_peer: - { - GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer")); - return GST_FLOW_NOT_LINKED; - } -no_length: - { - GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length")); - return GST_FLOW_NOT_SUPPORTED; - } -no_first_chain: - { - GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain")); - return GST_FLOW_ERROR; - } -no_last_page: - { - GST_DEBUG_OBJECT (ogg, "can't get last page"); - if (chain) - gst_ogg_chain_free (chain); - return ret; - } -} - -static GstFlowReturn -gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page) -{ - GstOggPad *pad; - gint64 granule; - glong serialno; - GstFlowReturn result = GST_FLOW_OK; - - serialno = ogg_page_serialno (page); - granule = ogg_page_granulepos (page); - - GST_LOG_OBJECT (ogg, - "processing ogg page (serial %08lx, pageno %ld, granulepos %" - G_GINT64_FORMAT ", bos %d)", - serialno, ogg_page_pageno (page), granule, ogg_page_bos (page)); - - if (ogg_page_bos (page)) { - GstOggChain *chain; - - /* first page */ - /* see if we know about the chain already */ - chain = gst_ogg_demux_find_chain (ogg, serialno); - if (chain) { - GstEvent *event; - gint64 start = 0; - - if (chain->segment_start != GST_CLOCK_TIME_NONE) - start = chain->segment_start; - - /* create the newsegment event we are going to send out */ - event = gst_event_new_new_segment (FALSE, ogg->segment.rate, - GST_FORMAT_TIME, start, chain->segment_stop, chain->begin_time); - gst_event_set_seqnum (event, ogg->seqnum); - - GST_DEBUG_OBJECT (ogg, - "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT - ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start), - GST_TIME_ARGS (chain->segment_stop), - GST_TIME_ARGS (chain->begin_time)); - - /* activate it as it means we have a non-header, this will also deactivate - * the currently running chain. */ - gst_ogg_demux_activate_chain (ogg, chain, event); - pad = gst_ogg_demux_find_pad (ogg, serialno); - } else { - GstClockTime chain_time; - GstOggChain *current_chain; - gint64 current_time; - - /* this can only happen in push mode */ - if (ogg->pullmode) - goto unknown_chain; - - current_chain = ogg->current_chain; - current_time = ogg->segment.last_stop; - - /* time of new chain is current time */ - chain_time = current_time; - - if (ogg->building_chain == NULL) { - GstOggChain *newchain; - - newchain = gst_ogg_chain_new (ogg); - newchain->offset = 0; - /* set new chain begin time aligned with end time of old chain */ - newchain->begin_time = chain_time; - GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT, - GST_TIME_ARGS (chain_time)); - - /* and this is the one we are building now */ - ogg->building_chain = newchain; - } - pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno); - } - } else { - pad = gst_ogg_demux_find_pad (ogg, serialno); - } - if (pad) { - result = gst_ogg_pad_submit_page (pad, page); - } else { - /* no pad. This means an ogg page without bos has been seen for this - * serialno. we just ignore it but post a warning... */ - GST_ELEMENT_WARNING (ogg, STREAM, DECODE, - (NULL), ("unknown ogg pad for serial %08lx detected", serialno)); - return GST_FLOW_OK; - } - return result; - - /* ERRORS */ -unknown_chain: - { - GST_ELEMENT_ERROR (ogg, STREAM, DECODE, - (NULL), ("unknown ogg chain for serial %08lx detected", serialno)); - return GST_FLOW_ERROR; - } -} - -/* streaming mode, receive a buffer, parse it, create pads for - * the serialno, submit pages and packets to the oggpads - */ -static GstFlowReturn -gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer) -{ - GstOggDemux *ogg; - gint ret = 0; - GstFlowReturn result = GST_FLOW_OK; - - ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad)); - - GST_DEBUG_OBJECT (ogg, "chain"); - result = gst_ogg_demux_submit_buffer (ogg, buffer); - - while (result == GST_FLOW_OK) { - ogg_page page; - - ret = ogg_sync_pageout (&ogg->sync, &page); - if (ret == 0) - /* need more data */ - break; - if (ret == -1) { - /* discontinuity in the pages */ - GST_DEBUG_OBJECT (ogg, "discont in page found, continuing"); - } else { - result = gst_ogg_demux_handle_page (ogg, &page); - } - } - if (ret == 0 || result == GST_FLOW_OK) { - gst_ogg_demux_sync_streams (ogg); - } - return result; -} - -static void -gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event) -{ - GstOggChain *chain = ogg->current_chain; - - if (chain) { - gint i; - - for (i = 0; i < chain->streams->len; i++) { - GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); - - gst_event_ref (event); - GST_DEBUG_OBJECT (pad, "Pushing event %" GST_PTR_FORMAT, event); - gst_pad_push_event (GST_PAD (pad), event); - } - } - gst_event_unref (event); -} - -static GstFlowReturn -gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad, - GstFlowReturn ret) -{ - GstOggChain *chain; - - /* store the value */ - pad->last_ret = ret; - - /* any other error that is not-linked can be returned right - * away */ - if (ret != GST_FLOW_NOT_LINKED) - goto done; - - /* only return NOT_LINKED if all other pads returned NOT_LINKED */ - chain = ogg->current_chain; - if (chain) { - gint i; - - for (i = 0; i < chain->streams->len; i++) { - GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i); - - ret = opad->last_ret; - /* some other return value (must be SUCCESS but we can return - * other values as well) */ - if (ret != GST_FLOW_NOT_LINKED) - goto done; - } - /* if we get here, all other pads were unlinked and we return - * NOT_LINKED then */ - } -done: - return ret; -} - -static GstFlowReturn -gst_ogg_demux_loop_forward (GstOggDemux * ogg) -{ - GstFlowReturn ret; - GstBuffer *buffer; - - if (ogg->offset == ogg->length) { - GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT - " == %" G_GINT64_FORMAT, ogg->offset, ogg->length); - ret = GST_FLOW_UNEXPECTED; - goto done; - } - - GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset); - ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer); - if (ret != GST_FLOW_OK) { - GST_LOG_OBJECT (ogg, "Failed pull_range"); - goto done; - } - - ogg->offset += GST_BUFFER_SIZE (buffer); - - if (G_UNLIKELY (ogg->newsegment)) { - gst_ogg_demux_send_event (ogg, ogg->newsegment); - ogg->newsegment = NULL; - } - - ret = gst_ogg_demux_chain (ogg->sinkpad, buffer); - if (ret != GST_FLOW_OK) { - GST_LOG_OBJECT (ogg, "Failed demux_chain"); - goto done; - } - - /* check for the end of the segment */ - if (ogg->segment.stop != -1 && ogg->segment.last_stop != -1) { - if (ogg->segment.last_stop > ogg->segment.stop) { - ret = GST_FLOW_UNEXPECTED; - goto done; - } - } -done: - return ret; -} - -/* reverse mode. - * - * We read the pages backwards and send the packets forwards. The first packet - * in the page will be pushed with the DISCONT flag set. - * - * Special care has to be taken for continued pages, which we can only decode - * when we have the previous page(s). - */ -static GstFlowReturn -gst_ogg_demux_loop_reverse (GstOggDemux * ogg) -{ - GstFlowReturn ret; - ogg_page page; - gint64 offset; - - if (ogg->offset == 0) { - GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT - " == 0", ogg->offset); - ret = GST_FLOW_UNEXPECTED; - goto done; - } - - GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset); - ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset); - if (ret != GST_FLOW_OK) - goto done; - - ogg->offset = offset; - - if (G_UNLIKELY (ogg->newsegment)) { - gst_ogg_demux_send_event (ogg, ogg->newsegment); - ogg->newsegment = NULL; - } - - ret = gst_ogg_demux_handle_page (ogg, &page); - if (ret != GST_FLOW_OK) - goto done; - - /* check for the end of the segment */ - if (ogg->segment.start != -1 && ogg->segment.last_stop != -1) { - if (ogg->segment.last_stop <= ogg->segment.start) { - ret = GST_FLOW_UNEXPECTED; - goto done; - } - } -done: - return ret; -} - -static void -gst_ogg_demux_sync_streams (GstOggDemux * ogg) -{ - GstClockTime cur; - GstOggChain *chain; - guint i; - - chain = ogg->current_chain; - cur = ogg->segment.last_stop; - if (chain == NULL || cur == -1) - return; - - for (i = 0; i < chain->streams->len; i++) { - GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i); - - /* Theoretically, we should be doing this for all streams, but we're only - * doing it for known-to-be-sparse streams at the moment in order not to - * break things for wrongly-muxed streams (like we used to produce once) */ - if (stream->is_sparse && stream->last_stop != GST_CLOCK_TIME_NONE) { - - /* Does this stream lag? Random threshold of 2 seconds */ - if (GST_CLOCK_DIFF (stream->last_stop, cur) > (2 * GST_SECOND)) { - GST_DEBUG_OBJECT (stream, "synchronizing stream with others by " - "advancing time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, - GST_TIME_ARGS (stream->last_stop), GST_TIME_ARGS (cur)); - stream->last_stop = cur; - /* advance stream time (FIXME: is this right, esp. time_pos?) */ - gst_pad_push_event (GST_PAD_CAST (stream), - gst_event_new_new_segment (TRUE, ogg->segment.rate, - GST_FORMAT_TIME, stream->last_stop, -1, stream->last_stop)); - } - } - } -} - -/* random access code - * - * - first find all the chains and streams by scanning the file. - * - then get and chain buffers, just like the streaming case. - * - when seeking, we can use the chain info to perform the seek. - */ -static void -gst_ogg_demux_loop (GstOggPad * pad) -{ - GstOggDemux *ogg; - GstFlowReturn ret; - GstEvent *event; - - ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad)); - - if (ogg->need_chains) { - gboolean res; - - /* this is the only place where we write chains and thus need to lock. */ - GST_CHAIN_LOCK (ogg); - ret = gst_ogg_demux_find_chains (ogg); - GST_CHAIN_UNLOCK (ogg); - if (ret != GST_FLOW_OK) - goto chain_read_failed; - - ogg->need_chains = FALSE; - - GST_OBJECT_LOCK (ogg); - ogg->running = TRUE; - event = ogg->event; - ogg->event = NULL; - GST_OBJECT_UNLOCK (ogg); - - /* and seek to configured positions without FLUSH */ - res = gst_ogg_demux_perform_seek (ogg, event); - if (event) - gst_event_unref (event); - - if (!res) - goto seek_failed; - } - - if (ogg->segment.rate >= 0.0) - ret = gst_ogg_demux_loop_forward (ogg); - else - ret = gst_ogg_demux_loop_reverse (ogg); - - if (ret != GST_FLOW_OK) - goto pause; - - gst_ogg_demux_sync_streams (ogg); - return; - - /* ERRORS */ -chain_read_failed: - { - /* error was posted */ - goto pause; - } -seek_failed: - { - GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), - ("failed to start demuxing ogg")); - ret = GST_FLOW_ERROR; - goto pause; - } -pause: - { - const gchar *reason = gst_flow_get_name (ret); - GstEvent *event = NULL; - - GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason); - ogg->segment_running = FALSE; - gst_pad_pause_task (ogg->sinkpad); - - if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) { - if (ret == GST_FLOW_UNEXPECTED) { - /* perform EOS logic */ - if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) { - gint64 stop; - GstMessage *message; - - /* for segment playback we need to post when (in stream time) - * we stopped, this is either stop (when set) or the duration. */ - if ((stop = ogg->segment.stop) == -1) - stop = ogg->segment.duration; - - GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment"); - message = - gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME, - stop); - gst_message_set_seqnum (message, ogg->seqnum); - - gst_element_post_message (GST_ELEMENT (ogg), message); - } else { - /* normal playback, send EOS to all linked pads */ - GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream"); - event = gst_event_new_eos (); - } - } else { - GST_ELEMENT_ERROR (ogg, STREAM, FAILED, - (_("Internal data stream error.")), - ("stream stopped, reason %s", reason)); - event = gst_event_new_eos (); - } - if (event) { - gst_event_set_seqnum (event, ogg->seqnum); - gst_ogg_demux_send_event (ogg, event); - } - } - return; - } -} - -static void -gst_ogg_demux_clear_chains (GstOggDemux * ogg) -{ - gint i; - - gst_ogg_demux_deactivate_current_chain (ogg); - - GST_CHAIN_LOCK (ogg); - for (i = 0; i < ogg->chains->len; i++) { - GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i); - - gst_ogg_chain_free (chain); - } - ogg->chains = g_array_set_size (ogg->chains, 0); - GST_CHAIN_UNLOCK (ogg); -} - -/* this function is called when the pad is activated and should start - * processing data. - * - * We check if we can do random access to decide if we work push or - * pull based. - */ -static gboolean -gst_ogg_demux_sink_activate (GstPad * sinkpad) -{ - if (gst_pad_check_pull_range (sinkpad)) { - GST_DEBUG_OBJECT (sinkpad, "activating pull"); - return gst_pad_activate_pull (sinkpad, TRUE); - } else { - GST_DEBUG_OBJECT (sinkpad, "activating push"); - return gst_pad_activate_push (sinkpad, TRUE); - } -} - -/* this function gets called when we activate ourselves in push mode. - * We cannot seek (ourselves) in the stream */ -static gboolean -gst_ogg_demux_sink_activate_push (GstPad * sinkpad, gboolean active) -{ - GstOggDemux *ogg; - - ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad)); - - ogg->pullmode = FALSE; - - return TRUE; -} - -/* this function gets called when we activate ourselves in pull mode. - * We can perform random access to the resource and we start a task - * to start reading */ -static gboolean -gst_ogg_demux_sink_activate_pull (GstPad * sinkpad, gboolean active) -{ - GstOggDemux *ogg; - - ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad)); - - if (active) { - ogg->need_chains = TRUE; - ogg->pullmode = TRUE; - - return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop, - sinkpad); - } else { - return gst_pad_stop_task (sinkpad); - } -} - -static GstStateChangeReturn -gst_ogg_demux_change_state (GstElement * element, GstStateChange transition) -{ - GstOggDemux *ogg; - GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE; - - ogg = GST_OGG_DEMUX (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - ogg->basetime = 0; - ogg->have_fishead = FALSE; - ogg_sync_init (&ogg->sync); - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - ogg_sync_reset (&ogg->sync); - ogg->running = FALSE; - ogg->segment_running = FALSE; - gst_segment_init (&ogg->segment, GST_FORMAT_TIME); - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - default: - break; - } - - result = parent_class->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_ogg_demux_clear_chains (ogg); - GST_OBJECT_LOCK (ogg); - ogg->running = FALSE; - ogg->segment_running = FALSE; - ogg->have_fishead = FALSE; - GST_OBJECT_UNLOCK (ogg); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - ogg_sync_clear (&ogg->sync); - break; - default: - break; - } - return result; -} - -gboolean -gst_ogg_demux_plugin_init (GstPlugin * plugin) -{ - GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer"); - GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0, - "ogg demuxer setup stage when parsing pipeline"); - -#if ENABLE_NLS - GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE, - LOCALEDIR); - bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); -#endif - - return gst_element_register (plugin, "oggdemux", GST_RANK_PRIMARY, - GST_TYPE_OGG_DEMUX); -} - -/* prints all info about the element */ -#undef GST_CAT_DEFAULT -#define GST_CAT_DEFAULT gst_ogg_demux_setup_debug - -#ifdef GST_DISABLE_GST_DEBUG - -static void -gst_ogg_print (GstOggDemux * ogg) -{ - /* NOP */ -} - -#else /* !GST_DISABLE_GST_DEBUG */ - -static void -gst_ogg_print (GstOggDemux * ogg) -{ - guint j, i; - - GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len); - GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT, - GST_TIME_ARGS (ogg->total_time)); - - for (i = 0; i < ogg->chains->len; i++) { - GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i); - - GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len); - GST_INFO_OBJECT (ogg, " offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, - chain->offset, chain->end_offset); - GST_INFO_OBJECT (ogg, " begin time: %" GST_TIME_FORMAT, - GST_TIME_ARGS (chain->begin_time)); - GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT, - GST_TIME_ARGS (chain->total_time)); - GST_INFO_OBJECT (ogg, " segment start: %" GST_TIME_FORMAT, - GST_TIME_ARGS (chain->segment_start)); - GST_INFO_OBJECT (ogg, " segment stop: %" GST_TIME_FORMAT, - GST_TIME_ARGS (chain->segment_stop)); - - for (j = 0; j < chain->streams->len; j++) { - GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j); - - GST_INFO_OBJECT (ogg, " stream %08lx:", stream->map.serialno); - GST_INFO_OBJECT (ogg, " start time: %" GST_TIME_FORMAT, - GST_TIME_ARGS (stream->start_time)); - } - } -} -#endif /* GST_DISABLE_GST_DEBUG */ diff --git a/ext/ogg/gstoggdemux.h b/ext/ogg/gstoggdemux.h deleted file mode 100644 index a47642b3..00000000 --- a/ext/ogg/gstoggdemux.h +++ /dev/null @@ -1,176 +0,0 @@ -/* GStreamer - * Copyright (C) 2004 Wim Taymans <wim@fluendo.com> - * - * gstoggdemux.c: ogg stream demuxer - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_OGG_DEMUX_H__ -#define __GST_OGG_DEMUX_H__ - -#include <ogg/ogg.h> - -#include <gst/gst.h> - -#include "gstoggstream.h" - -G_BEGIN_DECLS - -#define GST_TYPE_OGG_PAD (gst_ogg_pad_get_type()) -#define GST_OGG_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGG_PAD, GstOggPad)) -#define GST_OGG_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGG_PAD, GstOggPad)) -#define GST_IS_OGG_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_PAD)) -#define GST_IS_OGG_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_PAD)) - -typedef struct _GstOggPad GstOggPad; -typedef struct _GstOggPadClass GstOggPadClass; - -#define GST_TYPE_OGG_DEMUX (gst_ogg_demux_get_type()) -#define GST_OGG_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGG_DEMUX, GstOggDemux)) -#define GST_OGG_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGG_DEMUX, GstOggDemux)) -#define GST_IS_OGG_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_DEMUX)) -#define GST_IS_OGG_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_DEMUX)) - -GType gst_ogg_demux_get_type (void); - -typedef struct _GstOggDemux GstOggDemux; -typedef struct _GstOggDemuxClass GstOggDemuxClass; -typedef struct _GstOggChain GstOggChain; - -/* all information needed for one ogg chain (relevant for chained bitstreams) */ -struct _GstOggChain -{ - GstOggDemux *ogg; - - gint64 offset; /* starting offset of chain */ - gint64 end_offset; /* end offset of chain */ - gint64 bytes; /* number of bytes */ - - gboolean have_bos; - - GArray *streams; - - GstClockTime total_time; /* the total time of this chain, this is the MAX of - the totals of all streams */ - GstClockTime begin_time; /* when this chain starts in the stream */ - - GstClockTime segment_start; /* the timestamp of the first sample, this is the MIN of - the start times of all streams. */ - GstClockTime segment_stop; /* the timestamp of the last page, this is the MAX of the - streams. */ -}; - -/* different modes for the pad */ -typedef enum -{ - GST_OGG_PAD_MODE_INIT, /* we are feeding our internal decoder to get info */ - GST_OGG_PAD_MODE_STREAMING, /* we are streaming buffers to the outside */ -} GstOggPadMode; - -/* all information needed for one ogg stream */ -struct _GstOggPad -{ - GstPad pad; /* subclass GstPad */ - - gboolean have_type; - GstOggPadMode mode; - - GstOggChain *chain; /* the chain we are part of */ - GstOggDemux *ogg; /* the ogg demuxer we are part of */ - - GstOggStream map; - - gint64 packetno; - gint64 current_granule; - gint64 keyframe_granule; - - GstClockTime start_time; /* the timestamp of the first sample */ - - gint64 first_granule; /* the granulepos of first page == first sample in next page */ - GstClockTime first_time; /* the timestamp of the second page or granuletime of first page */ - - gboolean is_sparse; /* TRUE if this is a subtitle pad or some other sparse stream */ - GstClockTime last_stop; /* last_stop when last push occured; used to detect when we - * need to send a newsegment update event for sparse streams */ - - GList *continued; - - gboolean discont; - GstFlowReturn last_ret; /* last return of _pad_push() */ - - gboolean added; -}; - -struct _GstOggPadClass -{ - GstPadClass parent_class; -}; - -/** - * GstOggDemux: - * - * The ogg demuxer object structure. - */ -struct _GstOggDemux -{ - GstElement element; - - GstPad *sinkpad; - - gint64 length; - gint64 read_offset; - gint64 offset; - - gboolean pullmode; - gboolean running; - - gboolean need_chains; - - /* state */ - GMutex *chain_lock; /* we need the lock to protect the chains */ - GArray *chains; /* list of chains we know */ - GstClockTime total_time; - - GstOggChain *current_chain; - GstOggChain *building_chain; - - /* playback start/stop positions */ - GstSegment segment; - gboolean segment_running; - guint32 seqnum; - - GstEvent *event; - GstEvent *newsegment; /* pending newsegment to be sent from _loop */ - - /* annodex stuff */ - gboolean have_fishead; - gint64 basetime; - gint64 prestime; - - /* ogg stuff */ - ogg_sync_state sync; -}; - -struct _GstOggDemuxClass -{ - GstElementClass parent_class; -}; - - -G_END_DECLS - -#endif /* __GST_OGG_DEMUX_H__ */ diff --git a/ext/ogg/gstoggmux.c b/ext/ogg/gstoggmux.c deleted file mode 100644 index b37a5638..00000000 --- a/ext/ogg/gstoggmux.c +++ /dev/null @@ -1,1692 +0,0 @@ -/* OGG muxer plugin for GStreamer - * Copyright (C) 2004 Wim Taymans <wim@fluendo.com> - * Copyright (C) 2006 Thomas Vander Stichele <thomas at apestaart dot org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-oggmux - * @see_also: <link linkend="gst-plugins-base-plugins-deoggmux">oggdemux</link> - * - * This element merges streams (audio and video) into ogg files. - * - * <refsect2> - * <title>Example pipelines</title> - * |[ - * gst-launch v4l2src num-buffers=500 ! video/x-raw-yuv,width=320,height=240 ! ffmpegcolorspace ! theoraenc ! oggmux ! filesink location=video.ogg - * ]| Encodes a video stream captured from a v4l2-compatible camera to Ogg/Theora - * (the encoding will stop automatically after 500 frames) - * </refsect2> - * - * Last reviewed on 2008-02-06 (0.10.17) - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <gst/gst.h> -#include <gst/base/gstcollectpads.h> - -#include "gstoggmux.h" - -/* memcpy - if someone knows a way to get rid of it, please speak up - * note: the ogg docs even say you need this... */ -#include <string.h> -#include <time.h> -#include <stdlib.h> /* rand, srand, atoi */ - -GST_DEBUG_CATEGORY_STATIC (gst_ogg_mux_debug); -#define GST_CAT_DEFAULT gst_ogg_mux_debug - -/* This isn't generally what you'd want with an end-time macro, because - technically the end time of a buffer with invalid duration is invalid. But - for sorting ogg pages this is what we want. */ -#define GST_BUFFER_END_TIME(buf) \ - (GST_BUFFER_DURATION_IS_VALID (buf) \ - ? GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf) \ - : GST_BUFFER_TIMESTAMP (buf)) - -#define GST_GP_FORMAT "[gp %8" G_GINT64_FORMAT "]" -#define GST_GP_CAST(_gp) ((gint64) _gp) - -typedef enum -{ - GST_OGG_FLAG_BOS = GST_ELEMENT_FLAG_LAST, - GST_OGG_FLAG_EOS -} -GstOggFlag; - -/* elementfactory information */ -static const GstElementDetails gst_ogg_mux_details = -GST_ELEMENT_DETAILS ("Ogg muxer", - "Codec/Muxer", - "mux ogg streams (info about ogg: http://xiph.org)", - "Wim Taymans <wim@fluendo.com>"); - -/* OggMux signals and args */ -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; - -/* set to 0.5 seconds by default */ -#define DEFAULT_MAX_DELAY G_GINT64_CONSTANT(500000000) -#define DEFAULT_MAX_PAGE_DELAY G_GINT64_CONSTANT(500000000) -enum -{ - ARG_0, - ARG_MAX_DELAY, - ARG_MAX_PAGE_DELAY, -}; - -static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("application/ogg") - ); - -static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS ("video/x-theora; " - "audio/x-vorbis; audio/x-flac; audio/x-speex; audio/x-celt; " - "application/x-ogm-video; application/x-ogm-audio; video/x-dirac; " - "video/x-smoke; text/x-cmml, encoded = (boolean) TRUE; " - "subtitle/x-kate; application/x-kate") - ); - -static void gst_ogg_mux_base_init (gpointer g_class); -static void gst_ogg_mux_class_init (GstOggMuxClass * klass); -static void gst_ogg_mux_init (GstOggMux * ogg_mux); -static void gst_ogg_mux_finalize (GObject * object); - -static GstFlowReturn -gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux); -static gboolean gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event); -static GstPad *gst_ogg_mux_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * name); -static void gst_ogg_mux_release_pad (GstElement * element, GstPad * pad); - -static void gst_ogg_mux_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_ogg_mux_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); -static GstStateChangeReturn gst_ogg_mux_change_state (GstElement * element, - GstStateChange transition); - -static GstElementClass *parent_class = NULL; - -/*static guint gst_ogg_mux_signals[LAST_SIGNAL] = { 0 }; */ - -GType -gst_ogg_mux_get_type (void) -{ - static GType ogg_mux_type = 0; - - if (G_UNLIKELY (ogg_mux_type == 0)) { - static const GTypeInfo ogg_mux_info = { - sizeof (GstOggMuxClass), - gst_ogg_mux_base_init, - NULL, - (GClassInitFunc) gst_ogg_mux_class_init, - NULL, - NULL, - sizeof (GstOggMux), - 0, - (GInstanceInitFunc) gst_ogg_mux_init, - }; - static const GInterfaceInfo preset_info = { - NULL, - NULL, - NULL - }; - - ogg_mux_type = - g_type_register_static (GST_TYPE_ELEMENT, "GstOggMux", &ogg_mux_info, - 0); - - g_type_add_interface_static (ogg_mux_type, GST_TYPE_PRESET, &preset_info); - } - return ogg_mux_type; -} - -static void -gst_ogg_mux_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - 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, &gst_ogg_mux_details); -} - -static void -gst_ogg_mux_class_init (GstOggMuxClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->finalize = gst_ogg_mux_finalize; - gobject_class->get_property = gst_ogg_mux_get_property; - gobject_class->set_property = gst_ogg_mux_set_property; - - gstelement_class->request_new_pad = gst_ogg_mux_request_new_pad; - gstelement_class->release_pad = gst_ogg_mux_release_pad; - - g_object_class_install_property (gobject_class, ARG_MAX_DELAY, - g_param_spec_uint64 ("max-delay", "Max delay", - "Maximum delay in multiplexing streams", 0, G_MAXUINT64, - DEFAULT_MAX_DELAY, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_MAX_PAGE_DELAY, - g_param_spec_uint64 ("max-page-delay", "Max page delay", - "Maximum delay for sending out a page", 0, G_MAXUINT64, - DEFAULT_MAX_PAGE_DELAY, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - gstelement_class->change_state = gst_ogg_mux_change_state; - -} - -#if 0 -static const GstEventMask * -gst_ogg_mux_get_sink_event_masks (GstPad * pad) -{ - static const GstEventMask gst_ogg_mux_sink_event_masks[] = { - {GST_EVENT_EOS, 0}, - {GST_EVENT_DISCONTINUOUS, 0}, - {0,} - }; - - return gst_ogg_mux_sink_event_masks; -} -#endif - -static void -gst_ogg_mux_clear (GstOggMux * ogg_mux) -{ - ogg_mux->pulling = NULL; - ogg_mux->need_headers = TRUE; - ogg_mux->delta_pad = NULL; - ogg_mux->offset = 0; - ogg_mux->next_ts = 0; - ogg_mux->last_ts = GST_CLOCK_TIME_NONE; -} - -static void -gst_ogg_mux_init (GstOggMux * ogg_mux) -{ - GstElementClass *klass = GST_ELEMENT_GET_CLASS (ogg_mux); - - ogg_mux->srcpad = - gst_pad_new_from_template (gst_element_class_get_pad_template (klass, - "src"), "src"); - gst_pad_set_event_function (ogg_mux->srcpad, gst_ogg_mux_handle_src_event); - gst_element_add_pad (GST_ELEMENT (ogg_mux), ogg_mux->srcpad); - - GST_OBJECT_FLAG_SET (GST_ELEMENT (ogg_mux), GST_OGG_FLAG_BOS); - - /* seed random number generator for creation of serial numbers */ - srand (time (NULL)); - - ogg_mux->collect = gst_collect_pads_new (); - gst_collect_pads_set_function (ogg_mux->collect, - (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_ogg_mux_collected), - ogg_mux); - - ogg_mux->max_delay = DEFAULT_MAX_DELAY; - ogg_mux->max_page_delay = DEFAULT_MAX_PAGE_DELAY; - - gst_ogg_mux_clear (ogg_mux); -} - -static void -gst_ogg_mux_finalize (GObject * object) -{ - GstOggMux *ogg_mux; - - ogg_mux = GST_OGG_MUX (object); - - if (ogg_mux->collect) { - gst_object_unref (ogg_mux->collect); - ogg_mux->collect = NULL; - } - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_ogg_mux_ogg_pad_destroy_notify (GstCollectData * data) -{ - GstOggPad *oggpad = (GstOggPad *) data; - GstBuffer *buf; - - ogg_stream_clear (&oggpad->stream); - - if (oggpad->pagebuffers) { - while ((buf = g_queue_pop_head (oggpad->pagebuffers)) != NULL) { - gst_buffer_unref (buf); - } - g_queue_free (oggpad->pagebuffers); - oggpad->pagebuffers = NULL; - } -} - -static GstPadLinkReturn -gst_ogg_mux_sinkconnect (GstPad * pad, GstPad * peer) -{ - GstOggMux *ogg_mux; - - ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad)); - - GST_DEBUG_OBJECT (ogg_mux, "sinkconnect triggered on %s", GST_PAD_NAME (pad)); - - gst_object_unref (ogg_mux); - - return GST_PAD_LINK_OK; -} - -static gboolean -gst_ogg_mux_sink_event (GstPad * pad, GstEvent * event) -{ - GstOggMux *ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad)); - GstOggPad *ogg_pad = (GstOggPad *) gst_pad_get_element_private (pad); - gboolean ret; - - GST_DEBUG ("Got %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event), - GST_DEBUG_PAD_NAME (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_NEWSEGMENT: - /* We don't support NEWSEGMENT events */ - gst_event_unref (event); - ret = FALSE; - break; - default: - ret = TRUE; - break; - } - - /* now GstCollectPads can take care of the rest, e.g. EOS */ - if (ret) - ret = ogg_pad->collect_event (pad, event); - - gst_object_unref (ogg_mux); - return ret; -} - -static GstPad * -gst_ogg_mux_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * req_name) -{ - GstOggMux *ogg_mux; - GstPad *newpad; - GstElementClass *klass; - - g_return_val_if_fail (templ != NULL, NULL); - - if (templ->direction != GST_PAD_SINK) - goto wrong_direction; - - g_return_val_if_fail (GST_IS_OGG_MUX (element), NULL); - ogg_mux = GST_OGG_MUX (element); - - klass = GST_ELEMENT_GET_CLASS (element); - - if (templ != gst_element_class_get_pad_template (klass, "sink_%d")) - goto wrong_template; - - { - gint serial; - gchar *name; - - if (req_name == NULL || strlen (req_name) < 6) { - /* no name given when requesting the pad, use random serial number */ - serial = rand (); - } else { - /* parse serial number from requested padname */ - serial = atoi (&req_name[5]); - } - /* create new pad with the name */ - GST_DEBUG_OBJECT (ogg_mux, "Creating new pad for serial %d", serial); - name = g_strdup_printf ("sink_%d", serial); - newpad = gst_pad_new_from_template (templ, name); - g_free (name); - - /* construct our own wrapper data structure for the pad to - * keep track of its status */ - { - GstOggPad *oggpad; - - oggpad = (GstOggPad *) - gst_collect_pads_add_pad_full (ogg_mux->collect, newpad, - sizeof (GstOggPad), gst_ogg_mux_ogg_pad_destroy_notify); - ogg_mux->active_pads++; - - oggpad->serial = serial; - ogg_stream_init (&oggpad->stream, serial); - oggpad->packetno = 0; - oggpad->pageno = 0; - oggpad->eos = FALSE; - /* we assume there will be some control data first for this pad */ - oggpad->state = GST_OGG_PAD_STATE_CONTROL; - oggpad->new_page = TRUE; - oggpad->first_delta = FALSE; - oggpad->prev_delta = FALSE; - oggpad->pagebuffers = g_queue_new (); - - oggpad->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad); - gst_pad_set_event_function (newpad, - GST_DEBUG_FUNCPTR (gst_ogg_mux_sink_event)); - } - } - - /* setup some pad functions */ - gst_pad_set_link_function (newpad, gst_ogg_mux_sinkconnect); - - /* dd the pad to the element */ - gst_element_add_pad (element, newpad); - - return newpad; - - /* ERRORS */ -wrong_direction: - { - g_warning ("ogg_mux: request pad that is not a SINK pad\n"); - return NULL; - } -wrong_template: - { - g_warning ("ogg_mux: this is not our template!\n"); - return NULL; - } -} - -static void -gst_ogg_mux_release_pad (GstElement * element, GstPad * pad) -{ - GstOggMux *ogg_mux; - - ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad)); - - gst_collect_pads_remove_pad (ogg_mux->collect, pad); - gst_element_remove_pad (element, pad); - - gst_object_unref (ogg_mux); -} - -/* handle events */ -static gboolean -gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event) -{ - GstEventType type; - - type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN; - - switch (type) { - case GST_EVENT_SEEK: - /* disable seeking for now */ - return FALSE; - default: - break; - } - - return gst_pad_event_default (pad, event); -} - -static GstBuffer * -gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page, gboolean delta) -{ - GstBuffer *buffer; - - /* allocate space for header and body */ - buffer = gst_buffer_new_and_alloc (page->header_len + page->body_len); - memcpy (GST_BUFFER_DATA (buffer), page->header, page->header_len); - memcpy (GST_BUFFER_DATA (buffer) + page->header_len, - page->body, page->body_len); - - /* Here we set granulepos as our OFFSET_END to give easy direct access to - * this value later. Before we push it, we reset this to OFFSET + SIZE - * (see gst_ogg_mux_push_buffer). */ - GST_BUFFER_OFFSET_END (buffer) = ogg_page_granulepos (page); - if (delta) - GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); - - GST_LOG_OBJECT (mux, GST_GP_FORMAT - " created buffer %p from ogg page", - GST_GP_CAST (ogg_page_granulepos (page)), buffer); - - return buffer; -} - -static GstFlowReturn -gst_ogg_mux_push_buffer (GstOggMux * mux, GstBuffer * buffer) -{ - GstCaps *caps; - - /* fix up OFFSET and OFFSET_END again */ - GST_BUFFER_OFFSET (buffer) = mux->offset; - mux->offset += GST_BUFFER_SIZE (buffer); - GST_BUFFER_OFFSET_END (buffer) = mux->offset; - - /* Ensure we have monotonically increasing timestamps in the output. */ - if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) { - if (mux->last_ts != GST_CLOCK_TIME_NONE && - GST_BUFFER_TIMESTAMP (buffer) < mux->last_ts) - GST_BUFFER_TIMESTAMP (buffer) = mux->last_ts; - else - mux->last_ts = GST_BUFFER_TIMESTAMP (buffer); - } - - caps = gst_pad_get_negotiated_caps (mux->srcpad); - gst_buffer_set_caps (buffer, caps); - if (caps) - gst_caps_unref (caps); - - return gst_pad_push (mux->srcpad, buffer); -} - -/* if all queues have at least one page, dequeue the page with the lowest - * timestamp */ -static gboolean -gst_ogg_mux_dequeue_page (GstOggMux * mux, GstFlowReturn * flowret) -{ - GSList *walk; - GstOggPad *opad = NULL; /* "oldest" pad */ - GstClockTime oldest = GST_CLOCK_TIME_NONE; - GstBuffer *buf = NULL; - gboolean ret = FALSE; - - *flowret = GST_FLOW_OK; - - walk = mux->collect->data; - while (walk) { - GstOggPad *pad = (GstOggPad *) walk->data; - - /* We need each queue to either be at EOS, or have one or more pages - * available with a set granulepos (i.e. not -1), otherwise we don't have - * enough data yet to determine which stream needs to go next for correct - * time ordering. */ - if (pad->pagebuffers->length == 0) { - if (pad->eos) { - GST_LOG_OBJECT (pad->collect.pad, - "pad is EOS, skipping for dequeue decision"); - } else { - GST_LOG_OBJECT (pad->collect.pad, - "no pages in this queue, can't dequeue"); - return FALSE; - } - } else { - /* We then need to check for a non-negative granulepos */ - int i; - gboolean valid = FALSE; - - for (i = 0; i < pad->pagebuffers->length; i++) { - buf = g_queue_peek_nth (pad->pagebuffers, i); - /* Here we check the OFFSET_END, which is actually temporarily the - * granulepos value for this buffer */ - if (GST_BUFFER_OFFSET_END (buf) != -1) { - valid = TRUE; - break; - } - } - if (!valid) { - GST_LOG_OBJECT (pad->collect.pad, - "No page timestamps in queue, can't dequeue"); - return FALSE; - } - } - - walk = g_slist_next (walk); - } - - walk = mux->collect->data; - while (walk) { - GstOggPad *pad = (GstOggPad *) walk->data; - - /* any page with a granulepos of -1 can be pushed immediately. - * TODO: it CAN be, but it seems silly to do so? */ - buf = g_queue_peek_head (pad->pagebuffers); - while (buf && GST_BUFFER_OFFSET_END (buf) == -1) { - GST_LOG_OBJECT (pad->collect.pad, "[gp -1] pushing page"); - g_queue_pop_head (pad->pagebuffers); - *flowret = gst_ogg_mux_push_buffer (mux, buf); - buf = g_queue_peek_head (pad->pagebuffers); - ret = TRUE; - } - - if (buf) { - /* if no oldest buffer yet, take this one */ - if (oldest == GST_CLOCK_TIME_NONE) { - GST_LOG_OBJECT (mux, "no oldest yet, taking buffer %p from pad %" - GST_PTR_FORMAT " with gp time %" GST_TIME_FORMAT, - buf, pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf))); - oldest = GST_BUFFER_OFFSET (buf); - opad = pad; - } else { - /* if we have an oldest, compare with this one */ - if (GST_BUFFER_OFFSET (buf) < oldest) { - GST_LOG_OBJECT (mux, "older buffer %p, taking from pad %" - GST_PTR_FORMAT " with gp time %" GST_TIME_FORMAT, - buf, pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf))); - oldest = GST_BUFFER_OFFSET (buf); - opad = pad; - } - } - } - walk = g_slist_next (walk); - } - - if (oldest != GST_CLOCK_TIME_NONE) { - g_assert (opad); - buf = g_queue_pop_head (opad->pagebuffers); - GST_LOG_OBJECT (opad->collect.pad, - GST_GP_FORMAT " pushing oldest page buffer %p (granulepos time %" - GST_TIME_FORMAT ")", GST_BUFFER_OFFSET_END (buf), buf, - GST_TIME_ARGS (GST_BUFFER_OFFSET (buf))); - *flowret = gst_ogg_mux_push_buffer (mux, buf); - ret = TRUE; - } - - return ret; -} - -/* put the given ogg page on a per-pad queue, timestamping it correctly. - * after that, dequeue and push as many pages as possible. - * Caller should make sure: - * pad->timestamp was set with the timestamp of the first packet put - * on the page - * pad->timestamp_end was set with the timestamp + duration of the last packet - * put on the page - * pad->gp_time was set with the time matching the gp of the last - * packet put on the page - * - * will also reset timestamp and timestamp_end, so caller func can restart - * counting. - */ -static GstFlowReturn -gst_ogg_mux_pad_queue_page (GstOggMux * mux, GstOggPad * pad, ogg_page * page, - gboolean delta) -{ - GstFlowReturn ret; - GstBuffer *buffer = gst_ogg_mux_buffer_from_page (mux, page, delta); - - /* take the timestamp of the first packet on this page */ - GST_BUFFER_TIMESTAMP (buffer) = pad->timestamp; - GST_BUFFER_DURATION (buffer) = pad->timestamp_end - pad->timestamp; - /* take the gp time of the last completed packet on this page */ - GST_BUFFER_OFFSET (buffer) = pad->gp_time; - - /* the next page will start where the current page's end time leaves off */ - pad->timestamp = pad->timestamp_end; - - g_queue_push_tail (pad->pagebuffers, buffer); - GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT - " queued buffer page %p (gp time %" - GST_TIME_FORMAT ", timestamp %" GST_TIME_FORMAT - "), %d page buffers queued", GST_GP_CAST (ogg_page_granulepos (page)), - buffer, GST_TIME_ARGS (GST_BUFFER_OFFSET (buffer)), - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), - g_queue_get_length (pad->pagebuffers)); - - while (gst_ogg_mux_dequeue_page (mux, &ret)) { - if (ret != GST_FLOW_OK) - break; - } - - return ret; -} - -/* - * Given two pads, compare the buffers queued on it. - * Returns: - * 0 if they have an equal priority - * -1 if the first is better - * 1 if the second is better - * Priority decided by: a) validity, b) older timestamp, c) smaller number - * of muxed pages - */ -static gint -gst_ogg_mux_compare_pads (GstOggMux * ogg_mux, GstOggPad * first, - GstOggPad * second) -{ - guint64 firsttime, secondtime; - - /* if the first pad doesn't contain anything or is even NULL, return - * the second pad as best candidate and vice versa */ - if (first == NULL || (first->buffer == NULL && first->next_buffer == NULL)) - return 1; - if (second == NULL || (second->buffer == NULL && second->next_buffer == NULL)) - return -1; - - /* no timestamp on first buffer, it must go first */ - if (first->buffer) - firsttime = GST_BUFFER_TIMESTAMP (first->buffer); - else - firsttime = GST_BUFFER_TIMESTAMP (first->next_buffer); - if (firsttime == GST_CLOCK_TIME_NONE) - return -1; - - /* no timestamp on second buffer, it must go first */ - if (second->buffer) - secondtime = GST_BUFFER_TIMESTAMP (second->buffer); - else - secondtime = GST_BUFFER_TIMESTAMP (second->next_buffer); - if (secondtime == GST_CLOCK_TIME_NONE) - return 1; - - /* first buffer has higher timestamp, second one should go first */ - if (secondtime < firsttime) - return 1; - /* second buffer has higher timestamp, first one should go first */ - else if (secondtime > firsttime) - return -1; - else { - /* buffers with equal timestamps, prefer the pad that has the - * least number of pages muxed */ - if (second->pageno < first->pageno) - return 1; - else if (second->pageno > first->pageno) - return -1; - } - - /* same priority if all of the above failed */ - return 0; -} - -/* make sure at least one buffer is queued on all pads, two if possible - * - * if pad->buffer == NULL, pad->next_buffer != NULL, then - * we do not know if the buffer is the last or not - * if pad->buffer != NULL, pad->next_buffer != NULL, then - * pad->buffer is not the last buffer for the pad - * if pad->buffer != NULL, pad->next_buffer == NULL, then - * pad->buffer if the last buffer for the pad - * - * returns a pointer to an oggpad that holds the best buffer, or - * NULL when no pad was usable. "best" means the buffer marked - * with the lowest timestamp. If best->buffer == NULL then nothing - * should be done until more data arrives */ -static GstOggPad * -gst_ogg_mux_queue_pads (GstOggMux * ogg_mux) -{ - GstOggPad *bestpad = NULL, *still_hungry = NULL; - GSList *walk; - - /* try to make sure we have a buffer from each usable pad first */ - walk = ogg_mux->collect->data; - while (walk) { - GstOggPad *pad; - GstCollectData *data; - - data = (GstCollectData *) walk->data; - pad = (GstOggPad *) data; - - walk = g_slist_next (walk); - - GST_LOG_OBJECT (data->pad, "looking at pad for buffer"); - - /* try to get a new buffer for this pad if needed and possible */ - if (pad->buffer == NULL) { - GstBuffer *buf; - gboolean incaps; - - /* shift the buffer along if needed (it's okay if next_buffer is NULL) */ - if (pad->buffer == NULL) { - GST_LOG_OBJECT (data->pad, "shifting buffer %" GST_PTR_FORMAT, - pad->next_buffer); - pad->buffer = pad->next_buffer; - pad->next_buffer = NULL; - } - - buf = gst_collect_pads_pop (ogg_mux->collect, data); - GST_LOG_OBJECT (data->pad, "popped buffer %" GST_PTR_FORMAT, buf); - - /* On EOS we get a NULL buffer */ - if (buf != NULL) { - if (ogg_mux->delta_pad == NULL && - GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) - ogg_mux->delta_pad = pad; - - incaps = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_IN_CAPS); - /* if we need headers */ - if (pad->state == GST_OGG_PAD_STATE_CONTROL) { - /* and we have one */ - if (incaps) { - GST_DEBUG_OBJECT (ogg_mux, - "got incaps buffer in control state, ignoring"); - /* just ignore */ - gst_buffer_unref (buf); - buf = NULL; - } else { - GST_DEBUG_OBJECT (ogg_mux, - "got data buffer in control state, switching " "to data mode"); - /* this is a data buffer so switch to data state */ - pad->state = GST_OGG_PAD_STATE_DATA; - } - } - } else { - GST_DEBUG_OBJECT (data->pad, "EOS on pad"); - if (!pad->eos) { - ogg_page page; - GstFlowReturn ret; - - /* it's no longer active */ - ogg_mux->active_pads--; - - /* Just gone to EOS. Flush existing page(s) */ - pad->eos = TRUE; - - while (ogg_stream_flush (&pad->stream, &page)) { - /* Place page into the per-pad queue */ - ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page, - pad->first_delta); - /* increment the page number counter */ - pad->pageno++; - /* mark other pages as delta */ - pad->first_delta = TRUE; - } - } - } - - pad->next_buffer = buf; - } - - /* we should have a buffer now, see if it is the best pad to - * pull on */ - if (pad->buffer || pad->next_buffer) { - if (gst_ogg_mux_compare_pads (ogg_mux, bestpad, pad) > 0) { - GST_LOG_OBJECT (data->pad, - "new best pad, with buffers %" GST_PTR_FORMAT - " and %" GST_PTR_FORMAT, pad->buffer, pad->next_buffer); - - bestpad = pad; - } - } else if (!pad->eos) { - GST_LOG_OBJECT (data->pad, "hungry pad"); - still_hungry = pad; - } - } - - if (still_hungry) - /* drop back into collectpads... */ - return still_hungry; - else - return bestpad; -} - -static GList * -gst_ogg_mux_get_headers (GstOggPad * pad) -{ - GList *res = NULL; - GstStructure *structure; - GstCaps *caps; - GstPad *thepad; - - thepad = pad->collect.pad; - - GST_LOG_OBJECT (thepad, "getting headers"); - - caps = gst_pad_get_negotiated_caps (thepad); - if (caps != NULL) { - const GValue *streamheader; - - structure = gst_caps_get_structure (caps, 0); - streamheader = gst_structure_get_value (structure, "streamheader"); - if (streamheader != NULL) { - GST_LOG_OBJECT (thepad, "got header"); - if (G_VALUE_TYPE (streamheader) == GST_TYPE_ARRAY) { - GArray *bufarr = g_value_peek_pointer (streamheader); - gint i; - - GST_LOG_OBJECT (thepad, "got fixed list"); - - for (i = 0; i < bufarr->len; i++) { - GValue *bufval = &g_array_index (bufarr, GValue, i); - - GST_LOG_OBJECT (thepad, "item %d", i); - if (G_VALUE_TYPE (bufval) == GST_TYPE_BUFFER) { - GstBuffer *buf = g_value_peek_pointer (bufval); - - GST_LOG_OBJECT (thepad, "adding item %d to header list", i); - - gst_buffer_ref (buf); - res = g_list_append (res, buf); - } - } - } else { - GST_LOG_OBJECT (thepad, "streamheader is not fixed list"); - } - } else if (gst_structure_has_name (structure, "video/x-dirac")) { - res = g_list_append (res, pad->buffer); - pad->buffer = pad->next_buffer; - pad->next_buffer = NULL; - pad->always_flush_page = TRUE; - } else { - GST_LOG_OBJECT (thepad, "caps don't have streamheader"); - } - gst_caps_unref (caps); - } else { - GST_LOG_OBJECT (thepad, "got empty caps as negotiated format"); - } - return res; -} - -static GstCaps * -gst_ogg_mux_set_header_on_caps (GstCaps * caps, GList * buffers) -{ - GstStructure *structure; - GValue array = { 0 }; - GList *walk = buffers; - - caps = gst_caps_make_writable (caps); - - structure = gst_caps_get_structure (caps, 0); - - /* put buffers in a fixed list */ - g_value_init (&array, GST_TYPE_ARRAY); - - while (walk) { - GstBuffer *buf = GST_BUFFER (walk->data); - GstBuffer *copy; - GValue value = { 0 }; - - walk = walk->next; - - /* mark buffer */ - GST_LOG ("Setting IN_CAPS on buffer of length %d", GST_BUFFER_SIZE (buf)); - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); - - g_value_init (&value, GST_TYPE_BUFFER); - copy = gst_buffer_copy (buf); - gst_value_set_buffer (&value, copy); - gst_buffer_unref (copy); - gst_value_array_append_value (&array, &value); - g_value_unset (&value); - } - gst_structure_set_value (structure, "streamheader", &array); - g_value_unset (&array); - - return caps; -} - -/* - * For each pad we need to write out one (small) header in one - * page that allows decoders to identify the type of the stream. - * After that we need to write out all extra info for the decoders. - * In the case of a codec that also needs data as configuration, we can - * find that info in the streamcaps. - * After writing the headers we must start a new page for the data. - */ -static GstFlowReturn -gst_ogg_mux_send_headers (GstOggMux * mux) -{ - GSList *walk; - GList *hbufs, *hwalk; - GstCaps *caps; - GstFlowReturn ret; - - hbufs = NULL; - ret = GST_FLOW_OK; - - GST_LOG_OBJECT (mux, "collecting headers"); - - walk = mux->collect->data; - while (walk) { - GstOggPad *pad; - GstPad *thepad; - - pad = (GstOggPad *) walk->data; - thepad = pad->collect.pad; - - walk = g_slist_next (walk); - - GST_LOG_OBJECT (mux, "looking at pad %s:%s", GST_DEBUG_PAD_NAME (thepad)); - - /* if the pad has no buffer, we don't care */ - if (pad->buffer == NULL && pad->next_buffer == NULL) - continue; - - /* now figure out the headers */ - pad->headers = gst_ogg_mux_get_headers (pad); - } - - GST_LOG_OBJECT (mux, "creating BOS pages"); - walk = mux->collect->data; - while (walk) { - GstOggPad *pad; - GstBuffer *buf; - ogg_packet packet; - ogg_page page; - GstPad *thepad; - GstCaps *caps; - GstStructure *structure; - GstBuffer *hbuf; - - pad = (GstOggPad *) walk->data; - thepad = pad->collect.pad; - caps = gst_pad_get_negotiated_caps (thepad); - structure = gst_caps_get_structure (caps, 0); - - walk = walk->next; - - pad->packetno = 0; - - GST_LOG_OBJECT (thepad, "looping over headers"); - - if (pad->headers) { - buf = GST_BUFFER (pad->headers->data); - pad->headers = g_list_remove (pad->headers, buf); - } else if (pad->buffer) { - buf = pad->buffer; - gst_buffer_ref (buf); - } else if (pad->next_buffer) { - buf = pad->next_buffer; - gst_buffer_ref (buf); - } else { - /* fixme -- should be caught in the previous list traversal. */ - GST_OBJECT_LOCK (pad); - g_critical ("No headers or buffers on pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - GST_OBJECT_UNLOCK (pad); - continue; - } - - /* create a packet from the buffer */ - packet.packet = GST_BUFFER_DATA (buf); - packet.bytes = GST_BUFFER_SIZE (buf); - packet.granulepos = GST_BUFFER_OFFSET_END (buf); - if (packet.granulepos == -1) - packet.granulepos = 0; - /* mark BOS and packet number */ - packet.b_o_s = (pad->packetno == 0); - packet.packetno = pad->packetno++; - /* mark EOS */ - packet.e_o_s = 0; - - /* swap the packet in */ - ogg_stream_packetin (&pad->stream, &packet); - gst_buffer_unref (buf); - - GST_LOG_OBJECT (thepad, "flushing out BOS page"); - if (!ogg_stream_flush (&pad->stream, &page)) - g_critical ("Could not flush BOS page"); - - hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE); - - GST_LOG_OBJECT (mux, "swapped out page with mime type %s", - gst_structure_get_name (structure)); - - /* quick hack: put Theora and Dirac video pages at the front. - * Ideally, we would have a settable enum for which Ogg - * profile we work with, and order based on that. - * (FIXME: if there is more than one video stream, shouldn't we only put - * one's BOS into the first page, followed by an audio stream's BOS, and - * only then followed by the remaining video and audio streams?) */ - if (gst_structure_has_name (structure, "video/x-theora")) { - GST_DEBUG_OBJECT (thepad, "putting %s page at the front", "Theora"); - hbufs = g_list_prepend (hbufs, hbuf); - pad->always_flush_page = TRUE; - } else if (gst_structure_has_name (structure, "video/x-dirac")) { - GST_DEBUG_OBJECT (thepad, "putting %s page at the front", "Dirac"); - hbufs = g_list_prepend (hbufs, hbuf); - pad->always_flush_page = TRUE; - } else { - hbufs = g_list_append (hbufs, hbuf); - } - gst_caps_unref (caps); - } - - GST_LOG_OBJECT (mux, "creating next headers"); - walk = mux->collect->data; - while (walk) { - GstOggPad *pad; - GstPad *thepad; - - pad = (GstOggPad *) walk->data; - thepad = pad->collect.pad; - - walk = walk->next; - - GST_LOG_OBJECT (mux, "looping over headers for pad %s:%s", - GST_DEBUG_PAD_NAME (thepad)); - - hwalk = pad->headers; - while (hwalk) { - GstBuffer *buf = GST_BUFFER (hwalk->data); - ogg_packet packet; - ogg_page page; - - hwalk = hwalk->next; - - /* create a packet from the buffer */ - packet.packet = GST_BUFFER_DATA (buf); - packet.bytes = GST_BUFFER_SIZE (buf); - packet.granulepos = GST_BUFFER_OFFSET_END (buf); - if (packet.granulepos == -1) - packet.granulepos = 0; - /* mark BOS and packet number */ - packet.b_o_s = (pad->packetno == 0); - packet.packetno = pad->packetno++; - /* mark EOS */ - packet.e_o_s = 0; - - /* swap the packet in */ - ogg_stream_packetin (&pad->stream, &packet); - gst_buffer_unref (buf); - - /* if last header, flush page */ - if (hwalk == NULL) { - GST_LOG_OBJECT (mux, - "flushing page as packet %" G_GUINT64_FORMAT " is first or " - "last packet", pad->packetno); - while (ogg_stream_flush (&pad->stream, &page)) { - GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE); - - GST_LOG_OBJECT (mux, "swapped out page"); - hbufs = g_list_append (hbufs, hbuf); - } - } else { - GST_LOG_OBJECT (mux, "try to swap out page"); - /* just try to swap out a page then */ - while (ogg_stream_pageout (&pad->stream, &page) > 0) { - GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE); - - GST_LOG_OBJECT (mux, "swapped out page"); - hbufs = g_list_append (hbufs, hbuf); - } - } - } - g_list_free (pad->headers); - pad->headers = NULL; - } - /* hbufs holds all buffers for the headers now */ - - /* create caps with the buffers */ - caps = gst_pad_get_caps (mux->srcpad); - if (caps) { - caps = gst_ogg_mux_set_header_on_caps (caps, hbufs); - gst_pad_set_caps (mux->srcpad, caps); - gst_caps_unref (caps); - } - /* and send the buffers */ - while (hbufs != NULL) { - GstBuffer *buf = GST_BUFFER (hbufs->data); - - hbufs = g_list_delete_link (hbufs, hbufs); - - if ((ret = gst_ogg_mux_push_buffer (mux, buf)) != GST_FLOW_OK) - break; - } - /* free any remaining nodes/buffers in case we couldn't push them */ - g_list_foreach (hbufs, (GFunc) gst_mini_object_unref, NULL); - g_list_free (hbufs); - - return ret; -} - -/* this function is called to process data on the best pending pad. - * - * basic idea: - * - * 1) store the selected pad and keep on pulling until we fill a - * complete ogg page or the ogg page is filled above the max-delay - * threshold. This is needed because the ogg spec says that - * you should fill a complete page with data from the same logical - * stream. When the page is filled, go back to 1). - * 2) before filling a page, read ahead one more buffer to see if this - * packet is the last of the stream. We need to do this because the ogg - * spec mandates that the last packet should have the EOS flag set before - * sending it to ogg. if pad->buffer is NULL we need to wait to find out - * whether there are any more buffers. - * 3) pages get queued on a per-pad queue. Every time a page is queued, a - * dequeue is called, which will dequeue the oldest page on any pad, provided - * that ALL pads have at least one marked page in the queue (or remaining - * pads are at EOS) - */ -static GstFlowReturn -gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPad * best) -{ - GstFlowReturn ret = GST_FLOW_OK; - gboolean delta_unit; - gint64 granulepos = 0; - GstClockTime timestamp, gp_time; - - GST_LOG_OBJECT (ogg_mux, "best pad %" GST_PTR_FORMAT - ", currently pulling from %" GST_PTR_FORMAT, best->collect.pad, - ogg_mux->pulling); - - /* best->buffer is non-NULL, either the pad is EOS's or there is a next - * buffer */ - if (best->next_buffer == NULL && !best->eos) { - GST_WARNING_OBJECT (ogg_mux, "no subsequent buffer and EOS not reached"); - return GST_FLOW_WRONG_STATE; - } - - /* if we were already pulling from one pad, but the new "best" buffer is - * from another pad, we need to check if we have reason to flush a page - * for the pad we were pulling from before */ - if (ogg_mux->pulling && best && - ogg_mux->pulling != best && ogg_mux->pulling->buffer) { - GstOggPad *pad = ogg_mux->pulling; - - GstClockTime last_ts = GST_BUFFER_END_TIME (pad->buffer); - - /* if the next packet in the current page is going to make the page - * too long, we need to flush */ - if (last_ts > ogg_mux->next_ts + ogg_mux->max_delay) { - ogg_page page; - - GST_LOG_OBJECT (pad->collect.pad, - GST_GP_FORMAT " stored packet %" G_GINT64_FORMAT - " will make page too long, flushing", - GST_BUFFER_OFFSET_END (pad->buffer), (gint64) pad->stream.packetno); - - while (ogg_stream_flush (&pad->stream, &page)) { - /* end time of this page is the timestamp of the next buffer */ - ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer); - /* Place page into the per-pad queue */ - ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page, - pad->first_delta); - /* increment the page number counter */ - pad->pageno++; - /* mark other pages as delta */ - pad->first_delta = TRUE; - } - pad->new_page = TRUE; - ogg_mux->pulling = NULL; - } - } - - /* if we don't know which pad to pull on, use the best one */ - if (ogg_mux->pulling == NULL) { - ogg_mux->pulling = best; - GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "pulling from best pad"); - - /* remember timestamp and gp time of first buffer for this new pad */ - if (ogg_mux->pulling != NULL) { - ogg_mux->next_ts = GST_BUFFER_TIMESTAMP (ogg_mux->pulling->buffer); - GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "updated times, next ts %" - GST_TIME_FORMAT, GST_TIME_ARGS (ogg_mux->next_ts)); - } else { - /* no pad to pull on, send EOS */ - gst_pad_push_event (ogg_mux->srcpad, gst_event_new_eos ()); - return GST_FLOW_WRONG_STATE; - } - } - - if (ogg_mux->need_headers) { - ret = gst_ogg_mux_send_headers (ogg_mux); - ogg_mux->need_headers = FALSE; - } - - /* we are pulling from a pad, continue to do so until a page - * has been filled and queued */ - if (ogg_mux->pulling != NULL) { - ogg_packet packet; - ogg_page page; - GstBuffer *buf, *tmpbuf; - GstOggPad *pad = ogg_mux->pulling; - gint64 duration; - gboolean force_flush; - - GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "pulling from pad"); - - /* now see if we have a buffer */ - buf = pad->buffer; - if (buf == NULL) { - GST_DEBUG_OBJECT (ogg_mux, "pad was EOS"); - ogg_mux->pulling = NULL; - return GST_FLOW_OK; - } - - delta_unit = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); - duration = GST_BUFFER_DURATION (buf); - - /* if the current "next timestamp" on the pad is unset, then this is the - * first packet on the new page. Update our pad's page timestamp */ - if (ogg_mux->pulling->timestamp == GST_CLOCK_TIME_NONE) { - ogg_mux->pulling->timestamp = GST_BUFFER_TIMESTAMP (buf); - GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, - "updated pad timestamp to %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); - } - /* create a packet from the buffer */ - packet.packet = GST_BUFFER_DATA (buf); - packet.bytes = GST_BUFFER_SIZE (buf); - packet.granulepos = GST_BUFFER_OFFSET_END (buf); - if (packet.granulepos == -1) - packet.granulepos = 0; - /* mark BOS and packet number */ - packet.b_o_s = (pad->packetno == 0); - packet.packetno = pad->packetno++; - GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT - " packet %" G_GINT64_FORMAT " (%ld bytes) created from buffer", - GST_GP_CAST (packet.granulepos), (gint64) packet.packetno, - packet.bytes); - - packet.e_o_s = (pad->eos ? 1 : 0); - tmpbuf = NULL; - - /* we flush when we see a new keyframe */ - force_flush = (pad->prev_delta && !delta_unit) || pad->always_flush_page; - if (duration != -1) { - pad->duration += duration; - /* if page duration exceeds max, flush page */ - if (pad->duration > ogg_mux->max_page_delay) { - force_flush = TRUE; - pad->duration = 0; - } - } - - if (GST_BUFFER_IS_DISCONT (buf)) { - GST_LOG_OBJECT (pad->collect.pad, "got discont"); - packet.packetno++; - /* No public API for this; hack things in */ - pad->stream.pageno++; - force_flush = TRUE; - } - - /* flush the currently built page if necessary */ - if (force_flush) { - GST_LOG_OBJECT (pad->collect.pad, - GST_GP_FORMAT " forced flush of page before this packet", - GST_BUFFER_OFFSET_END (pad->buffer)); - while (ogg_stream_flush (&pad->stream, &page)) { - /* end time of this page is the timestamp of the next buffer */ - ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer); - ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page, - pad->first_delta); - - /* increment the page number counter */ - pad->pageno++; - /* mark other pages as delta */ - pad->first_delta = TRUE; - } - pad->new_page = TRUE; - } - - /* if this is the first packet of a new page figure out the delta flag */ - if (pad->new_page) { - if (delta_unit) { - /* mark the page as delta */ - pad->first_delta = TRUE; - } else { - /* got a keyframe */ - if (ogg_mux->delta_pad == pad) { - /* if we get it on the pad with deltaunits, - * we mark the page as non delta */ - pad->first_delta = FALSE; - } else if (ogg_mux->delta_pad != NULL) { - /* if there are pads with delta frames, we - * must mark this one as delta */ - pad->first_delta = TRUE; - } else { - pad->first_delta = FALSE; - } - } - pad->new_page = FALSE; - } - - /* save key unit to track delta->key unit transitions */ - pad->prev_delta = delta_unit; - - /* swap the packet in */ - if (packet.e_o_s == 1) - GST_DEBUG_OBJECT (pad->collect.pad, "swapping in EOS packet"); - if (packet.b_o_s == 1) - GST_DEBUG_OBJECT (pad->collect.pad, "swapping in BOS packet"); - - ogg_stream_packetin (&pad->stream, &packet); - - gp_time = GST_BUFFER_OFFSET (pad->buffer); - granulepos = GST_BUFFER_OFFSET_END (pad->buffer); - timestamp = GST_BUFFER_TIMESTAMP (pad->buffer); - - GST_LOG_OBJECT (pad->collect.pad, - GST_GP_FORMAT " packet %" G_GINT64_FORMAT ", gp time %" - GST_TIME_FORMAT ", timestamp %" GST_TIME_FORMAT " packetin'd", - granulepos, (gint64) packet.packetno, GST_TIME_ARGS (gp_time), - GST_TIME_ARGS (timestamp)); - /* don't need the old buffer anymore */ - gst_buffer_unref (pad->buffer); - /* store new readahead buffer */ - pad->buffer = tmpbuf; - - /* let ogg write out the pages now. The packet we got could end - * up in more than one page so we need to write them all */ - if (ogg_stream_pageout (&pad->stream, &page) > 0) { - /* we have a new page, so we need to timestamp it correctly. - * if this fresh packet ends on this page, then the page's granulepos - * comes from that packet, and we should set this buffer's timestamp */ - - GST_LOG_OBJECT (pad->collect.pad, - GST_GP_FORMAT " packet %" G_GINT64_FORMAT ", time %" - GST_TIME_FORMAT ") caused new page", - granulepos, (gint64) packet.packetno, GST_TIME_ARGS (timestamp)); - GST_LOG_OBJECT (pad->collect.pad, - GST_GP_FORMAT " new page %ld", - GST_GP_CAST (ogg_page_granulepos (&page)), pad->stream.pageno); - - if (ogg_page_granulepos (&page) == granulepos) { - /* the packet we streamed in finishes on the current page, - * because the page's granulepos is the granulepos of the last - * packet completed on that page, - * so update the timestamp that we will give to the page */ - GST_LOG_OBJECT (pad->collect.pad, - GST_GP_FORMAT - " packet finishes on current page, updating gp time to %" - GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (gp_time)); - pad->gp_time = gp_time; - } else { - GST_LOG_OBJECT (pad->collect.pad, - GST_GP_FORMAT - " packet spans beyond current page, keeping old gp time %" - GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (pad->gp_time)); - } - - /* push the page */ - /* end time of this page is the timestamp of the next buffer */ - pad->timestamp_end = timestamp; - ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page, pad->first_delta); - pad->pageno++; - /* mark next pages as delta */ - pad->first_delta = TRUE; - - /* use an inner loop here to flush the remaining pages and - * mark them as delta frames as well */ - while (ogg_stream_pageout (&pad->stream, &page) > 0) { - if (ogg_page_granulepos (&page) == granulepos) { - /* the page has taken up the new packet completely, which means - * the packet ends the page and we can update the gp time - * before pushing out */ - pad->gp_time = gp_time; - } - - /* we have a complete page now, we can push the page - * and make sure to pull on a new pad the next time around */ - ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page, - pad->first_delta); - /* increment the page number counter */ - pad->pageno++; - } - /* need a new page as well */ - pad->new_page = TRUE; - pad->duration = 0; - /* we're done pulling on this pad, make sure to choose a new - * pad for pulling in the next iteration */ - ogg_mux->pulling = NULL; - } - - /* Update the gp time, if necessary, since any future page will have at - * least this gp time. - */ - if (pad->gp_time < gp_time) { - pad->gp_time = gp_time; - GST_LOG_OBJECT (pad->collect.pad, - "Updated running gp time of pad %" GST_PTR_FORMAT - " to %" GST_TIME_FORMAT, pad->collect.pad, GST_TIME_ARGS (gp_time)); - } - } - - return ret; -} - -/* all_pads_eos: - * - * Checks if all pads are EOS'd by peeking. - * - * Returns TRUE if all pads are EOS. - */ -static gboolean -all_pads_eos (GstCollectPads * pads) -{ - GSList *walk; - gboolean alleos = TRUE; - - walk = pads->data; - while (walk) { - GstBuffer *buf; - GstCollectData *data = (GstCollectData *) walk->data; - - buf = gst_collect_pads_peek (pads, data); - if (buf) { - alleos = FALSE; - gst_buffer_unref (buf); - goto beach; - } - walk = walk->next; - } -beach: - return alleos; -} - -/* This function is called when there is data on all pads. - * - * It finds a pad to pull on, this is done by looking at the buffers - * to decide which one to use, and using the 'oldest' one first. It then calls - * gst_ogg_mux_process_best_pad() to process as much data as possible. - * - * If all the pads have received EOS, it flushes out all data by continually - * getting the best pad and calling gst_ogg_mux_process_best_pad() until they - * are all empty, and then sends EOS. - */ -static GstFlowReturn -gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux) -{ - GstOggPad *best; - GstFlowReturn ret; - gint activebefore; - - GST_LOG_OBJECT (ogg_mux, "collected"); - - activebefore = ogg_mux->active_pads; - - /* queue buffers on all pads; find a buffer with the lowest timestamp */ - best = gst_ogg_mux_queue_pads (ogg_mux); - if (best && !best->buffer) { - GST_DEBUG_OBJECT (ogg_mux, "No buffer available on best pad"); - return GST_FLOW_OK; - } - - if (!best) { - return GST_FLOW_WRONG_STATE; - } - - ret = gst_ogg_mux_process_best_pad (ogg_mux, best); - - if (ogg_mux->active_pads < activebefore) { - /* If the active pad count went down, this mean at least one pad has gone - * EOS. Since CollectPads only calls _collected() once when all pads are - * EOS, and our code doesn't _pop() from all pads we need to check that by - * peeking on all pads, else we won't be called again and the muxing will - * not terminate (push out EOS). */ - - /* if all the pads have been removed, flush all pending data */ - if ((ret == GST_FLOW_OK) && all_pads_eos (pads)) { - GST_LOG_OBJECT (ogg_mux, "no pads remaining, flushing data"); - - do { - best = gst_ogg_mux_queue_pads (ogg_mux); - if (best) - ret = gst_ogg_mux_process_best_pad (ogg_mux, best); - } while ((ret == GST_FLOW_OK) && (best != NULL)); - - GST_DEBUG_OBJECT (ogg_mux, "Pushing EOS"); - gst_pad_push_event (ogg_mux->srcpad, gst_event_new_eos ()); - } - } - - return ret; -} - -static void -gst_ogg_mux_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec) -{ - GstOggMux *ogg_mux; - - ogg_mux = GST_OGG_MUX (object); - - switch (prop_id) { - case ARG_MAX_DELAY: - g_value_set_uint64 (value, ogg_mux->max_delay); - break; - case ARG_MAX_PAGE_DELAY: - g_value_set_uint64 (value, ogg_mux->max_page_delay); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_ogg_mux_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec) -{ - GstOggMux *ogg_mux; - - ogg_mux = GST_OGG_MUX (object); - - switch (prop_id) { - case ARG_MAX_DELAY: - ogg_mux->max_delay = g_value_get_uint64 (value); - break; - case ARG_MAX_PAGE_DELAY: - ogg_mux->max_page_delay = g_value_get_uint64 (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/* reset all variables in the ogg pads. */ -static void -gst_ogg_mux_init_collectpads (GstCollectPads * collect) -{ - GSList *walk; - - walk = collect->data; - while (walk) { - GstOggPad *oggpad = (GstOggPad *) walk->data; - - ogg_stream_init (&oggpad->stream, oggpad->serial); - oggpad->packetno = 0; - oggpad->pageno = 0; - oggpad->eos = FALSE; - /* we assume there will be some control data first for this pad */ - oggpad->state = GST_OGG_PAD_STATE_CONTROL; - oggpad->new_page = TRUE; - oggpad->first_delta = FALSE; - oggpad->prev_delta = FALSE; - oggpad->pagebuffers = g_queue_new (); - - walk = g_slist_next (walk); - } -} - -/* Clear all buffers from the collectpads object */ -static void -gst_ogg_mux_clear_collectpads (GstCollectPads * collect) -{ - GSList *walk; - - for (walk = collect->data; walk; walk = g_slist_next (walk)) { - GstOggPad *oggpad = (GstOggPad *) walk->data; - GstBuffer *buf; - - ogg_stream_clear (&oggpad->stream); - - while ((buf = g_queue_pop_head (oggpad->pagebuffers)) != NULL) { - gst_buffer_unref (buf); - } - g_queue_free (oggpad->pagebuffers); - oggpad->pagebuffers = NULL; - - if (oggpad->buffer) { - gst_buffer_unref (oggpad->buffer); - oggpad->buffer = NULL; - } - if (oggpad->next_buffer) { - gst_buffer_unref (oggpad->next_buffer); - oggpad->next_buffer = NULL; - } - } -} - -static GstStateChangeReturn -gst_ogg_mux_change_state (GstElement * element, GstStateChange transition) -{ - GstOggMux *ogg_mux; - GstStateChangeReturn ret; - - ogg_mux = GST_OGG_MUX (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - gst_ogg_mux_clear (ogg_mux); - gst_ogg_mux_init_collectpads (ogg_mux->collect); - gst_collect_pads_start (ogg_mux->collect); - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_collect_pads_stop (ogg_mux->collect); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_ogg_mux_clear_collectpads (ogg_mux->collect); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - - return ret; -} - -gboolean -gst_ogg_mux_plugin_init (GstPlugin * plugin) -{ - GST_DEBUG_CATEGORY_INIT (gst_ogg_mux_debug, "oggmux", 0, "ogg muxer"); - - return gst_element_register (plugin, "oggmux", GST_RANK_PRIMARY, - GST_TYPE_OGG_MUX); -} diff --git a/ext/ogg/gstoggmux.h b/ext/ogg/gstoggmux.h deleted file mode 100644 index fc0944f2..00000000 --- a/ext/ogg/gstoggmux.h +++ /dev/null @@ -1,138 +0,0 @@ -/* OGG muxer plugin for GStreamer - * Copyright (C) 2004 Wim Taymans <wim@fluendo.com> - * Copyright (C) 2006 Thomas Vander Stichele <thomas at apestaart dot org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_OGG_MUX_H__ -#define __GST_OGGEMUX_H__ - -#include <ogg/ogg.h> - -#include <gst/gst.h> - -G_BEGIN_DECLS - -#define GST_TYPE_OGG_MUX (gst_ogg_mux_get_type()) -#define GST_OGG_MUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGG_MUX, GstOggMux)) -#define GST_OGG_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGG_MUX, GstOggMux)) -#define GST_IS_OGG_MUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_MUX)) -#define GST_IS_OGG_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_MUX)) - -typedef struct _GstOggMux GstOggMux; -typedef struct _GstOggMuxClass GstOggMuxClass; - -typedef enum -{ - GST_OGG_PAD_STATE_CONTROL = 0, - GST_OGG_PAD_STATE_DATA = 1 -} -GstOggPadState; - -/* all information needed for one ogg stream */ -typedef struct -{ - GstCollectData collect; /* we extend the CollectData */ - - /* These two buffers make a very simple queue - they enter as 'next_buffer' - * and (usually) leave as 'buffer', except at EOS, when buffer will be NULL */ - GstBuffer *buffer; /* the first waiting buffer for the pad */ - GstBuffer *next_buffer; /* the second waiting buffer for the pad */ - - gint serial; - ogg_stream_state stream; - gint64 packetno; /* number of next packet */ - gint64 pageno; /* number of next page */ - guint64 duration; /* duration of current page */ - gboolean eos; - gint64 offset; - GstClockTime timestamp; /* timestamp of the first packet on the next - * page to be dequeued */ - GstClockTime timestamp_end; /* end timestamp of last complete packet on - the next page to be dequeued */ - GstClockTime gp_time; /* time corresponding to the gp value of the - last complete packet on the next page to be - dequeued */ - - GstOggPadState state; /* state of the pad */ - - GList *headers; - - GQueue *pagebuffers; /* List of pages in buffers ready for pushing */ - - gboolean new_page; /* starting a new page */ - gboolean first_delta; /* was the first packet in the page a delta */ - gboolean prev_delta; /* was the previous buffer a delta frame */ - - GstPadEventFunction collect_event; - - gboolean always_flush_page; -} -GstOggPad; - -/** - * GstOggMux: - * - * The ogg muxer object structure. - */ -struct _GstOggMux -{ - GstElement element; - - /* source pad */ - GstPad *srcpad; - - /* sinkpads */ - GstCollectPads *collect; - - /* number of pads which have not received EOS */ - gint active_pads; - - /* the pad we are currently using to fill a page */ - GstOggPad *pulling; - - /* next timestamp for the page */ - GstClockTime next_ts; - - /* Last timestamp actually output on src pad */ - GstClockTime last_ts; - - /* offset in stream */ - guint64 offset; - - /* need_headers */ - gboolean need_headers; - - guint64 max_delay; - guint64 max_page_delay; - - GstOggPad *delta_pad; /* when a delta frame is detected on a stream, we mark - pages as delta frames up to the page that has the - keyframe */ - -}; - -struct _GstOggMuxClass -{ - GstElementClass parent_class; -}; - -GType gst_ogg_mux_get_type (void); - -G_END_DECLS - -#endif /* __GST_OGG_MUX_H__ */ diff --git a/ext/ogg/gstoggparse.c b/ext/ogg/gstoggparse.c deleted file mode 100644 index a86958b5..00000000 --- a/ext/ogg/gstoggparse.c +++ /dev/null @@ -1,698 +0,0 @@ -/* GStreamer - * Copyright (C) 2005 Michael Smith <msmith@fluendo.com> - * - * gstoggparse.c: ogg stream parser - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -/* This ogg parser is essentially a subset of the ogg demuxer - rather than - * fully demuxing into packets, we only parse out the pages, create one - * GstBuffer per page, set all the appropriate flags on those pages, set caps - * appropriately (particularly the 'streamheader' which gives all the header - * pages required for initialing decode). - * - * It's dramatically simpler than the full demuxer as it does not support - * seeking. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include <gst/gst.h> -#include <ogg/ogg.h> -#include <string.h> - -#include "gstoggstream.h" - -static const GstElementDetails gst_ogg_parse_details = -GST_ELEMENT_DETAILS ("Ogg parser", - "Codec/Parser", - "parse ogg streams into pages (info about ogg: http://xiph.org)", - "Michael Smith <msmith@fluendo.com>"); - -GST_DEBUG_CATEGORY_STATIC (gst_ogg_parse_debug); -#define GST_CAT_DEFAULT gst_ogg_parse_debug - -#define GST_TYPE_OGG_PARSE (gst_ogg_parse_get_type()) -#define GST_OGG_PARSE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGG_PARSE, GstOggParse)) -#define GST_OGG_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGG_PARSE, GstOggParse)) -#define GST_IS_OGG_PARSE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_PARSE)) -#define GST_IS_OGG_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_PARSE)) - -static GType gst_ogg_parse_get_type (void); - -typedef struct _GstOggParse GstOggParse; -typedef struct _GstOggParseClass GstOggParseClass; - -struct _GstOggParse -{ - GstElement element; - - GstPad *sinkpad; /* Sink pad we're reading data from */ - - GstPad *srcpad; /* Source pad we're writing to */ - - GSList *oggstreams; /* list of GstOggStreams for known streams */ - - gint64 offset; /* Current stream offset */ - - gboolean in_headers; /* Set if we're reading headers for streams */ - - gboolean last_page_not_bos; /* Set if we've seen a non-BOS page */ - - ogg_sync_state sync; /* Ogg page synchronisation */ - - GstCaps *caps; /* Our src caps */ -}; - -struct _GstOggParseClass -{ - GstElementClass parent_class; -}; - -static void gst_ogg_parse_base_init (gpointer g_class); -static void gst_ogg_parse_class_init (GstOggParseClass * klass); -static void gst_ogg_parse_init (GstOggParse * ogg); -static GstElementClass *parent_class = NULL; - -static GType -gst_ogg_parse_get_type (void) -{ - static GType ogg_parse_type = 0; - - if (!ogg_parse_type) { - static const GTypeInfo ogg_parse_info = { - sizeof (GstOggParseClass), - gst_ogg_parse_base_init, - NULL, - (GClassInitFunc) gst_ogg_parse_class_init, - NULL, - NULL, - sizeof (GstOggParse), - 0, - (GInstanceInitFunc) gst_ogg_parse_init, - }; - - ogg_parse_type = g_type_register_static (GST_TYPE_ELEMENT, "GstOggParse", - &ogg_parse_info, 0); - } - return ogg_parse_type; -} - -static void -free_stream (GstOggStream * stream) -{ - g_list_foreach (stream->headers, (GFunc) gst_mini_object_unref, NULL); - g_list_foreach (stream->unknown_pages, (GFunc) gst_mini_object_unref, NULL); - - g_free (stream); -} - -static void -gst_ogg_parse_delete_all_streams (GstOggParse * ogg) -{ - g_slist_foreach (ogg->oggstreams, (GFunc) free_stream, NULL); - g_slist_free (ogg->oggstreams); - ogg->oggstreams = NULL; -} - -static GstOggStream * -gst_ogg_parse_new_stream (GstOggParse * parser, ogg_page * page) -{ - GstOggStream *stream; - ogg_packet packet; - int ret; - guint32 serialno; - - serialno = ogg_page_serialno (page); - - GST_DEBUG_OBJECT (parser, "creating new stream %08x", serialno); - - stream = g_new0 (GstOggStream, 1); - - stream->serialno = serialno; - stream->in_headers = 1; - - if (ogg_stream_init (&stream->stream, serialno) != 0) { - GST_ERROR ("Could not initialize ogg_stream struct for serial %08x.", - serialno); - return NULL; - } - - /* FIXME check return */ - ogg_stream_pagein (&stream->stream, page); - - /* FIXME check return */ - ret = ogg_stream_packetout (&stream->stream, &packet); - if (ret == 1) { - gst_ogg_stream_setup_map (stream, &packet); - } - - parser->oggstreams = g_slist_append (parser->oggstreams, stream); - - return stream; -} - -static GstOggStream * -gst_ogg_parse_find_stream (GstOggParse * parser, guint32 serialno) -{ - GSList *l; - - for (l = parser->oggstreams; l != NULL; l = l->next) { - GstOggStream *stream = (GstOggStream *) l->data; - - if (stream->serialno == serialno) - return stream; - } - return NULL; -} - -/* signals and args */ -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; - -enum -{ - ARG_0 - /* FILL ME */ -}; - -static GstStaticPadTemplate ogg_parse_src_template_factory = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("application/ogg") - ); - -static GstStaticPadTemplate ogg_parse_sink_template_factory = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("application/ogg") - ); - -static void gst_ogg_parse_dispose (GObject * object); -static GstStateChangeReturn gst_ogg_parse_change_state (GstElement * element, - GstStateChange transition); -static GstFlowReturn gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer); - -static void -gst_ogg_parse_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_details (element_class, &gst_ogg_parse_details); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&ogg_parse_sink_template_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&ogg_parse_src_template_factory)); -} - -static void -gst_ogg_parse_class_init (GstOggParseClass * klass) -{ - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - parent_class = g_type_class_peek_parent (klass); - - gstelement_class->change_state = gst_ogg_parse_change_state; - - gobject_class->dispose = gst_ogg_parse_dispose; -} - -static void -gst_ogg_parse_init (GstOggParse * ogg) -{ - /* create the sink and source pads */ - ogg->sinkpad = - gst_pad_new_from_static_template (&ogg_parse_sink_template_factory, - "sink"); - ogg->srcpad = - gst_pad_new_from_static_template (&ogg_parse_src_template_factory, "src"); - - /* TODO: Are there any events we must handle? */ - /* gst_pad_set_event_function (ogg->sinkpad, gst_ogg_parse_handle_event); */ - gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_parse_chain); - - gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad); - gst_element_add_pad (GST_ELEMENT (ogg), ogg->srcpad); - - ogg->oggstreams = NULL; -} - -static void -gst_ogg_parse_dispose (GObject * object) -{ - GstOggParse *ogg = GST_OGG_PARSE (object); - - GST_LOG_OBJECT (ogg, "Disposing of object %p", ogg); - - ogg_sync_clear (&ogg->sync); - gst_ogg_parse_delete_all_streams (ogg); - - if (ogg->caps) { - gst_caps_unref (ogg->caps); - ogg->caps = NULL; - } - - if (G_OBJECT_CLASS (parent_class)->dispose) - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -/* submit the given buffer to the ogg sync. - * - * Returns the number of bytes submited. - */ -static gint -gst_ogg_parse_submit_buffer (GstOggParse * ogg, GstBuffer * buffer) -{ - guint size; - guint8 *data; - gchar *oggbuffer; - - size = GST_BUFFER_SIZE (buffer); - data = GST_BUFFER_DATA (buffer); - - /* We now have a buffer, submit it to the ogg sync layer */ - oggbuffer = ogg_sync_buffer (&ogg->sync, size); - memcpy (oggbuffer, data, size); - ogg_sync_wrote (&ogg->sync, size); - - /* We've copied all the neccesary data, so we're done with the buffer */ - gst_buffer_unref (buffer); - - return size; -} - -static void -gst_ogg_parse_append_header (GValue * array, GstBuffer * buf) -{ - GValue value = { 0 }; - /* We require a copy to avoid circular refcounts */ - GstBuffer *buffer = gst_buffer_copy (buf); - - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); - - g_value_init (&value, GST_TYPE_BUFFER); - gst_value_set_buffer (&value, buffer); - gst_value_array_append_value (array, &value); - g_value_unset (&value); - -} - -typedef enum -{ - PAGE_HEADER, /* Header page */ - PAGE_DATA, /* Data page */ - PAGE_PENDING, /* We don't know yet, we'll have to see some future pages */ -} page_type; - -static page_type -gst_ogg_parse_is_header (GstOggParse * ogg, GstOggStream * stream, - ogg_page * page) -{ - ogg_int64_t gpos = ogg_page_granulepos (page); - - if (gpos < 0) - return PAGE_PENDING; - - /* This is good enough for now, but technically requires codec-specific - * behaviour to be perfect. This is where we need the mooted library for - * this stuff, which nobody has written. - */ - if (gpos > 0) - return PAGE_DATA; - else - return PAGE_HEADER; -} - -static GstBuffer * -gst_ogg_parse_buffer_from_page (ogg_page * page, - guint64 offset, gboolean delta, GstClockTime timestamp) -{ - int size = page->header_len + page->body_len; - GstBuffer *buf = gst_buffer_new_and_alloc (size); - - memcpy (GST_BUFFER_DATA (buf), page->header, page->header_len); - memcpy (GST_BUFFER_DATA (buf) + page->header_len, page->body, page->body_len); - - GST_BUFFER_TIMESTAMP (buf) = timestamp; - GST_BUFFER_OFFSET (buf) = offset; - GST_BUFFER_OFFSET_END (buf) = offset + size; - - return buf; -} - - -/* Reads in buffers, parses them, reframes into one-buffer-per-ogg-page, submits - * pages to output pad. - */ -static GstFlowReturn -gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer) -{ - GstOggParse *ogg; - GstFlowReturn result = GST_FLOW_OK; - gint ret = -1; - guint32 serialno; - GstBuffer *pagebuffer; - GstClockTime buffertimestamp = GST_BUFFER_TIMESTAMP (buffer); - - ogg = GST_OGG_PARSE (GST_OBJECT_PARENT (pad)); - - GST_LOG_OBJECT (ogg, "Chain function received buffer of size %d", - GST_BUFFER_SIZE (buffer)); - - gst_ogg_parse_submit_buffer (ogg, buffer); - - while (ret != 0 && result == GST_FLOW_OK) { - ogg_page page; - - /* We use ogg_sync_pageseek() rather than ogg_sync_pageout() so that we can - * track how many bytes the ogg layer discarded (in the case of sync errors, - * etc.); this allows us to accurately track the current stream offset - */ - ret = ogg_sync_pageseek (&ogg->sync, &page); - if (ret == 0) { - /* need more data, that's fine... */ - break; - } else if (ret < 0) { - /* discontinuity; track how many bytes we skipped (-ret) */ - ogg->offset -= ret; - } else { - gint64 granule = ogg_page_granulepos (&page); -#ifndef GST_DISABLE_GST_DEBUG - int bos = ogg_page_bos (&page); -#endif - guint64 startoffset = ogg->offset; - GstOggStream *stream; - - serialno = ogg_page_serialno (&page); - stream = gst_ogg_parse_find_stream (ogg, serialno); - - GST_LOG_OBJECT (ogg, "Timestamping outgoing buffer as %" GST_TIME_FORMAT, - GST_TIME_ARGS (buffertimestamp)); - - buffertimestamp = gst_ogg_stream_get_end_time_for_granulepos (stream, - granule); - pagebuffer = gst_ogg_parse_buffer_from_page (&page, startoffset, FALSE, - buffertimestamp); - - /* We read out 'ret' bytes, so we set the next offset appropriately */ - ogg->offset += ret; - - GST_LOG_OBJECT (ogg, - "processing ogg page (serial %08x, pageno %ld, " - "granule pos %" G_GUINT64_FORMAT ", bos %d, offset %" - G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT ")", - serialno, ogg_page_pageno (&page), - granule, bos, startoffset, ogg->offset); - - if (ogg_page_bos (&page)) { - /* If we've seen this serialno before, this is technically an error, - * we log this case but accept it - this one replaces the previous - * stream with this serialno. We can do this since we're streaming, and - * not supporting seeking... - */ - GstOggStream *stream = gst_ogg_parse_find_stream (ogg, serialno); - - if (stream != NULL) { - GST_LOG_OBJECT (ogg, "Incorrect stream; repeats serial number %u " - "at offset %" G_GINT64_FORMAT, serialno, ogg->offset); - } - - if (ogg->last_page_not_bos) { - GST_LOG_OBJECT (ogg, "Deleting all referenced streams, found a new " - "chain starting with serial %u", serialno); - gst_ogg_parse_delete_all_streams (ogg); - } - - stream = gst_ogg_parse_new_stream (ogg, &page); - - ogg->last_page_not_bos = FALSE; - - gst_buffer_ref (pagebuffer); - stream->headers = g_list_append (stream->headers, pagebuffer); - - if (!ogg->in_headers) { - GST_LOG_OBJECT (ogg, - "Found start of new chain at offset %" G_GUINT64_FORMAT, - startoffset); - ogg->in_headers = 1; - } - - /* For now, we just keep the header buffer in the stream->headers list; - * it actually gets output once we've collected the entire set - */ - } else { - /* Non-BOS page. Either: we're outside headers, and this isn't a - * header (normal data), outside headers and this is (error!), inside - * headers, this is (append header), or inside headers and this isn't - * (we've found the end of headers; flush the lot!) - * - * Before that, we flag that the last page seen (this one) was not a - * BOS page; that way we know that when we next see a BOS page it's a - * new chain, and we can flush all existing streams. - */ - page_type type; - GstOggStream *stream = gst_ogg_parse_find_stream (ogg, serialno); - - if (!stream) { - GST_LOG_OBJECT (ogg, - "Non-BOS page unexpectedly found at %" G_GINT64_FORMAT, - ogg->offset); - goto failure; - } - - ogg->last_page_not_bos = TRUE; - - type = gst_ogg_parse_is_header (ogg, stream, &page); - - if (type == PAGE_PENDING && ogg->in_headers) { - gst_buffer_ref (pagebuffer); - - stream->unknown_pages = g_list_append (stream->unknown_pages, - pagebuffer); - } else if (type == PAGE_HEADER) { - if (!ogg->in_headers) { - GST_LOG_OBJECT (ogg, "Header page unexpectedly found outside " - "headers at offset %" G_GINT64_FORMAT, ogg->offset); - goto failure; - } else { - /* Append the header to the buffer list, after any unknown previous - * pages - */ - stream->headers = g_list_concat (stream->headers, - stream->unknown_pages); - g_list_free (stream->unknown_pages); - gst_buffer_ref (pagebuffer); - stream->headers = g_list_append (stream->headers, pagebuffer); - } - } else { /* PAGE_DATA, or PAGE_PENDING but outside headers */ - if (ogg->in_headers) { - /* First non-header page... set caps, flush headers. - * - * First up, we build a single GValue list of all the pagebuffers - * we're using for the headers, in order. - * Then we set this on the caps structure. Then we can start pushing - * buffers for the headers, and finally we send this non-header - * page. - */ - GstCaps *caps; - GstStructure *structure; - GValue array = { 0 }; - gint count = 0; - gboolean found_pending_headers = FALSE; - GSList *l; - - g_value_init (&array, GST_TYPE_ARRAY); - - for (l = ogg->oggstreams; l != NULL; l = l->next) { - GstOggStream *stream = (GstOggStream *) l->data; - - if (g_list_length (stream->headers) == 0) { - GST_LOG_OBJECT (ogg, "No primary header found for stream %08lx", - stream->serialno); - goto failure; - } - - gst_ogg_parse_append_header (&array, - GST_BUFFER (stream->headers->data)); - count++; - } - - for (l = ogg->oggstreams; l != NULL; l = l->next) { - GstOggStream *stream = (GstOggStream *) l->data; - int j; - - for (j = 1; j < g_list_length (stream->headers); j++) { - gst_ogg_parse_append_header (&array, - GST_BUFFER (g_list_nth_data (stream->headers, j))); - count++; - } - } - - caps = gst_pad_get_caps (ogg->srcpad); - caps = gst_caps_make_writable (caps); - - structure = gst_caps_get_structure (caps, 0); - gst_structure_set_value (structure, "streamheader", &array); - - gst_pad_set_caps (ogg->srcpad, caps); - - g_value_unset (&array); - - if (ogg->caps) - gst_caps_unref (ogg->caps); - ogg->caps = caps; - - GST_LOG_OBJECT (ogg, "Set \"streamheader\" caps with %d buffers " - "(one per page)", count); - - /* Now, we do the same thing, but push buffers... */ - for (l = ogg->oggstreams; l != NULL; l = l->next) { - GstOggStream *stream = (GstOggStream *) l->data; - GstBuffer *buf = GST_BUFFER (stream->headers->data); - - gst_buffer_set_caps (buf, caps); - - result = gst_pad_push (ogg->srcpad, buf); - if (result != GST_FLOW_OK) - return result; - } - for (l = ogg->oggstreams; l != NULL; l = l->next) { - GstOggStream *stream = (GstOggStream *) l->data; - int j; - - for (j = 1; j < g_list_length (stream->headers); j++) { - GstBuffer *buf = - GST_BUFFER (g_list_nth_data (stream->headers, j)); - gst_buffer_set_caps (buf, caps); - - result = gst_pad_push (ogg->srcpad, buf); - if (result != GST_FLOW_OK) - return result; - } - } - - ogg->in_headers = 0; - - /* And finally the pending data pages */ - for (l = ogg->oggstreams; l != NULL; l = l->next) { - GstOggStream *stream = (GstOggStream *) l->data; - GList *k; - - if (stream->unknown_pages == NULL) - continue; - - if (found_pending_headers) { - GST_WARNING_OBJECT (ogg, "Incorrectly muxed headers found at " - "approximate offset %" G_GINT64_FORMAT, ogg->offset); - } - found_pending_headers = TRUE; - - GST_LOG_OBJECT (ogg, "Pushing %d pending pages after headers", - g_list_length (stream->unknown_pages) + 1); - - for (k = stream->unknown_pages; k != NULL; k = k->next) { - GstBuffer *buf; - - buf = GST_BUFFER (k->data); - gst_buffer_set_caps (buf, caps); - result = gst_pad_push (ogg->srcpad, buf); - if (result != GST_FLOW_OK) - return result; - } - g_list_foreach (stream->unknown_pages, - (GFunc) gst_mini_object_unref, NULL); - g_list_free (stream->unknown_pages); - stream->unknown_pages = NULL; - } - - gst_buffer_set_caps (pagebuffer, caps); - - result = gst_pad_push (ogg->srcpad, GST_BUFFER (pagebuffer)); - if (result != GST_FLOW_OK) - return result; - } else { - /* Normal data page, submit buffer */ - gst_buffer_set_caps (pagebuffer, ogg->caps); - result = gst_pad_push (ogg->srcpad, GST_BUFFER (pagebuffer)); - if (result != GST_FLOW_OK) - return result; - } - } - } - } - } - - return result; - -failure: - gst_pad_push_event (GST_PAD (ogg->srcpad), gst_event_new_eos ()); - return GST_FLOW_ERROR; -} - -static GstStateChangeReturn -gst_ogg_parse_change_state (GstElement * element, GstStateChange transition) -{ - GstOggParse *ogg; - GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE; - - ogg = GST_OGG_PARSE (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - ogg_sync_init (&ogg->sync); - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - ogg_sync_reset (&ogg->sync); - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - default: - break; - } - - result = parent_class->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_NULL: - ogg_sync_clear (&ogg->sync); - break; - default: - break; - } - return result; -} - -gboolean -gst_ogg_parse_plugin_init (GstPlugin * plugin) -{ - GST_DEBUG_CATEGORY_INIT (gst_ogg_parse_debug, "oggparse", 0, "ogg parser"); - - return gst_element_register (plugin, "oggparse", GST_RANK_NONE, - GST_TYPE_OGG_PARSE); -} diff --git a/ext/ogg/gstoggstream.c b/ext/ogg/gstoggstream.c deleted file mode 100644 index 3dac5ad0..00000000 --- a/ext/ogg/gstoggstream.c +++ /dev/null @@ -1,1315 +0,0 @@ -/* GStreamer Ogg Granulepos Mapping Utility Functions - * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> - * Copyright (C) 2009 David Schleef <ds@schleef.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstoggstream.h" -#include "dirac_parse.h" - -#include <gst/riff/riff-media.h> - -#include <string.h> - -GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_debug); -GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_setup_debug); -#define GST_CAT_DEFAULT gst_ogg_demux_debug - -typedef struct _GstOggMap GstOggMap; - -typedef gboolean (*GstOggMapSetupFunc) (GstOggStream * pad, - ogg_packet * packet); -typedef GstClockTime (*GstOggMapToTimeFunc) (GstOggStream * pad, - gint64 granulepos); -typedef gint64 (*GstOggMapToGranuleFunc) (GstOggStream * pad, - gint64 granulepos); -typedef gint64 (*GstOggMapToGranuleposFunc) (GstOggStream * pad, - gint64 granule, gint64 keyframe_granule); - -/* returns TRUE if the granulepos denotes a key frame */ -typedef gboolean (*GstOggMapIsKeyFrameFunc) (GstOggStream * pad, - gint64 granulepos); - -/* returns TRUE if the given packet is a stream header packet */ -typedef gboolean (*GstOggMapIsHeaderPacketFunc) (GstOggStream * pad, - ogg_packet * packet); -typedef gint64 (*GstOggMapPacketDurationFunc) (GstOggStream * pad, - ogg_packet * packet); - - - -#define SKELETON_FISBONE_MIN_SIZE 52 - - -struct _GstOggMap -{ - const gchar *id; - int id_length; - int min_packet_size; - const gchar *media_type; - GstOggMapSetupFunc setup_func; - GstOggMapToGranuleFunc granulepos_to_granule_func; - GstOggMapToGranuleposFunc granule_to_granulepos_func; - GstOggMapIsKeyFrameFunc is_key_frame_func; - GstOggMapIsHeaderPacketFunc is_header_func; - GstOggMapPacketDurationFunc packet_duration_func; -}; - -static const GstOggMap mappers[]; - -GstClockTime -gst_ogg_stream_get_packet_start_time (GstOggStream * pad, ogg_packet * packet) -{ - int duration; - - if (packet->granulepos == -1) { - return GST_CLOCK_TIME_NONE; - } - - duration = gst_ogg_stream_get_packet_duration (pad, packet); - if (duration == -1) { - return GST_CLOCK_TIME_NONE; - } - - return gst_ogg_stream_granule_to_time (pad, - gst_ogg_stream_granulepos_to_granule (pad, - packet->granulepos) - duration); -} - -GstClockTime -gst_ogg_stream_get_start_time_for_granulepos (GstOggStream * pad, - gint64 granulepos) -{ - if (pad->frame_size == 0) - return GST_CLOCK_TIME_NONE; - - return gst_ogg_stream_granule_to_time (pad, - gst_ogg_stream_granulepos_to_granule (pad, granulepos)); -} - -GstClockTime -gst_ogg_stream_get_end_time_for_granulepos (GstOggStream * pad, - gint64 granulepos) -{ - return gst_ogg_stream_granule_to_time (pad, - gst_ogg_stream_granulepos_to_granule (pad, granulepos)); -} - -GstClockTime -gst_ogg_stream_granule_to_time (GstOggStream * pad, gint64 granule) -{ - if (granule == 0 || pad->granulerate_n == 0 || pad->granulerate_d == 0) - return 0; - - return gst_util_uint64_scale (granule, GST_SECOND * pad->granulerate_d, - pad->granulerate_n); -} - -gint64 -gst_ogg_stream_granulepos_to_granule (GstOggStream * pad, gint64 granulepos) -{ - if (granulepos == -1 || granulepos == 0) { - return granulepos; - } - - if (mappers[pad->map].granulepos_to_granule_func == NULL) { - GST_WARNING ("Failed to convert granulepos to granule"); - return -1; - } - - return mappers[pad->map].granulepos_to_granule_func (pad, granulepos); -} - -gint64 -gst_ogg_stream_granulepos_to_key_granule (GstOggStream * pad, gint64 granulepos) -{ - if (granulepos == -1 || granulepos == 0) { - return granulepos; - } - - return granulepos >> pad->granuleshift; -} - -gint64 -gst_ogg_stream_granule_to_granulepos (GstOggStream * pad, gint64 granule, - gint64 keyframe_granule) -{ - if (granule == -1 || granule == 0) { - return granule; - } - - if (mappers[pad->map].granule_to_granulepos_func == NULL) { - GST_WARNING ("Failed to convert granule to granulepos"); - return -1; - } - - return mappers[pad->map].granule_to_granulepos_func (pad, granule, - keyframe_granule); -} - -gboolean -gst_ogg_stream_packet_granulepos_is_key_frame (GstOggStream * pad, - gint64 granulepos) -{ - if (granulepos == -1) { - return FALSE; - } - - if (mappers[pad->map].is_key_frame_func == NULL) { - GST_WARNING ("Failed to determine key frame"); - return FALSE; - } - - return mappers[pad->map].is_key_frame_func (pad, granulepos); -} - -gboolean -gst_ogg_stream_packet_is_header (GstOggStream * pad, ogg_packet * packet) -{ - if (mappers[pad->map].is_header_func == NULL) { - GST_WARNING ("Failed to determine header"); - return FALSE; - } - - return mappers[pad->map].is_header_func (pad, packet); -} - -gint64 -gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet * packet) -{ - if (mappers[pad->map].packet_duration_func == NULL) { - GST_WARNING ("Failed to determine packet duration"); - return -1; - } - - return mappers[pad->map].packet_duration_func (pad, packet); -} - - - - -/* some generic functions */ - -static gboolean -is_keyframe_true (GstOggStream * pad, gint64 granulepos) -{ - return TRUE; -} - -static gint64 -granulepos_to_granule_default (GstOggStream * pad, gint64 granulepos) -{ - gint64 keyindex, keyoffset; - - if (pad->granuleshift != 0) { - keyindex = granulepos >> pad->granuleshift; - keyoffset = granulepos - (keyindex << pad->granuleshift); - return keyindex + keyoffset; - } else { - return granulepos; - } -} - - -static gint64 -granule_to_granulepos_default (GstOggStream * pad, gint64 granule, - gint64 keyframe_granule) -{ - gint64 keyoffset; - - if (pad->granuleshift != 0) { - keyoffset = granule - keyframe_granule; - return (keyframe_granule << pad->granuleshift) | keyoffset; - } else { - return granule; - } -} - -#ifdef unused -static gboolean -is_header_unknown (GstOggStream * pad, ogg_packet * packet) -{ - GST_WARNING ("don't know how to detect header"); - return FALSE; -} -#endif - -static gboolean -is_header_true (GstOggStream * pad, ogg_packet * packet) -{ - return TRUE; -} - -static gboolean -is_header_count (GstOggStream * pad, ogg_packet * packet) -{ - if (pad->n_header_packets_seen < pad->n_header_packets) { - return TRUE; - } - return FALSE; -} - -static gint64 -packet_duration_constant (GstOggStream * pad, ogg_packet * packet) -{ - return pad->frame_size; -} - -/* theora */ - -static gboolean -setup_theora_mapper (GstOggStream * pad, ogg_packet * packet) -{ - guint8 *data = packet->packet; - guint w, h, par_d, par_n; - - w = GST_READ_UINT24_BE (data + 14) & 0xFFFFF0; - h = GST_READ_UINT24_BE (data + 17) & 0xFFFFF0; - - pad->granulerate_n = GST_READ_UINT32_BE (data + 22); - pad->granulerate_d = GST_READ_UINT32_BE (data + 26); - - par_n = GST_READ_UINT24_BE (data + 30); - par_d = GST_READ_UINT24_BE (data + 33); - - GST_LOG ("fps = %d/%d, PAR = %u/%u, width = %u, height = %u", - pad->granulerate_n, pad->granulerate_d, par_n, par_d, w, h); - - /* 2 bits + 3 bits = 5 bits KFGSHIFT */ - pad->granuleshift = ((GST_READ_UINT8 (data + 40) & 0x03) << 3) + - (GST_READ_UINT8 (data + 41) >> 5); - - pad->n_header_packets = 3; - pad->frame_size = 1; - - if (pad->granulerate_n == 0 || pad->granulerate_d == 0) { - GST_WARNING ("frame rate %d/%d", pad->granulerate_n, pad->granulerate_d); - return FALSE; - } - - pad->caps = gst_caps_new_simple ("video/x-theora", NULL); - - if (w > 0 && h > 0) { - gst_caps_set_simple (pad->caps, "width", G_TYPE_INT, w, "height", - G_TYPE_INT, h, NULL); - } - - /* PAR of 0:N, N:0 and 0:0 is allowed and maps to 1:1 */ - if (par_n == 0 || par_d == 0) - par_n = par_d = 1; - - /* only add framerate now so caps look prettier, with width/height first */ - gst_caps_set_simple (pad->caps, "framerate", GST_TYPE_FRACTION, - pad->granulerate_n, pad->granulerate_d, "pixel-aspect-ratio", - GST_TYPE_FRACTION, par_n, par_d, NULL); - - return TRUE; -} - -static gint64 -granulepos_to_granule_theora (GstOggStream * pad, gint64 granulepos) -{ - gint64 keyindex, keyoffset; - - if (pad->granuleshift != 0) { - keyindex = granulepos >> pad->granuleshift; - keyoffset = granulepos - (keyindex << pad->granuleshift); - if (keyoffset == 0) { - pad->theora_has_zero_keyoffset = TRUE; - } - if (pad->theora_has_zero_keyoffset) { - keyoffset++; - } - return keyindex + keyoffset; - } else { - return granulepos; - } -} - -static gboolean -is_keyframe_theora (GstOggStream * pad, gint64 granulepos) -{ - gint64 frame_mask; - - if (granulepos == (gint64) - 1) - return FALSE; - - frame_mask = (1 << (pad->granuleshift + 1)) - 1; - - return ((granulepos & frame_mask) == 0); -} - -static gboolean -is_header_theora (GstOggStream * pad, ogg_packet * packet) -{ - return (packet->bytes > 0 && (packet->packet[0] & 0x80) == 0x80); -} - -/* dirac */ - -static gboolean -setup_dirac_mapper (GstOggStream * pad, ogg_packet * packet) -{ - int ret; - DiracSequenceHeader header; - - ret = dirac_sequence_header_parse (&header, packet->packet + 13, - packet->bytes - 13); - if (ret == 0) { - GST_DEBUG ("Failed to parse Dirac sequence header"); - return FALSE; - } - - pad->granulerate_n = header.frame_rate_numerator * 2; - pad->granulerate_d = header.frame_rate_denominator; - pad->granuleshift = 22; - pad->n_header_packets = 1; - pad->frame_size = 2; - - if (header.interlaced_coding != 0) { - GST_DEBUG ("non-progressive Dirac coding not implemented"); - return FALSE; - } - - pad->caps = gst_caps_new_simple ("video/x-dirac", - "width", G_TYPE_INT, header.width, - "height", G_TYPE_INT, header.height, - "interlaced", G_TYPE_BOOLEAN, header.interlaced, - "pixel-aspect-ratio", GST_TYPE_FRACTION, - header.aspect_ratio_numerator, header.aspect_ratio_denominator, - "framerate", GST_TYPE_FRACTION, header.frame_rate_numerator, - header.frame_rate_denominator, NULL); - - return TRUE; -} - -#define OGG_DIRAC_GRANULE_LOW_MASK ((1<<22) - 1) -static gboolean -is_keyframe_dirac (GstOggStream * pad, gint64 granulepos) -{ - gint64 pt; - int dist_h; - int dist_l; - int dist; - int delay; - gint64 dt; - - if (granulepos == -1) - return -1; - - pt = ((granulepos >> 22) + (granulepos & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9; - dist_h = (granulepos >> 22) & 0xff; - dist_l = granulepos & 0xff; - dist = (dist_h << 8) | dist_l; - delay = (granulepos >> 9) & 0x1fff; - dt = pt - delay; - - return (dist == 0); -} - -static gint64 -granulepos_to_granule_dirac (GstOggStream * pad, gint64 gp) -{ - gint64 pt; - int dist_h; - int dist_l; - int dist; - int delay; - gint64 dt; - - pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9; - dist_h = (gp >> 22) & 0xff; - dist_l = gp & 0xff; - dist = (dist_h << 8) | dist_l; - delay = (gp >> 9) & 0x1fff; - dt = pt - delay; - - GST_DEBUG ("pt %" G_GINT64_FORMAT " delay %d", pt, delay); - - return dt + 4; -} - -static gint64 -granule_to_granulepos_dirac (GstOggStream * pad, gint64 granule, - gint64 keyframe_granule) -{ - /* This conversion requires knowing more details about the Dirac - * stream. */ - return -1; -} - - -/* vorbis */ - -void parse_vorbis_header_packet (GstOggStream * pad, ogg_packet * op); -void parse_vorbis_setup_packet (GstOggStream * pad, ogg_packet * op); - - -static gboolean -setup_vorbis_mapper (GstOggStream * pad, ogg_packet * packet) -{ - guint8 *data = packet->packet; - guint chans; - - data += 1 + 6 + 4; - chans = GST_READ_UINT8 (data); - data += 1; - pad->granulerate_n = GST_READ_UINT32_LE (data); - pad->granulerate_d = 1; - pad->granuleshift = 0; - pad->last_size = 0; - GST_LOG ("sample rate: %d", pad->granulerate_n); - - pad->n_header_packets = 3; - - if (pad->granulerate_n == 0) - return FALSE; - - parse_vorbis_header_packet (pad, packet); - - pad->caps = gst_caps_new_simple ("audio/x-vorbis", - "rate", G_TYPE_INT, pad->granulerate_n, "channels", G_TYPE_INT, chans, - NULL); - - return TRUE; -} - -static gboolean -is_header_vorbis (GstOggStream * pad, ogg_packet * packet) -{ - if (packet->bytes > 0 && (packet->packet[0] & 0x01) == 0) - return FALSE; - - if (packet->packet[0] == 5) { - parse_vorbis_setup_packet (pad, packet); - } - - return TRUE; -} - -static gint64 -packet_duration_vorbis (GstOggStream * pad, ogg_packet * packet) -{ - int mode; - int size; - int duration; - - if (packet->packet[0] & 1) - return 0; - - mode = (packet->packet[0] >> 1) & ((1 << pad->vorbis_log2_num_modes) - 1); - size = pad->vorbis_mode_sizes[mode] ? pad->long_size : pad->short_size; - - if (pad->last_size == 0) { - duration = 0; - } else { - duration = pad->last_size / 4 + size / 4; - } - pad->last_size = size; - - GST_DEBUG ("duration %d", (int) duration); - - return duration; -} - -/* speex */ - - -static gboolean -setup_speex_mapper (GstOggStream * pad, ogg_packet * packet) -{ - guint8 *data = packet->packet; - guint chans; - - data += 8 + 20 + 4 + 4; - pad->granulerate_n = GST_READ_UINT32_LE (data); - pad->granulerate_d = 1; - pad->granuleshift = 0; - - data += 4 + 4 + 4; - chans = GST_READ_UINT32_LE (data); - - GST_LOG ("sample rate: %d, channels: %u", pad->granulerate_n, chans); - - pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 68) + 2; - pad->frame_size = GST_READ_UINT32_LE (packet->packet + 64) * - GST_READ_UINT32_LE (packet->packet + 56); - - if (pad->granulerate_n == 0) - return FALSE; - - pad->caps = gst_caps_new_simple ("audio/x-speex", "rate", G_TYPE_INT, - pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL); - - return TRUE; -} - - -/* flac */ - -static gboolean -setup_fLaC_mapper (GstOggStream * pad, ogg_packet * packet) -{ - pad->granulerate_n = 0; - pad->granulerate_d = 1; - pad->granuleshift = 0; - - pad->n_header_packets = 3; - - pad->caps = gst_caps_new_simple ("audio/x-flac", NULL); - - return TRUE; -} - -static gboolean -is_header_fLaC (GstOggStream * pad, ogg_packet * packet) -{ - if (pad->n_header_packets_seen == 1) { - pad->granulerate_n = (packet->packet[14] << 12) | - (packet->packet[15] << 4) | ((packet->packet[16] >> 4) & 0xf); - } - - if (pad->n_header_packets_seen < pad->n_header_packets) { - return TRUE; - } - - return FALSE; -} - -static gboolean -setup_flac_mapper (GstOggStream * pad, ogg_packet * packet) -{ - guint8 *data = packet->packet; - guint chans; - - /* see http://flac.sourceforge.net/ogg_mapping.html */ - - pad->granulerate_n = (GST_READ_UINT32_BE (data + 27) & 0xFFFFF000) >> 12; - pad->granulerate_d = 1; - pad->granuleshift = 0; - chans = ((GST_READ_UINT32_BE (data + 27) & 0x00000E00) >> 9) + 1; - - GST_DEBUG ("sample rate: %d, channels: %u", pad->granulerate_n, chans); - - pad->n_header_packets = GST_READ_UINT16_BE (packet->packet + 7); - - if (pad->granulerate_n == 0) - return FALSE; - - pad->caps = gst_caps_new_simple ("audio/x-flac", "rate", G_TYPE_INT, - pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL); - - return TRUE; -} - -static gboolean -is_header_flac (GstOggStream * pad, ogg_packet * packet) -{ - return (packet->bytes > 0 && (packet->packet[0] != 0xff)); -} - -static gint64 -packet_duration_flac (GstOggStream * pad, ogg_packet * packet) -{ - int block_size_index; - - if (packet->bytes < 4) - return -1; - - block_size_index = packet->packet[2] >> 4; - if (block_size_index == 1) - return 192; - if (block_size_index >= 2 && block_size_index <= 5) { - return 576 << (block_size_index - 2); - } - if (block_size_index >= 8) { - return 256 << (block_size_index - 8); - } - if (block_size_index == 6 || block_size_index == 7) { - guint len, bytes = (block_size_index - 6) + 1; - guint8 tmp; - - if (packet->bytes < 4 + 1 + bytes) - return -1; - tmp = packet->packet[4]; - /* utf-8 prefix */ - len = 0; - while (tmp & 0x80) { - len++; - tmp <<= 1; - } - if (len == 2) - return -1; - if (len == 0) - len++; - if (packet->bytes < 4 + len + bytes) - return -1; - if (bytes == 1) { - return packet->packet[4 + len] + 1; - } else { - return GST_READ_UINT16_BE (packet->packet + 4 + len) + 1; - } - } - return -1; -} - -/* fishead */ - -static gboolean -setup_fishead_mapper (GstOggStream * pad, ogg_packet * packet) -{ - guint8 *data; - gint64 prestime_n, prestime_d; - gint64 basetime_n, basetime_d; - gint64 basetime; - - data = packet->packet; - - data += 8 + 2 + 2; /* header + major/minor version */ - - prestime_n = (gint64) GST_READ_UINT64_LE (data); - data += 8; - prestime_d = (gint64) GST_READ_UINT64_LE (data); - data += 8; - basetime_n = (gint64) GST_READ_UINT64_LE (data); - data += 8; - basetime_d = (gint64) GST_READ_UINT64_LE (data); - data += 8; - - /* FIXME: we don't use basetime anywhere in the demuxer! */ - if (basetime_d != 0) - basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d); - else - basetime = -1; - - GST_INFO ("skeleton fishead parsed (basetime: %" GST_TIME_FORMAT ")", - GST_TIME_ARGS (basetime)); - - return TRUE; -} - -gboolean -gst_ogg_map_add_fisbone (GstOggStream * pad, - const guint8 * data, guint size, GstClockTime * p_start_time, - guint32 * p_preroll) -{ - GstClockTime start_time; - gint64 start_granule; - guint32 preroll; - - if (size < SKELETON_FISBONE_MIN_SIZE || memcmp (data, "fisbone\0", 8) != 0) { - GST_WARNING ("invalid fisbone packet, ignoring"); - return FALSE; - } - - if (pad->have_fisbone) { - GST_DEBUG ("already have fisbone, ignoring second one"); - return FALSE; - } - - /* skip "fisbone\0" + headers offset + serialno + num headers */ - data += 8 + 4 + 4 + 4; - - pad->have_fisbone = TRUE; - - /* we just overwrite whatever was set before by the format-specific setup */ - pad->granulerate_n = GST_READ_UINT64_LE (data); - pad->granulerate_d = GST_READ_UINT64_LE (data + 8); - - start_granule = GST_READ_UINT64_LE (data + 16); - preroll = GST_READ_UINT32_LE (data + 24); - pad->granuleshift = GST_READ_UINT8 (data + 28); - - start_time = granulepos_to_granule_default (pad, start_granule); - - if (p_start_time) - *p_start_time = start_time; - - if (p_preroll) - *p_preroll = preroll; - - return TRUE; -} - -/* Do we need these for something? - * ogm->hdr.size = GST_READ_UINT32_LE (&data[13]); - * ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]); - * ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[25]); - * ogm->hdr.default_len = GST_READ_UINT32_LE (&data[33]); - * ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[37]); - * ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[41]); - */ - -static gboolean -is_header_ogm (GstOggStream * pad, ogg_packet * packet) -{ - if (packet->bytes >= 1 && (packet->packet[0] & 0x01)) - return TRUE; - - return FALSE; -} - -static gint64 -packet_duration_ogm (GstOggStream * pad, ogg_packet * packet) -{ - const guint8 *data; - int samples; - int offset; - int n; - - data = packet->packet; - offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1)); - - if (offset > packet->bytes) { - GST_ERROR ("buffer too small"); - return -1; - } - - samples = 0; - for (n = offset - 1; n > 0; n--) { - samples = (samples << 8) | data[n]; - } - - return samples; -} - -static gboolean -setup_ogmaudio_mapper (GstOggStream * pad, ogg_packet * packet) -{ - guint8 *data = packet->packet; - guint32 fourcc; - - pad->granulerate_n = GST_READ_UINT64_LE (data + 25); - pad->granulerate_d = 1; - - fourcc = GST_READ_UINT32_LE (data + 9); - GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc)); - - pad->caps = gst_riff_create_audio_caps (fourcc, NULL, NULL, NULL, NULL, NULL); - - GST_LOG ("sample rate: %d", pad->granulerate_n); - if (pad->granulerate_n == 0) - return FALSE; - - if (pad->caps) { - gst_caps_set_simple (pad->caps, - "rate", G_TYPE_INT, pad->granulerate_n, NULL); - } else { - pad->caps = gst_caps_new_simple ("audio/x-ogm-unknown", - "fourcc", GST_TYPE_FOURCC, fourcc, - "rate", G_TYPE_INT, pad->granulerate_n, NULL); - } - - pad->n_header_packets = 1; - pad->is_ogm = TRUE; - - return TRUE; -} - -static gboolean -setup_ogmvideo_mapper (GstOggStream * pad, ogg_packet * packet) -{ - guint8 *data = packet->packet; - guint32 fourcc; - int width, height; - gint64 time_unit; - - GST_DEBUG ("time unit %d", GST_READ_UINT32_LE (data + 16)); - GST_DEBUG ("samples per unit %d", GST_READ_UINT32_LE (data + 24)); - - pad->granulerate_n = 10000000; - time_unit = GST_READ_UINT64_LE (data + 17); - if (time_unit > G_MAXINT || time_unit < G_MININT) { - GST_WARNING ("timeunit is out of range"); - } - pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT); - - GST_LOG ("fps = %d/%d = %.3f", - pad->granulerate_n, pad->granulerate_d, - (double) pad->granulerate_n / pad->granulerate_d); - - fourcc = GST_READ_UINT32_LE (data + 9); - width = GST_READ_UINT32_LE (data + 45); - height = GST_READ_UINT32_LE (data + 49); - GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc)); - - pad->caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL); - - if (pad->caps == NULL) { - pad->caps = gst_caps_new_simple ("video/x-ogm-unknown", - "fourcc", GST_TYPE_FOURCC, fourcc, - "framerate", GST_TYPE_FRACTION, pad->granulerate_n, - pad->granulerate_d, NULL); - } else { - gst_caps_set_simple (pad->caps, - "framerate", GST_TYPE_FRACTION, pad->granulerate_n, - pad->granulerate_d, - "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL); - } - GST_DEBUG ("caps: %" GST_PTR_FORMAT, pad->caps); - - pad->n_header_packets = 1; - pad->frame_size = 1; - pad->is_ogm = TRUE; - - return TRUE; -} - -static gboolean -setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet) -{ - guint8 *data = packet->packet; - gint64 time_unit; - - pad->granulerate_n = 10000000; - time_unit = GST_READ_UINT64_LE (data + 17); - if (time_unit > G_MAXINT || time_unit < G_MININT) { - GST_WARNING ("timeunit is out of range"); - } - pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT); - - GST_LOG ("fps = %d/%d = %.3f", - pad->granulerate_n, pad->granulerate_d, - (double) pad->granulerate_n / pad->granulerate_d); - - if (pad->granulerate_d <= 0) - return FALSE; - - pad->caps = gst_caps_new_simple ("text/plain", NULL); - - pad->n_header_packets = 1; - pad->is_ogm = TRUE; - pad->is_ogm_text = TRUE; - - return TRUE; -} - -/* PCM */ - -#define OGGPCM_FMT_S8 0x00000000 /* Signed integer 8 bit */ -#define OGGPCM_FMT_U8 0x00000001 /* Unsigned integer 8 bit */ -#define OGGPCM_FMT_S16_LE 0x00000002 /* Signed integer 16 bit little endian */ -#define OGGPCM_FMT_S16_BE 0x00000003 /* Signed integer 16 bit big endian */ -#define OGGPCM_FMT_S24_LE 0x00000004 /* Signed integer 24 bit little endian */ -#define OGGPCM_FMT_S24_BE 0x00000005 /* Signed integer 24 bit big endian */ -#define OGGPCM_FMT_S32_LE 0x00000006 /* Signed integer 32 bit little endian */ -#define OGGPCM_FMT_S32_BE 0x00000007 /* Signed integer 32 bit big endian */ - -#define OGGPCM_FMT_ULAW 0x00000010 /* G.711 u-law encoding (8 bit) */ -#define OGGPCM_FMT_ALAW 0x00000011 /* G.711 A-law encoding (8 bit) */ - -#define OGGPCM_FMT_FLT32_LE 0x00000020 /* IEEE Float [-1,1] 32 bit little endian */ -#define OGGPCM_FMT_FLT32_BE 0x00000021 /* IEEE Float [-1,1] 32 bit big endian */ -#define OGGPCM_FMT_FLT64_LE 0x00000022 /* IEEE Float [-1,1] 64 bit little endian */ -#define OGGPCM_FMT_FLT64_BE 0x00000023 /* IEEE Float [-1,1] 64 bit big endian */ - - -static gboolean -setup_pcm_mapper (GstOggStream * pad, ogg_packet * packet) -{ - guint8 *data = packet->packet; - int format; - int channels; - GstCaps *caps; - - pad->granulerate_n = GST_READ_UINT32_LE (data + 16); - pad->granulerate_d = 1; - GST_LOG ("sample rate: %d", pad->granulerate_n); - - format = GST_READ_UINT32_LE (data + 12); - channels = GST_READ_UINT8 (data + 21); - - pad->n_header_packets = 2 + GST_READ_UINT32_LE (data + 24); - - if (pad->granulerate_n == 0) - return FALSE; - - switch (format) { - case OGGPCM_FMT_S8: - caps = gst_caps_new_simple ("audio/x-raw-int", - "depth", G_TYPE_INT, 8, - "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, TRUE, NULL); - break; - case OGGPCM_FMT_U8: - caps = gst_caps_new_simple ("audio/x-raw-int", - "depth", G_TYPE_INT, 8, - "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, FALSE, NULL); - break; - case OGGPCM_FMT_S16_LE: - caps = gst_caps_new_simple ("audio/x-raw-int", - "depth", G_TYPE_INT, 16, - "width", G_TYPE_INT, 16, - "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, - "signed", G_TYPE_BOOLEAN, TRUE, NULL); - break; - case OGGPCM_FMT_S16_BE: - caps = gst_caps_new_simple ("audio/x-raw-int", - "depth", G_TYPE_INT, 16, - "width", G_TYPE_INT, 16, - "endianness", G_TYPE_INT, G_BIG_ENDIAN, - "signed", G_TYPE_BOOLEAN, TRUE, NULL); - break; - case OGGPCM_FMT_S24_LE: - caps = gst_caps_new_simple ("audio/x-raw-int", - "depth", G_TYPE_INT, 24, - "width", G_TYPE_INT, 24, - "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, - "signed", G_TYPE_BOOLEAN, TRUE, NULL); - break; - case OGGPCM_FMT_S24_BE: - caps = gst_caps_new_simple ("audio/x-raw-int", - "depth", G_TYPE_INT, 24, - "width", G_TYPE_INT, 24, - "endianness", G_TYPE_INT, G_BIG_ENDIAN, - "signed", G_TYPE_BOOLEAN, TRUE, NULL); - break; - case OGGPCM_FMT_S32_LE: - caps = gst_caps_new_simple ("audio/x-raw-int", - "depth", G_TYPE_INT, 32, - "width", G_TYPE_INT, 32, - "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, - "signed", G_TYPE_BOOLEAN, TRUE, NULL); - break; - case OGGPCM_FMT_S32_BE: - caps = gst_caps_new_simple ("audio/x-raw-int", - "depth", G_TYPE_INT, 32, - "width", G_TYPE_INT, 32, - "endianness", G_TYPE_INT, G_BIG_ENDIAN, - "signed", G_TYPE_BOOLEAN, TRUE, NULL); - break; - case OGGPCM_FMT_ULAW: - caps = gst_caps_new_simple ("audio/x-mulaw", NULL); - break; - case OGGPCM_FMT_ALAW: - caps = gst_caps_new_simple ("audio/x-alaw", NULL); - break; - case OGGPCM_FMT_FLT32_LE: - caps = gst_caps_new_simple ("audio/x-raw-float", - "width", G_TYPE_INT, 32, - "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL); - break; - case OGGPCM_FMT_FLT32_BE: - caps = gst_caps_new_simple ("audio/x-raw-float", - "width", G_TYPE_INT, 32, - "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL); - break; - case OGGPCM_FMT_FLT64_LE: - caps = gst_caps_new_simple ("audio/x-raw-float", - "width", G_TYPE_INT, 64, - "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL); - break; - case OGGPCM_FMT_FLT64_BE: - caps = gst_caps_new_simple ("audio/x-raw-float", - "width", G_TYPE_INT, 64, - "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL); - break; - default: - return FALSE; - } - - gst_caps_set_simple (caps, "audio/x-raw-int", - "rate", G_TYPE_INT, pad->granulerate_n, - "channels", G_TYPE_INT, channels, NULL); - pad->caps = caps; - - return TRUE; -} - -/* cmml */ - -static gboolean -setup_cmml_mapper (GstOggStream * pad, ogg_packet * packet) -{ - guint8 *data = packet->packet; - - pad->granulerate_n = GST_READ_UINT64_LE (data + 12); - pad->granulerate_d = GST_READ_UINT64_LE (data + 20); - pad->granuleshift = data[28]; - GST_LOG ("sample rate: %d", pad->granulerate_n); - - pad->n_header_packets = 3; - - if (pad->granulerate_n == 0) - return FALSE; - - data += 4 + (4 + 4 + 4); - GST_DEBUG ("blocksize0: %u", 1 << (data[0] >> 4)); - GST_DEBUG ("blocksize1: %u", 1 << (data[0] & 0x0F)); - - pad->caps = gst_caps_new_simple ("text/x-cmml", NULL); - - return TRUE; -} - -/* celt */ - -static gboolean -setup_celt_mapper (GstOggStream * pad, ogg_packet * packet) -{ - guint8 *data = packet->packet; - - pad->granulerate_n = GST_READ_UINT32_LE (data + 36); - pad->granulerate_d = 1; - pad->granuleshift = 0; - GST_LOG ("sample rate: %d", pad->granulerate_n); - - pad->frame_size = GST_READ_UINT32_LE (packet->packet + 44); - pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 56) + 2; - - if (pad->granulerate_n == 0) - return FALSE; - - pad->caps = gst_caps_new_simple ("audio/x-celt", - "rate", G_TYPE_INT, pad->granulerate_n, NULL); - - return TRUE; -} - -/* kate */ - -static gboolean -setup_kate_mapper (GstOggStream * pad, ogg_packet * packet) -{ - guint8 *data = packet->packet; - const char *category; - - if (packet->bytes < 64) - return FALSE; - - pad->granulerate_n = GST_READ_UINT32_LE (data + 24); - pad->granulerate_d = GST_READ_UINT32_LE (data + 28); - pad->granuleshift = GST_READ_UINT8 (data + 15); - GST_LOG ("sample rate: %d", pad->granulerate_n); - - pad->n_header_packets = GST_READ_UINT8 (data + 11); - - if (pad->granulerate_n == 0) - return FALSE; - - category = (const char *) data + 48; - if (strcmp (category, "subtitles") == 0 || strcmp (category, "SUB") == 0 || - strcmp (category, "spu-subtitles") == 0 || - strcmp (category, "K-SPU") == 0) { - pad->caps = gst_caps_new_simple ("subtitle/x-kate", NULL); - } else { - pad->caps = gst_caps_new_simple ("application/x-kate", NULL); - } - - return TRUE; -} - - -/* *INDENT-OFF* */ -/* indent hates our freedoms */ -static const GstOggMap mappers[] = { - { - "\200theora", 7, 42, - "video/x-theora", - setup_theora_mapper, - granulepos_to_granule_theora, - granule_to_granulepos_default, - is_keyframe_theora, - is_header_theora, - packet_duration_constant - }, - { - "\001vorbis", 7, 22, - "audio/x-vorbis", - setup_vorbis_mapper, - granulepos_to_granule_default, - granule_to_granulepos_default, - is_keyframe_true, - is_header_vorbis, - packet_duration_vorbis - }, - { - "Speex", 5, 80, - "audio/x-speex", - setup_speex_mapper, - granulepos_to_granule_default, - granule_to_granulepos_default, - is_keyframe_true, - is_header_count, - packet_duration_constant - }, - { - "PCM ", 8, 0, - "audio/x-raw-int", - setup_pcm_mapper, - NULL, - NULL, - NULL, - is_header_count, - NULL - }, - { - "CMML\0\0\0\0", 8, 0, - "text/x-cmml", - setup_cmml_mapper, - NULL, - NULL, - NULL, - is_header_count, - NULL - }, - { - "Annodex", 7, 0, - "application/x-annodex", - setup_fishead_mapper, - granulepos_to_granule_default, - granule_to_granulepos_default, - NULL, - is_header_count, - NULL - }, - { - "fishead", 7, 64, - "application/octet-stream", - setup_fishead_mapper, - NULL, - NULL, - NULL, - is_header_true, - NULL - }, - { - "fLaC", 4, 0, - "audio/x-flac", - setup_fLaC_mapper, - granulepos_to_granule_default, - granule_to_granulepos_default, - is_keyframe_true, - is_header_fLaC, - NULL - }, - { - "\177FLAC", 5, 36, - "audio/x-flac", - setup_flac_mapper, - granulepos_to_granule_default, - granule_to_granulepos_default, - is_keyframe_true, - is_header_flac, - packet_duration_flac - }, - { - "AnxData", 7, 0, - "application/octet-stream", - NULL, - NULL, - NULL, - NULL, - NULL, - }, - { - "CELT ", 8, 0, - "audio/x-celt", - setup_celt_mapper, - granulepos_to_granule_default, - granule_to_granulepos_default, - NULL, - is_header_count, - packet_duration_constant - }, - { - "\200kate\0\0\0", 8, 0, - "text/x-kate", - setup_kate_mapper, - granulepos_to_granule_default, - granule_to_granulepos_default, - NULL, - is_header_count, - NULL - }, - { - "BBCD\0", 5, 13, - "video/x-dirac", - setup_dirac_mapper, - granulepos_to_granule_dirac, - granule_to_granulepos_dirac, - is_keyframe_dirac, - is_header_count, - packet_duration_constant - }, - { - "\001audio\0\0\0", 9, 53, - "application/x-ogm-audio", - setup_ogmaudio_mapper, - granulepos_to_granule_default, - granule_to_granulepos_default, - is_keyframe_true, - is_header_ogm, - packet_duration_ogm - }, - { - "\001video\0\0\0", 9, 53, - "application/x-ogm-video", - setup_ogmvideo_mapper, - granulepos_to_granule_default, - granule_to_granulepos_default, - NULL, - is_header_ogm, - packet_duration_constant - }, - { - "\001text\0\0\0", 9, 9, - "application/x-ogm-text", - setup_ogmtext_mapper, - granulepos_to_granule_default, - granule_to_granulepos_default, - is_keyframe_true, - is_header_ogm, - packet_duration_ogm - } -}; -/* *INDENT-ON* */ - -gboolean -gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet * packet) -{ - int i; - gboolean ret; - - for (i = 0; i < G_N_ELEMENTS (mappers); i++) { - if (packet->bytes >= mappers[i].min_packet_size && - packet->bytes >= mappers[i].id_length && - memcmp (packet->packet, mappers[i].id, mappers[i].id_length) == 0) { - - GST_DEBUG ("found mapper for '%s'", mappers[i].id); - - if (mappers[i].setup_func) - ret = mappers[i].setup_func (pad, packet); - else - continue; - - if (ret) { - GST_DEBUG ("got stream type %" GST_PTR_FORMAT, pad->caps); - pad->map = i; - return TRUE; - } else { - GST_WARNING ("mapper '%s' did not accept setup header", - mappers[i].media_type); - } - } - } - - return FALSE; -} diff --git a/ext/ogg/gstoggstream.h b/ext/ogg/gstoggstream.h deleted file mode 100644 index 78b55c06..00000000 --- a/ext/ogg/gstoggstream.h +++ /dev/null @@ -1,95 +0,0 @@ -/* GStreamer - * Copyright (C) 2009 David Schleef <ds@schleef.org> - * - * gstoggstream.h: header for GstOggStream - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_OGG_STREAM_H__ -#define __GST_OGG_STREAM_H__ - -#include <ogg/ogg.h> - -#include <gst/gst.h> - -G_BEGIN_DECLS - -typedef struct _GstOggStream GstOggStream; - -struct _GstOggStream -{ - ogg_stream_state stream; - - glong serialno; - GList *headers; - gboolean have_headers; - GList *queued; - - /* for oggparse */ - gboolean in_headers; - GList *unknown_pages; - - gint map; - gboolean is_skeleton; - gboolean have_fisbone; - gint granulerate_n; - gint granulerate_d; - guint32 preroll; - guint granuleshift; - gint n_header_packets; - gint n_header_packets_seen; - gint64 accumulated_granule; - gint frame_size; - - GstCaps *caps; - - /* vorbis stuff */ - int nln_increments[4]; - int nsn_increment; - int short_size; - int long_size; - int vorbis_log2_num_modes; - int vorbis_mode_sizes[256]; - int last_size; - /* theora stuff */ - gboolean theora_has_zero_keyoffset; - /* OGM stuff */ - gboolean is_ogm; - gboolean is_ogm_text; -}; - - -gboolean gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet *packet); -GstClockTime gst_ogg_stream_get_end_time_for_granulepos (GstOggStream *pad, - gint64 granulepos); -GstClockTime gst_ogg_stream_get_start_time_for_granulepos (GstOggStream *pad, - gint64 granulepos); -GstClockTime gst_ogg_stream_granule_to_time (GstOggStream *pad, gint64 granule); -gint64 gst_ogg_stream_granulepos_to_granule (GstOggStream * pad, gint64 granulepos); -gint64 gst_ogg_stream_granulepos_to_key_granule (GstOggStream * pad, gint64 granulepos); -gint64 gst_ogg_stream_granule_to_granulepos (GstOggStream * pad, gint64 granule, gint64 keyframe_granule); -GstClockTime gst_ogg_stream_get_packet_start_time (GstOggStream *pad, - ogg_packet *packet); -gboolean gst_ogg_stream_granulepos_is_key_frame (GstOggStream *pad, - gint64 granulepos); -gboolean gst_ogg_stream_packet_is_header (GstOggStream *pad, ogg_packet *packet); -gint64 gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet *packet); - - -G_END_DECLS - -#endif /* __GST_OGG_STREAM_H__ */ diff --git a/ext/ogg/gstogmparse.c b/ext/ogg/gstogmparse.c deleted file mode 100644 index 31dcd905..00000000 --- a/ext/ogg/gstogmparse.c +++ /dev/null @@ -1,972 +0,0 @@ -/* GStreamer OGM parsing - * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net> - * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <stdio.h> -#include <string.h> - -#include <gst/gst.h> -#include <gst/tag/tag.h> -#include <gst/riff/riff-media.h> -#include <gst/riff/riff-read.h> - -GST_DEBUG_CATEGORY_STATIC (gst_ogm_parse_debug); -#define GST_CAT_DEFAULT gst_ogm_parse_debug - -#define GST_TYPE_OGM_VIDEO_PARSE (gst_ogm_video_parse_get_type()) -#define GST_IS_OGM_VIDEO_PARSE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_OGM_VIDEO_PARSE)) - -#define GST_TYPE_OGM_AUDIO_PARSE (gst_ogm_audio_parse_get_type()) -#define GST_IS_OGM_AUDIO_PARSE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_OGM_AUDIO_PARSE)) - -#define GST_TYPE_OGM_TEXT_PARSE (gst_ogm_text_parse_get_type()) -#define GST_IS_OGM_TEXT_PARSE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_OGM_TEXT_PARSE)) - -#define GST_TYPE_OGM_PARSE (gst_ogm_parse_get_type()) -#define GST_OGM_PARSE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_OGM_PARSE, GstOgmParse)) -#define GST_OGM_PARSE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_OGM_PARSE, GstOgmParse)) -#define GST_IS_OGM_PARSE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_OGM_PARSE)) -#define GST_IS_OGM_PARSE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_OGM_PARSE)) -#define GST_OGM_PARSE_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_OGM_PARSE, GstOgmParseClass)) - -static const GstElementDetails gst_ogm_audio_parse_details = -GST_ELEMENT_DETAILS ("OGM audio stream parser", - "Codec/Decoder/Audio", - "parse an OGM audio header and stream", - "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>"); - -static const GstElementDetails gst_ogm_video_parse_details = -GST_ELEMENT_DETAILS ("OGM video stream parser", - "Codec/Decoder/Video", - "parse an OGM video header and stream", - "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>"); - -static const GstElementDetails gst_ogm_text_parse_details = -GST_ELEMENT_DETAILS ("OGM text stream parser", - "Codec/Decoder/Subtitle", - "parse an OGM text header and stream", - "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>"); - -typedef struct _stream_header_video -{ - gint32 width; - gint32 height; -} stream_header_video; - -typedef struct _stream_header_audio -{ - gint16 channels; - gint16 blockalign; - gint32 avgbytespersec; -} stream_header_audio; - -/* sizeof(stream_header) might differ due to structure packing and - * alignment differences on some architectures, so not using that */ -#define OGM_STREAM_HEADER_SIZE (8+4+4+8+8+4+4+4+8) - -typedef struct _stream_header -{ - gchar streamtype[8]; - gchar subtype[4 + 1]; - - /* size of the structure */ - gint32 size; - - /* in reference time */ - gint64 time_unit; - - gint64 samples_per_unit; - - /* in media time */ - gint32 default_len; - - gint32 buffersize; - gint32 bits_per_sample; - - union - { - stream_header_video video; - stream_header_audio audio; - /* text has no additional data */ - } s; -} stream_header; - -typedef struct _GstOgmParse -{ - GstElement element; - - /* pads */ - GstPad *srcpad, *sinkpad; - GstPadTemplate *srcpadtempl; - - /* we need to cache events that we receive before creating the source pad */ - GList *cached_events; - - /* audio or video */ - stream_header hdr; - - /* expected next granulepos (used for timestamp guessing) */ - guint64 next_granulepos; -} GstOgmParse; - -typedef struct _GstOgmParseClass -{ - GstElementClass parent_class; -} GstOgmParseClass; - -static GstStaticPadTemplate sink_factory_video = -GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("application/x-ogm-video")); -static GstStaticPadTemplate sink_factory_audio = -GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("application/x-ogm-audio")); -static GstStaticPadTemplate sink_factory_text = -GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("application/x-ogm-text")); -static GstPadTemplate *video_src_templ, *audio_src_templ, *text_src_templ; - -static GType gst_ogm_audio_parse_get_type (void); -static GType gst_ogm_video_parse_get_type (void); -static GType gst_ogm_text_parse_get_type (void); -static GType gst_ogm_parse_get_type (void); - -static void gst_ogm_audio_parse_base_init (GstOgmParseClass * klass); -static void gst_ogm_video_parse_base_init (GstOgmParseClass * klass); -static void gst_ogm_text_parse_base_init (GstOgmParseClass * klass); -static void gst_ogm_parse_class_init (GstOgmParseClass * klass); -static void gst_ogm_parse_init (GstOgmParse * ogm); -static void gst_ogm_video_parse_init (GstOgmParse * ogm); -static void gst_ogm_audio_parse_init (GstOgmParse * ogm); -static void gst_ogm_text_parse_init (GstOgmParse * ogm); - -static const GstQueryType *gst_ogm_parse_get_sink_querytypes (GstPad * pad); -static gboolean gst_ogm_parse_sink_event (GstPad * pad, GstEvent * event); -static gboolean gst_ogm_parse_sink_query (GstPad * pad, GstQuery * query); -static gboolean gst_ogm_parse_sink_convert (GstPad * pad, GstFormat src_format, - gint64 src_value, GstFormat * dest_format, gint64 * dest_value); - -static GstFlowReturn gst_ogm_parse_chain (GstPad * pad, GstBuffer * buffer); - -static GstStateChangeReturn gst_ogm_parse_change_state (GstElement * element, - GstStateChange transition); - -static GstElementClass *parent_class = NULL; - -static GType -gst_ogm_parse_get_type (void) -{ - static GType ogm_parse_type = 0; - - if (!ogm_parse_type) { - static const GTypeInfo ogm_parse_info = { - sizeof (GstOgmParseClass), - NULL, - NULL, - (GClassInitFunc) gst_ogm_parse_class_init, - NULL, - NULL, - sizeof (GstOgmParse), - 0, - (GInstanceInitFunc) gst_ogm_parse_init, - }; - - ogm_parse_type = - g_type_register_static (GST_TYPE_ELEMENT, - "GstOgmParse", &ogm_parse_info, 0); - } - - return ogm_parse_type; -} - -static GType -gst_ogm_audio_parse_get_type (void) -{ - static GType ogm_audio_parse_type = 0; - - if (!ogm_audio_parse_type) { - static const GTypeInfo ogm_audio_parse_info = { - sizeof (GstOgmParseClass), - (GBaseInitFunc) gst_ogm_audio_parse_base_init, - NULL, - NULL, - NULL, - NULL, - sizeof (GstOgmParse), - 0, - (GInstanceInitFunc) gst_ogm_audio_parse_init, - }; - - ogm_audio_parse_type = - g_type_register_static (GST_TYPE_OGM_PARSE, - "GstOgmAudioParse", &ogm_audio_parse_info, 0); - } - - return ogm_audio_parse_type; -} - -static GType -gst_ogm_video_parse_get_type (void) -{ - static GType ogm_video_parse_type = 0; - - if (!ogm_video_parse_type) { - static const GTypeInfo ogm_video_parse_info = { - sizeof (GstOgmParseClass), - (GBaseInitFunc) gst_ogm_video_parse_base_init, - NULL, - NULL, - NULL, - NULL, - sizeof (GstOgmParse), - 0, - (GInstanceInitFunc) gst_ogm_video_parse_init, - }; - - ogm_video_parse_type = - g_type_register_static (GST_TYPE_OGM_PARSE, - "GstOgmVideoParse", &ogm_video_parse_info, 0); - } - - return ogm_video_parse_type; -} - -static GType -gst_ogm_text_parse_get_type (void) -{ - static GType ogm_text_parse_type = 0; - - if (!ogm_text_parse_type) { - static const GTypeInfo ogm_text_parse_info = { - sizeof (GstOgmParseClass), - (GBaseInitFunc) gst_ogm_text_parse_base_init, - NULL, - NULL, - NULL, - NULL, - sizeof (GstOgmParse), - 0, - (GInstanceInitFunc) gst_ogm_text_parse_init, - }; - - ogm_text_parse_type = - g_type_register_static (GST_TYPE_OGM_PARSE, - "GstOgmTextParse", &ogm_text_parse_info, 0); - } - - return ogm_text_parse_type; -} - -static void -gst_ogm_audio_parse_base_init (GstOgmParseClass * klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - GstCaps *caps = gst_riff_create_audio_template_caps (); - - gst_element_class_set_details (element_class, &gst_ogm_audio_parse_details); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_factory_audio)); - audio_src_templ = gst_pad_template_new ("src", - GST_PAD_SRC, GST_PAD_SOMETIMES, caps); - gst_element_class_add_pad_template (element_class, audio_src_templ); -} - -static void -gst_ogm_video_parse_base_init (GstOgmParseClass * klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - GstCaps *caps = gst_riff_create_video_template_caps (); - - gst_element_class_set_details (element_class, &gst_ogm_video_parse_details); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_factory_video)); - video_src_templ = gst_pad_template_new ("src", - GST_PAD_SRC, GST_PAD_SOMETIMES, caps); - gst_element_class_add_pad_template (element_class, video_src_templ); -} - -static void -gst_ogm_text_parse_base_init (GstOgmParseClass * klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - GstCaps *caps = gst_caps_new_simple ("text/plain", NULL, NULL); - - gst_element_class_set_details (element_class, &gst_ogm_text_parse_details); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_factory_text)); - text_src_templ = gst_pad_template_new ("src", - GST_PAD_SRC, GST_PAD_SOMETIMES, caps); - gst_element_class_add_pad_template (element_class, text_src_templ); -} - -static void -gst_ogm_parse_class_init (GstOgmParseClass * klass) -{ - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - - parent_class = g_type_class_peek_parent (klass); - - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_ogm_parse_change_state); -} - -static void -gst_ogm_parse_init (GstOgmParse * ogm) -{ - memset (&ogm->hdr, 0, sizeof (ogm->hdr)); - ogm->next_granulepos = 0; - ogm->srcpad = NULL; - ogm->cached_events = NULL; -} - -static void -gst_ogm_audio_parse_init (GstOgmParse * ogm) -{ - ogm->sinkpad = gst_pad_new_from_static_template (&sink_factory_audio, "sink"); - gst_pad_set_query_function (ogm->sinkpad, - GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_query)); - gst_pad_set_chain_function (ogm->sinkpad, - GST_DEBUG_FUNCPTR (gst_ogm_parse_chain)); - gst_pad_set_event_function (ogm->sinkpad, - GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_event)); - gst_element_add_pad (GST_ELEMENT (ogm), ogm->sinkpad); - - ogm->srcpad = NULL; - ogm->srcpadtempl = audio_src_templ; -} - -static void -gst_ogm_video_parse_init (GstOgmParse * ogm) -{ - ogm->sinkpad = gst_pad_new_from_static_template (&sink_factory_video, "sink"); - gst_pad_set_query_function (ogm->sinkpad, - GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_query)); - gst_pad_set_chain_function (ogm->sinkpad, - GST_DEBUG_FUNCPTR (gst_ogm_parse_chain)); - gst_pad_set_event_function (ogm->sinkpad, - GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_event)); - gst_element_add_pad (GST_ELEMENT (ogm), ogm->sinkpad); - - ogm->srcpad = NULL; - ogm->srcpadtempl = video_src_templ; -} - -static void -gst_ogm_text_parse_init (GstOgmParse * ogm) -{ - ogm->sinkpad = gst_pad_new_from_static_template (&sink_factory_text, "sink"); - gst_pad_set_query_type_function (ogm->sinkpad, - gst_ogm_parse_get_sink_querytypes); - gst_pad_set_query_function (ogm->sinkpad, - GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_query)); - gst_pad_set_chain_function (ogm->sinkpad, - GST_DEBUG_FUNCPTR (gst_ogm_parse_chain)); - gst_pad_set_event_function (ogm->sinkpad, - GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_event)); - gst_element_add_pad (GST_ELEMENT (ogm), ogm->sinkpad); - - ogm->srcpad = NULL; - ogm->srcpadtempl = text_src_templ; -} - -static const GstQueryType * -gst_ogm_parse_get_sink_querytypes (GstPad * pad) -{ - static const GstQueryType types[] = { - GST_QUERY_POSITION, - 0 - }; - - return types; -} - -static gboolean -gst_ogm_parse_sink_convert (GstPad * pad, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = FALSE; - GstOgmParse *ogm = GST_OGM_PARSE (gst_pad_get_parent (pad)); - - switch (src_format) { - case GST_FORMAT_DEFAULT: - switch (*dest_format) { - case GST_FORMAT_TIME: - switch (ogm->hdr.streamtype[0]) { - case 'a': - *dest_value = GST_SECOND * src_value / ogm->hdr.samples_per_unit; - res = TRUE; - break; - case 'v': - case 't': - *dest_value = (GST_SECOND / 10000000) * - ogm->hdr.time_unit * src_value; - res = TRUE; - break; - default: - break; - } - break; - default: - break; - } - break; - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_DEFAULT: - switch (ogm->hdr.streamtype[0]) { - case 'a': - *dest_value = ogm->hdr.samples_per_unit * src_value / GST_SECOND; - res = TRUE; - break; - case 'v': - case 't': - *dest_value = src_value / - ((GST_SECOND / 10000000) * ogm->hdr.time_unit); - res = TRUE; - break; - default: - break; - } - break; - default: - break; - } - break; - default: - break; - } - - gst_object_unref (ogm); - return res; -} - -static gboolean -gst_ogm_parse_sink_query (GstPad * pad, GstQuery * query) -{ - GstOgmParse *ogm = GST_OGM_PARSE (gst_pad_get_parent (pad)); - GstFormat format; - gboolean res = FALSE; - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - { - gint64 val; - - gst_query_parse_position (query, &format, NULL); - - if (format != GST_FORMAT_DEFAULT && format != GST_FORMAT_TIME) - break; - - if ((res = gst_ogm_parse_sink_convert (pad, - GST_FORMAT_DEFAULT, ogm->next_granulepos, &format, &val))) { - /* don't know the total length here.. */ - gst_query_set_position (query, format, val); - } - break; - } - case GST_QUERY_CONVERT: - { - GstFormat src_fmt, dest_fmt; - gint64 src_val, dest_val; - - /* peel off input */ - gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); - if ((res = gst_ogm_parse_sink_convert (pad, src_fmt, src_val, - &dest_fmt, &dest_val))) { - gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); - } - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } - - gst_object_unref (ogm); - return res; -} - -static GstFlowReturn -gst_ogm_parse_stream_header (GstOgmParse * ogm, const guint8 * data, guint size) -{ - GstCaps *caps = NULL; - - /* stream header */ - if (size < OGM_STREAM_HEADER_SIZE) - goto buffer_too_small; - - if (!memcmp (data, "video\000\000\000", 8)) { - ogm->hdr.s.video.width = GST_READ_UINT32_LE (&data[44]); - ogm->hdr.s.video.height = GST_READ_UINT32_LE (&data[48]); - } else if (!memcmp (data, "audio\000\000\000", 8)) { - ogm->hdr.s.audio.channels = GST_READ_UINT32_LE (&data[44]); - ogm->hdr.s.audio.blockalign = GST_READ_UINT32_LE (&data[46]); - ogm->hdr.s.audio.avgbytespersec = GST_READ_UINT32_LE (&data[48]); - } else if (!memcmp (data, "text\000\000\000\000", 8)) { - /* nothing here */ - } else { - goto cannot_decode; - } - memcpy (ogm->hdr.streamtype, &data[0], 8); - memcpy (ogm->hdr.subtype, &data[8], 4); - ogm->hdr.subtype[4] = '\0'; - ogm->hdr.size = GST_READ_UINT32_LE (&data[12]); - ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[16]); - ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[24]); - ogm->hdr.default_len = GST_READ_UINT32_LE (&data[32]); - ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[36]); - ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[40]); - - switch (ogm->hdr.streamtype[0]) { - case 'a':{ - guint codec_id = 0; - - if (sscanf (ogm->hdr.subtype, "%04x", &codec_id) != 1) { - GST_WARNING_OBJECT (ogm, "cannot parse subtype %s", ogm->hdr.subtype); - } - - caps = - gst_riff_create_audio_caps (codec_id, NULL, NULL, NULL, NULL, NULL); - - if (caps == NULL) { - GST_WARNING_OBJECT (ogm, "no audio caps for codec %u found", codec_id); - caps = gst_caps_new_simple ("audio/x-ogm-unknown", "codec_id", - G_TYPE_INT, (gint) codec_id, NULL); - } - - gst_caps_set_simple (caps, - "channels", G_TYPE_INT, ogm->hdr.s.audio.channels, - "rate", G_TYPE_INT, ogm->hdr.samples_per_unit, NULL); - - GST_LOG_OBJECT (ogm, "Type: %s, subtype: 0x%04x, channels: %d, " - "samplerate: %d, blockalign: %d, bps: %d, caps = %" GST_PTR_FORMAT, - ogm->hdr.streamtype, codec_id, ogm->hdr.s.audio.channels, - (gint) ogm->hdr.samples_per_unit, ogm->hdr.s.audio.blockalign, - ogm->hdr.s.audio.avgbytespersec, caps); - break; - } - case 'v':{ - guint32 fourcc; - gint time_unit; - - fourcc = GST_MAKE_FOURCC (ogm->hdr.subtype[0], - ogm->hdr.subtype[1], ogm->hdr.subtype[2], ogm->hdr.subtype[3]); - - caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL); - - if (caps == NULL) { - GST_WARNING_OBJECT (ogm, "could not find video caps for fourcc %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc)); - caps = gst_caps_new_simple ("video/x-ogm-unknown", "fourcc", - GST_TYPE_FOURCC, fourcc, NULL); - break; - } - - GST_LOG_OBJECT (ogm, "Type: %s, subtype: %" GST_FOURCC_FORMAT - ", size: %dx%d, timeunit: %" G_GINT64_FORMAT - " (fps: %lf), s/u: %" G_GINT64_FORMAT ", " - "def.len: %d, bufsize: %d, bps: %d, caps = %" GST_PTR_FORMAT, - ogm->hdr.streamtype, GST_FOURCC_ARGS (fourcc), - ogm->hdr.s.video.width, ogm->hdr.s.video.height, - ogm->hdr.time_unit, 10000000. / ogm->hdr.time_unit, - ogm->hdr.samples_per_unit, ogm->hdr.default_len, - ogm->hdr.buffersize, ogm->hdr.bits_per_sample, caps); - - /* GST_TYPE_FRACTION contains gint */ - if (ogm->hdr.time_unit > G_MAXINT || ogm->hdr.time_unit < G_MININT) - GST_WARNING_OBJECT (ogm, "timeunit is out of range"); - - time_unit = (gint) CLAMP (ogm->hdr.time_unit, G_MININT, G_MAXINT); - gst_caps_set_simple (caps, - "width", G_TYPE_INT, ogm->hdr.s.video.width, - "height", G_TYPE_INT, ogm->hdr.s.video.height, - "framerate", GST_TYPE_FRACTION, 10000000, time_unit, NULL); - break; - } - case 't':{ - GST_LOG_OBJECT (ogm, "Type: %s, s/u: %" G_GINT64_FORMAT - ", timeunit=%" G_GINT64_FORMAT, - ogm->hdr.streamtype, ogm->hdr.samples_per_unit, ogm->hdr.time_unit); - caps = gst_caps_new_simple ("text/plain", NULL); - break; - } - default: - g_assert_not_reached (); - } - - if (caps == NULL) - goto cannot_decode; - - if (ogm->srcpad) { - GstCaps *current_caps = GST_PAD_CAPS (ogm->srcpad); - - if (current_caps && caps && !gst_caps_is_equal (current_caps, caps)) { - GST_WARNING_OBJECT (ogm, "Already an existing pad %s:%s", - GST_DEBUG_PAD_NAME (ogm->srcpad)); - gst_pad_set_active (ogm->srcpad, FALSE); - gst_element_remove_pad (GST_ELEMENT (ogm), ogm->srcpad); - ogm->srcpad = NULL; - } else { - GST_DEBUG_OBJECT (ogm, "Existing pad has the same caps, do nothing"); - } - } - - if (ogm->srcpad == NULL) { - GList *l, *cached_events; - - ogm->srcpad = gst_pad_new_from_template (ogm->srcpadtempl, "src"); - gst_pad_use_fixed_caps (ogm->srcpad); - gst_pad_set_caps (ogm->srcpad, caps); - gst_pad_set_active (ogm->srcpad, TRUE); - gst_element_add_pad (GST_ELEMENT (ogm), ogm->srcpad); - GST_INFO_OBJECT (ogm, "Added pad %s:%s with caps %" GST_PTR_FORMAT, - GST_DEBUG_PAD_NAME (ogm->srcpad), caps); - - GST_OBJECT_LOCK (ogm); - cached_events = ogm->cached_events; - ogm->cached_events = NULL; - GST_OBJECT_UNLOCK (ogm); - - for (l = cached_events; l; l = l->next) { - GstEvent *event = GST_EVENT_CAST (l->data); - - GST_DEBUG_OBJECT (ogm, "Pushing cached event %" GST_PTR_FORMAT, event); - gst_pad_push_event (ogm->srcpad, event); - } - g_list_free (cached_events); - - { - GstTagList *tags; - - tags = gst_tag_list_new (); - gst_tag_list_add (tags, GST_TAG_MERGE_APPEND, GST_TAG_SUBTITLE_CODEC, - "Ogm", NULL); - gst_element_found_tags_for_pad (GST_ELEMENT (ogm), ogm->srcpad, tags); - } - } - - gst_caps_unref (caps); - - return GST_FLOW_OK; - -/* ERRORS */ -buffer_too_small: - { - GST_ELEMENT_ERROR (ogm, STREAM, WRONG_TYPE, ("Buffer too small"), (NULL)); - return GST_FLOW_ERROR; - } -cannot_decode: - { - GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL), ("unknown ogm format")); - return GST_FLOW_ERROR; - } -} - -static GstFlowReturn -gst_ogm_parse_comment_packet (GstOgmParse * ogm, GstBuffer * buf) -{ - GstFlowReturn ret; - - if (ogm->srcpad == NULL) { - GST_DEBUG ("no source pad"); - return GST_FLOW_WRONG_STATE; - } - - /* if this is not a subtitle stream, push the vorbiscomment packet - * on downstream, the respective decoder will handle it; if it is - * a subtitle stream, we will have to handle the comment ourself */ - if (ogm->hdr.streamtype[0] == 't') { - GstTagList *tags; - - tags = gst_tag_list_from_vorbiscomment_buffer (buf, - (guint8 *) "\003vorbis", 7, NULL); - - if (tags) { - GST_DEBUG_OBJECT (ogm, "tags = %" GST_PTR_FORMAT, tags); - gst_element_found_tags_for_pad (GST_ELEMENT (ogm), ogm->srcpad, tags); - } else { - GST_DEBUG_OBJECT (ogm, "failed to extract tags from vorbis comment"); - } - /* do not push packet downstream, just let parent unref it */ - ret = GST_FLOW_OK; - } else { - buf = gst_buffer_copy (buf); - gst_buffer_set_caps (buf, GST_PAD_CAPS (ogm->srcpad)); - ret = gst_pad_push (ogm->srcpad, buf); - } - - return ret; -} - -static void -gst_ogm_text_parse_strip_trailing_zeroes (GstOgmParse * ogm, GstBuffer * buf) -{ - const guint8 *data; - guint size; - - g_assert (gst_buffer_is_metadata_writable (buf)); - - /* zeroes are not valid UTF-8 characters, so strip them from output */ - data = GST_BUFFER_DATA (buf); - size = GST_BUFFER_SIZE (buf); - while (size > 0 && data[size - 1] == '\0') { - --size; - } - - GST_BUFFER_SIZE (buf) = size; -} - -static GstFlowReturn -gst_ogm_parse_data_packet (GstOgmParse * ogm, GstBuffer * buf) -{ - GstFlowReturn ret; - const guint8 *data; - GstBuffer *sbuf; - gboolean keyframe; - guint size, len, n, xsize = 0; - - data = GST_BUFFER_DATA (buf); - size = GST_BUFFER_SIZE (buf); - - if ((data[0] & 0x01) != 0) - goto invalid_startcode; - - /* data - push on */ - len = ((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1); - keyframe = (((data[0] & 0x08) >> 3) != 0); - - if ((1 + len) > size) - goto buffer_too_small; - - for (n = len; n > 0; n--) { - xsize = (xsize << 8) | data[n]; - } - - GST_LOG_OBJECT (ogm, "[0x%02x] samples: %d, hdrbytes: %d, datasize: %d", - data[0], xsize, len, size - len - 1); - - sbuf = gst_buffer_create_sub (buf, len + 1, size - len - 1); - - if (GST_BUFFER_OFFSET_END_IS_VALID (buf)) - ogm->next_granulepos = GST_BUFFER_OFFSET_END (buf); - - switch (ogm->hdr.streamtype[0]) { - case 't': - case 'v':{ - GstClockTime ts, next_ts; - guint samples; - - samples = (ogm->hdr.streamtype[0] == 'v') ? 1 : xsize; - - if (!keyframe) { - GST_BUFFER_FLAG_SET (sbuf, GST_BUFFER_FLAG_DELTA_UNIT); - } - - /* shouldn't this be granulepos - samples? (tpm) */ - ts = gst_util_uint64_scale (ogm->next_granulepos, - ogm->hdr.time_unit * GST_SECOND, 10000000); - next_ts = gst_util_uint64_scale (ogm->next_granulepos + samples, - ogm->hdr.time_unit * GST_SECOND, 10000000); - - GST_BUFFER_TIMESTAMP (sbuf) = ts; - GST_BUFFER_DURATION (sbuf) = next_ts - ts; - - ogm->next_granulepos += samples; - - if (ogm->hdr.streamtype[0] == 't') { - gst_ogm_text_parse_strip_trailing_zeroes (ogm, sbuf); - } - break; - } - case 'a':{ - GstClockTime ts, next_ts; - - /* shouldn't this be granulepos - samples? (tpm) */ - ts = gst_util_uint64_scale_int (ogm->next_granulepos, - GST_SECOND, ogm->hdr.samples_per_unit); - next_ts = gst_util_uint64_scale_int (ogm->next_granulepos + xsize, - GST_SECOND, ogm->hdr.samples_per_unit); - - GST_BUFFER_TIMESTAMP (sbuf) = ts; - GST_BUFFER_DURATION (sbuf) = next_ts - ts; - - ogm->next_granulepos += xsize; - break; - } - default: - g_assert_not_reached (); - break; - } - - if (ogm->srcpad) { - gst_buffer_set_caps (sbuf, GST_PAD_CAPS (ogm->srcpad)); - GST_LOG_OBJECT (ogm, "Pushing buffer with ts=%" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (sbuf))); - ret = gst_pad_push (ogm->srcpad, sbuf); - if (ret != GST_FLOW_OK) { - GST_DEBUG_OBJECT (ogm, "Flow on %s:%s = %s", - GST_DEBUG_PAD_NAME (ogm->srcpad), gst_flow_get_name (ret)); - } - } else { - ret = GST_FLOW_WRONG_STATE; - } - - return ret; - -/* ERRORS */ -invalid_startcode: - { - GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL), - ("unexpected packet startcode 0x%02x", data[0])); - return GST_FLOW_ERROR; - } -buffer_too_small: - { - GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL), - ("buffer too small, len+1=%u, size=%u", len + 1, size)); - return GST_FLOW_ERROR; - } -} - -static GstFlowReturn -gst_ogm_parse_chain (GstPad * pad, GstBuffer * buf) -{ - GstFlowReturn ret = GST_FLOW_OK; - GstOgmParse *ogm = GST_OGM_PARSE (GST_PAD_PARENT (pad)); - guint8 *data = GST_BUFFER_DATA (buf); - guint size = GST_BUFFER_SIZE (buf); - - if (size < 1) - goto buffer_too_small; - - GST_LOG_OBJECT (ogm, "Packet with start code 0x%02x", data[0]); - - switch (data[0]) { - case 0x01:{ - ret = gst_ogm_parse_stream_header (ogm, data + 1, size - 1); - break; - } - case 0x03:{ - ret = gst_ogm_parse_comment_packet (ogm, buf); - break; - } - default:{ - ret = gst_ogm_parse_data_packet (ogm, buf); - break; - } - } - - gst_buffer_unref (buf); - - if (ret != GST_FLOW_OK) { - GST_DEBUG_OBJECT (ogm, "Flow: %s", gst_flow_get_name (ret)); - } - - return ret; - -/* ERRORS */ -buffer_too_small: - { - GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL), ("buffer too small")); - gst_buffer_unref (buf); - return GST_FLOW_ERROR; - } -} - -static gboolean -gst_ogm_parse_sink_event (GstPad * pad, GstEvent * event) -{ - GstOgmParse *ogm = GST_OGM_PARSE (gst_pad_get_parent (pad)); - gboolean res; - - GST_LOG_OBJECT (ogm, "processing %s event", GST_EVENT_TYPE_NAME (event)); - - GST_OBJECT_LOCK (ogm); - if (ogm->srcpad == NULL) { - ogm->cached_events = g_list_append (ogm->cached_events, event); - GST_OBJECT_UNLOCK (ogm); - res = TRUE; - } else { - GST_OBJECT_UNLOCK (ogm); - res = gst_pad_event_default (pad, event); - } - - gst_object_unref (ogm); - return res; -} - -static GstStateChangeReturn -gst_ogm_parse_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret; - GstOgmParse *ogm = GST_OGM_PARSE (element); - - ret = parent_class->change_state (element, transition); - if (ret != GST_STATE_CHANGE_SUCCESS) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - if (ogm->srcpad) { - gst_pad_set_active (ogm->srcpad, FALSE); - gst_element_remove_pad (element, ogm->srcpad); - ogm->srcpad = NULL; - } - memset (&ogm->hdr, 0, sizeof (ogm->hdr)); - ogm->next_granulepos = 0; - g_list_foreach (ogm->cached_events, (GFunc) gst_mini_object_unref, NULL); - g_list_free (ogm->cached_events); - ogm->cached_events = NULL; - break; - default: - break; - } - - return ret; -} - -gboolean -gst_ogm_parse_plugin_init (GstPlugin * plugin) -{ - gst_riff_init (); - - GST_DEBUG_CATEGORY_INIT (gst_ogm_parse_debug, "ogmparse", 0, "ogm parser"); - - return gst_element_register (plugin, "ogmaudioparse", GST_RANK_PRIMARY, - GST_TYPE_OGM_AUDIO_PARSE) && - gst_element_register (plugin, "ogmvideoparse", GST_RANK_PRIMARY, - GST_TYPE_OGM_VIDEO_PARSE) && - gst_element_register (plugin, "ogmtextparse", GST_RANK_PRIMARY, - GST_TYPE_OGM_TEXT_PARSE); -} diff --git a/ext/ogg/vorbis_parse.c b/ext/ogg/vorbis_parse.c deleted file mode 100644 index f9ba746d..00000000 --- a/ext/ogg/vorbis_parse.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - This file borrowed from liboggz - */ -/* - Copyright (C) 2003 Commonwealth Scientific and Industrial Research - Organisation (CSIRO) Australia - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - - Neither the name of CSIRO Australia nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -/* - * oggz_auto.c - * - * Conrad Parker <conrad@annodex.net> - */ - -#include "config.h" - -#include <stdlib.h> -#include <string.h> - -#include "gstoggstream.h" - -/* - * Vorbis packets can be short or long, and each packet overlaps the previous - * and next packets. The granulepos of a packet is always the last sample - * that is completely decoded at the end of decoding that packet - i.e. the - * last packet before the first overlapping packet. If the sizes of packets - * are 's' and 'l', then the increment will depend on the previous and next - * packet types: - * v prev<<1 | next - * lll: l/2 3 - * lls: 3l/4 - s/4 2 - * lsl: s/2 - * lss: s/2 - * sll: l/4 + s/4 1 - * sls: l/2 0 - * ssl: s/2 - * sss: s/2 - * - * The previous and next packet types can be inferred from the current packet - * (additional information is not required) - * - * The two blocksizes can be determined from the first header packet, by reading - * byte 28. 1 << (packet[28] >> 4) == long_size. - * 1 << (packet[28] & 0xF) == short_size. - * - * (see http://xiph.org/vorbis/doc/Vorbis_I_spec.html for specification) - */ - - -void -parse_vorbis_header_packet (GstOggStream * pad, ogg_packet * packet) -{ - /* - * on the first (b_o_s) packet, determine the long and short sizes, - * and then calculate l/2, l/4 - s/4, 3 * l/4 - s/4, l/2 - s/2 and s/2 - */ - int short_size; - int long_size; - - long_size = 1 << (packet->packet[28] >> 4); - short_size = 1 << (packet->packet[28] & 0xF); - - pad->nln_increments[3] = long_size >> 1; - pad->nln_increments[2] = 3 * (long_size >> 2) - (short_size >> 2); - pad->nln_increments[1] = (long_size >> 2) + (short_size >> 2); - pad->nln_increments[0] = pad->nln_increments[3]; - pad->short_size = short_size; - pad->long_size = long_size; - pad->nsn_increment = short_size >> 1; -} - -void -parse_vorbis_setup_packet (GstOggStream * pad, ogg_packet * op) -{ - /* - * the code pages, a whole bunch of other fairly useless stuff, AND, - * RIGHT AT THE END (of a bunch of variable-length compressed rubbish that - * basically has only one actual set of values that everyone uses BUT YOU - * CAN'T BE SURE OF THAT, OH NO YOU CAN'T) is the only piece of data that's - * actually useful to us - the packet modes (because it's inconceivable to - * think people might want _just that_ and nothing else, you know, for - * seeking and stuff). - * - * Fortunately, because of the mandate that non-used bits must be zero - * at the end of the packet, we might be able to sneakily work backwards - * and find out the information we need (namely a mapping of modes to - * packet sizes) - */ - unsigned char *current_pos = &op->packet[op->bytes - 1]; - int offset; - int size; - int size_check; - int *mode_size_ptr; - int i; - int ii; - - /* - * This is the format of the mode data at the end of the packet for all - * Vorbis Version 1 : - * - * [ 6:number_of_modes ] - * [ 1:size | 16:window_type(0) | 16:transform_type(0) | 8:mapping ] - * [ 1:size | 16:window_type(0) | 16:transform_type(0) | 8:mapping ] - * [ 1:size | 16:window_type(0) | 16:transform_type(0) | 8:mapping ] - * [ 1:framing(1) ] - * - * e.g.: - * - * <- - * 0 0 0 0 0 1 0 0 - * 0 0 1 0 0 0 0 0 - * 0 0 1 0 0 0 0 0 - * 0 0 1|0 0 0 0 0 - * 0 0 0 0|0|0 0 0 - * 0 0 0 0 0 0 0 0 - * 0 0 0 0|0 0 0 0 - * 0 0 0 0 0 0 0 0 - * 0 0 0 0|0 0 0 0 - * 0 0 0|1|0 0 0 0 | - * 0 0 0 0 0 0 0 0 V - * 0 0 0|0 0 0 0 0 - * 0 0 0 0 0 0 0 0 - * 0 0 1|0 0 0 0 0 - * 0 0|1|0 0 0 0 0 - * - * - * i.e. each entry is an important bit, 32 bits of 0, 8 bits of blah, a - * bit of 1. - * Let's find our last 1 bit first. - * - */ - - size = 0; - - offset = 8; - while (!((1 << --offset) & *current_pos)) { - if (offset == 0) { - offset = 8; - current_pos -= 1; - } - } - - while (1) { - - /* - * from current_pos-5:(offset+1) to current_pos-1:(offset+1) should - * be zero - */ - offset = (offset + 7) % 8; - if (offset == 7) - current_pos -= 1; - - if (((current_pos[-5] & ~((1 << (offset + 1)) - 1)) != 0) - || - current_pos[-4] != 0 - || - current_pos[-3] != 0 - || - current_pos[-2] != 0 - || ((current_pos[-1] & ((1 << (offset + 1)) - 1)) != 0) - ) { - break; - } - - size += 1; - - current_pos -= 5; - - } - - /* Give ourselves a chance to recover if we went back too far by using - * the size check. */ - for (ii = 0; ii < 2; ii++) { - if (offset > 4) { - size_check = (current_pos[0] >> (offset - 5)) & 0x3F; - } else { - /* mask part of byte from current_pos */ - size_check = (current_pos[0] & ((1 << (offset + 1)) - 1)); - /* shift to appropriate position */ - size_check <<= (5 - offset); - /* or in part of byte from current_pos - 1 */ - size_check |= (current_pos[-1] & ~((1 << (offset + 3)) - 1)) >> - (offset + 3); - } - - size_check += 1; - if (size_check == size) { - break; - } - offset = (offset + 1) % 8; - if (offset == 0) - current_pos += 1; - current_pos += 5; - size -= 1; - } - - /* Store mode size information in our info struct */ - i = -1; - while ((1 << (++i)) < size); - pad->vorbis_log2_num_modes = i; - - mode_size_ptr = pad->vorbis_mode_sizes; - - for (i = 0; i < size; i++) { - offset = (offset + 1) % 8; - if (offset == 0) - current_pos += 1; - *mode_size_ptr++ = (current_pos[0] >> offset) & 0x1; - current_pos += 5; - } - -} diff --git a/ext/pango/Makefile.am b/ext/pango/Makefile.am deleted file mode 100644 index 197ed338..00000000 --- a/ext/pango/Makefile.am +++ /dev/null @@ -1,28 +0,0 @@ -plugin_LTLIBRARIES = libgstpango.la - -noinst_HEADERS = \ - gstclockoverlay.h \ - gsttextoverlay.h \ - gsttextrender.h \ - gsttimeoverlay.h - -libgstpango_la_SOURCES = \ - gstclockoverlay.c \ - gsttextoverlay.c \ - gsttextrender.c \ - gsttimeoverlay.c - -libgstpango_la_CFLAGS = \ - $(GST_PLUGINS_BASE_CFLAGS) \ - $(GST_BASE_CFLAGS) \ - $(GST_CFLAGS) \ - $(PANGO_CFLAGS) -libgstpango_la_LIBADD = \ - $(GST_PLUGINS_BASE_LIBS) \ - $(top_builddir)/gst-libs/gst/video/libgstvideo-$(GST_MAJORMINOR).la \ - $(GST_BASE_LIBS) \ - $(GST_LIBS) \ - $(PANGO_LIBS) -libgstpango_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstpango_la_LIBTOOLFLAGS = --tag=disable-static - diff --git a/ext/pango/gstclockoverlay.c b/ext/pango/gstclockoverlay.c deleted file mode 100644 index 5db475bc..00000000 --- a/ext/pango/gstclockoverlay.c +++ /dev/null @@ -1,240 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * Copyright (C) <2005> Tim-Philipp Müller <tim@centricular.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-clockoverlay - * @see_also: #GstTextOverlay, #GstTimeOverlay - * - * This element overlays the current clock time on top of a video - * stream. You can position the text and configure the font details - * using the properties of the #GstTextOverlay class. By default, the - * time is displayed in the top left corner of the picture, with some - * padding to the left and to the top. - * - * <refsect2> - * <title>Example launch lines</title> - * |[ - * gst-launch -v videotestsrc ! clockoverlay ! xvimagesink - * ]| Display the current time in the top left corner of the video picture - * |[ - * gst-launch -v videotestsrc ! clockoverlay halign=right valign=bottom text="Edge City" shaded-background=true ! ffmpegcolorspace ! ximagesink - * ]| Another pipeline that displays the current time with some leading - * text in the bottom right corner of the video picture, with the background - * of the text being shaded in order to make it more legible on top of a - * bright video background. - * </refsect2> - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <gstclockoverlay.h> -#include <gst/video/video.h> -#include <time.h> - - -#define DEFAULT_PROP_TIMEFORMAT "%H:%M:%S" - -enum -{ - PROP_0, - PROP_TIMEFORMAT, - PROP_LAST -}; - -GST_BOILERPLATE (GstClockOverlay, gst_clock_overlay, GstTextOverlay, - GST_TYPE_TEXT_OVERLAY); - -static void -gst_clock_overlay_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_details_simple (element_class, "Clock overlay", - "Filter/Editor/Video", - "Overlays the current clock time on a video stream", - "Tim-Philipp Müller <tim@centricular.net>"); -} - - -static void gst_clock_overlay_finalize (GObject * object); -static void gst_clock_overlay_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_clock_overlay_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static gchar * -gst_clock_overlay_render_time (GstClockOverlay * overlay) -{ - struct tm *t; - time_t now; - gchar buf[256]; - -#ifdef HAVE_LOCALTIME_R - struct tm dummy; -#endif - - now = time (NULL); - -#ifdef HAVE_LOCALTIME_R - /* Need to call tzset explicitly when calling localtime_r for changes - to the timezone between calls to be visible. */ - tzset (); - t = localtime_r (&now, &dummy); -#else - /* on win32 this apparently returns a per-thread struct which would be fine */ - t = localtime (&now); -#endif - - if (t == NULL) - return g_strdup ("--:--:--"); - - if (strftime (buf, sizeof (buf), overlay->format, t) == 0) - return g_strdup (""); - return g_strdup (buf); -} - -/* Called with lock held */ -static gchar * -gst_clock_overlay_get_text (GstTextOverlay * overlay, GstBuffer * video_frame) -{ - gchar *time_str, *txt, *ret; - - overlay->need_render = TRUE; - - txt = g_strdup (overlay->default_text); - - time_str = gst_clock_overlay_render_time (GST_CLOCK_OVERLAY (overlay)); - if (txt != NULL && *txt != '\0') { - ret = g_strdup_printf ("%s %s", txt, time_str); - } else { - ret = time_str; - time_str = NULL; - } - - g_free (txt); - g_free (time_str); - - return ret; -} - -static void -gst_clock_overlay_class_init (GstClockOverlayClass * klass) -{ - GObjectClass *gobject_class; - GstTextOverlayClass *gsttextoverlay_class; - - gobject_class = (GObjectClass *) klass; - gsttextoverlay_class = (GstTextOverlayClass *) klass; - - gobject_class->finalize = gst_clock_overlay_finalize; - gobject_class->set_property = gst_clock_overlay_set_property; - gobject_class->get_property = gst_clock_overlay_get_property; - - gsttextoverlay_class->get_text = gst_clock_overlay_get_text; - - g_object_class_install_property (gobject_class, PROP_TIMEFORMAT, - g_param_spec_string ("time-format", "Date/Time Format", - "Format to use for time and date value, as in strftime.", - DEFAULT_PROP_TIMEFORMAT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -} - - -static void -gst_clock_overlay_finalize (GObject * object) -{ - GstClockOverlay *overlay = GST_CLOCK_OVERLAY (object); - - g_free (overlay->format); - overlay->format = NULL; - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - - -static void -gst_clock_overlay_init (GstClockOverlay * overlay, GstClockOverlayClass * klass) -{ - PangoFontDescription *font_description; - GstTextOverlay *textoverlay; - PangoContext *context; - - textoverlay = GST_TEXT_OVERLAY (overlay); - - context = GST_TEXT_OVERLAY_CLASS (klass)->pango_context; - - pango_context_set_language (context, pango_language_from_string ("en_US")); - pango_context_set_base_dir (context, PANGO_DIRECTION_LTR); - - font_description = pango_font_description_new (); - pango_font_description_set_family_static (font_description, "Monospace"); - pango_font_description_set_style (font_description, PANGO_STYLE_NORMAL); - pango_font_description_set_variant (font_description, PANGO_VARIANT_NORMAL); - pango_font_description_set_weight (font_description, PANGO_WEIGHT_NORMAL); - pango_font_description_set_stretch (font_description, PANGO_STRETCH_NORMAL); - pango_font_description_set_size (font_description, 18 * PANGO_SCALE); - pango_context_set_font_description (context, font_description); - pango_font_description_free (font_description); - - textoverlay->valign = GST_TEXT_OVERLAY_VALIGN_TOP; - textoverlay->halign = GST_TEXT_OVERLAY_HALIGN_LEFT; - - overlay->format = g_strdup (DEFAULT_PROP_TIMEFORMAT); -} - - -static void -gst_clock_overlay_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstClockOverlay *overlay = GST_CLOCK_OVERLAY (object); - - GST_OBJECT_LOCK (overlay); - switch (prop_id) { - case PROP_TIMEFORMAT: - g_free (overlay->format); - overlay->format = g_value_dup_string (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - GST_OBJECT_UNLOCK (overlay); -} - - -static void -gst_clock_overlay_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstClockOverlay *overlay = GST_CLOCK_OVERLAY (object); - - GST_OBJECT_LOCK (overlay); - switch (prop_id) { - case PROP_TIMEFORMAT: - g_value_set_string (value, overlay->format); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - GST_OBJECT_UNLOCK (overlay); -} diff --git a/ext/pango/gstclockoverlay.h b/ext/pango/gstclockoverlay.h deleted file mode 100644 index 74e32e36..00000000 --- a/ext/pango/gstclockoverlay.h +++ /dev/null @@ -1,62 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * Copyright (C) <2005> Tim-Philipp Müller <tim@centricular.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_CLOCK_OVERLAY_H__ -#define __GST_CLOCK_OVERLAY_H__ - -#include "gsttextoverlay.h" - -G_BEGIN_DECLS - -#define GST_TYPE_CLOCK_OVERLAY \ - (gst_clock_overlay_get_type()) -#define GST_CLOCK_OVERLAY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CLOCK_OVERLAY,GstClockOverlay)) -#define GST_CLOCK_OVERLAY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CLOCK_OVERLAY,GstClockOverlayClass)) -#define GST_IS_CLOCK_OVERLAY(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CLOCK_OVERLAY)) -#define GST_IS_CLOCK_OVERLAY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CLOCK_OVERLAY)) - -typedef struct _GstClockOverlay GstClockOverlay; -typedef struct _GstClockOverlayClass GstClockOverlayClass; - -/** - * GstClockOverlay: - * - * Opaque clockoverlay data structure. - */ -struct _GstClockOverlay { - GstTextOverlay textoverlay; - gchar *format; /* as in strftime () */ -}; - -struct _GstClockOverlayClass { - GstTextOverlayClass parent_class; -}; - -GType gst_clock_overlay_get_type (void); - -G_END_DECLS - -#endif /* __GST_CLOCK_OVERLAY_H__ */ - diff --git a/ext/pango/gsttextoverlay.c b/ext/pango/gsttextoverlay.c deleted file mode 100644 index 3e4a0ee7..00000000 --- a/ext/pango/gsttextoverlay.c +++ /dev/null @@ -1,2352 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * Copyright (C) <2003> David Schleef <ds@schleef.org> - * Copyright (C) <2006> Julien Moutte <julien@moutte.net> - * Copyright (C) <2006> Zeeshan Ali <zeeshan.ali@nokia.com> - * Copyright (C) <2006-2008> Tim-Philipp Müller <tim centricular net> - * Copyright (C) <2009> Young-Ho Cha <ganadist@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-textoverlay - * @see_also: #GstTextRender, #GstClockOverlay, #GstTimeOverlay, #GstSubParse - * - * This plugin renders text on top of a video stream. This can be either - * static text or text from buffers received on the text sink pad, e.g. - * as produced by the subparse element. If the text sink pad is not linked, - * the text set via the "text" property will be rendered. If the text sink - * pad is linked, text will be rendered as it is received on that pad, - * honouring and matching the buffer timestamps of both input streams. - * - * The text can contain newline characters and text wrapping is enabled by - * default. - * - * <refsect2> - * <title>Example launch lines</title> - * |[ - * gst-launch -v videotestsrc ! textoverlay text="Room A" valign=top halign=left ! xvimagesink - * ]| Here is a simple pipeline that displays a static text in the top left - * corner of the video picture - * |[ - * gst-launch -v filesrc location=subtitles.srt ! subparse ! txt. videotestsrc ! timeoverlay ! textoverlay name=txt shaded-background=yes ! xvimagesink - * ]| Here is another pipeline that displays subtitles from an .srt subtitle - * file, centered at the bottom of the picture and with a rectangular shading - * around the text in the background: - * <para> - * If you do not have such a subtitle file, create one looking like this - * in a text editor: - * |[ - * 1 - * 00:00:03,000 --> 00:00:05,000 - * Hello? (3-5s) - * - * 2 - * 00:00:08,000 --> 00:00:13,000 - * Yes, this is a subtitle. Don't - * you like it? (8-13s) - * - * 3 - * 00:00:18,826 --> 00:01:02,886 - * Uh? What are you talking about? - * I don't understand (18-62s) - * ]| - * </para> - * </refsect2> - */ - -/* FIXME: alloc segment as part of instance struct */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <gst/video/video.h> - -#include "gsttextoverlay.h" -#include "gsttimeoverlay.h" -#include "gstclockoverlay.h" -#include "gsttextrender.h" -#include <string.h> - -/* FIXME: - * - use proper strides and offset for I420 - * - if text is wider than the video picture, it does not get - * clipped properly during blitting (if wrapping is disabled) - * - make 'shading_value' a property (or enum: light/normal/dark/verydark)? - */ - -GST_DEBUG_CATEGORY (pango_debug); -#define GST_CAT_DEFAULT pango_debug - -#define DEFAULT_PROP_TEXT "" -#define DEFAULT_PROP_SHADING FALSE -#define DEFAULT_PROP_VALIGNMENT GST_TEXT_OVERLAY_VALIGN_BASELINE -#define DEFAULT_PROP_HALIGNMENT GST_TEXT_OVERLAY_HALIGN_CENTER -#define DEFAULT_PROP_VALIGN "baseline" -#define DEFAULT_PROP_HALIGN "center" -#define DEFAULT_PROP_XPAD 25 -#define DEFAULT_PROP_YPAD 25 -#define DEFAULT_PROP_DELTAX 0 -#define DEFAULT_PROP_DELTAY 0 -#define DEFAULT_PROP_WRAP_MODE GST_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR -#define DEFAULT_PROP_FONT_DESC "" -#define DEFAULT_PROP_SILENT FALSE -#define DEFAULT_PROP_LINE_ALIGNMENT GST_TEXT_OVERLAY_LINE_ALIGN_CENTER -#define DEFAULT_PROP_WAIT_TEXT TRUE -#define DEFAULT_PROP_AUTO_ADJUST_SIZE TRUE -#define DEFAULT_PROP_VERTICAL_RENDER FALSE - -/* make a property of me */ -#define DEFAULT_SHADING_VALUE -80 - -#define MINIMUM_OUTLINE_OFFSET 1.0 -#define DEFAULT_SCALE_BASIS 640 - -#define COMP_Y(ret, r, g, b) \ -{ \ - ret = (int) (((19595 * r) >> 16) + ((38470 * g) >> 16) + ((7471 * b) >> 16)); \ - ret = CLAMP (ret, 0, 255); \ -} - -#define COMP_U(ret, r, g, b) \ -{ \ - ret = (int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) + ((32768 * b) >> 16) + 128); \ - ret = CLAMP (ret, 0, 255); \ -} - -#define COMP_V(ret, r, g, b) \ -{ \ - ret = (int) (((32768 * r) >> 16) - ((27439 * g) >> 16) - ((5329 * b) >> 16) + 128); \ - ret = CLAMP (ret, 0, 255); \ -} - -#define BLEND(ret, alpha, v0, v1) \ -{ \ - ret = (v0 * alpha + v1 * (255 - alpha)) / 255; \ -} - -#if G_BYTE_ORDER == G_LITTLE_ENDIAN -# define CAIRO_ARGB_A 3 -# define CAIRO_ARGB_R 2 -# define CAIRO_ARGB_G 1 -# define CAIRO_ARGB_B 0 -#else -# define CAIRO_ARGB_A 0 -# define CAIRO_ARGB_R 1 -# define CAIRO_ARGB_G 2 -# define CAIRO_ARGB_B 3 -#endif - -enum -{ - PROP_0, - PROP_TEXT, - PROP_SHADING, - PROP_VALIGN, /* deprecated */ - PROP_HALIGN, /* deprecated */ - PROP_HALIGNMENT, - PROP_VALIGNMENT, - PROP_XPAD, - PROP_YPAD, - PROP_DELTAX, - PROP_DELTAY, - PROP_WRAP_MODE, - PROP_FONT_DESC, - PROP_SILENT, - PROP_LINE_ALIGNMENT, - PROP_WAIT_TEXT, - PROP_AUTO_ADJUST_SIZE, - PROP_VERTICAL_RENDER, - PROP_LAST -}; - -static GstStaticPadTemplate src_template_factory = - GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx ";" - GST_VIDEO_CAPS_xRGB ";" - GST_VIDEO_CAPS_YUV ("I420") ";" GST_VIDEO_CAPS_YUV ("UYVY")) - ); - -static GstStaticPadTemplate video_sink_template_factory = - GST_STATIC_PAD_TEMPLATE ("video_sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx ";" - GST_VIDEO_CAPS_xRGB ";" - GST_VIDEO_CAPS_YUV ("I420") ";" GST_VIDEO_CAPS_YUV ("UYVY")) - ); - -static GstStaticPadTemplate text_sink_template_factory = - GST_STATIC_PAD_TEMPLATE ("text_sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("text/x-pango-markup; text/plain") - ); - -#define GST_TYPE_TEXT_OVERLAY_VALIGN (gst_text_overlay_valign_get_type()) -static GType -gst_text_overlay_valign_get_type (void) -{ - static GType text_overlay_valign_type = 0; - static const GEnumValue text_overlay_valign[] = { - {GST_TEXT_OVERLAY_VALIGN_BASELINE, "baseline", "baseline"}, - {GST_TEXT_OVERLAY_VALIGN_BOTTOM, "bottom", "bottom"}, - {GST_TEXT_OVERLAY_VALIGN_TOP, "top", "top"}, - {0, NULL, NULL}, - }; - - if (!text_overlay_valign_type) { - text_overlay_valign_type = - g_enum_register_static ("GstTextOverlayVAlign", text_overlay_valign); - } - return text_overlay_valign_type; -} - -#define GST_TYPE_TEXT_OVERLAY_HALIGN (gst_text_overlay_halign_get_type()) -static GType -gst_text_overlay_halign_get_type (void) -{ - static GType text_overlay_halign_type = 0; - static const GEnumValue text_overlay_halign[] = { - {GST_TEXT_OVERLAY_HALIGN_LEFT, "left", "left"}, - {GST_TEXT_OVERLAY_HALIGN_CENTER, "center", "center"}, - {GST_TEXT_OVERLAY_HALIGN_RIGHT, "right", "right"}, - {0, NULL, NULL}, - }; - - if (!text_overlay_halign_type) { - text_overlay_halign_type = - g_enum_register_static ("GstTextOverlayHAlign", text_overlay_halign); - } - return text_overlay_halign_type; -} - - -#define GST_TYPE_TEXT_OVERLAY_WRAP_MODE (gst_text_overlay_wrap_mode_get_type()) -static GType -gst_text_overlay_wrap_mode_get_type (void) -{ - static GType text_overlay_wrap_mode_type = 0; - static const GEnumValue text_overlay_wrap_mode[] = { - {GST_TEXT_OVERLAY_WRAP_MODE_NONE, "none", "none"}, - {GST_TEXT_OVERLAY_WRAP_MODE_WORD, "word", "word"}, - {GST_TEXT_OVERLAY_WRAP_MODE_CHAR, "char", "char"}, - {GST_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR, "wordchar", "wordchar"}, - {0, NULL, NULL}, - }; - - if (!text_overlay_wrap_mode_type) { - text_overlay_wrap_mode_type = - g_enum_register_static ("GstTextOverlayWrapMode", - text_overlay_wrap_mode); - } - return text_overlay_wrap_mode_type; -} - -#define GST_TYPE_TEXT_OVERLAY_LINE_ALIGN (gst_text_overlay_line_align_get_type()) -static GType -gst_text_overlay_line_align_get_type (void) -{ - static GType text_overlay_line_align_type = 0; - static const GEnumValue text_overlay_line_align[] = { - {GST_TEXT_OVERLAY_LINE_ALIGN_LEFT, "left", "left"}, - {GST_TEXT_OVERLAY_LINE_ALIGN_CENTER, "center", "center"}, - {GST_TEXT_OVERLAY_LINE_ALIGN_RIGHT, "right", "right"}, - {0, NULL, NULL} - }; - - if (!text_overlay_line_align_type) { - text_overlay_line_align_type = - g_enum_register_static ("GstTextOverlayLineAlign", - text_overlay_line_align); - } - return text_overlay_line_align_type; -} - -#define GST_TEXT_OVERLAY_GET_COND(ov) (((GstTextOverlay *)ov)->cond) -#define GST_TEXT_OVERLAY_WAIT(ov) (g_cond_wait (GST_TEXT_OVERLAY_GET_COND (ov), GST_OBJECT_GET_LOCK (ov))) -#define GST_TEXT_OVERLAY_SIGNAL(ov) (g_cond_signal (GST_TEXT_OVERLAY_GET_COND (ov))) -#define GST_TEXT_OVERLAY_BROADCAST(ov)(g_cond_broadcast (GST_TEXT_OVERLAY_GET_COND (ov))) - -static GstStateChangeReturn gst_text_overlay_change_state (GstElement * element, - GstStateChange transition); - -static GstCaps *gst_text_overlay_getcaps (GstPad * pad); -static gboolean gst_text_overlay_setcaps (GstPad * pad, GstCaps * caps); -static gboolean gst_text_overlay_setcaps_txt (GstPad * pad, GstCaps * caps); -static gboolean gst_text_overlay_src_event (GstPad * pad, GstEvent * event); -static gboolean gst_text_overlay_src_query (GstPad * pad, GstQuery * query); - -static gboolean gst_text_overlay_video_event (GstPad * pad, GstEvent * event); -static GstFlowReturn gst_text_overlay_video_chain (GstPad * pad, - GstBuffer * buffer); -static GstFlowReturn gst_text_overlay_video_bufferalloc (GstPad * pad, - guint64 offset, guint size, GstCaps * caps, GstBuffer ** buffer); - -static gboolean gst_text_overlay_text_event (GstPad * pad, GstEvent * event); -static GstFlowReturn gst_text_overlay_text_chain (GstPad * pad, - GstBuffer * buffer); -static GstPadLinkReturn gst_text_overlay_text_pad_link (GstPad * pad, - GstPad * peer); -static void gst_text_overlay_text_pad_unlink (GstPad * pad); -static void gst_text_overlay_pop_text (GstTextOverlay * overlay); -static void gst_text_overlay_update_render_mode (GstTextOverlay * overlay); - -static void gst_text_overlay_finalize (GObject * object); -static void gst_text_overlay_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_text_overlay_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static void gst_text_overlay_adjust_values_with_fontdesc (GstTextOverlay * - overlay, PangoFontDescription * desc); - -GST_BOILERPLATE (GstTextOverlay, gst_text_overlay, GstElement, - GST_TYPE_ELEMENT); - -static void -gst_text_overlay_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_template_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&video_sink_template_factory)); - - /* ugh */ - if (!GST_IS_TIME_OVERLAY_CLASS (g_class) && - !GST_IS_CLOCK_OVERLAY_CLASS (g_class)) { - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&text_sink_template_factory)); - } - - gst_element_class_set_details_simple (element_class, "Text overlay", - "Filter/Editor/Video", - "Adds text strings on top of a video buffer", - "David Schleef <ds@schleef.org>, " "Zeeshan Ali <zeeshan.ali@nokia.com>"); -} - -static gchar * -gst_text_overlay_get_text (GstTextOverlay * overlay, GstBuffer * video_frame) -{ - return g_strdup (overlay->default_text); -} - -static void -gst_text_overlay_class_init (GstTextOverlayClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - PangoFontMap *fontmap; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - - gobject_class->finalize = gst_text_overlay_finalize; - gobject_class->set_property = gst_text_overlay_set_property; - gobject_class->get_property = gst_text_overlay_get_property; - - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_text_overlay_change_state); - - klass->get_text = gst_text_overlay_get_text; - fontmap = pango_cairo_font_map_get_default (); - klass->pango_context = - pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap)); - - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT, - g_param_spec_string ("text", "text", - "Text to be display.", DEFAULT_PROP_TEXT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHADING, - g_param_spec_boolean ("shaded-background", "shaded background", - "Whether to shade the background under the text area", - DEFAULT_PROP_SHADING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT, - g_param_spec_enum ("valignment", "vertical alignment", - "Vertical alignment of the text", GST_TYPE_TEXT_OVERLAY_VALIGN, - DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT, - g_param_spec_enum ("halignment", "horizontal alignment", - "Horizontal alignment of the text", GST_TYPE_TEXT_OVERLAY_HALIGN, - DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGN, - g_param_spec_string ("valign", "vertical alignment", - "Vertical alignment of the text (deprecated; use valignment)", - DEFAULT_PROP_VALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGN, - g_param_spec_string ("halign", "horizontal alignment", - "Horizontal alignment of the text (deprecated; use halignment)", - DEFAULT_PROP_HALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD, - g_param_spec_int ("xpad", "horizontal paddding", - "Horizontal paddding when using left/right alignment", 0, G_MAXINT, - DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD, - g_param_spec_int ("ypad", "vertical padding", - "Vertical padding when using top/bottom alignment", 0, G_MAXINT, - DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAX, - g_param_spec_int ("deltax", "X position modifier", - "Shift X position to the left or to the right. Unit is pixels.", - G_MININT, G_MAXINT, DEFAULT_PROP_DELTAX, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAY, - g_param_spec_int ("deltay", "Y position modifier", - "Shift Y position up or down. Unit is pixels.", G_MININT, G_MAXINT, - DEFAULT_PROP_DELTAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WRAP_MODE, - g_param_spec_enum ("wrap-mode", "wrap mode", - "Whether to wrap the text and if so how.", - GST_TYPE_TEXT_OVERLAY_WRAP_MODE, DEFAULT_PROP_WRAP_MODE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC, - g_param_spec_string ("font-desc", "font description", - "Pango font description of font to be used for rendering. " - "See documentation of pango_font_description_from_string " - "for syntax.", DEFAULT_PROP_FONT_DESC, - G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); - /** - * GstTextOverlay:line-alignment - * - * Alignment of text lines relative to each other (for multi-line text) - * - * Since: 0.10.15 - **/ - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT, - g_param_spec_enum ("line-alignment", "line alignment", - "Alignment of text lines relative to each other.", - GST_TYPE_TEXT_OVERLAY_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /** - * GstTextOverlay:silent - * - * If set, no text is rendered. Useful to switch off text rendering - * temporarily without removing the textoverlay element from the pipeline. - * - * Since: 0.10.15 - **/ - /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */ - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT, - g_param_spec_boolean ("silent", "silent", - "Whether to render the text string", - DEFAULT_PROP_SILENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /** - * GstTextOverlay:wait-text - * - * If set, the video will block until a subtitle is received on the text pad. - * If video and subtitles are sent in sync, like from the same demuxer, this - * property should be set. - * - * Since: 0.10.20 - **/ - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WAIT_TEXT, - g_param_spec_boolean ("wait-text", "Wait Text", - "Whether to wait for subtitles", - DEFAULT_PROP_WAIT_TEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (G_OBJECT_CLASS (klass), - PROP_AUTO_ADJUST_SIZE, g_param_spec_boolean ("auto-resize", "auto resize", - "Automatically adjust font size to screen-size.", - DEFAULT_PROP_AUTO_ADJUST_SIZE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VERTICAL_RENDER, - g_param_spec_boolean ("vertical-render", "vertical render", - "Vertical Render.", DEFAULT_PROP_VERTICAL_RENDER, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -} - -static void -gst_text_overlay_finalize (GObject * object) -{ - GstTextOverlay *overlay = GST_TEXT_OVERLAY (object); - - g_free (overlay->default_text); - - if (overlay->text_image) { - g_free (overlay->text_image); - overlay->text_image = NULL; - } - - if (overlay->layout) { - g_object_unref (overlay->layout); - overlay->layout = NULL; - } - - if (overlay->text_buffer) { - gst_buffer_unref (overlay->text_buffer); - overlay->text_buffer = NULL; - } - - if (overlay->cond) { - g_cond_free (overlay->cond); - overlay->cond = NULL; - } - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_text_overlay_init (GstTextOverlay * overlay, GstTextOverlayClass * klass) -{ - GstPadTemplate *template; - PangoFontDescription *desc; - - /* video sink */ - template = gst_static_pad_template_get (&video_sink_template_factory); - overlay->video_sinkpad = gst_pad_new_from_template (template, "video_sink"); - gst_object_unref (template); - gst_pad_set_getcaps_function (overlay->video_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_getcaps)); - gst_pad_set_setcaps_function (overlay->video_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_setcaps)); - gst_pad_set_event_function (overlay->video_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_video_event)); - gst_pad_set_chain_function (overlay->video_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_video_chain)); - gst_pad_set_bufferalloc_function (overlay->video_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_video_bufferalloc)); - gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad); - - if (!GST_IS_TIME_OVERLAY_CLASS (klass) && !GST_IS_CLOCK_OVERLAY_CLASS (klass)) { - /* text sink */ - template = gst_static_pad_template_get (&text_sink_template_factory); - overlay->text_sinkpad = gst_pad_new_from_template (template, "text_sink"); - gst_object_unref (template); - gst_pad_set_setcaps_function (overlay->text_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_setcaps_txt)); - gst_pad_set_event_function (overlay->text_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_text_event)); - gst_pad_set_chain_function (overlay->text_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_text_chain)); - gst_pad_set_link_function (overlay->text_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_text_pad_link)); - gst_pad_set_unlink_function (overlay->text_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_text_pad_unlink)); - gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad); - } - - /* (video) source */ - template = gst_static_pad_template_get (&src_template_factory); - overlay->srcpad = gst_pad_new_from_template (template, "src"); - gst_object_unref (template); - gst_pad_set_getcaps_function (overlay->srcpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_getcaps)); - gst_pad_set_event_function (overlay->srcpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_src_event)); - gst_pad_set_query_function (overlay->srcpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_src_query)); - gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad); - - overlay->line_align = DEFAULT_PROP_LINE_ALIGNMENT; - overlay->layout = - pango_layout_new (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_context); - desc = - pango_context_get_font_description (GST_TEXT_OVERLAY_GET_CLASS - (overlay)->pango_context); - gst_text_overlay_adjust_values_with_fontdesc (overlay, desc); - - overlay->halign = DEFAULT_PROP_HALIGNMENT; - overlay->valign = DEFAULT_PROP_VALIGNMENT; - overlay->xpad = DEFAULT_PROP_XPAD; - overlay->ypad = DEFAULT_PROP_YPAD; - overlay->deltax = DEFAULT_PROP_DELTAX; - overlay->deltay = DEFAULT_PROP_DELTAY; - - overlay->wrap_mode = DEFAULT_PROP_WRAP_MODE; - - overlay->want_shading = DEFAULT_PROP_SHADING; - overlay->shading_value = DEFAULT_SHADING_VALUE; - overlay->silent = DEFAULT_PROP_SILENT; - overlay->wait_text = DEFAULT_PROP_WAIT_TEXT; - overlay->auto_adjust_size = DEFAULT_PROP_AUTO_ADJUST_SIZE; - - overlay->default_text = g_strdup (DEFAULT_PROP_TEXT); - overlay->need_render = TRUE; - overlay->text_image = NULL; - overlay->use_vertical_render = DEFAULT_PROP_VERTICAL_RENDER; - gst_text_overlay_update_render_mode (overlay); - - overlay->fps_n = 0; - overlay->fps_d = 1; - - overlay->text_buffer = NULL; - overlay->text_linked = FALSE; - overlay->cond = g_cond_new (); - gst_segment_init (&overlay->segment, GST_FORMAT_TIME); -} - -static void -gst_text_overlay_update_wrap_mode (GstTextOverlay * overlay) -{ - if (overlay->wrap_mode == GST_TEXT_OVERLAY_WRAP_MODE_NONE) { - GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE"); - pango_layout_set_width (overlay->layout, -1); - } else { - int width; - - if (overlay->auto_adjust_size) { - width = DEFAULT_SCALE_BASIS * PANGO_SCALE; - if (overlay->use_vertical_render) { - width = width * (overlay->height - overlay->ypad * 2) / overlay->width; - } - } else { - width = - (overlay->use_vertical_render ? overlay->height : overlay->width) * - PANGO_SCALE; - } - - GST_DEBUG_OBJECT (overlay, "Set layout width %d", overlay->width); - GST_DEBUG_OBJECT (overlay, "Set wrap mode %d", overlay->wrap_mode); - pango_layout_set_width (overlay->layout, width); - pango_layout_set_wrap (overlay->layout, (PangoWrapMode) overlay->wrap_mode); - } -} - -static void -gst_text_overlay_update_render_mode (GstTextOverlay * overlay) -{ - PangoMatrix matrix = PANGO_MATRIX_INIT; - PangoContext *context = pango_layout_get_context (overlay->layout); - - if (overlay->use_vertical_render) { - pango_matrix_rotate (&matrix, -90); - pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO); - pango_context_set_matrix (context, &matrix); - pango_layout_set_alignment (overlay->layout, PANGO_ALIGN_LEFT); - } else { - pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH); - pango_context_set_matrix (context, &matrix); - pango_layout_set_alignment (overlay->layout, overlay->line_align); - } -} - -static gboolean -gst_text_overlay_setcaps_txt (GstPad * pad, GstCaps * caps) -{ - GstTextOverlay *overlay; - GstStructure *structure; - - overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - - structure = gst_caps_get_structure (caps, 0); - overlay->have_pango_markup = - gst_structure_has_name (structure, "text/x-pango-markup"); - - gst_object_unref (overlay); - - return TRUE; -} - -/* FIXME: upstream nego (e.g. when the video window is resized) */ - -static gboolean -gst_text_overlay_setcaps (GstPad * pad, GstCaps * caps) -{ - GstTextOverlay *overlay; - GstStructure *structure; - gboolean ret = FALSE; - const GValue *fps; - - if (!GST_PAD_IS_SINK (pad)) - return TRUE; - - g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE); - - overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - - overlay->width = 0; - overlay->height = 0; - structure = gst_caps_get_structure (caps, 0); - fps = gst_structure_get_value (structure, "framerate"); - - if (fps - && gst_video_format_parse_caps (caps, &overlay->format, &overlay->width, - &overlay->height)) { - ret = gst_pad_set_caps (overlay->srcpad, caps); - } - - overlay->fps_n = gst_value_get_fraction_numerator (fps); - overlay->fps_d = gst_value_get_fraction_denominator (fps); - - if (ret) { - GST_OBJECT_LOCK (overlay); - gst_text_overlay_update_wrap_mode (overlay); - GST_OBJECT_UNLOCK (overlay); - } - - gst_object_unref (overlay); - - return ret; -} - -static void -gst_text_overlay_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstTextOverlay *overlay = GST_TEXT_OVERLAY (object); - - GST_OBJECT_LOCK (overlay); - switch (prop_id) { - case PROP_TEXT: - g_free (overlay->default_text); - overlay->default_text = g_value_dup_string (value); - overlay->need_render = TRUE; - break; - case PROP_SHADING: - overlay->want_shading = g_value_get_boolean (value); - break; - case PROP_XPAD: - overlay->xpad = g_value_get_int (value); - break; - case PROP_YPAD: - overlay->ypad = g_value_get_int (value); - break; - case PROP_DELTAX: - overlay->deltax = g_value_get_int (value); - break; - case PROP_DELTAY: - overlay->deltay = g_value_get_int (value); - break; - case PROP_HALIGN:{ - const gchar *s = g_value_get_string (value); - - if (s && g_ascii_strcasecmp (s, "left") == 0) - overlay->halign = GST_TEXT_OVERLAY_HALIGN_LEFT; - else if (s && g_ascii_strcasecmp (s, "center") == 0) - overlay->halign = GST_TEXT_OVERLAY_HALIGN_CENTER; - else if (s && g_ascii_strcasecmp (s, "right") == 0) - overlay->halign = GST_TEXT_OVERLAY_HALIGN_RIGHT; - else - g_warning ("Invalid value '%s' for textoverlay property 'halign'", - GST_STR_NULL (s)); - break; - } - case PROP_VALIGN:{ - const gchar *s = g_value_get_string (value); - - if (s && g_ascii_strcasecmp (s, "baseline") == 0) - overlay->valign = GST_TEXT_OVERLAY_VALIGN_BASELINE; - else if (s && g_ascii_strcasecmp (s, "bottom") == 0) - overlay->valign = GST_TEXT_OVERLAY_VALIGN_BOTTOM; - else if (s && g_ascii_strcasecmp (s, "top") == 0) - overlay->valign = GST_TEXT_OVERLAY_VALIGN_TOP; - else - g_warning ("Invalid value '%s' for textoverlay property 'valign'", - GST_STR_NULL (s)); - break; - } - case PROP_VALIGNMENT: - overlay->valign = g_value_get_enum (value); - break; - case PROP_HALIGNMENT: - overlay->halign = g_value_get_enum (value); - break; - case PROP_WRAP_MODE: - overlay->wrap_mode = g_value_get_enum (value); - gst_text_overlay_update_wrap_mode (overlay); - break; - case PROP_FONT_DESC: - { - PangoFontDescription *desc; - const gchar *fontdesc_str; - - fontdesc_str = g_value_get_string (value); - desc = pango_font_description_from_string (fontdesc_str); - if (desc) { - GST_LOG_OBJECT (overlay, "font description set: %s", fontdesc_str); - pango_layout_set_font_description (overlay->layout, desc); - gst_text_overlay_adjust_values_with_fontdesc (overlay, desc); - pango_font_description_free (desc); - } else { - GST_WARNING_OBJECT (overlay, "font description parse failed: %s", - fontdesc_str); - } - break; - } - case PROP_SILENT: - overlay->silent = g_value_get_boolean (value); - break; - case PROP_LINE_ALIGNMENT: - overlay->line_align = g_value_get_enum (value); - pango_layout_set_alignment (overlay->layout, - (PangoAlignment) overlay->line_align); - break; - case PROP_WAIT_TEXT: - overlay->wait_text = g_value_get_boolean (value); - break; - case PROP_AUTO_ADJUST_SIZE: - { - overlay->auto_adjust_size = g_value_get_boolean (value); - overlay->need_render = TRUE; - } - case PROP_VERTICAL_RENDER: - overlay->use_vertical_render = g_value_get_boolean (value); - gst_text_overlay_update_render_mode (overlay); - overlay->need_render = TRUE; - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - - overlay->need_render = TRUE; - GST_OBJECT_UNLOCK (overlay); -} - -static void -gst_text_overlay_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstTextOverlay *overlay = GST_TEXT_OVERLAY (object); - - GST_OBJECT_LOCK (overlay); - switch (prop_id) { - case PROP_TEXT: - g_value_set_string (value, overlay->default_text); - break; - case PROP_SHADING: - g_value_set_boolean (value, overlay->want_shading); - break; - case PROP_XPAD: - g_value_set_int (value, overlay->xpad); - break; - case PROP_YPAD: - g_value_set_int (value, overlay->ypad); - break; - case PROP_DELTAX: - g_value_set_int (value, overlay->deltax); - break; - case PROP_DELTAY: - g_value_set_int (value, overlay->deltay); - break; - case PROP_VALIGNMENT: - g_value_set_enum (value, overlay->valign); - break; - case PROP_HALIGNMENT: - g_value_set_enum (value, overlay->halign); - break; - case PROP_WRAP_MODE: - g_value_set_enum (value, overlay->wrap_mode); - break; - case PROP_SILENT: - g_value_set_boolean (value, overlay->silent); - break; - case PROP_LINE_ALIGNMENT: - g_value_set_enum (value, overlay->line_align); - break; - case PROP_WAIT_TEXT: - g_value_set_boolean (value, overlay->wait_text); - break; - case PROP_AUTO_ADJUST_SIZE: - g_value_set_boolean (value, overlay->auto_adjust_size); - break; - case PROP_VERTICAL_RENDER: - g_value_set_boolean (value, overlay->use_vertical_render); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - - overlay->need_render = TRUE; - GST_OBJECT_UNLOCK (overlay); -} - -static gboolean -gst_text_overlay_src_query (GstPad * pad, GstQuery * query) -{ - gboolean ret = FALSE; - GstTextOverlay *overlay = NULL; - - overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - - ret = gst_pad_peer_query (overlay->video_sinkpad, query); - - gst_object_unref (overlay); - - return ret; -} - -static gboolean -gst_text_overlay_src_event (GstPad * pad, GstEvent * event) -{ - gboolean ret = FALSE; - GstTextOverlay *overlay = NULL; - - overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK:{ - GstSeekFlags flags; - - /* We don't handle seek if we have not text pad */ - if (!overlay->text_linked) { - GST_DEBUG_OBJECT (overlay, "seek received, pushing upstream"); - ret = gst_pad_push_event (overlay->video_sinkpad, event); - goto beach; - } - - GST_DEBUG_OBJECT (overlay, "seek received, driving from here"); - - gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL); - - /* Flush downstream, only for flushing seek */ - if (flags & GST_SEEK_FLAG_FLUSH) - gst_pad_push_event (overlay->srcpad, gst_event_new_flush_start ()); - - /* Mark ourself as flushing, unblock chains */ - GST_OBJECT_LOCK (overlay); - overlay->video_flushing = TRUE; - overlay->text_flushing = TRUE; - gst_text_overlay_pop_text (overlay); - GST_OBJECT_UNLOCK (overlay); - - /* Seek on each sink pad */ - gst_event_ref (event); - ret = gst_pad_push_event (overlay->video_sinkpad, event); - if (ret) { - ret = gst_pad_push_event (overlay->text_sinkpad, event); - } else { - gst_event_unref (event); - } - break; - } - default: - if (overlay->text_linked) { - gst_event_ref (event); - ret = gst_pad_push_event (overlay->video_sinkpad, event); - gst_pad_push_event (overlay->text_sinkpad, event); - } else { - ret = gst_pad_push_event (overlay->video_sinkpad, event); - } - break; - } - -beach: - gst_object_unref (overlay); - - return ret; -} - -static GstCaps * -gst_text_overlay_getcaps (GstPad * pad) -{ - GstTextOverlay *overlay; - GstPad *otherpad; - GstCaps *caps; - - overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - - if (pad == overlay->srcpad) - otherpad = overlay->video_sinkpad; - else - otherpad = overlay->srcpad; - - /* we can do what the peer can */ - caps = gst_pad_peer_get_caps (otherpad); - if (caps) { - GstCaps *temp; - const GstCaps *templ; - - GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, caps); - - /* filtered against our padtemplate */ - templ = gst_pad_get_pad_template_caps (otherpad); - GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ); - temp = gst_caps_intersect (caps, templ); - GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp); - gst_caps_unref (caps); - /* this is what we can do */ - caps = temp; - } else { - /* no peer, our padtemplate is enough then */ - caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); - } - - GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps); - - gst_object_unref (overlay); - - return caps; -} - -static void -gst_text_overlay_adjust_values_with_fontdesc (GstTextOverlay * overlay, - PangoFontDescription * desc) -{ - gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE; - overlay->shadow_offset = (double) (font_size) / 13.0; - overlay->outline_offset = (double) (font_size) / 15.0; - if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET) - overlay->outline_offset = MINIMUM_OUTLINE_OFFSET; -} - -#define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \ - b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \ - g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \ - r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \ -} G_STMT_END - -static inline void -gst_text_overlay_blit_1 (GstTextOverlay * overlay, guchar * dest, gint xpos, - gint ypos, guchar * text_image, guint dest_stride) -{ - gint i, j = 0; - gint x, y; - guchar r, g, b, a; - guchar *pimage; - guchar *py; - gint width = overlay->image_width; - gint height = overlay->image_height; - - if (xpos < 0) { - xpos = 0; - } - - if (xpos + width > overlay->width) { - width = overlay->width - xpos; - } - - if (ypos + height > overlay->height) { - height = overlay->height - ypos; - } - - dest += (ypos / 1) * dest_stride; - - for (i = 0; i < height; i++) { - pimage = text_image + 4 * (i * overlay->image_width); - py = dest + i * dest_stride + xpos; - for (j = 0; j < width; j++) { - b = pimage[CAIRO_ARGB_B]; - g = pimage[CAIRO_ARGB_G]; - r = pimage[CAIRO_ARGB_R]; - a = pimage[CAIRO_ARGB_A]; - CAIRO_UNPREMULTIPLY (a, r, g, b); - - pimage += 4; - if (a == 0) { - py++; - continue; - } - COMP_Y (y, r, g, b); - x = *py; - BLEND (*py++, a, y, x); - } - } -} - -static inline void -gst_text_overlay_blit_sub2x2cbcr (GstTextOverlay * overlay, - guchar * destcb, guchar * destcr, gint xpos, gint ypos, guchar * text_image, - guint destcb_stride, guint destcr_stride) -{ - gint i, j; - gint x, cb, cr; - gushort r, g, b, a; - gushort r1, g1, b1, a1; - guchar *pimage1, *pimage2; - guchar *pcb, *pcr; - gint width = overlay->image_width - 2; - gint height = overlay->image_height - 2; - - if (xpos < 0) { - xpos = 0; - } - - if (xpos + width > overlay->width) { - width = overlay->width - xpos; - } - - if (ypos + height > overlay->height) { - height = overlay->height - ypos; - } - - destcb += (ypos / 2) * destcb_stride; - destcr += (ypos / 2) * destcr_stride; - - for (i = 0; i < height; i += 2) { - pimage1 = text_image + 4 * (i * overlay->image_width); - pimage2 = pimage1 + 4 * overlay->image_width; - pcb = destcb + (i / 2) * destcb_stride + xpos / 2; - pcr = destcr + (i / 2) * destcr_stride + xpos / 2; - for (j = 0; j < width; j += 2) { - b = pimage1[CAIRO_ARGB_B]; - g = pimage1[CAIRO_ARGB_G]; - r = pimage1[CAIRO_ARGB_R]; - a = pimage1[CAIRO_ARGB_A]; - CAIRO_UNPREMULTIPLY (a, r, g, b); - pimage1 += 4; - - b1 = pimage1[CAIRO_ARGB_B]; - g1 = pimage1[CAIRO_ARGB_G]; - r1 = pimage1[CAIRO_ARGB_R]; - a1 = pimage1[CAIRO_ARGB_A]; - CAIRO_UNPREMULTIPLY (a1, r1, g1, b1); - b += b1; - g += g1; - r += r1; - a += a1; - pimage1 += 4; - - b1 = pimage2[CAIRO_ARGB_B]; - g1 = pimage2[CAIRO_ARGB_G]; - r1 = pimage2[CAIRO_ARGB_R]; - a1 = pimage2[CAIRO_ARGB_A]; - CAIRO_UNPREMULTIPLY (a1, r1, g1, b1); - b += b1; - g += g1; - r += r1; - a += a1; - pimage2 += 4; - - /* + 2 for rounding */ - b1 = pimage2[CAIRO_ARGB_B]; - g1 = pimage2[CAIRO_ARGB_G]; - r1 = pimage2[CAIRO_ARGB_R]; - a1 = pimage2[CAIRO_ARGB_A]; - CAIRO_UNPREMULTIPLY (a1, r1, g1, b1); - b += b1 + 2; - g += g1 + 2; - r += r1 + 2; - a += a1 + 2; - pimage2 += 4; - - b /= 4; - g /= 4; - r /= 4; - a /= 4; - - if (a == 0) { - pcb++; - pcr++; - continue; - } - COMP_U (cb, r, g, b); - COMP_V (cr, r, g, b); - - x = *pcb; - BLEND (*pcb++, a, cb, x); - x = *pcr; - BLEND (*pcr++, a, cr, x); - } - } -} - -static void -gst_text_overlay_render_pangocairo (GstTextOverlay * overlay, - const gchar * string, gint textlen) -{ - cairo_t *cr; - cairo_surface_t *surface; - PangoRectangle ink_rect, logical_rect; - cairo_matrix_t cairo_matrix; - int width, height; - double scalef = 1.0; - - if (overlay->auto_adjust_size) { - /* 640 pixel is default */ - scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS; - } - pango_layout_set_width (overlay->layout, -1); - /* set text on pango layout */ - pango_layout_set_markup (overlay->layout, string, textlen); - - /* get subtitle image size */ - pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect); - - width = (logical_rect.width + overlay->shadow_offset) * scalef; - - if (width + overlay->deltax > - (overlay->use_vertical_render ? overlay->height : overlay->width)) { - /* - * subtitle image width is larger then overlay width - * so rearrange overlay wrap mode. - */ - gst_text_overlay_update_wrap_mode (overlay); - pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect); - width = overlay->width; - } - - height = - (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef; - if (height > overlay->height) { - height = overlay->height; - } - if (overlay->use_vertical_render) { - PangoRectangle rect; - PangoContext *context; - PangoMatrix matrix = PANGO_MATRIX_INIT; - int tmp; - - context = pango_layout_get_context (overlay->layout); - - pango_matrix_rotate (&matrix, -90); - - rect.x = rect.y = 0; - rect.width = width; - rect.height = height; - pango_matrix_transform_pixel_rectangle (&matrix, &rect); - matrix.x0 = -rect.x; - matrix.y0 = -rect.y; - - pango_context_set_matrix (context, &matrix); - - cairo_matrix.xx = matrix.xx; - cairo_matrix.yx = matrix.yx; - cairo_matrix.xy = matrix.xy; - cairo_matrix.yy = matrix.yy; - cairo_matrix.x0 = matrix.x0; - cairo_matrix.y0 = matrix.y0; - cairo_matrix_scale (&cairo_matrix, scalef, scalef); - - tmp = height; - height = width; - width = tmp; - } else { - cairo_matrix_init_scale (&cairo_matrix, scalef, scalef); - } - - /* reallocate surface */ - overlay->text_image = g_realloc (overlay->text_image, 4 * width * height); - - surface = cairo_image_surface_create_for_data (overlay->text_image, - CAIRO_FORMAT_ARGB32, width, height, width * 4); - cr = cairo_create (surface); - - /* clear surface */ - cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); - cairo_paint (cr); - - cairo_set_operator (cr, CAIRO_OPERATOR_OVER); - - if (overlay->want_shading) - cairo_paint_with_alpha (cr, overlay->shading_value); - - /* apply transformations */ - cairo_set_matrix (cr, &cairo_matrix); - - /* FIXME: We use show_layout everywhere except for the surface - * because it's really faster and internally does all kinds of - * caching. Unfortunately we have to paint to a cairo path for - * the outline and this is slow. Once Pango supports user fonts - * we should use them, see - * https://bugzilla.gnome.org/show_bug.cgi?id=598695 - * - * Idea would the be, to create a cairo user font that - * does shadow, outline, text painting in the - * render_glyph function. - */ - - /* draw shadow text */ - cairo_save (cr); - cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset); - cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5); - pango_cairo_show_layout (cr, overlay->layout); - cairo_restore (cr); - - /* draw outline text */ - cairo_save (cr); - cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); - cairo_set_line_width (cr, overlay->outline_offset); - pango_cairo_layout_path (cr, overlay->layout); - cairo_stroke (cr); - cairo_restore (cr); - - /* draw text */ - cairo_save (cr); - cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); - pango_cairo_show_layout (cr, overlay->layout); - cairo_restore (cr); - - cairo_destroy (cr); - cairo_surface_destroy (surface); - overlay->image_width = width; - overlay->image_height = height; - overlay->baseline_y = ink_rect.y; -} - -#define BOX_XPAD 6 -#define BOX_YPAD 6 - -static inline void -gst_text_overlay_shade_I420_y (GstTextOverlay * overlay, guchar * dest, - gint x0, gint x1, gint y0, gint y1) -{ - gint i, j, dest_stride; - - dest_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, - overlay->width); - - x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width); - x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width); - - y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height); - y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height); - - for (i = y0; i < y1; ++i) { - for (j = x0; j < x1; ++j) { - gint y = dest[(i * dest_stride) + j] + overlay->shading_value; - - dest[(i * dest_stride) + j] = CLAMP (y, 0, 255); - } - } -} - -static inline void -gst_text_overlay_shade_UYVY_y (GstTextOverlay * overlay, guchar * dest, - gint x0, gint x1, gint y0, gint y1) -{ - gint i, j; - guint dest_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_UYVY, 0, - overlay->width); - - x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width); - x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width); - - y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height); - y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height); - - for (i = y0; i < y1; i++) { - for (j = x0; j < x1; j++) { - gint y; - gint y_pos; - - y_pos = (i * dest_stride) + j * 2 + 1; - y = dest[y_pos] + overlay->shading_value; - - dest[y_pos] = CLAMP (y, 0, 255); - } - } -} - -#define gst_text_overlay_shade_BGRx gst_text_overlay_shade_xRGB -static inline void -gst_text_overlay_shade_xRGB (GstTextOverlay * overlay, guchar * dest, - gint x0, gint x1, gint y0, gint y1) -{ - gint i, j; - - x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width); - x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width); - - y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height); - y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height); - - for (i = y0; i < y1; i++) { - for (j = x0; j < x1; j++) { - gint y, y_pos, k; - - y_pos = (i * 4 * overlay->width) + j * 4; - for (k = 0; k < 4; k++) { - y = dest[y_pos + k] + overlay->shading_value; - dest[y_pos + k] = CLAMP (y, 0, 255); - } - } - } -} - -/* FIXME: - * - use proper strides and offset for I420 - * - don't draw over the edge of the picture (try a longer - * text with a huge font size) - */ - -static inline void -gst_text_overlay_blit_I420 (GstTextOverlay * overlay, - guint8 * yuv_pixels, gint xpos, gint ypos) -{ - int y_stride, u_stride, v_stride; - int u_offset, v_offset; - int h, w; - - w = overlay->width; - h = overlay->height; - - y_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, w); - u_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, w); - v_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2, w); - u_offset = - gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1, w, h); - v_offset = - gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2, w, h); - - gst_text_overlay_blit_1 (overlay, yuv_pixels, xpos, ypos, overlay->text_image, - y_stride); - gst_text_overlay_blit_sub2x2cbcr (overlay, yuv_pixels + u_offset, - yuv_pixels + v_offset, xpos, ypos, overlay->text_image, u_stride, - v_stride); -} - -static inline void -gst_text_overlay_blit_UYVY (GstTextOverlay * overlay, - guint8 * yuv_pixels, gint xpos, gint ypos) -{ - int a0, r0, g0, b0; - int a1, r1, g1, b1; - int y0, y1, u, v; - int i, j; - int h, w; - guchar *pimage, *dest; - - w = overlay->image_width - 2; - h = overlay->image_height - 2; - - if (xpos < 0) { - xpos = 0; - } - - if (xpos + w > overlay->width) { - w = overlay->width - xpos; - } - - if (ypos + h > overlay->height) { - h = overlay->height - ypos; - } - - for (i = 0; i < h; i++) { - pimage = overlay->text_image + i * overlay->image_width * 4; - dest = yuv_pixels + (i + ypos) * overlay->width * 2 + xpos * 2; - for (j = 0; j < w; j += 2) { - b0 = pimage[CAIRO_ARGB_B]; - g0 = pimage[CAIRO_ARGB_G]; - r0 = pimage[CAIRO_ARGB_R]; - a0 = pimage[CAIRO_ARGB_A]; - CAIRO_UNPREMULTIPLY (a0, r0, g0, b0); - pimage += 4; - - b1 = pimage[CAIRO_ARGB_B]; - g1 = pimage[CAIRO_ARGB_G]; - r1 = pimage[CAIRO_ARGB_R]; - a1 = pimage[CAIRO_ARGB_A]; - CAIRO_UNPREMULTIPLY (a1, r1, g1, b1); - pimage += 4; - - a0 += a1 + 2; - a0 /= 2; - if (a0 == 0) { - dest += 4; - continue; - } - - COMP_Y (y0, r0, g0, b0); - COMP_Y (y1, r1, g1, b1); - - b0 += b1 + 2; - g0 += g1 + 2; - r0 += r1 + 2; - - b0 /= 2; - g0 /= 2; - r0 /= 2; - - COMP_U (u, r0, g0, b0); - COMP_V (v, r0, g0, b0); - - BLEND (*dest, a0, u, *dest); - dest++; - BLEND (*dest, a0, y0, *dest); - dest++; - BLEND (*dest, a0, v, *dest); - dest++; - BLEND (*dest, a0, y1, *dest); - dest++; - } - } -} - -#define xRGB_BLIT_FUNCTION(name, R, G, B) \ -static inline void \ -gst_text_overlay_blit_##name (GstTextOverlay * overlay, \ - guint8 * rgb_pixels, gint xpos, gint ypos) \ -{ \ - int a, r, g, b; \ - int i, j; \ - int h, w; \ - guchar *pimage, *dest; \ - \ - w = overlay->image_width; \ - h = overlay->image_height; \ - \ - if (xpos < 0) { \ - xpos = 0; \ - } \ - \ - if (xpos + w > overlay->width) { \ - w = overlay->width - xpos; \ - } \ - \ - if (ypos + h > overlay->height) { \ - h = overlay->height - ypos; \ - } \ - \ - for (i = 0; i < h; i++) { \ - pimage = overlay->text_image + i * overlay->image_width * 4; \ - dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \ - for (j = 0; j < w; j++) { \ - a = pimage[CAIRO_ARGB_A]; \ - b = pimage[CAIRO_ARGB_B]; \ - g = pimage[CAIRO_ARGB_G]; \ - r = pimage[CAIRO_ARGB_R]; \ - CAIRO_UNPREMULTIPLY (a, r, g, b); \ - b = (b*a + dest[B] * (255-a)) / 255; \ - g = (g*a + dest[G] * (255-a)) / 255; \ - r = (r*a + dest[R] * (255-a)) / 255; \ - \ - dest[B] = b; \ - dest[G] = g; \ - dest[R] = r; \ - pimage += 4; \ - dest += 4; \ - } \ - } \ -} -xRGB_BLIT_FUNCTION (xRGB, 1, 2, 3); -xRGB_BLIT_FUNCTION (BGRx, 2, 1, 0); - -static void -gst_text_overlay_render_text (GstTextOverlay * overlay, - const gchar * text, gint textlen) -{ - gchar *string; - - if (!overlay->need_render) { - GST_DEBUG ("Using previously rendered text."); - return; - } - - /* -1 is the whole string */ - if (text != NULL && textlen < 0) { - textlen = strlen (text); - } - - if (text != NULL) { - string = g_strndup (text, textlen); - } else { /* empty string */ - string = g_strdup (" "); - } - g_strdelimit (string, "\r\t", ' '); - textlen = strlen (string); - - /* FIXME: should we check for UTF-8 here? */ - - GST_DEBUG ("Rendering '%s'", string); - gst_text_overlay_render_pangocairo (overlay, string, textlen); - - g_free (string); - - overlay->need_render = FALSE; -} - -static GstFlowReturn -gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame) -{ - gint xpos, ypos; - gint width, height; - GstTextOverlayVAlign valign; - GstTextOverlayHAlign halign; - - width = overlay->image_width; - height = overlay->image_height; - - video_frame = gst_buffer_make_writable (video_frame); - - if (overlay->use_vertical_render) - halign = GST_TEXT_OVERLAY_HALIGN_RIGHT; - else - halign = overlay->halign; - - switch (halign) { - case GST_TEXT_OVERLAY_HALIGN_LEFT: - xpos = overlay->xpad; - break; - case GST_TEXT_OVERLAY_HALIGN_CENTER: - xpos = (overlay->width - width) / 2; - break; - case GST_TEXT_OVERLAY_HALIGN_RIGHT: - xpos = overlay->width - width - overlay->xpad; - break; - default: - xpos = 0; - } - xpos += overlay->deltax; - - if (overlay->use_vertical_render) - valign = GST_TEXT_OVERLAY_VALIGN_TOP; - else - valign = overlay->valign; - - switch (valign) { - case GST_TEXT_OVERLAY_VALIGN_BOTTOM: - ypos = overlay->height - height - overlay->ypad; - break; - case GST_TEXT_OVERLAY_VALIGN_BASELINE: - ypos = overlay->height - (height + overlay->ypad); - break; - case GST_TEXT_OVERLAY_VALIGN_TOP: - ypos = overlay->ypad; - break; - default: - ypos = overlay->ypad; - break; - } - ypos += overlay->deltay; - - /* shaded background box */ - if (overlay->want_shading) { - switch (overlay->format) { - case GST_VIDEO_FORMAT_I420: - gst_text_overlay_shade_I420_y (overlay, - GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width, - ypos, ypos + overlay->image_height); - break; - case GST_VIDEO_FORMAT_UYVY: - gst_text_overlay_shade_UYVY_y (overlay, - GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width, - ypos, ypos + overlay->image_height); - break; - case GST_VIDEO_FORMAT_xRGB: - gst_text_overlay_shade_xRGB (overlay, - GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width, - ypos, ypos + overlay->image_height); - break; - case GST_VIDEO_FORMAT_BGRx: - gst_text_overlay_shade_BGRx (overlay, - GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width, - ypos, ypos + overlay->image_height); - break; - default: - g_assert_not_reached (); - } - } - - if (ypos < 0) - ypos = 0; - - if (overlay->text_image) { - switch (overlay->format) { - case GST_VIDEO_FORMAT_I420: - gst_text_overlay_blit_I420 (overlay, - GST_BUFFER_DATA (video_frame), xpos, ypos); - break; - case GST_VIDEO_FORMAT_UYVY: - gst_text_overlay_blit_UYVY (overlay, - GST_BUFFER_DATA (video_frame), xpos, ypos); - break; - case GST_VIDEO_FORMAT_BGRx: - gst_text_overlay_blit_BGRx (overlay, - GST_BUFFER_DATA (video_frame), xpos, ypos); - break; - case GST_VIDEO_FORMAT_xRGB: - gst_text_overlay_blit_xRGB (overlay, - GST_BUFFER_DATA (video_frame), xpos, ypos); - break; - default: - g_assert_not_reached (); - } - } - return gst_pad_push (overlay->srcpad, video_frame); -} - -static GstPadLinkReturn -gst_text_overlay_text_pad_link (GstPad * pad, GstPad * peer) -{ - GstTextOverlay *overlay; - - overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - - GST_DEBUG_OBJECT (overlay, "Text pad linked"); - - overlay->text_linked = TRUE; - - gst_object_unref (overlay); - - return GST_PAD_LINK_OK; -} - -static void -gst_text_overlay_text_pad_unlink (GstPad * pad) -{ - GstTextOverlay *overlay; - - /* don't use gst_pad_get_parent() here, will deadlock */ - overlay = GST_TEXT_OVERLAY (GST_PAD_PARENT (pad)); - - GST_DEBUG_OBJECT (overlay, "Text pad unlinked"); - - overlay->text_linked = FALSE; - - gst_segment_init (&overlay->text_segment, GST_FORMAT_UNDEFINED); -} - -static gboolean -gst_text_overlay_text_event (GstPad * pad, GstEvent * event) -{ - gboolean ret = FALSE; - GstTextOverlay *overlay = NULL; - - overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - - GST_LOG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_NEWSEGMENT:{ - GstFormat fmt; - gboolean update; - gdouble rate, applied_rate; - gint64 cur, stop, time; - - overlay->text_eos = FALSE; - - gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, - &fmt, &cur, &stop, &time); - - if (fmt == GST_FORMAT_TIME) { - GST_OBJECT_LOCK (overlay); - gst_segment_set_newsegment_full (&overlay->text_segment, update, rate, - applied_rate, GST_FORMAT_TIME, cur, stop, time); - GST_DEBUG_OBJECT (overlay, "TEXT SEGMENT now: %" GST_SEGMENT_FORMAT, - &overlay->text_segment); - GST_OBJECT_UNLOCK (overlay); - } else { - GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL), - ("received non-TIME newsegment event on text input")); - } - - gst_event_unref (event); - ret = TRUE; - - /* wake up the video chain, it might be waiting for a text buffer or - * a text segment update */ - GST_OBJECT_LOCK (overlay); - GST_TEXT_OVERLAY_BROADCAST (overlay); - GST_OBJECT_UNLOCK (overlay); - break; - } - case GST_EVENT_FLUSH_STOP: - GST_OBJECT_LOCK (overlay); - GST_INFO_OBJECT (overlay, "text flush stop"); - overlay->text_flushing = FALSE; - overlay->text_eos = FALSE; - gst_text_overlay_pop_text (overlay); - gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME); - GST_OBJECT_UNLOCK (overlay); - gst_event_unref (event); - ret = TRUE; - break; - case GST_EVENT_FLUSH_START: - GST_OBJECT_LOCK (overlay); - GST_INFO_OBJECT (overlay, "text flush start"); - overlay->text_flushing = TRUE; - GST_TEXT_OVERLAY_BROADCAST (overlay); - GST_OBJECT_UNLOCK (overlay); - gst_event_unref (event); - ret = TRUE; - break; - case GST_EVENT_EOS: - GST_OBJECT_LOCK (overlay); - overlay->text_eos = TRUE; - GST_INFO_OBJECT (overlay, "text EOS"); - /* wake up the video chain, it might be waiting for a text buffer or - * a text segment update */ - GST_TEXT_OVERLAY_BROADCAST (overlay); - GST_OBJECT_UNLOCK (overlay); - gst_event_unref (event); - ret = TRUE; - break; - default: - ret = gst_pad_event_default (pad, event); - break; - } - - gst_object_unref (overlay); - - return ret; -} - -static gboolean -gst_text_overlay_video_event (GstPad * pad, GstEvent * event) -{ - gboolean ret = FALSE; - GstTextOverlay *overlay = NULL; - - overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - - GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_NEWSEGMENT: - { - GstFormat format; - gdouble rate; - gint64 start, stop, time; - gboolean update; - - GST_DEBUG_OBJECT (overlay, "received new segment"); - - gst_event_parse_new_segment (event, &update, &rate, &format, &start, - &stop, &time); - - if (format == GST_FORMAT_TIME) { - GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT, - &overlay->segment); - - gst_segment_set_newsegment (&overlay->segment, update, rate, format, - start, stop, time); - } else { - GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL), - ("received non-TIME newsegment event on video input")); - } - - ret = gst_pad_event_default (pad, event); - break; - } - case GST_EVENT_EOS: - GST_OBJECT_LOCK (overlay); - GST_INFO_OBJECT (overlay, "video EOS"); - overlay->video_eos = TRUE; - GST_OBJECT_UNLOCK (overlay); - ret = gst_pad_event_default (pad, event); - break; - case GST_EVENT_FLUSH_START: - GST_OBJECT_LOCK (overlay); - GST_INFO_OBJECT (overlay, "video flush start"); - overlay->video_flushing = TRUE; - GST_TEXT_OVERLAY_BROADCAST (overlay); - GST_OBJECT_UNLOCK (overlay); - ret = gst_pad_event_default (pad, event); - break; - case GST_EVENT_FLUSH_STOP: - GST_OBJECT_LOCK (overlay); - GST_INFO_OBJECT (overlay, "video flush stop"); - overlay->video_flushing = FALSE; - overlay->video_eos = FALSE; - gst_segment_init (&overlay->segment, GST_FORMAT_TIME); - GST_OBJECT_UNLOCK (overlay); - ret = gst_pad_event_default (pad, event); - break; - default: - ret = gst_pad_event_default (pad, event); - break; - } - - gst_object_unref (overlay); - - return ret; -} - -static GstFlowReturn -gst_text_overlay_video_bufferalloc (GstPad * pad, guint64 offset, guint size, - GstCaps * caps, GstBuffer ** buffer) -{ - GstTextOverlay *overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - GstFlowReturn ret = GST_FLOW_WRONG_STATE; - GstPad *allocpad; - - GST_OBJECT_LOCK (overlay); - allocpad = overlay->srcpad ? gst_object_ref (overlay->srcpad) : NULL; - GST_OBJECT_UNLOCK (overlay); - - if (allocpad) { - ret = gst_pad_alloc_buffer (allocpad, offset, size, caps, buffer); - gst_object_unref (allocpad); - } - - gst_object_unref (overlay); - return ret; -} - -/* Called with lock held */ -static void -gst_text_overlay_pop_text (GstTextOverlay * overlay) -{ - g_return_if_fail (GST_IS_TEXT_OVERLAY (overlay)); - - if (overlay->text_buffer) { - GST_DEBUG_OBJECT (overlay, "releasing text buffer %p", - overlay->text_buffer); - gst_buffer_unref (overlay->text_buffer); - overlay->text_buffer = NULL; - } - - /* Let the text task know we used that buffer */ - GST_TEXT_OVERLAY_BROADCAST (overlay); -} - -/* We receive text buffers here. If they are out of segment we just ignore them. - If the buffer is in our segment we keep it internally except if another one - is already waiting here, in that case we wait that it gets kicked out */ -static GstFlowReturn -gst_text_overlay_text_chain (GstPad * pad, GstBuffer * buffer) -{ - GstFlowReturn ret = GST_FLOW_OK; - GstTextOverlay *overlay = NULL; - gboolean in_seg = FALSE; - gint64 clip_start = 0, clip_stop = 0; - - overlay = GST_TEXT_OVERLAY (GST_PAD_PARENT (pad)); - - GST_OBJECT_LOCK (overlay); - - if (overlay->text_flushing) { - GST_OBJECT_UNLOCK (overlay); - ret = GST_FLOW_WRONG_STATE; - GST_LOG_OBJECT (overlay, "text flushing"); - goto beach; - } - - if (overlay->text_eos) { - GST_OBJECT_UNLOCK (overlay); - ret = GST_FLOW_UNEXPECTED; - GST_LOG_OBJECT (overlay, "text EOS"); - goto beach; - } - - GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%" - GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) + - GST_BUFFER_DURATION (buffer))); - - if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) { - GstClockTime stop; - - if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer))) - stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer); - else - stop = GST_CLOCK_TIME_NONE; - - in_seg = gst_segment_clip (&overlay->text_segment, GST_FORMAT_TIME, - GST_BUFFER_TIMESTAMP (buffer), stop, &clip_start, &clip_stop); - } else { - in_seg = TRUE; - } - - if (in_seg) { - if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) - GST_BUFFER_TIMESTAMP (buffer) = clip_start; - else if (GST_BUFFER_DURATION_IS_VALID (buffer)) - GST_BUFFER_DURATION (buffer) = clip_stop - clip_start; - - /* Wait for the previous buffer to go away */ - while (overlay->text_buffer != NULL) { - GST_DEBUG ("Pad %s:%s has a buffer queued, waiting", - GST_DEBUG_PAD_NAME (pad)); - GST_TEXT_OVERLAY_WAIT (overlay); - GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad)); - if (overlay->text_flushing) { - GST_OBJECT_UNLOCK (overlay); - ret = GST_FLOW_WRONG_STATE; - goto beach; - } - } - - if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) - gst_segment_set_last_stop (&overlay->text_segment, GST_FORMAT_TIME, - clip_start); - - overlay->text_buffer = buffer; - /* That's a new text buffer we need to render */ - overlay->need_render = TRUE; - - /* in case the video chain is waiting for a text buffer, wake it up */ - GST_TEXT_OVERLAY_BROADCAST (overlay); - } - - GST_OBJECT_UNLOCK (overlay); - -beach: - - return ret; -} - -static GstFlowReturn -gst_text_overlay_video_chain (GstPad * pad, GstBuffer * buffer) -{ - GstTextOverlayClass *klass; - GstTextOverlay *overlay; - GstFlowReturn ret = GST_FLOW_OK; - gboolean in_seg = FALSE; - gint64 start, stop, clip_start = 0, clip_stop = 0; - gchar *text = NULL; - - overlay = GST_TEXT_OVERLAY (GST_PAD_PARENT (pad)); - klass = GST_TEXT_OVERLAY_GET_CLASS (overlay); - - if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) - goto missing_timestamp; - - /* ignore buffers that are outside of the current segment */ - start = GST_BUFFER_TIMESTAMP (buffer); - - if (!GST_BUFFER_DURATION_IS_VALID (buffer)) { - stop = GST_CLOCK_TIME_NONE; - } else { - stop = start + GST_BUFFER_DURATION (buffer); - } - - GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%" - GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment, - GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); - - /* segment_clip() will adjust start unconditionally to segment_start if - * no stop time is provided, so handle this ourselves */ - if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment.start) - goto out_of_segment; - - in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop, - &clip_start, &clip_stop); - - if (!in_seg) - goto out_of_segment; - - /* if the buffer is only partially in the segment, fix up stamps */ - if (clip_start != start || (stop != -1 && clip_stop != stop)) { - GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment"); - buffer = gst_buffer_make_metadata_writable (buffer); - GST_BUFFER_TIMESTAMP (buffer) = clip_start; - if (stop != -1) - GST_BUFFER_DURATION (buffer) = clip_stop - clip_start; - } - - /* now, after we've done the clipping, fix up end time if there's no - * duration (we only use those estimated values internally though, we - * don't want to set bogus values on the buffer itself) */ - if (stop == -1) { - GstStructure *s; - gint fps_num, fps_denom; - - s = gst_caps_get_structure (GST_PAD_CAPS (pad), 0); - if (gst_structure_get_fraction (s, "framerate", &fps_num, &fps_denom) && - fps_num && fps_denom) { - GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate"); - stop = start + gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num); - } else { - GST_WARNING_OBJECT (overlay, "no duration, assuming minimal duration"); - stop = start + 1; /* we need to assume some interval */ - } - } - -wait_for_text_buf: - - GST_OBJECT_LOCK (overlay); - - if (overlay->video_flushing) - goto flushing; - - if (overlay->video_eos) - goto have_eos; - - if (overlay->silent) { - GST_OBJECT_UNLOCK (overlay); - ret = gst_pad_push (overlay->srcpad, buffer); - - /* Update last_stop */ - gst_segment_set_last_stop (&overlay->segment, GST_FORMAT_TIME, clip_start); - - return ret; - } - - /* Text pad not linked, rendering internal text */ - if (!overlay->text_linked) { - if (klass->get_text) { - text = klass->get_text (overlay, buffer); - } else { - text = g_strdup (overlay->default_text); - } - - GST_LOG_OBJECT (overlay, "Text pad not linked, rendering default " - "text: '%s'", GST_STR_NULL (text)); - - GST_OBJECT_UNLOCK (overlay); - - if (text != NULL && *text != '\0') { - /* Render and push */ - gst_text_overlay_render_text (overlay, text, -1); - ret = gst_text_overlay_push_frame (overlay, buffer); - } else { - /* Invalid or empty string */ - ret = gst_pad_push (overlay->srcpad, buffer); - } - } else { - /* Text pad linked, check if we have a text buffer queued */ - if (overlay->text_buffer) { - gboolean pop_text = FALSE, valid_text_time = TRUE; - GstClockTime text_start = GST_CLOCK_TIME_NONE; - GstClockTime text_end = GST_CLOCK_TIME_NONE; - GstClockTime text_running_time = GST_CLOCK_TIME_NONE; - GstClockTime text_running_time_end = GST_CLOCK_TIME_NONE; - GstClockTime vid_running_time, vid_running_time_end; - - /* if the text buffer isn't stamped right, pop it off the - * queue and display it for the current video frame only */ - if (!GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer) || - !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) { - GST_WARNING_OBJECT (overlay, - "Got text buffer with invalid timestamp or duration"); - pop_text = TRUE; - valid_text_time = FALSE; - } else { - text_start = GST_BUFFER_TIMESTAMP (overlay->text_buffer); - text_end = text_start + GST_BUFFER_DURATION (overlay->text_buffer); - } - - vid_running_time = - gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME, - start); - vid_running_time_end = - gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME, - stop); - - /* If timestamp and duration are valid */ - if (valid_text_time) { - text_running_time = - gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME, - text_start); - text_running_time_end = - gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME, - text_end); - } - - GST_LOG_OBJECT (overlay, "T: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT, - GST_TIME_ARGS (text_running_time), - GST_TIME_ARGS (text_running_time_end)); - GST_LOG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT, - GST_TIME_ARGS (vid_running_time), - GST_TIME_ARGS (vid_running_time_end)); - - /* Text too old or in the future */ - if (valid_text_time && text_running_time_end <= vid_running_time) { - /* text buffer too old, get rid of it and do nothing */ - GST_LOG_OBJECT (overlay, "text buffer too old, popping"); - pop_text = FALSE; - gst_text_overlay_pop_text (overlay); - GST_OBJECT_UNLOCK (overlay); - goto wait_for_text_buf; - } else if (valid_text_time && vid_running_time_end <= text_running_time) { - GST_LOG_OBJECT (overlay, "text in future, pushing video buf"); - GST_OBJECT_UNLOCK (overlay); - /* Push the video frame */ - ret = gst_pad_push (overlay->srcpad, buffer); - } else { - gchar *in_text; - gsize in_size; - - in_text = (gchar *) GST_BUFFER_DATA (overlay->text_buffer); - in_size = GST_BUFFER_SIZE (overlay->text_buffer); - - /* g_markup_escape_text() absolutely requires valid UTF8 input, it - * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING - * here on purpose, this is something that needs fixing upstream */ - if (!g_utf8_validate (in_text, in_size, NULL)) { - const gchar *end = NULL; - - GST_WARNING_OBJECT (overlay, "received invalid UTF-8"); - in_text = g_strndup (in_text, in_size); - while (!g_utf8_validate (in_text, in_size, &end) && end) - *((gchar *) end) = '*'; - } - - /* Get the string */ - if (overlay->have_pango_markup) { - text = g_strndup (in_text, in_size); - } else { - text = g_markup_escape_text (in_text, in_size); - } - - if (text != NULL && *text != '\0') { - gint text_len = strlen (text); - - while (text_len > 0 && (text[text_len - 1] == '\n' || - text[text_len - 1] == '\r')) { - --text_len; - } - GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text); - gst_text_overlay_render_text (overlay, text, text_len); - } else { - GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)"); - gst_text_overlay_render_text (overlay, " ", 1); - } - - if (in_text != (gchar *) GST_BUFFER_DATA (overlay->text_buffer)) - g_free (in_text); - - GST_OBJECT_UNLOCK (overlay); - ret = gst_text_overlay_push_frame (overlay, buffer); - - if (valid_text_time && text_running_time_end <= vid_running_time_end) { - GST_LOG_OBJECT (overlay, "text buffer not needed any longer"); - pop_text = TRUE; - } - } - if (pop_text) { - GST_OBJECT_LOCK (overlay); - gst_text_overlay_pop_text (overlay); - GST_OBJECT_UNLOCK (overlay); - } - } else { - gboolean wait_for_text_buf = TRUE; - - if (overlay->text_eos) - wait_for_text_buf = FALSE; - - if (!overlay->wait_text) - wait_for_text_buf = FALSE; - - /* Text pad linked, but no text buffer available - what now? */ - if (overlay->text_segment.format == GST_FORMAT_TIME) { - GstClockTime text_start_running_time, text_last_stop_running_time; - GstClockTime vid_running_time; - - vid_running_time = - gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME, - GST_BUFFER_TIMESTAMP (buffer)); - text_start_running_time = - gst_segment_to_running_time (&overlay->text_segment, - GST_FORMAT_TIME, overlay->text_segment.start); - text_last_stop_running_time = - gst_segment_to_running_time (&overlay->text_segment, - GST_FORMAT_TIME, overlay->text_segment.last_stop); - - if ((GST_CLOCK_TIME_IS_VALID (text_start_running_time) && - vid_running_time < text_start_running_time) || - (GST_CLOCK_TIME_IS_VALID (text_last_stop_running_time) && - vid_running_time < text_last_stop_running_time)) { - wait_for_text_buf = FALSE; - } - } - - if (wait_for_text_buf) { - GST_DEBUG_OBJECT (overlay, "no text buffer, need to wait for one"); - GST_TEXT_OVERLAY_WAIT (overlay); - GST_DEBUG_OBJECT (overlay, "resuming"); - GST_OBJECT_UNLOCK (overlay); - goto wait_for_text_buf; - } else { - GST_OBJECT_UNLOCK (overlay); - GST_LOG_OBJECT (overlay, "no need to wait for a text buffer"); - ret = gst_pad_push (overlay->srcpad, buffer); - } - } - } - - g_free (text); - - /* Update last_stop */ - gst_segment_set_last_stop (&overlay->segment, GST_FORMAT_TIME, clip_start); - - return ret; - -missing_timestamp: - { - GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding"); - gst_buffer_unref (buffer); - return GST_FLOW_OK; - } - -flushing: - { - GST_OBJECT_UNLOCK (overlay); - GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer"); - gst_buffer_unref (buffer); - return GST_FLOW_WRONG_STATE; - } -have_eos: - { - GST_OBJECT_UNLOCK (overlay); - GST_DEBUG_OBJECT (overlay, "eos, discarding buffer"); - gst_buffer_unref (buffer); - return GST_FLOW_UNEXPECTED; - } -out_of_segment: - { - GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding"); - gst_buffer_unref (buffer); - return GST_FLOW_OK; - } -} - -static GstStateChangeReturn -gst_text_overlay_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - GstTextOverlay *overlay = GST_TEXT_OVERLAY (element); - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_OBJECT_LOCK (overlay); - overlay->text_flushing = TRUE; - overlay->video_flushing = TRUE; - /* pop_text will broadcast on the GCond and thus also make the video - * chain exit if it's waiting for a text buffer */ - gst_text_overlay_pop_text (overlay); - GST_OBJECT_UNLOCK (overlay); - break; - default: - break; - } - - ret = parent_class->change_state (element, transition); - if (ret == GST_STATE_CHANGE_FAILURE) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - GST_OBJECT_LOCK (overlay); - overlay->text_flushing = FALSE; - overlay->video_flushing = FALSE; - overlay->video_eos = FALSE; - overlay->text_eos = FALSE; - gst_segment_init (&overlay->segment, GST_FORMAT_TIME); - gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME); - GST_OBJECT_UNLOCK (overlay); - break; - default: - break; - } - - return ret; -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE, - GST_TYPE_TEXT_OVERLAY) || - !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE, - GST_TYPE_TIME_OVERLAY) || - !gst_element_register (plugin, "clockoverlay", GST_RANK_NONE, - GST_TYPE_CLOCK_OVERLAY) || - !gst_element_register (plugin, "textrender", GST_RANK_NONE, - GST_TYPE_TEXT_RENDER)) { - return FALSE; - } - - /*texttestsrc_plugin_init(module, plugin); */ - - GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements"); - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, - "pango", "Pango-based text rendering and overlay", plugin_init, - VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/pango/gsttextoverlay.h b/ext/pango/gsttextoverlay.h deleted file mode 100644 index 4590b270..00000000 --- a/ext/pango/gsttextoverlay.h +++ /dev/null @@ -1,158 +0,0 @@ -#ifndef __GST_TEXT_OVERLAY_H__ -#define __GST_TEXT_OVERLAY_H__ - -#include <gst/gst.h> -#include <gst/video/video.h> -#include <pango/pangocairo.h> - -G_BEGIN_DECLS - -#define GST_TYPE_TEXT_OVERLAY (gst_text_overlay_get_type()) -#define GST_TEXT_OVERLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\ - GST_TYPE_TEXT_OVERLAY, GstTextOverlay)) -#define GST_TEXT_OVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),\ - GST_TYPE_TEXT_OVERLAY,GstTextOverlayClass)) -#define GST_TEXT_OVERLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ - GST_TYPE_TEXT_OVERLAY, GstTextOverlayClass)) -#define GST_IS_TEXT_OVERLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),\ - GST_TYPE_TEXT_OVERLAY)) -#define GST_IS_TEXT_OVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),\ - GST_TYPE_TEXT_OVERLAY)) - -typedef struct _GstTextOverlay GstTextOverlay; -typedef struct _GstTextOverlayClass GstTextOverlayClass; - -/** - * GstTextOverlayVAlign: - * @GST_TEXT_OVERLAY_VALIGN_BASELINE: draw text on the baseline - * @GST_TEXT_OVERLAY_VALIGN_BOTTOM: draw text on the bottom - * @GST_TEXT_OVERLAY_VALIGN_TOP: draw test on top - * - * Vertical alignment of the text. - */ -typedef enum { - GST_TEXT_OVERLAY_VALIGN_BASELINE, - GST_TEXT_OVERLAY_VALIGN_BOTTOM, - GST_TEXT_OVERLAY_VALIGN_TOP -} GstTextOverlayVAlign; - -/** - * GstTextOverlayHAlign: - * @GST_TEXT_OVERLAY_HALIGN_LEFT: align text left - * @GST_TEXT_OVERLAY_HALIGN_CENTER: align text center - * @GST_TEXT_OVERLAY_HALIGN_RIGHT: align text right - * - * Horizontal alignment of the text. - */ -typedef enum { - GST_TEXT_OVERLAY_HALIGN_LEFT, - GST_TEXT_OVERLAY_HALIGN_CENTER, - GST_TEXT_OVERLAY_HALIGN_RIGHT -} GstTextOverlayHAlign; - -/** - * GstTextOverlayWrapMode: - * @GST_TEXT_OVERLAY_WRAP_MODE_NONE: no wrapping - * @GST_TEXT_OVERLAY_WRAP_MODE_WORD: do word wrapping - * @GST_TEXT_OVERLAY_WRAP_MODE_CHAR: do char wrapping - * @GST_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR: do word and char wrapping - * - * Whether to wrap the text and if so how. - */ -typedef enum { - GST_TEXT_OVERLAY_WRAP_MODE_NONE = -1, - GST_TEXT_OVERLAY_WRAP_MODE_WORD = PANGO_WRAP_WORD, - GST_TEXT_OVERLAY_WRAP_MODE_CHAR = PANGO_WRAP_CHAR, - GST_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR = PANGO_WRAP_WORD_CHAR -} GstTextOverlayWrapMode; - -/** - * GstTextOverlayLineAlign: - * @GST_TEXT_OVERLAY_LINE_ALIGN_LEFT: lines are left-aligned - * @GST_TEXT_OVERLAY_LINE_ALIGN_CENTER: lines are center-aligned - * @GST_TEXT_OVERLAY_LINE_ALIGN_RIGHT: lines are right-aligned - * - * Alignment of text lines relative to each other - */ -typedef enum { - GST_TEXT_OVERLAY_LINE_ALIGN_LEFT = PANGO_ALIGN_LEFT, - GST_TEXT_OVERLAY_LINE_ALIGN_CENTER = PANGO_ALIGN_CENTER, - GST_TEXT_OVERLAY_LINE_ALIGN_RIGHT = PANGO_ALIGN_RIGHT -} GstTextOverlayLineAlign; - -/** - * GstTextOverlay: - * - * Opaque textoverlay object structure - */ -struct _GstTextOverlay { - GstElement element; - - GstPad *video_sinkpad; - GstPad *text_sinkpad; - GstPad *srcpad; - - GstSegment segment; - GstSegment text_segment; - GstBuffer *text_buffer; - gboolean text_linked; - gboolean video_flushing; - gboolean video_eos; - gboolean text_flushing; - gboolean text_eos; - - GCond *cond; /* to signal removal of a queued text - * buffer, arrival of a text buffer, - * a text segment update, or a change - * in status (e.g. shutdown, flushing) */ - - gint width; - gint height; - gint fps_n; - gint fps_d; - GstVideoFormat format; - - GstTextOverlayVAlign valign; - GstTextOverlayHAlign halign; - GstTextOverlayWrapMode wrap_mode; - GstTextOverlayLineAlign line_align; - - gint xpad; - gint ypad; - gint deltax; - gint deltay; - gchar *default_text; - gboolean want_shading; - gboolean silent; - gboolean wait_text; - - PangoLayout *layout; - gdouble shadow_offset; - gdouble outline_offset; - guchar *text_image; - gint image_width; - gint image_height; - gint baseline_y; - - gboolean auto_adjust_size; - gboolean need_render; - - gint shading_value; /* for timeoverlay subclass */ - - gboolean have_pango_markup; - gboolean use_vertical_render; -}; - -struct _GstTextOverlayClass { - GstElementClass parent_class; - - PangoContext *pango_context; - - gchar * (*get_text) (GstTextOverlay *overlay, GstBuffer *video_frame); -}; - -GType gst_text_overlay_get_type(void) G_GNUC_CONST; - -G_END_DECLS - -#endif /* __GST_TEXT_OVERLAY_H */ diff --git a/ext/pango/gsttextrender.c b/ext/pango/gsttextrender.c deleted file mode 100644 index c9372449..00000000 --- a/ext/pango/gsttextrender.c +++ /dev/null @@ -1,705 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * Copyright (C) <2003> David Schleef <ds@schleef.org> - * Copyright (C) <2009> Young-Ho Cha <ganadist@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-textrender - * @see_also: #GstTextOverlay - * - * This plugin renders text received on the text sink pad to a video - * buffer (retaining the alpha channel), so it can later be overlayed - * on top of video streams using other elements. - * - * The text can contain newline characters. (FIXME: What about text - * wrapping? It does not make sense in this context) - * - * <refsect2> - * <title>Example launch lines</title> - * |[ - * gst-launch -v filesrc location=subtitles.srt ! subparse ! textrender ! xvimagesink - * ]| - * </refsect2> - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <gst/gst.h> -#include <gst/video/video.h> - -#include "gsttextrender.h" -#include <string.h> - -#if G_BYTE_ORDER == G_LITTLE_ENDIAN -# define CAIRO_ARGB_A 3 -# define CAIRO_ARGB_R 2 -# define CAIRO_ARGB_G 1 -# define CAIRO_ARGB_B 0 -#else -# define CAIRO_ARGB_A 0 -# define CAIRO_ARGB_R 1 -# define CAIRO_ARGB_G 2 -# define CAIRO_ARGB_B 3 -#endif - -GST_DEBUG_CATEGORY_EXTERN (pango_debug); -#define GST_CAT_DEFAULT pango_debug - -#define MINIMUM_OUTLINE_OFFSET 1.0 - -#define DEFAULT_PROP_VALIGNMENT GST_TEXT_RENDER_VALIGN_BASELINE -#define DEFAULT_PROP_HALIGNMENT GST_TEXT_RENDER_HALIGN_CENTER -#define DEFAULT_PROP_LINE_ALIGNMENT GST_TEXT_RENDER_LINE_ALIGN_CENTER -#define DEFAULT_PROP_XPAD 25 -#define DEFAULT_PROP_YPAD 25 - -#define DEFAULT_RENDER_WIDTH 720 -#define DEFAULT_RENDER_HEIGHT 576 - -enum -{ - PROP_0, - PROP_HALIGNMENT, - PROP_VALIGNMENT, - PROP_LINE_ALIGNMENT, - PROP_XPAD, - PROP_YPAD, - PROP_FONT_DESC -}; - - -static GstStaticPadTemplate src_template_factory = - GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_ARGB) - ); - -static GstStaticPadTemplate sink_template_factory = - GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("text/x-pango-markup; text/plain") - ); - -#define GST_TYPE_TEXT_RENDER_VALIGN (gst_text_render_valign_get_type()) -static GType -gst_text_render_valign_get_type (void) -{ - static GType text_render_valign_type = 0; - static const GEnumValue text_render_valign[] = { - {GST_TEXT_RENDER_VALIGN_BASELINE, "baseline", "baseline"}, - {GST_TEXT_RENDER_VALIGN_BOTTOM, "bottom", "bottom"}, - {GST_TEXT_RENDER_VALIGN_TOP, "top", "top"}, - {0, NULL, NULL}, - }; - - if (!text_render_valign_type) { - text_render_valign_type = - g_enum_register_static ("GstTextRenderVAlign", text_render_valign); - } - return text_render_valign_type; -} - -#define GST_TYPE_TEXT_RENDER_HALIGN (gst_text_render_halign_get_type()) -static GType -gst_text_render_halign_get_type (void) -{ - static GType text_render_halign_type = 0; - static const GEnumValue text_render_halign[] = { - {GST_TEXT_RENDER_HALIGN_LEFT, "left", "left"}, - {GST_TEXT_RENDER_HALIGN_CENTER, "center", "center"}, - {GST_TEXT_RENDER_HALIGN_RIGHT, "right", "right"}, - {0, NULL, NULL}, - }; - - if (!text_render_halign_type) { - text_render_halign_type = - g_enum_register_static ("GstTextRenderHAlign", text_render_halign); - } - return text_render_halign_type; -} - -#define GST_TYPE_TEXT_RENDER_LINE_ALIGN (gst_text_render_line_align_get_type()) -static GType -gst_text_render_line_align_get_type (void) -{ - static GType text_render_line_align_type = 0; - static const GEnumValue text_render_line_align[] = { - {GST_TEXT_RENDER_LINE_ALIGN_LEFT, "left", "left"}, - {GST_TEXT_RENDER_LINE_ALIGN_CENTER, "center", "center"}, - {GST_TEXT_RENDER_LINE_ALIGN_RIGHT, "right", "right"}, - {0, NULL, NULL} - }; - - if (!text_render_line_align_type) { - text_render_line_align_type = - g_enum_register_static ("GstTextRenderLineAlign", - text_render_line_align); - } - return text_render_line_align_type; -} - -static void gst_text_render_adjust_values_with_fontdesc (GstTextRender * - render, PangoFontDescription * desc); - -GST_BOILERPLATE (GstTextRender, gst_text_render, GstElement, GST_TYPE_ELEMENT); - -static void gst_text_render_finalize (GObject * object); -static void gst_text_render_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_text_render_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); - -static void -gst_text_render_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_template_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_template_factory)); - - gst_element_class_set_details_simple (element_class, "Text renderer", - "Filter/Editor/Video", - "Renders a text string to an image bitmap", - "David Schleef <ds@schleef.org>, " - "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>"); -} - -static void -gst_text_render_class_init (GstTextRenderClass * klass) -{ - GObjectClass *gobject_class; - PangoFontMap *fontmap; - - gobject_class = (GObjectClass *) klass; - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->finalize = gst_text_render_finalize; - gobject_class->set_property = gst_text_render_set_property; - gobject_class->get_property = gst_text_render_get_property; - - fontmap = pango_cairo_font_map_get_default (); - klass->pango_context = - pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC, - g_param_spec_string ("font-desc", "font description", - "Pango font description of font " - "to be used for rendering. " - "See documentation of " - "pango_font_description_from_string" - " for syntax.", "", G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT, - g_param_spec_enum ("valignment", "vertical alignment", - "Vertical alignment of the text", GST_TYPE_TEXT_RENDER_VALIGN, - DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT, - g_param_spec_enum ("halignment", "horizontal alignment", - "Horizontal alignment of the text", GST_TYPE_TEXT_RENDER_HALIGN, - DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD, - g_param_spec_int ("xpad", "horizontal paddding", - "Horizontal paddding when using left/right alignment", 0, G_MAXINT, - DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD, - g_param_spec_int ("ypad", "vertical padding", - "Vertical padding when using top/bottom alignment", 0, G_MAXINT, - DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT, - g_param_spec_enum ("line-alignment", "line alignment", - "Alignment of text lines relative to each other.", - GST_TYPE_TEXT_RENDER_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -} - -static void -gst_text_render_adjust_values_with_fontdesc (GstTextRender * render, - PangoFontDescription * desc) -{ - gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE; - - render->shadow_offset = (double) (font_size) / 13.0; - render->outline_offset = (double) (font_size) / 15.0; - if (render->outline_offset < MINIMUM_OUTLINE_OFFSET) - render->outline_offset = MINIMUM_OUTLINE_OFFSET; -} - -static void -gst_text_render_render_pangocairo (GstTextRender * render) -{ - cairo_t *cr; - cairo_surface_t *surface; - cairo_t *cr_shadow; - cairo_surface_t *surface_shadow; - PangoRectangle ink_rect, logical_rect; - gint width, height; - - pango_layout_get_pixel_extents (render->layout, &ink_rect, &logical_rect); - - width = logical_rect.width + render->shadow_offset; - height = logical_rect.height + logical_rect.y + render->shadow_offset; - - surface_shadow = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); - cr_shadow = cairo_create (surface_shadow); - - /* clear shadow surface */ - cairo_set_operator (cr_shadow, CAIRO_OPERATOR_CLEAR); - cairo_paint (cr_shadow); - cairo_set_operator (cr_shadow, CAIRO_OPERATOR_OVER); - - /* draw shadow text */ - cairo_save (cr_shadow); - cairo_set_source_rgba (cr_shadow, 0.0, 0.0, 0.0, 0.5); - cairo_translate (cr_shadow, render->shadow_offset, render->shadow_offset); - pango_cairo_show_layout (cr_shadow, render->layout); - cairo_restore (cr_shadow); - - /* draw outline text */ - cairo_save (cr_shadow); - cairo_set_source_rgb (cr_shadow, 0.0, 0.0, 0.0); - cairo_set_line_width (cr_shadow, render->outline_offset); - pango_cairo_layout_path (cr_shadow, render->layout); - cairo_stroke (cr_shadow); - cairo_restore (cr_shadow); - - cairo_destroy (cr_shadow); - - render->text_image = g_realloc (render->text_image, 4 * width * height); - - surface = cairo_image_surface_create_for_data (render->text_image, - CAIRO_FORMAT_ARGB32, width, height, width * 4); - cr = cairo_create (surface); - cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); - cairo_paint (cr); - cairo_set_operator (cr, CAIRO_OPERATOR_OVER); - - /* set default color */ - cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); - - cairo_save (cr); - /* draw text */ - pango_cairo_show_layout (cr, render->layout); - cairo_restore (cr); - - /* composite shadow with offset */ - cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER); - cairo_set_source_surface (cr, surface_shadow, 0.0, 0.0); - cairo_paint (cr); - - cairo_destroy (cr); - cairo_surface_destroy (surface_shadow); - cairo_surface_destroy (surface); - render->image_width = width; - render->image_height = height; -} - -static void -gst_text_render_check_argb (GstTextRender * render) -{ - GstCaps *peer_caps; - peer_caps = gst_pad_get_allowed_caps (render->srcpad); - if (G_LIKELY (peer_caps)) { - guint i = 0, n = 0; - - n = gst_caps_get_size (peer_caps); - GST_DEBUG_OBJECT (render, "peer allowed caps (%u structure(s)) are %" - GST_PTR_FORMAT, n, peer_caps); - - /* Check if AYUV or ARGB is first */ - for (i = 0; i < n; i++) { - GstStructure *s = gst_caps_get_structure (peer_caps, i); - if (gst_structure_has_name (s, "video/x-raw-rgb") && - gst_structure_has_field (s, "alpha_mask")) { - render->use_ARGB = TRUE; - break; - } else if (gst_structure_has_name (s, "video/x-raw-yuv")) { - guint fourcc; - if (gst_structure_get_fourcc (s, "format", &fourcc) && - fourcc == GST_MAKE_FOURCC ('A', 'Y', 'U', 'V')) { - render->use_ARGB = FALSE; - break; - } - } - } - gst_caps_unref (peer_caps); - } -} - -static gboolean -gst_text_render_setcaps (GstPad * pad, GstCaps * caps) -{ - GstTextRender *render = GST_TEXT_RENDER (gst_pad_get_parent (pad)); - GstStructure *structure; - gboolean ret = FALSE; - gint width = 0, height = 0; - - structure = gst_caps_get_structure (caps, 0); - gst_structure_get_int (structure, "width", &width); - gst_structure_get_int (structure, "height", &height); - - GST_DEBUG ("Got caps %" GST_PTR_FORMAT, caps); - - if (width >= render->image_width && height >= render->image_height) { - render->width = width; - render->height = height; - ret = TRUE; - } - - gst_text_render_check_argb (render); - - gst_object_unref (render); - return ret; -} - -static void -gst_text_render_fixate_caps (GstPad * pad, GstCaps * caps) -{ - GstTextRender *render = GST_TEXT_RENDER (gst_pad_get_parent (pad)); - GstStructure *s = gst_caps_get_structure (caps, 0); - - GST_DEBUG ("Fixating caps %" GST_PTR_FORMAT, caps); - gst_structure_fixate_field_nearest_int (s, "width", render->image_width); - gst_structure_fixate_field_nearest_int (s, "height", render->image_height); - GST_DEBUG ("Fixated to %" GST_PTR_FORMAT, caps); - - gst_object_unref (render); -} - -#define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \ - b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \ - g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \ - r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \ -} G_STMT_END - -static void -gst_text_renderer_image_to_ayuv (GstTextRender * render, guchar * pixbuf, - int xpos, int ypos, int stride) -{ - int y; /* text bitmap coordinates */ - guchar *p, *bitp; - guchar a, r, g, b; - int width, height; - - width = render->image_width; - height = render->image_height; - bitp = render->text_image; - - for (y = 0; y < height; y++) { - int n; - p = pixbuf + (ypos + y) * stride + xpos * 4; - for (n = 0; n < width; n++) { - b = bitp[CAIRO_ARGB_B]; - g = bitp[CAIRO_ARGB_G]; - r = bitp[CAIRO_ARGB_R]; - a = bitp[CAIRO_ARGB_A]; - bitp += 4; - - /* Cairo uses pre-multiplied ARGB, unpremultiply it */ - CAIRO_UNPREMULTIPLY (a, r, g, b); - - *p++ = a; - *p++ = CLAMP ((int) (((19595 * r) >> 16) + ((38470 * g) >> 16) + - ((7471 * b) >> 16)), 0, 255); - *p++ = CLAMP ((int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) + - ((32768 * b) >> 16) + 128), 0, 255); - *p++ = CLAMP ((int) (((32768 * r) >> 16) - ((27439 * g) >> 16) - - ((5329 * b) >> 16) + 128), 0, 255); - } - } -} - -static void -gst_text_renderer_image_to_argb (GstTextRender * render, guchar * pixbuf, - int xpos, int ypos, int stride) -{ - int i, j; - guchar *p, *bitp; - int width, height; - - width = render->image_width; - height = render->image_height; - bitp = render->text_image; - - for (i = 0; i < height; i++) { - p = pixbuf + (ypos + i) * stride + xpos * 4; - for (j = 0; j < width; j++) { - p[0] = bitp[CAIRO_ARGB_A]; - p[1] = bitp[CAIRO_ARGB_R]; - p[2] = bitp[CAIRO_ARGB_G]; - p[3] = bitp[CAIRO_ARGB_B]; - - /* Cairo uses pre-multiplied ARGB, unpremultiply it */ - CAIRO_UNPREMULTIPLY (p[0], p[1], p[2], p[3]); - - bitp += 4; - p += 4; - } - } -} - -static GstFlowReturn -gst_text_render_chain (GstPad * pad, GstBuffer * inbuf) -{ - GstTextRender *render; - GstFlowReturn ret; - GstBuffer *outbuf; - GstCaps *caps = NULL; - guint8 *data = GST_BUFFER_DATA (inbuf); - guint size = GST_BUFFER_SIZE (inbuf); - gint n; - gint xpos, ypos; - - render = GST_TEXT_RENDER (gst_pad_get_parent (pad)); - - /* somehow pango barfs over "\0" buffers... */ - while (size > 0 && - (data[size - 1] == '\r' || - data[size - 1] == '\n' || data[size - 1] == '\0')) { - size--; - } - - /* render text */ - GST_DEBUG ("rendering '%*s'", size, data); - pango_layout_set_markup (render->layout, (gchar *) data, size); - gst_text_render_render_pangocairo (render); - - gst_text_render_check_argb (render); - - if (!render->use_ARGB) { - caps = - gst_video_format_new_caps (GST_VIDEO_FORMAT_AYUV, render->width, - render->height, 1, 1, 1, 1); - } else { - caps = - gst_video_format_new_caps (GST_VIDEO_FORMAT_ARGB, render->width, - render->height, 1, 1, 1, 1); - } - - if (!gst_pad_set_caps (render->srcpad, caps)) { - gst_caps_unref (caps); - GST_ELEMENT_ERROR (render, CORE, NEGOTIATION, (NULL), (NULL)); - ret = GST_FLOW_ERROR; - goto done; - } - - GST_DEBUG ("Allocating buffer WxH = %dx%d", render->width, render->height); - ret = - gst_pad_alloc_buffer_and_set_caps (render->srcpad, GST_BUFFER_OFFSET_NONE, - render->width * render->height * 4, caps, &outbuf); - - if (ret != GST_FLOW_OK) - goto done; - - gst_buffer_copy_metadata (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS); - data = GST_BUFFER_DATA (outbuf); - - if (render->use_ARGB) { - memset (data, 0, render->width * render->height * 4); - } else { - for (n = 0; n < render->width * render->height; n++) { - data[n * 4] = data[n * 4 + 1] = 0; - data[n * 4 + 2] = data[n * 4 + 3] = 128; - } - } - - switch (render->halign) { - case GST_TEXT_RENDER_HALIGN_LEFT: - xpos = render->xpad; - break; - case GST_TEXT_RENDER_HALIGN_CENTER: - xpos = (render->width - render->image_width) / 2; - break; - case GST_TEXT_RENDER_HALIGN_RIGHT: - xpos = render->width - render->image_width - render->xpad; - break; - default: - xpos = 0; - } - - switch (render->valign) { - case GST_TEXT_RENDER_VALIGN_BOTTOM: - ypos = render->height - render->image_height - render->ypad; - break; - case GST_TEXT_RENDER_VALIGN_BASELINE: - ypos = render->height - (render->image_height + render->ypad); - break; - case GST_TEXT_RENDER_VALIGN_TOP: - ypos = render->ypad; - break; - default: - ypos = render->ypad; - break; - } - - if (render->text_image) { - if (render->use_ARGB) { - gst_text_renderer_image_to_argb (render, data, xpos, ypos, - render->width * 4); - } else { - gst_text_renderer_image_to_ayuv (render, data, xpos, ypos, - render->width * 4); - } - } - - ret = gst_pad_push (render->srcpad, outbuf); - -done: - if (caps) - gst_caps_unref (caps); - gst_buffer_unref (inbuf); - gst_object_unref (render); - return ret; -} - -static void -gst_text_render_finalize (GObject * object) -{ - GstTextRender *render = GST_TEXT_RENDER (object); - - g_free (render->text_image); - - if (render->layout) - g_object_unref (render->layout); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_text_render_init (GstTextRender * render, GstTextRenderClass * klass) -{ - GstPadTemplate *template; - - /* sink */ - template = gst_static_pad_template_get (&sink_template_factory); - render->sinkpad = gst_pad_new_from_template (template, "sink"); - gst_object_unref (template); - gst_pad_set_chain_function (render->sinkpad, - GST_DEBUG_FUNCPTR (gst_text_render_chain)); - gst_element_add_pad (GST_ELEMENT (render), render->sinkpad); - - /* source */ - template = gst_static_pad_template_get (&src_template_factory); - render->srcpad = gst_pad_new_from_template (template, "src"); - gst_object_unref (template); - gst_pad_set_fixatecaps_function (render->srcpad, - GST_DEBUG_FUNCPTR (gst_text_render_fixate_caps)); - gst_pad_set_setcaps_function (render->srcpad, - GST_DEBUG_FUNCPTR (gst_text_render_setcaps)); - - gst_element_add_pad (GST_ELEMENT (render), render->srcpad); - - render->line_align = DEFAULT_PROP_LINE_ALIGNMENT; - render->layout = - pango_layout_new (GST_TEXT_RENDER_GET_CLASS (render)->pango_context); - pango_layout_set_alignment (render->layout, - (PangoAlignment) render->line_align); - - render->halign = DEFAULT_PROP_HALIGNMENT; - render->valign = DEFAULT_PROP_VALIGNMENT; - render->xpad = DEFAULT_PROP_XPAD; - render->ypad = DEFAULT_PROP_YPAD; - - render->width = DEFAULT_RENDER_WIDTH; - render->height = DEFAULT_RENDER_HEIGHT; - - render->use_ARGB = FALSE; -} - -static void -gst_text_render_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstTextRender *render = GST_TEXT_RENDER (object); - - switch (prop_id) { - case PROP_VALIGNMENT: - render->valign = g_value_get_enum (value); - break; - case PROP_HALIGNMENT: - render->halign = g_value_get_enum (value); - break; - case PROP_LINE_ALIGNMENT: - render->line_align = g_value_get_enum (value); - pango_layout_set_alignment (render->layout, - (PangoAlignment) render->line_align); - break; - case PROP_XPAD: - render->xpad = g_value_get_int (value); - break; - case PROP_YPAD: - render->ypad = g_value_get_int (value); - break; - case PROP_FONT_DESC: - { - PangoFontDescription *desc; - - desc = pango_font_description_from_string (g_value_get_string (value)); - if (desc) { - GST_LOG ("font description set: %s", g_value_get_string (value)); - GST_OBJECT_LOCK (render); - pango_layout_set_font_description (render->layout, desc); - gst_text_render_adjust_values_with_fontdesc (render, desc); - pango_font_description_free (desc); - gst_text_render_render_pangocairo (render); - GST_OBJECT_UNLOCK (render); - } else { - GST_WARNING ("font description parse failed: %s", - g_value_get_string (value)); - } - break; - } - - default: - break; - } -} - -static void -gst_text_render_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstTextRender *render = GST_TEXT_RENDER (object); - - switch (prop_id) { - case PROP_VALIGNMENT: - g_value_set_enum (value, render->valign); - break; - case PROP_HALIGNMENT: - g_value_set_enum (value, render->halign); - break; - case PROP_LINE_ALIGNMENT: - g_value_set_enum (value, render->line_align); - break; - case PROP_XPAD: - g_value_set_int (value, render->xpad); - break; - case PROP_YPAD: - g_value_set_int (value, render->ypad); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} diff --git a/ext/pango/gsttextrender.h b/ext/pango/gsttextrender.h deleted file mode 100644 index 66d37dbf..00000000 --- a/ext/pango/gsttextrender.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef __GST_TEXT_RENDER_H__ -#define __GST_TEXT_RENDER_H__ - -#include <gst/gst.h> -#include <pango/pangocairo.h> - -G_BEGIN_DECLS - -#define GST_TYPE_TEXT_RENDER (gst_text_render_get_type()) -#define GST_TEXT_RENDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\ - GST_TYPE_TEXT_RENDER, GstTextRender)) -#define GST_TEXT_RENDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),\ - GST_TYPE_TEXT_RENDER, GstTextRenderClass)) -#define GST_TEXT_RENDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ - GST_TYPE_TEXT_RENDER, GstTextRenderClass)) -#define GST_IS_TEXT_RENDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),\ - GST_TYPE_TEXT_RENDER)) -#define GST_IS_TEXT_RENDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),\ - GST_TYPE_TEXT_RENDER)) - -typedef struct _GstTextRender GstTextRender; -typedef struct _GstTextRenderClass GstTextRenderClass; - -/** - * GstTextRenderVAlign: - * @GST_TEXT_RENDER_VALIGN_BASELINE: draw text on the baseline - * @GST_TEXT_RENDER_VALIGN_BOTTOM: draw text on the bottom - * @GST_TEXT_RENDER_VALIGN_TOP: draw test on top - * - * Vertical alignment of the text. - */ -typedef enum { - GST_TEXT_RENDER_VALIGN_BASELINE, - GST_TEXT_RENDER_VALIGN_BOTTOM, - GST_TEXT_RENDER_VALIGN_TOP -} GstTextRenderVAlign; - -/** - * GstTextRenderHAlign: - * @GST_TEXT_RENDER_HALIGN_LEFT: align text left - * @GST_TEXT_RENDER_HALIGN_CENTER: align text center - * @GST_TEXT_RENDER_HALIGN_RIGHT: align text right - * - * Horizontal alignment of the text. - */ -typedef enum { - GST_TEXT_RENDER_HALIGN_LEFT, - GST_TEXT_RENDER_HALIGN_CENTER, - GST_TEXT_RENDER_HALIGN_RIGHT -} GstTextRenderHAlign; - -/** - * GstTextRenderLineAlign: - * @GST_TEXT_RENDER_LINE_ALIGN_LEFT: lines are left-aligned - * @GST_TEXT_RENDER_LINE_ALIGN_CENTER: lines are center-aligned - * @GST_TEXT_RENDER_LINE_ALIGN_RIGHT: lines are right-aligned - * - * Alignment of text lines relative to each other - */ -typedef enum { - GST_TEXT_RENDER_LINE_ALIGN_LEFT = PANGO_ALIGN_LEFT, - GST_TEXT_RENDER_LINE_ALIGN_CENTER = PANGO_ALIGN_CENTER, - GST_TEXT_RENDER_LINE_ALIGN_RIGHT = PANGO_ALIGN_RIGHT -} GstTextRenderLineAlign; - -/** - * GstTextRender: - * - * Opaque textrender data structure. - */ -struct _GstTextRender { - GstElement element; - - GstPad *sinkpad, *srcpad; - gint width; - gint height; - PangoLayout *layout; - gdouble shadow_offset; - gdouble outline_offset; - guchar *text_image; - gint image_width; - gint image_height; - gint baseline_y; - gboolean use_ARGB; - - GstTextRenderVAlign valign; - GstTextRenderHAlign halign; - GstTextRenderLineAlign line_align; - - gint xpad; - gint ypad; -}; - -struct _GstTextRenderClass { - GstElementClass parent_class; - - PangoContext *pango_context; -}; - -GType gst_text_render_get_type(void) G_GNUC_CONST; - -G_END_DECLS - -#endif /* __GST_TEXT_RENDER_H */ diff --git a/ext/pango/gsttimeoverlay.c b/ext/pango/gsttimeoverlay.c deleted file mode 100644 index a342a51e..00000000 --- a/ext/pango/gsttimeoverlay.c +++ /dev/null @@ -1,151 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * Copyright (C) <2005> Tim-Philipp Müller <tim@centricular.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-timeoverlay - * @see_also: #GstTextOverlay, #GstClockOverlay - * - * This element overlays the buffer time stamps of a video stream on - * top of itself. You can position the text and configure the font details - * using the properties of the #GstTextOverlay class. By default, the - * time stamp is displayed in the top left corner of the picture, with some - * padding to the left and to the top. - * - * <refsect2> - * |[ - * gst-launch -v videotestsrc ! timeoverlay ! xvimagesink - * ]| Display the time stamps in the top left - * corner of the video picture. - * |[ - * gst-launch -v videotestsrc ! timeoverlay halign=right valign=bottom text="Stream time:" shaded-background=true ! xvimagesink - * ]| Another pipeline that displays the time stamps with some leading - * text in the bottom right corner of the video picture, with the background - * of the text being shaded in order to make it more legible on top of a - * bright video background. - * </refsect2> - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <gst/video/video.h> - -#include <gsttimeoverlay.h> - -GST_BOILERPLATE (GstTimeOverlay, gst_time_overlay, GstTextOverlay, - GST_TYPE_TEXT_OVERLAY); - -static void -gst_time_overlay_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_details_simple (element_class, "Time overlay", - "Filter/Editor/Video", - "Overlays buffer time stamps on a video stream", - "Tim-Philipp Müller <tim@centricular.net>"); -} - -static gchar * -gst_time_overlay_render_time (GstTimeOverlay * overlay, GstClockTime time) -{ - guint hours, mins, secs, msecs; - - if (!GST_CLOCK_TIME_IS_VALID (time)) - return g_strdup (""); - - hours = (guint) (time / (GST_SECOND * 60 * 60)); - mins = (guint) ((time / (GST_SECOND * 60)) % 60); - secs = (guint) ((time / GST_SECOND) % 60); - msecs = (guint) ((time % GST_SECOND) / (1000 * 1000)); - - return g_strdup_printf ("%u:%02u:%02u.%03u", hours, mins, secs, msecs); -} - -/* Called with lock held */ -static gchar * -gst_time_overlay_get_text (GstTextOverlay * overlay, GstBuffer * video_frame) -{ - GstClockTime time = GST_BUFFER_TIMESTAMP (video_frame); - gchar *time_str, *txt, *ret; - - overlay->need_render = TRUE; - - if (!GST_CLOCK_TIME_IS_VALID (time)) { - GST_DEBUG ("buffer without valid timestamp"); - return g_strdup (""); - } - - GST_DEBUG ("buffer with timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (time)); - - txt = g_strdup (overlay->default_text); - - time_str = gst_time_overlay_render_time (GST_TIME_OVERLAY (overlay), time); - if (txt != NULL && *txt != '\0') { - ret = g_strdup_printf ("%s %s", txt, time_str); - } else { - ret = time_str; - time_str = NULL; - } - - g_free (txt); - g_free (time_str); - - return ret; -} - -static void -gst_time_overlay_class_init (GstTimeOverlayClass * klass) -{ - GstTextOverlayClass *gsttextoverlay_class; - - gsttextoverlay_class = (GstTextOverlayClass *) klass; - - gsttextoverlay_class->get_text = gst_time_overlay_get_text; -} - -static void -gst_time_overlay_init (GstTimeOverlay * overlay, GstTimeOverlayClass * klass) -{ - PangoFontDescription *font_description; - GstTextOverlay *textoverlay; - PangoContext *context; - - textoverlay = GST_TEXT_OVERLAY (overlay); - - context = GST_TEXT_OVERLAY_CLASS (klass)->pango_context; - - pango_context_set_language (context, pango_language_from_string ("en_US")); - pango_context_set_base_dir (context, PANGO_DIRECTION_LTR); - - font_description = pango_font_description_new (); - pango_font_description_set_family_static (font_description, "Monospace"); - pango_font_description_set_style (font_description, PANGO_STYLE_NORMAL); - pango_font_description_set_variant (font_description, PANGO_VARIANT_NORMAL); - pango_font_description_set_weight (font_description, PANGO_WEIGHT_NORMAL); - pango_font_description_set_stretch (font_description, PANGO_STRETCH_NORMAL); - pango_font_description_set_size (font_description, 18 * PANGO_SCALE); - pango_context_set_font_description (context, font_description); - pango_font_description_free (font_description); - - textoverlay->valign = GST_TEXT_OVERLAY_VALIGN_TOP; - textoverlay->halign = GST_TEXT_OVERLAY_HALIGN_LEFT; -} diff --git a/ext/pango/gsttimeoverlay.h b/ext/pango/gsttimeoverlay.h deleted file mode 100644 index 5fbfba97..00000000 --- a/ext/pango/gsttimeoverlay.h +++ /dev/null @@ -1,61 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * Copyright (C) <2005> Tim-Philipp Müller <tim@centricular.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_TIME_OVERLAY_H__ -#define __GST_TIME_OVERLAY_H__ - -#include "gsttextoverlay.h" - -G_BEGIN_DECLS - -#define GST_TYPE_TIME_OVERLAY \ - (gst_time_overlay_get_type()) -#define GST_TIME_OVERLAY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TIME_OVERLAY,GstTimeOverlay)) -#define GST_TIME_OVERLAY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TIME_OVERLAY,GstTimeOverlayClass)) -#define GST_IS_TIME_OVERLAY(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TIME_OVERLAY)) -#define GST_IS_TIME_OVERLAY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TIME_OVERLAY)) - -typedef struct _GstTimeOverlay GstTimeOverlay; -typedef struct _GstTimeOverlayClass GstTimeOverlayClass; - -/** - * GstTimeOverlay: - * - * Opaque timeoverlay data structure. - */ -struct _GstTimeOverlay { - GstTextOverlay textoverlay; -}; - -struct _GstTimeOverlayClass { - GstTextOverlayClass parent_class; -}; - -GType gst_time_overlay_get_type (void); - -G_END_DECLS - -#endif /* __GST_TIME_OVERLAY_H__ */ - diff --git a/ext/theora/Makefile.am b/ext/theora/Makefile.am deleted file mode 100644 index 52164b04..00000000 --- a/ext/theora/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -plugin_LTLIBRARIES = libgsttheora.la - -noinst_HEADERS = gsttheoraenc.h \ - gsttheoradec.h \ - gsttheoraparse.h - -libgsttheora_la_SOURCES = gsttheora.c \ - gsttheoraenc.c \ - gsttheoradec.c \ - gsttheoraparse.c - -libgsttheora_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(THEORA_CFLAGS) -libgsttheora_la_LIBADD = \ - $(top_builddir)/gst-libs/gst/tag/libgsttag-$(GST_MAJORMINOR).la \ - $(top_builddir)/gst-libs/gst/video/libgstvideo-$(GST_MAJORMINOR).la \ - $(GST_LIBS) \ - $(THEORA_LIBS) -libgsttheora_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgsttheora_la_LIBTOOLFLAGS = --tag=disable-static - diff --git a/ext/theora/gsttheora.c b/ext/theora/gsttheora.c deleted file mode 100644 index 37f32114..00000000 --- a/ext/theora/gsttheora.c +++ /dev/null @@ -1,52 +0,0 @@ -/* GStreamer - * Copyright (C) 2004 Benjamin Otte <in7y118@public.uni-hamburg.de> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <gst/gst.h> - -extern GType gst_theora_dec_get_type (void); -extern GType gst_theora_enc_get_type (void); -extern GType gst_theora_parse_get_type (void); - -static gboolean -plugin_init (GstPlugin * plugin) -{ - if (!gst_element_register (plugin, "theoradec", GST_RANK_PRIMARY, - gst_theora_dec_get_type ())) - return FALSE; - - if (!gst_element_register (plugin, "theoraenc", GST_RANK_PRIMARY, - gst_theora_enc_get_type ())) - return FALSE; - - if (!gst_element_register (plugin, "theoraparse", GST_RANK_NONE, - gst_theora_parse_get_type ())) - return FALSE; - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "theora", - "Theora plugin library", - plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/theora/gsttheoradec.c b/ext/theora/gsttheoradec.c deleted file mode 100644 index 7ec3e920..00000000 --- a/ext/theora/gsttheoradec.c +++ /dev/null @@ -1,1467 +0,0 @@ -/* GStreamer - * Copyright (C) 2004 Benjamin Otte <in7y118@public.uni-hamburg.de> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-theoradec - * @see_also: theoraenc, oggdemux - * - * This element decodes theora streams into raw video - * <ulink url="http://www.theora.org/">Theora</ulink> is a royalty-free - * video codec maintained by the <ulink url="http://www.xiph.org/">Xiph.org - * Foundation</ulink>, based on the VP3 codec. - * - * <refsect2> - * <title>Example pipeline</title> - * |[ - * gst-launch -v filesrc location=videotestsrc.ogg ! oggdemux ! theoradec ! xvimagesink - * ]| This example pipeline will decode an ogg stream and decodes the theora video. Refer to - * the theoraenc example to create the ogg file. - * </refsect2> - * - * Last reviewed on 2006-03-01 (0.10.4) - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "gsttheoradec.h" -#include <gst/tag/tag.h> -#include <gst/video/video.h> - -#define GST_CAT_DEFAULT theoradec_debug -GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); - -#define THEORA_DEF_CROP TRUE -enum -{ - ARG_0, - ARG_CROP -}; - -static const GstElementDetails theora_dec_details = -GST_ELEMENT_DETAILS ("Theora video decoder", - "Codec/Decoder/Video", - "decode raw theora streams to raw YUV video", - "Benjamin Otte <in7y118@public.uni-hamburg.de>, " - "Wim Taymans <wim@fluendo.com>"); - -static GstStaticPadTemplate theora_dec_src_factory = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-raw-yuv, " - "format = (fourcc) { I420, Y42B, Y444 }, " - "framerate = (fraction) [0/1, MAX], " - "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]") - ); - -static GstStaticPadTemplate theora_dec_sink_factory = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-theora") - ); - -GST_BOILERPLATE (GstTheoraDec, gst_theora_dec, GstElement, GST_TYPE_ELEMENT); - -static void theora_dec_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static void theora_dec_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); - -static gboolean theora_dec_sink_event (GstPad * pad, GstEvent * event); -static gboolean theora_dec_setcaps (GstPad * pad, GstCaps * caps); -static GstFlowReturn theora_dec_chain (GstPad * pad, GstBuffer * buffer); -static GstStateChangeReturn theora_dec_change_state (GstElement * element, - GstStateChange transition); -static gboolean theora_dec_src_event (GstPad * pad, GstEvent * event); -static gboolean theora_dec_src_query (GstPad * pad, GstQuery * query); -static gboolean theora_dec_src_convert (GstPad * pad, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value); - -#if 0 -static const GstFormat *theora_get_formats (GstPad * pad); -#endif -#if 0 -static const GstEventMask *theora_get_event_masks (GstPad * pad); -#endif -static const GstQueryType *theora_get_query_types (GstPad * pad); - - -static void -gst_theora_dec_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&theora_dec_src_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&theora_dec_sink_factory)); - gst_element_class_set_details (element_class, &theora_dec_details); -} - -static void -gst_theora_dec_class_init (GstTheoraDecClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - - gobject_class->set_property = theora_dec_set_property; - gobject_class->get_property = theora_dec_get_property; - - g_object_class_install_property (gobject_class, ARG_CROP, - g_param_spec_boolean ("crop", "Crop", - "Crop the image to the visible region", THEORA_DEF_CROP, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - gstelement_class->change_state = theora_dec_change_state; - - GST_DEBUG_CATEGORY_INIT (theoradec_debug, "theoradec", 0, "Theora decoder"); -} - -static void -gst_theora_dec_init (GstTheoraDec * dec, GstTheoraDecClass * g_class) -{ - dec->sinkpad = - gst_pad_new_from_static_template (&theora_dec_sink_factory, "sink"); - gst_pad_set_event_function (dec->sinkpad, theora_dec_sink_event); - gst_pad_set_setcaps_function (dec->sinkpad, theora_dec_setcaps); - gst_pad_set_chain_function (dec->sinkpad, theora_dec_chain); - gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad); - - dec->srcpad = - gst_pad_new_from_static_template (&theora_dec_src_factory, "src"); - gst_pad_set_event_function (dec->srcpad, theora_dec_src_event); - gst_pad_set_query_type_function (dec->srcpad, theora_get_query_types); - gst_pad_set_query_function (dec->srcpad, theora_dec_src_query); - gst_pad_use_fixed_caps (dec->srcpad); - - gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); - - dec->crop = THEORA_DEF_CROP; - dec->gather = NULL; - dec->decode = NULL; - dec->queued = NULL; - dec->pendingevents = NULL; -} - -static void -gst_theora_dec_reset (GstTheoraDec * dec) -{ - dec->need_keyframe = TRUE; - dec->last_timestamp = -1; - dec->discont = TRUE; - dec->frame_nr = -1; - dec->seqnum = gst_util_seqnum_next (); - gst_segment_init (&dec->segment, GST_FORMAT_TIME); - - GST_OBJECT_LOCK (dec); - dec->proportion = 1.0; - dec->earliest_time = -1; - GST_OBJECT_UNLOCK (dec); - - g_list_foreach (dec->queued, (GFunc) gst_mini_object_unref, NULL); - g_list_free (dec->queued); - dec->queued = NULL; - g_list_foreach (dec->gather, (GFunc) gst_mini_object_unref, NULL); - g_list_free (dec->gather); - dec->gather = NULL; - g_list_foreach (dec->decode, (GFunc) gst_mini_object_unref, NULL); - g_list_free (dec->decode); - dec->decode = NULL; - g_list_foreach (dec->pendingevents, (GFunc) gst_mini_object_unref, NULL); - g_list_free (dec->pendingevents); - dec->pendingevents = NULL; - - if (dec->tags) { - gst_tag_list_free (dec->tags); - dec->tags = NULL; - } -} - -#if 0 -static const GstFormat * -theora_get_formats (GstPad * pad) -{ - static GstFormat src_formats[] = { - GST_FORMAT_DEFAULT, /* frames in this case */ - GST_FORMAT_TIME, - GST_FORMAT_BYTES, - 0 - }; - static GstFormat sink_formats[] = { - GST_FORMAT_DEFAULT, - GST_FORMAT_TIME, - 0 - }; - - return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats); -} -#endif - -#if 0 -static const GstEventMask * -theora_get_event_masks (GstPad * pad) -{ - static const GstEventMask theora_src_event_masks[] = { - {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH}, - {0,} - }; - - return theora_src_event_masks; -} -#endif - -static const GstQueryType * -theora_get_query_types (GstPad * pad) -{ - static const GstQueryType theora_src_query_types[] = { - GST_QUERY_POSITION, - GST_QUERY_DURATION, - GST_QUERY_CONVERT, - 0 - }; - - return theora_src_query_types; -} - - -static gboolean -theora_dec_src_convert (GstPad * pad, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = TRUE; - GstTheoraDec *dec; - guint64 scale = 1; - - if (src_format == *dest_format) { - *dest_value = src_value; - return TRUE; - } - - dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); - - /* we need the info part before we can done something */ - if (!dec->have_header) - goto no_header; - - switch (src_format) { - case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_DEFAULT: - *dest_value = gst_util_uint64_scale_int (src_value, 8, - dec->info.pic_height * dec->info.pic_width * dec->output_bpp); - break; - case GST_FORMAT_TIME: - /* seems like a rather silly conversion, implement me if you like */ - default: - res = FALSE; - } - break; - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_BYTES: - scale = - dec->output_bpp * (dec->info.pic_width * dec->info.pic_height) / - 8; - case GST_FORMAT_DEFAULT: - *dest_value = scale * gst_util_uint64_scale (src_value, - dec->info.fps_numerator, dec->info.fps_denominator * GST_SECOND); - break; - default: - res = FALSE; - } - break; - case GST_FORMAT_DEFAULT: - switch (*dest_format) { - case GST_FORMAT_TIME: - *dest_value = gst_util_uint64_scale (src_value, - GST_SECOND * dec->info.fps_denominator, dec->info.fps_numerator); - break; - case GST_FORMAT_BYTES: - *dest_value = gst_util_uint64_scale_int (src_value, - dec->output_bpp * dec->info.pic_width * dec->info.pic_height, 8); - break; - default: - res = FALSE; - } - break; - default: - res = FALSE; - } -done: - gst_object_unref (dec); - return res; - - /* ERRORS */ -no_header: - { - GST_DEBUG_OBJECT (dec, "no header yet, cannot convert"); - res = FALSE; - goto done; - } -} - -#if 0 -static gboolean -theora_dec_sink_convert (GstPad * pad, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = TRUE; - GstTheoraDec *dec; - - if (src_format == *dest_format) { - *dest_value = src_value; - return TRUE; - } - - dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); - - /* we need the info part before we can done something */ - if (!dec->have_header) - goto no_header; - - switch (src_format) { - case GST_FORMAT_DEFAULT: - switch (*dest_format) { - case GST_FORMAT_TIME: - *dest_value = _theora_granule_start_time (dec, src_value); - break; - default: - res = FALSE; - } - break; - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_DEFAULT: - { - guint rest; - - /* framecount */ - *dest_value = gst_util_uint64_scale (src_value, - dec->info.fps_numerator, GST_SECOND * dec->info.fps_denominator); - - /* funny way of calculating granulepos in theora */ - rest = *dest_value / dec->info.keyframe_granule_shift; - *dest_value -= rest; - *dest_value <<= dec->granule_shift; - *dest_value += rest; - break; - } - default: - res = FALSE; - break; - } - break; - default: - res = FALSE; - } -done: - gst_object_unref (dec); - return res; - - /* ERRORS */ -no_header: - { - GST_DEBUG_OBJECT (dec, "no header yet, cannot convert"); - res = FALSE; - goto done; - } -} -#endif - -static gboolean -theora_dec_src_query (GstPad * pad, GstQuery * query) -{ - GstTheoraDec *dec; - - gboolean res = FALSE; - - dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - { - gint64 value; - GstFormat format; - gint64 time; - - /* parse format */ - gst_query_parse_position (query, &format, NULL); - - time = dec->last_timestamp; - time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time); - - GST_LOG_OBJECT (dec, - "query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time)); - - if (!(res = - theora_dec_src_convert (pad, GST_FORMAT_TIME, time, &format, - &value))) - goto error; - - gst_query_set_position (query, format, value); - - GST_LOG_OBJECT (dec, - "query %p: we return %" G_GINT64_FORMAT " (format %u)", query, value, - format); - break; - } - case GST_QUERY_DURATION: - { - /* forward to peer for total */ - res = gst_pad_peer_query (dec->sinkpad, query); - if (!res) - goto error; - - break; - } - case GST_QUERY_CONVERT: - { - GstFormat src_fmt, dest_fmt; - gint64 src_val, dest_val; - - gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); - if (!(res = - theora_dec_src_convert (pad, src_fmt, src_val, &dest_fmt, - &dest_val))) - goto error; - - gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } -done: - gst_object_unref (dec); - - return res; - - /* ERRORS */ -error: - { - GST_DEBUG_OBJECT (dec, "query failed"); - goto done; - } -} - -static gboolean -theora_dec_src_event (GstPad * pad, GstEvent * event) -{ - gboolean res = TRUE; - GstTheoraDec *dec; - - dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - { - GstFormat format, tformat; - gdouble rate; - GstEvent *real_seek; - GstSeekFlags flags; - GstSeekType cur_type, stop_type; - gint64 cur, stop; - gint64 tcur, tstop; - guint32 seqnum; - - gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, - &stop_type, &stop); - seqnum = gst_event_get_seqnum (event); - gst_event_unref (event); - - /* we have to ask our peer to seek to time here as we know - * nothing about how to generate a granulepos from the src - * formats or anything. - * - * First bring the requested format to time - */ - tformat = GST_FORMAT_TIME; - if (!(res = theora_dec_src_convert (pad, format, cur, &tformat, &tcur))) - goto convert_error; - if (!(res = theora_dec_src_convert (pad, format, stop, &tformat, &tstop))) - goto convert_error; - - /* then seek with time on the peer */ - real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME, - flags, cur_type, tcur, stop_type, tstop); - gst_event_set_seqnum (real_seek, seqnum); - - res = gst_pad_push_event (dec->sinkpad, real_seek); - break; - } - case GST_EVENT_QOS: - { - gdouble proportion; - GstClockTimeDiff diff; - GstClockTime timestamp; - - gst_event_parse_qos (event, &proportion, &diff, ×tamp); - - /* we cannot randomly skip frame decoding since we don't have - * B frames. we can however use the timestamp and diff to not - * push late frames. This would at least save us the time to - * crop/memcpy the data. */ - GST_OBJECT_LOCK (dec); - dec->proportion = proportion; - dec->earliest_time = timestamp + diff; - GST_OBJECT_UNLOCK (dec); - - GST_DEBUG_OBJECT (dec, "got QoS %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT, - GST_TIME_ARGS (timestamp), diff); - - res = gst_pad_push_event (dec->sinkpad, event); - break; - } - default: - res = gst_pad_push_event (dec->sinkpad, event); - break; - } -done: - gst_object_unref (dec); - - return res; - - /* ERRORS */ -convert_error: - { - GST_DEBUG_OBJECT (dec, "could not convert format"); - goto done; - } -} - -static gboolean -theora_dec_sink_event (GstPad * pad, GstEvent * event) -{ - gboolean ret = FALSE; - GstTheoraDec *dec; - - dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); - - GST_LOG_OBJECT (dec, "handling event"); - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - ret = gst_pad_push_event (dec->srcpad, event); - break; - case GST_EVENT_FLUSH_STOP: - gst_theora_dec_reset (dec); - ret = gst_pad_push_event (dec->srcpad, event); - break; - case GST_EVENT_EOS: - ret = gst_pad_push_event (dec->srcpad, event); - break; - case GST_EVENT_NEWSEGMENT: - { - gboolean update; - GstFormat format; - gdouble rate, arate; - gint64 start, stop, time; - - gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, - &start, &stop, &time); - - /* we need TIME format */ - if (format != GST_FORMAT_TIME) - goto newseg_wrong_format; - - GST_DEBUG_OBJECT (dec, - "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT - ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, - update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), - GST_TIME_ARGS (time)); - - /* now configure the values */ - gst_segment_set_newsegment_full (&dec->segment, update, - rate, arate, format, start, stop, time); - dec->seqnum = gst_event_get_seqnum (event); - - /* We don't forward this unless/until the decoder is initialised */ - if (dec->have_header) { - ret = gst_pad_push_event (dec->srcpad, event); - } else { - dec->pendingevents = g_list_append (dec->pendingevents, event); - ret = TRUE; - } - break; - } - case GST_EVENT_TAG: - { - if (dec->have_header) - /* and forward */ - ret = gst_pad_push_event (dec->srcpad, event); - else { - /* store it to send once we're initialized */ - dec->pendingevents = g_list_append (dec->pendingevents, event); - ret = TRUE; - } - break; - } - default: - ret = gst_pad_push_event (dec->srcpad, event); - break; - } -done: - gst_object_unref (dec); - - return ret; - - /* ERRORS */ -newseg_wrong_format: - { - GST_DEBUG_OBJECT (dec, "received non TIME newsegment"); - gst_event_unref (event); - goto done; - } -} - -static gboolean -theora_dec_setcaps (GstPad * pad, GstCaps * caps) -{ - GstTheoraDec *dec; - GstStructure *s; - const GValue *codec_data; - - dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); - - s = gst_caps_get_structure (caps, 0); - - /* parse the par, this overrides the encoded par */ - dec->have_par = gst_structure_get_fraction (s, "pixel-aspect-ratio", - &dec->par_num, &dec->par_den); - - if ((codec_data = gst_structure_get_value (s, "codec_data"))) { - if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) { - GstBuffer *buffer; - guint8 *data; - guint size; - guint offset; - - buffer = gst_value_get_buffer (codec_data); - - offset = 0; - size = GST_BUFFER_SIZE (buffer); - data = GST_BUFFER_DATA (buffer); - - while (size > 2) { - guint psize; - GstBuffer *buf; - - psize = (data[0] << 8) | data[1]; - /* skip header */ - data += 2; - size -= 2; - offset += 2; - - /* make sure we don't read too much */ - psize = MIN (psize, size); - - buf = gst_buffer_create_sub (buffer, offset, psize); - - /* first buffer is a discont buffer */ - if (offset == 2) - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); - - /* now feed it to the decoder we can ignore the error */ - theora_dec_chain (pad, buf); - - /* skip the data */ - size -= psize; - data += psize; - offset += psize; - } - } - } - - gst_object_unref (dec); - - return TRUE; -} - -static GstFlowReturn -theora_handle_comment_packet (GstTheoraDec * dec, ogg_packet * packet) -{ - gchar *encoder = NULL; - GstBuffer *buf; - GstTagList *list; - - GST_DEBUG_OBJECT (dec, "parsing comment packet"); - - buf = gst_buffer_new (); - GST_BUFFER_SIZE (buf) = packet->bytes; - GST_BUFFER_DATA (buf) = packet->packet; - - list = - gst_tag_list_from_vorbiscomment_buffer (buf, (guint8 *) "\201theora", 7, - &encoder); - - gst_buffer_unref (buf); - - if (!list) { - GST_ERROR_OBJECT (dec, "couldn't decode comments"); - list = gst_tag_list_new (); - } - if (encoder) { - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, - GST_TAG_ENCODER, encoder, NULL); - g_free (encoder); - } - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, - GST_TAG_ENCODER_VERSION, dec->info.version_major, - GST_TAG_VIDEO_CODEC, "Theora", NULL); - - if (dec->info.target_bitrate > 0) { - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, - GST_TAG_BITRATE, dec->info.target_bitrate, - GST_TAG_NOMINAL_BITRATE, dec->info.target_bitrate, NULL); - } - - dec->tags = list; - - return GST_FLOW_OK; -} - -static GstFlowReturn -theora_handle_type_packet (GstTheoraDec * dec, ogg_packet * packet) -{ - GstCaps *caps; - gint par_num, par_den; - GstFlowReturn ret = GST_FLOW_OK; - GList *walk; - guint32 fourcc; - - GST_DEBUG_OBJECT (dec, "fps %d/%d, PAR %d/%d", - dec->info.fps_numerator, dec->info.fps_denominator, - dec->info.aspect_numerator, dec->info.aspect_denominator); - - /* calculate par - * the info.aspect_* values reflect PAR; - * 0:x and x:0 are allowed and can be interpreted as 1:1. - */ - if (dec->have_par) { - /* we had a par on the sink caps, override the encoded par */ - GST_DEBUG_OBJECT (dec, "overriding with input PAR"); - par_num = dec->par_num; - par_den = dec->par_den; - } else { - /* take encoded par */ - par_num = dec->info.aspect_numerator; - par_den = dec->info.aspect_denominator; - } - if (par_num == 0 || par_den == 0) { - par_num = par_den = 1; - } - /* theora has: - * - * width/height : dimension of the encoded frame - * pic_width/pic_height : dimension of the visible part - * pic_x/pic_y : offset in encoded frame where visible part starts - */ - GST_DEBUG_OBJECT (dec, "dimension %dx%d, PAR %d/%d", dec->info.pic_width, - dec->info.pic_height, par_num, par_den); - GST_DEBUG_OBJECT (dec, "frame dimension %dx%d, offset %d:%d", - dec->info.pic_width, dec->info.pic_height, - dec->info.pic_x, dec->info.pic_y); - - if (dec->info.pixel_fmt == TH_PF_420) { - dec->output_bpp = 12; /* Average bits per pixel. */ - fourcc = GST_MAKE_FOURCC ('I', '4', '2', '0'); - } else if (dec->info.pixel_fmt == TH_PF_422) { - dec->output_bpp = 16; - fourcc = GST_MAKE_FOURCC ('Y', '4', '2', 'B'); - } else if (dec->info.pixel_fmt == TH_PF_444) { - dec->output_bpp = 24; - fourcc = GST_MAKE_FOURCC ('Y', '4', '4', '4'); - } else { - GST_ERROR_OBJECT (dec, "Invalid pixel format %d", dec->info.pixel_fmt); - return GST_FLOW_ERROR; - } - - if (dec->crop) { - dec->width = dec->info.pic_width; - dec->height = dec->info.pic_height; - dec->offset_x = dec->info.pic_x; - dec->offset_y = dec->info.pic_y; - /* Ensure correct offsets in chroma for formats that need it - * by rounding the offset. libtheora will add proper pixels, - * so no need to handle them ourselves. */ - if (dec->offset_x & 1 && dec->info.pixel_fmt != TH_PF_444) { - dec->offset_x--; - dec->width++; - } - if (dec->offset_y & 1 && dec->info.pixel_fmt == TH_PF_420) { - dec->offset_y--; - dec->height++; - } - } else { - /* no cropping, use the encoded dimensions */ - dec->width = dec->info.frame_width; - dec->height = dec->info.frame_height; - dec->offset_x = 0; - dec->offset_y = 0; - } - - GST_DEBUG_OBJECT (dec, "after fixup frame dimension %dx%d, offset %d:%d", - dec->width, dec->height, dec->offset_x, dec->offset_y); - - /* done */ - dec->decoder = th_decode_alloc (&dec->info, dec->setup); - - caps = gst_caps_new_simple ("video/x-raw-yuv", - "format", GST_TYPE_FOURCC, fourcc, - "framerate", GST_TYPE_FRACTION, - dec->info.fps_numerator, dec->info.fps_denominator, - "pixel-aspect-ratio", GST_TYPE_FRACTION, par_num, par_den, - "width", G_TYPE_INT, dec->width, "height", G_TYPE_INT, dec->height, NULL); - gst_pad_set_caps (dec->srcpad, caps); - gst_caps_unref (caps); - - dec->have_header = TRUE; - - if (dec->pendingevents) { - for (walk = dec->pendingevents; walk; walk = g_list_next (walk)) - gst_pad_push_event (dec->srcpad, GST_EVENT_CAST (walk->data)); - g_list_free (dec->pendingevents); - dec->pendingevents = NULL; - } - - if (dec->tags) { - gst_element_found_tags_for_pad (GST_ELEMENT_CAST (dec), dec->srcpad, - dec->tags); - dec->tags = NULL; - } - - return ret; -} - -static GstFlowReturn -theora_handle_header_packet (GstTheoraDec * dec, ogg_packet * packet) -{ - GstFlowReturn res; - int ret; - - GST_DEBUG_OBJECT (dec, "parsing header packet"); - - ret = th_decode_headerin (&dec->info, &dec->comment, &dec->setup, packet); - if (ret < 0) - goto header_read_error; - - switch (packet->packet[0]) { - case 0x81: - res = theora_handle_comment_packet (dec, packet); - break; - case 0x82: - res = theora_handle_type_packet (dec, packet); - break; - default: - /* ignore */ - g_warning ("unknown theora header packet found"); - case 0x80: - /* nothing special, this is the identification header */ - res = GST_FLOW_OK; - break; - } - return res; - - /* ERRORS */ -header_read_error: - { - GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, - (NULL), ("couldn't read header packet")); - return GST_FLOW_ERROR; - } -} - -/* returns TRUE if buffer is within segment, else FALSE. - * if Buffer is on segment border, it's timestamp and duration will be clipped */ -static gboolean -clip_buffer (GstTheoraDec * dec, GstBuffer * buf) -{ - gboolean res = TRUE; - GstClockTime in_ts, in_dur, stop; - gint64 cstart, cstop; - - in_ts = GST_BUFFER_TIMESTAMP (buf); - in_dur = GST_BUFFER_DURATION (buf); - - GST_LOG_OBJECT (dec, - "timestamp:%" GST_TIME_FORMAT " , duration:%" GST_TIME_FORMAT, - GST_TIME_ARGS (in_ts), GST_TIME_ARGS (in_dur)); - - /* can't clip without TIME segment */ - if (dec->segment.format != GST_FORMAT_TIME) - goto beach; - - /* we need a start time */ - if (!GST_CLOCK_TIME_IS_VALID (in_ts)) - goto beach; - - /* generate valid stop, if duration unknown, we have unknown stop */ - stop = - GST_CLOCK_TIME_IS_VALID (in_dur) ? (in_ts + in_dur) : GST_CLOCK_TIME_NONE; - - /* now clip */ - if (!(res = gst_segment_clip (&dec->segment, GST_FORMAT_TIME, - in_ts, stop, &cstart, &cstop))) - goto beach; - - /* update timestamp and possibly duration if the clipped stop time is - * valid */ - GST_BUFFER_TIMESTAMP (buf) = cstart; - if (GST_CLOCK_TIME_IS_VALID (cstop)) - GST_BUFFER_DURATION (buf) = cstop - cstart; - -beach: - GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : "")); - return res; -} - -static GstFlowReturn -theora_dec_push_forward (GstTheoraDec * dec, GstBuffer * buf) -{ - GstFlowReturn result = GST_FLOW_OK; - - if (clip_buffer (dec, buf)) { - if (dec->discont) { - GST_LOG_OBJECT (dec, "setting DISCONT"); - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); - dec->discont = FALSE; - } - result = gst_pad_push (dec->srcpad, buf); - } else { - gst_buffer_unref (buf); - } - - return result; -} - -static GstFlowReturn -theora_dec_push_reverse (GstTheoraDec * dec, GstBuffer * buf) -{ - GstFlowReturn result = GST_FLOW_OK; - - dec->queued = g_list_prepend (dec->queued, buf); - - return result; -} - -/* Allocate buffer and copy image data into Y444 format */ -static GstFlowReturn -theora_handle_image (GstTheoraDec * dec, th_ycbcr_buffer buf, GstBuffer ** out) -{ - gint width, height, stride; - GstFlowReturn result; - int i, plane; - GstVideoFormat format; - guint8 *dest, *src; - - switch (dec->info.pixel_fmt) { - case TH_PF_444: - format = GST_VIDEO_FORMAT_Y444; - break; - case TH_PF_420: - format = GST_VIDEO_FORMAT_I420; - break; - case TH_PF_422: - format = GST_VIDEO_FORMAT_Y42B; - break; - default: - g_assert_not_reached (); - } - - result = - gst_pad_alloc_buffer_and_set_caps (dec->srcpad, GST_BUFFER_OFFSET_NONE, - gst_video_format_get_size (format, dec->width, dec->height), - GST_PAD_CAPS (dec->srcpad), out); - if (G_UNLIKELY (result != GST_FLOW_OK)) { - GST_DEBUG_OBJECT (dec, "could not get buffer, reason: %s", - gst_flow_get_name (result)); - return result; - } - - for (plane = 0; plane < 3; plane++) { - width = gst_video_format_get_component_width (format, plane, dec->width); - height = gst_video_format_get_component_height (format, plane, dec->height); - stride = gst_video_format_get_row_stride (format, plane, dec->width); - - dest = - GST_BUFFER_DATA (*out) + gst_video_format_get_component_offset (format, - plane, dec->width, dec->height); - src = buf[plane].data; - src += ((height == dec->height) ? dec->offset_y : dec->offset_y / 2) - * buf[plane].stride; - src += (width == dec->width) ? dec->offset_x : dec->offset_x / 2; - - for (i = 0; i < height; i++) { - memcpy (dest, src, width); - - dest += stride; - src += buf[plane].stride; - } - } - - return GST_FLOW_OK; -} - -static GstFlowReturn -theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet, - GstClockTime outtime, GstClockTime outdur) -{ - /* normal data packet */ - th_ycbcr_buffer buf; - GstBuffer *out; - gboolean keyframe; - GstFlowReturn result; - ogg_int64_t gp; - - if (G_UNLIKELY (!dec->have_header)) - goto not_initialized; - - /* get timestamp and durations */ - if (outtime == -1) - outtime = dec->last_timestamp; - if (outdur == -1) - outdur = gst_util_uint64_scale_int (GST_SECOND, dec->info.fps_denominator, - dec->info.fps_numerator); - - /* calculate expected next timestamp */ - if (outtime != -1 && outdur != -1) - dec->last_timestamp = outtime + outdur; - - /* the second most significant bit of the first data byte is cleared - * for keyframes. We can only check it if it's not a zero-length packet. */ - keyframe = packet->bytes && ((packet->packet[0] & 0x40) == 0); - if (G_UNLIKELY (keyframe)) { - GST_DEBUG_OBJECT (dec, "we have a keyframe"); - dec->need_keyframe = FALSE; - } else if (G_UNLIKELY (dec->need_keyframe)) { - goto dropping; - } - - GST_DEBUG_OBJECT (dec, "parsing data packet"); - - /* this does the decoding */ - if (G_UNLIKELY (th_decode_packetin (dec->decoder, packet, &gp) < 0)) - goto decode_error; - - if (outtime != -1) { - gboolean need_skip; - GstClockTime qostime; - - /* qos needs to be done on running time */ - qostime = gst_segment_to_running_time (&dec->segment, GST_FORMAT_TIME, - outtime); - - GST_OBJECT_LOCK (dec); - /* check for QoS, don't perform the last steps of getting and - * pushing the buffers that are known to be late. */ - need_skip = dec->earliest_time != -1 && qostime <= dec->earliest_time; - GST_OBJECT_UNLOCK (dec); - - if (need_skip) - goto dropping_qos; - } - - /* this does postprocessing and set up the decoded frame - * pointers in our yuv variable */ - if (G_UNLIKELY (th_decode_ycbcr_out (dec->decoder, buf) < 0)) - goto no_yuv; - - if (G_UNLIKELY ((buf[0].width != dec->info.frame_width) - || (buf[0].height != dec->info.frame_height))) - goto wrong_dimensions; - - result = theora_handle_image (dec, buf, &out); - if (result != GST_FLOW_OK) - return result; - - GST_BUFFER_OFFSET (out) = dec->frame_nr; - if (dec->frame_nr != -1) - dec->frame_nr++; - GST_BUFFER_OFFSET_END (out) = dec->frame_nr; - - GST_BUFFER_TIMESTAMP (out) = outtime; - GST_BUFFER_DURATION (out) = outdur; - - if (dec->segment.rate >= 0.0) - result = theora_dec_push_forward (dec, out); - else - result = theora_dec_push_reverse (dec, out); - - return result; - - /* ERRORS */ -not_initialized: - { - GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, - (NULL), ("no header sent yet")); - return GST_FLOW_ERROR; - } -dropping: - { - GST_WARNING_OBJECT (dec, "dropping frame because we need a keyframe"); - dec->discont = TRUE; - return GST_FLOW_OK; - } -dropping_qos: - { - if (dec->frame_nr != -1) - dec->frame_nr++; - dec->discont = TRUE; - GST_WARNING_OBJECT (dec, "dropping frame because of QoS"); - return GST_FLOW_OK; - } -decode_error: - { - GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, - (NULL), ("theora decoder did not decode data packet")); - return GST_FLOW_ERROR; - } -no_yuv: - { - GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, - (NULL), ("couldn't read out YUV image")); - return GST_FLOW_ERROR; - } -wrong_dimensions: - { - GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, FORMAT, - (NULL), ("dimensions of image do not match header")); - return GST_FLOW_ERROR; - } -} - -static GstFlowReturn -theora_dec_decode_buffer (GstTheoraDec * dec, GstBuffer * buf) -{ - ogg_packet packet; - GstFlowReturn result = GST_FLOW_OK; - GstClockTime timestamp, duration; - - /* make ogg_packet out of the buffer */ - packet.packet = GST_BUFFER_DATA (buf); - packet.bytes = GST_BUFFER_SIZE (buf); - packet.granulepos = -1; - packet.packetno = 0; /* we don't really care */ - packet.b_o_s = dec->have_header ? 0 : 1; - /* EOS does not matter for the decoder */ - packet.e_o_s = 0; - - GST_LOG_OBJECT (dec, "decode buffer of size %ld", packet.bytes); - - /* save last seem timestamp for interpolating the next timestamps using the - * framerate when we need to */ - timestamp = GST_BUFFER_TIMESTAMP (buf); - duration = GST_BUFFER_DURATION (buf); - - GST_DEBUG_OBJECT (dec, "header=%02x, outtime=%" GST_TIME_FORMAT, - packet.bytes ? packet.packet[0] : -1, GST_TIME_ARGS (timestamp)); - - /* switch depending on packet type. A zero byte packet is always a data - * packet; we don't dereference it in that case. */ - if (packet.bytes && packet.packet[0] & 0x80) { - if (dec->have_header) { - GST_WARNING_OBJECT (GST_OBJECT (dec), "Ignoring header"); - goto done; - } - result = theora_handle_header_packet (dec, &packet); - } else { - result = theora_handle_data_packet (dec, &packet, timestamp, duration); - } - -done: - return result; -} - -/* For reverse playback we use a technique that can be used for - * any keyframe based video codec. - * - * Input: - * Buffer decoding order: 7 8 9 4 5 6 1 2 3 EOS - * Keyframe flag: K K - * Discont flag: D D D - * - * - Each Discont marks a discont in the decoding order. - * - The keyframes mark where we can start decoding. - * - * First we prepend incomming buffers to the gather queue, whenever we receive - * a discont, we flush out the gather queue. - * - * The above data will be accumulated in the gather queue like this: - * - * gather queue: 9 8 7 - * D - * - * Whe buffer 4 is received (with a DISCONT), we flush the gather queue like - * this: - * - * while (gather) - * take head of queue and prepend to decode queue. - * if we copied a keyframe, decode the decode queue. - * - * After we flushed the gather queue, we add 4 to the (now empty) gather queue. - * We get the following situation: - * - * gather queue: 4 - * decode queue: 7 8 9 - * - * After we received 5 (Keyframe) and 6: - * - * gather queue: 6 5 4 - * decode queue: 7 8 9 - * - * When we receive 1 (DISCONT) which triggers a flush of the gather queue: - * - * Copy head of the gather queue (6) to decode queue: - * - * gather queue: 5 4 - * decode queue: 6 7 8 9 - * - * Copy head of the gather queue (5) to decode queue. This is a keyframe so we - * can start decoding. - * - * gather queue: 4 - * decode queue: 5 6 7 8 9 - * - * Decode frames in decode queue, store raw decoded data in output queue, we - * can take the head of the decode queue and prepend the decoded result in the - * output queue: - * - * gather queue: 4 - * decode queue: - * output queue: 9 8 7 6 5 - * - * Now output all the frames in the output queue, picking a frame from the - * head of the queue. - * - * Copy head of the gather queue (4) to decode queue, we flushed the gather - * queue and can now store input buffer in the gather queue: - * - * gather queue: 1 - * decode queue: 4 - * - * When we receive EOS, the queue looks like: - * - * gather queue: 3 2 1 - * decode queue: 4 - * - * Fill decode queue, first keyframe we copy is 2: - * - * gather queue: 1 - * decode queue: 2 3 4 - * - * Decoded output: - * - * gather queue: 1 - * decode queue: - * output queue: 4 3 2 - * - * Leftover buffer 1 cannot be decoded and must be discarded. - */ -static GstFlowReturn -theora_dec_flush_decode (GstTheoraDec * dec) -{ - GstFlowReturn res = GST_FLOW_OK; - - while (dec->decode) { - GstBuffer *buf = GST_BUFFER_CAST (dec->decode->data); - - GST_DEBUG_OBJECT (dec, "decoding buffer %p, ts %" GST_TIME_FORMAT, - buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); - - /* decode buffer, prepend to output queue */ - res = theora_dec_decode_buffer (dec, buf); - - /* don't need it anymore now */ - gst_buffer_unref (buf); - - dec->decode = g_list_delete_link (dec->decode, dec->decode); - } - while (dec->queued) { - GstBuffer *buf = GST_BUFFER_CAST (dec->queued->data); - - /* iterate ouput queue an push downstream */ - res = gst_pad_push (dec->srcpad, buf); - - dec->queued = g_list_delete_link (dec->queued, dec->queued); - } - - return res; -} - -static GstFlowReturn -theora_dec_chain_reverse (GstTheoraDec * dec, gboolean discont, GstBuffer * buf) -{ - GstFlowReturn res = GST_FLOW_OK; - - /* if we have a discont, move buffers to the decode list */ - if (G_UNLIKELY (discont)) { - GST_DEBUG_OBJECT (dec, "received discont,gathering buffers"); - while (dec->gather) { - GstBuffer *gbuf; - guint8 *data; - - gbuf = GST_BUFFER_CAST (dec->gather->data); - /* remove from the gather list */ - dec->gather = g_list_delete_link (dec->gather, dec->gather); - /* copy to decode queue */ - dec->decode = g_list_prepend (dec->decode, gbuf); - - /* if we copied a keyframe, flush and decode the decode queue */ - data = GST_BUFFER_DATA (gbuf); - if ((data[0] & 0x40) == 0) { - GST_DEBUG_OBJECT (dec, "copied keyframe"); - res = theora_dec_flush_decode (dec); - } - } - } - - /* add buffer to gather queue */ - GST_DEBUG_OBJECT (dec, "gathering buffer %p, size %u", buf, - GST_BUFFER_SIZE (buf)); - dec->gather = g_list_prepend (dec->gather, buf); - - return res; -} - -static GstFlowReturn -theora_dec_chain_forward (GstTheoraDec * dec, gboolean discont, - GstBuffer * buffer) -{ - GstFlowReturn result; - - result = theora_dec_decode_buffer (dec, buffer); - - gst_buffer_unref (buffer); - - return result; -} - -static GstFlowReturn -theora_dec_chain (GstPad * pad, GstBuffer * buf) -{ - GstTheoraDec *dec; - GstFlowReturn res; - gboolean discont; - - dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); - - /* peel of DISCONT flag */ - discont = GST_BUFFER_IS_DISCONT (buf); - - /* resync on DISCONT */ - if (G_UNLIKELY (discont)) { - GST_DEBUG_OBJECT (dec, "received DISCONT buffer"); - dec->need_keyframe = TRUE; - dec->last_timestamp = -1; - dec->discont = TRUE; - } - - if (dec->segment.rate > 0.0) - res = theora_dec_chain_forward (dec, discont, buf); - else - res = theora_dec_chain_reverse (dec, discont, buf); - - gst_object_unref (dec); - - return res; -} - -static GstStateChangeReturn -theora_dec_change_state (GstElement * element, GstStateChange transition) -{ - GstTheoraDec *dec = GST_THEORA_DEC (element); - GstStateChangeReturn ret; - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - th_info_clear (&dec->info); - th_comment_clear (&dec->comment); - GST_DEBUG_OBJECT (dec, "Setting have_header to FALSE in READY->PAUSED"); - dec->have_header = FALSE; - dec->have_par = FALSE; - gst_theora_dec_reset (dec); - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - default: - break; - } - - ret = parent_class->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - th_info_clear (&dec->info); - th_comment_clear (&dec->comment); - th_setup_free (dec->setup); - dec->setup = NULL; - th_decode_free (dec->decoder); - dec->decoder = NULL; - gst_theora_dec_reset (dec); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - - return ret; -} - -static void -theora_dec_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstTheoraDec *dec = GST_THEORA_DEC (object); - - switch (prop_id) { - case ARG_CROP: - dec->crop = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -theora_dec_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstTheoraDec *dec = GST_THEORA_DEC (object); - - switch (prop_id) { - case ARG_CROP: - g_value_set_boolean (value, dec->crop); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} diff --git a/ext/theora/gsttheoradec.h b/ext/theora/gsttheoradec.h deleted file mode 100644 index 14f806cd..00000000 --- a/ext/theora/gsttheoradec.h +++ /dev/null @@ -1,110 +0,0 @@ -/* GStreamer - * Copyright (C) 2004 Benjamin Otte <in7y118@public.uni-hamburg.de> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_THEORADEC_H__ -#define __GST_THEORADEC_H__ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <gst/gst.h> -#include <theora/theoradec.h> -#include <string.h> - -G_BEGIN_DECLS - -#define GST_TYPE_THEORA_DEC \ - (gst_theora_dec_get_type()) -#define GST_THEORA_DEC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_THEORA_DEC,GstTheoraDec)) -#define GST_THEORA_DEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_THEORA_DEC,GstTheoraDecClass)) -#define GST_IS_THEORA_DEC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_THEORA_DEC)) -#define GST_IS_THEORA_DEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_THEORA_DEC)) - -typedef struct _GstTheoraDec GstTheoraDec; -typedef struct _GstTheoraDecClass GstTheoraDecClass; - -/** - * GstTheoraDec: - * - * Opaque object data structure. - */ -struct _GstTheoraDec -{ - GstElement element; - - /* Pads */ - GstPad *sinkpad; - GstPad *srcpad; - - /* theora decoder state */ - th_dec_ctx *decoder; - //theora_state state; - th_setup_info *setup; - th_info info; - th_comment comment; - - gboolean have_header; - - GstClockTime last_timestamp; - guint64 frame_nr; /* unused */ - gboolean need_keyframe; - gint width, height; - gint offset_x, offset_y; - gint output_bpp; - - gboolean crop; - - /* list of buffers that need timestamps */ - GList *queued; - /* list of raw output buffers */ - GList *output; - /* gather/decode queues for reverse playback */ - GList *gather; - GList *decode; - GList *pendingevents; - - GstTagList *tags; - - /* segment info */ /* with STREAM_LOCK */ - GstSegment segment; - gboolean discont; - guint32 seqnum; - - /* QoS stuff */ /* with LOCK*/ - gdouble proportion; - GstClockTime earliest_time; - - gboolean have_par; - gint par_num; - gint par_den; -}; - -struct _GstTheoraDecClass -{ - GstElementClass parent_class; -}; - -G_END_DECLS - -#endif /* __GST_THEORADEC_H__ */ diff --git a/ext/theora/gsttheoraenc.c b/ext/theora/gsttheoraenc.c deleted file mode 100644 index 121d3820..00000000 --- a/ext/theora/gsttheoraenc.c +++ /dev/null @@ -1,1256 +0,0 @@ -/* GStreamer - * Copyright (C) 2004 Wim Taymans <wim@fluendo.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-theoraenc - * @see_also: theoradec, oggmux - * - * This element encodes raw video into a Theora stream. - * <ulink url="http://www.theora.org/">Theora</ulink> is a royalty-free - * video codec maintained by the <ulink url="http://www.xiph.org/">Xiph.org - * Foundation</ulink>, based on the VP3 codec. - * - * The theora codec internally only supports encoding of images that are a - * multiple of 16 pixels in both X and Y direction. It is however perfectly - * possible to encode images with other dimensions because an arbitrary - * rectangular cropping region can be set up. This element will automatically - * set up a correct cropping region if the dimensions are not multiples of 16 - * pixels. - * - * To control the quality of the encoding, the #GstTheoraEnc::bitrate and - * #GstTheoraEnc::quality properties can be used. These two properties are - * mutualy exclusive. Setting the bitrate property will produce a constant - * bitrate (CBR) stream while setting the quality property will produce a - * variable bitrate (VBR) stream. - * - * <refsect2> - * <title>Example pipeline</title> - * |[ - * gst-launch -v videotestsrc num-buffers=1000 ! theoraenc ! oggmux ! filesink location=videotestsrc.ogg - * ]| This example pipeline will encode a test video source to theora muxed in an - * ogg container. Refer to the theoradec documentation to decode the create - * stream. - * </refsect2> - * - * Last reviewed on 2006-03-01 (0.10.4) - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "gsttheoraenc.h" - -#include <string.h> -#include <stdlib.h> /* free */ - -#include <gst/tag/tag.h> -#include <gst/video/video.h> - -#define GST_CAT_DEFAULT theoraenc_debug -GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); - -#define GST_TYPE_BORDER_MODE (gst_border_mode_get_type()) -static GType -gst_border_mode_get_type (void) -{ - static GType border_mode_type = 0; - static const GEnumValue border_mode[] = { - {BORDER_NONE, "No Border", "none"}, - {BORDER_BLACK, "Black Border", "black"}, - {BORDER_MIRROR, "Mirror image in borders", "mirror"}, - {0, NULL, NULL}, - }; - - if (!border_mode_type) { - border_mode_type = - g_enum_register_static ("GstTheoraEncBorderMode", border_mode); - } - return border_mode_type; -} - -/* taken from theora/lib/toplevel.c */ -static int -_ilog (unsigned int v) -{ - int ret = 0; - - while (v) { - ret++; - v >>= 1; - } - return (ret); -} - -#define THEORA_DEF_BITRATE 0 -#define THEORA_DEF_QUALITY 48 -#define THEORA_DEF_KEYFRAME_AUTO TRUE -#define THEORA_DEF_KEYFRAME_FREQ 64 -#define THEORA_DEF_KEYFRAME_FREQ_FORCE 64 -#define THEORA_DEF_SPEEDLEVEL 1 -#define THEORA_DEF_VP3_COMPATIBLE FALSE -#define THEORA_DEF_DROP_FRAMES TRUE -#define THEORA_DEF_CAP_OVERFLOW TRUE -#define THEORA_DEF_CAP_UNDERFLOW FALSE -#define THEORA_DEF_RATE_BUFFER 0 -enum -{ - ARG_0, - ARG_CENTER, - ARG_BORDER, - ARG_BITRATE, - ARG_QUALITY, - ARG_QUICK, - ARG_KEYFRAME_AUTO, - ARG_KEYFRAME_FREQ, - ARG_KEYFRAME_FREQ_FORCE, - ARG_KEYFRAME_THRESHOLD, - ARG_KEYFRAME_MINDISTANCE, - ARG_NOISE_SENSITIVITY, - ARG_SHARPNESS, - ARG_SPEEDLEVEL, - ARG_VP3_COMPATIBLE, - ARG_DROP_FRAMES, - ARG_CAP_OVERFLOW, - ARG_CAP_UNDERFLOW, - ARG_RATE_BUFFER, - /* FILL ME */ -}; - -/* this function does a straight granulepos -> timestamp conversion */ -static GstClockTime -granulepos_to_timestamp (GstTheoraEnc * theoraenc, ogg_int64_t granulepos) -{ - guint64 iframe, pframe; - int shift = theoraenc->info.keyframe_granule_shift; - - if (granulepos < 0) - return GST_CLOCK_TIME_NONE; - - iframe = granulepos >> shift; - pframe = granulepos - (iframe << shift); - - /* num and den are 32 bit, so we can safely multiply with GST_SECOND */ - return gst_util_uint64_scale ((guint64) (iframe + pframe), - GST_SECOND * theoraenc->info.fps_denominator, - theoraenc->info.fps_numerator); -} - -static const GstElementDetails theora_enc_details = -GST_ELEMENT_DETAILS ("Theora video encoder", - "Codec/Encoder/Video", - "encode raw YUV video to a theora stream", - "Wim Taymans <wim@fluendo.com>"); - -static GstStaticPadTemplate theora_enc_sink_factory = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-raw-yuv, " - "format = (fourcc) { I420, Y42B, Y444 }, " - "framerate = (fraction) [0/1, MAX], " - "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]") - ); - -static GstStaticPadTemplate theora_enc_src_factory = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-theora") - ); - -static void -_do_init (GType object_type) -{ - const GInterfaceInfo preset_interface_info = { - NULL, /* interface_init */ - NULL, /* interface_finalize */ - NULL /* interface_data */ - }; - - g_type_add_interface_static (object_type, GST_TYPE_PRESET, - &preset_interface_info); -} - -GST_BOILERPLATE_FULL (GstTheoraEnc, gst_theora_enc, GstElement, - GST_TYPE_ELEMENT, _do_init); - -static gboolean theora_enc_sink_event (GstPad * pad, GstEvent * event); -static gboolean theora_enc_src_event (GstPad * pad, GstEvent * event); -static GstFlowReturn theora_enc_chain (GstPad * pad, GstBuffer * buffer); -static GstStateChangeReturn theora_enc_change_state (GstElement * element, - GstStateChange transition); -static GstCaps *theora_enc_sink_getcaps (GstPad * pad); -static gboolean theora_enc_sink_setcaps (GstPad * pad, GstCaps * caps); -static void theora_enc_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static void theora_enc_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void theora_enc_finalize (GObject * object); - -static void -gst_theora_enc_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&theora_enc_src_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&theora_enc_sink_factory)); - gst_element_class_set_details (element_class, &theora_enc_details); -} - -static void -gst_theora_enc_class_init (GstTheoraEncClass * klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - - gobject_class->set_property = theora_enc_set_property; - gobject_class->get_property = theora_enc_get_property; - gobject_class->finalize = theora_enc_finalize; - - g_object_class_install_property (gobject_class, ARG_CENTER, - g_param_spec_boolean ("center", "Center", - "ignored and kept for API compat only", TRUE, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_BORDER, - g_param_spec_enum ("border", "Border", - "ignored and kept for API compat only", - GST_TYPE_BORDER_MODE, BORDER_BLACK, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /* general encoding stream options */ - g_object_class_install_property (gobject_class, ARG_BITRATE, - g_param_spec_int ("bitrate", "Bitrate", "Compressed video bitrate (kbps)", - 0, (1 << 24) - 1, THEORA_DEF_BITRATE, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_QUALITY, - g_param_spec_int ("quality", "Quality", "Video quality", 0, 63, - THEORA_DEF_QUALITY, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_QUICK, - g_param_spec_boolean ("quick", "Quick", - "ignored and kept for API compat only", TRUE, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_KEYFRAME_AUTO, - g_param_spec_boolean ("keyframe-auto", "Keyframe Auto", - "Automatic keyframe detection", THEORA_DEF_KEYFRAME_AUTO, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_KEYFRAME_FREQ, - g_param_spec_int ("keyframe-freq", "Keyframe frequency", - "Keyframe frequency", 1, 32768, THEORA_DEF_KEYFRAME_FREQ, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_KEYFRAME_FREQ_FORCE, - g_param_spec_int ("keyframe-force", "Keyframe force", - "Force keyframe every N frames", 1, 32768, - THEORA_DEF_KEYFRAME_FREQ_FORCE, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_KEYFRAME_THRESHOLD, - g_param_spec_int ("keyframe-threshold", "Keyframe threshold", - "ignored and kept for API compat only", 0, 32768, 80, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_KEYFRAME_MINDISTANCE, - g_param_spec_int ("keyframe-mindistance", "Keyframe mindistance", - "ignored and kept for API compat only", 1, 32768, 8, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_NOISE_SENSITIVITY, - g_param_spec_int ("noise-sensitivity", "Noise sensitivity", - "ignored and kept for API compat only", 0, 32768, 1, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_SHARPNESS, - g_param_spec_int ("sharpness", "Sharpness", - "ignored and kept for API compat only", 0, 2, 0, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_SPEEDLEVEL, - g_param_spec_int ("speed-level", "Speed level", - "Controls the amount of motion vector searching done while " - "encoding. This property requires libtheora version >= 1.0", - 0, 2, THEORA_DEF_SPEEDLEVEL, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_VP3_COMPATIBLE, - g_param_spec_boolean ("vp3-compatible", "VP3 Compatible", - "Disables non-VP3 compatible features." - " This property requires libtheora version >= 1.1", - THEORA_DEF_VP3_COMPATIBLE, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_DROP_FRAMES, - g_param_spec_boolean ("drop-frames", "VP3 Compatible", - "Allow or disallow frame dropping." - " This property requires libtheora version >= 1.1", - THEORA_DEF_DROP_FRAMES, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_CAP_OVERFLOW, - g_param_spec_boolean ("cap-overflow", "VP3 Compatible", - "Enable capping of bit reservoir overflows." - " This property requires libtheora version >= 1.1", - THEORA_DEF_CAP_OVERFLOW, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_CAP_UNDERFLOW, - g_param_spec_boolean ("cap-underflow", "VP3 Compatible", - "Enable capping of bit reservoir underflows." - " This property requires libtheora version >= 1.1", - THEORA_DEF_CAP_UNDERFLOW, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_RATE_BUFFER, - g_param_spec_int ("rate-buffer", "Rate Control Buffer", - "Sets the size of the rate control buffer, in units of frames. " - "The default value of 0 instructs the encoder to automatically " - "select an appropriate value." - " This property requires libtheora version >= 1.1", - 0, 1000, THEORA_DEF_RATE_BUFFER, - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - gstelement_class->change_state = theora_enc_change_state; - GST_DEBUG_CATEGORY_INIT (theoraenc_debug, "theoraenc", 0, "Theora encoder"); -} - -static void -gst_theora_enc_init (GstTheoraEnc * enc, GstTheoraEncClass * g_class) -{ - enc->sinkpad = - gst_pad_new_from_static_template (&theora_enc_sink_factory, "sink"); - gst_pad_set_chain_function (enc->sinkpad, theora_enc_chain); - gst_pad_set_event_function (enc->sinkpad, theora_enc_sink_event); - gst_pad_set_getcaps_function (enc->sinkpad, theora_enc_sink_getcaps); - gst_pad_set_setcaps_function (enc->sinkpad, theora_enc_sink_setcaps); - gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad); - - enc->srcpad = - gst_pad_new_from_static_template (&theora_enc_src_factory, "src"); - gst_pad_set_event_function (enc->srcpad, theora_enc_src_event); - gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad); - - gst_segment_init (&enc->segment, GST_FORMAT_UNDEFINED); - - enc->video_bitrate = THEORA_DEF_BITRATE; - enc->video_quality = THEORA_DEF_QUALITY; - enc->keyframe_auto = THEORA_DEF_KEYFRAME_AUTO; - enc->keyframe_freq = THEORA_DEF_KEYFRAME_FREQ; - enc->keyframe_force = THEORA_DEF_KEYFRAME_FREQ_FORCE; - - enc->expected_ts = GST_CLOCK_TIME_NONE; - - enc->speed_level = THEORA_DEF_SPEEDLEVEL; - enc->vp3_compatible = THEORA_DEF_VP3_COMPATIBLE; - enc->drop_frames = THEORA_DEF_DROP_FRAMES; - enc->cap_overflow = THEORA_DEF_CAP_OVERFLOW; - enc->cap_underflow = THEORA_DEF_CAP_UNDERFLOW; - enc->rate_buffer = THEORA_DEF_RATE_BUFFER; -} - -static void -theora_enc_finalize (GObject * object) -{ - GstTheoraEnc *enc = GST_THEORA_ENC (object); - - GST_DEBUG_OBJECT (enc, "Finalizing"); - if (enc->encoder) - th_encode_free (enc->encoder); - th_comment_clear (&enc->comment); - th_info_clear (&enc->info); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -theora_enc_reset (GstTheoraEnc * enc) -{ - ogg_uint32_t keyframe_force; - int rate_flags; - - if (enc->encoder) - th_encode_free (enc->encoder); - enc->encoder = th_encode_alloc (&enc->info); - /* We ensure this function cannot fail. */ - g_assert (enc->encoder != NULL); -#ifdef TH_ENCCTL_SET_SPLEVEL - th_encode_ctl (enc->encoder, TH_ENCCTL_SET_SPLEVEL, &enc->speed_level, - sizeof (enc->speed_level)); -#endif -#ifdef TH_ENCCTL_SET_VP3_COMPATIBLE - th_encode_ctl (enc->encoder, TH_ENCCTL_SET_VP3_COMPATIBLE, - &enc->vp3_compatible, sizeof (enc->vp3_compatible)); -#endif - - rate_flags = 0; -#ifdef TH_ENCCTL_SET_RATE_FLAGS - if (enc->drop_frames) - rate_flags |= TH_RATECTL_DROP_FRAMES; - if (enc->drop_frames) - rate_flags |= TH_RATECTL_CAP_OVERFLOW; - if (enc->drop_frames) - rate_flags |= TH_RATECTL_CAP_UNDERFLOW; - th_encode_ctl (enc->encoder, TH_ENCCTL_SET_RATE_FLAGS, - &rate_flags, sizeof (rate_flags)); -#endif - -#ifdef TH_ENCCTL_SET_RATE_BUFFER - if (enc->rate_buffer) { - th_encode_ctl (enc->encoder, TH_ENCCTL_SET_RATE_BUFFER, - &enc->rate_buffer, sizeof (enc->rate_buffer)); - } else { - /* FIXME */ - } -#endif - - keyframe_force = enc->keyframe_auto ? - enc->keyframe_force : enc->keyframe_freq; - th_encode_ctl (enc->encoder, TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE, - &keyframe_force, sizeof (keyframe_force)); -} - -static void -theora_enc_clear (GstTheoraEnc * enc) -{ - enc->packetno = 0; - enc->bytes_out = 0; - enc->granulepos_offset = 0; - enc->timestamp_offset = 0; - - enc->next_ts = GST_CLOCK_TIME_NONE; - enc->next_discont = FALSE; - enc->expected_ts = GST_CLOCK_TIME_NONE; -} - -static char * -theora_enc_get_supported_formats (void) -{ - th_enc_ctx *encoder; - th_info info; - struct - { - th_pixel_fmt pixelformat; - char *fourcc; - } formats[] = { - { - TH_PF_420, "I420"}, { - TH_PF_422, "Y42B"}, { - TH_PF_444, "Y444"} - }; - GString *string = NULL; - guint i; - - th_info_init (&info); - info.frame_width = 16; - info.frame_height = 16; - info.fps_numerator = 25; - info.fps_denominator = 1; - for (i = 0; i < G_N_ELEMENTS (formats); i++) { - info.pixel_fmt = formats[i].pixelformat; - - encoder = th_encode_alloc (&info); - if (encoder == NULL) - continue; - - GST_LOG ("format %s is supported", formats[i].fourcc); - th_encode_free (encoder); - - if (string == NULL) { - string = g_string_new (formats[i].fourcc); - } else { - g_string_append (string, ", "); - g_string_append (string, formats[i].fourcc); - } - } - th_info_clear (&info); - - return string == NULL ? NULL : g_string_free (string, FALSE); -} - -static GstCaps * -theora_enc_sink_getcaps (GstPad * pad) -{ - GstCaps *caps; - char *supported_formats, *caps_string; - - supported_formats = theora_enc_get_supported_formats (); - if (!supported_formats) { - GST_WARNING ("no supported formats found. Encoder disabled?"); - return gst_caps_new_empty (); - } - - caps_string = g_strdup_printf ("video/x-raw-yuv, " - "format = (fourcc) { %s }, " - "framerate = (fraction) [0/1, MAX], " - "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]", - supported_formats); - caps = gst_caps_from_string (caps_string); - g_free (caps_string); - g_free (supported_formats); - GST_DEBUG ("Supported caps: %" GST_PTR_FORMAT, caps); - - return caps; -} - -static gboolean -theora_enc_sink_setcaps (GstPad * pad, GstCaps * caps) -{ - GstStructure *structure = gst_caps_get_structure (caps, 0); - GstTheoraEnc *enc = GST_THEORA_ENC (gst_pad_get_parent (pad)); - guint32 fourcc; - const GValue *par; - gint fps_n, fps_d; - - gst_structure_get_fourcc (structure, "format", &fourcc); - gst_structure_get_int (structure, "width", &enc->width); - gst_structure_get_int (structure, "height", &enc->height); - gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d); - par = gst_structure_get_value (structure, "pixel-aspect-ratio"); - - th_info_clear (&enc->info); - th_info_init (&enc->info); - /* Theora has a divisible-by-sixteen restriction for the encoded video size but - * we can define a picture area using pic_width/pic_height */ - enc->info.frame_width = GST_ROUND_UP_16 (enc->width); - enc->info.frame_height = GST_ROUND_UP_16 (enc->height); - enc->info.pic_width = enc->width; - enc->info.pic_height = enc->height; - switch (fourcc) { - case GST_MAKE_FOURCC ('I', '4', '2', '0'): - enc->info.pixel_fmt = TH_PF_420; - break; - case GST_MAKE_FOURCC ('Y', '4', '2', 'B'): - enc->info.pixel_fmt = TH_PF_422; - break; - case GST_MAKE_FOURCC ('Y', '4', '4', '4'): - enc->info.pixel_fmt = TH_PF_444; - break; - default: - g_assert_not_reached (); - } - - enc->info.fps_numerator = enc->fps_n = fps_n; - enc->info.fps_denominator = enc->fps_d = fps_d; - if (par) { - enc->info.aspect_numerator = gst_value_get_fraction_numerator (par); - enc->info.aspect_denominator = gst_value_get_fraction_denominator (par); - } else { - /* setting them to 0 indicates that the decoder can chose a good aspect - * ratio, defaulting to 1/1 */ - enc->info.aspect_numerator = 0; - enc->info.aspect_denominator = 0; - } - - enc->info.colorspace = TH_CS_UNSPECIFIED; - enc->info.target_bitrate = enc->video_bitrate; - enc->info.quality = enc->video_quality; - - /* as done in theora */ - enc->info.keyframe_granule_shift = _ilog (enc->keyframe_force - 1); - GST_DEBUG_OBJECT (enc, - "keyframe_frequency_force is %d, granule shift is %d", - enc->keyframe_force, enc->info.keyframe_granule_shift); - - theora_enc_reset (enc); - enc->initialised = TRUE; - - gst_object_unref (enc); - - return TRUE; -} - -static guint64 -granulepos_add (guint64 granulepos, guint64 addend, gint shift) -{ - guint64 iframe, pframe; - - iframe = granulepos >> shift; - pframe = granulepos - (iframe << shift); - iframe += addend; - - return (iframe << shift) + pframe; -} - -/* prepare a buffer for transmission by passing data through libtheora */ -static GstFlowReturn -theora_buffer_from_packet (GstTheoraEnc * enc, ogg_packet * packet, - GstClockTime timestamp, GstClockTime running_time, - GstClockTime duration, GstBuffer ** buffer) -{ - GstBuffer *buf; - GstFlowReturn ret = GST_FLOW_OK; - - buf = gst_buffer_new_and_alloc (packet->bytes); - if (!buf) { - GST_WARNING_OBJECT (enc, "Could not allocate buffer"); - ret = GST_FLOW_ERROR; - goto done; - } - - memcpy (GST_BUFFER_DATA (buf), packet->packet, packet->bytes); - gst_buffer_set_caps (buf, GST_PAD_CAPS (enc->srcpad)); - /* see ext/ogg/README; OFFSET_END takes "our" granulepos, OFFSET its - * time representation */ - GST_BUFFER_OFFSET_END (buf) = - granulepos_add (packet->granulepos, enc->granulepos_offset, - enc->info.keyframe_granule_shift); - GST_BUFFER_OFFSET (buf) = granulepos_to_timestamp (enc, - GST_BUFFER_OFFSET_END (buf)); - - GST_BUFFER_TIMESTAMP (buf) = timestamp; - GST_BUFFER_DURATION (buf) = duration; - - if (enc->next_discont) { - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); - enc->next_discont = FALSE; - } - - /* the second most significant bit of the first data byte is cleared - * for keyframes */ - if ((packet->packet[0] & 0x40) == 0) { - GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DELTA_UNIT); - } else { - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); - } - enc->packetno++; - -done: - *buffer = buf; - return ret; -} - -/* push out the buffer and do internal bookkeeping */ -static GstFlowReturn -theora_push_buffer (GstTheoraEnc * enc, GstBuffer * buffer) -{ - GstFlowReturn ret; - - enc->bytes_out += GST_BUFFER_SIZE (buffer); - - ret = gst_pad_push (enc->srcpad, buffer); - - return ret; -} - -static GstFlowReturn -theora_push_packet (GstTheoraEnc * enc, ogg_packet * packet, - GstClockTime timestamp, GstClockTime running_time, GstClockTime duration) -{ - GstBuffer *buf; - GstFlowReturn ret; - - ret = - theora_buffer_from_packet (enc, packet, timestamp, running_time, duration, - &buf); - if (ret == GST_FLOW_OK) - ret = theora_push_buffer (enc, buf); - - return ret; -} - -static GstCaps * -theora_set_header_on_caps (GstCaps * caps, GSList * buffers) -{ - GstStructure *structure; - GValue array = { 0 }; - GValue value = { 0 }; - GstBuffer *buffer; - GSList *walk; - - caps = gst_caps_make_writable (caps); - structure = gst_caps_get_structure (caps, 0); - - /* put copies of the buffers in a fixed list */ - g_value_init (&array, GST_TYPE_ARRAY); - - for (walk = buffers; walk; walk = walk->next) { - buffer = walk->data; - - /* mark buffer */ - GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_IN_CAPS); - - /* Copy buffer, because we can't use the original - - * it creates a circular refcount with the caps<->buffers */ - buffer = gst_buffer_copy (buffer); - - g_value_init (&value, GST_TYPE_BUFFER); - gst_value_set_buffer (&value, buffer); - gst_value_array_append_value (&array, &value); - g_value_unset (&value); - - /* Unref our copy */ - gst_buffer_unref (buffer); - } - - gst_structure_set_value (structure, "streamheader", &array); - g_value_unset (&array); - - return caps; -} - -static void -theora_enc_force_keyframe (GstTheoraEnc * enc) -{ - GstClockTime next_ts; - - /* make sure timestamps increment after resetting the decoder */ - next_ts = enc->next_ts + enc->timestamp_offset; - - theora_enc_reset (enc); - enc->granulepos_offset = - gst_util_uint64_scale (next_ts, enc->fps_n, GST_SECOND * enc->fps_d); - enc->timestamp_offset = next_ts; - enc->next_ts = 0; -} - -static gboolean -theora_enc_sink_event (GstPad * pad, GstEvent * event) -{ - GstTheoraEnc *enc; - ogg_packet op; - gboolean res; - - enc = GST_THEORA_ENC (GST_PAD_PARENT (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_NEWSEGMENT: - { - gboolean update; - gdouble rate, applied_rate; - GstFormat format; - gint64 start, stop, time; - - gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, - &format, &start, &stop, &time); - - gst_segment_set_newsegment_full (&enc->segment, update, rate, - applied_rate, format, start, stop, time); - - res = gst_pad_push_event (enc->srcpad, event); - break; - } - case GST_EVENT_EOS: - if (enc->initialised) { - /* push last packet with eos flag, should not be called */ - while (th_encode_packetout (enc->encoder, 1, &op)) { - GstClockTime next_time = - th_granule_time (enc->encoder, op.granulepos) * GST_SECOND; - - theora_push_packet (enc, &op, GST_CLOCK_TIME_NONE, enc->next_ts, - next_time - enc->next_ts); - enc->next_ts = next_time; - } - } - res = gst_pad_push_event (enc->srcpad, event); - break; - case GST_EVENT_FLUSH_STOP: - gst_segment_init (&enc->segment, GST_FORMAT_UNDEFINED); - res = gst_pad_push_event (enc->srcpad, event); - break; - case GST_EVENT_CUSTOM_DOWNSTREAM: - { - const GstStructure *s; - - s = gst_event_get_structure (event); - - if (gst_structure_has_name (s, "GstForceKeyUnit")) - theora_enc_force_keyframe (enc); - res = gst_pad_push_event (enc->srcpad, event); - break; - } - default: - res = gst_pad_push_event (enc->srcpad, event); - break; - } - return res; -} - -static gboolean -theora_enc_src_event (GstPad * pad, GstEvent * event) -{ - GstTheoraEnc *enc; - gboolean res = TRUE; - - enc = GST_THEORA_ENC (GST_PAD_PARENT (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_CUSTOM_UPSTREAM: - { - const GstStructure *s; - - s = gst_event_get_structure (event); - - if (gst_structure_has_name (s, "GstForceKeyUnit")) { - GST_OBJECT_LOCK (enc); - enc->force_keyframe = TRUE; - GST_OBJECT_UNLOCK (enc); - /* consume the event */ - res = TRUE; - gst_event_unref (event); - } else { - res = gst_pad_push_event (enc->sinkpad, event); - } - break; - } - default: - res = gst_pad_push_event (enc->sinkpad, event); - break; - } - - return res; -} - -static gboolean -theora_enc_is_discontinuous (GstTheoraEnc * enc, GstClockTime timestamp, - GstClockTime duration) -{ - GstClockTimeDiff max_diff; - gboolean ret = FALSE; - - /* Allow 3/4 a frame off */ - max_diff = (enc->info.fps_denominator * GST_SECOND * 3) / - (enc->info.fps_numerator * 4); - - if (timestamp != GST_CLOCK_TIME_NONE - && enc->expected_ts != GST_CLOCK_TIME_NONE) { - if ((GstClockTimeDiff) (timestamp - enc->expected_ts) > max_diff) { - GST_DEBUG_OBJECT (enc, "Incoming TS %" GST_TIME_FORMAT - " exceeds expected value %" GST_TIME_FORMAT - " by too much, marking discontinuity", - GST_TIME_ARGS (timestamp), GST_TIME_ARGS (enc->expected_ts)); - ret = TRUE; - } - } - - if (GST_CLOCK_TIME_IS_VALID (duration)) - enc->expected_ts = timestamp + duration; - else - enc->expected_ts = GST_CLOCK_TIME_NONE; - - return ret; -} - -static void -theora_enc_init_buffer (th_ycbcr_buffer buf, th_info * info, guint8 * data) -{ - GstVideoFormat format; - guint i; - - switch (info->pixel_fmt) { - case TH_PF_444: - format = GST_VIDEO_FORMAT_Y444; - break; - case TH_PF_420: - format = GST_VIDEO_FORMAT_I420; - break; - case TH_PF_422: - format = GST_VIDEO_FORMAT_Y42B; - break; - default: - g_assert_not_reached (); - } - - /* According to Theora developer Timothy Terriberry, the Theora - * encoder will not use memory outside of pic_width/height, even when - * the frame size is bigger. The values outside this region will be encoded - * to default values. - * Due to this, setting the frame's width/height as the buffer width/height - * is perfectly ok, even though it does not strictly look ok. - */ - for (i = 0; i < 3; i++) { - buf[i].width = - gst_video_format_get_component_width (format, i, info->frame_width); - buf[i].height = - gst_video_format_get_component_height (format, i, info->frame_height); - - buf[i].data = - data + gst_video_format_get_component_offset (format, i, - info->pic_width, info->pic_height); - buf[i].stride = - gst_video_format_get_row_stride (format, i, info->pic_width); - } -} - -static GstFlowReturn -theora_enc_chain (GstPad * pad, GstBuffer * buffer) -{ - GstTheoraEnc *enc; - ogg_packet op; - GstClockTime timestamp, duration, running_time; - GstFlowReturn ret; - gboolean force_keyframe; - - enc = GST_THEORA_ENC (GST_PAD_PARENT (pad)); - - /* we keep track of two timelines. - * - The timestamps from the incomming buffers, which we copy to the outgoing - * encoded buffers as-is. We need to do this as we simply forward the - * newsegment events. - * - The running_time of the buffers, which we use to construct the granulepos - * in the packets. - */ - timestamp = GST_BUFFER_TIMESTAMP (buffer); - duration = GST_BUFFER_DURATION (buffer); - - running_time = - gst_segment_to_running_time (&enc->segment, GST_FORMAT_TIME, timestamp); - if ((gint64) running_time < 0) { - GST_DEBUG_OBJECT (enc, "Dropping buffer, timestamp: %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer))); - gst_buffer_unref (buffer); - return GST_FLOW_OK; - } - - /* see if we need to schedule a keyframe */ - GST_OBJECT_LOCK (enc); - force_keyframe = enc->force_keyframe; - enc->force_keyframe = FALSE; - GST_OBJECT_UNLOCK (enc); - - if (force_keyframe) { - GstClockTime stream_time; - GstStructure *s; - - stream_time = gst_segment_to_stream_time (&enc->segment, - GST_FORMAT_TIME, timestamp); - - s = gst_structure_new ("GstForceKeyUnit", - "timestamp", G_TYPE_UINT64, timestamp, - "stream-time", G_TYPE_UINT64, stream_time, - "running-time", G_TYPE_UINT64, running_time, NULL); - - theora_enc_force_keyframe (enc); - - gst_pad_push_event (enc->srcpad, - gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s)); - } - - /* make sure we copy the discont flag to the next outgoing buffer when it's - * set on the incomming buffer */ - if (GST_BUFFER_IS_DISCONT (buffer)) { - enc->next_discont = TRUE; - } - - if (enc->packetno == 0) { - /* no packets written yet, setup headers */ - GstCaps *caps; - GstBuffer *buf; - GSList *buffers = NULL; - int result; - - enc->granulepos_offset = 0; - enc->timestamp_offset = 0; - - GST_DEBUG_OBJECT (enc, "output headers"); - /* Theora streams begin with three headers; the initial header (with - most of the codec setup parameters) which is mandated by the Ogg - bitstream spec. The second header holds any comment fields. The - third header holds the bitstream codebook. We merely need to - make the headers, then pass them to libtheora one at a time; - libtheora handles the additional Ogg bitstream constraints */ - - /* create the remaining theora headers */ - th_comment_clear (&enc->comment); - th_comment_init (&enc->comment); - - while ((result = - th_encode_flushheader (enc->encoder, &enc->comment, &op)) > 0) { - ret = - theora_buffer_from_packet (enc, &op, GST_CLOCK_TIME_NONE, - GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE, &buf); - if (ret != GST_FLOW_OK) { - goto header_buffer_alloc; - } - buffers = g_slist_prepend (buffers, buf); - } - if (result < 0) { - g_slist_foreach (buffers, (GFunc) gst_buffer_unref, NULL); - g_slist_free (buffers); - goto encoder_disabled; - } - - buffers = g_slist_reverse (buffers); - - /* mark buffers and put on caps */ - caps = gst_pad_get_caps (enc->srcpad); - caps = theora_set_header_on_caps (caps, buffers); - GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps); - gst_pad_set_caps (enc->srcpad, caps); - - g_slist_foreach (buffers, (GFunc) gst_buffer_set_caps, caps); - - gst_caps_unref (caps); - - /* push out the header buffers */ - while (buffers) { - buf = buffers->data; - buffers = g_slist_delete_link (buffers, buffers); - if ((ret = theora_push_buffer (enc, buf)) != GST_FLOW_OK) { - g_slist_foreach (buffers, (GFunc) gst_buffer_unref, NULL); - g_slist_free (buffers); - goto header_push; - } - } - - enc->granulepos_offset = - gst_util_uint64_scale (running_time, enc->fps_n, - GST_SECOND * enc->fps_d); - enc->timestamp_offset = running_time; - enc->next_ts = 0; - } - - { - th_ycbcr_buffer ycbcr; - gint res; - - theora_enc_init_buffer (ycbcr, &enc->info, GST_BUFFER_DATA (buffer)); - - if (theora_enc_is_discontinuous (enc, running_time, duration)) { - theora_enc_reset (enc); - enc->granulepos_offset = - gst_util_uint64_scale (running_time, enc->fps_n, - GST_SECOND * enc->fps_d); - enc->timestamp_offset = running_time; - enc->next_ts = 0; - enc->next_discont = TRUE; - } - - res = th_encode_ycbcr_in (enc->encoder, ycbcr); - /* none of the failure cases can happen here */ - g_assert (res == 0); - - ret = GST_FLOW_OK; - while (th_encode_packetout (enc->encoder, 0, &op)) { - GstClockTime next_time; - - next_time = th_granule_time (enc->encoder, op.granulepos) * GST_SECOND; - - ret = - theora_push_packet (enc, &op, timestamp, enc->next_ts, - next_time - enc->next_ts); - - enc->next_ts = next_time; - if (ret != GST_FLOW_OK) - goto data_push; - } - gst_buffer_unref (buffer); - } - - return ret; - - /* ERRORS */ -header_buffer_alloc: - { - gst_buffer_unref (buffer); - return ret; - } -header_push: - { - gst_buffer_unref (buffer); - return ret; - } -data_push: - { - gst_buffer_unref (buffer); - return ret; - } -encoder_disabled: - { - GST_ELEMENT_ERROR (enc, STREAM, ENCODE, (NULL), - ("libtheora has been compiled with the encoder disabled")); - gst_buffer_unref (buffer); - return GST_FLOW_ERROR; - } -} - -static GstStateChangeReturn -theora_enc_change_state (GstElement * element, GstStateChange transition) -{ - GstTheoraEnc *enc; - GstStateChangeReturn ret; - - enc = GST_THEORA_ENC (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - GST_DEBUG_OBJECT (enc, "READY->PAUSED Initing theora state"); - th_info_init (&enc->info); - th_comment_init (&enc->comment); - enc->packetno = 0; - enc->force_keyframe = FALSE; - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - default: - break; - } - - ret = parent_class->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_DEBUG_OBJECT (enc, "PAUSED->READY Clearing theora state"); - if (enc->encoder) { - th_encode_free (enc->encoder); - enc->encoder = NULL; - } - th_comment_clear (&enc->comment); - th_info_clear (&enc->info); - - theora_enc_clear (enc); - enc->initialised = FALSE; - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - - return ret; -} - -static void -theora_enc_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstTheoraEnc *enc = GST_THEORA_ENC (object); - - switch (prop_id) { - case ARG_CENTER: - case ARG_BORDER: - case ARG_QUICK: - case ARG_KEYFRAME_THRESHOLD: - case ARG_KEYFRAME_MINDISTANCE: - case ARG_NOISE_SENSITIVITY: - case ARG_SHARPNESS: - /* kept for API compat, but ignored */ - break; - case ARG_BITRATE: - enc->video_bitrate = g_value_get_int (value) * 1000; - enc->video_quality = 0; - break; - case ARG_QUALITY: - enc->video_quality = g_value_get_int (value); - enc->video_bitrate = 0; - break; - case ARG_KEYFRAME_AUTO: - enc->keyframe_auto = g_value_get_boolean (value); - break; - case ARG_KEYFRAME_FREQ: - enc->keyframe_freq = g_value_get_int (value); - break; - case ARG_KEYFRAME_FREQ_FORCE: - enc->keyframe_force = g_value_get_int (value); - break; - case ARG_SPEEDLEVEL: -#ifdef TH_ENCCTL_SET_SPLEVEL - enc->speed_level = g_value_get_int (value); -#endif - break; - case ARG_VP3_COMPATIBLE: -#ifdef TH_ENCCTL_SET_VP3_COMPATIBLE - enc->vp3_compatible = g_value_get_boolean (value); -#endif - break; - case ARG_DROP_FRAMES: -#ifdef TH_ENCCTL_SET_RATE_FLAGS - enc->drop_frames = g_value_get_boolean (value); -#endif - break; - case ARG_CAP_OVERFLOW: -#ifdef TH_ENCCTL_SET_RATE_FLAGS - enc->cap_overflow = g_value_get_boolean (value); -#endif - break; - case ARG_CAP_UNDERFLOW: -#ifdef TH_ENCCTL_SET_RATE_FLAGS - enc->cap_underflow = g_value_get_boolean (value); -#endif - break; - case ARG_RATE_BUFFER: -#ifdef TH_ENCCTL_SET_RATE_BUFFER - enc->rate_buffer = g_value_get_int (value); -#endif - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -theora_enc_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstTheoraEnc *enc = GST_THEORA_ENC (object); - - switch (prop_id) { - case ARG_CENTER: - g_value_set_boolean (value, TRUE); - break; - case ARG_BORDER: - g_value_set_enum (value, BORDER_BLACK); - break; - case ARG_BITRATE: - g_value_set_int (value, enc->video_bitrate / 1000); - break; - case ARG_QUALITY: - g_value_set_int (value, enc->video_quality); - break; - case ARG_QUICK: - g_value_set_boolean (value, TRUE); - break; - case ARG_KEYFRAME_AUTO: - g_value_set_boolean (value, enc->keyframe_auto); - break; - case ARG_KEYFRAME_FREQ: - g_value_set_int (value, enc->keyframe_freq); - break; - case ARG_KEYFRAME_FREQ_FORCE: - g_value_set_int (value, enc->keyframe_force); - break; - case ARG_KEYFRAME_THRESHOLD: - g_value_set_int (value, 80); - break; - case ARG_KEYFRAME_MINDISTANCE: - g_value_set_int (value, 8); - break; - case ARG_NOISE_SENSITIVITY: - g_value_set_int (value, 1); - break; - case ARG_SHARPNESS: - g_value_set_int (value, 0); - break; - case ARG_SPEEDLEVEL: - g_value_set_int (value, enc->speed_level); - break; - case ARG_VP3_COMPATIBLE: - g_value_set_boolean (value, enc->vp3_compatible); - break; - case ARG_DROP_FRAMES: - g_value_set_boolean (value, enc->drop_frames); - break; - case ARG_CAP_OVERFLOW: - g_value_set_boolean (value, enc->cap_overflow); - break; - case ARG_CAP_UNDERFLOW: - g_value_set_boolean (value, enc->cap_underflow); - break; - case ARG_RATE_BUFFER: - g_value_set_int (value, enc->rate_buffer); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} diff --git a/ext/theora/gsttheoraenc.h b/ext/theora/gsttheoraenc.h deleted file mode 100644 index 3efdab20..00000000 --- a/ext/theora/gsttheoraenc.h +++ /dev/null @@ -1,116 +0,0 @@ -/* GStreamer - * Copyright (C) 2004 Wim Taymans <wim@fluendo.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_THEORAENC_H__ -#define __GST_THEORAENC_H__ - -#include <gst/gst.h> -#include <theora/theoraenc.h> - -G_BEGIN_DECLS - -#define GST_TYPE_THEORA_ENC \ - (gst_theora_enc_get_type()) -#define GST_THEORA_ENC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_THEORA_ENC,GstTheoraEnc)) -#define GST_THEORA_ENC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_THEORA_ENC,GstTheoraEncClass)) -#define GST_IS_THEORA_ENC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_THEORA_ENC)) -#define GST_IS_THEORA_ENC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_THEORA_ENC)) - -typedef struct _GstTheoraEnc GstTheoraEnc; -typedef struct _GstTheoraEncClass GstTheoraEncClass; - -/** - * GstTheoraEncBorderMode: - * @BORDER_NONE: no border - * @BORDER_BLACK: black border - * @BORDER_MIRROR: Mirror image in border - * - * Border color to add when sizes not multiple of 16. - */ -typedef enum -{ - BORDER_NONE, - BORDER_BLACK, - BORDER_MIRROR -} -GstTheoraEncBorderMode; - -/** - * GstTheoraEnc: - * - * Opaque data structure. - */ -struct _GstTheoraEnc -{ - GstElement element; - - GstPad *sinkpad; - GstPad *srcpad; - - GstSegment segment; - - ogg_stream_state to; - - th_enc_ctx *encoder; - th_info info; - th_comment comment; - gboolean initialised; - - gint video_bitrate; /* bitrate target for Theora video */ - gint video_quality; /* Theora quality selector 0 = low, 63 = high */ - gboolean keyframe_auto; - gint keyframe_freq; - gint keyframe_force; - - gint info_width, info_height; - gint width, height; - gint fps_n, fps_d; - GstClockTime next_ts; - - GstClockTime expected_ts; - gboolean next_discont; - - gboolean force_keyframe; - - guint packetno; - guint64 bytes_out; - guint64 granulepos_offset; - guint64 timestamp_offset; - - gint speed_level; - gboolean vp3_compatible; - gboolean drop_frames; - gboolean cap_overflow; - gboolean cap_underflow; - int rate_buffer; -}; - -struct _GstTheoraEncClass -{ - GstElementClass parent_class; -}; - -G_END_DECLS - -#endif /* __GST_THEORAENC_H__ */ - diff --git a/ext/theora/gsttheoraparse.c b/ext/theora/gsttheoraparse.c deleted file mode 100644 index 79c90072..00000000 --- a/ext/theora/gsttheoraparse.c +++ /dev/null @@ -1,941 +0,0 @@ -/* GStreamer - * Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org> - * Copyright (C) 2006 Andy Wingo <wingo@pobox.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-theoraparse - * @see_also: theoradec, oggdemux, vorbisparse - * - * The theoraparse element will parse the header packets of the Theora - * stream and put them as the streamheader in the caps. This is used in the - * multifdsink case where you want to stream live theora streams to multiple - * clients, each client has to receive the streamheaders first before they can - * consume the theora packets. - * - * This element also makes sure that the buffers that it pushes out are properly - * timestamped and that their offset and offset_end are set. The buffers that - * theoraparse outputs have all of the metadata that oggmux expects to receive, - * which allows you to (for example) remux an ogg/theora file. - * - * In addition, this element allows you to fix badly synchronized streams. You - * pass in an array of (granule time, buffer time) synchronization points via - * the synchronization-points GValueArray property, and this element will adjust - * the granulepos values that it outputs. The adjustment will be made by - * offsetting all buffers that it outputs by a specified amount, and updating - * that offset from the value array whenever a keyframe is processed. - * - * <refsect2> - * <title>Example pipelines</title> - * |[ - * gst-launch -v filesrc location=video.ogg ! oggdemux ! theoraparse ! fakesink - * ]| This pipeline shows that the streamheader is set in the caps, and that each - * buffer has the timestamp, duration, offset, and offset_end set. - * |[ - * gst-launch filesrc location=video.ogg ! oggdemux ! theoraparse \ - * ! oggmux ! filesink location=video-remuxed.ogg - * ]| This pipeline shows remuxing. video-remuxed.ogg might not be exactly the same - * as video.ogg, but they should produce exactly the same decoded data. - * </refsect2> - * - * Last reviewed on 2008-05-28 (0.10.20) - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "gsttheoraparse.h" - -#define GST_CAT_DEFAULT theoraparse_debug -GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); - -static GstElementDetails theora_parse_details = { - "Theora video parser", - "Codec/Parser/Video", - "parse raw theora streams", - "Andy Wingo <wingo@pobox.com>" -}; - -static GstStaticPadTemplate theora_parse_sink_factory = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-theora") - ); - -static GstStaticPadTemplate theora_parse_src_factory = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-theora") - ); - -enum -{ - PROP_0, - PROP_SYNCHRONIZATION_POINTS -}; - -GST_BOILERPLATE (GstTheoraParse, gst_theora_parse, GstElement, - GST_TYPE_ELEMENT); - -static void theora_parse_dispose (GObject * object); -static void theora_parse_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static void theora_parse_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); - -static gboolean theora_parse_src_query (GstPad * pad, GstQuery * query); -static GstFlowReturn theora_parse_chain (GstPad * pad, GstBuffer * buffer); -static GstStateChangeReturn theora_parse_change_state (GstElement * element, - GstStateChange transition); -static gboolean theora_parse_sink_event (GstPad * pad, GstEvent * event); -static gboolean theora_parse_src_query (GstPad * pad, GstQuery * query); - -static void -gst_theora_parse_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&theora_parse_src_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&theora_parse_sink_factory)); - gst_element_class_set_details (element_class, &theora_parse_details); -} - -static void -gst_theora_parse_class_init (GstTheoraParseClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - - gobject_class->dispose = theora_parse_dispose; - gobject_class->get_property = theora_parse_get_property; - gobject_class->set_property = theora_parse_set_property; - - /** - * GstTheoraParse:sychronization-points - * - * An array of (granuletime, buffertime) pairs - * - * Since: 0.10.10 - */ - g_object_class_install_property (gobject_class, PROP_SYNCHRONIZATION_POINTS, - g_param_spec_value_array ("synchronization-points", - "Synchronization points", - "An array of (granuletime, buffertime) pairs", - g_param_spec_uint64 ("time", "Time", - "Time (either granuletime or buffertime)", 0, G_MAXUINT64, 0, - G_PARAM_READWRITE), - (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - gstelement_class->change_state = theora_parse_change_state; - - GST_DEBUG_CATEGORY_INIT (theoraparse_debug, "theoraparse", 0, - "Theora parser"); -} - -static void -gst_theora_parse_init (GstTheoraParse * parse, GstTheoraParseClass * g_class) -{ - parse->sinkpad = - gst_pad_new_from_static_template (&theora_parse_sink_factory, "sink"); - gst_pad_set_chain_function (parse->sinkpad, theora_parse_chain); - gst_pad_set_event_function (parse->sinkpad, theora_parse_sink_event); - gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad); - - parse->srcpad = - gst_pad_new_from_static_template (&theora_parse_src_factory, "src"); - gst_pad_set_query_function (parse->srcpad, theora_parse_src_query); - gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad); -} - -static void -theora_parse_dispose (GObject * object) -{ - GstTheoraParse *parse = GST_THEORA_PARSE (object); - - g_free (parse->times); - parse->times = NULL; - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -theora_parse_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstTheoraParse *parse = GST_THEORA_PARSE (object); - - switch (prop_id) { - case PROP_SYNCHRONIZATION_POINTS: - { - GValueArray *array; - guint i; - - array = g_value_get_boxed (value); - - if (array) { - if (array->n_values % 2) - goto odd_values; - - g_free (parse->times); - parse->times = g_new (GstClockTime, array->n_values); - parse->npairs = array->n_values / 2; - for (i = 0; i < array->n_values; i++) - parse->times[i] = g_value_get_uint64 (&array->values[i]); - } else { - g_free (parse->times); - parse->npairs = 0; - } - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - - return; - -odd_values: - { - g_critical ("expected an even number of time values for " - "synchronization-points"); - return; - } -} - -static void -theora_parse_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstTheoraParse *parse = GST_THEORA_PARSE (object); - - switch (prop_id) { - case PROP_SYNCHRONIZATION_POINTS: - { - GValueArray *array = NULL; - guint i; - - array = g_value_array_new (parse->npairs * 2); - - for (i = 0; i < parse->npairs; i++) { - GValue v = { 0, }; - - g_value_init (&v, G_TYPE_UINT64); - g_value_set_uint64 (&v, parse->times[i * 2]); - g_value_array_append (array, &v); - g_value_set_uint64 (&v, parse->times[i * 2 + 1]); - g_value_array_append (array, &v); - g_value_unset (&v); - } - - g_value_set_boxed (value, array); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -theora_parse_set_header_on_caps (GstTheoraParse * parse, GstCaps * caps) -{ - GstBuffer **bufs; - GstStructure *structure; - gint i; - GValue array = { 0 }; - GValue value = { 0 }; - - bufs = parse->streamheader; - structure = gst_caps_get_structure (caps, 0); - g_value_init (&array, GST_TYPE_ARRAY); - - for (i = 0; i < 3; i++) { - if (bufs[i] == NULL) - continue; - - bufs[i] = gst_buffer_make_metadata_writable (bufs[i]); - GST_BUFFER_FLAG_SET (bufs[i], GST_BUFFER_FLAG_IN_CAPS); - - g_value_init (&value, GST_TYPE_BUFFER); - gst_value_set_buffer (&value, bufs[i]); - gst_value_array_append_value (&array, &value); - g_value_unset (&value); - } - - gst_structure_set_value (structure, "streamheader", &array); - g_value_unset (&array); -} - -/* two tasks to do here: set the streamheader on the caps, and use libtheora to - parse the headers */ -static void -theora_parse_set_streamheader (GstTheoraParse * parse) -{ - GstCaps *caps; - gint i; - guint32 bitstream_version; - th_setup_info *setup = NULL; - - g_assert (!parse->streamheader_received); - - caps = gst_caps_make_writable (gst_pad_get_caps (parse->srcpad)); - theora_parse_set_header_on_caps (parse, caps); - GST_DEBUG_OBJECT (parse, "here are the caps: %" GST_PTR_FORMAT, caps); - gst_pad_set_caps (parse->srcpad, caps); - gst_caps_unref (caps); - - for (i = 0; i < 3; i++) { - ogg_packet packet; - GstBuffer *buf; - int ret; - - buf = parse->streamheader[i]; - if (buf == NULL) - continue; - - gst_buffer_set_caps (buf, GST_PAD_CAPS (parse->srcpad)); - - packet.packet = GST_BUFFER_DATA (buf); - packet.bytes = GST_BUFFER_SIZE (buf); - packet.granulepos = GST_BUFFER_OFFSET_END (buf); - packet.packetno = i + 1; - packet.e_o_s = 0; - packet.b_o_s = (i == 0); - ret = th_decode_headerin (&parse->info, &parse->comment, &setup, &packet); - if (ret < 0) { - GST_WARNING_OBJECT (parse, "Failed to decode Theora header %d: %d\n", - i + 1, ret); - } - } - if (setup) { - th_setup_free (setup); - } - - parse->fps_n = parse->info.fps_numerator; - parse->fps_d = parse->info.fps_denominator; - parse->shift = parse->info.keyframe_granule_shift; - - /* With libtheora-1.0beta1 the granulepos scheme was changed: - * where earlier the granulepos refered to the index/beginning - * of a frame, it now refers to the end, which matches the use - * in vorbis/speex. We check the bitstream version from the header so - * we know which way to interpret the incoming granuepos - */ - bitstream_version = (parse->info.version_major << 16) | - (parse->info.version_minor << 8) | parse->info.version_subminor; - parse->is_old_bitstream = (bitstream_version <= 0x00030200); - - parse->streamheader_received = TRUE; -} - -static void -theora_parse_drain_event_queue (GstTheoraParse * parse) -{ - while (parse->event_queue->length) { - GstEvent *event; - - event = GST_EVENT_CAST (g_queue_pop_head (parse->event_queue)); - gst_pad_event_default (parse->sinkpad, event); - } -} - -static void -theora_parse_push_headers (GstTheoraParse * parse) -{ - gint i; - - theora_parse_drain_event_queue (parse); - - if (!parse->streamheader_received) - theora_parse_set_streamheader (parse); - - /* ignore return values, we pass along the result of pushing data packets only - */ - for (i = 0; i < 3; i++) { - GstBuffer *buf; - - if ((buf = parse->streamheader[i])) - gst_pad_push (parse->srcpad, gst_buffer_ref (buf)); - } -} - -static void -theora_parse_clear_queue (GstTheoraParse * parse) -{ - while (parse->buffer_queue->length) { - GstBuffer *buf; - - buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue)); - gst_buffer_unref (buf); - } - while (parse->event_queue->length) { - GstEvent *event; - - event = GST_EVENT_CAST (g_queue_pop_head (parse->event_queue)); - gst_event_unref (event); - } -} - -static gint64 -make_granulepos (GstTheoraParse * parse, gint64 keyframe, gint64 frame) -{ - gint64 iframe; - - if (keyframe == -1) - keyframe = 0; - /* If using newer theora, offset the granulepos by +1, see comment in - * theora_parse_set_streamheader. - * - * We don't increment keyframe directly, as internally we always index frames - * starting from 0 and we do some sanity checking below. */ - if (!parse->is_old_bitstream) - iframe = keyframe + 1; - else - iframe = keyframe; - - g_return_val_if_fail (frame >= keyframe, -1); - g_return_val_if_fail (frame - keyframe < 1 << parse->shift, -1); - - return (iframe << parse->shift) + (frame - keyframe); -} - -static void -parse_granulepos (GstTheoraParse * parse, gint64 granulepos, - gint64 * keyframe, gint64 * frame) -{ - gint64 kf; - - kf = granulepos >> parse->shift; - /* If using newer theora, offset the granulepos by -1, see comment - * in theora_parse_set_streamheader */ - if (!parse->is_old_bitstream) - kf -= 1; - if (keyframe) - *keyframe = kf; - if (frame) - *frame = kf + (granulepos & ((1 << parse->shift) - 1)); -} - -static gboolean -is_keyframe (GstBuffer * buf) -{ - if (!GST_BUFFER_DATA (buf)) - return FALSE; - if (!GST_BUFFER_SIZE (buf)) - return FALSE; - return ((GST_BUFFER_DATA (buf)[0] & 0x40) == 0); -} - -static void -theora_parse_munge_granulepos (GstTheoraParse * parse, GstBuffer * buf, - gint64 keyframe, gint64 frame) -{ - gint64 frames_diff; - GstClockTimeDiff time_diff; - - if (keyframe == frame) { - gint i; - - /* update granule_offset */ - for (i = 0; i < parse->npairs; i++) { - if (parse->times[i * 2] >= GST_BUFFER_OFFSET (buf)) - break; - } - if (i > 0) { - /* time_diff gets reset below */ - time_diff = parse->times[i * 2 - 1] - parse->times[i * 2 - 2]; - parse->granule_offset = gst_util_uint64_scale (time_diff, - parse->fps_n, parse->fps_d * GST_SECOND); - parse->granule_offset <<= parse->shift; - } - } - - frames_diff = parse->granule_offset >> parse->shift; - time_diff = gst_util_uint64_scale_int (GST_SECOND * frames_diff, - parse->fps_d, parse->fps_n); - - GST_DEBUG_OBJECT (parse, "offsetting theora stream by %" G_GINT64_FORMAT - " frames (%" GST_TIME_FORMAT ")", frames_diff, GST_TIME_ARGS (time_diff)); - - GST_BUFFER_OFFSET_END (buf) += parse->granule_offset; - GST_BUFFER_OFFSET (buf) += time_diff; - GST_BUFFER_TIMESTAMP (buf) += time_diff; -} - -static GstFlowReturn -theora_parse_push_buffer (GstTheoraParse * parse, GstBuffer * buf, - gint64 keyframe, gint64 frame) -{ - - GstClockTime this_time, next_time; - - this_time = gst_util_uint64_scale_int (GST_SECOND * frame, - parse->fps_d, parse->fps_n); - - next_time = gst_util_uint64_scale_int (GST_SECOND * (frame + 1), - parse->fps_d, parse->fps_n); - - GST_BUFFER_OFFSET_END (buf) = make_granulepos (parse, keyframe, frame); - GST_BUFFER_OFFSET (buf) = this_time; - GST_BUFFER_TIMESTAMP (buf) = this_time; - GST_BUFFER_DURATION (buf) = next_time - this_time; - - gst_buffer_set_caps (buf, GST_PAD_CAPS (parse->srcpad)); - - if (parse->times) - theora_parse_munge_granulepos (parse, buf, keyframe, frame); - - GST_DEBUG_OBJECT (parse, "pushing buffer with granulepos %" G_GINT64_FORMAT - "|%" G_GINT64_FORMAT, keyframe, frame - keyframe); - - return gst_pad_push (parse->srcpad, buf); -} - -static GstFlowReturn -theora_parse_drain_queue_prematurely (GstTheoraParse * parse) -{ - GstFlowReturn ret = GST_FLOW_OK; - - /* got an EOS event, make sure to push out any buffers that were in the queue - * -- won't normally be the case, but this catches the - * didn't-get-a-granulepos-on-the-last-packet case. Assuming a continuous - * stream. */ - - GST_DEBUG_OBJECT (parse, "got EOS, draining queue"); - - /* if we get an eos before pushing the streamheaders, drain our events before - * eos */ - theora_parse_drain_event_queue (parse); - - while (!g_queue_is_empty (parse->buffer_queue)) { - GstBuffer *buf; - - buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue)); - - parse->prev_frame++; - - if (is_keyframe (buf)) - /* we have a keyframe */ - parse->prev_keyframe = parse->prev_frame; - else - GST_BUFFER_FLAGS (buf) |= GST_BUFFER_FLAG_DELTA_UNIT; - - if (parse->prev_keyframe < 0) { - if (GST_BUFFER_OFFSET_END_IS_VALID (buf)) { - parse_granulepos (parse, GST_BUFFER_OFFSET_END (buf), - &parse->prev_keyframe, NULL); - } else { - /* No previous keyframe known; can't extract one from this frame. That - * means we can't do any valid output for this frame, just continue to - * the next frame. - */ - gst_buffer_unref (buf); - continue; - } - } - - ret = theora_parse_push_buffer (parse, buf, parse->prev_keyframe, - parse->prev_frame); - - if (ret != GST_FLOW_OK) - goto done; - } - -done: - return ret; -} - -static GstFlowReturn -theora_parse_drain_queue (GstTheoraParse * parse, gint64 granulepos) -{ - GstFlowReturn ret = GST_FLOW_OK; - gint64 keyframe, prev_frame, frame; - - parse_granulepos (parse, granulepos, &keyframe, &frame); - - GST_DEBUG ("draining queue of length %d", - g_queue_get_length (parse->buffer_queue)); - - GST_LOG_OBJECT (parse, "gp %" G_GINT64_FORMAT ", kf %" G_GINT64_FORMAT - ", frame %" G_GINT64_FORMAT, granulepos, keyframe, frame); - - prev_frame = frame - g_queue_get_length (parse->buffer_queue); - - GST_LOG_OBJECT (parse, - "new prev %" G_GINT64_FORMAT ", prev %" G_GINT64_FORMAT, prev_frame, - parse->prev_frame); - - if (prev_frame < parse->prev_frame) { - GST_WARNING ("jumped %" G_GINT64_FORMAT - " frames backwards! not sure what to do here", - parse->prev_frame - prev_frame); - ret = GST_FLOW_ERROR; - goto done; - } else if (prev_frame > parse->prev_frame) { - GST_INFO ("discontinuity detected (%" G_GINT64_FORMAT - " frames)", prev_frame - parse->prev_frame); - if (keyframe <= prev_frame && keyframe > parse->prev_keyframe) - parse->prev_keyframe = keyframe; - parse->prev_frame = prev_frame; - } - - while (!g_queue_is_empty (parse->buffer_queue)) { - GstBuffer *buf; - - parse->prev_frame++; - g_assert (parse->prev_frame >= 0); - - buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue)); - - if (is_keyframe (buf)) - /* we have a keyframe */ - parse->prev_keyframe = parse->prev_frame; - else - GST_BUFFER_FLAGS (buf) |= GST_BUFFER_FLAG_DELTA_UNIT; - - ret = theora_parse_push_buffer (parse, buf, parse->prev_keyframe, - parse->prev_frame); - - if (ret != GST_FLOW_OK) - goto done; - } - -done: - return ret; -} - -static GstFlowReturn -theora_parse_queue_buffer (GstTheoraParse * parse, GstBuffer * buf) -{ - GstFlowReturn ret = GST_FLOW_OK; - - buf = gst_buffer_make_metadata_writable (buf); - - g_queue_push_tail (parse->buffer_queue, buf); - - if (GST_BUFFER_OFFSET_END_IS_VALID (buf)) { - if (parse->prev_keyframe < 0) { - parse_granulepos (parse, GST_BUFFER_OFFSET_END (buf), - &parse->prev_keyframe, NULL); - } - ret = theora_parse_drain_queue (parse, GST_BUFFER_OFFSET_END (buf)); - } - - return ret; -} - -static GstFlowReturn -theora_parse_chain (GstPad * pad, GstBuffer * buffer) -{ - GstFlowReturn ret; - GstTheoraParse *parse; - guint8 *data; - guint size; - gboolean have_header; - - parse = GST_THEORA_PARSE (gst_pad_get_parent (pad)); - - data = GST_BUFFER_DATA (buffer); - size = GST_BUFFER_SIZE (buffer); - - have_header = FALSE; - if (size >= 1) { - if (data[0] & 0x80) - have_header = TRUE; - } - - if (have_header) { - if (parse->send_streamheader) { - /* we need to collect the headers still */ - /* so put it on the streamheader list and return */ - if (data[0] >= 0x80 && data[0] <= 0x82) - parse->streamheader[data[0] - 0x80] = buffer; - } - ret = GST_FLOW_OK; - } else { - /* data packet, push the headers we collected before */ - if (parse->send_streamheader) { - theora_parse_push_headers (parse); - parse->send_streamheader = FALSE; - } - - ret = theora_parse_queue_buffer (parse, buffer); - } - - gst_object_unref (parse); - - return ret; -} - -static gboolean -theora_parse_queue_event (GstTheoraParse * parse, GstEvent * event) -{ - g_queue_push_tail (parse->event_queue, event); - return TRUE; -} - -static gboolean -theora_parse_sink_event (GstPad * pad, GstEvent * event) -{ - gboolean ret; - GstTheoraParse *parse; - - parse = GST_THEORA_PARSE (gst_pad_get_parent (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_STOP: - theora_parse_clear_queue (parse); - parse->prev_keyframe = -1; - parse->prev_frame = -1; - ret = gst_pad_event_default (pad, event); - break; - case GST_EVENT_EOS: - theora_parse_drain_queue_prematurely (parse); - ret = gst_pad_event_default (pad, event); - break; - default: - if (parse->send_streamheader && GST_EVENT_IS_SERIALIZED (event)) - ret = theora_parse_queue_event (parse, event); - else - ret = gst_pad_event_default (pad, event); - break; - } - - gst_object_unref (parse); - - return ret; -} - -static gboolean -theora_parse_src_convert (GstPad * pad, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = TRUE; - GstTheoraParse *parse; - guint64 scale = 1; - - if (src_format == *dest_format) { - *dest_value = src_value; - return TRUE; - } - - parse = GST_THEORA_PARSE (gst_pad_get_parent (pad)); - - /* we need the info part before we can done something */ - if (!parse->streamheader_received) - goto no_header; - - switch (src_format) { - case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_DEFAULT: - *dest_value = gst_util_uint64_scale_int (src_value, 2, - parse->info.pic_height * parse->info.pic_width * 3); - break; - case GST_FORMAT_TIME: - /* seems like a rather silly conversion, implement me if you like */ - default: - res = FALSE; - } - break; - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_BYTES: - scale = 3 * (parse->info.pic_width * parse->info.pic_height) / 2; - case GST_FORMAT_DEFAULT: - *dest_value = scale * gst_util_uint64_scale (src_value, - parse->info.fps_numerator, - parse->info.fps_denominator * GST_SECOND); - break; - default: - GST_DEBUG_OBJECT (parse, "cannot convert to format %s", - gst_format_get_name (*dest_format)); - res = FALSE; - } - break; - case GST_FORMAT_DEFAULT: - switch (*dest_format) { - case GST_FORMAT_TIME: - *dest_value = gst_util_uint64_scale (src_value, - GST_SECOND * parse->info.fps_denominator, - parse->info.fps_numerator); - break; - case GST_FORMAT_BYTES: - *dest_value = gst_util_uint64_scale_int (src_value, - 3 * parse->info.pic_width * parse->info.pic_height, 2); - break; - default: - res = FALSE; - } - break; - default: - res = FALSE; - } -done: - gst_object_unref (parse); - return res; - - /* ERRORS */ -no_header: - { - GST_DEBUG_OBJECT (parse, "no header yet, cannot convert"); - res = FALSE; - goto done; - } -} - -static gboolean -theora_parse_src_query (GstPad * pad, GstQuery * query) -{ - GstTheoraParse *parse; - - gboolean res = FALSE; - - parse = GST_THEORA_PARSE (gst_pad_get_parent (pad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - { - gint64 frame, value; - GstFormat my_format, format; - gint64 time; - - frame = parse->prev_frame; - - GST_LOG_OBJECT (parse, - "query %p: we have current frame: %" G_GINT64_FORMAT, query, frame); - - /* parse format */ - gst_query_parse_position (query, &format, NULL); - - /* and convert to the final format in two steps with time as the - * intermediate step */ - my_format = GST_FORMAT_TIME; - if (!(res = - theora_parse_src_convert (parse->sinkpad, GST_FORMAT_DEFAULT, - frame, &my_format, &time))) - goto error; - - /* fixme: handle segments - time = (time - parse->segment.start) + parse->segment.time; - */ - - GST_LOG_OBJECT (parse, - "query %p: our time: %" GST_TIME_FORMAT " (conv to %s)", - query, GST_TIME_ARGS (time), gst_format_get_name (format)); - - if (!(res = - theora_parse_src_convert (pad, my_format, time, &format, &value))) - goto error; - - gst_query_set_position (query, format, value); - - GST_LOG_OBJECT (parse, - "query %p: we return %" G_GINT64_FORMAT " (format %u)", query, value, - format); - - break; - } - case GST_QUERY_DURATION: - /* forward to peer for total */ - if (!(res = gst_pad_query (GST_PAD_PEER (parse->sinkpad), query))) - goto error; - break; - case GST_QUERY_CONVERT: - { - GstFormat src_fmt, dest_fmt; - gint64 src_val, dest_val; - - gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); - if (!(res = - theora_parse_src_convert (pad, src_fmt, src_val, &dest_fmt, - &dest_val))) - goto error; - - gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } -done: - gst_object_unref (parse); - - return res; - - /* ERRORS */ -error: - { - GST_DEBUG_OBJECT (parse, "query failed"); - goto done; - } -} - -static GstStateChangeReturn -theora_parse_change_state (GstElement * element, GstStateChange transition) -{ - GstTheoraParse *parse = GST_THEORA_PARSE (element); - GstStateChangeReturn ret; - gint i; - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - th_info_init (&parse->info); - th_comment_init (&parse->comment); - parse->send_streamheader = TRUE; - parse->buffer_queue = g_queue_new (); - parse->event_queue = g_queue_new (); - parse->prev_keyframe = -1; - parse->prev_frame = -1; - parse->granule_offset = 0; - break; - default: - break; - } - - ret = parent_class->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - th_info_clear (&parse->info); - th_comment_clear (&parse->comment); - theora_parse_clear_queue (parse); - g_queue_free (parse->buffer_queue); - g_queue_free (parse->event_queue); - parse->buffer_queue = NULL; - for (i = 0; i < 3; i++) { - if (parse->streamheader[i]) { - gst_buffer_unref (parse->streamheader[i]); - parse->streamheader[i] = NULL; - } - } - parse->streamheader_received = FALSE; - break; - default: - break; - } - - return ret; -} diff --git a/ext/theora/gsttheoraparse.h b/ext/theora/gsttheoraparse.h deleted file mode 100644 index 83142879..00000000 --- a/ext/theora/gsttheoraparse.h +++ /dev/null @@ -1,87 +0,0 @@ -/* -*- c-basic-offset: 2 -*- - * GStreamer - * Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org> - * Copyright (C) 2006 Andy Wingo <wingo@pobox.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_THEORA_PARSE_H__ -#define __GST_THEORA_PARSE_H__ - - -#include <gst/gst.h> -#include <theora/theoradec.h> - -G_BEGIN_DECLS - -#define GST_TYPE_THEORA_PARSE \ - (gst_theora_parse_get_type()) -#define GST_THEORA_PARSE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_THEORA_PARSE,GstTheoraParse)) -#define GST_THEORA_PARSE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_THEORA_PARSE,GstTheoraParseClass)) -#define GST_IS_THEORA_PARSE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_THEORA_PARSE)) -#define GST_IS_THEORA_PARSE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_THEORA_PARSE)) - -typedef struct _GstTheoraParse GstTheoraParse; -typedef struct _GstTheoraParseClass GstTheoraParseClass; - -/** - * GstTheoraParse: - * - * Opaque data structure. - */ -struct _GstTheoraParse { - GstElement element; - - GstPad * sinkpad; - GstPad * srcpad; - - gboolean send_streamheader; - gboolean streamheader_received; - gboolean is_old_bitstream; - GstBuffer * streamheader[3]; - - GQueue * event_queue; - GQueue * buffer_queue; - - th_info info; - th_comment comment; - - gint64 prev_frame; - gint64 prev_keyframe; - guint32 fps_n; - guint32 fps_d; - gint shift; - gint64 granule_offset; - - GstClockTime *times; - gint npairs; -}; - -struct _GstTheoraParseClass { - GstElementClass parent_class; -}; - -GType gst_theora_parse_get_type(void); - -G_END_DECLS - -#endif /* __GST_THEORA_PARSE_H__ */ diff --git a/ext/vorbis/Makefile.am b/ext/vorbis/Makefile.am deleted file mode 100644 index fc285a8d..00000000 --- a/ext/vorbis/Makefile.am +++ /dev/null @@ -1,46 +0,0 @@ -plugin_LTLIBRARIES = - -if USE_VORBIS -plugin_LTLIBRARIES += libgstvorbis.la - -libgstvorbis_la_SOURCES = gstvorbis.c \ - gstvorbisdec.c \ - gstvorbisdeclib.c \ - gstvorbisenc.c \ - gstvorbisparse.c \ - gstvorbistag.c \ - gstvorbiscommon.c - -libgstvorbis_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(VORBIS_CFLAGS) -## AM_PATH_VORBIS also sets VORBISENC_LIBS -libgstvorbis_la_LIBADD = \ - $(top_builddir)/gst-libs/gst/tag/libgsttag-@GST_MAJORMINOR@.la \ - $(top_builddir)/gst-libs/gst/audio/libgstaudio-@GST_MAJORMINOR@.la \ - $(GST_LIBS) \ - $(VORBIS_LIBS) $(VORBISENC_LIBS) -libgstvorbis_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstvorbis_la_LIBTOOLFLAGS = --tag=disable-static -endif - -if USE_IVORBIS -plugin_LTLIBRARIES += libgstivorbisdec.la - -libgstivorbisdec_la_SOURCES = gstivorbisdec.c \ - gstvorbisdec.c gstvorbisdeclib.c gstvorbiscommon.c -libgstivorbisdec_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \ - -DTREMOR $(IVORBIS_CFLAGS) -libgstivorbisdec_la_LIBADD = \ - $(top_builddir)/gst-libs/gst/tag/libgsttag-@GST_MAJORMINOR@.la \ - $(top_builddir)/gst-libs/gst/audio/libgstaudio-@GST_MAJORMINOR@.la \ - $(GST_LIBS) $(IVORBIS_LIBS) -libgstivorbisdec_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstivorbisdec_la_LIBTOOLFLAGS = --tag=disable-static -endif - -noinst_HEADERS = gstvorbisenc.h \ - gstvorbisdec.h \ - gstvorbisdeclib.h \ - gstvorbisparse.h \ - gstvorbistag.h \ - gstvorbiscommon.h - diff --git a/ext/vorbis/README b/ext/vorbis/README deleted file mode 100644 index 6b315101..00000000 --- a/ext/vorbis/README +++ /dev/null @@ -1,16 +0,0 @@ -oggvorbisenc : encodes to vorbis inside an ogg stream. This is not the - GStreamer way of doing things and should be removed for - 0.9. It is still called "vorbisenc" for backward compatibility - reasons. It also takes integer audio as input. -vorbisenc : Encodes to a raw vorbis stream and should be used together - with an ogg muxer such as "oggmux" it is called "rawvorbisenc". - It also takes raw float samples as input. - -TODO for 0.9: - -- remove oggvorbisenc.c and oggvorbisenc.h -- remove references to oggvorbisenc.[ch] in the Makefile and in vorbis.c -- remove the element vorbisenc. -- rename the element rawvorbisenc to vorbisenc. - - diff --git a/ext/vorbis/gstivorbisdec.c b/ext/vorbis/gstivorbisdec.c deleted file mode 100644 index d938238d..00000000 --- a/ext/vorbis/gstivorbisdec.c +++ /dev/null @@ -1,47 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstvorbisdec.h" - -GST_DEBUG_CATEGORY (vorbisdec_debug); - -static gboolean -plugin_init (GstPlugin * plugin) -{ - - /* if tremor is around, there is probably good reason for it, so preferred */ - if (!gst_element_register (plugin, "ivorbisdec", GST_RANK_SECONDARY, - gst_vorbis_dec_get_type ())) - return FALSE; - - GST_DEBUG_CATEGORY_INIT (vorbisdec_debug, "ivorbisdec", 0, - "vorbis decoding element (integer decoder)"); - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "ivorbisdec", - "Vorbis Tremor decoder", - plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/vorbis/gstvorbis.c b/ext/vorbis/gstvorbis.c deleted file mode 100644 index f710aaa3..00000000 --- a/ext/vorbis/gstvorbis.c +++ /dev/null @@ -1,73 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gst/tag/tag.h" - -#include "gstvorbisenc.h" -#include "gstvorbisdec.h" -#include "gstvorbisparse.h" -#include "gstvorbistag.h" - -GST_DEBUG_CATEGORY (vorbisenc_debug); -GST_DEBUG_CATEGORY (vorbisdec_debug); -GST_DEBUG_CATEGORY (vorbisparse_debug); -GST_DEBUG_CATEGORY (vorbistag_debug); - -static gboolean -plugin_init (GstPlugin * plugin) -{ - if (!gst_element_register (plugin, "vorbisenc", GST_RANK_PRIMARY, - GST_TYPE_VORBISENC)) - return FALSE; - - if (!gst_element_register (plugin, "vorbisdec", GST_RANK_PRIMARY, - gst_vorbis_dec_get_type ())) - return FALSE; - - if (!gst_element_register (plugin, "vorbisparse", GST_RANK_NONE, - gst_vorbis_parse_get_type ())) - return FALSE; - - if (!gst_element_register (plugin, "vorbistag", GST_RANK_NONE, - gst_vorbis_tag_get_type ())) - return FALSE; - - GST_DEBUG_CATEGORY_INIT (vorbisenc_debug, "vorbisenc", 0, - "vorbis encoding element"); - GST_DEBUG_CATEGORY_INIT (vorbisdec_debug, "vorbisdec", 0, - "vorbis decoding element"); - GST_DEBUG_CATEGORY_INIT (vorbisparse_debug, "vorbisparse", 0, - "vorbis parsing element"); - GST_DEBUG_CATEGORY_INIT (vorbistag_debug, "vorbistag", 0, - "vorbis tagging element"); - - gst_tag_register_musicbrainz_tags (); - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "vorbis", - "Vorbis plugin library", - plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/vorbis/gstvorbiscommon.c b/ext/vorbis/gstvorbiscommon.c deleted file mode 100644 index c4dd408d..00000000 --- a/ext/vorbis/gstvorbiscommon.c +++ /dev/null @@ -1,75 +0,0 @@ -/* GStreamer - * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstvorbiscommon.h" - -/* http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9 */ -const GstAudioChannelPosition gst_vorbis_channel_positions[][8] = { - { /* Mono */ - GST_AUDIO_CHANNEL_POSITION_FRONT_MONO}, - { /* Stereo */ - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, - { /* Stereo + Centre */ - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, - { /* Quadraphonic */ - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, - }, - { /* Stereo + Centre + rear stereo */ - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, - }, - { /* Full 5.1 Surround */ - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, - GST_AUDIO_CHANNEL_POSITION_LFE, - }, - { /* 6.1 Surround, in Vorbis spec since 2010-01-13 */ - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, - GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, - GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, - GST_AUDIO_CHANNEL_POSITION_LFE}, - { /* 7.1 Surround, in Vorbis spec since 2010-01-13 */ - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, - GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, - GST_AUDIO_CHANNEL_POSITION_LFE}, -}; diff --git a/ext/vorbis/gstvorbiscommon.h b/ext/vorbis/gstvorbiscommon.h deleted file mode 100644 index 563b8f71..00000000 --- a/ext/vorbis/gstvorbiscommon.h +++ /dev/null @@ -1,28 +0,0 @@ -/* GStreamer - * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_VORBIS_COMMON_H__ -#define __GST_VORBIS_COMMON_H__ - -#include <gst/gst.h> -#include <gst/audio/multichannel.h> - -extern const GstAudioChannelPosition gst_vorbis_channel_positions[][8]; - -#endif /* __GST_VORBIS_COMMON_H__ */ diff --git a/ext/vorbis/gstvorbisdec.c b/ext/vorbis/gstvorbisdec.c deleted file mode 100644 index 85f36d09..00000000 --- a/ext/vorbis/gstvorbisdec.c +++ /dev/null @@ -1,1228 +0,0 @@ -/* GStreamer - * Copyright (C) 2004 Benjamin Otte <in7y118@public.uni-hamburg.de> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-vorbisdec - * @see_also: vorbisenc, oggdemux - * - * This element decodes a Vorbis stream to raw float audio. - * <ulink url="http://www.vorbis.com/">Vorbis</ulink> is a royalty-free - * audio codec maintained by the <ulink url="http://www.xiph.org/">Xiph.org - * Foundation</ulink>. - * - * <refsect2> - * <title>Example pipelines</title> - * |[ - * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! alsasink - * ]| Decode an Ogg/Vorbis. To create an Ogg/Vorbis file refer to the documentation of vorbisenc. - * </refsect2> - * - * Last reviewed on 2006-03-01 (0.10.4) - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "gstvorbisdec.h" -#include <string.h> -#include <gst/audio/audio.h> -#include <gst/tag/tag.h> -#include <gst/audio/multichannel.h> - -#include "gstvorbiscommon.h" - -GST_DEBUG_CATEGORY_EXTERN (vorbisdec_debug); -#define GST_CAT_DEFAULT vorbisdec_debug - -static const GstElementDetails vorbis_dec_details = - GST_VORBIS_DEC_ELEMENT_DETAILS; - -static GstStaticPadTemplate vorbis_dec_src_factory = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_VORBIS_DEC_SRC_CAPS); - -static GstStaticPadTemplate vorbis_dec_sink_factory = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-vorbis") - ); - -GST_BOILERPLATE (GST_VORBIS_DEC_GLIB_TYPE_NAME, gst_vorbis_dec, GstElement, - GST_TYPE_ELEMENT); - -static void vorbis_dec_finalize (GObject * object); -static gboolean vorbis_dec_sink_event (GstPad * pad, GstEvent * event); -static GstFlowReturn vorbis_dec_chain (GstPad * pad, GstBuffer * buffer); -static GstFlowReturn vorbis_dec_chain_forward (GstVorbisDec * vd, - gboolean discont, GstBuffer * buffer); -static GstStateChangeReturn vorbis_dec_change_state (GstElement * element, - GstStateChange transition); - -static gboolean vorbis_dec_src_event (GstPad * pad, GstEvent * event); -static gboolean vorbis_dec_src_query (GstPad * pad, GstQuery * query); -static gboolean vorbis_dec_convert (GstPad * pad, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value); - -static gboolean vorbis_dec_sink_query (GstPad * pad, GstQuery * query); - -static void -gst_vorbis_dec_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - GstPadTemplate *src_template, *sink_template; - - src_template = gst_static_pad_template_get (&vorbis_dec_src_factory); - gst_element_class_add_pad_template (element_class, src_template); - - sink_template = gst_static_pad_template_get (&vorbis_dec_sink_factory); - gst_element_class_add_pad_template (element_class, sink_template); - - gst_element_class_set_details (element_class, &vorbis_dec_details); -} - -static void -gst_vorbis_dec_class_init (GstVorbisDecClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - - gobject_class->finalize = vorbis_dec_finalize; - - gstelement_class->change_state = GST_DEBUG_FUNCPTR (vorbis_dec_change_state); -} - -static const GstQueryType * -vorbis_get_query_types (GstPad * pad) -{ - static const GstQueryType vorbis_dec_src_query_types[] = { - GST_QUERY_POSITION, - GST_QUERY_DURATION, - GST_QUERY_CONVERT, - 0 - }; - - return vorbis_dec_src_query_types; -} - -static void -gst_vorbis_dec_init (GstVorbisDec * dec, GstVorbisDecClass * g_class) -{ - dec->sinkpad = gst_pad_new_from_static_template (&vorbis_dec_sink_factory, - "sink"); - - gst_pad_set_event_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (vorbis_dec_sink_event)); - gst_pad_set_chain_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (vorbis_dec_chain)); - gst_pad_set_query_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (vorbis_dec_sink_query)); - gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad); - - dec->srcpad = gst_pad_new_from_static_template (&vorbis_dec_src_factory, - "src"); - - gst_pad_set_event_function (dec->srcpad, - GST_DEBUG_FUNCPTR (vorbis_dec_src_event)); - gst_pad_set_query_type_function (dec->srcpad, - GST_DEBUG_FUNCPTR (vorbis_get_query_types)); - gst_pad_set_query_function (dec->srcpad, - GST_DEBUG_FUNCPTR (vorbis_dec_src_query)); - gst_pad_use_fixed_caps (dec->srcpad); - gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); - - dec->queued = NULL; - dec->pendingevents = NULL; - dec->taglist = NULL; -} - -static void -vorbis_dec_finalize (GObject * object) -{ - /* Release any possibly allocated libvorbis data. - * _clear functions can safely be called multiple times - */ - GstVorbisDec *vd = GST_VORBIS_DEC (object); - - vorbis_block_clear (&vd->vb); - vorbis_dsp_clear (&vd->vd); - vorbis_comment_clear (&vd->vc); - vorbis_info_clear (&vd->vi); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_vorbis_dec_reset (GstVorbisDec * dec) -{ - dec->last_timestamp = GST_CLOCK_TIME_NONE; - dec->discont = TRUE; - dec->seqnum = gst_util_seqnum_next (); - gst_segment_init (&dec->segment, GST_FORMAT_TIME); - - g_list_foreach (dec->queued, (GFunc) gst_mini_object_unref, NULL); - g_list_free (dec->queued); - dec->queued = NULL; - g_list_foreach (dec->gather, (GFunc) gst_mini_object_unref, NULL); - g_list_free (dec->gather); - dec->gather = NULL; - g_list_foreach (dec->decode, (GFunc) gst_mini_object_unref, NULL); - g_list_free (dec->decode); - dec->decode = NULL; - g_list_foreach (dec->pendingevents, (GFunc) gst_mini_object_unref, NULL); - g_list_free (dec->pendingevents); - dec->pendingevents = NULL; - - if (dec->taglist) - gst_tag_list_free (dec->taglist); - dec->taglist = NULL; -} - - -static gboolean -vorbis_dec_convert (GstPad * pad, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = TRUE; - GstVorbisDec *dec; - guint64 scale = 1; - - if (src_format == *dest_format) { - *dest_value = src_value; - return TRUE; - } - - dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); - - if (!dec->initialized) - goto no_header; - - if (dec->sinkpad == pad && - (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) - goto no_format; - - switch (src_format) { - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_BYTES: - scale = dec->width * dec->vi.channels; - case GST_FORMAT_DEFAULT: - *dest_value = - scale * gst_util_uint64_scale_int (src_value, dec->vi.rate, - GST_SECOND); - break; - default: - res = FALSE; - } - break; - case GST_FORMAT_DEFAULT: - switch (*dest_format) { - case GST_FORMAT_BYTES: - *dest_value = src_value * dec->width * dec->vi.channels; - break; - case GST_FORMAT_TIME: - *dest_value = - gst_util_uint64_scale_int (src_value, GST_SECOND, dec->vi.rate); - break; - default: - res = FALSE; - } - break; - case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_DEFAULT: - *dest_value = src_value / (dec->width * dec->vi.channels); - break; - case GST_FORMAT_TIME: - *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND, - dec->vi.rate * dec->width * dec->vi.channels); - break; - default: - res = FALSE; - } - break; - default: - res = FALSE; - } -done: - gst_object_unref (dec); - - return res; - - /* ERRORS */ -no_header: - { - GST_DEBUG_OBJECT (dec, "no header packets received"); - res = FALSE; - goto done; - } -no_format: - { - GST_DEBUG_OBJECT (dec, "formats unsupported"); - res = FALSE; - goto done; - } -} - -static gboolean -vorbis_dec_src_query (GstPad * pad, GstQuery * query) -{ - GstVorbisDec *dec; - gboolean res = FALSE; - - dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - { - gint64 value; - GstFormat format; - gint64 time; - - gst_query_parse_position (query, &format, NULL); - - /* we start from the last seen time */ - time = dec->last_timestamp; - /* correct for the segment values */ - time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time); - - GST_LOG_OBJECT (dec, - "query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time)); - - /* and convert to the final format */ - if (!(res = - vorbis_dec_convert (pad, GST_FORMAT_TIME, time, &format, &value))) - goto error; - - gst_query_set_position (query, format, value); - - GST_LOG_OBJECT (dec, - "query %p: we return %" G_GINT64_FORMAT " (format %u)", query, value, - format); - - break; - } - case GST_QUERY_DURATION: - { - res = gst_pad_peer_query (dec->sinkpad, query); - if (!res) - goto error; - - break; - } - case GST_QUERY_CONVERT: - { - GstFormat src_fmt, dest_fmt; - gint64 src_val, dest_val; - - gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); - if (!(res = - vorbis_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val))) - goto error; - gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } -done: - gst_object_unref (dec); - - return res; - - /* ERRORS */ -error: - { - GST_WARNING_OBJECT (dec, "error handling query"); - goto done; - } -} - -static gboolean -vorbis_dec_sink_query (GstPad * pad, GstQuery * query) -{ - GstVorbisDec *dec; - gboolean res; - - dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_CONVERT: - { - GstFormat src_fmt, dest_fmt; - gint64 src_val, dest_val; - - gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); - if (!(res = - vorbis_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val))) - goto error; - gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } - -done: - gst_object_unref (dec); - - return res; - - /* ERRORS */ -error: - { - GST_DEBUG_OBJECT (dec, "error converting value"); - goto done; - } -} - -static gboolean -vorbis_dec_src_event (GstPad * pad, GstEvent * event) -{ - gboolean res = TRUE; - GstVorbisDec *dec; - - dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - { - GstFormat format, tformat; - gdouble rate; - GstEvent *real_seek; - GstSeekFlags flags; - GstSeekType cur_type, stop_type; - gint64 cur, stop; - gint64 tcur, tstop; - guint32 seqnum; - - gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, - &stop_type, &stop); - seqnum = gst_event_get_seqnum (event); - gst_event_unref (event); - - /* First bring the requested format to time */ - tformat = GST_FORMAT_TIME; - if (!(res = vorbis_dec_convert (pad, format, cur, &tformat, &tcur))) - goto convert_error; - if (!(res = vorbis_dec_convert (pad, format, stop, &tformat, &tstop))) - goto convert_error; - - /* then seek with time on the peer */ - real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME, - flags, cur_type, tcur, stop_type, tstop); - gst_event_set_seqnum (real_seek, seqnum); - - res = gst_pad_push_event (dec->sinkpad, real_seek); - break; - } - default: - res = gst_pad_push_event (dec->sinkpad, event); - break; - } -done: - gst_object_unref (dec); - - return res; - - /* ERRORS */ -convert_error: - { - GST_DEBUG_OBJECT (dec, "cannot convert start/stop for seek"); - goto done; - } -} - -static gboolean -vorbis_dec_sink_event (GstPad * pad, GstEvent * event) -{ - gboolean ret = FALSE; - GstVorbisDec *dec; - - dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); - - GST_LOG_OBJECT (dec, "handling event"); - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - ret = gst_pad_push_event (dec->srcpad, event); - break; - case GST_EVENT_FLUSH_START: - ret = gst_pad_push_event (dec->srcpad, event); - break; - case GST_EVENT_FLUSH_STOP: - /* here we must clean any state in the decoder */ -#ifdef HAVE_VORBIS_SYNTHESIS_RESTART - vorbis_synthesis_restart (&dec->vd); -#endif - gst_vorbis_dec_reset (dec); - ret = gst_pad_push_event (dec->srcpad, event); - break; - case GST_EVENT_NEWSEGMENT: - { - GstFormat format; - gdouble rate, arate; - gint64 start, stop, time; - gboolean update; - - gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, - &start, &stop, &time); - - /* we need time for now */ - if (format != GST_FORMAT_TIME) - goto newseg_wrong_format; - - GST_DEBUG_OBJECT (dec, - "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT - ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, - update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), - GST_TIME_ARGS (time)); - - /* now configure the values */ - gst_segment_set_newsegment_full (&dec->segment, update, - rate, arate, format, start, stop, time); - dec->seqnum = gst_event_get_seqnum (event); - - if (dec->initialized) - /* and forward */ - ret = gst_pad_push_event (dec->srcpad, event); - else { - /* store it to send once we're initialized */ - dec->pendingevents = g_list_append (dec->pendingevents, event); - ret = TRUE; - } - break; - } - case GST_EVENT_TAG: - { - if (dec->initialized) - /* and forward */ - ret = gst_pad_push_event (dec->srcpad, event); - else { - /* store it to send once we're initialized */ - dec->pendingevents = g_list_append (dec->pendingevents, event); - ret = TRUE; - } - break; - } - default: - ret = gst_pad_push_event (dec->srcpad, event); - break; - } -done: - gst_object_unref (dec); - - return ret; - - /* ERRORS */ -newseg_wrong_format: - { - GST_DEBUG_OBJECT (dec, "received non TIME newsegment"); - goto done; - } -} - -static GstFlowReturn -vorbis_handle_identification_packet (GstVorbisDec * vd) -{ - GstCaps *caps; - const GstAudioChannelPosition *pos = NULL; - gint width = GST_VORBIS_DEC_DEFAULT_SAMPLE_WIDTH; - - switch (vd->vi.channels) { - case 1: - case 2: - /* nothing */ - break; - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - pos = gst_vorbis_channel_positions[vd->vi.channels - 1]; - break; - default:{ - gint i; - GstAudioChannelPosition *posn = - g_new (GstAudioChannelPosition, vd->vi.channels); - - GST_ELEMENT_WARNING (GST_ELEMENT (vd), STREAM, DECODE, - (NULL), ("Using NONE channel layout for more than 8 channels")); - - for (i = 0; i < vd->vi.channels; i++) - posn[i] = GST_AUDIO_CHANNEL_POSITION_NONE; - - pos = posn; - } - } - - /* negotiate width with downstream */ - caps = gst_pad_get_allowed_caps (vd->srcpad); - if (caps) { - if (!gst_caps_is_empty (caps)) { - GstStructure *s; - - s = gst_caps_get_structure (caps, 0); - /* template ensures 16 or 32 */ - gst_structure_get_int (s, "width", &width); - } - gst_caps_unref (caps); - } - vd->width = width >> 3; - - caps = gst_caps_copy (gst_pad_get_pad_template_caps (vd->srcpad)); - gst_caps_set_simple (caps, "rate", G_TYPE_INT, vd->vi.rate, - "channels", G_TYPE_INT, vd->vi.channels, - "width", G_TYPE_INT, width, NULL); - - if (pos) { - gst_audio_set_channel_positions (gst_caps_get_structure (caps, 0), pos); - } - - if (vd->vi.channels > 8) { - g_free ((GstAudioChannelPosition *) pos); - } - - gst_pad_set_caps (vd->srcpad, caps); - gst_caps_unref (caps); - - return GST_FLOW_OK; -} - -static GstFlowReturn -vorbis_handle_comment_packet (GstVorbisDec * vd, ogg_packet * packet) -{ - guint bitrate = 0; - gchar *encoder = NULL; - GstTagList *list, *old_list; - GstBuffer *buf; - - GST_DEBUG_OBJECT (vd, "parsing comment packet"); - - buf = gst_buffer_new (); - GST_BUFFER_DATA (buf) = gst_ogg_packet_data (packet); - GST_BUFFER_SIZE (buf) = gst_ogg_packet_size (packet); - - list = - gst_tag_list_from_vorbiscomment_buffer (buf, (guint8 *) "\003vorbis", 7, - &encoder); - - old_list = vd->taglist; - vd->taglist = gst_tag_list_merge (vd->taglist, list, GST_TAG_MERGE_REPLACE); - - if (old_list) - gst_tag_list_free (old_list); - gst_tag_list_free (list); - gst_buffer_unref (buf); - - if (!vd->taglist) { - GST_ERROR_OBJECT (vd, "couldn't decode comments"); - vd->taglist = gst_tag_list_new (); - } - if (encoder) { - if (encoder[0]) - gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_ENCODER, encoder, NULL); - g_free (encoder); - } - gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_ENCODER_VERSION, vd->vi.version, - GST_TAG_AUDIO_CODEC, "Vorbis", NULL); - if (vd->vi.bitrate_nominal > 0 && vd->vi.bitrate_nominal <= 0x7FFFFFFF) { - gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_NOMINAL_BITRATE, (guint) vd->vi.bitrate_nominal, NULL); - bitrate = vd->vi.bitrate_nominal; - } - if (vd->vi.bitrate_upper > 0 && vd->vi.bitrate_upper <= 0x7FFFFFFF) { - gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_MAXIMUM_BITRATE, (guint) vd->vi.bitrate_upper, NULL); - if (!bitrate) - bitrate = vd->vi.bitrate_upper; - } - if (vd->vi.bitrate_lower > 0 && vd->vi.bitrate_lower <= 0x7FFFFFFF) { - gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_MINIMUM_BITRATE, (guint) vd->vi.bitrate_lower, NULL); - if (!bitrate) - bitrate = vd->vi.bitrate_lower; - } - if (bitrate) { - gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_BITRATE, (guint) bitrate, NULL); - } - - if (vd->initialized) { - gst_element_found_tags_for_pad (GST_ELEMENT_CAST (vd), vd->srcpad, - vd->taglist); - vd->taglist = NULL; - } else { - /* Only post them as messages for the time being. * - * They will be pushed on the pad once the decoder is initialized */ - gst_element_post_message (GST_ELEMENT_CAST (vd), - gst_message_new_tag (GST_OBJECT (vd), gst_tag_list_copy (vd->taglist))); - } - - return GST_FLOW_OK; -} - -static GstFlowReturn -vorbis_handle_type_packet (GstVorbisDec * vd) -{ - GList *walk; - gint res; - - g_assert (vd->initialized == FALSE); - - if (G_UNLIKELY ((res = vorbis_synthesis_init (&vd->vd, &vd->vi)))) - goto synthesis_init_error; - - if (G_UNLIKELY ((res = vorbis_block_init (&vd->vd, &vd->vb)))) - goto block_init_error; - - vd->initialized = TRUE; - - if (vd->pendingevents) { - for (walk = vd->pendingevents; walk; walk = g_list_next (walk)) - gst_pad_push_event (vd->srcpad, GST_EVENT_CAST (walk->data)); - g_list_free (vd->pendingevents); - vd->pendingevents = NULL; - } - - if (vd->taglist) { - /* The tags have already been sent on the bus as messages. */ - gst_pad_push_event (vd->srcpad, gst_event_new_tag (vd->taglist)); - vd->taglist = NULL; - } - return GST_FLOW_OK; - - /* ERRORS */ -synthesis_init_error: - { - GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE, - (NULL), ("couldn't initialize synthesis (%d)", res)); - return GST_FLOW_ERROR; - } -block_init_error: - { - GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE, - (NULL), ("couldn't initialize block (%d)", res)); - return GST_FLOW_ERROR; - } -} - -static GstFlowReturn -vorbis_handle_header_packet (GstVorbisDec * vd, ogg_packet * packet) -{ - GstFlowReturn res; - gint ret; - - GST_DEBUG_OBJECT (vd, "parsing header packet"); - - /* Packetno = 0 if the first byte is exactly 0x01 */ - packet->b_o_s = ((gst_ogg_packet_data (packet))[0] == 0x1) ? 1 : 0; - - if ((ret = vorbis_synthesis_headerin (&vd->vi, &vd->vc, packet))) - goto header_read_error; - - switch ((gst_ogg_packet_data (packet))[0]) { - case 0x01: - res = vorbis_handle_identification_packet (vd); - break; - case 0x03: - res = vorbis_handle_comment_packet (vd, packet); - break; - case 0x05: - res = vorbis_handle_type_packet (vd); - break; - default: - /* ignore */ - g_warning ("unknown vorbis header packet found"); - res = GST_FLOW_OK; - break; - } - return res; - - /* ERRORS */ -header_read_error: - { - GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE, - (NULL), ("couldn't read header packet (%d)", ret)); - return GST_FLOW_ERROR; - } -} - -static GstFlowReturn -vorbis_dec_push_forward (GstVorbisDec * dec, GstBuffer * buf) -{ - GstFlowReturn result; - - /* clip */ - if (!(buf = gst_audio_buffer_clip (buf, &dec->segment, dec->vi.rate, - dec->vi.channels * dec->width))) { - GST_LOG_OBJECT (dec, "clipped buffer"); - return GST_FLOW_OK; - } - - if (dec->discont) { - GST_LOG_OBJECT (dec, "setting DISCONT"); - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); - dec->discont = FALSE; - } - - GST_DEBUG_OBJECT (dec, - "pushing time %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), - GST_TIME_ARGS (GST_BUFFER_DURATION (buf))); - - result = gst_pad_push (dec->srcpad, buf); - - return result; -} - -static GstFlowReturn -vorbis_dec_push_reverse (GstVorbisDec * dec, GstBuffer * buf) -{ - GstFlowReturn result = GST_FLOW_OK; - - dec->queued = g_list_prepend (dec->queued, buf); - - return result; -} - -static void -vorbis_do_timestamps (GstVorbisDec * vd, GstBuffer * buf, gboolean reverse, - GstClockTime timestamp, GstClockTime duration) -{ - /* interpolate reverse */ - if (vd->last_timestamp != -1 && reverse) - vd->last_timestamp -= duration; - - /* take buffer timestamp, use interpolated timestamp otherwise */ - if (timestamp != -1) - vd->last_timestamp = timestamp; - else - timestamp = vd->last_timestamp; - - /* interpolate forwards */ - if (vd->last_timestamp != -1 && !reverse) - vd->last_timestamp += duration; - - GST_BUFFER_TIMESTAMP (buf) = timestamp; - GST_BUFFER_DURATION (buf) = duration; -} - -static GstFlowReturn -vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet, - GstClockTime timestamp, GstClockTime duration) -{ - vorbis_sample_t **pcm; - guint sample_count; - GstBuffer *out; - GstFlowReturn result; - gint size; - - if (G_UNLIKELY (!vd->initialized)) - goto not_initialized; - - /* normal data packet */ - /* FIXME, we can skip decoding if the packet is outside of the - * segment, this is however not very trivial as we need a previous - * packet to decode the current one so we must be carefull not to - * throw away too much. For now we decode everything and clip right - * before pushing data. */ - if (G_UNLIKELY (vorbis_synthesis (&vd->vb, packet))) - goto could_not_read; - - if (G_UNLIKELY (vorbis_synthesis_blockin (&vd->vd, &vd->vb) < 0)) - goto not_accepted; - - /* assume all goes well here */ - result = GST_FLOW_OK; - - /* count samples ready for reading */ - if ((sample_count = vorbis_synthesis_pcmout (&vd->vd, NULL)) == 0) - goto done; - - size = sample_count * vd->vi.channels * vd->width; - GST_LOG_OBJECT (vd, "%d samples ready for reading, size %d", sample_count, - size); - - /* alloc buffer for it */ - result = - gst_pad_alloc_buffer_and_set_caps (vd->srcpad, GST_BUFFER_OFFSET_NONE, - size, GST_PAD_CAPS (vd->srcpad), &out); - if (G_UNLIKELY (result != GST_FLOW_OK)) - goto done; - - /* get samples ready for reading now, should be sample_count */ - if (G_UNLIKELY ((vorbis_synthesis_pcmout (&vd->vd, &pcm)) != sample_count)) - goto wrong_samples; - - /* copy samples in buffer */ - copy_samples ((vorbis_sample_t *) GST_BUFFER_DATA (out), pcm, sample_count, - vd->vi.channels, vd->width); - - GST_LOG_OBJECT (vd, "setting output size to %d", size); - GST_BUFFER_SIZE (out) = size; - - /* this should not overflow */ - if (duration == -1) - duration = sample_count * GST_SECOND / vd->vi.rate; - - vorbis_do_timestamps (vd, out, FALSE, timestamp, duration); - - if (vd->segment.rate >= 0.0) - result = vorbis_dec_push_forward (vd, out); - else - result = vorbis_dec_push_reverse (vd, out); - -done: - vorbis_synthesis_read (&vd->vd, sample_count); - - return result; - - /* ERRORS */ -not_initialized: - { - GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE, - (NULL), ("no header sent yet")); - return GST_FLOW_ERROR; - } -could_not_read: - { - GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE, - (NULL), ("couldn't read data packet")); - return GST_FLOW_ERROR; - } -not_accepted: - { - GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE, - (NULL), ("vorbis decoder did not accept data packet")); - return GST_FLOW_ERROR; - } -wrong_samples: - { - gst_buffer_unref (out); - GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE, - (NULL), ("vorbis decoder reported wrong number of samples")); - return GST_FLOW_ERROR; - } -} - -static GstFlowReturn -vorbis_dec_decode_buffer (GstVorbisDec * vd, GstBuffer * buffer) -{ - ogg_packet *packet; - ogg_packet_wrapper packet_wrapper; - GstFlowReturn result = GST_FLOW_OK; - - /* make ogg_packet out of the buffer */ - gst_ogg_packet_wrapper_from_buffer (&packet_wrapper, buffer); - packet = gst_ogg_packet_from_wrapper (&packet_wrapper); - /* set some more stuff */ - packet->granulepos = -1; - packet->packetno = 0; /* we don't care */ - /* EOS does not matter, it is used in vorbis to implement clipping the last - * block of samples based on the granulepos. We clip based on segments. */ - packet->e_o_s = 0; - - GST_LOG_OBJECT (vd, "decode buffer of size %ld", packet->bytes); - - /* error out on empty header packets, but just skip empty data packets */ - if (G_UNLIKELY (packet->bytes == 0)) { - if (vd->initialized) - goto empty_buffer; - else - goto empty_header; - } - - /* switch depending on packet type */ - if ((gst_ogg_packet_data (packet))[0] & 1) { - if (vd->initialized) { - GST_WARNING_OBJECT (vd, "Already initialized, so ignoring header packet"); - goto done; - } - result = vorbis_handle_header_packet (vd, packet); - } else { - GstClockTime timestamp, duration; - - timestamp = GST_BUFFER_TIMESTAMP (buffer); - duration = GST_BUFFER_DURATION (buffer); - - result = vorbis_handle_data_packet (vd, packet, timestamp, duration); - } - -done: - return result; - -empty_buffer: - { - /* don't error out here, just ignore the buffer, it's invalid for vorbis - * but not fatal. */ - GST_WARNING_OBJECT (vd, "empty buffer received, ignoring"); - result = GST_FLOW_OK; - goto done; - } - -/* ERRORS */ -empty_header: - { - GST_ELEMENT_ERROR (vd, STREAM, DECODE, (NULL), ("empty header received")); - result = GST_FLOW_ERROR; - vd->discont = TRUE; - goto done; - } -} - -/* - * Input: - * Buffer decoding order: 7 8 9 4 5 6 3 1 2 EOS - * Discont flag: D D D D - * - * - Each Discont marks a discont in the decoding order. - * - * for vorbis, each buffer is a keyframe when we have the previous - * buffer. This means that to decode buffer 7, we need buffer 6, which - * arrives out of order. - * - * we first gather buffers in the gather queue until we get a DISCONT. We - * prepend each incomming buffer so that they are in reversed order. - * - * gather queue: 9 8 7 - * decode queue: - * output queue: - * - * When a DISCONT is received (buffer 4), we move the gather queue to the - * decode queue. This is simply done be taking the head of the gather queue - * and prepending it to the decode queue. This yields: - * - * gather queue: - * decode queue: 7 8 9 - * output queue: - * - * Then we decode each buffer in the decode queue in order and put the output - * buffer in the output queue. The first buffer (7) will not produce any output - * because it needs the previous buffer (6) which did not arrive yet. This - * yields: - * - * gather queue: - * decode queue: 7 8 9 - * output queue: 9 8 - * - * Then we remove the consumed buffers from the decode queue. Buffer 7 is not - * completely consumed, we need to keep it around for when we receive buffer - * 6. This yields: - * - * gather queue: - * decode queue: 7 - * output queue: 9 8 - * - * Then we accumulate more buffers: - * - * gather queue: 6 5 4 - * decode queue: 7 - * output queue: - * - * prepending to the decode queue on DISCONT yields: - * - * gather queue: - * decode queue: 4 5 6 7 - * output queue: - * - * after decoding and keeping buffer 4: - * - * gather queue: - * decode queue: 4 - * output queue: 7 6 5 - * - * Etc.. - */ -static GstFlowReturn -vorbis_dec_flush_decode (GstVorbisDec * dec) -{ - GstFlowReturn res = GST_FLOW_OK; - GList *walk; - - walk = dec->decode; - - GST_DEBUG_OBJECT (dec, "flushing buffers to decoder"); - - while (walk) { - GList *next; - GstBuffer *buf = GST_BUFFER_CAST (walk->data); - - GST_DEBUG_OBJECT (dec, "decoding buffer %p, ts %" GST_TIME_FORMAT, - buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); - - next = g_list_next (walk); - - /* decode buffer, prepend to output queue */ - res = vorbis_dec_decode_buffer (dec, buf); - - /* if we generated output, we can discard the buffer, else we - * keep it in the queue */ - if (dec->queued) { - GST_DEBUG_OBJECT (dec, "decoded buffer to %p", dec->queued->data); - dec->decode = g_list_delete_link (dec->decode, walk); - gst_buffer_unref (buf); - } else { - GST_DEBUG_OBJECT (dec, "buffer did not decode, keeping"); - } - walk = next; - } - while (dec->queued) { - GstBuffer *buf = GST_BUFFER_CAST (dec->queued->data); - GstClockTime timestamp, duration; - - timestamp = GST_BUFFER_TIMESTAMP (buf); - duration = GST_BUFFER_DURATION (buf); - - vorbis_do_timestamps (dec, buf, TRUE, timestamp, duration); - res = vorbis_dec_push_forward (dec, buf); - - dec->queued = g_list_delete_link (dec->queued, dec->queued); - } - return res; -} - -static GstFlowReturn -vorbis_dec_chain_reverse (GstVorbisDec * vd, gboolean discont, GstBuffer * buf) -{ - GstFlowReturn result = GST_FLOW_OK; - - /* if we have a discont, move buffers to the decode list */ - if (G_UNLIKELY (discont)) { - GST_DEBUG_OBJECT (vd, "received discont"); - while (vd->gather) { - GstBuffer *gbuf; - - gbuf = GST_BUFFER_CAST (vd->gather->data); - /* remove from the gather list */ - vd->gather = g_list_delete_link (vd->gather, vd->gather); - /* copy to decode queue */ - vd->decode = g_list_prepend (vd->decode, gbuf); - } - /* flush and decode the decode queue */ - result = vorbis_dec_flush_decode (vd); - } - - GST_DEBUG_OBJECT (vd, "gathering buffer %p of size %u, time %" GST_TIME_FORMAT - ", dur %" GST_TIME_FORMAT, buf, GST_BUFFER_SIZE (buf), - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), - GST_TIME_ARGS (GST_BUFFER_DURATION (buf))); - - /* add buffer to gather queue */ - vd->gather = g_list_prepend (vd->gather, buf); - - return result; -} - -static GstFlowReturn -vorbis_dec_chain_forward (GstVorbisDec * vd, gboolean discont, - GstBuffer * buffer) -{ - GstFlowReturn result; - - result = vorbis_dec_decode_buffer (vd, buffer); - - gst_buffer_unref (buffer); - - return result; -} - -static GstFlowReturn -vorbis_dec_chain (GstPad * pad, GstBuffer * buffer) -{ - GstVorbisDec *vd; - GstFlowReturn result = GST_FLOW_OK; - gboolean discont; - - vd = GST_VORBIS_DEC (gst_pad_get_parent (pad)); - - discont = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT); - - /* resync on DISCONT */ - if (G_UNLIKELY (discont)) { - GST_DEBUG_OBJECT (vd, "received DISCONT buffer"); - vd->last_timestamp = GST_CLOCK_TIME_NONE; -#ifdef HAVE_VORBIS_SYNTHESIS_RESTART - vorbis_synthesis_restart (&vd->vd); -#endif - vd->discont = TRUE; - } - - if (vd->segment.rate >= 0.0) - result = vorbis_dec_chain_forward (vd, discont, buffer); - else - result = vorbis_dec_chain_reverse (vd, discont, buffer); - - gst_object_unref (vd); - - return result; -} - -static GstStateChangeReturn -vorbis_dec_change_state (GstElement * element, GstStateChange transition) -{ - GstVorbisDec *vd = GST_VORBIS_DEC (element); - GstStateChangeReturn res; - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - vorbis_info_init (&vd->vi); - vorbis_comment_init (&vd->vc); - vd->initialized = FALSE; - gst_vorbis_dec_reset (vd); - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - default: - break; - } - - res = parent_class->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_DEBUG_OBJECT (vd, "PAUSED -> READY, clearing vorbis structures"); - vd->initialized = FALSE; - vorbis_block_clear (&vd->vb); - vorbis_dsp_clear (&vd->vd); - vorbis_comment_clear (&vd->vc); - vorbis_info_clear (&vd->vi); - gst_vorbis_dec_reset (vd); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - - return res; -} diff --git a/ext/vorbis/gstvorbisdec.h b/ext/vorbis/gstvorbisdec.h deleted file mode 100644 index 1c3ddad3..00000000 --- a/ext/vorbis/gstvorbisdec.h +++ /dev/null @@ -1,90 +0,0 @@ -/* -*- c-basic-offset: 2 -*- - * GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_VORBIS_DEC_H__ -#define __GST_VORBIS_DEC_H__ - - -#include <gst/gst.h> -#include "gstvorbisdeclib.h" - -G_BEGIN_DECLS - -#define GST_TYPE_VORBIS_DEC \ - (gst_vorbis_dec_get_type()) -#define GST_VORBIS_DEC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VORBIS_DEC,GstVorbisDec)) -#define GST_VORBIS_DEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VORBIS_DEC,GstVorbisDecClass)) -#define GST_IS_VORBIS_DEC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VORBIS_DEC)) -#define GST_IS_VORBIS_DEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VORBIS_DEC)) - -typedef struct _GstVorbisDec GstVorbisDec; -typedef struct _GstVorbisDecClass GstVorbisDecClass; - -/** - * GstVorbisDec: - * - * Opaque data structure. - */ -struct _GstVorbisDec { - GstElement element; - - GstPad *sinkpad; - GstPad *srcpad; - - vorbis_dsp_state vd; - vorbis_info vi; - vorbis_comment vc; - vorbis_block vb; - - gboolean initialized; - guint width; - - /* list of buffers that need timestamps */ - GList *queued; - /* list of raw output buffers */ - GList *output; - /* gather/decode queues for reverse playback */ - GList *gather; - GList *decode; - - GstSegment segment; - gboolean discont; - guint32 seqnum; - - GstClockTime last_timestamp; - - GList *pendingevents; - GstTagList *taglist; -}; - -struct _GstVorbisDecClass { - GstElementClass parent_class; -}; - -GType gst_vorbis_dec_get_type(void); - -G_END_DECLS - -#endif /* __GST_VORBIS_DEC_H__ */ diff --git a/ext/vorbis/gstvorbisdeclib.c b/ext/vorbis/gstvorbisdeclib.c deleted file mode 100644 index 9f3331c3..00000000 --- a/ext/vorbis/gstvorbisdeclib.c +++ /dev/null @@ -1,122 +0,0 @@ -/* GStreamer - * Copyright (C) 2010 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk> - * Copyright (C) 2010 Nokia Corporation. All rights reserved. - * Contact: Stefan Kost <stefan.kost@nokia.com> - * - * Tremor modifications <2006>: - * Chris Lord, OpenedHand Ltd. <chris@openedhand.com>, http://www.o-hand.com/ - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstvorbisdeclib.h" - -#ifndef TREMOR -/* These samples can be outside of the float -1.0 -- 1.0 range, this - * is allowed, downstream elements are supposed to clip */ -void -copy_samples (vorbis_sample_t * out, vorbis_sample_t ** in, guint samples, - gint channels, gint width) -{ - gint i, j; - - g_assert (width == 4); - -#ifdef GST_VORBIS_DEC_SEQUENTIAL - for (i = 0; i < channels; i++) { - memcpy (out, in[i], samples * sizeof (float)); - out += samples; - } -#else - for (j = 0; j < samples; j++) { - for (i = 0; i < channels; i++) { - *out++ = in[i][j]; - } - } -#endif -} - -#else - -/* Taken from Tremor, misc.h */ -#ifdef _ARM_ASSEM_ -static inline ogg_int32_t -CLIP_TO_15 (ogg_int32_t x) -{ - int tmp; - asm volatile ("subs %1, %0, #32768\n\t" - "movpl %0, #0x7f00\n\t" - "orrpl %0, %0, #0xff\n" - "adds %1, %0, #32768\n\t" - "movmi %0, #0x8000":"+r" (x), "=r" (tmp) - ::"cc"); - - return (x); -} -#else -static inline ogg_int32_t -CLIP_TO_15 (ogg_int32_t x) -{ - int ret = x; - - ret -= ((x <= 32767) - 1) & (x - 32767); - ret -= ((x >= -32768) - 1) & (x + 32768); - return (ret); -} -#endif - -static void -copy_samples_32 (gint32 * out, ogg_int32_t ** in, guint samples, gint channels) -{ - gint i, j; - - for (j = 0; j < samples; j++) { - for (i = 0; i < channels; i++) { - *out++ = CLIP_TO_15 (in[i][j] >> 9); - } - } -} - -static void -copy_samples_16 (gint16 * out, ogg_int32_t ** in, guint samples, gint channels) -{ - gint i, j; - - for (j = 0; j < samples; j++) { - for (i = 0; i < channels; i++) { - *out++ = CLIP_TO_15 (in[i][j] >> 9); - } - } -} - -void -copy_samples (vorbis_sample_t * out, vorbis_sample_t ** in, guint samples, - gint channels, gint width) -{ - if (width == 4) { - copy_samples_32 ((gint32 *) out, in, samples, channels); - } else if (width == 2) { - copy_samples_16 ((gint16 *) out, in, samples, channels); - } else { - g_assert_not_reached (); - } -} - -#endif diff --git a/ext/vorbis/gstvorbisdeclib.h b/ext/vorbis/gstvorbisdeclib.h deleted file mode 100644 index a438fa85..00000000 --- a/ext/vorbis/gstvorbisdeclib.h +++ /dev/null @@ -1,163 +0,0 @@ -/* GStreamer - * Copyright (C) 2010 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk> - * Copyright (C) 2010 Nokia Corporation. All rights reserved. - * Contact: Stefan Kost <stefan.kost@nokia.com> - * - * Tremor modifications <2006>: - * Chris Lord, OpenedHand Ltd. <chris@openedhand.com>, http://www.o-hand.com/ - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_VORBIS_DEC_LIB_H__ -#define __GST_VORBIS_DEC_LIB_H__ - -#include <gst/gst.h> - -#ifndef TREMOR - -#include <vorbis/codec.h> - -typedef float vorbis_sample_t; -typedef ogg_packet ogg_packet_wrapper; - -#define GST_VORBIS_DEC_ELEMENT_DETAILS \ -GST_ELEMENT_DETAILS ("Vorbis audio decoder", \ - "Codec/Decoder/Audio", \ - "decode raw vorbis streams to float audio", \ - "Benjamin Otte <in7y118@public.uni-hamburg.de>") - -#define GST_VORBIS_DEC_SRC_CAPS \ - GST_STATIC_CAPS ("audio/x-raw-float, " "rate = (int) [ 1, MAX ], " \ - "channels = (int) [ 1, 256 ], " "endianness = (int) BYTE_ORDER, " \ - "width = (int) 32") - -#define GST_VORBIS_DEC_DEFAULT_SAMPLE_WIDTH (32) - -#define GST_VORBIS_DEC_GLIB_TYPE_NAME GstVorbisDec - -static inline guint8 * -gst_ogg_packet_data (ogg_packet * p) -{ - return (guint8 *) p->packet; -} - -static inline gint -gst_ogg_packet_size (ogg_packet * p) -{ - return p->bytes; -} - -static inline void -gst_ogg_packet_wrapper_from_buffer (ogg_packet * packet, GstBuffer * buffer) -{ - packet->packet = GST_BUFFER_DATA (buffer); - packet->bytes = GST_BUFFER_SIZE (buffer); -} - -static inline ogg_packet * -gst_ogg_packet_from_wrapper (ogg_packet_wrapper * packet) -{ - return packet; -} - -#else - -#include <tremor/ivorbiscodec.h> - -typedef ogg_int32_t vorbis_sample_t; -typedef struct _ogg_packet_wrapper ogg_packet_wrapper; - -struct _ogg_packet_wrapper { - ogg_packet packet; - ogg_reference ref; - ogg_buffer buf; -}; - -#define GST_VORBIS_DEC_ELEMENT_DETAILS \ -GST_ELEMENT_DETAILS ("Vorbis audio decoder", \ - "Codec/Decoder/Audio", \ - "decode raw vorbis streams to integer audio", \ - "Benjamin Otte <in7y118@public.uni-hamburg.de>\n" \ - "Chris Lord <chris@openedhand.com>") - -#define GST_VORBIS_DEC_SRC_CAPS \ - GST_STATIC_CAPS ("audio/x-raw-int, " \ - "rate = (int) [ 1, MAX ], " \ - "channels = (int) [ 1, 6 ], " \ - "endianness = (int) BYTE_ORDER, " \ - "width = (int) { 16, 32 }, " \ - "depth = (int) 16, " "signed = (boolean) true") - -#define GST_VORBIS_DEC_DEFAULT_SAMPLE_WIDTH (16) - -/* we need a different type name here */ -#define GST_VORBIS_DEC_GLIB_TYPE_NAME GstIVorbisDec - -/* and still have it compile */ -typedef struct _GstVorbisDec GstIVorbisDec; -typedef struct _GstVorbisDecClass GstIVorbisDecClass; - -/* compensate minor variation */ -#define vorbis_synthesis(a, b) vorbis_synthesis (a, b, 1) - -static inline guint8 * -gst_ogg_packet_data (ogg_packet * p) -{ - return (guint8 *) p->packet->buffer->data; -} - -static inline gint -gst_ogg_packet_size (ogg_packet * p) -{ - return p->packet->buffer->size; -} - -static inline void -gst_ogg_packet_wrapper_from_buffer (ogg_packet_wrapper * packet, - GstBuffer * buffer) -{ - ogg_reference *ref = &packet->ref; - ogg_buffer *buf = &packet->buf; - - buf->data = GST_BUFFER_DATA (buffer); - buf->size = GST_BUFFER_SIZE (buffer); - buf->refcount = 1; - buf->ptr.owner = NULL; - buf->ptr.next = NULL; - - ref->buffer = buf; - ref->begin = 0; - ref->length = buf->size; - ref->next = NULL; - - packet->packet.packet = ref; - packet->packet.bytes = ref->length; -} - -static inline ogg_packet * -gst_ogg_packet_from_wrapper (ogg_packet_wrapper * packet) -{ - return &(packet->packet); -} - -#endif - -void copy_samples (vorbis_sample_t *out, vorbis_sample_t **in, - guint samples, gint channels, gint width); - - -#endif /* __GST_VORBIS_DEC_LIB_H__ */ diff --git a/ext/vorbis/gstvorbisenc.c b/ext/vorbis/gstvorbisenc.c deleted file mode 100644 index b09723b2..00000000 --- a/ext/vorbis/gstvorbisenc.c +++ /dev/null @@ -1,1432 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-vorbisenc - * @see_also: vorbisdec, oggmux - * - * This element encodes raw float audio into a Vorbis stream. - * <ulink url="http://www.vorbis.com/">Vorbis</ulink> is a royalty-free - * audio codec maintained by the <ulink url="http://www.xiph.org/">Xiph.org - * Foundation</ulink>. - * - * <refsect2> - * <title>Example pipelines</title> - * |[ - * gst-launch -v audiotestsrc wave=sine num-buffers=100 ! audioconvert ! vorbisenc ! oggmux ! filesink location=sine.ogg - * ]| Encode a test sine signal to Ogg/Vorbis. Note that the resulting file - * will be really small because a sine signal compresses very well. - * |[ - * gst-launch -v alsasrc ! audioconvert ! vorbisenc ! oggmux ! filesink location=alsasrc.ogg - * ]| Record from a sound card using ALSA and encode to Ogg/Vorbis. - * </refsect2> - * - * Last reviewed on 2006-03-01 (0.10.4) - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <vorbis/vorbisenc.h> - -#include <gst/gsttagsetter.h> -#include <gst/tag/tag.h> -#include <gst/audio/multichannel.h> -#include <gst/audio/audio.h> -#include "gstvorbisenc.h" - -#include "gstvorbiscommon.h" - -GST_DEBUG_CATEGORY_EXTERN (vorbisenc_debug); -#define GST_CAT_DEFAULT vorbisenc_debug - -static GstStaticPadTemplate vorbis_enc_sink_factory = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-float, " - "rate = (int) [ 1, 200000 ], " - "channels = (int) [ 1, 256 ], " "endianness = (int) BYTE_ORDER, " - "width = (int) 32") - ); - -static GstStaticPadTemplate vorbis_enc_src_factory = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-vorbis") - ); - - -/* elementfactory information */ -static const GstElementDetails vorbisenc_details = -GST_ELEMENT_DETAILS ("Vorbis audio encoder", - "Codec/Encoder/Audio", - "Encodes audio in Vorbis format", - "Monty <monty@xiph.org>, " "Wim Taymans <wim@fluendo.com>"); - -enum -{ - ARG_0, - ARG_MAX_BITRATE, - ARG_BITRATE, - ARG_MIN_BITRATE, - ARG_QUALITY, - ARG_MANAGED, - ARG_LAST_MESSAGE -}; - -static GstFlowReturn gst_vorbis_enc_output_buffers (GstVorbisEnc * vorbisenc); - -/* this function takes into account the granulepos_offset and the subgranule - * time offset */ -static GstClockTime -granulepos_to_timestamp_offset (GstVorbisEnc * vorbisenc, - ogg_int64_t granulepos) -{ - if (granulepos >= 0) - return gst_util_uint64_scale ((guint64) granulepos - + vorbisenc->granulepos_offset, GST_SECOND, vorbisenc->frequency) - + vorbisenc->subgranule_offset; - return GST_CLOCK_TIME_NONE; -} - -/* this function does a straight granulepos -> timestamp conversion */ -static GstClockTime -granulepos_to_timestamp (GstVorbisEnc * vorbisenc, ogg_int64_t granulepos) -{ - if (granulepos >= 0) - return gst_util_uint64_scale ((guint64) granulepos, - GST_SECOND, vorbisenc->frequency); - return GST_CLOCK_TIME_NONE; -} - -#define MAX_BITRATE_DEFAULT -1 -#define BITRATE_DEFAULT -1 -#define MIN_BITRATE_DEFAULT -1 -#define QUALITY_DEFAULT 0.3 -#define LOWEST_BITRATE 6000 /* lowest allowed for a 8 kHz stream */ -#define HIGHEST_BITRATE 250001 /* highest allowed for a 44 kHz stream */ - -static gboolean gst_vorbis_enc_sink_event (GstPad * pad, GstEvent * event); -static GstFlowReturn gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer); -static gboolean gst_vorbis_enc_setup (GstVorbisEnc * vorbisenc); - -static void gst_vorbis_enc_dispose (GObject * object); -static void gst_vorbis_enc_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static void gst_vorbis_enc_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static GstStateChangeReturn gst_vorbis_enc_change_state (GstElement * element, - GstStateChange transition); -static void gst_vorbis_enc_add_interfaces (GType vorbisenc_type); - -GST_BOILERPLATE_FULL (GstVorbisEnc, gst_vorbis_enc, GstElement, - GST_TYPE_ELEMENT, gst_vorbis_enc_add_interfaces); - -static void -gst_vorbis_enc_add_interfaces (GType vorbisenc_type) -{ - static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL }; - static const GInterfaceInfo preset_info = { NULL, NULL, NULL }; - - g_type_add_interface_static (vorbisenc_type, GST_TYPE_TAG_SETTER, - &tag_setter_info); - g_type_add_interface_static (vorbisenc_type, GST_TYPE_PRESET, &preset_info); -} - -static void -gst_vorbis_enc_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - GstPadTemplate *src_template, *sink_template; - - - src_template = gst_static_pad_template_get (&vorbis_enc_src_factory); - gst_element_class_add_pad_template (element_class, src_template); - - sink_template = gst_static_pad_template_get (&vorbis_enc_sink_factory); - gst_element_class_add_pad_template (element_class, sink_template); - gst_element_class_set_details (element_class, &vorbisenc_details); -} - -static void -gst_vorbis_enc_class_init (GstVorbisEncClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - - gobject_class->set_property = gst_vorbis_enc_set_property; - gobject_class->get_property = gst_vorbis_enc_get_property; - gobject_class->dispose = gst_vorbis_enc_dispose; - - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MAX_BITRATE, - g_param_spec_int ("max-bitrate", "Maximum Bitrate", - "Specify a maximum bitrate (in bps). Useful for streaming " - "applications. (-1 == disabled)", - -1, HIGHEST_BITRATE, MAX_BITRATE_DEFAULT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BITRATE, - g_param_spec_int ("bitrate", "Target Bitrate", - "Attempt to encode at a bitrate averaging this (in bps). " - "This uses the bitrate management engine, and is not recommended for most users. " - "Quality is a better alternative. (-1 == disabled)", -1, - HIGHEST_BITRATE, BITRATE_DEFAULT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MIN_BITRATE, - g_param_spec_int ("min-bitrate", "Minimum Bitrate", - "Specify a minimum bitrate (in bps). Useful for encoding for a " - "fixed-size channel. (-1 == disabled)", -1, HIGHEST_BITRATE, - MIN_BITRATE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_QUALITY, - g_param_spec_float ("quality", "Quality", - "Specify quality instead of specifying a particular bitrate.", -0.1, - 1.0, QUALITY_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MANAGED, - g_param_spec_boolean ("managed", "Managed", - "Enable bitrate management engine", FALSE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE, - g_param_spec_string ("last-message", "last-message", - "The last status message", NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_vorbis_enc_change_state); -} - -static void -gst_vorbis_enc_dispose (GObject * object) -{ - GstVorbisEnc *vorbisenc = GST_VORBISENC (object); - - if (vorbisenc->sinkcaps) { - gst_caps_unref (vorbisenc->sinkcaps); - vorbisenc->sinkcaps = NULL; - } - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static GstCaps * -gst_vorbis_enc_generate_sink_caps (void) -{ - GstCaps *caps = gst_caps_new_empty (); - int i, c; - - gst_caps_append_structure (caps, gst_structure_new ("audio/x-raw-float", - "rate", GST_TYPE_INT_RANGE, 1, 200000, - "channels", G_TYPE_INT, 1, - "endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32, - NULL)); - - gst_caps_append_structure (caps, gst_structure_new ("audio/x-raw-float", - "rate", GST_TYPE_INT_RANGE, 1, 200000, - "channels", G_TYPE_INT, 2, - "endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32, - NULL)); - - for (i = 3; i <= 8; i++) { - GValue chanpos = { 0 }; - GValue pos = { 0 }; - GstStructure *structure; - - g_value_init (&chanpos, GST_TYPE_ARRAY); - g_value_init (&pos, GST_TYPE_AUDIO_CHANNEL_POSITION); - - for (c = 0; c < i; c++) { - g_value_set_enum (&pos, gst_vorbis_channel_positions[i - 1][c]); - gst_value_array_append_value (&chanpos, &pos); - } - g_value_unset (&pos); - - structure = gst_structure_new ("audio/x-raw-float", - "rate", GST_TYPE_INT_RANGE, 1, 200000, - "channels", G_TYPE_INT, i, - "endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32, NULL); - gst_structure_set_value (structure, "channel-positions", &chanpos); - g_value_unset (&chanpos); - - gst_caps_append_structure (caps, structure); - } - - gst_caps_append_structure (caps, gst_structure_new ("audio/x-raw-float", - "rate", GST_TYPE_INT_RANGE, 1, 200000, - "channels", GST_TYPE_INT_RANGE, 9, 256, - "endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32, - NULL)); - - return caps; -} - -static GstCaps * -gst_vorbis_enc_sink_getcaps (GstPad * pad) -{ - GstVorbisEnc *vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad)); - - if (vorbisenc->sinkcaps == NULL) - vorbisenc->sinkcaps = gst_vorbis_enc_generate_sink_caps (); - - return gst_caps_ref (vorbisenc->sinkcaps); -} - -static gboolean -gst_vorbis_enc_sink_setcaps (GstPad * pad, GstCaps * caps) -{ - GstVorbisEnc *vorbisenc; - GstStructure *structure; - - vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad)); - vorbisenc->setup = FALSE; - - structure = gst_caps_get_structure (caps, 0); - gst_structure_get_int (structure, "channels", &vorbisenc->channels); - gst_structure_get_int (structure, "rate", &vorbisenc->frequency); - - gst_vorbis_enc_setup (vorbisenc); - - if (vorbisenc->setup) - return TRUE; - - return FALSE; -} - -static gboolean -gst_vorbis_enc_convert_src (GstPad * pad, GstFormat src_format, - gint64 src_value, GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = TRUE; - GstVorbisEnc *vorbisenc; - gint64 avg; - - vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad)); - - if (vorbisenc->samples_in == 0 || - vorbisenc->bytes_out == 0 || vorbisenc->frequency == 0) { - gst_object_unref (vorbisenc); - return FALSE; - } - - avg = (vorbisenc->bytes_out * vorbisenc->frequency) / (vorbisenc->samples_in); - - switch (src_format) { - case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_TIME: - *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND, avg); - break; - default: - res = FALSE; - } - break; - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_BYTES: - *dest_value = gst_util_uint64_scale_int (src_value, avg, GST_SECOND); - break; - default: - res = FALSE; - } - break; - default: - res = FALSE; - } - gst_object_unref (vorbisenc); - return res; -} - -static gboolean -gst_vorbis_enc_convert_sink (GstPad * pad, GstFormat src_format, - gint64 src_value, GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = TRUE; - guint scale = 1; - gint bytes_per_sample; - GstVorbisEnc *vorbisenc; - - vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad)); - - bytes_per_sample = vorbisenc->channels * 2; - - switch (src_format) { - case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_DEFAULT: - if (bytes_per_sample == 0) - return FALSE; - *dest_value = src_value / bytes_per_sample; - break; - case GST_FORMAT_TIME: - { - gint byterate = bytes_per_sample * vorbisenc->frequency; - - if (byterate == 0) - return FALSE; - *dest_value = - gst_util_uint64_scale_int (src_value, GST_SECOND, byterate); - break; - } - default: - res = FALSE; - } - break; - case GST_FORMAT_DEFAULT: - switch (*dest_format) { - case GST_FORMAT_BYTES: - *dest_value = src_value * bytes_per_sample; - break; - case GST_FORMAT_TIME: - if (vorbisenc->frequency == 0) - return FALSE; - *dest_value = - gst_util_uint64_scale_int (src_value, GST_SECOND, - vorbisenc->frequency); - break; - default: - res = FALSE; - } - break; - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_BYTES: - scale = bytes_per_sample; - /* fallthrough */ - case GST_FORMAT_DEFAULT: - *dest_value = - gst_util_uint64_scale_int (src_value, - scale * vorbisenc->frequency, GST_SECOND); - break; - default: - res = FALSE; - } - break; - default: - res = FALSE; - } - gst_object_unref (vorbisenc); - return res; -} - -static gint64 -gst_vorbis_enc_get_latency (GstVorbisEnc * vorbisenc) -{ - /* FIXME, this probably depends on the bitrate and other setting but for now - * we return this value, which was obtained by totally unscientific - * measurements */ - return 58 * GST_MSECOND; -} - -static const GstQueryType * -gst_vorbis_enc_get_query_types (GstPad * pad) -{ - static const GstQueryType gst_vorbis_enc_src_query_types[] = { - GST_QUERY_POSITION, - GST_QUERY_DURATION, - GST_QUERY_CONVERT, - 0 - }; - - return gst_vorbis_enc_src_query_types; -} - -static gboolean -gst_vorbis_enc_src_query (GstPad * pad, GstQuery * query) -{ - gboolean res = TRUE; - GstVorbisEnc *vorbisenc; - GstPad *peerpad; - - vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad)); - peerpad = gst_pad_get_peer (GST_PAD (vorbisenc->sinkpad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - { - GstFormat fmt, req_fmt; - gint64 pos, val; - - gst_query_parse_position (query, &req_fmt, NULL); - if ((res = gst_pad_query_position (peerpad, &req_fmt, &val))) { - gst_query_set_position (query, req_fmt, val); - break; - } - - fmt = GST_FORMAT_TIME; - if (!(res = gst_pad_query_position (peerpad, &fmt, &pos))) - break; - - if ((res = gst_pad_query_convert (peerpad, fmt, pos, &req_fmt, &val))) { - gst_query_set_position (query, req_fmt, val); - } - break; - } - case GST_QUERY_DURATION: - { - GstFormat fmt, req_fmt; - gint64 dur, val; - - gst_query_parse_duration (query, &req_fmt, NULL); - if ((res = gst_pad_query_duration (peerpad, &req_fmt, &val))) { - gst_query_set_duration (query, req_fmt, val); - break; - } - - fmt = GST_FORMAT_TIME; - if (!(res = gst_pad_query_duration (peerpad, &fmt, &dur))) - break; - - if ((res = gst_pad_query_convert (peerpad, fmt, dur, &req_fmt, &val))) { - gst_query_set_duration (query, req_fmt, val); - } - break; - } - case GST_QUERY_CONVERT: - { - GstFormat src_fmt, dest_fmt; - gint64 src_val, dest_val; - - gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); - if (!(res = - gst_vorbis_enc_convert_src (pad, src_fmt, src_val, &dest_fmt, - &dest_val))) - goto error; - gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); - break; - } - case GST_QUERY_LATENCY: - { - gboolean live; - GstClockTime min_latency, max_latency; - gint64 latency; - - if ((res = gst_pad_query (peerpad, query))) { - gst_query_parse_latency (query, &live, &min_latency, &max_latency); - - latency = gst_vorbis_enc_get_latency (vorbisenc); - - /* add our latency */ - min_latency += latency; - if (max_latency != -1) - max_latency += latency; - - gst_query_set_latency (query, live, min_latency, max_latency); - } - break; - } - default: - res = gst_pad_query (peerpad, query); - break; - } - -error: - gst_object_unref (peerpad); - gst_object_unref (vorbisenc); - return res; -} - -static gboolean -gst_vorbis_enc_sink_query (GstPad * pad, GstQuery * query) -{ - gboolean res = TRUE; - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_CONVERT: - { - GstFormat src_fmt, dest_fmt; - gint64 src_val, dest_val; - - gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); - if (!(res = - gst_vorbis_enc_convert_sink (pad, src_fmt, src_val, &dest_fmt, - &dest_val))) - goto error; - gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } - -error: - return res; -} - -static void -gst_vorbis_enc_init (GstVorbisEnc * vorbisenc, GstVorbisEncClass * klass) -{ - vorbisenc->sinkpad = - gst_pad_new_from_static_template (&vorbis_enc_sink_factory, "sink"); - gst_pad_set_event_function (vorbisenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_event)); - gst_pad_set_chain_function (vorbisenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_vorbis_enc_chain)); - gst_pad_set_setcaps_function (vorbisenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_setcaps)); - gst_pad_set_getcaps_function (vorbisenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_getcaps)); - gst_pad_set_query_function (vorbisenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_query)); - gst_element_add_pad (GST_ELEMENT (vorbisenc), vorbisenc->sinkpad); - - vorbisenc->srcpad = - gst_pad_new_from_static_template (&vorbis_enc_src_factory, "src"); - gst_pad_set_query_function (vorbisenc->srcpad, - GST_DEBUG_FUNCPTR (gst_vorbis_enc_src_query)); - gst_pad_set_query_type_function (vorbisenc->srcpad, - GST_DEBUG_FUNCPTR (gst_vorbis_enc_get_query_types)); - gst_element_add_pad (GST_ELEMENT (vorbisenc), vorbisenc->srcpad); - - vorbisenc->channels = -1; - vorbisenc->frequency = -1; - - vorbisenc->managed = FALSE; - vorbisenc->max_bitrate = MAX_BITRATE_DEFAULT; - vorbisenc->bitrate = BITRATE_DEFAULT; - vorbisenc->min_bitrate = MIN_BITRATE_DEFAULT; - vorbisenc->quality = QUALITY_DEFAULT; - vorbisenc->quality_set = FALSE; - vorbisenc->last_message = NULL; -} - -static void -gst_vorbis_enc_metadata_set1 (const GstTagList * list, const gchar * tag, - gpointer vorbisenc) -{ - GstVorbisEnc *enc = GST_VORBISENC (vorbisenc); - GList *vc_list, *l; - - vc_list = gst_tag_to_vorbis_comments (list, tag); - - for (l = vc_list; l != NULL; l = l->next) { - const gchar *vc_string = (const gchar *) l->data; - gchar *key = NULL, *val = NULL; - - GST_LOG_OBJECT (vorbisenc, "vorbis comment: %s", vc_string); - if (gst_tag_parse_extended_comment (vc_string, &key, NULL, &val, TRUE)) { - vorbis_comment_add_tag (&enc->vc, key, val); - g_free (key); - g_free (val); - } - } - - g_list_foreach (vc_list, (GFunc) g_free, NULL); - g_list_free (vc_list); -} - -static void -gst_vorbis_enc_set_metadata (GstVorbisEnc * enc) -{ - GstTagList *merged_tags; - const GstTagList *user_tags; - - vorbis_comment_init (&enc->vc); - - user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc)); - - GST_DEBUG_OBJECT (enc, "upstream tags = %" GST_PTR_FORMAT, enc->tags); - GST_DEBUG_OBJECT (enc, "user-set tags = %" GST_PTR_FORMAT, user_tags); - - /* gst_tag_list_merge() will handle NULL for either or both lists fine */ - merged_tags = gst_tag_list_merge (user_tags, enc->tags, - gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (enc))); - - if (merged_tags) { - GST_DEBUG_OBJECT (enc, "merged tags = %" GST_PTR_FORMAT, merged_tags); - gst_tag_list_foreach (merged_tags, gst_vorbis_enc_metadata_set1, enc); - gst_tag_list_free (merged_tags); - } -} - -static gchar * -get_constraints_string (GstVorbisEnc * vorbisenc) -{ - gint min = vorbisenc->min_bitrate; - gint max = vorbisenc->max_bitrate; - gchar *result; - - if (min > 0 && max > 0) - result = g_strdup_printf ("(min %d bps, max %d bps)", min, max); - else if (min > 0) - result = g_strdup_printf ("(min %d bps, no max)", min); - else if (max > 0) - result = g_strdup_printf ("(no min, max %d bps)", max); - else - result = g_strdup_printf ("(no min or max)"); - - return result; -} - -static void -update_start_message (GstVorbisEnc * vorbisenc) -{ - gchar *constraints; - - g_free (vorbisenc->last_message); - - if (vorbisenc->bitrate > 0) { - if (vorbisenc->managed) { - constraints = get_constraints_string (vorbisenc); - vorbisenc->last_message = - g_strdup_printf ("encoding at average bitrate %d bps %s", - vorbisenc->bitrate, constraints); - g_free (constraints); - } else { - vorbisenc->last_message = - g_strdup_printf - ("encoding at approximate bitrate %d bps (VBR encoding enabled)", - vorbisenc->bitrate); - } - } else { - if (vorbisenc->quality_set) { - if (vorbisenc->managed) { - constraints = get_constraints_string (vorbisenc); - vorbisenc->last_message = - g_strdup_printf - ("encoding at quality level %2.2f using constrained VBR %s", - vorbisenc->quality, constraints); - g_free (constraints); - } else { - vorbisenc->last_message = - g_strdup_printf ("encoding at quality level %2.2f", - vorbisenc->quality); - } - } else { - constraints = get_constraints_string (vorbisenc); - vorbisenc->last_message = - g_strdup_printf ("encoding using bitrate management %s", constraints); - g_free (constraints); - } - } - - g_object_notify (G_OBJECT (vorbisenc), "last_message"); -} - -static gboolean -gst_vorbis_enc_setup (GstVorbisEnc * vorbisenc) -{ - vorbisenc->setup = FALSE; - - if (vorbisenc->bitrate < 0 && vorbisenc->min_bitrate < 0 - && vorbisenc->max_bitrate < 0) { - vorbisenc->quality_set = TRUE; - } - - update_start_message (vorbisenc); - - /* choose an encoding mode */ - /* (mode 0: 44kHz stereo uncoupled, roughly 128kbps VBR) */ - vorbis_info_init (&vorbisenc->vi); - - if (vorbisenc->quality_set) { - if (vorbis_encode_setup_vbr (&vorbisenc->vi, - vorbisenc->channels, vorbisenc->frequency, - vorbisenc->quality) != 0) { - GST_ERROR_OBJECT (vorbisenc, - "vorbisenc: initialisation failed: invalid parameters for quality"); - vorbis_info_clear (&vorbisenc->vi); - return FALSE; - } - - /* do we have optional hard quality restrictions? */ - if (vorbisenc->max_bitrate > 0 || vorbisenc->min_bitrate > 0) { - struct ovectl_ratemanage_arg ai; - - vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_GET, &ai); - - ai.bitrate_hard_min = vorbisenc->min_bitrate; - ai.bitrate_hard_max = vorbisenc->max_bitrate; - ai.management_active = 1; - - vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_SET, &ai); - } - } else { - long min_bitrate, max_bitrate; - - min_bitrate = vorbisenc->min_bitrate > 0 ? vorbisenc->min_bitrate : -1; - max_bitrate = vorbisenc->max_bitrate > 0 ? vorbisenc->max_bitrate : -1; - - if (vorbis_encode_setup_managed (&vorbisenc->vi, - vorbisenc->channels, - vorbisenc->frequency, - max_bitrate, vorbisenc->bitrate, min_bitrate) != 0) { - GST_ERROR_OBJECT (vorbisenc, - "vorbis_encode_setup_managed " - "(c %d, rate %d, max br %ld, br %d, min br %ld) failed", - vorbisenc->channels, vorbisenc->frequency, max_bitrate, - vorbisenc->bitrate, min_bitrate); - vorbis_info_clear (&vorbisenc->vi); - return FALSE; - } - } - - if (vorbisenc->managed && vorbisenc->bitrate < 0) { - vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_AVG, NULL); - } else if (!vorbisenc->managed) { - /* Turn off management entirely (if it was turned on). */ - vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_SET, NULL); - } - vorbis_encode_setup_init (&vorbisenc->vi); - - /* set up the analysis state and auxiliary encoding storage */ - vorbis_analysis_init (&vorbisenc->vd, &vorbisenc->vi); - vorbis_block_init (&vorbisenc->vd, &vorbisenc->vb); - - vorbisenc->next_ts = 0; - - vorbisenc->setup = TRUE; - - return TRUE; -} - -static GstFlowReturn -gst_vorbis_enc_clear (GstVorbisEnc * vorbisenc) -{ - GstFlowReturn ret = GST_FLOW_OK; - - if (vorbisenc->setup) { - vorbis_analysis_wrote (&vorbisenc->vd, 0); - ret = gst_vorbis_enc_output_buffers (vorbisenc); - - vorbisenc->setup = FALSE; - } - - /* clean up and exit. vorbis_info_clear() must be called last */ - vorbis_block_clear (&vorbisenc->vb); - vorbis_dsp_clear (&vorbisenc->vd); - vorbis_info_clear (&vorbisenc->vi); - - vorbisenc->header_sent = FALSE; - - return ret; -} - -/* prepare a buffer for transmission by passing data through libvorbis */ -static GstBuffer * -gst_vorbis_enc_buffer_from_packet (GstVorbisEnc * vorbisenc, - ogg_packet * packet) -{ - GstBuffer *outbuf; - - outbuf = gst_buffer_new_and_alloc (packet->bytes); - memcpy (GST_BUFFER_DATA (outbuf), packet->packet, packet->bytes); - /* see ext/ogg/README; OFFSET_END takes "our" granulepos, OFFSET its - * time representation */ - GST_BUFFER_OFFSET_END (outbuf) = packet->granulepos + - vorbisenc->granulepos_offset; - GST_BUFFER_OFFSET (outbuf) = granulepos_to_timestamp (vorbisenc, - GST_BUFFER_OFFSET_END (outbuf)); - GST_BUFFER_TIMESTAMP (outbuf) = vorbisenc->next_ts; - - /* update the next timestamp, taking granulepos_offset and subgranule offset - * into account */ - vorbisenc->next_ts = - granulepos_to_timestamp_offset (vorbisenc, packet->granulepos) + - vorbisenc->initial_ts; - GST_BUFFER_DURATION (outbuf) = - vorbisenc->next_ts - GST_BUFFER_TIMESTAMP (outbuf); - - if (vorbisenc->next_discont) { - GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); - vorbisenc->next_discont = FALSE; - } - - gst_buffer_set_caps (outbuf, vorbisenc->srccaps); - - GST_LOG_OBJECT (vorbisenc, "encoded buffer of %d bytes", - GST_BUFFER_SIZE (outbuf)); - return outbuf; -} - -/* the same as above, but different logic for setting timestamp and granulepos - * */ -static GstBuffer * -gst_vorbis_enc_buffer_from_header_packet (GstVorbisEnc * vorbisenc, - ogg_packet * packet) -{ - GstBuffer *outbuf; - - outbuf = gst_buffer_new_and_alloc (packet->bytes); - memcpy (GST_BUFFER_DATA (outbuf), packet->packet, packet->bytes); - GST_BUFFER_OFFSET (outbuf) = vorbisenc->bytes_out; - GST_BUFFER_OFFSET_END (outbuf) = 0; - GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE; - GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE; - - gst_buffer_set_caps (outbuf, vorbisenc->srccaps); - - GST_DEBUG ("created header packet buffer, %d bytes", - GST_BUFFER_SIZE (outbuf)); - return outbuf; -} - -/* push out the buffer and do internal bookkeeping */ -static GstFlowReturn -gst_vorbis_enc_push_buffer (GstVorbisEnc * vorbisenc, GstBuffer * buffer) -{ - vorbisenc->bytes_out += GST_BUFFER_SIZE (buffer); - - GST_DEBUG_OBJECT (vorbisenc, - "Pushing buffer with GP %" G_GINT64_FORMAT ", ts %" GST_TIME_FORMAT, - GST_BUFFER_OFFSET_END (buffer), - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer))); - return gst_pad_push (vorbisenc->srcpad, buffer); -} - -static GstFlowReturn -gst_vorbis_enc_push_packet (GstVorbisEnc * vorbisenc, ogg_packet * packet) -{ - GstBuffer *outbuf; - - outbuf = gst_vorbis_enc_buffer_from_packet (vorbisenc, packet); - return gst_vorbis_enc_push_buffer (vorbisenc, outbuf); -} - -/* Set a copy of these buffers as 'streamheader' on the caps. - * We need a copy to avoid these buffers ending up with (indirect) refs on - * themselves - */ -static GstCaps * -gst_vorbis_enc_set_header_on_caps (GstCaps * caps, GstBuffer * buf1, - GstBuffer * buf2, GstBuffer * buf3) -{ - GstBuffer *buf; - GstStructure *structure; - GValue array = { 0 }; - GValue value = { 0 }; - - caps = gst_caps_make_writable (caps); - structure = gst_caps_get_structure (caps, 0); - - /* mark buffers */ - GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_IN_CAPS); - GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_FLAG_IN_CAPS); - GST_BUFFER_FLAG_SET (buf3, GST_BUFFER_FLAG_IN_CAPS); - - /* put buffers in a fixed list */ - g_value_init (&array, GST_TYPE_ARRAY); - g_value_init (&value, GST_TYPE_BUFFER); - buf = gst_buffer_copy (buf1); - gst_value_set_buffer (&value, buf); - gst_buffer_unref (buf); - gst_value_array_append_value (&array, &value); - g_value_unset (&value); - g_value_init (&value, GST_TYPE_BUFFER); - buf = gst_buffer_copy (buf2); - gst_value_set_buffer (&value, buf); - gst_buffer_unref (buf); - gst_value_array_append_value (&array, &value); - g_value_unset (&value); - g_value_init (&value, GST_TYPE_BUFFER); - buf = gst_buffer_copy (buf3); - gst_value_set_buffer (&value, buf); - gst_buffer_unref (buf); - gst_value_array_append_value (&array, &value); - gst_structure_set_value (structure, "streamheader", &array); - g_value_unset (&value); - g_value_unset (&array); - - return caps; -} - -static gboolean -gst_vorbis_enc_sink_event (GstPad * pad, GstEvent * event) -{ - gboolean res = TRUE; - GstVorbisEnc *vorbisenc; - - vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - /* Tell the library we're at end of stream so that it can handle - * the last frame and mark end of stream in the output properly */ - GST_DEBUG_OBJECT (vorbisenc, "EOS, clearing state and sending event on"); - gst_vorbis_enc_clear (vorbisenc); - - res = gst_pad_push_event (vorbisenc->srcpad, event); - break; - case GST_EVENT_TAG: - if (vorbisenc->tags) { - GstTagList *list; - - gst_event_parse_tag (event, &list); - gst_tag_list_insert (vorbisenc->tags, list, - gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (vorbisenc))); - } else { - g_assert_not_reached (); - } - res = gst_pad_push_event (vorbisenc->srcpad, event); - break; - case GST_EVENT_NEWSEGMENT: - { - gboolean update; - gdouble rate, applied_rate; - GstFormat format; - gint64 start, stop, position; - - gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, - &format, &start, &stop, &position); - if (format == GST_FORMAT_TIME) { - gst_segment_set_newsegment (&vorbisenc->segment, update, rate, format, - start, stop, position); - if (vorbisenc->initial_ts == GST_CLOCK_TIME_NONE) { - GST_DEBUG_OBJECT (vorbisenc, "Initial segment %" GST_SEGMENT_FORMAT, - &vorbisenc->segment); - vorbisenc->initial_ts = start; - } - } - } - /* fall through */ - default: - res = gst_pad_push_event (vorbisenc->srcpad, event); - break; - } - return res; -} - -static gboolean -gst_vorbis_enc_buffer_check_discontinuous (GstVorbisEnc * vorbisenc, - GstClockTime timestamp, GstClockTime duration) -{ - gboolean ret = FALSE; - - if (timestamp != GST_CLOCK_TIME_NONE && - vorbisenc->expected_ts != GST_CLOCK_TIME_NONE && - timestamp + duration != vorbisenc->expected_ts) { - /* It turns out that a lot of elements don't generate perfect streams due - * to rounding errors. So, we permit small errors (< 1/2 a sample) without - * causing a discont. - */ - int halfsample = GST_SECOND / vorbisenc->frequency / 2; - - if ((GstClockTimeDiff) (timestamp - vorbisenc->expected_ts) > halfsample) { - GST_DEBUG_OBJECT (vorbisenc, "Expected TS %" GST_TIME_FORMAT - ", buffer TS %" GST_TIME_FORMAT, - GST_TIME_ARGS (vorbisenc->expected_ts), GST_TIME_ARGS (timestamp)); - ret = TRUE; - } - } - - if (timestamp != GST_CLOCK_TIME_NONE && duration != GST_CLOCK_TIME_NONE) { - vorbisenc->expected_ts = timestamp + duration; - } else - vorbisenc->expected_ts = GST_CLOCK_TIME_NONE; - - return ret; -} - -static GstFlowReturn -gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer) -{ - GstVorbisEnc *vorbisenc; - GstFlowReturn ret = GST_FLOW_OK; - gfloat *data; - gulong size; - gulong i, j; - float **vorbis_buffer; - GstBuffer *buf1, *buf2, *buf3; - gboolean first = FALSE; - GstClockTime timestamp = GST_CLOCK_TIME_NONE; - GstClockTime running_time = GST_CLOCK_TIME_NONE; - - vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad)); - - if (!vorbisenc->setup) - goto not_setup; - - buffer = gst_audio_buffer_clip (buffer, &vorbisenc->segment, - vorbisenc->frequency, 4 * vorbisenc->channels); - if (buffer == NULL) { - GST_DEBUG_OBJECT (vorbisenc, "Dropping buffer, out of segment"); - return GST_FLOW_OK; - } - running_time = - gst_segment_to_running_time (&vorbisenc->segment, GST_FORMAT_TIME, - GST_BUFFER_TIMESTAMP (buffer)); - timestamp = running_time + vorbisenc->initial_ts; - GST_DEBUG_OBJECT (vorbisenc, "Initial ts is %" GST_TIME_FORMAT, - GST_TIME_ARGS (vorbisenc->initial_ts)); - if (!vorbisenc->header_sent) { - /* Vorbis streams begin with three headers; the initial header (with - most of the codec setup parameters) which is mandated by the Ogg - bitstream spec. The second header holds any comment fields. The - third header holds the bitstream codebook. We merely need to - make the headers, then pass them to libvorbis one at a time; - libvorbis handles the additional Ogg bitstream constraints */ - ogg_packet header; - ogg_packet header_comm; - ogg_packet header_code; - GstCaps *caps; - - /* first, make sure header buffers get timestamp == 0 */ - vorbisenc->next_ts = 0; - vorbisenc->granulepos_offset = 0; - vorbisenc->subgranule_offset = 0; - - GST_DEBUG_OBJECT (vorbisenc, "creating and sending header packets"); - gst_vorbis_enc_set_metadata (vorbisenc); - vorbis_analysis_headerout (&vorbisenc->vd, &vorbisenc->vc, &header, - &header_comm, &header_code); - vorbis_comment_clear (&vorbisenc->vc); - - /* create header buffers */ - buf1 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header); - buf2 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header_comm); - buf3 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header_code); - - /* mark and put on caps */ - vorbisenc->srccaps = gst_caps_new_simple ("audio/x-vorbis", NULL); - caps = vorbisenc->srccaps; - caps = gst_vorbis_enc_set_header_on_caps (caps, buf1, buf2, buf3); - - /* negotiate with these caps */ - GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps); - gst_pad_set_caps (vorbisenc->srcpad, caps); - - gst_buffer_set_caps (buf1, caps); - gst_buffer_set_caps (buf2, caps); - gst_buffer_set_caps (buf3, caps); - - /* push out buffers */ - /* push_buffer takes the reference even for failure */ - if ((ret = gst_vorbis_enc_push_buffer (vorbisenc, buf1)) != GST_FLOW_OK) - goto failed_header_push; - if ((ret = gst_vorbis_enc_push_buffer (vorbisenc, buf2)) != GST_FLOW_OK) { - buf2 = NULL; - goto failed_header_push; - } - if ((ret = gst_vorbis_enc_push_buffer (vorbisenc, buf3)) != GST_FLOW_OK) { - buf3 = NULL; - goto failed_header_push; - } - - /* now adjust starting granulepos accordingly if the buffer's timestamp is - nonzero */ - vorbisenc->next_ts = timestamp; - vorbisenc->expected_ts = timestamp; - vorbisenc->granulepos_offset = gst_util_uint64_scale - (running_time, vorbisenc->frequency, GST_SECOND); - vorbisenc->subgranule_offset = 0; - vorbisenc->subgranule_offset = - (vorbisenc->next_ts - vorbisenc->initial_ts) - - granulepos_to_timestamp_offset (vorbisenc, 0); - - vorbisenc->header_sent = TRUE; - first = TRUE; - } - - if (vorbisenc->expected_ts != GST_CLOCK_TIME_NONE && - timestamp < vorbisenc->expected_ts) { - guint64 diff = vorbisenc->expected_ts - timestamp; - guint64 diff_bytes; - - GST_WARNING_OBJECT (vorbisenc, "Buffer is older than previous " - "timestamp + duration (%" GST_TIME_FORMAT "< %" GST_TIME_FORMAT - "), cannot handle. Clipping buffer.", - GST_TIME_ARGS (timestamp), GST_TIME_ARGS (vorbisenc->expected_ts)); - - diff_bytes = - GST_CLOCK_TIME_TO_FRAMES (diff, - vorbisenc->frequency) * vorbisenc->channels * sizeof (gfloat); - if (diff_bytes >= GST_BUFFER_SIZE (buffer)) { - gst_buffer_unref (buffer); - return GST_FLOW_OK; - } - buffer = gst_buffer_make_metadata_writable (buffer); - GST_BUFFER_DATA (buffer) += diff_bytes; - GST_BUFFER_SIZE (buffer) -= diff_bytes; - - GST_BUFFER_TIMESTAMP (buffer) += diff; - if (GST_BUFFER_DURATION_IS_VALID (buffer)) - GST_BUFFER_DURATION (buffer) -= diff; - } - - if (gst_vorbis_enc_buffer_check_discontinuous (vorbisenc, timestamp, - GST_BUFFER_DURATION (buffer)) && !first) { - GST_WARNING_OBJECT (vorbisenc, - "Buffer is discontinuous, flushing encoder " - "and restarting (Discont from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT - ")", GST_TIME_ARGS (vorbisenc->next_ts), GST_TIME_ARGS (timestamp)); - /* Re-initialise encoder (there's unfortunately no API to flush it) */ - if ((ret = gst_vorbis_enc_clear (vorbisenc)) != GST_FLOW_OK) - return ret; - if (!gst_vorbis_enc_setup (vorbisenc)) - return GST_FLOW_ERROR; /* Should be impossible, we can only get here if - we successfully initialised earlier */ - - /* Now, set our granulepos offset appropriately. */ - vorbisenc->next_ts = timestamp; - /* We need to round to the nearest whole number of samples, not just do - * a truncating division here */ - vorbisenc->granulepos_offset = gst_util_uint64_scale - (running_time + GST_SECOND / vorbisenc->frequency / 2 - - vorbisenc->subgranule_offset, vorbisenc->frequency, GST_SECOND); - - vorbisenc->header_sent = TRUE; - - /* And our next output buffer must have DISCONT set on it */ - vorbisenc->next_discont = TRUE; - } - - /* Sending zero samples to libvorbis marks EOS, so we mustn't do that */ - if (GST_BUFFER_SIZE (buffer) == 0) { - gst_buffer_unref (buffer); - return GST_FLOW_OK; - } - - /* data to encode */ - data = (gfloat *) GST_BUFFER_DATA (buffer); - size = GST_BUFFER_SIZE (buffer) / (vorbisenc->channels * sizeof (float)); - - /* expose the buffer to submit data */ - vorbis_buffer = vorbis_analysis_buffer (&vorbisenc->vd, size); - - /* deinterleave samples, write the buffer data */ - for (i = 0; i < size; i++) { - for (j = 0; j < vorbisenc->channels; j++) { - vorbis_buffer[j][i] = *data++; - } - } - - /* tell the library how much we actually submitted */ - vorbis_analysis_wrote (&vorbisenc->vd, size); - - GST_LOG_OBJECT (vorbisenc, "wrote %lu samples to vorbis", size); - - vorbisenc->samples_in += size; - - gst_buffer_unref (buffer); - - ret = gst_vorbis_enc_output_buffers (vorbisenc); - - return ret; - - /* error cases */ -not_setup: - { - gst_buffer_unref (buffer); - GST_ELEMENT_ERROR (vorbisenc, CORE, NEGOTIATION, (NULL), - ("encoder not initialized (input is not audio?)")); - return GST_FLOW_UNEXPECTED; - } -failed_header_push: - { - GST_WARNING_OBJECT (vorbisenc, "Failed to push headers"); - /* buf1 is always already unreffed */ - if (buf2) - gst_buffer_unref (buf2); - if (buf3) - gst_buffer_unref (buf3); - gst_buffer_unref (buffer); - return ret; - } -} - -static GstFlowReturn -gst_vorbis_enc_output_buffers (GstVorbisEnc * vorbisenc) -{ - GstFlowReturn ret; - - /* vorbis does some data preanalysis, then divides up blocks for - more involved (potentially parallel) processing. Get a single - block for encoding now */ - while (vorbis_analysis_blockout (&vorbisenc->vd, &vorbisenc->vb) == 1) { - ogg_packet op; - - GST_LOG_OBJECT (vorbisenc, "analysed to a block"); - - /* analysis */ - vorbis_analysis (&vorbisenc->vb, NULL); - vorbis_bitrate_addblock (&vorbisenc->vb); - - while (vorbis_bitrate_flushpacket (&vorbisenc->vd, &op)) { - GST_LOG_OBJECT (vorbisenc, "pushing out a data packet"); - ret = gst_vorbis_enc_push_packet (vorbisenc, &op); - - if (ret != GST_FLOW_OK) - return ret; - } - } - - return GST_FLOW_OK; -} - -static void -gst_vorbis_enc_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstVorbisEnc *vorbisenc; - - g_return_if_fail (GST_IS_VORBISENC (object)); - - vorbisenc = GST_VORBISENC (object); - - switch (prop_id) { - case ARG_MAX_BITRATE: - g_value_set_int (value, vorbisenc->max_bitrate); - break; - case ARG_BITRATE: - g_value_set_int (value, vorbisenc->bitrate); - break; - case ARG_MIN_BITRATE: - g_value_set_int (value, vorbisenc->min_bitrate); - break; - case ARG_QUALITY: - g_value_set_float (value, vorbisenc->quality); - break; - case ARG_MANAGED: - g_value_set_boolean (value, vorbisenc->managed); - break; - case ARG_LAST_MESSAGE: - g_value_set_string (value, vorbisenc->last_message); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_vorbis_enc_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstVorbisEnc *vorbisenc; - - g_return_if_fail (GST_IS_VORBISENC (object)); - - vorbisenc = GST_VORBISENC (object); - - switch (prop_id) { - case ARG_MAX_BITRATE: - { - gboolean old_value = vorbisenc->managed; - - vorbisenc->max_bitrate = g_value_get_int (value); - if (vorbisenc->max_bitrate >= 0 - && vorbisenc->max_bitrate < LOWEST_BITRATE) { - g_warning ("Lowest allowed bitrate is %d", LOWEST_BITRATE); - vorbisenc->max_bitrate = LOWEST_BITRATE; - } - if (vorbisenc->min_bitrate > 0 && vorbisenc->max_bitrate > 0) - vorbisenc->managed = TRUE; - else - vorbisenc->managed = FALSE; - - if (old_value != vorbisenc->managed) - g_object_notify (object, "managed"); - break; - } - case ARG_BITRATE: - vorbisenc->bitrate = g_value_get_int (value); - if (vorbisenc->bitrate >= 0 && vorbisenc->bitrate < LOWEST_BITRATE) { - g_warning ("Lowest allowed bitrate is %d", LOWEST_BITRATE); - vorbisenc->bitrate = LOWEST_BITRATE; - } - break; - case ARG_MIN_BITRATE: - { - gboolean old_value = vorbisenc->managed; - - vorbisenc->min_bitrate = g_value_get_int (value); - if (vorbisenc->min_bitrate >= 0 - && vorbisenc->min_bitrate < LOWEST_BITRATE) { - g_warning ("Lowest allowed bitrate is %d", LOWEST_BITRATE); - vorbisenc->min_bitrate = LOWEST_BITRATE; - } - if (vorbisenc->min_bitrate > 0 && vorbisenc->max_bitrate > 0) - vorbisenc->managed = TRUE; - else - vorbisenc->managed = FALSE; - - if (old_value != vorbisenc->managed) - g_object_notify (object, "managed"); - break; - } - case ARG_QUALITY: - vorbisenc->quality = g_value_get_float (value); - if (vorbisenc->quality >= 0.0) - vorbisenc->quality_set = TRUE; - else - vorbisenc->quality_set = FALSE; - break; - case ARG_MANAGED: - vorbisenc->managed = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static GstStateChangeReturn -gst_vorbis_enc_change_state (GstElement * element, GstStateChange transition) -{ - GstVorbisEnc *vorbisenc = GST_VORBISENC (element); - GstStateChangeReturn res; - - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - vorbisenc->tags = gst_tag_list_new (); - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - vorbisenc->setup = FALSE; - vorbisenc->next_discont = FALSE; - vorbisenc->header_sent = FALSE; - gst_segment_init (&vorbisenc->segment, GST_FORMAT_TIME); - vorbisenc->initial_ts = GST_CLOCK_TIME_NONE; - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - default: - break; - } - - res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - vorbis_block_clear (&vorbisenc->vb); - vorbis_dsp_clear (&vorbisenc->vd); - vorbis_info_clear (&vorbisenc->vi); - g_free (vorbisenc->last_message); - vorbisenc->last_message = NULL; - if (vorbisenc->srccaps) { - gst_caps_unref (vorbisenc->srccaps); - vorbisenc->srccaps = NULL; - } - break; - case GST_STATE_CHANGE_READY_TO_NULL: - gst_tag_list_free (vorbisenc->tags); - vorbisenc->tags = NULL; - default: - break; - } - - return res; -} diff --git a/ext/vorbis/gstvorbisenc.h b/ext/vorbis/gstvorbisenc.h deleted file mode 100644 index 9375a16c..00000000 --- a/ext/vorbis/gstvorbisenc.h +++ /dev/null @@ -1,101 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_VORBIS_ENC_H__ -#define __GST_VORBIS_ENC_H__ - - -#include <gst/gst.h> - -#include <vorbis/codec.h> - -G_BEGIN_DECLS - -#define GST_TYPE_VORBISENC \ - (gst_vorbis_enc_get_type()) -#define GST_VORBISENC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VORBISENC,GstVorbisEnc)) -#define GST_VORBISENC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VORBISENC,GstVorbisEncClass)) -#define GST_IS_VORBISENC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VORBISENC)) -#define GST_IS_VORBISENC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VORBISENC)) - -typedef struct _GstVorbisEnc GstVorbisEnc; -typedef struct _GstVorbisEncClass GstVorbisEncClass; - -/** - * GstVorbisEnc: - * - * Opaque data structure. - */ -struct _GstVorbisEnc { - GstElement element; - - GstPad *sinkpad; - GstPad *srcpad; - - GstCaps *srccaps; - GstCaps *sinkcaps; - - vorbis_info vi; /* struct that stores all the static vorbis bitstream - settings */ - vorbis_comment vc; /* struct that stores all the user comments */ - - vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ - vorbis_block vb; /* local working space for packet->PCM decode */ - - gboolean managed; - gint bitrate; - gint min_bitrate; - gint max_bitrate; - gfloat quality; - gboolean quality_set; - - gint channels; - gint frequency; - - guint64 samples_in; - guint64 bytes_out; - GstClockTime next_ts; - GstClockTime expected_ts; - gboolean next_discont; - guint64 granulepos_offset; - gint64 subgranule_offset; - GstSegment segment; - GstClockTime initial_ts; - - GstTagList * tags; - - gboolean setup; - gboolean header_sent; - gchar *last_message; -}; - -struct _GstVorbisEncClass { - GstElementClass parent_class; -}; - -GType gst_vorbis_enc_get_type(void); - -G_END_DECLS - -#endif /* __GST_VORBIS_ENC_H__ */ diff --git a/ext/vorbis/gstvorbisparse.c b/ext/vorbis/gstvorbisparse.c deleted file mode 100644 index 5e3c12d9..00000000 --- a/ext/vorbis/gstvorbisparse.c +++ /dev/null @@ -1,671 +0,0 @@ -/* GStreamer - * Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org> - * Copyright (C) 2006 Andy Wingo <wingo@pobox.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-vorbisparse - * @see_also: vorbisdec, oggdemux, theoraparse - * - * The vorbisparse element will parse the header packets of the Vorbis - * stream and put them as the streamheader in the caps. This is used in the - * multifdsink case where you want to stream live vorbis streams to multiple - * clients, each client has to receive the streamheaders first before they can - * consume the vorbis packets. - * - * This element also makes sure that the buffers that it pushes out are properly - * timestamped and that their offset and offset_end are set. The buffers that - * vorbisparse outputs have all of the metadata that oggmux expects to receive, - * which allows you to (for example) remux an ogg/vorbis file. - * - * <refsect2> - * <title>Example pipelines</title> - * |[ - * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisparse ! fakesink - * ]| This pipeline shows that the streamheader is set in the caps, and that each - * buffer has the timestamp, duration, offset, and offset_end set. - * |[ - * gst-launch filesrc location=sine.ogg ! oggdemux ! vorbisparse \ - * ! oggmux ! filesink location=sine-remuxed.ogg - * ]| This pipeline shows remuxing. sine-remuxed.ogg might not be exactly the same - * as sine.ogg, but they should produce exactly the same decoded data. - * </refsect2> - * - * Last reviewed on 2006-04-01 (0.10.4.1) - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "gstvorbisparse.h" - -GST_DEBUG_CATEGORY_EXTERN (vorbisparse_debug); -#define GST_CAT_DEFAULT vorbisparse_debug - -static const GstElementDetails vorbis_parse_details = { - "VorbisParse", - "Codec/Parser/Audio", - "parse raw vorbis streams", - "Thomas Vander Stichele <thomas at apestaart dot org>" -}; - -static GstStaticPadTemplate vorbis_parse_sink_factory = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-vorbis") - ); - -static GstStaticPadTemplate vorbis_parse_src_factory = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-vorbis") - ); - -GST_BOILERPLATE (GstVorbisParse, gst_vorbis_parse, GstElement, - GST_TYPE_ELEMENT); - -static GstFlowReturn vorbis_parse_chain (GstPad * pad, GstBuffer * buffer); -static GstStateChangeReturn vorbis_parse_change_state (GstElement * element, - GstStateChange transition); -static gboolean vorbis_parse_sink_event (GstPad * pad, GstEvent * event); -static gboolean vorbis_parse_src_query (GstPad * pad, GstQuery * query); -static gboolean vorbis_parse_convert (GstPad * pad, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value); -static GstFlowReturn vorbis_parse_parse_packet (GstVorbisParse * parse, - GstBuffer * buf); - -static void -gst_vorbis_parse_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&vorbis_parse_src_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&vorbis_parse_sink_factory)); - gst_element_class_set_details (element_class, &vorbis_parse_details); -} - -static void -gst_vorbis_parse_class_init (GstVorbisParseClass * klass) -{ - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - - gstelement_class->change_state = vorbis_parse_change_state; - - klass->parse_packet = GST_DEBUG_FUNCPTR (vorbis_parse_parse_packet); -} - -static void -gst_vorbis_parse_init (GstVorbisParse * parse, GstVorbisParseClass * g_class) -{ - parse->sinkpad = - gst_pad_new_from_static_template (&vorbis_parse_sink_factory, "sink"); - gst_pad_set_chain_function (parse->sinkpad, - GST_DEBUG_FUNCPTR (vorbis_parse_chain)); - gst_pad_set_event_function (parse->sinkpad, - GST_DEBUG_FUNCPTR (vorbis_parse_sink_event)); - gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad); - - parse->srcpad = - gst_pad_new_from_static_template (&vorbis_parse_src_factory, "src"); - gst_pad_set_query_function (parse->srcpad, - GST_DEBUG_FUNCPTR (vorbis_parse_src_query)); - gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad); -} - -static void -vorbis_parse_set_header_on_caps (GstVorbisParse * parse, GstCaps * caps) -{ - GstBuffer *buf1, *buf2, *buf3; - GstStructure *structure; - GValue array = { 0 }; - GValue value = { 0 }; - - g_assert (parse); - g_assert (parse->streamheader); - g_assert (parse->streamheader->next); - g_assert (parse->streamheader->next->next); - buf1 = parse->streamheader->data; - g_assert (buf1); - buf2 = parse->streamheader->next->data; - g_assert (buf2); - buf3 = parse->streamheader->next->next->data; - g_assert (buf3); - - structure = gst_caps_get_structure (caps, 0); - - /* mark buffers */ - GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_IN_CAPS); - GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_FLAG_IN_CAPS); - GST_BUFFER_FLAG_SET (buf3, GST_BUFFER_FLAG_IN_CAPS); - - /* put buffers in a fixed list */ - g_value_init (&array, GST_TYPE_ARRAY); - g_value_init (&value, GST_TYPE_BUFFER); - gst_value_set_buffer (&value, buf1); - gst_value_array_append_value (&array, &value); - g_value_unset (&value); - g_value_init (&value, GST_TYPE_BUFFER); - gst_value_set_buffer (&value, buf2); - gst_value_array_append_value (&array, &value); - g_value_unset (&value); - g_value_init (&value, GST_TYPE_BUFFER); - gst_value_set_buffer (&value, buf3); - gst_value_array_append_value (&array, &value); - gst_structure_set_value (structure, "streamheader", &array); - g_value_unset (&value); - g_value_unset (&array); -} - -static void -vorbis_parse_drain_event_queue (GstVorbisParse * parse) -{ - while (parse->event_queue->length) { - GstEvent *event; - - event = GST_EVENT_CAST (g_queue_pop_head (parse->event_queue)); - gst_pad_event_default (parse->sinkpad, event); - } -} - -static void -vorbis_parse_push_headers (GstVorbisParse * parse) -{ - /* mark and put on caps */ - GstCaps *caps; - GstBuffer *outbuf, *outbuf1, *outbuf2, *outbuf3; - ogg_packet packet; - - /* get the headers into the caps, passing them to vorbis as we go */ - caps = gst_caps_make_writable (gst_pad_get_caps (parse->srcpad)); - vorbis_parse_set_header_on_caps (parse, caps); - GST_DEBUG_OBJECT (parse, "here are the caps: %" GST_PTR_FORMAT, caps); - gst_pad_set_caps (parse->srcpad, caps); - gst_caps_unref (caps); - - outbuf = GST_BUFFER_CAST (parse->streamheader->data); - packet.packet = GST_BUFFER_DATA (outbuf); - packet.bytes = GST_BUFFER_SIZE (outbuf); - packet.granulepos = GST_BUFFER_OFFSET_END (outbuf); - packet.packetno = 1; - packet.e_o_s = 0; - packet.b_o_s = 1; - vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet); - parse->sample_rate = parse->vi.rate; - outbuf1 = outbuf; - - outbuf = GST_BUFFER_CAST (parse->streamheader->next->data); - packet.packet = GST_BUFFER_DATA (outbuf); - packet.bytes = GST_BUFFER_SIZE (outbuf); - packet.granulepos = GST_BUFFER_OFFSET_END (outbuf); - packet.packetno = 2; - packet.e_o_s = 0; - packet.b_o_s = 0; - vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet); - outbuf2 = outbuf; - - outbuf = GST_BUFFER_CAST (parse->streamheader->next->next->data); - packet.packet = GST_BUFFER_DATA (outbuf); - packet.bytes = GST_BUFFER_SIZE (outbuf); - packet.granulepos = GST_BUFFER_OFFSET_END (outbuf); - packet.packetno = 3; - packet.e_o_s = 0; - packet.b_o_s = 0; - vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet); - outbuf3 = outbuf; - - /* first process queued events */ - vorbis_parse_drain_event_queue (parse); - - /* push out buffers, ignoring return value... */ - gst_buffer_set_caps (outbuf1, GST_PAD_CAPS (parse->srcpad)); - gst_pad_push (parse->srcpad, outbuf1); - gst_buffer_set_caps (outbuf2, GST_PAD_CAPS (parse->srcpad)); - gst_pad_push (parse->srcpad, outbuf2); - gst_buffer_set_caps (outbuf3, GST_PAD_CAPS (parse->srcpad)); - gst_pad_push (parse->srcpad, outbuf3); - - g_list_free (parse->streamheader); - parse->streamheader = NULL; -} - -static void -vorbis_parse_clear_queue (GstVorbisParse * parse) -{ - while (parse->buffer_queue->length) { - GstBuffer *buf; - - buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue)); - gst_buffer_unref (buf); - } - while (parse->event_queue->length) { - GstEvent *event; - - event = GST_EVENT_CAST (g_queue_pop_head (parse->event_queue)); - gst_event_unref (event); - } -} - -static GstFlowReturn -vorbis_parse_push_buffer (GstVorbisParse * parse, GstBuffer * buf, - gint64 granulepos) -{ - guint64 samples; - - /* our hack as noted below */ - samples = GST_BUFFER_OFFSET (buf); - - GST_BUFFER_OFFSET_END (buf) = granulepos; - GST_BUFFER_DURATION (buf) = samples * GST_SECOND / parse->sample_rate; - GST_BUFFER_OFFSET (buf) = granulepos * GST_SECOND / parse->sample_rate; - GST_BUFFER_TIMESTAMP (buf) = - GST_BUFFER_OFFSET (buf) - GST_BUFFER_DURATION (buf); - - gst_buffer_set_caps (buf, GST_PAD_CAPS (parse->srcpad)); - - return gst_pad_push (parse->srcpad, buf); -} - -static GstFlowReturn -vorbis_parse_drain_queue_prematurely (GstVorbisParse * parse) -{ - GstFlowReturn ret = GST_FLOW_OK; - gint64 granulepos = MAX (parse->prev_granulepos, 0); - - /* got an EOS event, make sure to push out any buffers that were in the queue - * -- won't normally be the case, but this catches the - * didn't-get-a-granulepos-on-the-last-packet case. Assuming a continuous - * stream. */ - - /* if we got EOS before any buffers came, go ahead and push the other events - * first */ - vorbis_parse_drain_event_queue (parse); - - while (!g_queue_is_empty (parse->buffer_queue)) { - GstBuffer *buf; - - buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue)); - - granulepos += GST_BUFFER_OFFSET (buf); - ret = vorbis_parse_push_buffer (parse, buf, granulepos); - - if (ret != GST_FLOW_OK) - goto done; - } - - parse->prev_granulepos = granulepos; - -done: - return ret; -} - -static GstFlowReturn -vorbis_parse_drain_queue (GstVorbisParse * parse, gint64 granulepos) -{ - GstFlowReturn ret = GST_FLOW_OK; - GList *walk; - gint64 cur = granulepos; - gint64 gp; - - for (walk = parse->buffer_queue->head; walk; walk = walk->next) - cur -= GST_BUFFER_OFFSET (walk->data); - - if (parse->prev_granulepos != -1) - cur = MAX (cur, parse->prev_granulepos); - - while (!g_queue_is_empty (parse->buffer_queue)) { - GstBuffer *buf; - - buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue)); - - cur += GST_BUFFER_OFFSET (buf); - gp = CLAMP (cur, 0, granulepos); - - ret = vorbis_parse_push_buffer (parse, buf, gp); - - if (ret != GST_FLOW_OK) - goto done; - } - - parse->prev_granulepos = granulepos; - -done: - return ret; -} - -static GstFlowReturn -vorbis_parse_queue_buffer (GstVorbisParse * parse, GstBuffer * buf) -{ - GstFlowReturn ret = GST_FLOW_OK; - long blocksize; - ogg_packet packet; - - buf = gst_buffer_make_metadata_writable (buf); - - packet.packet = GST_BUFFER_DATA (buf); - packet.bytes = GST_BUFFER_SIZE (buf); - packet.granulepos = GST_BUFFER_OFFSET_END (buf); - packet.packetno = parse->packetno + parse->buffer_queue->length; - packet.e_o_s = 0; - - blocksize = vorbis_packet_blocksize (&parse->vi, &packet); - - /* temporarily store the sample count in OFFSET -- we overwrite this later */ - - if (parse->prev_blocksize < 0) - GST_BUFFER_OFFSET (buf) = 0; - else - GST_BUFFER_OFFSET (buf) = (blocksize + parse->prev_blocksize) / 4; - - parse->prev_blocksize = blocksize; - - g_queue_push_tail (parse->buffer_queue, buf); - - if (GST_BUFFER_OFFSET_END_IS_VALID (buf)) - ret = vorbis_parse_drain_queue (parse, GST_BUFFER_OFFSET_END (buf)); - - return ret; -} - -static GstFlowReturn -vorbis_parse_parse_packet (GstVorbisParse * parse, GstBuffer * buf) -{ - GstFlowReturn ret; - guint8 *data; - guint size; - gboolean have_header; - - data = GST_BUFFER_DATA (buf); - size = GST_BUFFER_SIZE (buf); - - parse->packetno++; - - have_header = FALSE; - if (size >= 1) { - if (data[0] >= 0x01 && data[0] <= 0x05) - have_header = TRUE; - } - - if (have_header) { - if (!parse->streamheader_sent) { - /* we need to collect the headers still */ - /* so put it on the streamheader list and return */ - parse->streamheader = g_list_append (parse->streamheader, buf); - } - ret = GST_FLOW_OK; - } else { - /* data packet, push the headers we collected before */ - if (!parse->streamheader_sent) { - vorbis_parse_push_headers (parse); - parse->streamheader_sent = TRUE; - } - ret = vorbis_parse_queue_buffer (parse, buf); - } - - return ret; -} - -static GstFlowReturn -vorbis_parse_chain (GstPad * pad, GstBuffer * buffer) -{ - GstVorbisParseClass *klass; - GstVorbisParse *parse; - - parse = GST_VORBIS_PARSE (GST_PAD_PARENT (pad)); - klass = GST_VORBIS_PARSE_CLASS (G_OBJECT_GET_CLASS (parse)); - - g_assert (klass->parse_packet != NULL); - - return klass->parse_packet (parse, buffer); -} - -static gboolean -vorbis_parse_queue_event (GstVorbisParse * parse, GstEvent * event) -{ - GstFlowReturn ret = TRUE; - - g_queue_push_tail (parse->event_queue, event); - - return ret; -} - -static gboolean -vorbis_parse_sink_event (GstPad * pad, GstEvent * event) -{ - gboolean ret; - GstVorbisParse *parse; - - parse = GST_VORBIS_PARSE (gst_pad_get_parent (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - vorbis_parse_clear_queue (parse); - parse->prev_granulepos = -1; - parse->prev_blocksize = -1; - ret = gst_pad_event_default (pad, event); - break; - case GST_EVENT_EOS: - vorbis_parse_drain_queue_prematurely (parse); - ret = gst_pad_event_default (pad, event); - break; - default: - if (!parse->streamheader_sent && GST_EVENT_IS_SERIALIZED (event)) - ret = vorbis_parse_queue_event (parse, event); - else - ret = gst_pad_event_default (pad, event); - break; - } - - gst_object_unref (parse); - - return ret; -} - -static gboolean -vorbis_parse_convert (GstPad * pad, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = TRUE; - GstVorbisParse *parse; - guint64 scale = 1; - - parse = GST_VORBIS_PARSE (GST_PAD_PARENT (pad)); - - /* fixme: assumes atomic access to lots of instance variables modified from - * the streaming thread, including 64-bit variables */ - - if (parse->packetno < 4) - return FALSE; - - if (src_format == *dest_format) { - *dest_value = src_value; - return TRUE; - } - - if (parse->sinkpad == pad && - (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) - return FALSE; - - switch (src_format) { - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_BYTES: - scale = sizeof (float) * parse->vi.channels; - case GST_FORMAT_DEFAULT: - *dest_value = - scale * gst_util_uint64_scale_int (src_value, parse->vi.rate, - GST_SECOND); - break; - default: - res = FALSE; - } - break; - case GST_FORMAT_DEFAULT: - switch (*dest_format) { - case GST_FORMAT_BYTES: - *dest_value = src_value * sizeof (float) * parse->vi.channels; - break; - case GST_FORMAT_TIME: - *dest_value = - gst_util_uint64_scale_int (src_value, GST_SECOND, parse->vi.rate); - break; - default: - res = FALSE; - } - break; - case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_DEFAULT: - *dest_value = src_value / (sizeof (float) * parse->vi.channels); - break; - case GST_FORMAT_TIME: - *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND, - parse->vi.rate * sizeof (float) * parse->vi.channels); - break; - default: - res = FALSE; - } - break; - default: - res = FALSE; - } - - return res; -} - -static gboolean -vorbis_parse_src_query (GstPad * pad, GstQuery * query) -{ - gint64 granulepos; - GstVorbisParse *parse; - gboolean res = FALSE; - - parse = GST_VORBIS_PARSE (GST_PAD_PARENT (pad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - { - GstFormat format; - gint64 value; - - granulepos = parse->prev_granulepos; - - gst_query_parse_position (query, &format, NULL); - - /* and convert to the final format */ - if (!(res = - vorbis_parse_convert (pad, GST_FORMAT_DEFAULT, granulepos, - &format, &value))) - goto error; - - /* fixme: support segments - value = (value - parse->segment_start) + parse->segment_time; - */ - - gst_query_set_position (query, format, value); - - GST_LOG_OBJECT (parse, "query %p: peer returned granulepos: %" - G_GUINT64_FORMAT " - we return %" G_GUINT64_FORMAT " (format %u)", - query, granulepos, value, format); - - break; - } - case GST_QUERY_DURATION: - { - /* fixme: not threadsafe */ - /* query peer for total length */ - if (!gst_pad_is_linked (parse->sinkpad)) { - GST_WARNING_OBJECT (parse, "sink pad %" GST_PTR_FORMAT " is not linked", - parse->sinkpad); - goto error; - } - if (!(res = gst_pad_query (GST_PAD_PEER (parse->sinkpad), query))) - goto error; - break; - } - case GST_QUERY_CONVERT: - { - GstFormat src_fmt, dest_fmt; - gint64 src_val, dest_val; - - gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); - if (!(res = - vorbis_parse_convert (pad, src_fmt, src_val, &dest_fmt, - &dest_val))) - goto error; - gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } - return res; - -error: - { - GST_WARNING_OBJECT (parse, "error handling query"); - return res; - } -} - -static GstStateChangeReturn -vorbis_parse_change_state (GstElement * element, GstStateChange transition) -{ - GstVorbisParse *parse = GST_VORBIS_PARSE (element); - GstStateChangeReturn ret; - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - vorbis_info_init (&parse->vi); - vorbis_comment_init (&parse->vc); - parse->prev_granulepos = -1; - parse->prev_blocksize = -1; - parse->packetno = 0; - parse->streamheader_sent = FALSE; - parse->buffer_queue = g_queue_new (); - parse->event_queue = g_queue_new (); - break; - default: - break; - } - - ret = parent_class->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - vorbis_info_clear (&parse->vi); - vorbis_comment_clear (&parse->vc); - vorbis_parse_clear_queue (parse); - g_queue_free (parse->buffer_queue); - parse->buffer_queue = NULL; - g_queue_free (parse->event_queue); - parse->event_queue = NULL; - break; - default: - break; - } - - return ret; -} diff --git a/ext/vorbis/gstvorbisparse.h b/ext/vorbis/gstvorbisparse.h deleted file mode 100644 index 65389497..00000000 --- a/ext/vorbis/gstvorbisparse.h +++ /dev/null @@ -1,82 +0,0 @@ -/* -*- c-basic-offset: 2 -*- - * GStreamer - * Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_VORBIS_PARSE_H__ -#define __GST_VORBIS_PARSE_H__ - - -#include <gst/gst.h> -#include <vorbis/codec.h> - -G_BEGIN_DECLS - -#define GST_TYPE_VORBIS_PARSE \ - (gst_vorbis_parse_get_type()) -#define GST_VORBIS_PARSE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VORBIS_PARSE,GstVorbisParse)) -#define GST_VORBIS_PARSE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VORBIS_PARSE,GstVorbisParseClass)) -#define GST_IS_VORBIS_PARSE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VORBIS_PARSE)) -#define GST_IS_VORBIS_PARSE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VORBIS_PARSE)) - -typedef struct _GstVorbisParse GstVorbisParse; -typedef struct _GstVorbisParseClass GstVorbisParseClass; - -/** - * GstVorbisParse: - * - * Opaque data structure. - */ -struct _GstVorbisParse { - GstElement element; - - GstPad * sinkpad; - GstPad * srcpad; - - guint packetno; - gboolean streamheader_sent; - GList * streamheader; - - GQueue * event_queue; - GQueue * buffer_queue; - - vorbis_info vi; - vorbis_comment vc; - - gint64 prev_granulepos; - gint32 prev_blocksize; - guint32 sample_rate; -}; - -struct _GstVorbisParseClass { - GstElementClass parent_class; - - /* virtual functions */ - GstFlowReturn (*parse_packet) (GstVorbisParse * parse, GstBuffer * buf); -}; - -GType gst_vorbis_parse_get_type(void); - -G_END_DECLS - -#endif /* __GST_VORBIS_PARSE_H__ */ diff --git a/ext/vorbis/gstvorbistag.c b/ext/vorbis/gstvorbistag.c deleted file mode 100644 index 67a931d1..00000000 --- a/ext/vorbis/gstvorbistag.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2006 James Livingston <doclivingston@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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:element-vorbistag - * @see_also: #oggdemux, #oggmux, #vorbisparse, #GstTagSetter - * - * The vorbistags element can change the tag contained within a raw - * vorbis stream. Specifically, it modifies the comments header packet - * of the vorbis stream. - * - * The element will also process the stream as the #vorbisparse element does - * so it can be used when remuxing an Ogg Vorbis stream, without additional - * elements. - * - * Applications can set the tags to write using the #GstTagSetter interface. - * Tags contained withing the vorbis bitstream will be picked up - * automatically (and merged according to the merge mode set via the tag - * setter interface). - * - * <refsect2> - * <title>Example pipelines</title> - * |[ - * gst-launch -v filesrc location=foo.ogg ! oggdemux ! vorbistag ! oggmux ! filesink location=bar.ogg - * ]| This element is not useful with gst-launch, because it does not support - * setting the tags on a #GstTagSetter interface. Conceptually, the element - * will usually be used in this order though. - * </refsect2> - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <glib.h> -#include <gst/tag/tag.h> -#include <gst/gsttagsetter.h> - -#include <vorbis/codec.h> - -#include "gstvorbistag.h" - - -GST_DEBUG_CATEGORY_EXTERN (vorbisparse_debug); -#define GST_CAT_DEFAULT vorbisparse_debug - -static void gst_vorbis_tag_base_init (gpointer g_class); -static void gst_vorbis_tag_class_init (GstVorbisTagClass * klass); -static void gst_vorbis_tag_init (GstVorbisTag * tagger, - GstVorbisTagClass * g_class); -static GstFlowReturn gst_vorbis_tag_parse_packet (GstVorbisParse * parse, - GstBuffer * buffer); - -#define _do_init(type) \ - G_STMT_START{ \ - static const GInterfaceInfo tag_setter_info = { \ - NULL, \ - NULL, \ - NULL \ - }; \ - g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, \ - &tag_setter_info); \ - }G_STMT_END - -GST_BOILERPLATE_FULL (GstVorbisTag, gst_vorbis_tag, GstVorbisParse, - GST_TYPE_VORBIS_PARSE, _do_init); - -static GstElementDetails vorbis_tag_details = { - "VorbisTag", - "Formatter/Metadata", - "Retags vorbis streams", - "James Livingston <doclivingston@gmail.com>" -}; - - -static void -gst_vorbis_tag_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_details (element_class, &vorbis_tag_details); -} - -static void -gst_vorbis_tag_class_init (GstVorbisTagClass * klass) -{ - GstVorbisParseClass *vorbisparse_class = GST_VORBIS_PARSE_CLASS (klass); - - vorbisparse_class->parse_packet = gst_vorbis_tag_parse_packet; -} - -static void -gst_vorbis_tag_init (GstVorbisTag * tagger, GstVorbisTagClass * g_class) -{ - /* nothing to do */ -} - - -static GstFlowReturn -gst_vorbis_tag_parse_packet (GstVorbisParse * parse, GstBuffer * buffer) -{ - GstTagList *old_tags, *new_tags; - const GstTagList *user_tags; - GstVorbisTag *tagger; - gchar *encoder = NULL; - GstBuffer *new_buf; - - /* just pass everything except the comments packet */ - if (GST_BUFFER_SIZE (buffer) >= 1 && GST_BUFFER_DATA (buffer)[0] != 0x03) { - return GST_VORBIS_PARSE_CLASS (parent_class)->parse_packet (parse, buffer); - } - - tagger = GST_VORBIS_TAG (parse); - - old_tags = - gst_tag_list_from_vorbiscomment_buffer (buffer, (guint8 *) "\003vorbis", - 7, &encoder); - user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (tagger)); - - /* build new tag list */ - new_tags = gst_tag_list_merge (user_tags, old_tags, - gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (tagger))); - gst_tag_list_free (old_tags); - - new_buf = - gst_tag_list_to_vorbiscomment_buffer (new_tags, (guint8 *) "\003vorbis", - 7, encoder); - gst_buffer_copy_metadata (new_buf, buffer, GST_BUFFER_COPY_TIMESTAMPS); - - gst_tag_list_free (new_tags); - g_free (encoder); - gst_buffer_unref (buffer); - - return GST_VORBIS_PARSE_CLASS (parent_class)->parse_packet (parse, new_buf); -} diff --git a/ext/vorbis/gstvorbistag.h b/ext/vorbis/gstvorbistag.h deleted file mode 100644 index 9a443006..00000000 --- a/ext/vorbis/gstvorbistag.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -*- c-basic-offset: 2 -*- - * GStreamer - * Copyright (C) <2006> James Livingston <doclivingston@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library 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 __GST_VORBIS_TAG_H__ -#define __GST_VORBIS_TAG_H__ - -#include "gstvorbisparse.h" - - -G_BEGIN_DECLS - - -#define GST_TYPE_VORBIS_TAG \ - (gst_vorbis_tag_get_type()) -#define GST_VORBIS_TAG(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VORBIS_TAG,GstVorbisTag)) -#define GST_VORBIS_TAG_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VORBIS_TAG,GstVorbisTagClass)) -#define GST_IS_VORBIS_TAG(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VORBIS_TAG)) -#define GST_IS_VORBIS_TAG_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VORBIS_TAG)) - - -typedef struct _GstVorbisTag GstVorbisTag; -typedef struct _GstVorbisTagClass GstVorbisTagClass; - -/** - * GstVorbisTag: - * - * Opaque data structure. - */ -struct _GstVorbisTag { - GstVorbisParse parse; -}; - -struct _GstVorbisTagClass { - GstVorbisParseClass parent_class; -}; - -GType gst_vorbis_tag_get_type(void); - -G_END_DECLS - -#endif /* __GST_VORBIS_TAG_H__ */ |