diff options
Diffstat (limited to 'sys')
47 files changed, 2715 insertions, 2984 deletions
diff --git a/sys/directsound/gstdirectsoundsink.c b/sys/directsound/gstdirectsoundsink.c index 2f9a04c65b..49f2afa8ae 100644 --- a/sys/directsound/gstdirectsoundsink.c +++ b/sys/directsound/gstdirectsoundsink.c @@ -52,6 +52,8 @@ #include "config.h" #endif +#include <gst/base/gstbasesink.h> +#include <gst/audio/streamvolume.h> #include "gstdirectsoundsink.h" #include <math.h> @@ -63,228 +65,85 @@ #endif #endif +#define DEFAULT_MUTE FALSE + GST_DEBUG_CATEGORY_STATIC (directsoundsink_debug); #define GST_CAT_DEFAULT directsoundsink_debug -static void gst_directsound_sink_finalise (GObject * object); +static void gst_directsound_sink_finalize (GObject * object); static void gst_directsound_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_directsound_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static GstCaps *gst_directsound_sink_getcaps (GstBaseSink * bsink); +static GstCaps *gst_directsound_sink_getcaps (GstBaseSink * bsink, + GstCaps * filter); static gboolean gst_directsound_sink_prepare (GstAudioSink * asink, - GstRingBufferSpec * spec); + GstAudioRingBufferSpec * spec); static gboolean gst_directsound_sink_unprepare (GstAudioSink * asink); - static gboolean gst_directsound_sink_open (GstAudioSink * asink); static gboolean gst_directsound_sink_close (GstAudioSink * asink); -static guint gst_directsound_sink_write (GstAudioSink * asink, gpointer data, - guint length); +static gint gst_directsound_sink_write (GstAudioSink * asink, + gpointer data, guint length); static guint gst_directsound_sink_delay (GstAudioSink * asink); static void gst_directsound_sink_reset (GstAudioSink * asink); static GstCaps *gst_directsound_probe_supported_formats (GstDirectSoundSink * dsoundsink, const GstCaps * template_caps); -/* interfaces */ -static void gst_directsound_sink_interfaces_init (GType type); -static void -gst_directsound_sink_implements_interface_init (GstImplementsInterfaceClass * - iface); -static void gst_directsound_sink_mixer_interface_init (GstMixerClass * iface); +static void gst_directsound_sink_set_volume (GstDirectSoundSink * sink, + gdouble volume, gboolean store); +static gdouble gst_directsound_sink_get_volume (GstDirectSoundSink * sink); +static void gst_directsound_sink_set_mute (GstDirectSoundSink * sink, + gboolean mute); +static gboolean gst_directsound_sink_get_mute (GstDirectSoundSink * sink); static GstStaticPadTemplate directsoundsink_sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-int, " - "signed = (boolean) TRUE, " - "width = (int) 16, " - "depth = (int) 16, " + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) S16LE, " + "layout = (string) interleaved, " "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; " - "audio/x-raw-int, " - "signed = (boolean) FALSE, " - "width = (int) 8, " - "depth = (int) 8, " + "audio/x-raw, " + "format = (string) S8, " + "layout = (string) interleaved, " "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ];" "audio/x-iec958")); enum { PROP_0, - PROP_VOLUME + PROP_VOLUME, + PROP_MUTE }; -GST_BOILERPLATE_FULL (GstDirectSoundSink, gst_directsound_sink, GstAudioSink, - GST_TYPE_AUDIO_SINK, gst_directsound_sink_interfaces_init); - -/* interfaces stuff */ -static void -gst_directsound_sink_interfaces_init (GType type) -{ - static const GInterfaceInfo implements_interface_info = { - (GInterfaceInitFunc) gst_directsound_sink_implements_interface_init, - NULL, - NULL, - }; - - static const GInterfaceInfo mixer_interface_info = { - (GInterfaceInitFunc) gst_directsound_sink_mixer_interface_init, - NULL, - NULL, - }; - - g_type_add_interface_static (type, - GST_TYPE_IMPLEMENTS_INTERFACE, &implements_interface_info); - g_type_add_interface_static (type, GST_TYPE_MIXER, &mixer_interface_info); -} - -static gboolean -gst_directsound_sink_interface_supported (GstImplementsInterface * iface, - GType iface_type) -{ - g_return_val_if_fail (iface_type == GST_TYPE_MIXER, FALSE); - - /* for the sake of this example, we'll always support it. However, normally, - * you would check whether the device you've opened supports mixers. */ - return TRUE; -} - -static void -gst_directsound_sink_implements_interface_init (GstImplementsInterfaceClass * - iface) -{ - iface->supported = gst_directsound_sink_interface_supported; -} - -/* - * This function returns the list of support tracks (inputs, outputs) - * on this element instance. Elements usually build this list during - * _init () or when going from NULL to READY. - */ - -static const GList * -gst_directsound_sink_mixer_list_tracks (GstMixer * mixer) -{ - GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (mixer); - - return dsoundsink->tracks; -} - -static void -gst_directsound_sink_set_volume (GstDirectSoundSink * dsoundsink) -{ - if (dsoundsink->pDSBSecondary) { - /* DirectSound controls volume using units of 100th of a decibel, - * ranging from -10000 to 0. We use a linear scale of 0 - 100 - * here, so remap. - */ - long dsVolume; - if (dsoundsink->volume == 0) - dsVolume = -10000; - else - dsVolume = 100 * (long) (20 * log10 ((double) dsoundsink->volume / 100.)); - dsVolume = CLAMP (dsVolume, -10000, 0); - - GST_DEBUG_OBJECT (dsoundsink, - "Setting volume on secondary buffer to %d from %d", (int) dsVolume, - (int) dsoundsink->volume); - IDirectSoundBuffer_SetVolume (dsoundsink->pDSBSecondary, dsVolume); - } -} - -/* - * Set volume. volumes is an array of size track->num_channels, and - * each value in the array gives the wanted volume for one channel - * on the track. - */ - -static void -gst_directsound_sink_mixer_set_volume (GstMixer * mixer, - GstMixerTrack * track, gint * volumes) -{ - GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (mixer); - - if (volumes[0] != dsoundsink->volume) { - dsoundsink->volume = volumes[0]; - - gst_directsound_sink_set_volume (dsoundsink); - } -} - -static void -gst_directsound_sink_mixer_get_volume (GstMixer * mixer, - GstMixerTrack * track, gint * volumes) -{ - GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (mixer); - - volumes[0] = dsoundsink->volume; -} +#define gst_directsound_sink_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstDirectSoundSink, gst_directsound_sink, + GST_TYPE_AUDIO_SINK, G_IMPLEMENT_INTERFACE (GST_TYPE_STREAM_VOLUME, NULL) + ); static void -gst_directsound_sink_mixer_interface_init (GstMixerClass * iface) +gst_directsound_sink_finalize (GObject * object) { - /* the mixer interface requires a definition of the mixer type: - * hardware or software? */ - GST_MIXER_TYPE (iface) = GST_MIXER_SOFTWARE; - - /* virtual function pointers */ - iface->list_tracks = gst_directsound_sink_mixer_list_tracks; - iface->set_volume = gst_directsound_sink_mixer_set_volume; - iface->get_volume = gst_directsound_sink_mixer_get_volume; -} - -static void -gst_directsound_sink_finalise (GObject * object) -{ - GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (object); - - g_mutex_free (dsoundsink->dsound_lock); - - if (dsoundsink->tracks) { - g_list_foreach (dsoundsink->tracks, (GFunc) g_object_unref, NULL); - g_list_free (dsoundsink->tracks); - dsoundsink->tracks = NULL; - } - G_OBJECT_CLASS (parent_class)->finalize (object); } static void -gst_directsound_sink_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_details_simple (element_class, - "Direct Sound Audio Sink", "Sink/Audio", - "Output to a sound card via Direct Sound", - "Sebastien Moutte <sebastien@moutte.net>"); - gst_element_class_add_static_pad_template (element_class, - &directsoundsink_sink_factory); -} - -static void gst_directsound_sink_class_init (GstDirectSoundSinkClass * klass) { - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - GstBaseSinkClass *gstbasesink_class; - GstBaseAudioSinkClass *gstbaseaudiosink_class; - GstAudioSinkClass *gstaudiosink_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - gstbasesink_class = (GstBaseSinkClass *) klass; - gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass; - gstaudiosink_class = (GstAudioSinkClass *) klass; + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass); + GstAudioSinkClass *gstaudiosink_class = GST_AUDIO_SINK_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GST_DEBUG_CATEGORY_INIT (directsoundsink_debug, "directsoundsink", 0, "DirectSound sink"); parent_class = g_type_class_peek_parent (klass); - gobject_class->finalize = gst_directsound_sink_finalise; + gobject_class->finalize = gst_directsound_sink_finalize; gobject_class->set_property = gst_directsound_sink_set_property; gobject_class->get_property = gst_directsound_sink_get_property; @@ -306,23 +165,27 @@ gst_directsound_sink_class_init (GstDirectSoundSinkClass * klass) g_param_spec_double ("volume", "Volume", "Volume of this stream", 0.0, 1.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_MUTE, + g_param_spec_boolean ("mute", "Mute", + "Mute state of this stream", DEFAULT_MUTE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_set_details_simple (element_class, + "Direct Sound Audio Sink", "Sink/Audio", + "Output to a sound card via Direct Sound", + "Sebastien Moutte <sebastien@moutte.net>"); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&directsoundsink_sink_factory)); } static void -gst_directsound_sink_init (GstDirectSoundSink * dsoundsink, - GstDirectSoundSinkClass * g_class) +gst_directsound_sink_init (GstDirectSoundSink * dsoundsink) { - GstMixerTrack *track = NULL; - - dsoundsink->tracks = NULL; - track = g_object_new (GST_TYPE_MIXER_TRACK, NULL); - track->label = g_strdup ("DSoundTrack"); - track->num_channels = 2; - track->min_volume = 0; - track->max_volume = 100; - track->flags = GST_MIXER_TRACK_OUTPUT; - dsoundsink->tracks = g_list_append (dsoundsink->tracks, track); - + dsoundsink->volume = 100; + dsoundsink->mute = FALSE; dsoundsink->pDS = NULL; dsoundsink->cached_caps = NULL; dsoundsink->pDSBSecondary = NULL; @@ -341,8 +204,10 @@ gst_directsound_sink_set_property (GObject * object, switch (prop_id) { case PROP_VOLUME: - sink->volume = (int) (g_value_get_double (value) * 100); - gst_directsound_sink_set_volume (sink); + gst_directsound_sink_set_volume (sink, g_value_get_double (value), TRUE); + break; + case PROP_MUTE: + gst_directsound_sink_set_mute (sink, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -358,7 +223,10 @@ gst_directsound_sink_get_property (GObject * object, switch (prop_id) { case PROP_VOLUME: - g_value_set_double (value, (double) sink->volume / 100.); + g_value_set_double (value, gst_directsound_sink_get_volume (sink)); + break; + case PROP_MUTE: + g_value_set_boolean (value, gst_directsound_sink_get_mute (sink)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -367,7 +235,7 @@ gst_directsound_sink_get_property (GObject * object, } static GstCaps * -gst_directsound_sink_getcaps (GstBaseSink * bsink) +gst_directsound_sink_getcaps (GstBaseSink * bsink, GstCaps * filter) { GstElementClass *element_class; GstPadTemplate *pad_template; @@ -409,9 +277,11 @@ gst_directsound_sink_getcaps (GstBaseSink * bsink) static gboolean gst_directsound_sink_open (GstAudioSink * asink) { - GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (asink); + GstDirectSoundSink *dsoundsink; HRESULT hRes; + dsoundsink = GST_DIRECTSOUND_SINK (asink); + /* create and initialize a DirecSound object */ if (FAILED (hRes = DirectSoundCreate (NULL, &dsoundsink->pDS, NULL))) { GST_ELEMENT_ERROR (dsoundsink, RESOURCE, OPEN_READ, @@ -432,47 +302,50 @@ gst_directsound_sink_open (GstAudioSink * asink) } static gboolean -gst_directsound_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec) +gst_directsound_sink_prepare (GstAudioSink * asink, + GstAudioRingBufferSpec * spec) { - GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (asink); + GstDirectSoundSink *dsoundsink; HRESULT hRes; DSBUFFERDESC descSecondary; WAVEFORMATEX wfx; + dsoundsink = GST_DIRECTSOUND_SINK (asink); + /*save number of bytes per sample and buffer format */ - dsoundsink->bytes_per_sample = spec->bytes_per_sample; - dsoundsink->buffer_format = spec->format; + dsoundsink->bytes_per_sample = spec->info.bpf; + dsoundsink->type = spec->type; /* fill the WAVEFORMATEX structure with spec params */ memset (&wfx, 0, sizeof (wfx)); - if (spec->format != GST_IEC958) { + if (spec->type != GST_AUDIO_RING_BUFFER_FORMAT_TYPE_IEC958) { wfx.cbSize = sizeof (wfx); wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nChannels = spec->channels; - wfx.nSamplesPerSec = spec->rate; - wfx.wBitsPerSample = (spec->bytes_per_sample * 8) / wfx.nChannels; - wfx.nBlockAlign = spec->bytes_per_sample; + wfx.nChannels = spec->info.channels; + wfx.nSamplesPerSec = spec->info.rate; + wfx.wBitsPerSample = (spec->info.bpf * 8) / wfx.nChannels; + wfx.nBlockAlign = spec->info.bpf; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; - /* Create directsound buffer with size based on our configured + /* Create directsound buffer with size based on our configured * buffer_size (which is 200 ms by default) */ dsoundsink->buffer_size = gst_util_uint64_scale_int (wfx.nAvgBytesPerSec, spec->buffer_time, GST_MSECOND); /* Make sure we make those numbers multiple of our sample size in bytes */ - dsoundsink->buffer_size += dsoundsink->buffer_size % spec->bytes_per_sample; + dsoundsink->buffer_size += dsoundsink->buffer_size % spec->info.bpf; spec->segsize = gst_util_uint64_scale_int (wfx.nAvgBytesPerSec, spec->latency_time, GST_MSECOND); - spec->segsize += spec->segsize % spec->bytes_per_sample; + spec->segsize += spec->segsize % spec->info.bpf; spec->segtotal = dsoundsink->buffer_size / spec->segsize; } else { #ifdef WAVE_FORMAT_DOLBY_AC3_SPDIF wfx.cbSize = 0; wfx.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF; wfx.nChannels = 2; - wfx.nSamplesPerSec = spec->rate; + wfx.nSamplesPerSec = spec->info.rate; wfx.wBitsPerSample = 16; wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; @@ -490,15 +363,15 @@ gst_directsound_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec) GST_INFO_OBJECT (dsoundsink, "GstRingBufferSpec->channels: %d, GstRingBufferSpec->rate: %d, GstRingBufferSpec->bytes_per_sample: %d\n" "WAVEFORMATEX.nSamplesPerSec: %ld, WAVEFORMATEX.wBitsPerSample: %d, WAVEFORMATEX.nBlockAlign: %d, WAVEFORMATEX.nAvgBytesPerSec: %ld\n" - "Size of dsound circular buffer=>%d\n", spec->channels, spec->rate, - spec->bytes_per_sample, wfx.nSamplesPerSec, wfx.wBitsPerSample, + "Size of dsound circular buffer=>%d\n", spec->info.channels, + spec->info.rate, spec->info.bpf, wfx.nSamplesPerSec, wfx.wBitsPerSample, wfx.nBlockAlign, wfx.nAvgBytesPerSec, dsoundsink->buffer_size); /* create a secondary directsound buffer */ memset (&descSecondary, 0, sizeof (DSBUFFERDESC)); descSecondary.dwSize = sizeof (DSBUFFERDESC); descSecondary.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; - if (spec->format != GST_IEC958) + if (spec->type != GST_AUDIO_RING_BUFFER_FORMAT_TYPE_IEC958) descSecondary.dwFlags |= DSBCAPS_CTRLVOLUME; descSecondary.dwBufferBytes = dsoundsink->buffer_size; @@ -513,7 +386,7 @@ gst_directsound_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec) return FALSE; } - gst_directsound_sink_set_volume (dsoundsink); + gst_directsound_sink_set_volume (dsoundsink, dsoundsink->volume, FALSE); return TRUE; } @@ -551,7 +424,7 @@ gst_directsound_sink_close (GstAudioSink * asink) return TRUE; } -static guint +static gint gst_directsound_sink_write (GstAudioSink * asink, gpointer data, guint length) { GstDirectSoundSink *dsoundsink; @@ -564,7 +437,7 @@ gst_directsound_sink_write (GstAudioSink * asink, gpointer data, guint length) dsoundsink = GST_DIRECTSOUND_SINK (asink); /* Fix endianness */ - if (dsoundsink->buffer_format == GST_IEC958) + if (dsoundsink->type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_IEC958) _swab (data, data, length); GST_DSOUND_LOCK (dsoundsink); @@ -719,12 +592,12 @@ gst_directsound_sink_reset (GstAudioSink * asink) GST_DSOUND_UNLOCK (dsoundsink); } -/* - * gst_directsound_probe_supported_formats: - * - * Takes the template caps and returns the subset which is actually - * supported by this device. - * +/* + * gst_directsound_probe_supported_formats: + * + * Takes the template caps and returns the subset which is actually + * supported by this device. + * */ static GstCaps * @@ -738,8 +611,8 @@ gst_directsound_probe_supported_formats (GstDirectSoundSink * dsoundsink, caps = gst_caps_copy (template_caps); - /* - * Check availability of digital output by trying to create an SPDIF buffer + /* + * Check availability of digital output by trying to create an SPDIF buffer */ #ifdef WAVE_FORMAT_DOLBY_AC3_SPDIF @@ -753,7 +626,7 @@ gst_directsound_probe_supported_formats (GstDirectSoundSink * dsoundsink, wfx.nBlockAlign = 4; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; - // create a secondary directsound buffer + // create a secondary directsound buffer memset (&descSecondary, 0, sizeof (DSBUFFERDESC)); descSecondary.dwSize = sizeof (DSBUFFERDESC); descSecondary.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; @@ -767,7 +640,7 @@ gst_directsound_probe_supported_formats (GstDirectSoundSink * dsoundsink, "(IDirectSound_CreateSoundBuffer returned: %s)\n", DXGetErrorString9 (hRes)); caps = - gst_caps_subtract (caps, gst_caps_new_simple ("audio/x-iec958", NULL)); + gst_caps_subtract (caps, gst_caps_new_empty_simple ("audio/x-iec958")); } else { GST_INFO_OBJECT (dsoundsink, "AC3 passthrough supported"); hRes = IDirectSoundBuffer_Release (dsoundsink->pDSBSecondary); @@ -783,3 +656,53 @@ gst_directsound_probe_supported_formats (GstDirectSoundSink * dsoundsink, return caps; } + +static void +gst_directsound_sink_set_volume (GstDirectSoundSink * dsoundsink, + gdouble dvolume, gboolean store) +{ + glong volume; + + volume = dvolume * 100; + if (store) + dsoundsink->volume = volume; + + if (dsoundsink->pDSBSecondary) { + /* DirectSound controls volume using units of 100th of a decibel, + * ranging from -10000 to 0. We use a linear scale of 0 - 100 + * here, so remap. + */ + long dsVolume; + if (dsoundsink->volume == 0) + dsVolume = -10000; + else + dsVolume = 100 * (long) (20 * log10 ((double) dsoundsink->volume / 100.)); + dsVolume = CLAMP (dsVolume, -10000, 0); + + GST_DEBUG_OBJECT (dsoundsink, + "Setting volume on secondary buffer to %d from %d", (int) dsVolume, + (int) dsoundsink->volume); + IDirectSoundBuffer_SetVolume (dsoundsink->pDSBSecondary, dsVolume); + } +} + +gdouble +gst_directsound_sink_get_volume (GstDirectSoundSink * dsoundsink) +{ + return (gdouble) dsoundsink->volume / 100; +} + +static void +gst_directsound_sink_set_mute (GstDirectSoundSink * dsoundsink, gboolean mute) +{ + if (mute) + gst_directsound_sink_set_volume (dsoundsink, 0, FALSE); + else + gst_directsound_sink_set_volume (dsoundsink, dsoundsink->volume, FALSE); +} + +static gboolean +gst_directsound_sink_get_mute (GstDirectSoundSink * dsoundsink) +{ + return FALSE; +} diff --git a/sys/directsound/gstdirectsoundsink.h b/sys/directsound/gstdirectsoundsink.h index 8bb10bf34d..5e23f19342 100644 --- a/sys/directsound/gstdirectsoundsink.h +++ b/sys/directsound/gstdirectsoundsink.h @@ -31,7 +31,6 @@ #include <gst/gst.h> #include <gst/audio/gstaudiosink.h> -#include <gst/interfaces/mixer.h> #include <windows.h> #include <dxerr9.h> @@ -56,6 +55,7 @@ struct _GstDirectSoundSink { GstAudioSink sink; + /* directsound object interface pointer */ LPDIRECTSOUND pDS; @@ -72,18 +72,15 @@ struct _GstDirectSoundSink /* current volume setup by mixer interface */ glong volume; - - /* tracks list of our mixer interface implementation */ - GList *tracks; + gboolean mute; GstCaps *cached_caps; - /* lock used to protect writes and resets */ GMutex *dsound_lock; gboolean first_buffer_after_reset; - GstBufferFormat buffer_format; + GstAudioRingBufferFormatType type; }; struct _GstDirectSoundSinkClass diff --git a/sys/oss/gstosshelper.c b/sys/oss/gstosshelper.c index 6d7e6bd3e1..639ee9d8f4 100644 --- a/sys/oss/gstosshelper.c +++ b/sys/oss/gstosshelper.c @@ -169,53 +169,34 @@ static GstStructure * gst_oss_helper_get_format_structure (unsigned int format_bit) { GstStructure *structure; - int endianness; - gboolean sign; - int width; + const gchar *format; switch (format_bit) { case AFMT_U8: - endianness = 0; - sign = FALSE; - width = 8; + format = "U8"; break; case AFMT_S16_LE: - endianness = G_LITTLE_ENDIAN; - sign = TRUE; - width = 16; + format = "S16_LE"; break; case AFMT_S16_BE: - endianness = G_BIG_ENDIAN; - sign = TRUE; - width = 16; + format = "S16_BE"; break; case AFMT_S8: - endianness = 0; - sign = TRUE; - width = 8; + format = "S8"; break; case AFMT_U16_LE: - endianness = G_LITTLE_ENDIAN; - sign = FALSE; - width = 16; + format = "U16_LE"; break; case AFMT_U16_BE: - endianness = G_BIG_ENDIAN; - sign = FALSE; - width = 16; + format = "U16_BE"; break; default: g_assert_not_reached (); return NULL; } - structure = gst_structure_new ("audio/x-raw-int", - "width", G_TYPE_INT, width, - "depth", G_TYPE_INT, width, "signed", G_TYPE_BOOLEAN, sign, NULL); - - if (endianness) { - gst_structure_set (structure, "endianness", G_TYPE_INT, endianness, NULL); - } + structure = gst_structure_new ("audio/x-raw", + "format", G_TYPE_STRING, format, NULL); return structure; } diff --git a/sys/oss/gstossmixer.h b/sys/oss/gstossmixer.h index d2e06fed92..4ae888aba7 100644 --- a/sys/oss/gstossmixer.h +++ b/sys/oss/gstossmixer.h @@ -152,16 +152,16 @@ interface_as_function ## _set_mute (GstMixer * mixer, GstMixerTrack * track, } \ \ static void \ -interface_as_function ## _interface_init (GstMixerClass * klass) \ +interface_as_function ## _interface_init (GstMixerInterface * iface) \ { \ - GST_MIXER_TYPE (klass) = GST_MIXER_HARDWARE; \ + GST_MIXER_TYPE (iface) = 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; \ + iface->list_tracks = interface_as_function ## _list_tracks; \ + iface->set_volume = interface_as_function ## _set_volume; \ + iface->get_volume = interface_as_function ## _get_volume; \ + iface->set_mute = interface_as_function ## _set_mute; \ + iface->set_record = interface_as_function ## _set_record; \ } diff --git a/sys/oss/gstosssink.c b/sys/oss/gstosssink.c index a604d9c3a8..67813476b2 100644 --- a/sys/oss/gstosssink.c +++ b/sys/oss/gstosssink.c @@ -110,21 +110,22 @@ enum PROP_DEVICE, }; +#define FORMATS "{" GST_AUDIO_NE(S16)","GST_AUDIO_NE(U16)", S8, U8 }" + static GstStaticPadTemplate osssink_sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-int, " - "endianness = (int) { " G_STRINGIFY (G_BYTE_ORDER) " }, " - "signed = (boolean) { TRUE, FALSE }, " - "width = (int) 16, " - "depth = (int) 16, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; " - "audio/x-raw-int, " - "signed = (boolean) { TRUE, FALSE }, " - "width = (int) 8, " - "depth = (int) 8, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]") + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) " FORMATS ", " + "layout = (string) interleaved, " + "rate = (int) [ 1, MAX ], " + "channels = (int) 1; " + "audio/x-raw, " + "format = (string) " FORMATS ", " + "layout = (string) interleaved, " + "rate = (int) [ 1, MAX ], " + "channels = (int) 2, " "channel-mask = (bitmask) 0x3") ); static GstElementClass *parent_class = NULL; @@ -181,8 +182,8 @@ gst_oss_sink_base_init (gpointer g_class) "Erik Walthinsen <omega@cse.ogi.edu>, " "Wim Taymans <wim.taymans@chello.be>"); - gst_element_class_add_static_pad_template (element_class, - &osssink_sink_factory); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&osssink_sink_factory)); } static void diff --git a/sys/oss/gstosssrc.c b/sys/oss/gstosssrc.c index 2bd931b1bc..f8ac8a93bf 100644 --- a/sys/oss/gstosssrc.c +++ b/sys/oss/gstosssrc.c @@ -101,25 +101,23 @@ static guint gst_oss_src_read (GstAudioSrc * asrc, gpointer data, guint length); static guint gst_oss_src_delay (GstAudioSrc * asrc); static void gst_oss_src_reset (GstAudioSrc * asrc); - +#define FORMATS "{" GST_AUDIO_NE(S16)","GST_AUDIO_NE(U16)", S8, U8 }" static GstStaticPadTemplate osssrc_src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-int, " - "endianness = (int) { " G_STRINGIFY (G_BYTE_ORDER) " }, " - "signed = (boolean) { TRUE, FALSE }, " - "width = (int) 16, " - "depth = (int) 16, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; " - "audio/x-raw-int, " - "signed = (boolean) { TRUE, FALSE }, " - "width = (int) 8, " - "depth = (int) 8, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]") + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) " FORMATS ", " + "layout = (string) interleaved, " + "rate = (int) [ 1, MAX ], " + "channels = (int) 1; " + "audio/x-raw, " + "format = (string) " FORMATS ", " + "layout = (string) interleaved, " + "rate = (int) [ 1, MAX ], " + "channels = (int) 2, " "channel-mask = (bitmask) 0x3") ); - static void gst_oss_src_dispose (GObject * object) { @@ -136,8 +134,8 @@ gst_oss_src_base_init (gpointer g_class) "Capture from a sound card via OSS", "Erik Walthinsen <omega@cse.ogi.edu>, " "Wim Taymans <wim@fluendo.com>"); - gst_element_class_add_static_pad_template (element_class, - &osssrc_src_factory); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&osssrc_src_factory)); } static void diff --git a/sys/oss4/oss4-audio.c b/sys/oss4/oss4-audio.c index 6317400dc5..c788a13807 100644 --- a/sys/oss4/oss4-audio.c +++ b/sys/oss4/oss4-audio.c @@ -458,7 +458,7 @@ gst_oss4_audio_probe_caps (GstObject * obj, int fd) } } - gst_caps_do_simplify (caps); + caps = gst_caps_do_simplify (caps); GST_LOG_OBJECT (obj, "formats: %" GST_PTR_FORMAT, caps); if (!gst_oss4_audio_detect_rates (obj, &ai, caps)) @@ -505,7 +505,7 @@ gst_oss4_audio_get_template_caps (void) gst_oss4_append_format_to_caps (&fmt_map[i], caps); } - gst_caps_do_simplify (caps); + caps = gst_caps_do_simplify (caps); for (i = 0; i < gst_caps_get_size (caps); ++i) { GstStructure *s; diff --git a/sys/oss4/oss4-mixer-slider.c b/sys/oss4/oss4-mixer-slider.c index ea2bc8cb2a..e71d1348fe 100644 --- a/sys/oss4/oss4-mixer-slider.c +++ b/sys/oss4/oss4-mixer-slider.c @@ -219,7 +219,7 @@ gst_oss4_mixer_slider_set_mute (GstOss4MixerSlider * s, gboolean mute) } ret = gst_oss4_mixer_set_control_val (s->mixer, s->mc, volume); } else { - ret = gst_oss4_mixer_set_control_val (s->mixer, s->mc->mute, ! !mute); + ret = gst_oss4_mixer_set_control_val (s->mixer, s->mc->mute, !!mute); } if (mute) { @@ -286,7 +286,7 @@ gst_oss4_mixer_slider_process_change_unlocked (GstMixerTrack * track) if (s->mc->mute != NULL && s->mc->mute->changed) { gst_mixer_mute_toggled (GST_MIXER (s->mixer), track, - ! !s->mc->mute->last_val); + !!s->mc->mute->last_val); } else { /* nothing to do here, since we don't/can't easily implement the record * flag */ diff --git a/sys/oss4/oss4-mixer.c b/sys/oss4/oss4-mixer.c index 03e2d9d729..49da03ceb1 100644 --- a/sys/oss4/oss4-mixer.c +++ b/sys/oss4/oss4-mixer.c @@ -542,13 +542,8 @@ gst_oss4_mixer_start_watch_task (GstOss4Mixer * mixer) mixer->watch_cond = g_cond_new (); mixer->watch_shutdown = FALSE; -#if !GLIB_CHECK_VERSION (2, 31, 0) - mixer->watch_thread = g_thread_create (gst_oss4_mixer_watch_thread, - gst_object_ref (mixer), TRUE, &err); -#else mixer->watch_thread = g_thread_try_new ("oss4-mixer-thread", gst_oss4_mixer_watch_thread, gst_object_ref (mixer), &err); -#endif if (mixer->watch_thread == NULL) { GST_ERROR_OBJECT (mixer, "Could not create watch thread: %s", err->message); @@ -1806,18 +1801,18 @@ gst_oss4_mixer_get_mixer_flags (GstMixer * mixer) } static void -gst_oss4_mixer_interface_init (GstMixerClass * klass) +gst_oss4_mixer_interface_init (GstMixerInterface * iface) { - GST_MIXER_TYPE (klass) = GST_MIXER_HARDWARE; - - klass->list_tracks = gst_oss4_mixer_list_tracks; - klass->set_volume = gst_oss4_mixer_set_volume; - klass->get_volume = gst_oss4_mixer_get_volume; - klass->set_mute = gst_oss4_mixer_set_mute; - klass->set_record = gst_oss4_mixer_set_record; - klass->set_option = gst_oss4_mixer_set_option; - klass->get_option = gst_oss4_mixer_get_option; - klass->get_mixer_flags = gst_oss4_mixer_get_mixer_flags; + GST_MIXER_TYPE (iface) = GST_MIXER_HARDWARE; + + iface->list_tracks = gst_oss4_mixer_list_tracks; + iface->set_volume = gst_oss4_mixer_set_volume; + iface->get_volume = gst_oss4_mixer_get_volume; + iface->set_mute = gst_oss4_mixer_set_mute; + iface->set_record = gst_oss4_mixer_set_record; + iface->set_option = gst_oss4_mixer_set_option; + iface->get_option = gst_oss4_mixer_get_option; + iface->get_mixer_flags = gst_oss4_mixer_get_mixer_flags; } /* Implement the horror that is GstImplementsInterface */ diff --git a/sys/oss4/oss4-sink.c b/sys/oss4/oss4-sink.c index 54e95b9f9e..81f8d2af28 100644 --- a/sys/oss4/oss4-sink.c +++ b/sys/oss4/oss4-sink.c @@ -134,7 +134,6 @@ gst_oss4_sink_base_init (gpointer g_class) templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, gst_oss4_audio_get_template_caps ()); gst_element_class_add_pad_template (element_class, templ); - gst_object_unref (templ); } static void @@ -477,7 +476,7 @@ gst_oss4_sink_open (GstAudioSink * asink, gboolean silent_errors) if (ioctl (oss->fd, SNDCTL_DSP_GET_PLAYTGT_NAMES, &routings) != -1) { GST_LOG_OBJECT (oss, "%u output routings (static list: %d)", - routings.nvalues, !!(routings.version == 0)); + routings.nvalues, ! !(routings.version == 0)); for (i = 0; i < routings.nvalues; ++i) { GST_LOG_OBJECT (oss, " output routing %d: %s", i, &routings.strings[routings.strindex[i]]); diff --git a/sys/oss4/oss4-source.c b/sys/oss4/oss4-source.c index eadb4c5e31..b80f9b7707 100644 --- a/sys/oss4/oss4-source.c +++ b/sys/oss4/oss4-source.c @@ -115,7 +115,6 @@ gst_oss4_source_base_init (gpointer g_class) templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, gst_oss4_audio_get_template_caps ()); gst_element_class_add_pad_template (element_class, templ); - gst_object_unref (templ); } static void @@ -936,15 +935,15 @@ gst_oss4_source_mixer_set_mute (GstMixer * mixer, GstMixerTrack * track, } static void -gst_oss4_source_mixer_interface_init (GstMixerClass * klass) +gst_oss4_source_mixer_interface_init (GstMixerInterface * iface) { - GST_MIXER_TYPE (klass) = GST_MIXER_HARDWARE; + GST_MIXER_TYPE (iface) = GST_MIXER_HARDWARE; - klass->list_tracks = gst_oss4_source_mixer_list_tracks; - klass->set_volume = gst_oss4_source_mixer_set_volume; - klass->get_volume = gst_oss4_source_mixer_get_volume; - klass->set_mute = gst_oss4_source_mixer_set_mute; - klass->set_record = gst_oss4_source_mixer_set_record; + iface->list_tracks = gst_oss4_source_mixer_list_tracks; + iface->set_volume = gst_oss4_source_mixer_set_volume; + iface->get_volume = gst_oss4_source_mixer_get_volume; + iface->set_mute = gst_oss4_source_mixer_set_mute; + iface->set_record = gst_oss4_source_mixer_set_record; } /* Implement the horror that is GstImplementsInterface */ diff --git a/sys/osxaudio/gstosxaudioelement.c b/sys/osxaudio/gstosxaudioelement.c index a41f4b8772..16cee0fd07 100644 --- a/sys/osxaudio/gstosxaudioelement.c +++ b/sys/osxaudio/gstosxaudioelement.c @@ -50,7 +50,7 @@ #include "gstosxaudioelement.h" static void -gst_osx_audio_element_class_init (GstOsxAudioElementInterface * klass); +gst_osx_audio_element_interface_init (GstOsxAudioElementInterface * iface); GType gst_osx_audio_element_get_type (void) @@ -60,7 +60,7 @@ gst_osx_audio_element_get_type (void) if (!gst_osxaudioelement_type) { static const GTypeInfo gst_osxaudioelement_info = { sizeof (GstOsxAudioElementInterface), - (GBaseInitFunc) gst_osx_audio_element_class_init, + (GBaseInitFunc) gst_osx_audio_element_interface_init, NULL, NULL, NULL, @@ -79,7 +79,7 @@ gst_osx_audio_element_get_type (void) } static void -gst_osx_audio_element_class_init (GstOsxAudioElementInterface * klass) +gst_osx_audio_element_interface_init (GstOsxAudioElementInterface * iface) { static gboolean initialized = FALSE; @@ -88,5 +88,5 @@ gst_osx_audio_element_class_init (GstOsxAudioElementInterface * klass) } /* default virtual functions */ - klass->io_proc = NULL; + iface->io_proc = NULL; } diff --git a/sys/osxaudio/gstosxaudiosink.c b/sys/osxaudio/gstosxaudiosink.c index cd456e5973..e3ade70110 100644 --- a/sys/osxaudio/gstosxaudiosink.c +++ b/sys/osxaudio/gstosxaudiosink.c @@ -142,7 +142,8 @@ gst_osx_audio_sink_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - gst_element_class_add_static_pad_template (element_class, &sink_factory); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_factory)); gst_element_class_set_details_simple (element_class, "Audio Sink (OSX)", "Sink/Audio", diff --git a/sys/osxaudio/gstosxaudiosrc.c b/sys/osxaudio/gstosxaudiosrc.c index 7e9ad245bd..2bb21a79ba 100644 --- a/sys/osxaudio/gstosxaudiosrc.c +++ b/sys/osxaudio/gstosxaudiosrc.c @@ -133,7 +133,8 @@ gst_osx_audio_src_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - gst_element_class_add_static_pad_template (element_class, &src_factory); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_factory)); gst_element_class_set_details_simple (element_class, "Audio Source (OSX)", "Source/Audio", diff --git a/sys/osxvideo/osxvideosink.m b/sys/osxvideo/osxvideosink.m index 59145503e1..4c325278fc 100644 --- a/sys/osxvideo/osxvideosink.m +++ b/sys/osxvideo/osxvideosink.m @@ -354,8 +354,8 @@ gst_osx_video_sink_base_init (gpointer g_class) "Sink/Video", "OSX native videosink", "Zaheer Abbas Merali <zaheerabbas at merali dot org>"); - gst_element_class_add_static_pad_template (element_class, - &gst_osx_video_sink_sink_template_factory); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_osx_video_sink_sink_template_factory)); } static void diff --git a/sys/sunaudio/gstsunaudiomixerctrl.h b/sys/sunaudio/gstsunaudiomixerctrl.h index d38f02f25d..2ca17f0ed3 100644 --- a/sys/sunaudio/gstsunaudiomixerctrl.h +++ b/sys/sunaudio/gstsunaudiomixerctrl.h @@ -169,19 +169,19 @@ interface_as_function ## _get_mixer_flags (GstMixer * mixer) } \ \ static void \ -interface_as_function ## _interface_init (GstMixerClass * klass) \ +interface_as_function ## _interface_init (GstMixerInterface * iface) \ { \ - GST_MIXER_TYPE (klass) = GST_MIXER_HARDWARE; \ + GST_MIXER_TYPE (iface) = 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->get_option = interface_as_function ## _get_option; \ - klass->set_option = interface_as_function ## _set_option; \ - klass->get_mixer_flags = interface_as_function ## _get_mixer_flags; \ + iface->list_tracks = interface_as_function ## _list_tracks; \ + iface->set_volume = interface_as_function ## _set_volume; \ + iface->get_volume = interface_as_function ## _get_volume; \ + iface->set_mute = interface_as_function ## _set_mute; \ + iface->set_record = interface_as_function ## _set_record; \ + iface->get_option = interface_as_function ## _get_option; \ + iface->set_option = interface_as_function ## _set_option; \ + iface->get_mixer_flags = interface_as_function ## _get_mixer_flags; \ } G_END_DECLS diff --git a/sys/sunaudio/gstsunaudiosink.c b/sys/sunaudio/gstsunaudiosink.c index 2a3cf19284..0110b26fab 100644 --- a/sys/sunaudio/gstsunaudiosink.c +++ b/sys/sunaudio/gstsunaudiosink.c @@ -146,8 +146,8 @@ gst_sunaudiosink_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - gst_element_class_add_static_pad_template (element_class, - &gst_sunaudiosink_factory); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_sunaudiosink_factory)); gst_element_class_set_details_simple (element_class, "Sun Audio Sink", "Sink/Audio", "Audio sink for Sun Audio devices", diff --git a/sys/sunaudio/gstsunaudiosrc.c b/sys/sunaudio/gstsunaudiosrc.c index 08282cf02b..f0529be932 100644 --- a/sys/sunaudio/gstsunaudiosrc.c +++ b/sys/sunaudio/gstsunaudiosrc.c @@ -108,8 +108,8 @@ gst_sunaudiosrc_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - gst_element_class_add_static_pad_template (element_class, - &gst_sunaudiosrc_factory); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_sunaudiosrc_factory)); gst_element_class_set_details_simple (element_class, "Sun Audio Source", "Source/Audio", "Audio source for Sun Audio devices", diff --git a/sys/v4l2/Makefile.am b/sys/v4l2/Makefile.am index a7a99dea31..86f4fd2d08 100644 --- a/sys/v4l2/Makefile.am +++ b/sys/v4l2/Makefile.am @@ -1,7 +1,7 @@ plugin_LTLIBRARIES = libgstvideo4linux2.la if USE_XVIDEO -xv_source = gstv4l2xoverlay.c +xv_source = gstv4l2videooverlay.c xv_libs = $(X_LIBS) $(XVIDEO_LIBS) else xv_source = @@ -17,16 +17,11 @@ libgstvideo4linux2_la_SOURCES = gstv4l2.c \ gstv4l2tuner.c \ gstv4l2vidorient.c \ v4l2_calls.c \ - v4l2src_calls.c \ $(xv_source) - -if BUILD_EXPERIMENTAL libgstvideo4linux2_la_SOURCES += gstv4l2sink.c -endif libgstvideo4linux2_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_BASE_CFLAGS) \ - $(GST_CONTROLLER_CFLAGS) \ $(GST_CFLAGS) \ $(X_CFLAGS) \ $(LIBV4L2_CFLAGS) \ @@ -37,7 +32,6 @@ libgstvideo4linux2_la_LIBTOOLFLAGS = --tag=disable-static libgstvideo4linux2_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ $(GST_BASE_LIBS) \ - $(GST_CONTROLLER_LIBS) \ $(GST_PLUGINS_BASE_LIBS) \ -lgstvideo-$(GST_MAJORMINOR) \ -lgstinterfaces-$(GST_MAJORMINOR) \ @@ -55,6 +49,5 @@ noinst_HEADERS = \ gstv4l2radio.h \ gstv4l2tuner.h \ gstv4l2vidorient.h \ - gstv4l2xoverlay.h \ - v4l2_calls.h \ - v4l2src_calls.h + gstv4l2videooverlay.h \ + v4l2_calls.h diff --git a/sys/v4l2/gstv4l2.c b/sys/v4l2/gstv4l2.c index 95f64db785..22959c6635 100644 --- a/sys/v4l2/gstv4l2.c +++ b/sys/v4l2/gstv4l2.c @@ -28,13 +28,10 @@ #include "gst/gst-i18n-plugin.h" #include <gst/gst.h> -#include <gst/controller/gstcontroller.h> #include "gstv4l2object.h" #include "gstv4l2src.h" -#ifdef HAVE_EXPERIMENTAL #include "gstv4l2sink.h" -#endif #include "gstv4l2radio.h" /* #include "gstv4l2jpegsrc.h" */ /* #include "gstv4l2mjpegsrc.h" */ @@ -50,15 +47,10 @@ plugin_init (GstPlugin * plugin) GST_DEBUG_CATEGORY_INIT (v4l2_debug, "v4l2", 0, "V4L2 API calls"); GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE"); - /* initialize gst controller library */ - gst_controller_init (NULL, NULL); - if (!gst_element_register (plugin, "v4l2src", GST_RANK_PRIMARY, GST_TYPE_V4L2SRC) || -#ifdef HAVE_EXPERIMENTAL !gst_element_register (plugin, "v4l2sink", GST_RANK_NONE, GST_TYPE_V4L2SINK) || -#endif !gst_element_register (plugin, "v4l2radio", GST_RANK_NONE, GST_TYPE_V4L2RADIO) || /* !gst_element_register (plugin, "v4l2jpegsrc", */ diff --git a/sys/v4l2/gstv4l2bufferpool.c b/sys/v4l2/gstv4l2bufferpool.c index a0b4c842aa..306814717c 100644 --- a/sys/v4l2/gstv4l2bufferpool.c +++ b/sys/v4l2/gstv4l2bufferpool.c @@ -31,12 +31,13 @@ #include <unistd.h> #include "gst/video/video.h" +#include "gst/video/gstvideometa.h" +#include "gst/video/gstvideopool.h" #include <gstv4l2bufferpool.h> + #include "gstv4l2src.h" -#ifdef HAVE_EXPERIMENTAL #include "gstv4l2sink.h" -#endif #include "v4l2_calls.h" #include "gst/gst-i18n-plugin.h" #include <gst/glib-compat-private.h> @@ -53,153 +54,167 @@ GST_DEBUG_CATEGORY_EXTERN (v4l2_debug); #define GST_CAT_DEFAULT v4l2_debug - /* * GstV4l2Buffer: */ - -static GstBufferClass *v4l2buffer_parent_class = NULL; - -static void -gst_v4l2_buffer_finalize (GstV4l2Buffer * buffer) +GType +gst_v4l2_meta_api_get_type (void) { - GstV4l2BufferPool *pool; - gboolean resuscitated = FALSE; - gint index; - - pool = buffer->pool; - - index = buffer->vbuffer.index; - - GST_LOG_OBJECT (pool->v4l2elem, "finalizing buffer %p %d", buffer, index); - - GST_V4L2_BUFFER_POOL_LOCK (pool); - if (pool->running) { - if (pool->requeuebuf) { - if (!gst_v4l2_buffer_pool_qbuf (pool, buffer)) { - GST_WARNING ("could not requeue buffer %p %d", buffer, index); - } else { - resuscitated = TRUE; - } - } else { - resuscitated = TRUE; - /* XXX double check this... I think it is ok to not synchronize this - * w.r.t. destruction of the pool, since the buffer is still live and - * the buffer holds a ref to the pool.. - */ - g_async_queue_push (pool->avail_buffers, buffer); - } - } else { - GST_LOG_OBJECT (pool->v4l2elem, "the pool is shutting down"); - } + static volatile GType type; + static const gchar *tags[] = { "memory", NULL }; - if (resuscitated) { - /* FIXME: check that the caps didn't change */ - GST_LOG_OBJECT (pool->v4l2elem, "reviving buffer %p, %d", buffer, index); - gst_buffer_ref (GST_BUFFER (buffer)); - GST_BUFFER_SIZE (buffer) = 0; - GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DISCONT); - pool->buffers[index] = buffer; - } - - GST_V4L2_BUFFER_POOL_UNLOCK (pool); - - if (!resuscitated) { - GST_LOG_OBJECT (pool->v4l2elem, - "buffer %p (data %p, len %u) not recovered, unmapping", - buffer, GST_BUFFER_DATA (buffer), buffer->mmap_length); - gst_mini_object_unref (GST_MINI_OBJECT (pool)); - v4l2_munmap ((void *) GST_BUFFER_DATA (buffer), buffer->mmap_length); - - GST_MINI_OBJECT_CLASS (v4l2buffer_parent_class)->finalize (GST_MINI_OBJECT - (buffer)); + if (g_once_init_enter (&type)) { + GType _type = gst_meta_api_type_register ("GstV4l2MetaAPI", tags); + g_once_init_leave (&type, _type); } + return type; } -static void -gst_v4l2_buffer_class_init (gpointer g_class, gpointer class_data) +const GstMetaInfo * +gst_v4l2_meta_get_info (void) { - GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class); + static const GstMetaInfo *meta_info = NULL; - v4l2buffer_parent_class = g_type_class_peek_parent (g_class); - - mini_object_class->finalize = (GstMiniObjectFinalizeFunction) - gst_v4l2_buffer_finalize; -} - -GType -gst_v4l2_buffer_get_type (void) -{ - static GType _gst_v4l2_buffer_type; - - if (G_UNLIKELY (_gst_v4l2_buffer_type == 0)) { - static const GTypeInfo v4l2_buffer_info = { - sizeof (GstBufferClass), - NULL, - NULL, - gst_v4l2_buffer_class_init, - NULL, - NULL, - sizeof (GstV4l2Buffer), - 0, - NULL, - NULL - }; - _gst_v4l2_buffer_type = g_type_register_static (GST_TYPE_BUFFER, - "GstV4l2Buffer", &v4l2_buffer_info, 0); - } - return _gst_v4l2_buffer_type; + if (meta_info == NULL) { + meta_info = + gst_meta_register (gst_v4l2_meta_api_get_type (), "GstV4l2Meta", + sizeof (GstV4l2Meta), (GstMetaInitFunction) NULL, + (GstMetaFreeFunction) NULL, (GstMetaTransformFunction) NULL); + } + return meta_info; } -static GstV4l2Buffer * -gst_v4l2_buffer_new (GstV4l2BufferPool * pool, guint index, GstCaps * caps) -{ - GstV4l2Buffer *ret; - guint8 *data; - - ret = (GstV4l2Buffer *) gst_mini_object_new (GST_TYPE_V4L2_BUFFER); +/* + * GstV4l2BufferPool: + */ +#define gst_v4l2_buffer_pool_parent_class parent_class +G_DEFINE_TYPE (GstV4l2BufferPool, gst_v4l2_buffer_pool, GST_TYPE_BUFFER_POOL); - GST_LOG_OBJECT (pool->v4l2elem, "creating buffer %u, %p in pool %p", index, - ret, pool); +static void gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, + GstBuffer * buffer); - ret->pool = - (GstV4l2BufferPool *) gst_mini_object_ref (GST_MINI_OBJECT (pool)); +static void +gst_v4l2_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer) +{ + GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); + GstV4l2Object *obj; - ret->vbuffer.index = index; - ret->vbuffer.type = pool->type; - ret->vbuffer.memory = V4L2_MEMORY_MMAP; + obj = pool->obj; - if (v4l2_ioctl (pool->video_fd, VIDIOC_QUERYBUF, &ret->vbuffer) < 0) - goto querybuf_failed; + switch (obj->mode) { + case GST_V4L2_IO_RW: + break; + case GST_V4L2_IO_MMAP: + { + GstV4l2Meta *meta; + gint index; - GST_LOG_OBJECT (pool->v4l2elem, " index: %u", ret->vbuffer.index); - GST_LOG_OBJECT (pool->v4l2elem, " type: %d", ret->vbuffer.type); - GST_LOG_OBJECT (pool->v4l2elem, " bytesused: %u", ret->vbuffer.bytesused); - GST_LOG_OBJECT (pool->v4l2elem, " flags: %08x", ret->vbuffer.flags); - GST_LOG_OBJECT (pool->v4l2elem, " field: %d", ret->vbuffer.field); - GST_LOG_OBJECT (pool->v4l2elem, " memory: %d", ret->vbuffer.memory); - if (ret->vbuffer.memory == V4L2_MEMORY_MMAP) - GST_LOG_OBJECT (pool->v4l2elem, " MMAP offset: %u", - ret->vbuffer.m.offset); - GST_LOG_OBJECT (pool->v4l2elem, " length: %u", ret->vbuffer.length); - GST_LOG_OBJECT (pool->v4l2elem, " input: %u", ret->vbuffer.input); + meta = GST_V4L2_META_GET (buffer); + g_assert (meta != NULL); - ret->mmap_length = ret->vbuffer.length; - data = (guint8 *) v4l2_mmap (0, ret->vbuffer.length, - PROT_READ | PROT_WRITE, MAP_SHARED, pool->video_fd, - ret->vbuffer.m.offset); + index = meta->vbuffer.index; + GST_LOG_OBJECT (pool, + "mmap buffer %p idx %d (data %p, len %u) freed, unmapping", buffer, + index, meta->mem, meta->vbuffer.length); - if (data == MAP_FAILED) - goto mmap_failed; + v4l2_munmap (meta->mem, meta->vbuffer.length); + pool->buffers[index] = NULL; + break; + } + case GST_V4L2_IO_USERPTR: + default: + g_assert_not_reached (); + break; + } + gst_buffer_unref (buffer); +} - GST_BUFFER_DATA (ret) = data; - GST_BUFFER_SIZE (ret) = ret->vbuffer.length; +static GstFlowReturn +gst_v4l2_buffer_pool_alloc_buffer (GstBufferPool * bpool, GstBuffer ** buffer, + GstBufferPoolAcquireParams * params) +{ + GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); + GstBuffer *newbuf; + GstV4l2Meta *meta; + GstV4l2Object *obj; + GstVideoInfo *info; + guint index; + + obj = pool->obj; + info = &obj->info; + + switch (obj->mode) { + case GST_V4L2_IO_RW: + { + newbuf = + gst_buffer_new_allocate (pool->allocator, pool->size, &pool->params); + break; + } + case GST_V4L2_IO_MMAP: + { + newbuf = gst_buffer_new (); + meta = GST_V4L2_META_ADD (newbuf); + + index = pool->num_allocated; + + GST_LOG_OBJECT (pool, "creating buffer %u, %p", index, newbuf); + + meta->vbuffer.index = index; + meta->vbuffer.type = obj->type; + meta->vbuffer.memory = V4L2_MEMORY_MMAP; + + if (v4l2_ioctl (pool->video_fd, VIDIOC_QUERYBUF, &meta->vbuffer) < 0) + goto querybuf_failed; + + GST_LOG_OBJECT (pool, " index: %u", meta->vbuffer.index); + GST_LOG_OBJECT (pool, " type: %d", meta->vbuffer.type); + GST_LOG_OBJECT (pool, " bytesused: %u", meta->vbuffer.bytesused); + GST_LOG_OBJECT (pool, " flags: %08x", meta->vbuffer.flags); + GST_LOG_OBJECT (pool, " field: %d", meta->vbuffer.field); + GST_LOG_OBJECT (pool, " memory: %d", meta->vbuffer.memory); + if (meta->vbuffer.memory == V4L2_MEMORY_MMAP) + GST_LOG_OBJECT (pool, " MMAP offset: %u", meta->vbuffer.m.offset); + GST_LOG_OBJECT (pool, " length: %u", meta->vbuffer.length); + GST_LOG_OBJECT (pool, " input: %u", meta->vbuffer.input); + + meta->mem = v4l2_mmap (0, meta->vbuffer.length, + PROT_READ | PROT_WRITE, MAP_SHARED, pool->video_fd, + meta->vbuffer.m.offset); + if (meta->mem == MAP_FAILED) + goto mmap_failed; + + gst_buffer_take_memory (newbuf, -1, + gst_memory_new_wrapped (0, + meta->mem, meta->vbuffer.length, 0, meta->vbuffer.length, NULL, + NULL)); + + /* add metadata to raw video buffers */ + if (pool->add_videometa && info->finfo) { + gsize offset[GST_VIDEO_MAX_PLANES]; + gint stride[GST_VIDEO_MAX_PLANES]; + + offset[0] = 0; + stride[0] = obj->bytesperline; + + GST_DEBUG_OBJECT (pool, "adding video meta, stride %d", stride[0]); + gst_buffer_add_video_meta_full (newbuf, info->flags, + GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info), + GST_VIDEO_INFO_HEIGHT (info), GST_VIDEO_INFO_N_PLANES (info), + offset, stride); + } + break; + } + case GST_V4L2_IO_USERPTR: + default: + g_assert_not_reached (); + break; + } - GST_BUFFER_FLAG_SET (ret, GST_BUFFER_FLAG_READONLY); + pool->num_allocated++; - gst_buffer_set_caps (GST_BUFFER (ret), caps); + *buffer = newbuf; - return ret; + return GST_FLOW_OK; /* ERRORS */ querybuf_failed: @@ -207,448 +222,909 @@ querybuf_failed: gint errnosave = errno; GST_WARNING ("Failed QUERYBUF: %s", g_strerror (errnosave)); - gst_buffer_unref (GST_BUFFER (ret)); + gst_buffer_unref (newbuf); errno = errnosave; - return NULL; + return GST_FLOW_ERROR; } mmap_failed: { gint errnosave = errno; GST_WARNING ("Failed to mmap: %s", g_strerror (errnosave)); - gst_buffer_unref (GST_BUFFER (ret)); + gst_buffer_unref (newbuf); errno = errnosave; - return NULL; + return GST_FLOW_ERROR; } } +static gboolean +gst_v4l2_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config) +{ + GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); + GstV4l2Object *obj = pool->obj; + const GstCaps *caps; + guint size, min_buffers, max_buffers; + GstAllocator *allocator; + GstAllocationParams params; -/* - * GstV4l2BufferPool: - */ + GST_DEBUG_OBJECT (pool, "set config"); -static GstMiniObjectClass *buffer_pool_parent_class = NULL; + pool->add_videometa = + gst_buffer_pool_config_has_option (config, + GST_BUFFER_POOL_OPTION_VIDEO_META); -static void -gst_v4l2_buffer_pool_finalize (GstV4l2BufferPool * pool) -{ - g_mutex_free (pool->lock); - pool->lock = NULL; + if (!pool->add_videometa) { + gint stride; - g_async_queue_unref (pool->avail_buffers); - pool->avail_buffers = NULL; + /* we don't have video metadata, see if the strides are compatible */ + stride = GST_VIDEO_INFO_PLANE_STRIDE (&obj->info, 0); - if (pool->video_fd >= 0) - v4l2_close (pool->video_fd); + GST_DEBUG_OBJECT (pool, "no videometadata, checking strides %d and %u", + stride, obj->bytesperline); - if (pool->buffers) { - g_free (pool->buffers); - pool->buffers = NULL; + if (stride != obj->bytesperline) + goto missing_video_api; } - GST_MINI_OBJECT_CLASS (buffer_pool_parent_class)->finalize (GST_MINI_OBJECT - (pool)); -} + /* parse the config and keep around */ + if (!gst_buffer_pool_config_get_params (config, &caps, &size, &min_buffers, + &max_buffers)) + goto wrong_config; -static void -gst_v4l2_buffer_pool_init (GstV4l2BufferPool * pool, gpointer g_class) -{ - pool->lock = g_mutex_new (); - pool->running = FALSE; - pool->num_live_buffers = 0; -} + if (!gst_buffer_pool_config_get_allocator (config, &allocator, ¶ms)) + goto wrong_config; -static void -gst_v4l2_buffer_pool_class_init (gpointer g_class, gpointer class_data) -{ - GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class); + GST_DEBUG_OBJECT (pool, "config %" GST_PTR_FORMAT, config); - buffer_pool_parent_class = g_type_class_peek_parent (g_class); + pool->size = size; + pool->max_buffers = MAX (min_buffers, max_buffers); + pool->min_buffers = MIN (pool->max_buffers, min_buffers); + pool->params = params; - mini_object_class->finalize = (GstMiniObjectFinalizeFunction) - gst_v4l2_buffer_pool_finalize; -} + gst_buffer_pool_config_set_params (config, caps, size, min_buffers, + max_buffers); -GType -gst_v4l2_buffer_pool_get_type (void) -{ - static GType _gst_v4l2_buffer_pool_type; - - if (G_UNLIKELY (_gst_v4l2_buffer_pool_type == 0)) { - static const GTypeInfo v4l2_buffer_pool_info = { - sizeof (GstMiniObjectClass), - NULL, - NULL, - gst_v4l2_buffer_pool_class_init, - NULL, - NULL, - sizeof (GstV4l2BufferPool), - 0, - (GInstanceInitFunc) gst_v4l2_buffer_pool_init, - NULL - }; - _gst_v4l2_buffer_pool_type = g_type_register_static (GST_TYPE_MINI_OBJECT, - "GstV4l2BufferPool", &v4l2_buffer_pool_info, 0); - } - return _gst_v4l2_buffer_pool_type; -} + return GST_BUFFER_POOL_CLASS (parent_class)->set_config (bpool, config); + /* ERRORS */ +missing_video_api: + { + GST_ERROR_OBJECT (pool, "missing GstMetaVideo API in config, " + "default stride: %d, wanted stride %u", + GST_VIDEO_INFO_PLANE_STRIDE (&obj->info, 0), obj->bytesperline); + return FALSE; + } +wrong_config: + { + GST_ERROR_OBJECT (pool, "invalid config %" GST_PTR_FORMAT, config); + return FALSE; + } +} -/* this is somewhat of a hack.. but better to keep the hack in - * one place than copy/pasting it around.. - */ -static GstV4l2Object * -get_v4l2_object (GstElement * v4l2elem) +static gboolean +start_streaming (GstV4l2BufferPool * pool) { - GstV4l2Object *v4l2object = NULL; - if (GST_IS_V4L2SRC (v4l2elem)) { - v4l2object = (GST_V4L2SRC (v4l2elem))->v4l2object; -#ifdef HAVE_EXPERIMENTAL - } else if (GST_IS_V4L2SINK (v4l2elem)) { - v4l2object = (GST_V4L2SINK (v4l2elem))->v4l2object; -#endif - } else { - GST_ERROR_OBJECT (v4l2elem, "unknown v4l2 element"); + GstV4l2Object *obj = pool->obj; + + switch (obj->mode) { + case GST_V4L2_IO_RW: + break; + case GST_V4L2_IO_MMAP: + case GST_V4L2_IO_USERPTR: + GST_DEBUG_OBJECT (pool, "STREAMON"); + if (v4l2_ioctl (pool->video_fd, VIDIOC_STREAMON, &obj->type) < 0) + goto start_failed; + break; + default: + g_assert_not_reached (); + break; } - return v4l2object; -} + pool->streaming = TRUE; + return TRUE; -/** - * gst_v4l2_buffer_pool_new: - * @v4l2elem: the v4l2 element (src or sink) that owns this pool - * @fd: the video device file descriptor - * @num_buffers: the requested number of buffers in the pool - * @caps: the caps to set on the buffer - * @requeuebuf: if %TRUE, and if the pool is still in the running state, a - * buffer with no remaining references is immediately passed back to v4l2 - * (VIDIOC_QBUF), otherwise it is returned to the pool of available buffers - * (which can be accessed via gst_v4l2_buffer_pool_get(). - * - * Construct a new buffer pool. - * - * Returns: the new pool, use gst_v4l2_buffer_pool_destroy() to free resources - */ -GstV4l2BufferPool * -gst_v4l2_buffer_pool_new (GstElement * v4l2elem, gint fd, gint num_buffers, - GstCaps * caps, gboolean requeuebuf, enum v4l2_buf_type type) + /* ERRORS */ +start_failed: + { + GST_ERROR_OBJECT (pool, "error with STREAMON %d (%s)", errno, + g_strerror (errno)); + return FALSE; + } +} + +static gboolean +gst_v4l2_buffer_pool_start (GstBufferPool * bpool) { - GstV4l2BufferPool *pool; + GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); + GstV4l2Object *obj = pool->obj; gint n; struct v4l2_requestbuffers breq; + gint min_buffers, max_buffers; - pool = (GstV4l2BufferPool *) gst_mini_object_new (GST_TYPE_V4L2_BUFFER_POOL); - - pool->video_fd = v4l2_dup (fd); - if (pool->video_fd < 0) - goto dup_failed; + min_buffers = pool->min_buffers; + max_buffers = pool->max_buffers; + switch (obj->mode) { + case GST_V4L2_IO_RW: + { + break; + } + case GST_V4L2_IO_MMAP: + { + /* first, lets request buffers, and see how many we can get: */ + GST_DEBUG_OBJECT (pool, "starting, requesting %d MMAP buffers", + max_buffers); - /* first, lets request buffers, and see how many we can get: */ - GST_DEBUG_OBJECT (v4l2elem, "STREAMING, requesting %d MMAP buffers", - num_buffers); + if (max_buffers == 0) + max_buffers = 4; - memset (&breq, 0, sizeof (struct v4l2_requestbuffers)); - breq.type = type; - breq.count = num_buffers; - breq.memory = V4L2_MEMORY_MMAP; + memset (&breq, 0, sizeof (struct v4l2_requestbuffers)); + breq.type = obj->type; + breq.count = max_buffers; + breq.memory = V4L2_MEMORY_MMAP; - if (v4l2_ioctl (fd, VIDIOC_REQBUFS, &breq) < 0) - goto reqbufs_failed; + if (v4l2_ioctl (pool->video_fd, VIDIOC_REQBUFS, &breq) < 0) + goto reqbufs_failed; - GST_LOG_OBJECT (v4l2elem, " count: %u", breq.count); - GST_LOG_OBJECT (v4l2elem, " type: %d", breq.type); - GST_LOG_OBJECT (v4l2elem, " memory: %d", breq.memory); + GST_LOG_OBJECT (pool, " count: %u", breq.count); + GST_LOG_OBJECT (pool, " type: %d", breq.type); + GST_LOG_OBJECT (pool, " memory: %d", breq.memory); - if (breq.count < GST_V4L2_MIN_BUFFERS) - goto no_buffers; + if (breq.count < GST_V4L2_MIN_BUFFERS) + goto no_buffers; - if (num_buffers != breq.count) { - GST_WARNING_OBJECT (v4l2elem, "using %u buffers instead", breq.count); - num_buffers = breq.count; + if (max_buffers != breq.count) { + GST_WARNING_OBJECT (pool, "using %u buffers instead", breq.count); + max_buffers = breq.count; + } + break; + } + case GST_V4L2_IO_USERPTR: + default: + g_assert_not_reached (); + break; } - pool->v4l2elem = v4l2elem; - pool->requeuebuf = requeuebuf; - pool->type = type; - pool->buffer_count = num_buffers; - pool->buffers = g_new0 (GstV4l2Buffer *, num_buffers); - pool->avail_buffers = g_async_queue_new (); + pool->obj = obj; + pool->max_buffers = max_buffers; + pool->buffers = g_new0 (GstBuffer *, max_buffers); + pool->num_allocated = 0; + + /* now, allocate the buffers: */ + for (n = 0; n < min_buffers; n++) { + GstBuffer *buffer; - /* now, map the buffers: */ - for (n = 0; n < num_buffers; n++) { - pool->buffers[n] = gst_v4l2_buffer_new (pool, n, caps); - if (!pool->buffers[n]) + if (gst_v4l2_buffer_pool_alloc_buffer (bpool, &buffer, NULL) != GST_FLOW_OK) goto buffer_new_failed; - pool->num_live_buffers++; - g_async_queue_push (pool->avail_buffers, pool->buffers[n]); - } - return pool; + gst_v4l2_buffer_pool_release_buffer (bpool, buffer); + } - /* ERRORS */ -dup_failed: - { - gint errnosave = errno; + /* we can start capturing now, we wait for the playback case until we queued + * the first buffer */ + if (obj->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (!start_streaming (pool)) + goto start_failed; - gst_mini_object_unref (GST_MINI_OBJECT (pool)); + gst_poll_set_flushing (obj->poll, FALSE); - errno = errnosave; + return TRUE; - return NULL; - } + /* ERRORS */ reqbufs_failed: { - GstV4l2Object *v4l2object = get_v4l2_object (v4l2elem); - GST_ELEMENT_ERROR (v4l2elem, RESOURCE, READ, - (_("Could not get buffers from device '%s'."), - v4l2object->videodev), - ("error requesting %d buffers: %s", num_buffers, g_strerror (errno))); - return NULL; + GST_ERROR_OBJECT (pool, + "error requesting %d buffers: %s", max_buffers, g_strerror (errno)); + return FALSE; } no_buffers: { - GstV4l2Object *v4l2object = get_v4l2_object (v4l2elem); - GST_ELEMENT_ERROR (v4l2elem, RESOURCE, READ, - (_("Could not get enough buffers from device '%s'."), - v4l2object->videodev), - ("we received %d from device '%s', we want at least %d", - breq.count, v4l2object->videodev, GST_V4L2_MIN_BUFFERS)); - return NULL; + GST_ERROR_OBJECT (pool, + "we received %d from device '%s', we want at least %d", + breq.count, obj->videodev, GST_V4L2_MIN_BUFFERS); + return FALSE; } buffer_new_failed: { - gint errnosave = errno; + GST_ERROR_OBJECT (pool, "failed to create a buffer"); + return FALSE; + } +start_failed: + { + GST_ERROR_OBJECT (pool, "failed to start streaming"); + return FALSE; + } +} - gst_v4l2_buffer_pool_destroy (pool); +static gboolean +gst_v4l2_buffer_pool_stop (GstBufferPool * bpool) +{ + gboolean ret; + GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); + GstV4l2Object *obj = pool->obj; + guint n; + + GST_DEBUG_OBJECT (pool, "stopping pool"); + + gst_poll_set_flushing (obj->poll, TRUE); + + if (pool->streaming) { + switch (obj->mode) { + case GST_V4L2_IO_RW: + break; + case GST_V4L2_IO_MMAP: + case GST_V4L2_IO_USERPTR: + /* we actually need to sync on all queued buffers but not + * on the non-queued ones */ + GST_DEBUG_OBJECT (pool, "STREAMOFF"); + if (v4l2_ioctl (pool->video_fd, VIDIOC_STREAMOFF, &obj->type) < 0) + goto stop_failed; + break; + default: + g_assert_not_reached (); + break; + } + pool->streaming = FALSE; + } - errno = errnosave; + /* first free the buffers in the queue */ + ret = GST_BUFFER_POOL_CLASS (parent_class)->stop (bpool); - return NULL; + /* then free the remaining buffers */ + for (n = 0; n < pool->num_allocated; n++) { + if (pool->buffers[n]) + gst_v4l2_buffer_pool_free_buffer (bpool, pool->buffers[n]); + } + g_free (pool->buffers); + pool->buffers = NULL; + + return ret; + + /* ERRORS */ +stop_failed: + { + GST_ERROR_OBJECT (pool, "error with STREAMOFF %d (%s)", errno, + g_strerror (errno)); + return FALSE; } } -/** - * gst_v4l2_buffer_pool_destroy: - * @pool: the pool - * - * Free all resources in the pool and the pool itself. - */ -void -gst_v4l2_buffer_pool_destroy (GstV4l2BufferPool * pool) +static GstFlowReturn +gst_v4l2_object_poll (GstV4l2Object * v4l2object) { - gint n; + gint ret; + + if (v4l2object->can_poll_device) { + GST_LOG_OBJECT (v4l2object->element, "polling device"); + ret = gst_poll_wait (v4l2object->poll, GST_CLOCK_TIME_NONE); + if (G_UNLIKELY (ret < 0)) { + if (errno == EBUSY) + goto stopped; + if (errno == ENXIO) { + GST_WARNING_OBJECT (v4l2object->element, + "v4l2 device doesn't support polling. Disabling"); + v4l2object->can_poll_device = FALSE; + } else { + if (errno != EAGAIN && errno != EINTR) + goto select_error; + } + } + } + return GST_FLOW_OK; - GST_V4L2_BUFFER_POOL_LOCK (pool); - pool->running = FALSE; - GST_V4L2_BUFFER_POOL_UNLOCK (pool); + /* ERRORS */ +stopped: + { + GST_DEBUG ("stop called"); + return GST_FLOW_FLUSHING; + } +select_error: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, (NULL), + ("poll error %d: %s (%d)", ret, g_strerror (errno), errno)); + return GST_FLOW_ERROR; + } +} - GST_DEBUG_OBJECT (pool->v4l2elem, "destroy pool"); +static GstFlowReturn +gst_v4l2_buffer_pool_qbuf (GstV4l2BufferPool * pool, GstBuffer * buf) +{ + GstV4l2Meta *meta; + gint index; - /* after this point, no more buffers will be queued or dequeued; no buffer - * from pool->buffers that is NULL will be set to a buffer, and no buffer that - * is not NULL will be pushed out. */ + meta = GST_V4L2_META_GET (buf); + g_assert (meta != NULL); - /* miniobjects have no dispose, so they can't break ref-cycles, as buffers ref - * the pool, we need to unref the buffer to properly finalize te pool */ - for (n = 0; n < pool->buffer_count; n++) { - GstBuffer *buf; + index = meta->vbuffer.index; - GST_V4L2_BUFFER_POOL_LOCK (pool); - buf = GST_BUFFER (pool->buffers[n]); - GST_V4L2_BUFFER_POOL_UNLOCK (pool); + GST_LOG_OBJECT (pool, "enqueue buffer %p, index:%d, queued:%d", buf, + index, pool->num_queued); - if (buf) - /* we own the ref if the buffer is in pool->buffers; drop it. */ - gst_buffer_unref (buf); - } + if (pool->buffers[index] != NULL) + goto already_queued; - gst_mini_object_unref (GST_MINI_OBJECT (pool)); + if (v4l2_ioctl (pool->video_fd, VIDIOC_QBUF, &meta->vbuffer) < 0) + goto queue_failed; + + pool->buffers[index] = buf; + pool->num_queued++; + + return GST_FLOW_OK; + + /* ERRORS */ +already_queued: + { + GST_WARNING_OBJECT (pool, "the buffer was already queued"); + return GST_FLOW_ERROR; + } +queue_failed: + { + GST_WARNING_OBJECT (pool, "could not queue a buffer %d (%s)", errno, + g_strerror (errno)); + return GST_FLOW_ERROR; + } } -/** - * gst_v4l2_buffer_pool_get: - * @pool: the "this" object - * @blocking: should this call suspend until there is a buffer available - * in the buffer pool? - * - * Get an available buffer in the pool - */ -GstV4l2Buffer * -gst_v4l2_buffer_pool_get (GstV4l2BufferPool * pool, gboolean blocking) +static GstFlowReturn +gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer) { - GstV4l2Buffer *buf; + GstFlowReturn res; + GstBuffer *outbuf; + struct v4l2_buffer vbuffer; + GstV4l2Object *obj = pool->obj; + + if (obj->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + /* select works for input devices when data is available. According to the + * specs we can also poll to find out when a frame has been displayed but + * that just seems to lock up here */ + if ((res = gst_v4l2_object_poll (obj)) != GST_FLOW_OK) + goto poll_error; + } - if (blocking) { - buf = g_async_queue_pop (pool->avail_buffers); - } else { - buf = g_async_queue_try_pop (pool->avail_buffers); + memset (&vbuffer, 0x00, sizeof (vbuffer)); + vbuffer.type = obj->type; + vbuffer.memory = V4L2_MEMORY_MMAP; + + GST_LOG_OBJECT (pool, "doing DQBUF"); + if (v4l2_ioctl (pool->video_fd, VIDIOC_DQBUF, &vbuffer) < 0) + goto error; + + /* get our GstBuffer with that index from the pool, if the buffer was + * outstanding we have a serious problem. + */ + outbuf = pool->buffers[vbuffer.index]; + if (outbuf == NULL) + goto no_buffer; + + /* mark the buffer outstanding */ + pool->buffers[vbuffer.index] = NULL; + pool->num_queued--; + + GST_LOG_OBJECT (pool, + "dequeued buffer %p seq:%d (ix=%d), used %d, flags %08x, pool-queued=%d, buffer=%p", + outbuf, vbuffer.sequence, vbuffer.index, vbuffer.bytesused, vbuffer.flags, + pool->num_queued, outbuf); + + /* set top/bottom field first if v4l2_buffer has the information */ + if (vbuffer.field == V4L2_FIELD_INTERLACED_TB) { + GST_BUFFER_FLAG_SET (outbuf, GST_VIDEO_BUFFER_FLAG_TFF); + GST_BUFFER_FLAG_SET (outbuf, GST_VIDEO_BUFFER_FLAG_INTERLACED); } + if (vbuffer.field == V4L2_FIELD_INTERLACED_BT) { + GST_BUFFER_FLAG_UNSET (outbuf, GST_VIDEO_BUFFER_FLAG_TFF); + GST_BUFFER_FLAG_SET (outbuf, GST_VIDEO_BUFFER_FLAG_INTERLACED); + } + + /* this can change at every frame, esp. with jpeg */ + if (obj->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + gst_buffer_resize (outbuf, 0, vbuffer.bytesused); + else + gst_buffer_resize (outbuf, 0, vbuffer.length); + + *buffer = outbuf; - if (buf) { - GST_V4L2_BUFFER_POOL_LOCK (pool); - GST_BUFFER_SIZE (buf) = buf->vbuffer.length; - GST_BUFFER_FLAG_UNSET (buf, 0xffffffff); - GST_V4L2_BUFFER_POOL_UNLOCK (pool); + return GST_FLOW_OK; + + /* ERRORS */ +poll_error: + { + GST_DEBUG_OBJECT (pool, "poll error %s", gst_flow_get_name (res)); + return res; + } +error: + { + GST_WARNING_OBJECT (pool, + "problem dequeuing frame %d (ix=%d), pool-ct=%d, buf.flags=%d", + vbuffer.sequence, vbuffer.index, + GST_MINI_OBJECT_REFCOUNT (pool), vbuffer.flags); + + switch (errno) { + case EAGAIN: + GST_WARNING_OBJECT (pool, + "Non-blocking I/O has been selected using O_NONBLOCK and" + " no buffer was in the outgoing queue. device %s", obj->videodev); + break; + case EINVAL: + GST_ERROR_OBJECT (pool, + "The buffer type is not supported, or the index is out of bounds, " + "or no buffers have been allocated yet, or the userptr " + "or length are invalid. device %s", obj->videodev); + break; + case ENOMEM: + GST_ERROR_OBJECT (pool, + "insufficient memory to enqueue a user pointer buffer"); + break; + case EIO: + GST_INFO_OBJECT (pool, + "VIDIOC_DQBUF failed due to an internal error." + " Can also indicate temporary problems like signal loss." + " Note the driver might dequeue an (empty) buffer despite" + " returning an error, or even stop capturing." + " device %s", obj->videodev); + /* have we de-queued a buffer ? */ + if (!(vbuffer.flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE))) { + GST_DEBUG_OBJECT (pool, "reenqueing buffer"); + /* FIXME ... should we do something here? */ + } + break; + case EINTR: + GST_WARNING_OBJECT (pool, + "could not sync on a buffer on device %s", obj->videodev); + break; + default: + GST_WARNING_OBJECT (pool, + "Grabbing frame got interrupted on %s unexpectedly. %d: %s.", + obj->videodev, errno, g_strerror (errno)); + break; + } + return GST_FLOW_ERROR; } +no_buffer: + { + GST_ERROR_OBJECT (pool, "No free buffer found in the pool at index %d.", + vbuffer.index); + return GST_FLOW_ERROR; + } +} - pool->running = TRUE; +static GstFlowReturn +gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer, + GstBufferPoolAcquireParams * params) +{ + GstFlowReturn ret; + GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); + GstV4l2Object *obj = pool->obj; + + GST_DEBUG_OBJECT (pool, "acquire"); + + if (GST_BUFFER_POOL_IS_FLUSHING (bpool)) + goto flushing; + + switch (obj->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + /* capture, This function should return a buffer with new captured data */ + switch (obj->mode) { + case GST_V4L2_IO_RW: + /* take empty buffer from the pool */ + ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool, + buffer, params); + break; + + case GST_V4L2_IO_MMAP: + /* just dequeue a buffer, we basically use the queue of v4l2 as the + * storage for our buffers. This function does poll first so we can + * interrupt it fine. */ + ret = gst_v4l2_buffer_pool_dqbuf (pool, buffer); + break; + + case GST_V4L2_IO_USERPTR: + default: + g_assert_not_reached (); + break; + } + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + /* playback, This function should return an empty buffer */ + switch (obj->mode) { + case GST_V4L2_IO_RW: + /* get an empty buffer */ + ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool, + buffer, params); + break; + + case GST_V4L2_IO_MMAP: + /* get a free unqueued buffer */ + ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool, + buffer, params); + break; + + case GST_V4L2_IO_USERPTR: + default: + g_assert_not_reached (); + break; + } + break; - return buf; + default: + g_assert_not_reached (); + break; + } + return ret; + + /* ERRORS */ +flushing: + { + GST_DEBUG_OBJECT (pool, "We are flushing"); + return GST_FLOW_FLUSHING; + } } +static void +gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) +{ + GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); + GstV4l2Object *obj = pool->obj; + + GST_DEBUG_OBJECT (pool, "release buffer %p", buffer); + + switch (obj->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + /* capture, put the buffer back in the queue so that we can refill it + * later. */ + switch (obj->mode) { + case GST_V4L2_IO_RW: + /* release back in the pool */ + GST_BUFFER_POOL_CLASS (parent_class)->release_buffer (bpool, buffer); + break; + + case GST_V4L2_IO_MMAP: + /* queue back in the device */ + gst_v4l2_buffer_pool_qbuf (pool, buffer); + break; + + case GST_V4L2_IO_USERPTR: + default: + g_assert_not_reached (); + break; + } + break; -/** - * gst_v4l2_buffer_pool_qbuf: - * @pool: the pool - * @buf: the buffer to queue - * - * Queue a buffer to the driver - * - * Returns: %TRUE for success - */ -gboolean -gst_v4l2_buffer_pool_qbuf (GstV4l2BufferPool * pool, GstV4l2Buffer * buf) + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + switch (obj->mode) { + case GST_V4L2_IO_RW: + /* release back in the pool */ + GST_BUFFER_POOL_CLASS (parent_class)->release_buffer (bpool, buffer); + break; + + case GST_V4L2_IO_MMAP: + { + GstV4l2Meta *meta; + + meta = GST_V4L2_META_GET (buffer); + g_assert (meta != NULL); + + if (pool->buffers[meta->vbuffer.index] == NULL) { + GST_LOG_OBJECT (pool, "buffer not queued, putting on free list"); + /* playback, put the buffer back in the queue to refill later. */ + GST_BUFFER_POOL_CLASS (parent_class)->release_buffer (bpool, + buffer); + } else { + /* the buffer is queued in the device but maybe not played yet. We just + * leave it there and not make it available for future calls to acquire + * for now. The buffer will be dequeued and reused later. */ + GST_LOG_OBJECT (pool, "buffer is queued"); + } + break; + } + + case GST_V4L2_IO_USERPTR: + default: + g_assert_not_reached (); + break; + } + break; + + default: + g_assert_not_reached (); + break; + } +} + +static void +gst_v4l2_buffer_pool_finalize (GObject * object) { - GST_LOG_OBJECT (pool->v4l2elem, "enqueue pool buffer %d", buf->vbuffer.index); + GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (object); - if (v4l2_ioctl (pool->video_fd, VIDIOC_QBUF, &buf->vbuffer) < 0) - return FALSE; + if (pool->video_fd >= 0) + v4l2_close (pool->video_fd); - pool->num_live_buffers--; - GST_DEBUG_OBJECT (pool->v4l2elem, "num_live_buffers--: %d", - pool->num_live_buffers); + g_free (pool->buffers); - return TRUE; + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_v4l2_buffer_pool_init (GstV4l2BufferPool * pool) +{ +} + +static void +gst_v4l2_buffer_pool_class_init (GstV4l2BufferPoolClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GstBufferPoolClass *bufferpool_class = GST_BUFFER_POOL_CLASS (klass); + + object_class->finalize = gst_v4l2_buffer_pool_finalize; + + bufferpool_class->start = gst_v4l2_buffer_pool_start; + bufferpool_class->stop = gst_v4l2_buffer_pool_stop; + bufferpool_class->set_config = gst_v4l2_buffer_pool_set_config; + bufferpool_class->alloc_buffer = gst_v4l2_buffer_pool_alloc_buffer; + bufferpool_class->acquire_buffer = gst_v4l2_buffer_pool_acquire_buffer; + bufferpool_class->release_buffer = gst_v4l2_buffer_pool_release_buffer; + bufferpool_class->free_buffer = gst_v4l2_buffer_pool_free_buffer; } /** - * gst_v4l2_buffer_pool_dqbuf: - * @pool: the pool + * gst_v4l2_buffer_pool_new: + * @obj: the v4l2 object owning the pool * - * Dequeue a buffer from the driver. Some generic error handling is done in - * this function, but any error handling specific to v4l2src (capture) or - * v4l2sink (output) can be done outside this function by checking 'errno' + * Construct a new buffer pool. * - * Returns: a buffer + * Returns: the new pool, use gst_object_unref() to free resources */ -GstV4l2Buffer * -gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool) +GstBufferPool * +gst_v4l2_buffer_pool_new (GstV4l2Object * obj, GstCaps * caps) { - GstV4l2Object *v4l2object = get_v4l2_object (pool->v4l2elem); - GstV4l2Buffer *pool_buffer; - struct v4l2_buffer buffer; + GstV4l2BufferPool *pool; + GstStructure *s; + gint fd; - memset (&buffer, 0x00, sizeof (buffer)); - buffer.type = pool->type; - buffer.memory = V4L2_MEMORY_MMAP; + fd = v4l2_dup (obj->video_fd); + if (fd < 0) + goto dup_failed; + pool = (GstV4l2BufferPool *) g_object_new (GST_TYPE_V4L2_BUFFER_POOL, NULL); + pool->video_fd = fd; + pool->obj = obj; - if (v4l2_ioctl (pool->video_fd, VIDIOC_DQBUF, &buffer) >= 0) { + s = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool)); + gst_buffer_pool_config_set_params (s, caps, obj->sizeimage, 2, 0); + gst_buffer_pool_set_config (GST_BUFFER_POOL_CAST (pool), s); - GST_V4L2_BUFFER_POOL_LOCK (pool); + return GST_BUFFER_POOL (pool); - /* get our GstBuffer with that index from the pool, if the buffer was - * outstanding we have a serious problem. - */ - pool_buffer = pool->buffers[buffer.index]; + /* ERRORS */ +dup_failed: + { + GST_DEBUG ("failed to dup fd %d (%s)", errno, g_strerror (errno)); + return NULL; + } +} - if (pool_buffer == NULL) { - GST_ELEMENT_ERROR (pool->v4l2elem, RESOURCE, FAILED, - (_("Failed trying to get video frames from device '%s'."), - v4l2object->videodev), - (_("No free buffers found in the pool at index %d."), buffer.index)); - GST_V4L2_BUFFER_POOL_UNLOCK (pool); - return NULL; - } +static GstFlowReturn +gst_v4l2_do_read (GstV4l2BufferPool * pool, GstBuffer * buf) +{ + GstFlowReturn res; + GstV4l2Object *obj = pool->obj; + gint amount; + GstMapInfo map; + gint toread; - GST_LOG_OBJECT (pool->v4l2elem, - "grabbed frame %d (ix=%d), flags %08x, pool-ct=%d, buffer=%p", - buffer.sequence, buffer.index, buffer.flags, pool->num_live_buffers, - pool_buffer); + toread = obj->sizeimage; - pool->num_live_buffers++; - GST_DEBUG_OBJECT (pool->v4l2elem, "num_live_buffers++: %d", - pool->num_live_buffers); + GST_LOG_OBJECT (pool, "reading %d bytes into buffer %p", toread, buf); - /* set top/bottom field first if v4l2_buffer has the information */ - if (buffer.field == V4L2_FIELD_INTERLACED_TB) - GST_BUFFER_FLAG_SET (pool_buffer, GST_VIDEO_BUFFER_TFF); - if (buffer.field == V4L2_FIELD_INTERLACED_BT) - GST_BUFFER_FLAG_UNSET (pool_buffer, GST_VIDEO_BUFFER_TFF); + gst_buffer_map (buf, &map, GST_MAP_WRITE); - /* this can change at every frame, esp. with jpeg */ - GST_BUFFER_SIZE (pool_buffer) = buffer.bytesused; + do { + if ((res = gst_v4l2_object_poll (obj)) != GST_FLOW_OK) + goto poll_error; - GST_V4L2_BUFFER_POOL_UNLOCK (pool); + amount = v4l2_read (obj->video_fd, map.data, toread); - return pool_buffer; - } + if (amount == toread) { + break; + } else if (amount == -1) { + if (errno == EAGAIN || errno == EINTR) { + continue; + } else + goto read_error; + } else { + /* short reads can happen if a signal interrupts the read */ + continue; + } + } while (TRUE); + GST_LOG_OBJECT (pool, "read %d bytes", amount); + gst_buffer_unmap (buf, &map); + gst_buffer_resize (buf, 0, amount); - GST_WARNING_OBJECT (pool->v4l2elem, - "problem grabbing frame %d (ix=%d), pool-ct=%d, buf.flags=%d", - buffer.sequence, buffer.index, - GST_MINI_OBJECT_REFCOUNT (pool), buffer.flags); + return GST_FLOW_OK; - switch (errno) { - case EAGAIN: - GST_WARNING_OBJECT (pool->v4l2elem, - "Non-blocking I/O has been selected using O_NONBLOCK and" - " no buffer was in the outgoing queue. device %s", - v4l2object->videodev); - break; - case EINVAL: - GST_ELEMENT_ERROR (pool->v4l2elem, RESOURCE, FAILED, - (_("Failed trying to get video frames from device '%s'."), - v4l2object->videodev), - (_("The buffer type is not supported, or the index is out of bounds," - " or no buffers have been allocated yet, or the userptr" - " or length are invalid. device %s"), v4l2object->videodev)); - break; - case ENOMEM: - GST_ELEMENT_ERROR (pool->v4l2elem, RESOURCE, FAILED, - (_("Failed trying to get video frames from device '%s'. Not enough memory."), v4l2object->videodev), (_("insufficient memory to enqueue a user pointer buffer. device %s."), v4l2object->videodev)); - break; - case EIO: - GST_INFO_OBJECT (pool->v4l2elem, - "VIDIOC_DQBUF failed due to an internal error." - " Can also indicate temporary problems like signal loss." - " Note the driver might dequeue an (empty) buffer despite" - " returning an error, or even stop capturing." - " device %s", v4l2object->videodev); - /* have we de-queued a buffer ? */ - if (!(buffer.flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE))) { - GST_DEBUG_OBJECT (pool->v4l2elem, "reenqueing buffer"); - /* FIXME ... should we do something here? */ - } - break; - case EINTR: - GST_WARNING_OBJECT (pool->v4l2elem, - "could not sync on a buffer on device %s", v4l2object->videodev); - break; - default: - GST_WARNING_OBJECT (pool->v4l2elem, - "Grabbing frame got interrupted on %s unexpectedly. %d: %s.", - v4l2object->videodev, errno, g_strerror (errno)); - break; + /* ERRORS */ +poll_error: + { + GST_DEBUG ("poll error %s", gst_flow_get_name (res)); + goto cleanup; + } +read_error: + { + GST_ELEMENT_ERROR (obj->element, RESOURCE, READ, + (_("Error reading %d bytes from device '%s'."), + toread, obj->videodev), GST_ERROR_SYSTEM); + res = GST_FLOW_ERROR; + goto cleanup; + } +cleanup: + { + gst_buffer_unmap (buf, &map); + gst_buffer_resize (buf, 0, 0); + return res; } - - return NULL; } /** - * gst_v4l2_buffer_pool_available_buffers: - * @pool: the pool + * gst_v4l2_buffer_pool_process: + * @bpool: a #GstBufferPool + * @buf: a #GstBuffer * - * Check the number of buffers available to the driver, ie. buffers that - * have been QBUF'd but not yet DQBUF'd. + * Process @buf in @bpool. For capture devices, this functions fills @buf with + * data from the device. For output devices, this functions send the contents of + * @buf to the device for playback. * - * Returns: the number of buffers available. + * Returns: %GST_FLOW_OK on success. */ -gint -gst_v4l2_buffer_pool_available_buffers (GstV4l2BufferPool * pool) +GstFlowReturn +gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer * buf) { - return pool->buffer_count - pool->num_live_buffers; + GstFlowReturn ret = GST_FLOW_OK; + GstBufferPool *bpool = GST_BUFFER_POOL_CAST (pool); + GstV4l2Object *obj = pool->obj; + + GST_DEBUG_OBJECT (pool, "process buffer %p", buf); + + switch (obj->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + /* capture */ + switch (obj->mode) { + case GST_V4L2_IO_RW: + /* capture into the buffer */ + ret = gst_v4l2_do_read (pool, buf); + break; + + case GST_V4L2_IO_MMAP: + { + GstBuffer *tmp; + + if (buf->pool == bpool) + /* nothing, data was inside the buffer when we did _acquire() */ + goto done; + + /* buffer not from our pool, grab a frame and copy it into the target */ + if ((ret = gst_v4l2_buffer_pool_dqbuf (pool, &tmp)) != GST_FLOW_OK) + goto done; + + if (!gst_v4l2_object_copy (obj, buf, tmp)) + goto copy_failed; + + /* an queue the buffer again after the copy */ + if ((ret = gst_v4l2_buffer_pool_qbuf (pool, tmp)) != GST_FLOW_OK) + goto done; + break; + } + + case GST_V4L2_IO_USERPTR: + default: + g_assert_not_reached (); + break; + } + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + /* playback */ + switch (obj->mode) { + case GST_V4L2_IO_RW: + /* FIXME, do write() */ + GST_WARNING_OBJECT (pool, "implement write()"); + break; + + case GST_V4L2_IO_MMAP: + { + GstBuffer *to_queue; + + if (buf->pool == bpool) { + /* nothing, we can queue directly */ + to_queue = buf; + GST_LOG_OBJECT (pool, "processing buffer from our pool"); + } else { + GST_LOG_OBJECT (pool, "alloc buffer from our pool"); + if (!gst_buffer_pool_is_active (bpool)) { + GstStructure *config; + + /* this pool was not activated, configure and activate */ + GST_DEBUG_OBJECT (pool, "activating pool"); + + config = gst_buffer_pool_get_config (bpool); + gst_buffer_pool_config_add_option (config, + GST_BUFFER_POOL_OPTION_VIDEO_META); + gst_buffer_pool_set_config (bpool, config); + + if (!gst_buffer_pool_set_active (bpool, TRUE)) + goto activate_failed; + } + + /* this can block if all buffers are outstanding which would be + * strange because we would expect the upstream element to have + * allocated them and returned to us.. */ + ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool, + &to_queue, NULL); + if (ret != GST_FLOW_OK) + goto acquire_failed; + + /* copy into it and queue */ + if (!gst_v4l2_object_copy (obj, to_queue, buf)) + goto copy_failed; + } + + if ((ret = gst_v4l2_buffer_pool_qbuf (pool, to_queue)) != GST_FLOW_OK) + goto done; + + /* if we are not streaming yet (this is the first buffer, start + * streaming now */ + if (!pool->streaming) + if (!start_streaming (pool)) + goto start_failed; + + if (pool->num_queued == pool->num_allocated) { + /* all buffers are queued, try to dequeue one and release it back + * into the pool so that _acquire can get to it again. */ + ret = gst_v4l2_buffer_pool_dqbuf (pool, &to_queue); + if (ret != GST_FLOW_OK) + goto done; + + /* release the rendered buffer back into the pool. This wakes up any + * thread waiting for a buffer in _acquire() */ + gst_v4l2_buffer_pool_release_buffer (bpool, to_queue); + } + break; + } + + case GST_V4L2_IO_USERPTR: + default: + g_assert_not_reached (); + break; + } + break; + default: + g_assert_not_reached (); + break; + } +done: + return ret; + + /* ERRORS */ +activate_failed: + { + GST_ERROR_OBJECT (obj->element, "failed to activate pool"); + return GST_FLOW_ERROR; + } +acquire_failed: + { + GST_WARNING_OBJECT (obj->element, "failed to acquire a buffer: %s", + gst_flow_get_name (ret)); + return ret; + } +copy_failed: + { + GST_ERROR_OBJECT (obj->element, "failed to copy data"); + return GST_FLOW_ERROR; + } +start_failed: + { + GST_ERROR_OBJECT (obj->element, "failed to start streaming"); + return GST_FLOW_ERROR; + } } diff --git a/sys/v4l2/gstv4l2bufferpool.h b/sys/v4l2/gstv4l2bufferpool.h index 36ea3233a6..532a39b0c9 100644 --- a/sys/v4l2/gstv4l2bufferpool.h +++ b/sys/v4l2/gstv4l2bufferpool.h @@ -22,79 +22,73 @@ * Boston, MA 02111-1307, USA. */ -#ifndef __GSTV4L2BUFFER_H__ -#define __GSTV4L2BUFFER_H__ +#ifndef __GST_V4L2_BUFFER_POOL_H__ +#define __GST_V4L2_BUFFER_POOL_H__ #include <gst/gst.h> -#include "v4l2_calls.h" + +typedef struct _GstV4l2BufferPool GstV4l2BufferPool; +typedef struct _GstV4l2BufferPoolClass GstV4l2BufferPoolClass; +typedef struct _GstV4l2Meta GstV4l2Meta; + +#include "gstv4l2object.h" +//#include "v4l2_calls.h" GST_DEBUG_CATEGORY_EXTERN (v4l2buffer_debug); G_BEGIN_DECLS -GType gst_v4l2_buffer_get_type (void); -#define GST_TYPE_V4L2_BUFFER (gst_v4l2_buffer_get_type()) -#define GST_IS_V4L2_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_V4L2_BUFFER)) -#define GST_V4L2_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_V4L2_BUFFER, GstV4l2Buffer)) +#define GST_TYPE_V4L2_BUFFER_POOL (gst_v4l2_buffer_pool_get_type()) +#define GST_IS_V4L2_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_V4L2_BUFFER_POOL)) +#define GST_V4L2_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_V4L2_BUFFER_POOL, GstV4l2BufferPool)) +#define GST_V4L2_BUFFER_POOL_CAST(obj) ((GstV4l2BufferPool*)(obj)) -GType gst_v4l2_buffer_pool_get_type (void); -#define GST_TYPE_V4L2_BUFFER_POOL (gst_v4l2_buffer_pool_get_type()) -#define GST_IS_V4L2_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_V4L2_BUFFER_POOL)) -#define GST_V4L2_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_V4L2_BUFFER_POOL, GstV4l2BufferPool)) +struct _GstV4l2BufferPool +{ + GstBufferPool parent; + GstV4l2Object *obj; /* the v4l2 object */ + gint video_fd; /* a dup(2) of the v4l2object's video_fd */ + GstAllocator *allocator; + GstAllocationParams params; + guint size; + guint min_buffers; + guint max_buffers; + gboolean add_videometa; -typedef struct _GstV4l2BufferPool GstV4l2BufferPool; -typedef struct _GstV4l2Buffer GstV4l2Buffer; + guint num_allocated; /* number of buffers allocated by the driver */ + guint num_queued; /* number of buffers queued in the driver */ + gboolean streaming; -struct _GstV4l2BufferPool -{ - GstMiniObject parent; - - GstElement *v4l2elem; /* the v4l2 src/sink that owns us.. maybe we should be owned by v4l2object? */ - gboolean requeuebuf; /* if true, unusued buffers are automatically re-QBUF'd */ - enum v4l2_buf_type type; /* V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_OUTPUT */ + GstBuffer **buffers; +}; - GMutex *lock; - gboolean running; /* with lock */ - gint num_live_buffers; /* number of buffers not with driver */ - GAsyncQueue* avail_buffers;/* pool of available buffers, not with the driver and which aren't held outside the bufferpool */ - gint video_fd; /* a dup(2) of the v4l2object's video_fd */ - guint buffer_count; - GstV4l2Buffer **buffers; +struct _GstV4l2BufferPoolClass +{ + GstBufferPoolClass parent_class; }; -struct _GstV4l2Buffer { - GstBuffer buffer; +struct _GstV4l2Meta { + GstMeta meta; + gpointer mem; struct v4l2_buffer vbuffer; - /* warning: the size of mmap buffer and - * the actual frame-buffer can be different. */ - size_t mmap_length; - - /* FIXME: have GstV4l2Src* instead, as this has GstV4l2BufferPool* */ - /* FIXME: do we really want to fix this if GstV4l2Buffer/Pool is shared - * between v4l2src and v4l2sink?? - */ - GstV4l2BufferPool *pool; }; -void gst_v4l2_buffer_pool_destroy (GstV4l2BufferPool * pool); -GstV4l2BufferPool *gst_v4l2_buffer_pool_new (GstElement *v4l2elem, gint fd, gint num_buffers, GstCaps * caps, gboolean requeuebuf, enum v4l2_buf_type type); - +GType gst_v4l2_meta_api_get_type (void); +const GstMetaInfo * gst_v4l2_meta_get_info (void); +#define GST_V4L2_META_GET(buf) ((GstV4l2Meta *)gst_buffer_get_meta(buf,gst_v4l2_meta_api_get_type())) +#define GST_V4L2_META_ADD(buf) ((GstV4l2Meta *)gst_buffer_add_meta(buf,gst_v4l2_meta_get_info(),NULL)) -GstV4l2Buffer *gst_v4l2_buffer_pool_get (GstV4l2BufferPool *pool, gboolean blocking); -gboolean gst_v4l2_buffer_pool_qbuf (GstV4l2BufferPool *pool, GstV4l2Buffer *buf); -GstV4l2Buffer *gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool *pool); - -gint gst_v4l2_buffer_pool_available_buffers (GstV4l2BufferPool *pool); +GType gst_v4l2_buffer_pool_get_type (void); +GstBufferPool * gst_v4l2_buffer_pool_new (GstV4l2Object *obj, GstCaps *caps); -#define GST_V4L2_BUFFER_POOL_LOCK(pool) g_mutex_lock ((pool)->lock) -#define GST_V4L2_BUFFER_POOL_UNLOCK(pool) g_mutex_unlock ((pool)->lock) +GstFlowReturn gst_v4l2_buffer_pool_process (GstV4l2BufferPool * bpool, GstBuffer * buf); G_END_DECLS -#endif /* __GSTV4L2BUFFER_H__ */ +#endif /*__GST_V4L2_BUFFER_POOL_H__ */ diff --git a/sys/v4l2/gstv4l2colorbalance.c b/sys/v4l2/gstv4l2colorbalance.c index f5cde096f4..a07c9be316 100644 --- a/sys/v4l2/gstv4l2colorbalance.c +++ b/sys/v4l2/gstv4l2colorbalance.c @@ -29,14 +29,9 @@ #include "gstv4l2colorbalance.h" #include "gstv4l2object.h" -GST_BOILERPLATE (GstV4l2ColorBalanceChannel, - gst_v4l2_color_balance_channel, - GstColorBalanceChannel, GST_TYPE_COLOR_BALANCE_CHANNEL); - -static void -gst_v4l2_color_balance_channel_base_init (gpointer g_class) -{ -} +#define gst_v4l2_color_balance_channel_parent_class parent_class +G_DEFINE_TYPE (GstV4l2ColorBalanceChannel, + gst_v4l2_color_balance_channel, GST_TYPE_COLOR_BALANCE_CHANNEL); static void gst_v4l2_color_balance_channel_class_init (GstV4l2ColorBalanceChannelClass * @@ -45,8 +40,7 @@ gst_v4l2_color_balance_channel_class_init (GstV4l2ColorBalanceChannelClass * } static void -gst_v4l2_color_balance_channel_init (GstV4l2ColorBalanceChannel * channel, - GstV4l2ColorBalanceChannelClass * klass) +gst_v4l2_color_balance_channel_init (GstV4l2ColorBalanceChannel * channel) { channel->id = (guint32) - 1; } diff --git a/sys/v4l2/gstv4l2colorbalance.h b/sys/v4l2/gstv4l2colorbalance.h index 9e183f0144..7bf47e1fb4 100644 --- a/sys/v4l2/gstv4l2colorbalance.h +++ b/sys/v4l2/gstv4l2colorbalance.h @@ -25,7 +25,7 @@ #define __GST_V4L2_COLOR_BALANCE_H__ #include <gst/gst.h> -#include <gst/interfaces/colorbalance.h> +#include <gst/video/colorbalance.h> #include "v4l2_calls.h" G_BEGIN_DECLS @@ -90,15 +90,20 @@ interface_as_function ## _color_balance_get_value (GstColorBalance * balance, return gst_v4l2_color_balance_get_value(this->v4l2object, channel); \ } \ \ -static void \ -interface_as_function ## _color_balance_interface_init (GstColorBalanceClass * klass) \ +static GstColorBalanceType \ +interface_as_function ## _color_balance_get_balance_type (GstColorBalance * balance) \ { \ - GST_COLOR_BALANCE_TYPE (klass) = GST_COLOR_BALANCE_HARDWARE; \ + return GST_COLOR_BALANCE_HARDWARE; \ +} \ \ +static void \ +interface_as_function ## _color_balance_interface_init (GstColorBalanceInterface * iface) \ +{ \ /* default virtual functions */ \ - klass->list_channels = interface_as_function ## _color_balance_list_channels; \ - klass->set_value = interface_as_function ## _color_balance_set_value; \ - klass->get_value = interface_as_function ## _color_balance_get_value; \ + iface->list_channels = interface_as_function ## _color_balance_list_channels; \ + iface->set_value = interface_as_function ## _color_balance_set_value; \ + iface->get_value = interface_as_function ## _color_balance_get_value; \ + iface->get_balance_type = interface_as_function ## _color_balance_get_balance_type; \ } \ #endif /* __GST_V4L2_COLOR_BALANCE_H__ */ diff --git a/sys/v4l2/gstv4l2object.c b/sys/v4l2/gstv4l2object.c index 6ff5ab4a47..6bcbf16b89 100644 --- a/sys/v4l2/gstv4l2object.c +++ b/sys/v4l2/gstv4l2object.c @@ -39,12 +39,14 @@ #include "v4l2_calls.h" #include "gstv4l2tuner.h" #ifdef HAVE_XVIDEO -#include "gstv4l2xoverlay.h" +#include "gstv4l2videooverlay.h" #endif #include "gstv4l2colorbalance.h" #include "gst/gst-i18n-plugin.h" +#include <gst/video/video.h> + /* videodev2.h is not versioned and we can't easily check for the presence * of enum values at compile time, but the V4L2_CAP_VIDEO_OUTPUT_OVERLAY define * was added in the same commit as V4L2_FIELD_INTERLACED_{TB,BT} (b2787845) */ @@ -54,15 +56,16 @@ #endif GST_DEBUG_CATEGORY_EXTERN (v4l2_debug); +GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE); #define GST_CAT_DEFAULT v4l2_debug - #define DEFAULT_PROP_DEVICE_NAME NULL #define DEFAULT_PROP_DEVICE_FD -1 #define DEFAULT_PROP_FLAGS 0 #define DEFAULT_PROP_TV_NORM 0 #define DEFAULT_PROP_CHANNEL NULL #define DEFAULT_PROP_FREQUENCY 0 +#define DEFAULT_PROP_IO_MODE GST_V4L2_IO_AUTO enum { @@ -70,6 +73,7 @@ enum V4L2_STD_OBJECT_PROPS, }; +#if 0 G_LOCK_DEFINE_STATIC (probe_lock); const GList * @@ -78,8 +82,6 @@ gst_v4l2_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? */ G_LOCK (probe_lock); if (!list) { @@ -293,6 +295,7 @@ gst_v4l2_probe_get_values (GstPropertyProbe * probe, return array; } +#endif #define GST_TYPE_V4L2_DEVICE_FLAGS (gst_v4l2_device_get_type ()) static GType @@ -374,6 +377,26 @@ gst_v4l2_tv_norm_get_type (void) return v4l2_tv_norm; } +#define GST_TYPE_V4L2_IO_MODE (gst_v4l2_io_mode_get_type ()) +static GType +gst_v4l2_io_mode_get_type (void) +{ + static GType v4l2_io_mode = 0; + + if (!v4l2_io_mode) { + static const GEnumValue io_modes[] = { + {GST_V4L2_IO_AUTO, "GST_V4L2_IO_AUTO", "auto"}, + {GST_V4L2_IO_RW, "GST_V4L2_IO_RW", "rw"}, + {GST_V4L2_IO_MMAP, "GST_V4L2_IO_MMAP", "mmap"}, + {GST_V4L2_IO_USERPTR, "GST_V4L2_IO_USERPTR", "userptr"}, + + {0, NULL, NULL} + }; + v4l2_io_mode = g_enum_register_static ("GstV4l2IOMode", io_modes); + } + return v4l2_io_mode; +} + void gst_v4l2_object_install_properties_helper (GObjectClass * gobject_class, const char *default_device) @@ -455,6 +478,17 @@ gst_v4l2_object_install_properties_helper (GObjectClass * gobject_class, "video standard", GST_TYPE_V4L2_TV_NORM, DEFAULT_PROP_TV_NORM, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstV4l2Src:io-mode + * + * IO Mode + */ + g_object_class_install_property (gobject_class, PROP_IO_MODE, + g_param_spec_enum ("io-mode", "IO mode", + "I/O mode", + GST_TYPE_V4L2_IO_MODE, DEFAULT_PROP_IO_MODE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } GstV4l2Object * @@ -482,7 +516,7 @@ gst_v4l2_object_new (GstElement * element, v4l2object->video_fd = -1; v4l2object->poll = gst_poll_new (TRUE); - v4l2object->buffer = NULL; + v4l2object->active = FALSE; v4l2object->videodev = g_strdup (default_device); v4l2object->norms = NULL; @@ -616,6 +650,9 @@ gst_v4l2_object_set_property_helper (GstV4l2Object * v4l2object, } break; #endif + case PROP_IO_MODE: + v4l2object->req_mode = g_value_get_enum (value); + break; default: return FALSE; break; @@ -689,6 +726,9 @@ gst_v4l2_object_get_property_helper (GstV4l2Object * v4l2object, case PROP_TV_NORM: g_value_set_enum (value, v4l2object->tv_norm); break; + case PROP_IO_MODE: + g_value_set_enum (value, v4l2object->req_mode); + break; default: return FALSE; break; @@ -755,7 +795,7 @@ gst_v4l2_set_defaults (GstV4l2Object * v4l2object) } gboolean -gst_v4l2_object_start (GstV4l2Object * v4l2object) +gst_v4l2_object_open (GstV4l2Object * v4l2object) { if (gst_v4l2_open (v4l2object)) gst_v4l2_set_defaults (v4l2object); @@ -763,17 +803,17 @@ gst_v4l2_object_start (GstV4l2Object * v4l2object) return FALSE; #ifdef HAVE_XVIDEO - gst_v4l2_xoverlay_start (v4l2object); + gst_v4l2_video_overlay_start (v4l2object); #endif return TRUE; } gboolean -gst_v4l2_object_stop (GstV4l2Object * v4l2object) +gst_v4l2_object_close (GstV4l2Object * v4l2object) { #ifdef HAVE_XVIDEO - gst_v4l2_xoverlay_stop (v4l2object); + gst_v4l2_video_overlay_stop (v4l2object); #endif if (!gst_v4l2_close (v4l2object)) @@ -1153,99 +1193,24 @@ gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc) case V4L2_PIX_FMT_PJPG: /* Progressive-JPEG */ #endif case V4L2_PIX_FMT_JPEG: /* JFIF JPEG */ - structure = gst_structure_new ("image/jpeg", NULL); + structure = gst_structure_new_empty ("image/jpeg"); + break; + case V4L2_PIX_FMT_YYUV: /* 16 YUV 4:2:2 */ + case V4L2_PIX_FMT_HI240: /* 8 8-bit color */ + /* FIXME: get correct fourccs here */ break; case V4L2_PIX_FMT_RGB332: - case V4L2_PIX_FMT_RGB555: case V4L2_PIX_FMT_RGB555X: - case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_RGB565X: + /* FIXME: get correct fourccs here */ + break; + case V4L2_PIX_FMT_GREY: /* 8 Greyscale */ + case V4L2_PIX_FMT_RGB555: + case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: case V4L2_PIX_FMT_RGB32: - case V4L2_PIX_FMT_BGR32:{ - guint depth = 0, bpp = 0; - - gint endianness = 0; - - guint32 r_mask = 0, b_mask = 0, g_mask = 0; - - switch (fourcc) { - case V4L2_PIX_FMT_RGB332: - bpp = depth = 8; - endianness = G_BYTE_ORDER; /* 'like, whatever' */ - r_mask = 0xe0; - g_mask = 0x1c; - b_mask = 0x03; - break; - case V4L2_PIX_FMT_RGB555: - case V4L2_PIX_FMT_RGB555X: - bpp = 16; - depth = 15; - endianness = - fourcc == V4L2_PIX_FMT_RGB555X ? G_BIG_ENDIAN : G_LITTLE_ENDIAN; - r_mask = 0x7c00; - g_mask = 0x03e0; - b_mask = 0x001f; - break; - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - bpp = depth = 16; - endianness = - fourcc == V4L2_PIX_FMT_RGB565X ? G_BIG_ENDIAN : G_LITTLE_ENDIAN; - r_mask = 0xf800; - g_mask = 0x07e0; - b_mask = 0x001f; - break; - case V4L2_PIX_FMT_RGB24: - bpp = depth = 24; - endianness = G_BIG_ENDIAN; - r_mask = 0xff0000; - g_mask = 0x00ff00; - b_mask = 0x0000ff; - break; - case V4L2_PIX_FMT_BGR24: - bpp = depth = 24; - endianness = G_BIG_ENDIAN; - r_mask = 0x0000ff; - g_mask = 0x00ff00; - b_mask = 0xff0000; - break; - case V4L2_PIX_FMT_RGB32: - bpp = depth = 32; - endianness = G_BIG_ENDIAN; - r_mask = 0xff000000; - g_mask = 0x00ff0000; - b_mask = 0x0000ff00; - break; - case V4L2_PIX_FMT_BGR32: - bpp = depth = 32; - endianness = G_BIG_ENDIAN; - r_mask = 0x000000ff; - g_mask = 0x0000ff00; - b_mask = 0x00ff0000; - break; - default: - g_assert_not_reached (); - break; - } - structure = gst_structure_new ("video/x-raw-rgb", - "bpp", G_TYPE_INT, bpp, - "depth", G_TYPE_INT, depth, - "red_mask", G_TYPE_INT, r_mask, - "green_mask", G_TYPE_INT, g_mask, - "blue_mask", G_TYPE_INT, b_mask, - "endianness", G_TYPE_INT, endianness, NULL); - break; - } - case V4L2_PIX_FMT_GREY: /* 8 Greyscale */ - structure = gst_structure_new ("video/x-raw-gray", - "bpp", G_TYPE_INT, 8, NULL); - break; - case V4L2_PIX_FMT_YYUV: /* 16 YUV 4:2:2 */ - case V4L2_PIX_FMT_HI240: /* 8 8-bit color */ - /* FIXME: get correct fourccs here */ - break; + case V4L2_PIX_FMT_BGR32: case V4L2_PIX_FMT_NV12: /* 12 Y/CbCr 4:2:0 */ case V4L2_PIX_FMT_NV21: /* 12 Y/CrCb 4:2:0 */ case V4L2_PIX_FMT_YVU410: @@ -1254,59 +1219,84 @@ gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc) case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_UYVY: +#if 0 case V4L2_PIX_FMT_Y41P: +#endif case V4L2_PIX_FMT_YUV422P: #ifdef V4L2_PIX_FMT_YVYU case V4L2_PIX_FMT_YVYU: #endif case V4L2_PIX_FMT_YUV411P:{ - guint32 fcc = 0; + GstVideoFormat format; switch (fourcc) { + case V4L2_PIX_FMT_GREY: /* 8 Greyscale */ + format = GST_VIDEO_FORMAT_GRAY8; + break; + case V4L2_PIX_FMT_RGB555: + format = GST_VIDEO_FORMAT_RGB15; + break; + case V4L2_PIX_FMT_RGB565: + format = GST_VIDEO_FORMAT_RGB16; + break; + case V4L2_PIX_FMT_RGB24: + format = GST_VIDEO_FORMAT_RGB; + break; + case V4L2_PIX_FMT_BGR24: + format = GST_VIDEO_FORMAT_BGR; + break; + case V4L2_PIX_FMT_RGB32: + format = GST_VIDEO_FORMAT_RGBx; + break; + case V4L2_PIX_FMT_BGR32: + format = GST_VIDEO_FORMAT_BGRx; + break; case V4L2_PIX_FMT_NV12: - fcc = GST_MAKE_FOURCC ('N', 'V', '1', '2'); + format = GST_VIDEO_FORMAT_NV12; break; case V4L2_PIX_FMT_NV21: - fcc = GST_MAKE_FOURCC ('N', 'V', '2', '1'); + format = GST_VIDEO_FORMAT_NV21; break; case V4L2_PIX_FMT_YVU410: - fcc = GST_MAKE_FOURCC ('Y', 'V', 'U', '9'); + format = GST_VIDEO_FORMAT_YVU9; break; case V4L2_PIX_FMT_YUV410: - fcc = GST_MAKE_FOURCC ('Y', 'U', 'V', '9'); + format = GST_VIDEO_FORMAT_YUV9; break; case V4L2_PIX_FMT_YUV420: - fcc = GST_MAKE_FOURCC ('I', '4', '2', '0'); + format = GST_VIDEO_FORMAT_I420; break; case V4L2_PIX_FMT_YUYV: - fcc = GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'); + format = GST_VIDEO_FORMAT_YUY2; break; case V4L2_PIX_FMT_YVU420: - fcc = GST_MAKE_FOURCC ('Y', 'V', '1', '2'); + format = GST_VIDEO_FORMAT_YV12; break; case V4L2_PIX_FMT_UYVY: - fcc = GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'); + format = GST_VIDEO_FORMAT_UYVY; break; +#if 0 case V4L2_PIX_FMT_Y41P: - fcc = GST_MAKE_FOURCC ('Y', '4', '1', 'P'); + format = GST_VIDEO_FORMAT_Y41P; break; +#endif case V4L2_PIX_FMT_YUV411P: - fcc = GST_MAKE_FOURCC ('Y', '4', '1', 'B'); + format = GST_VIDEO_FORMAT_Y41B; break; case V4L2_PIX_FMT_YUV422P: - fcc = GST_MAKE_FOURCC ('Y', '4', '2', 'B'); + format = GST_VIDEO_FORMAT_Y42B; break; #ifdef V4L2_PIX_FMT_YVYU case V4L2_PIX_FMT_YVYU: - fcc = GST_MAKE_FOURCC ('Y', 'V', 'Y', 'U'); + format = GST_VIDEO_FORMAT_YVYU; break; #endif default: g_assert_not_reached (); break; } - structure = gst_structure_new ("video/x-raw-yuv", - "format", GST_TYPE_FOURCC, fcc, NULL); + structure = gst_structure_new ("video/x-raw", + "format", G_TYPE_STRING, gst_video_format_to_string (format), NULL); break; } case V4L2_PIX_FMT_DV: @@ -1315,28 +1305,28 @@ gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc) NULL); break; case V4L2_PIX_FMT_MPEG: /* MPEG */ - structure = gst_structure_new ("video/mpegts", NULL); + structure = gst_structure_new_empty ("video/mpegts"); break; case V4L2_PIX_FMT_WNVA: /* Winnov hw compres */ break; #ifdef V4L2_PIX_FMT_SBGGR8 case V4L2_PIX_FMT_SBGGR8: - structure = gst_structure_new ("video/x-raw-bayer", NULL); + structure = gst_structure_new_empty ("video/x-bayer"); break; #endif #ifdef V4L2_PIX_FMT_SN9C10X case V4L2_PIX_FMT_SN9C10X: - structure = gst_structure_new ("video/x-sonix", NULL); + structure = gst_structure_new_empty ("video/x-sonix"); break; #endif #ifdef V4L2_PIX_FMT_PWC1 case V4L2_PIX_FMT_PWC1: - structure = gst_structure_new ("video/x-pwc1", NULL); + structure = gst_structure_new_empty ("video/x-pwc1"); break; #endif #ifdef V4L2_PIX_FMT_PWC2 case V4L2_PIX_FMT_PWC2: - structure = gst_structure_new ("video/x-pwc2", NULL); + structure = gst_structure_new_empty ("video/x-pwc2"); break; #endif default: @@ -1387,160 +1377,181 @@ gst_v4l2_object_get_all_caps (void) * @fps_n/@fps_d: location for framerate * @size: location for expected size of the frame or 0 if unknown */ -gboolean +static gboolean gst_v4l2_object_get_caps_info (GstV4l2Object * v4l2object, GstCaps * caps, - struct v4l2_fmtdesc ** format, gint * w, gint * h, - gboolean * interlaced, guint * fps_n, guint * fps_d, guint * size) + struct v4l2_fmtdesc **format, GstVideoInfo * info) { GstStructure *structure; - const GValue *framerate; guint32 fourcc; const gchar *mimetype; - guint outsize; + struct v4l2_fmtdesc *fmt; /* default unknown values */ fourcc = 0; - outsize = 0; structure = gst_caps_get_structure (caps, 0); mimetype = gst_structure_get_name (structure); - if (strcmp (mimetype, "video/mpegts") == 0) { - fourcc = V4L2_PIX_FMT_MPEG; - *fps_n = 0; - *fps_d = 1; - goto done; - } - - if (!gst_structure_get_int (structure, "width", w)) - return FALSE; - - if (!gst_structure_get_int (structure, "height", h)) - return FALSE; - - if (!gst_structure_get_boolean (structure, "interlaced", interlaced)) - *interlaced = FALSE; + if (g_str_equal (mimetype, "video/x-raw")) { + /* raw caps, parse into video info */ + if (!gst_video_info_from_caps (info, caps)) + goto invalid_format; - framerate = gst_structure_get_value (structure, "framerate"); - if (!framerate) - return FALSE; - - *fps_n = gst_value_get_fraction_numerator (framerate); - *fps_d = gst_value_get_fraction_denominator (framerate); - - if (!strcmp (mimetype, "video/x-raw-yuv")) { - gst_structure_get_fourcc (structure, "format", &fourcc); - - switch (fourcc) { - case GST_MAKE_FOURCC ('I', '4', '2', '0'): - case GST_MAKE_FOURCC ('I', 'Y', 'U', 'V'): + switch (GST_VIDEO_INFO_FORMAT (info)) { + case GST_VIDEO_FORMAT_I420: fourcc = V4L2_PIX_FMT_YUV420; - outsize = GST_ROUND_UP_4 (*w) * GST_ROUND_UP_2 (*h); - outsize += 2 * ((GST_ROUND_UP_8 (*w) / 2) * (GST_ROUND_UP_2 (*h) / 2)); break; - case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'): + case GST_VIDEO_FORMAT_YUY2: fourcc = V4L2_PIX_FMT_YUYV; - outsize = (GST_ROUND_UP_2 (*w) * 2) * *h; break; - case GST_MAKE_FOURCC ('Y', '4', '1', 'P'): +#if 0 + case GST_VIDEO_FORMAT_Y41P: fourcc = V4L2_PIX_FMT_Y41P; - outsize = (GST_ROUND_UP_2 (*w) * 2) * *h; break; - case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'): +#endif + case GST_VIDEO_FORMAT_UYVY: fourcc = V4L2_PIX_FMT_UYVY; - outsize = (GST_ROUND_UP_2 (*w) * 2) * *h; break; - case GST_MAKE_FOURCC ('Y', 'V', '1', '2'): + case GST_VIDEO_FORMAT_YV12: fourcc = V4L2_PIX_FMT_YVU420; - outsize = GST_ROUND_UP_4 (*w) * GST_ROUND_UP_2 (*h); - outsize += 2 * ((GST_ROUND_UP_8 (*w) / 2) * (GST_ROUND_UP_2 (*h) / 2)); break; - case GST_MAKE_FOURCC ('Y', '4', '1', 'B'): + case GST_VIDEO_FORMAT_Y41B: fourcc = V4L2_PIX_FMT_YUV411P; - outsize = GST_ROUND_UP_4 (*w) * *h; - outsize += 2 * ((GST_ROUND_UP_8 (*w) / 4) * *h); break; - case GST_MAKE_FOURCC ('Y', '4', '2', 'B'): + case GST_VIDEO_FORMAT_Y42B: fourcc = V4L2_PIX_FMT_YUV422P; - outsize = GST_ROUND_UP_4 (*w) * *h; - outsize += 2 * ((GST_ROUND_UP_8 (*w) / 2) * *h); break; - case GST_MAKE_FOURCC ('N', 'V', '1', '2'): + case GST_VIDEO_FORMAT_NV12: fourcc = V4L2_PIX_FMT_NV12; - outsize = GST_ROUND_UP_4 (*w) * GST_ROUND_UP_2 (*h); - outsize += (GST_ROUND_UP_4 (*w) * *h) / 2; break; - case GST_MAKE_FOURCC ('N', 'V', '2', '1'): + case GST_VIDEO_FORMAT_NV21: fourcc = V4L2_PIX_FMT_NV21; - outsize = GST_ROUND_UP_4 (*w) * GST_ROUND_UP_2 (*h); - outsize += (GST_ROUND_UP_4 (*w) * *h) / 2; break; #ifdef V4L2_PIX_FMT_YVYU - case GST_MAKE_FOURCC ('Y', 'V', 'Y', 'U'): + case GST_VIDEO_FORMAT_YVYU: fourcc = V4L2_PIX_FMT_YVYU; - outsize = (GST_ROUND_UP_2 (*w) * 2) * *h; break; #endif - } - } else if (!strcmp (mimetype, "video/x-raw-rgb")) { - gint depth, endianness, r_mask; - - gst_structure_get_int (structure, "depth", &depth); - gst_structure_get_int (structure, "endianness", &endianness); - gst_structure_get_int (structure, "red_mask", &r_mask); - - switch (depth) { - case 8: - fourcc = V4L2_PIX_FMT_RGB332; + case GST_VIDEO_FORMAT_RGB15: + fourcc = V4L2_PIX_FMT_RGB555; + break; + case GST_VIDEO_FORMAT_RGB16: + fourcc = V4L2_PIX_FMT_RGB565; break; - case 15: - fourcc = (endianness == G_LITTLE_ENDIAN) ? - V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB555X; + case GST_VIDEO_FORMAT_RGB: + fourcc = V4L2_PIX_FMT_RGB24; break; - case 16: - fourcc = (endianness == G_LITTLE_ENDIAN) ? - V4L2_PIX_FMT_RGB565 : V4L2_PIX_FMT_RGB565X; + case GST_VIDEO_FORMAT_BGR: + fourcc = V4L2_PIX_FMT_BGR24; break; - case 24: - fourcc = (r_mask == 0xFF) ? V4L2_PIX_FMT_BGR24 : V4L2_PIX_FMT_RGB24; + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_RGBA: + fourcc = V4L2_PIX_FMT_RGB32; break; - case 32: - fourcc = (r_mask == 0xFF) ? V4L2_PIX_FMT_BGR32 : V4L2_PIX_FMT_RGB32; + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_BGRA: + fourcc = V4L2_PIX_FMT_BGR32; + break; + case GST_VIDEO_FORMAT_GRAY8: + fourcc = V4L2_PIX_FMT_GREY; + default: break; } - } else if (strcmp (mimetype, "video/x-dv") == 0) { - fourcc = V4L2_PIX_FMT_DV; - } else if (strcmp (mimetype, "image/jpeg") == 0) { - fourcc = V4L2_PIX_FMT_JPEG; + } else { + gboolean dimensions = TRUE; + + /* no video caps, construct videoinfo ourselves */ + gst_video_info_init (info); + + if (g_str_equal (mimetype, "video/mpegts")) { + fourcc = V4L2_PIX_FMT_MPEG; + dimensions = FALSE; + } else if (g_str_equal (mimetype, "video/x-dv")) { + fourcc = V4L2_PIX_FMT_DV; + } else if (g_str_equal (mimetype, "image/jpeg")) { + fourcc = V4L2_PIX_FMT_JPEG; #ifdef V4L2_PIX_FMT_SBGGR8 - } else if (strcmp (mimetype, "video/x-raw-bayer") == 0) { - fourcc = V4L2_PIX_FMT_SBGGR8; + } else if (g_str_equal (mimetype, "video/x-bayer")) { + fourcc = V4L2_PIX_FMT_SBGGR8; #endif #ifdef V4L2_PIX_FMT_SN9C10X - } else if (strcmp (mimetype, "video/x-sonix") == 0) { - fourcc = V4L2_PIX_FMT_SN9C10X; + } else if (g_str_equal (mimetype, "video/x-sonix")) { + fourcc = V4L2_PIX_FMT_SN9C10X; #endif #ifdef V4L2_PIX_FMT_PWC1 - } else if (strcmp (mimetype, "video/x-pwc1") == 0) { - fourcc = V4L2_PIX_FMT_PWC1; + } else if (g_str_equal (mimetype, "video/x-pwc1")) { + fourcc = V4L2_PIX_FMT_PWC1; #endif #ifdef V4L2_PIX_FMT_PWC2 - } else if (strcmp (mimetype, "video/x-pwc2") == 0) { - fourcc = V4L2_PIX_FMT_PWC2; + } else if (g_str_equal (mimetype, "video/x-pwc2")) { + fourcc = V4L2_PIX_FMT_PWC2; + } #endif - } else if (strcmp (mimetype, "video/x-raw-gray") == 0) { - fourcc = V4L2_PIX_FMT_GREY; + + if (dimensions) { + const gchar *interlace_mode; + + if (!gst_structure_get_int (structure, "width", &info->width)) + goto no_width; + + if (!gst_structure_get_int (structure, "height", &info->height)) + goto no_height; + + interlace_mode = gst_structure_get_string (structure, "interlace-mode"); + if (g_str_equal (interlace_mode, "progressive")) { + info->interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE; + } else { + info->interlace_mode = GST_VIDEO_INTERLACE_MODE_MIXED; + } + if (!gst_structure_get_fraction (structure, "framerate", &info->fps_n, + &info->fps_d)) + goto no_framerate; + } } if (fourcc == 0) - return FALSE; + goto unhandled_format; -done: - *format = gst_v4l2_object_get_format_from_fourcc (v4l2object, fourcc); - *size = outsize; + fmt = gst_v4l2_object_get_format_from_fourcc (v4l2object, fourcc); + if (fmt == NULL) + goto unsupported_format; + + *format = fmt; return TRUE; + + /* ERRORS */ +no_width: + { + GST_DEBUG_OBJECT (v4l2object, "no width"); + return FALSE; + } +no_height: + { + GST_DEBUG_OBJECT (v4l2object, "no height"); + return FALSE; + } +no_framerate: + { + GST_DEBUG_OBJECT (v4l2object, "no framerate"); + return FALSE; + } +invalid_format: + { + GST_DEBUG_OBJECT (v4l2object, "invalid format"); + return FALSE; + } +unhandled_format: + { + GST_DEBUG_OBJECT (v4l2object, "unhandled format"); + return FALSE; + } +unsupported_format: + { + GST_DEBUG_OBJECT (v4l2object, "unsupported format"); + return FALSE; + } } @@ -1727,7 +1738,7 @@ return_data: s = gst_structure_copy (template); gst_structure_set (s, "width", G_TYPE_INT, (gint) width, "height", G_TYPE_INT, (gint) height, - "interlaced", G_TYPE_BOOLEAN, interlaced, + "interlace-mode", G_TYPE_STRING, (interlaced ? "mixed" : "progressive"), "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL); if (G_IS_VALUE (&rates)) { @@ -1790,7 +1801,7 @@ gst_v4l2_object_probe_caps_for_format (GstV4l2Object * v4l2object, guint32 w, h; if (pixelformat == GST_MAKE_FOURCC ('M', 'P', 'E', 'G')) - return gst_caps_new_simple ("video/mpegts", NULL); + return gst_caps_new_empty_simple ("video/mpegts"); memset (&size, 0, sizeof (struct v4l2_frmsizeenum)); size.index = 0; @@ -1988,9 +1999,10 @@ default_frame_sizes: else gst_structure_set (tmp, "height", GST_TYPE_INT_RANGE, min_h, max_h, NULL); - gst_structure_set (tmp, "interlaced", G_TYPE_BOOLEAN, interlaced, NULL); - gst_structure_set (tmp, "pixel-aspect-ratio", - GST_TYPE_FRACTION, 1, 1, NULL); + gst_structure_set (tmp, "interlace-mode", G_TYPE_STRING, + (interlaced ? "mixed" : "progressive"), NULL); + gst_structure_set (tmp, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, + NULL); gst_caps_append_structure (ret, tmp); @@ -2103,16 +2115,102 @@ error: return FALSE; } +static gboolean +gst_v4l2_object_setup_pool (GstV4l2Object * v4l2object, GstCaps * caps) +{ + GstV4l2IOMode mode; + + GST_DEBUG_OBJECT (v4l2object->element, "initializing the capture system"); + + GST_V4L2_CHECK_OPEN (v4l2object); + GST_V4L2_CHECK_NOT_ACTIVE (v4l2object); + + /* find transport */ + mode = v4l2object->req_mode; + + if (v4l2object->vcap.capabilities & V4L2_CAP_READWRITE) { + if (v4l2object->req_mode == GST_V4L2_IO_AUTO) + mode = GST_V4L2_IO_RW; + } else if (v4l2object->req_mode == GST_V4L2_IO_RW) + goto method_not_supported; + + if (v4l2object->vcap.capabilities & V4L2_CAP_STREAMING) { + if (v4l2object->req_mode == GST_V4L2_IO_AUTO) + mode = GST_V4L2_IO_MMAP; + } else if (v4l2object->req_mode == GST_V4L2_IO_MMAP) + goto method_not_supported; + + /* if still no transport selected, error out */ + if (mode == GST_V4L2_IO_AUTO) + goto no_supported_capture_method; + + GST_INFO_OBJECT (v4l2object->element, "accessing buffers via mode %d", mode); + v4l2object->mode = mode; + + /* Map the buffers */ + GST_LOG_OBJECT (v4l2object->element, "initiating buffer pool"); + + if (!(v4l2object->pool = gst_v4l2_buffer_pool_new (v4l2object, caps))) + goto buffer_pool_new_failed; + + GST_V4L2_SET_ACTIVE (v4l2object); + + return TRUE; + + /* ERRORS */ +buffer_pool_new_failed: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, + (_("Could not map buffers from device '%s'"), + v4l2object->videodev), + ("Failed to create buffer pool: %s", g_strerror (errno))); + return FALSE; + } +method_not_supported: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, + (_("The driver of device '%s' does not support the IO method %d"), + v4l2object->videodev, mode), (NULL)); + return FALSE; + } +no_supported_capture_method: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, + (_("The driver of device '%s' does not support any known IO " + "method."), v4l2object->videodev), (NULL)); + return FALSE; + } +} + + +/* Note about fraction simplification + * * n1/d1 == n2/d2 is also written as n1 == ( n2 * d1 ) / d2 + * */ +#define fractions_are_equal(n1,d1,n2,d2) ((n1) == gst_util_uint64_scale_int((n2), (d1), (d2))) gboolean -gst_v4l2_object_set_format (GstV4l2Object * v4l2object, guint32 pixelformat, - guint32 width, guint32 height, gboolean interlaced) +gst_v4l2_object_set_format (GstV4l2Object * v4l2object, GstCaps * caps) { gint fd = v4l2object->video_fd; struct v4l2_format format; + struct v4l2_streamparm streamparm; enum v4l2_field field; - - if (interlaced) { + guint32 pixelformat; + struct v4l2_fmtdesc *fmtdesc; + GstVideoInfo info; + gint width, height, fps_n, fps_d, stride; + + if (!gst_v4l2_object_get_caps_info (v4l2object, caps, &fmtdesc, &info)) + goto invalid_caps; + + pixelformat = fmtdesc->pixelformat; + width = GST_VIDEO_INFO_WIDTH (&info); + height = GST_VIDEO_INFO_HEIGHT (&info); + fps_n = GST_VIDEO_INFO_FPS_N (&info); + fps_d = GST_VIDEO_INFO_FPS_D (&info); + stride = GST_VIDEO_INFO_PLANE_STRIDE (&info, 0); + + if (info.flags & GST_VIDEO_FLAG_INTERLACED) { GST_DEBUG_OBJECT (v4l2object->element, "interlaced video"); /* ideally we would differentiate between types of interlaced video * but there is not sufficient information in the caps.. @@ -2123,8 +2221,9 @@ gst_v4l2_object_set_format (GstV4l2Object * v4l2object, guint32 pixelformat, field = V4L2_FIELD_NONE; } - GST_DEBUG_OBJECT (v4l2object->element, "Setting format to %dx%d, format " - "%" GST_FOURCC_FORMAT, width, height, GST_FOURCC_ARGS (pixelformat)); + GST_DEBUG_OBJECT (v4l2object->element, "Desired format %dx%d, format " + "%" GST_FOURCC_FORMAT " stride: %d", width, height, + GST_FOURCC_ARGS (pixelformat), stride); GST_V4L2_CHECK_OPEN (v4l2object); GST_V4L2_CHECK_NOT_ACTIVE (v4l2object); @@ -2132,7 +2231,7 @@ gst_v4l2_object_set_format (GstV4l2Object * v4l2object, guint32 pixelformat, /* Only unconditionally accept mpegts for sources */ if ((v4l2object->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && (pixelformat == GST_MAKE_FOURCC ('M', 'P', 'E', 'G'))) - return TRUE; + goto done; memset (&format, 0x00, sizeof (struct v4l2_format)); format.type = v4l2object->type; @@ -2140,42 +2239,128 @@ gst_v4l2_object_set_format (GstV4l2Object * v4l2object, guint32 pixelformat, if (v4l2_ioctl (fd, VIDIOC_G_FMT, &format) < 0) goto get_fmt_failed; - if (format.type == v4l2object->type && - format.fmt.pix.width == width && - format.fmt.pix.height == height && - format.fmt.pix.pixelformat == pixelformat && - format.fmt.pix.field == field) { - /* Nothing to do. We want to succeed immediately - * here because setting the same format back - * can still fail due to EBUSY. By short-circuiting - * here, we allow pausing and re-playing pipelines - * with changed caps, as long as the changed caps - * do not change the webcam's format. Otherwise, - * any caps change would require us to go to NULL - * state to close the device and set format. - */ - return TRUE; + GST_DEBUG_OBJECT (v4l2object->element, "Got format to %dx%d, format " + "%" GST_FOURCC_FORMAT " bytesperline %d, colorspace %d", + format.fmt.pix.width, format.fmt.pix.height, + GST_FOURCC_ARGS (format.fmt.pix.pixelformat), format.fmt.pix.bytesperline, + format.fmt.pix.colorspace); + + if (format.type != v4l2object->type || + format.fmt.pix.width != width || + format.fmt.pix.height != height || + format.fmt.pix.pixelformat != pixelformat || + format.fmt.pix.field != field || format.fmt.pix.bytesperline != stride) { + /* something different, set the format */ + GST_DEBUG_OBJECT (v4l2object->element, "Setting format to %dx%d, format " + "%" GST_FOURCC_FORMAT " bytesperline %d", width, height, + GST_FOURCC_ARGS (pixelformat), stride); + + format.type = v4l2object->type; + format.fmt.pix.width = width; + format.fmt.pix.height = height; + format.fmt.pix.pixelformat = pixelformat; + format.fmt.pix.field = field; + /* try to ask our prefered stride */ + format.fmt.pix.bytesperline = stride; + + if (v4l2_ioctl (fd, VIDIOC_S_FMT, &format) < 0) + goto set_fmt_failed; + + GST_DEBUG_OBJECT (v4l2object->element, "Got format to %dx%d, format " + "%" GST_FOURCC_FORMAT " stride %d", format.fmt.pix.width, + format.fmt.pix.height, GST_FOURCC_ARGS (format.fmt.pix.pixelformat), + format.fmt.pix.bytesperline); + + if (format.fmt.pix.width != width || format.fmt.pix.height != height) + goto invalid_dimensions; + + if (format.fmt.pix.pixelformat != pixelformat) + goto invalid_pixelformat; } - format.type = v4l2object->type; - format.fmt.pix.width = width; - format.fmt.pix.height = height; - format.fmt.pix.pixelformat = pixelformat; - format.fmt.pix.field = field; + /* figure out the frame layout */ + v4l2object->bytesperline = format.fmt.pix.bytesperline; + v4l2object->sizeimage = format.fmt.pix.sizeimage; + + GST_DEBUG_OBJECT (v4l2object->element, "Got sizeimage %u", + v4l2object->sizeimage); + + /* Is there a reason we require the caller to always specify a framerate? */ + GST_DEBUG_OBJECT (v4l2object->element, "Desired framerate: %u/%u", fps_n, + fps_d); + + memset (&streamparm, 0x00, sizeof (struct v4l2_streamparm)); + streamparm.type = v4l2object->type; + + if (v4l2_ioctl (fd, VIDIOC_G_PARM, &streamparm) < 0) + goto get_parm_failed; + + GST_VIDEO_INFO_FPS_N (&info) = + streamparm.parm.capture.timeperframe.denominator; + GST_VIDEO_INFO_FPS_D (&info) = streamparm.parm.capture.timeperframe.numerator; + + if (v4l2object->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + GST_DEBUG_OBJECT (v4l2object->element, "Got framerate: %u/%u", + streamparm.parm.capture.timeperframe.denominator, + streamparm.parm.capture.timeperframe.numerator); + + /* We used to skip frame rate setup if the camera was already setup + * with the requested frame rate. This breaks some cameras though, + * causing them to not output data (several models of Thinkpad cameras + * have this problem at least). + * So, don't skip. */ + GST_LOG_OBJECT (v4l2object->element, "Setting framerate to %u/%u", fps_n, + fps_d); + /* We want to change the frame rate, so check whether we can. Some cheap USB + * cameras don't have the capability */ + if ((streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) == 0) { + GST_DEBUG_OBJECT (v4l2object->element, + "Not setting framerate (not supported)"); + goto done; + } - if (v4l2_ioctl (fd, VIDIOC_S_FMT, &format) < 0) { - goto set_fmt_failed; + /* Note: V4L2 wants the frame interval, we have the frame rate */ + streamparm.parm.capture.timeperframe.numerator = fps_d; + streamparm.parm.capture.timeperframe.denominator = fps_n; + + /* some cheap USB cam's won't accept any change */ + if (v4l2_ioctl (fd, VIDIOC_S_PARM, &streamparm) < 0) + goto set_parm_failed; + + /* get new values */ + fps_d = streamparm.parm.capture.timeperframe.numerator; + fps_n = streamparm.parm.capture.timeperframe.denominator; + + GST_INFO_OBJECT (v4l2object->element, "Set framerate to %u/%u", fps_n, + fps_d); + + GST_VIDEO_INFO_FPS_N (&info) = fps_n; + GST_VIDEO_INFO_FPS_D (&info) = fps_d; } - if (format.fmt.pix.width != width || format.fmt.pix.height != height) - goto invalid_dimensions; +done: + /* if we have a framerate pre-calculate duration */ + if (fps_n > 0 && fps_d > 0) { + v4l2object->duration = gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n); + } else { + v4l2object->duration = GST_CLOCK_TIME_NONE; + } + v4l2object->info = info; + v4l2object->fmtdesc = fmtdesc; - if (format.fmt.pix.pixelformat != pixelformat) - goto invalid_pixelformat; + /* now configure ther pools */ + if (!gst_v4l2_object_setup_pool (v4l2object, caps)) + goto pool_failed; return TRUE; /* ERRORS */ +invalid_caps: + { + GST_DEBUG_OBJECT (v4l2object->element, "can't parse caps %" GST_PTR_FORMAT, + caps); + return FALSE; + } get_fmt_failed: { GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS, @@ -2222,30 +2407,219 @@ invalid_pixelformat: GST_FOURCC_ARGS (format.fmt.pix.pixelformat))); return FALSE; } +get_parm_failed: + { + /* it's possible that this call is not supported */ + if (errno != EINVAL) { + GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS, + (_("Could not get parameters on device '%s'"), + v4l2object->videodev), GST_ERROR_SYSTEM); + } + goto done; + } +set_parm_failed: + { + GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, SETTINGS, + (_("Video device did not accept new frame rate setting.")), + GST_ERROR_SYSTEM); + goto done; + } +pool_failed: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, SETTINGS, + (_("Video device could not create buffer pool.")), GST_ERROR_SYSTEM); + return FALSE; + } } gboolean -gst_v4l2_object_start_streaming (GstV4l2Object * v4l2object) +gst_v4l2_object_unlock (GstV4l2Object * v4l2object) { - if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_STREAMON, - &(v4l2object->type)) < 0) { - GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, OPEN_READ, - (_("Error starting streaming on device '%s'."), v4l2object->videodev), - GST_ERROR_SYSTEM); - return FALSE; + GST_LOG_OBJECT (v4l2object->element, "flush poll"); + gst_poll_set_flushing (v4l2object->poll, TRUE); + + return TRUE; +} + +gboolean +gst_v4l2_object_unlock_stop (GstV4l2Object * v4l2object) +{ + GST_LOG_OBJECT (v4l2object->element, "flush stop poll"); + gst_poll_set_flushing (v4l2object->poll, FALSE); + + return TRUE; +} + +gboolean +gst_v4l2_object_stop (GstV4l2Object * v4l2object) +{ + GST_DEBUG_OBJECT (v4l2object->element, "stopping"); + + if (!GST_V4L2_IS_OPEN (v4l2object)) + goto done; + if (!GST_V4L2_IS_ACTIVE (v4l2object)) + goto done; + + if (v4l2object->pool) { + GST_DEBUG_OBJECT (v4l2object->element, "deactivating pool"); + gst_buffer_pool_set_active (GST_BUFFER_POOL_CAST (v4l2object->pool), FALSE); + gst_object_unref (v4l2object->pool); + v4l2object->pool = NULL; } + + GST_V4L2_SET_INACTIVE (v4l2object); + +done: return TRUE; } +#if 0 +static GstFlowReturn +gst_v4l2_object_get_mmap (GstV4l2Object * v4l2object, GstBuffer ** buf) +{ + GstFlowReturn res; +#define NUM_TRIALS 50 + GstBufferPool *pool; + gint32 trials = NUM_TRIALS; + GstBuffer *pool_buffer; + gboolean need_copy; + + pool = v4l2object->pool; + if (!pool) + goto no_buffer_pool; + + GST_DEBUG_OBJECT (v4l2object->element, "grab frame"); + + for (;;) { + if ((res = gst_v4l2_object_poll (v4l2object)) != GST_FLOW_OK) + goto poll_error; + + res = gst_buffer_pool_acquire_buffer (pool, &pool_buffer, NULL); + if (res != GST_FLOW_OK) + goto no_buffer; + + if (v4l2object->size > 0) { + gsize size = gst_buffer_get_size (pool_buffer); + + /* if size does not match what we expected, try again */ + if (size != v4l2object->size) { + GST_ELEMENT_WARNING (v4l2object->element, RESOURCE, READ, + (_("Got unexpected frame size of %u instead of %u."), + size, v4l2object->size), (NULL)); + gst_buffer_unref (pool_buffer); + goto no_buffer; + } + } + /* when we get here all is fine */ + break; + + no_buffer: + GST_WARNING_OBJECT (v4l2object->element, "trials=%d", trials); + + /* if the sync() got interrupted, we can retry */ + switch (errno) { + case EINVAL: + case ENOMEM: + /* fatal */ + return GST_FLOW_ERROR; + + case EAGAIN: + case EIO: + case EINTR: + default: + /* try again, until too many trials */ + break; + } + + /* check nr. of attempts to capture */ + if (--trials == -1) { + goto too_many_trials; + } + } + + + /* if we are handing out the last buffer in the pool, we need to make a + * copy and bring the buffer back in the pool. */ + need_copy = v4l2object->always_copy + || !gst_v4l2_buffer_pool_available_buffers (pool); + + if (G_UNLIKELY (need_copy)) { + if (!v4l2object->always_copy) { + GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, v4l2object->element, + "running out of buffers, making a copy to reuse current one"); + } + *buf = gst_buffer_copy (pool_buffer); + /* this will requeue */ + gst_buffer_unref (pool_buffer); + } else { + *buf = pool_buffer; + } + + return GST_FLOW_OK; + + /* ERRORS */ +no_buffer_pool: + { + GST_DEBUG_OBJECT (v4l2object->element, "no buffer pool"); + return GST_FLOW_FLUSHING; + } +poll_error: + { + return res; + } +too_many_trials: + { + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, FAILED, + (_("Failed trying to get video frames from device '%s'."), + v4l2object->videodev), + (_("Failed after %d tries. device %s. system error: %s"), + NUM_TRIALS, v4l2object->videodev, g_strerror (errno))); + return GST_FLOW_ERROR; + } +} +#endif + gboolean -gst_v4l2_object_stop_streaming (GstV4l2Object * v4l2object) +gst_v4l2_object_copy (GstV4l2Object * v4l2object, GstBuffer * dest, + GstBuffer * src) { - if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_STREAMOFF, - &(v4l2object->type)) < 0) { - GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, OPEN_READ, - (_("Error stopping streaming on device '%s'."), v4l2object->videodev), - GST_ERROR_SYSTEM); - return FALSE; + if (v4l2object->info.finfo) { + GstVideoFrame src_frame, dest_frame; + + GST_DEBUG_OBJECT (v4l2object->element, "copy video frame"); + + /* we have raw video, use videoframe copy to get strides right */ + if (!gst_video_frame_map (&src_frame, &v4l2object->info, src, GST_MAP_READ)) + goto invalid_buffer; + + if (!gst_video_frame_map (&dest_frame, &v4l2object->info, dest, + GST_MAP_WRITE)) { + gst_video_frame_unmap (&src_frame); + goto invalid_buffer; + } + + gst_video_frame_copy (&dest_frame, &src_frame); + + gst_video_frame_unmap (&src_frame); + gst_video_frame_unmap (&dest_frame); + } else { + GstMapInfo map; + + GST_DEBUG_OBJECT (v4l2object->element, "copy raw bytes"); + gst_buffer_map (src, &map, GST_MAP_READ); + gst_buffer_fill (dest, 0, map.data, map.size); + gst_buffer_unmap (src, &map); } + GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, v4l2object->element, + "slow copy into buffer %p", dest); + return TRUE; + + /* ERRORS */ +invalid_buffer: + { + /* No Window available to put our image into */ + GST_WARNING_OBJECT (v4l2object->element, "could not map image"); + return FALSE; + } } diff --git a/sys/v4l2/gstv4l2object.h b/sys/v4l2/gstv4l2object.h index a7b590d9b9..1b15627ef6 100644 --- a/sys/v4l2/gstv4l2object.h +++ b/sys/v4l2/gstv4l2object.h @@ -49,10 +49,14 @@ #include <gst/gst.h> #include <gst/base/gstpushsrc.h> -#include <gst/controller/gstcontroller.h> -#include <gst/interfaces/propertyprobe.h> +#include <gst/video/video.h> +typedef struct _GstV4l2Object GstV4l2Object; +typedef struct _GstV4l2ObjectClassHelper GstV4l2ObjectClassHelper; +typedef struct _GstV4l2Xv GstV4l2Xv; + +#include <gstv4l2bufferpool.h> /* size of v4l2 buffer pool in streaming case */ #define GST_V4L2_MAX_BUFFERS 16 @@ -61,35 +65,66 @@ /* max frame width/height */ #define GST_V4L2_MAX_SIZE (1<<15) /* 2^15 == 32768 */ - - G_BEGIN_DECLS #define GST_V4L2_OBJECT(obj) (GstV4l2Object *)(obj) -typedef struct _GstV4l2Object GstV4l2Object; -typedef struct _GstV4l2ObjectClassHelper GstV4l2ObjectClassHelper; -typedef struct _GstV4l2Xv GstV4l2Xv; +typedef enum { + GST_V4L2_IO_AUTO = 0, + GST_V4L2_IO_RW = 1, + GST_V4L2_IO_MMAP = 2, + GST_V4L2_IO_USERPTR = 3 +} GstV4l2IOMode; typedef gboolean (*GstV4l2GetInOutFunction) (GstV4l2Object * v4l2object, gint * input); typedef gboolean (*GstV4l2SetInOutFunction) (GstV4l2Object * v4l2object, gint input); typedef gboolean (*GstV4l2UpdateFpsFunction) (GstV4l2Object * v4l2object); +#define GST_V4L2_WIDTH(o) (GST_VIDEO_INFO_WIDTH (&(o)->info)) +#define GST_V4L2_HEIGHT(o) (GST_VIDEO_INFO_HEIGHT (&(o)->info)) +#define GST_V4L2_PIXELFORMAT(o) ((o)->fmtdesc->pixelformat) +#define GST_V4L2_FPS_N(o) (GST_VIDEO_INFO_FPS_N (&(o)->info)) +#define GST_V4L2_FPS_D(o) (GST_VIDEO_INFO_FPS_D (&(o)->info)) + +/* simple check whether the device is open */ +#define GST_V4L2_IS_OPEN(o) ((o)->video_fd > 0) + +/* check whether the device is 'active' */ +#define GST_V4L2_IS_ACTIVE(o) ((o)->active) +#define GST_V4L2_SET_ACTIVE(o) ((o)->active = TRUE) +#define GST_V4L2_SET_INACTIVE(o) ((o)->active = FALSE) + struct _GstV4l2Object { GstElement * element; + enum v4l2_buf_type type; /* V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_OUTPUT */ + /* the video device */ char *videodev; /* the video-device's file descriptor */ gint video_fd; + GstV4l2IOMode mode; GstPoll * poll; gboolean can_poll_device; - /* the video buffer (mmap()'ed) */ - guint8 **buffer; + gboolean active; + gboolean streaming; - enum v4l2_buf_type type; /* V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_OUTPUT */ + /* the current format */ + struct v4l2_fmtdesc *fmtdesc; + GstVideoInfo info; + + guint32 bytesperline; + guint32 sizeimage; + GstClockTime duration; + + /* wanted mode */ + GstV4l2IOMode req_mode; + + /* optional pool */ + gboolean always_copy; + GstBufferPool *pool; /* the video device's capabilities */ struct v4l2_capability vcap; @@ -138,7 +173,8 @@ GType gst_v4l2_object_get_type (void); PROP_CONTRAST, \ PROP_SATURATION, \ PROP_HUE, \ - PROP_TV_NORM + PROP_TV_NORM, \ + PROP_IO_MODE /* create/destroy */ GstV4l2Object * gst_v4l2_object_new (GstElement * element, @@ -159,11 +195,12 @@ gboolean gst_v4l2_object_set_property_helper (GstV4l2Object *v4l2object, gboolean gst_v4l2_object_get_property_helper (GstV4l2Object *v4l2object, guint prop_id, GValue * value, GParamSpec * pspec); -/* starting/stopping */ -gboolean gst_v4l2_object_start (GstV4l2Object *v4l2object); -gboolean gst_v4l2_object_stop (GstV4l2Object *v4l2object); +/* open/close */ +gboolean gst_v4l2_object_open (GstV4l2Object *v4l2object); +gboolean gst_v4l2_object_close (GstV4l2Object *v4l2object); /* probing */ +#if 0 const GList* gst_v4l2_probe_get_properties (GstPropertyProbe * probe); void gst_v4l2_probe_probe_property (GstPropertyProbe * probe, guint prop_id, @@ -175,25 +212,27 @@ gboolean gst_v4l2_probe_needs_probe (GstPropertyProbe * probe, guint pro GValueArray* gst_v4l2_probe_get_values (GstPropertyProbe * probe, guint prop_id, const GParamSpec * pspec, GList ** klass_devices); +#endif GstCaps* gst_v4l2_object_probe_caps_for_format (GstV4l2Object *v4l2object, guint32 pixelformat, const GstStructure * template); -gboolean gst_v4l2_object_get_caps_info (GstV4l2Object *v4l2object, GstCaps *caps, - struct v4l2_fmtdesc **format, gint *w, gint *h, - gboolean * interlaced, guint *fps_n, guint *fps_d, guint *size); - - GSList* gst_v4l2_object_get_format_list (GstV4l2Object *v4l2object); GstCaps* gst_v4l2_object_get_all_caps (void); GstStructure* gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc); -gboolean gst_v4l2_object_set_format (GstV4l2Object *v4l2object, guint32 pixelformat, guint32 width, guint32 height, gboolean interlaced); +gboolean gst_v4l2_object_set_format (GstV4l2Object *v4l2object, GstCaps * caps); + +gboolean gst_v4l2_object_unlock (GstV4l2Object *v4l2object); +gboolean gst_v4l2_object_unlock_stop (GstV4l2Object *v4l2object); + +gboolean gst_v4l2_object_stop (GstV4l2Object *v4l2object); + -gboolean gst_v4l2_object_start_streaming (GstV4l2Object *v4l2object); -gboolean gst_v4l2_object_stop_streaming (GstV4l2Object *v4l2object); +gboolean gst_v4l2_object_copy (GstV4l2Object * v4l2object, + GstBuffer * dest, GstBuffer *src); #define GST_IMPLEMENT_V4L2_PROBE_METHODS(Type_Class, interface_as_function) \ diff --git a/sys/v4l2/gstv4l2radio.c b/sys/v4l2/gstv4l2radio.c index 63a2fed17f..bfae360234 100644 --- a/sys/v4l2/gstv4l2radio.c +++ b/sys/v4l2/gstv4l2radio.c @@ -251,73 +251,23 @@ gst_v4l2radio_set_unmute (GstV4l2Radio * radio) return gst_v4l2radio_set_mute_on (radio, FALSE); } -GST_IMPLEMENT_V4L2_PROBE_METHODS (GstV4l2RadioClass, gst_v4l2radio); GST_IMPLEMENT_V4L2_TUNER_METHODS (GstV4l2Radio, gst_v4l2radio); static void gst_v4l2radio_uri_handler_init (gpointer g_iface, gpointer iface_data); -static gboolean -gst_v4l2radio_interface_supported (GstImplementsInterface * iface, - GType iface_type) -{ - if (iface_type == GST_TYPE_TUNER) - return TRUE; - else - return FALSE; -} - static void -gst_v4l2radio_implements_interface_init (GstImplementsInterfaceClass * iface) -{ - iface->supported = gst_v4l2radio_interface_supported; -} - -static void -gst_v4l2radio_tuner_interface_reinit (GstTunerClass * iface) +gst_v4l2radio_tuner_interface_reinit (GstTunerInterface * iface) { gst_v4l2radio_tuner_interface_init (iface); } -static void -gst_v4l2radio_interfaces (GType type) -{ - static const GInterfaceInfo urihandler_info = { - (GInterfaceInitFunc) gst_v4l2radio_uri_handler_init, - NULL, - NULL - }; - - static const GInterfaceInfo implements_interface_info = { - (GInterfaceInitFunc) gst_v4l2radio_implements_interface_init, - NULL, - NULL, - }; - - static const GInterfaceInfo propertyprobe_info = { - (GInterfaceInitFunc) gst_v4l2radio_property_probe_interface_init, - NULL, - NULL, - }; - - static const GInterfaceInfo tuner_interface_info = { - (GInterfaceInitFunc) gst_v4l2radio_tuner_interface_reinit, - NULL, - NULL, - }; - - g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info); - g_type_add_interface_static (type, - GST_TYPE_IMPLEMENTS_INTERFACE, &implements_interface_info); - - g_type_add_interface_static (type, GST_TYPE_TUNER, &tuner_interface_info); - - g_type_add_interface_static (type, - GST_TYPE_PROPERTY_PROBE, &propertyprobe_info); -} - -GST_BOILERPLATE_FULL (GstV4l2Radio, gst_v4l2radio, GstElement, GST_TYPE_ELEMENT, - gst_v4l2radio_interfaces); +#define gst_v4l2radio_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstV4l2Radio, gst_v4l2radio, GST_TYPE_ELEMENT, + G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, + gst_v4l2radio_uri_handler_init); + G_IMPLEMENT_INTERFACE (GST_TYPE_TUNER, + gst_v4l2radio_tuner_interface_reinit)); static void gst_v4l2radio_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); @@ -329,24 +279,6 @@ static GstStateChangeReturn gst_v4l2radio_change_state (GstElement * element, GstStateChange transition); static void -gst_v4l2radio_base_init (gpointer gclass) -{ - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (gclass); - GstV4l2RadioClass *gstv4l2radio_class = GST_V4L2RADIO_CLASS (gclass); - - GST_DEBUG_CATEGORY_INIT (v4l2radio_debug, "v4l2radio", 0, - "V4l2 radio element"); - - gstv4l2radio_class->v4l2_class_devices = NULL; - - gst_element_class_set_details_simple (gstelement_class, - "Radio (video4linux2) Tuner", - "Tuner", - "Controls a Video4Linux2 radio device", - "Alexey Chernov <4ernov@gmail.com>"); -} - -static void gst_v4l2radio_class_init (GstV4l2RadioClass * klass) { GObjectClass *gobject_class; @@ -355,6 +287,8 @@ gst_v4l2radio_class_init (GstV4l2RadioClass * klass) gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; + gobject_class->dispose = gst_v4l2radio_dispose; + gobject_class->finalize = (GObjectFinalizeFunc) gst_v4l2radio_finalize; gobject_class->set_property = gst_v4l2radio_set_property; gobject_class->get_property = gst_v4l2radio_get_property; @@ -368,16 +302,23 @@ gst_v4l2radio_class_init (GstV4l2RadioClass * klass) "Station frequency in Hz", MIN_FREQUENCY, MAX_FREQUENCY, DEFAULT_FREQUENCY, G_PARAM_READWRITE)); - gobject_class->dispose = gst_v4l2radio_dispose; - gobject_class->finalize = (GObjectFinalizeFunc) gst_v4l2radio_finalize; - gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_v4l2radio_change_state); + gst_element_class_set_details_simple (gstelement_class, + "Radio (video4linux2) Tuner", + "Tuner", + "Controls a Video4Linux2 radio device", + "Alexey Chernov <4ernov@gmail.com>"); + + klass->v4l2_class_devices = NULL; + + GST_DEBUG_CATEGORY_INIT (v4l2radio_debug, "v4l2radio", 0, + "V4l2 radio element"); } static void -gst_v4l2radio_init (GstV4l2Radio * filter, GstV4l2RadioClass * gclass) +gst_v4l2radio_init (GstV4l2Radio * filter) { filter->v4l2object = gst_v4l2_object_new (GST_ELEMENT (filter), V4L2_BUF_TYPE_VIDEO_CAPTURE, DEFAULT_PROP_DEVICE, @@ -473,7 +414,7 @@ gst_v4l2radio_start (GstV4l2Radio * radio) static gboolean gst_v4l2radio_stop (GstV4l2Radio * radio) { - if (!gst_v4l2_object_stop (radio->v4l2object)) + if (!gst_v4l2_object_close (radio->v4l2object)) return FALSE; return TRUE; @@ -563,19 +504,20 @@ gst_v4l2radio_get_property (GObject * object, guint prop_id, /* GstURIHandler interface */ static GstURIType -gst_v4l2radio_uri_get_type (void) +gst_v4l2radio_uri_get_type (GType type) { return GST_URI_SRC; } -static gchar ** -gst_v4l2radio_uri_get_protocols (void) +static const gchar *const * +gst_v4l2radio_uri_get_protocols (GType type) { - static gchar *protocols[] = { (char *) "radio", NULL }; + static const gchar *protocols[] = { "radio", NULL }; + return protocols; } -static const gchar * +static gchar * gst_v4l2radio_uri_get_uri (GstURIHandler * handler) { GstV4l2Radio *radio = GST_V4L2RADIO (handler); @@ -583,19 +525,17 @@ gst_v4l2radio_uri_get_uri (GstURIHandler * handler) if (radio->v4l2object->videodev != NULL) { if (gst_v4l2_get_frequency (radio->v4l2object, 0, &(radio->v4l2object->frequency))) { - gchar uri[20]; - gchar freq[6]; - g_ascii_formatd (freq, 6, "%4.1f", radio->v4l2object->frequency / 1e6); - g_snprintf (uri, sizeof (uri), "radio://%s", freq); - return g_intern_string (uri); + return g_strdup_printf ("radio://%4.1f", + radio->v4l2object->frequency / 1e6); } } - return "radio://"; + return g_strdup ("radio://"); } static gboolean -gst_v4l2radio_uri_set_uri (GstURIHandler * handler, const gchar * uri) +gst_v4l2radio_uri_set_uri (GstURIHandler * handler, const gchar * uri, + GError ** error) { GstV4l2Radio *radio = GST_V4L2RADIO (handler); gdouble dfreq; @@ -620,6 +560,8 @@ gst_v4l2radio_uri_set_uri (GstURIHandler * handler, const gchar * uri) return TRUE; uri_failed: + g_set_error_literal (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE, + "Bad radio URI, could not parse frequency"); return FALSE; } diff --git a/sys/v4l2/gstv4l2sink.c b/sys/v4l2/gstv4l2sink.c index f6aba2455d..a2e2fe5d28 100644 --- a/sys/v4l2/gstv4l2sink.c +++ b/sys/v4l2/gstv4l2sink.c @@ -53,11 +53,12 @@ #include <config.h> #endif +#include "gst/video/gstvideometa.h" #include "gstv4l2colorbalance.h" #include "gstv4l2tuner.h" #ifdef HAVE_XVIDEO -#include "gstv4l2xoverlay.h" +#include "gstv4l2videooverlay.h" #endif #include "gstv4l2vidorient.h" @@ -69,16 +70,12 @@ GST_DEBUG_CATEGORY (v4l2sink_debug); #define GST_CAT_DEFAULT v4l2sink_debug -#define PROP_DEF_QUEUE_SIZE 12 -#define PROP_DEF_MIN_QUEUED_BUFS 1 #define DEFAULT_PROP_DEVICE "/dev/video1" enum { PROP_0, V4L2_STD_OBJECT_PROPS, - PROP_QUEUE_SIZE, - PROP_MIN_QUEUED_BUFS, PROP_OVERLAY_TOP, PROP_OVERLAY_LEFT, PROP_OVERLAY_WIDTH, @@ -90,53 +87,13 @@ enum }; -GST_IMPLEMENT_V4L2_PROBE_METHODS (GstV4l2SinkClass, gst_v4l2sink); GST_IMPLEMENT_V4L2_COLOR_BALANCE_METHODS (GstV4l2Sink, gst_v4l2sink); GST_IMPLEMENT_V4L2_TUNER_METHODS (GstV4l2Sink, gst_v4l2sink); #ifdef HAVE_XVIDEO -GST_IMPLEMENT_V4L2_XOVERLAY_METHODS (GstV4l2Sink, gst_v4l2sink); +GST_IMPLEMENT_V4L2_VIDEO_OVERLAY_METHODS (GstV4l2Sink, gst_v4l2sink); #endif GST_IMPLEMENT_V4L2_VIDORIENT_METHODS (GstV4l2Sink, gst_v4l2sink); -static gboolean -gst_v4l2sink_iface_supported (GstImplementsInterface * iface, GType iface_type) -{ - GstV4l2Object *v4l2object = GST_V4L2SINK (iface)->v4l2object; - -#ifdef HAVE_XVIDEO - g_assert (iface_type == GST_TYPE_X_OVERLAY || - iface_type == GST_TYPE_NAVIGATION || - iface_type == GST_TYPE_COLOR_BALANCE || - iface_type == GST_TYPE_VIDEO_ORIENTATION || - iface_type == GST_TYPE_TUNER); -#else - g_assert (iface_type == GST_TYPE_COLOR_BALANCE || - iface_type == GST_TYPE_VIDEO_ORIENTATION || - iface_type == GST_TYPE_TUNER); -#endif - - if (v4l2object->video_fd == -1) - return FALSE; - -#ifdef HAVE_XVIDEO - if (!GST_V4L2_IS_OVERLAY (v4l2object)) { - if (iface_type == GST_TYPE_X_OVERLAY || iface_type == GST_TYPE_NAVIGATION) - return FALSE; - } -#endif - - return TRUE; -} - -static void -gst_v4l2sink_interface_init (GstImplementsInterfaceClass * klass) -{ - /* - * default virtual functions - */ - klass->supported = gst_v4l2sink_iface_supported; -} - #ifdef HAVE_XVIDEO static void gst_v4l2sink_navigation_send_event (GstNavigation * navigation, GstStructure * structure); @@ -147,66 +104,18 @@ gst_v4l2sink_navigation_init (GstNavigationInterface * iface) } #endif -static void -gst_v4l2sink_init_interfaces (GType type) -{ - static const GInterfaceInfo v4l2iface_info = { - (GInterfaceInitFunc) gst_v4l2sink_interface_init, - NULL, - NULL, - }; - static const GInterfaceInfo v4l2_tuner_info = { - (GInterfaceInitFunc) gst_v4l2sink_tuner_interface_init, - NULL, - NULL, - }; +#define gst_v4l2sink_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstV4l2Sink, gst_v4l2sink, GST_TYPE_VIDEO_SINK, + G_IMPLEMENT_INTERFACE (GST_TYPE_TUNER, gst_v4l2sink_tuner_interface_init); #ifdef HAVE_XVIDEO - static const GInterfaceInfo v4l2_xoverlay_info = { - (GInterfaceInitFunc) gst_v4l2sink_xoverlay_interface_init, - NULL, - NULL, - }; - static const GInterfaceInfo v4l2_navigation_info = { - (GInterfaceInitFunc) gst_v4l2sink_navigation_init, - NULL, - NULL, - }; + G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY, + gst_v4l2sink_video_overlay_interface_init); + G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION, gst_v4l2sink_navigation_init); #endif - static const GInterfaceInfo v4l2_colorbalance_info = { - (GInterfaceInitFunc) gst_v4l2sink_color_balance_interface_init, - NULL, - NULL, - }; - static const GInterfaceInfo v4l2_videoorientation_info = { - (GInterfaceInitFunc) gst_v4l2sink_video_orientation_interface_init, - NULL, - NULL, - }; - static const GInterfaceInfo v4l2_propertyprobe_info = { - (GInterfaceInitFunc) gst_v4l2sink_property_probe_interface_init, - NULL, - NULL, - }; - - g_type_add_interface_static (type, - GST_TYPE_IMPLEMENTS_INTERFACE, &v4l2iface_info); - g_type_add_interface_static (type, GST_TYPE_TUNER, &v4l2_tuner_info); -#ifdef HAVE_XVIDEO - g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &v4l2_xoverlay_info); - g_type_add_interface_static (type, - GST_TYPE_NAVIGATION, &v4l2_navigation_info); -#endif - g_type_add_interface_static (type, - GST_TYPE_COLOR_BALANCE, &v4l2_colorbalance_info); - g_type_add_interface_static (type, - GST_TYPE_VIDEO_ORIENTATION, &v4l2_videoorientation_info); - g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE, - &v4l2_propertyprobe_info); -} - - -GST_BOILERPLATE_FULL (GstV4l2Sink, gst_v4l2sink, GstVideoSink, - GST_TYPE_VIDEO_SINK, gst_v4l2sink_init_interfaces); + G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE, + gst_v4l2sink_color_balance_interface_init); + G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_ORIENTATION, + gst_v4l2sink_video_orientation_interface_init)); static void gst_v4l2sink_dispose (GObject * object); @@ -218,42 +127,23 @@ static void gst_v4l2sink_set_property (GObject * object, guint prop_id, static void gst_v4l2sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); - /* GstElement methods: */ static GstStateChangeReturn gst_v4l2sink_change_state (GstElement * element, GstStateChange transition); /* GstBaseSink methods: */ -static GstCaps *gst_v4l2sink_get_caps (GstBaseSink * bsink); +static gboolean gst_v4l2sink_propose_allocation (GstBaseSink * bsink, + GstQuery * query); +static GstCaps *gst_v4l2sink_get_caps (GstBaseSink * bsink, GstCaps * filter); static gboolean gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps); +#if 0 static GstFlowReturn gst_v4l2sink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf); +#endif static GstFlowReturn gst_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buf); static void -gst_v4l2sink_base_init (gpointer g_class) -{ - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); - GstV4l2SinkClass *gstv4l2sink_class = GST_V4L2SINK_CLASS (g_class); - GstPadTemplate *pad_template; - - gstv4l2sink_class->v4l2_class_devices = NULL; - - GST_DEBUG_CATEGORY_INIT (v4l2sink_debug, "v4l2sink", 0, "V4L2 sink element"); - - gst_element_class_set_details_simple (gstelement_class, - "Video (video4linux2) Sink", "Sink/Video", - "Displays frames on a video4linux2 device", "Rob Clark <rob@ti.com>,"); - - pad_template = - gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - gst_v4l2_object_get_all_caps ()); - gst_element_class_add_pad_template (gstelement_class, pad_template); - gst_object_unref (pad_template); -} - -static void gst_v4l2sink_class_init (GstV4l2SinkClass * klass) { GObjectClass *gobject_class; @@ -273,17 +163,7 @@ gst_v4l2sink_class_init (GstV4l2SinkClass * klass) gst_v4l2_object_install_properties_helper (gobject_class, DEFAULT_PROP_DEVICE); - g_object_class_install_property (gobject_class, PROP_QUEUE_SIZE, - g_param_spec_uint ("queue-size", "Queue size", - "Number of buffers to be enqueud in the driver in streaming mode", - GST_V4L2_MIN_BUFFERS, GST_V4L2_MAX_BUFFERS, PROP_DEF_QUEUE_SIZE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_MIN_QUEUED_BUFS, - g_param_spec_uint ("min-queued-bufs", "Minimum queued bufs", - "Minimum number of queued bufs; v4l2sink won't dqbuf if the driver " - "doesn't have more than this number (which normally you shouldn't change)", - 0, GST_V4L2_MAX_BUFFERS, PROP_DEF_MIN_QUEUED_BUFS, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_OVERLAY_TOP, g_param_spec_int ("overlay-top", "Overlay top", "The topmost (y) coordinate of the video overlay; top left corner of screen is 0,0", @@ -318,14 +198,28 @@ gst_v4l2sink_class_init (GstV4l2SinkClass * klass) "The height of the video crop; default is equal to negotiated image height", 0, 0xffffffff, 0, G_PARAM_READWRITE)); + gst_element_class_set_details_simple (element_class, + "Video (video4linux2) Sink", "Sink/Video", + "Displays frames on a video4linux2 device", "Rob Clark <rob@ti.com>,"); + + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + gst_v4l2_object_get_all_caps ())); + basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_v4l2sink_get_caps); basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_v4l2sink_set_caps); - basesink_class->buffer_alloc = GST_DEBUG_FUNCPTR (gst_v4l2sink_buffer_alloc); + basesink_class->propose_allocation = + GST_DEBUG_FUNCPTR (gst_v4l2sink_propose_allocation); basesink_class->render = GST_DEBUG_FUNCPTR (gst_v4l2sink_show_frame); + + klass->v4l2_class_devices = NULL; + + GST_DEBUG_CATEGORY_INIT (v4l2sink_debug, "v4l2sink", 0, "V4L2 sink element"); + } static void -gst_v4l2sink_init (GstV4l2Sink * v4l2sink, GstV4l2SinkClass * klass) +gst_v4l2sink_init (GstV4l2Sink * v4l2sink) { v4l2sink->v4l2object = gst_v4l2_object_new (GST_ELEMENT (v4l2sink), V4L2_BUF_TYPE_VIDEO_OUTPUT, DEFAULT_PROP_DEVICE, @@ -338,16 +232,10 @@ gst_v4l2sink_init (GstV4l2Sink * v4l2sink, GstV4l2SinkClass * klass) */ g_object_set (v4l2sink, "device", "/dev/video1", NULL); - /* number of buffers requested */ - v4l2sink->num_buffers = PROP_DEF_QUEUE_SIZE; - v4l2sink->min_queued_bufs = PROP_DEF_MIN_QUEUED_BUFS; - v4l2sink->probed_caps = NULL; - v4l2sink->current_caps = NULL; v4l2sink->overlay_fields_set = 0; v4l2sink->crop_fields_set = 0; - v4l2sink->state = 0; } @@ -360,10 +248,6 @@ gst_v4l2sink_dispose (GObject * object) gst_caps_unref (v4l2sink->probed_caps); } - if (v4l2sink->current_caps) { - gst_caps_unref (v4l2sink->current_caps); - } - G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -378,16 +262,6 @@ gst_v4l2sink_finalize (GstV4l2Sink * v4l2sink) /* - * State values - */ -enum -{ - STATE_OFF = 0, - STATE_PENDING_STREAMON, - STATE_STREAMING -}; - -/* * flags to indicate which overlay/crop properties the user has set (and * therefore which ones should override the defaults from the driver) */ @@ -497,12 +371,6 @@ gst_v4l2sink_set_property (GObject * object, if (!gst_v4l2_object_set_property_helper (v4l2sink->v4l2object, prop_id, value, pspec)) { switch (prop_id) { - case PROP_QUEUE_SIZE: - v4l2sink->num_buffers = g_value_get_uint (value); - break; - case PROP_MIN_QUEUED_BUFS: - v4l2sink->min_queued_bufs = g_value_get_uint (value); - break; case PROP_OVERLAY_TOP: v4l2sink->overlay.top = g_value_get_int (value); v4l2sink->overlay_fields_set |= RECT_TOP_SET; @@ -560,12 +428,6 @@ gst_v4l2sink_get_property (GObject * object, if (!gst_v4l2_object_get_property_helper (v4l2sink->v4l2object, prop_id, value, pspec)) { switch (prop_id) { - case PROP_QUEUE_SIZE: - g_value_set_uint (value, v4l2sink->num_buffers); - break; - case PROP_MIN_QUEUED_BUFS: - g_value_set_uint (value, v4l2sink->min_queued_bufs); - break; case PROP_OVERLAY_TOP: g_value_set_int (value, v4l2sink->overlay.top); break; @@ -610,7 +472,7 @@ gst_v4l2sink_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: /* open the device */ - if (!gst_v4l2_object_start (v4l2sink->v4l2object)) + if (!gst_v4l2_object_open (v4l2sink->v4l2object)) return GST_STATE_CHANGE_FAILURE; break; default: @@ -621,21 +483,16 @@ gst_v4l2sink_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: - if (v4l2sink->state == STATE_STREAMING) { - if (!gst_v4l2_object_stop_streaming (v4l2sink->v4l2object)) { - return GST_STATE_CHANGE_FAILURE; - } - v4l2sink->state = STATE_PENDING_STREAMON; - } + if (!gst_v4l2_object_stop (v4l2sink->v4l2object)) + return GST_STATE_CHANGE_FAILURE; break; case GST_STATE_CHANGE_READY_TO_NULL: - if (NULL != v4l2sink->pool) - gst_v4l2_buffer_pool_destroy (v4l2sink->pool); - v4l2sink->pool = NULL; - /* close the device */ + /* we need to call stop here too */ if (!gst_v4l2_object_stop (v4l2sink->v4l2object)) return GST_STATE_CHANGE_FAILURE; - v4l2sink->state = STATE_OFF; + /* close the device */ + if (!gst_v4l2_object_close (v4l2sink->v4l2object)) + return GST_STATE_CHANGE_FAILURE; break; default: break; @@ -646,7 +503,7 @@ gst_v4l2sink_change_state (GstElement * element, GstStateChange transition) static GstCaps * -gst_v4l2sink_get_caps (GstBaseSink * bsink) +gst_v4l2sink_get_caps (GstBaseSink * bsink, GstCaps * filter) { GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink); GstCaps *ret; @@ -661,40 +518,44 @@ gst_v4l2sink_get_caps (GstBaseSink * bsink) (v4l2sink))); } - if (v4l2sink->probed_caps) { - LOG_CAPS (v4l2sink, v4l2sink->probed_caps); - return gst_caps_ref (v4l2sink->probed_caps); - } - - formats = gst_v4l2_object_get_format_list (v4l2sink->v4l2object); + if (v4l2sink->probed_caps == NULL) { + formats = gst_v4l2_object_get_format_list (v4l2sink->v4l2object); - ret = gst_caps_new_empty (); + ret = gst_caps_new_empty (); - for (walk = formats; walk; walk = walk->next) { - struct v4l2_fmtdesc *format; + for (walk = formats; walk; walk = walk->next) { + struct v4l2_fmtdesc *format; - GstStructure *template; + GstStructure *template; - format = (struct v4l2_fmtdesc *) walk->data; + format = (struct v4l2_fmtdesc *) walk->data; - template = gst_v4l2_object_v4l2fourcc_to_structure (format->pixelformat); + template = gst_v4l2_object_v4l2fourcc_to_structure (format->pixelformat); - if (template) { - GstCaps *tmp; + if (template) { + GstCaps *tmp; - tmp = - gst_v4l2_object_probe_caps_for_format (v4l2sink->v4l2object, - format->pixelformat, template); - if (tmp) - gst_caps_append (ret, tmp); + tmp = + gst_v4l2_object_probe_caps_for_format (v4l2sink->v4l2object, + format->pixelformat, template); + if (tmp) + gst_caps_append (ret, tmp); - gst_structure_free (template); - } else { - GST_DEBUG_OBJECT (v4l2sink, "unknown format %u", format->pixelformat); + gst_structure_free (template); + } else { + GST_DEBUG_OBJECT (v4l2sink, "unknown format %u", format->pixelformat); + } } + v4l2sink->probed_caps = ret; } - v4l2sink->probed_caps = gst_caps_ref (ret); + if (filter) { + ret = + gst_caps_intersect_full (filter, v4l2sink->probed_caps, + GST_CAPS_INTERSECT_FIRST); + } else { + ret = gst_caps_ref (v4l2sink->probed_caps); + } GST_INFO_OBJECT (v4l2sink, "probed caps: %p", ret); LOG_CAPS (v4l2sink, ret); @@ -706,11 +567,7 @@ static gboolean gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps) { GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink); - gint w = 0, h = 0; - gboolean interlaced; - struct v4l2_fmtdesc *format; - guint fps_n, fps_d; - guint size; + GstV4l2Object *obj = v4l2sink->v4l2object; LOG_CAPS (v4l2sink, caps); @@ -719,118 +576,102 @@ gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps) return FALSE; } - if (v4l2sink->current_caps) { - GST_DEBUG_OBJECT (v4l2sink, "already have caps set.. are they equal?"); - LOG_CAPS (v4l2sink, v4l2sink->current_caps); - if (gst_caps_is_equal (v4l2sink->current_caps, caps)) { - GST_DEBUG_OBJECT (v4l2sink, "yes they are!"); - return TRUE; - } - GST_DEBUG_OBJECT (v4l2sink, "no they aren't!"); - } + if (!gst_v4l2_object_stop (obj)) + goto stop_failed; - if (v4l2sink->pool) { - /* TODO: if we've already allocated buffers, we probably need to - * do something here to free and reallocate.... - * - * gst_v4l2_object_stop_streaming() - * gst_v4l2_buffer_pool_destroy() - * - */ - GST_DEBUG_OBJECT (v4l2sink, "warning, changing caps not supported yet"); - return FALSE; - } + if (!gst_v4l2_object_set_format (v4l2sink->v4l2object, caps)) + goto invalid_format; - /* we want our own v4l2 type of fourcc codes */ - if (!gst_v4l2_object_get_caps_info (v4l2sink->v4l2object, caps, - &format, &w, &h, &interlaced, &fps_n, &fps_d, &size)) { - GST_DEBUG_OBJECT (v4l2sink, "can't get capture format from caps %p", caps); - return FALSE; - } + gst_v4l2sink_sync_overlay_fields (v4l2sink); + gst_v4l2sink_sync_crop_fields (v4l2sink); - if (!format) { - GST_DEBUG_OBJECT (v4l2sink, "unrecognized caps!!"); - return FALSE; - } +#ifdef HAVE_XVIDEO + gst_v4l2_video_overlay_prepare_window_handle (v4l2sink->v4l2object, TRUE); +#endif - if (!gst_v4l2_object_set_format (v4l2sink->v4l2object, format->pixelformat, - w, h, interlaced)) { - /* error already posted */ - return FALSE; - } + GST_INFO_OBJECT (v4l2sink, "outputting buffers via mmap()"); - v4l2sink->video_width = w; - v4l2sink->video_height = h; + v4l2sink->video_width = GST_V4L2_WIDTH (v4l2sink->v4l2object); + v4l2sink->video_height = GST_V4L2_HEIGHT (v4l2sink->v4l2object); /* TODO: videosink width/height should be scaled according to * pixel-aspect-ratio */ - GST_VIDEO_SINK_WIDTH (v4l2sink) = w; - GST_VIDEO_SINK_HEIGHT (v4l2sink) = h; - - v4l2sink->current_caps = gst_caps_ref (caps); + GST_VIDEO_SINK_WIDTH (v4l2sink) = v4l2sink->video_width; + GST_VIDEO_SINK_HEIGHT (v4l2sink) = v4l2sink->video_height; return TRUE; + + /* ERRORS */ +stop_failed: + { + GST_DEBUG_OBJECT (v4l2sink, "failed to stop streaming"); + return FALSE; + } +invalid_format: + { + /* error already posted */ + GST_DEBUG_OBJECT (v4l2sink, "can't set format"); + return FALSE; + } } -/* buffer alloc function to implement pad_alloc for upstream element */ -static GstFlowReturn -gst_v4l2sink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, - GstCaps * caps, GstBuffer ** buf) +static gboolean +gst_v4l2sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) { GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink); - GstV4l2Buffer *v4l2buf; - - if (v4l2sink->v4l2object->vcap.capabilities & V4L2_CAP_STREAMING) { - - /* initialize the buffer pool if not initialized yet (first buffer): */ - if (G_UNLIKELY (!v4l2sink->pool)) { - - /* set_caps() might not be called yet.. so just to make sure: */ - if (!gst_v4l2sink_set_caps (bsink, caps)) { - return GST_FLOW_ERROR; - } - - GST_V4L2_CHECK_OPEN (v4l2sink->v4l2object); + GstV4l2Object *obj = v4l2sink->v4l2object; + GstBufferPool *pool; + guint size = 0; + GstCaps *caps; + gboolean need_pool; - if (!(v4l2sink->pool = gst_v4l2_buffer_pool_new (GST_ELEMENT (v4l2sink), - v4l2sink->v4l2object->video_fd, - v4l2sink->num_buffers, caps, FALSE, - V4L2_BUF_TYPE_VIDEO_OUTPUT))) { - return GST_FLOW_ERROR; - } + gst_query_parse_allocation (query, &caps, &need_pool); - gst_v4l2sink_sync_overlay_fields (v4l2sink); - gst_v4l2sink_sync_crop_fields (v4l2sink); + if (caps == NULL) + goto no_caps; -#ifdef HAVE_XVIDEO - gst_v4l2_xoverlay_prepare_xwindow_id (v4l2sink->v4l2object, TRUE); -#endif + if ((pool = obj->pool)) + gst_object_ref (pool); - v4l2sink->state = STATE_PENDING_STREAMON; + if (pool != NULL) { + const GstCaps *pcaps; + GstStructure *config; - GST_INFO_OBJECT (v4l2sink, "outputting buffers via mmap()"); + /* we had a pool, check caps */ + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL); - if (v4l2sink->num_buffers != v4l2sink->pool->buffer_count) { - v4l2sink->num_buffers = v4l2sink->pool->buffer_count; - g_object_notify (G_OBJECT (v4l2sink), "queue-size"); - } + GST_DEBUG_OBJECT (v4l2sink, + "we had a pool with caps %" GST_PTR_FORMAT, pcaps); + if (!gst_caps_is_equal (caps, pcaps)) { + gst_object_unref (pool); + goto different_caps; } + } + /* we need at least 2 buffers to operate */ + gst_query_add_allocation_pool (query, pool, size, 2, 0); - v4l2buf = gst_v4l2_buffer_pool_get (v4l2sink->pool, TRUE); + /* we also support various metadata */ + gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE); + gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE); - if (G_LIKELY (v4l2buf)) { - GST_DEBUG_OBJECT (v4l2sink, "allocated buffer: %p", v4l2buf); - *buf = GST_BUFFER (v4l2buf); - return GST_FLOW_OK; - } else { - GST_DEBUG_OBJECT (v4l2sink, "failed to allocate buffer"); - return GST_FLOW_ERROR; - } + if (pool) + gst_object_unref (pool); - } else { - GST_ERROR_OBJECT (v4l2sink, "only supporting streaming mode for now..."); - return GST_FLOW_ERROR; + return TRUE; + + /* ERRORS */ +no_caps: + { + GST_DEBUG_OBJECT (v4l2sink, "no caps specified"); + return FALSE; + } +different_caps: + { + /* different caps, we can't use this pool */ + GST_DEBUG_OBJECT (v4l2sink, "pool has different caps"); + return FALSE; } } @@ -838,92 +679,26 @@ gst_v4l2sink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, static GstFlowReturn gst_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buf) { + GstFlowReturn ret; GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink); - GstBuffer *newbuf = NULL; + GstV4l2Object *obj = v4l2sink->v4l2object; GST_DEBUG_OBJECT (v4l2sink, "render buffer: %p", buf); - if (!GST_IS_V4L2_BUFFER (buf)) { - GstFlowReturn ret; - - /* special case check for sub-buffers: In certain cases, places like - * GstBaseTransform, which might check that the buffer is writable - * before copying metadata, timestamp, and such, will find that the - * buffer has more than one reference to it. In these cases, they - * will create a sub-buffer with an offset=0 and length equal to the - * original buffer size. - * - * This could happen in two scenarios: (1) a tee in the pipeline, and - * (2) because the refcnt is incremented in gst_mini_object_free() - * before the finalize function is called, and decremented after it - * returns.. but returning this buffer to the buffer pool in the - * finalize function, could wake up a thread blocked in _buffer_alloc() - * which could run and get a buffer w/ refcnt==2 before the thread - * originally unref'ing the buffer returns from finalize function and - * decrements the refcnt back to 1! - */ - if (buf->parent && - (GST_BUFFER_DATA (buf) == GST_BUFFER_DATA (buf->parent)) && - (GST_BUFFER_SIZE (buf) == GST_BUFFER_SIZE (buf->parent))) { - GST_DEBUG_OBJECT (v4l2sink, "I have a sub-buffer!"); - return gst_v4l2sink_show_frame (bsink, buf->parent); - } - - GST_DEBUG_OBJECT (v4l2sink, "slow-path.. I got a %s so I need to memcpy", - g_type_name (G_OBJECT_TYPE (buf))); - - ret = gst_v4l2sink_buffer_alloc (bsink, - GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf), GST_BUFFER_CAPS (buf), - &newbuf); - - if (GST_FLOW_OK != ret) { - GST_DEBUG_OBJECT (v4l2sink, - "dropping frame! Consider increasing 'queue-size' property!"); - return GST_FLOW_OK; - } - - memcpy (GST_BUFFER_DATA (newbuf), - GST_BUFFER_DATA (buf), - MIN (GST_BUFFER_SIZE (newbuf), GST_BUFFER_SIZE (buf))); + if (G_UNLIKELY (obj->pool == NULL)) + goto not_negotiated; - GST_DEBUG_OBJECT (v4l2sink, "render copied buffer: %p", newbuf); + ret = + gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL_CAST (obj->pool), buf); - buf = newbuf; - } - - if (!gst_v4l2_buffer_pool_qbuf (v4l2sink->pool, GST_V4L2_BUFFER (buf))) { - return GST_FLOW_ERROR; - } - if (v4l2sink->state == STATE_PENDING_STREAMON) { - if (!gst_v4l2_object_start_streaming (v4l2sink->v4l2object)) { - return GST_FLOW_ERROR; - } - v4l2sink->state = STATE_STREAMING; - } - - if (!newbuf) { - gst_buffer_ref (buf); - } + return ret; - /* if the driver has more than one buffer, ie. more than just the one we - * just queued, then dequeue one immediately to make it available via - * _buffer_alloc(): - */ - if (gst_v4l2_buffer_pool_available_buffers (v4l2sink->pool) > - v4l2sink->min_queued_bufs) { - GstV4l2Buffer *v4l2buf = gst_v4l2_buffer_pool_dqbuf (v4l2sink->pool); - - /* note: if we get a buf, we don't want to use it directly (because - * someone else could still hold a ref).. but instead we release our - * reference to it, and if no one else holds a ref it will be returned - * to the pool of available buffers.. and if not, we keep looping. - */ - if (v4l2buf) { - gst_buffer_unref (GST_BUFFER (v4l2buf)); - } + /* ERRORS */ +not_negotiated: + { + GST_ERROR_OBJECT (bsink, "not negotiated"); + return GST_FLOW_NOT_NEGOTIATED; } - - return GST_FLOW_OK; } #ifdef HAVE_XVIDEO @@ -942,7 +717,7 @@ gst_v4l2sink_navigation_send_event (GstNavigation * navigation, GstVideoRectangle rect; gdouble x, y, xscale = 1.0, yscale = 1.0; - gst_v4l2_xoverlay_get_render_rect (v4l2sink->v4l2object, &rect); + gst_v4l2_video_overlay_get_render_rect (v4l2sink->v4l2object, &rect); /* We calculate scaling using the original video frames geometry to * include pixel aspect ratio scaling. diff --git a/sys/v4l2/gstv4l2sink.h b/sys/v4l2/gstv4l2sink.h index 8fe82221ae..b461e2c922 100644 --- a/sys/v4l2/gstv4l2sink.h +++ b/sys/v4l2/gstv4l2sink.h @@ -55,10 +55,6 @@ struct _GstV4l2Sink { /*< private >*/ GstV4l2Object * v4l2object; GstCaps *probed_caps; /* all supported caps of underlying v4l2 device */ - GstCaps *current_caps; /* the current negotiated caps */ - GstV4l2BufferPool *pool; - guint32 num_buffers; - guint32 min_queued_bufs; gint video_width, video_height; /* original (unscaled) video w/h */ @@ -73,8 +69,6 @@ struct _GstV4l2Sink { * setting properties: */ guint8 overlay_fields_set, crop_fields_set; - - guint8 state; }; struct _GstV4l2SinkClass { diff --git a/sys/v4l2/gstv4l2src.c b/sys/v4l2/gstv4l2src.c index f8ae09ccc1..a27ad3a71c 100644 --- a/sys/v4l2/gstv4l2src.c +++ b/sys/v4l2/gstv4l2src.c @@ -48,9 +48,13 @@ #include <string.h> #include <sys/time.h> -#include "v4l2src_calls.h" #include <unistd.h> +#include "gst/video/gstvideometa.h" +#include "gst/video/gstvideopool.h" + +#include "gstv4l2src.h" + #include "gstv4l2colorbalance.h" #include "gstv4l2tuner.h" #ifdef HAVE_XVIDEO @@ -63,7 +67,6 @@ GST_DEBUG_CATEGORY (v4l2src_debug); #define GST_CAT_DEFAULT v4l2src_debug -#define PROP_DEF_QUEUE_SIZE 2 #define PROP_DEF_ALWAYS_COPY TRUE #define PROP_DEF_DECIMATE 1 @@ -73,12 +76,10 @@ enum { PROP_0, V4L2_STD_OBJECT_PROPS, - PROP_QUEUE_SIZE, PROP_ALWAYS_COPY, PROP_DECIMATE }; -GST_IMPLEMENT_V4L2_PROBE_METHODS (GstV4l2SrcClass, gst_v4l2src); GST_IMPLEMENT_V4L2_COLOR_BALANCE_METHODS (GstV4l2Src, gst_v4l2src); GST_IMPLEMENT_V4L2_TUNER_METHODS (GstV4l2Src, gst_v4l2src); #ifdef HAVE_XVIDEO @@ -89,104 +90,19 @@ GST_IMPLEMENT_V4L2_VIDORIENT_METHODS (GstV4l2Src, gst_v4l2src); static void gst_v4l2src_uri_handler_init (gpointer g_iface, gpointer iface_data); -static gboolean -gst_v4l2src_iface_supported (GstImplementsInterface * iface, GType iface_type) -{ - GstV4l2Object *v4l2object = GST_V4L2SRC (iface)->v4l2object; - -#ifdef HAVE_XVIDEO - if (!(iface_type == GST_TYPE_TUNER || - iface_type == GST_TYPE_X_OVERLAY || - iface_type == GST_TYPE_COLOR_BALANCE || - iface_type == GST_TYPE_VIDEO_ORIENTATION)) - return FALSE; -#else - if (!(iface_type == GST_TYPE_TUNER || - iface_type == GST_TYPE_COLOR_BALANCE || - iface_type == GST_TYPE_VIDEO_ORIENTATION)) - return FALSE; -#endif - - if (v4l2object->video_fd == -1) - return FALSE; - -#ifdef HAVE_XVIDEO - if (iface_type == GST_TYPE_X_OVERLAY && !GST_V4L2_IS_OVERLAY (v4l2object)) - return FALSE; -#endif - - return TRUE; -} - -static void -gst_v4l2src_interface_init (GstImplementsInterfaceClass * klass) -{ - /* - * default virtual functions - */ - klass->supported = gst_v4l2src_iface_supported; -} - -static void -gst_v4l2src_init_interfaces (GType type) -{ - static const GInterfaceInfo urihandler_info = { - gst_v4l2src_uri_handler_init, - NULL, - NULL - }; - - static const GInterfaceInfo v4l2iface_info = { - (GInterfaceInitFunc) gst_v4l2src_interface_init, - NULL, - NULL, - }; - static const GInterfaceInfo v4l2_tuner_info = { - (GInterfaceInitFunc) gst_v4l2src_tuner_interface_init, - NULL, - NULL, - }; +#define gst_v4l2src_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstV4l2Src, gst_v4l2src, GST_TYPE_PUSH_SRC, + G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_v4l2src_uri_handler_init); + G_IMPLEMENT_INTERFACE (GST_TYPE_TUNER, gst_v4l2src_tuner_interface_init); #ifdef HAVE_XVIDEO - /* FIXME: does GstXOverlay for v4l2src make sense in a GStreamer context? */ - static const GInterfaceInfo v4l2_xoverlay_info = { - (GInterfaceInitFunc) gst_v4l2src_xoverlay_interface_init, - NULL, - NULL, - }; + /* FIXME: does GstXOverlay for v4l2src make sense in a GStreamer context? */ + G_IMPLEMENT_INTERFACE (GST_TYPE_X_OVERLAY, + gst_v4l2src_xoverlay_interface_init); #endif - static const GInterfaceInfo v4l2_colorbalance_info = { - (GInterfaceInitFunc) gst_v4l2src_color_balance_interface_init, - NULL, - NULL, - }; - static const GInterfaceInfo v4l2_videoorientation_info = { - (GInterfaceInitFunc) gst_v4l2src_video_orientation_interface_init, - NULL, - NULL, - }; - static const GInterfaceInfo v4l2_propertyprobe_info = { - (GInterfaceInitFunc) gst_v4l2src_property_probe_interface_init, - NULL, - NULL, - }; - - g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info); - g_type_add_interface_static (type, - GST_TYPE_IMPLEMENTS_INTERFACE, &v4l2iface_info); - g_type_add_interface_static (type, GST_TYPE_TUNER, &v4l2_tuner_info); -#ifdef HAVE_XVIDEO - g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &v4l2_xoverlay_info); -#endif - g_type_add_interface_static (type, - GST_TYPE_COLOR_BALANCE, &v4l2_colorbalance_info); - g_type_add_interface_static (type, - GST_TYPE_VIDEO_ORIENTATION, &v4l2_videoorientation_info); - g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE, - &v4l2_propertyprobe_info); -} - -GST_BOILERPLATE_FULL (GstV4l2Src, gst_v4l2src, GstPushSrc, GST_TYPE_PUSH_SRC, - gst_v4l2src_init_interfaces); + G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE, + gst_v4l2src_color_balance_interface_init); + G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_ORIENTATION, + gst_v4l2src_video_orientation_interface_init)); static void gst_v4l2src_dispose (GObject * object); static void gst_v4l2src_finalize (GstV4l2Src * v4l2src); @@ -201,10 +117,12 @@ static gboolean gst_v4l2src_unlock (GstBaseSrc * src); static gboolean gst_v4l2src_unlock_stop (GstBaseSrc * src); static gboolean gst_v4l2src_stop (GstBaseSrc * src); static gboolean gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps); -static GstCaps *gst_v4l2src_get_caps (GstBaseSrc * src); +static GstCaps *gst_v4l2src_get_caps (GstBaseSrc * src, GstCaps * filter); static gboolean gst_v4l2src_query (GstBaseSrc * bsrc, GstQuery * query); -static GstFlowReturn gst_v4l2src_create (GstPushSrc * src, GstBuffer ** out); -static void gst_v4l2src_fixate (GstBaseSrc * basesrc, GstCaps * caps); +static gboolean gst_v4l2src_decide_allocation (GstBaseSrc * src, + GstQuery * query); +static GstFlowReturn gst_v4l2src_fill (GstPushSrc * src, GstBuffer * out); +static GstCaps *gst_v4l2src_fixate (GstBaseSrc * basesrc, GstCaps * caps); static gboolean gst_v4l2src_negotiate (GstBaseSrc * basesrc); static void gst_v4l2src_set_property (GObject * object, guint prop_id, @@ -212,36 +130,6 @@ static void gst_v4l2src_set_property (GObject * object, guint prop_id, static void gst_v4l2src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -/* get_frame io methods */ -static GstFlowReturn -gst_v4l2src_get_read (GstV4l2Src * v4l2src, GstBuffer ** buf); -static GstFlowReturn -gst_v4l2src_get_mmap (GstV4l2Src * v4l2src, GstBuffer ** buf); - -static void -gst_v4l2src_base_init (gpointer g_class) -{ - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); - GstV4l2SrcClass *gstv4l2src_class = GST_V4L2SRC_CLASS (g_class); - GstPadTemplate *pad_template; - - gstv4l2src_class->v4l2_class_devices = NULL; - - GST_DEBUG_CATEGORY_INIT (v4l2src_debug, "v4l2src", 0, "V4L2 source element"); - - gst_element_class_set_details_simple (gstelement_class, - "Video (video4linux2) Source", "Source/Video", - "Reads frames from a Video4Linux2 device", - "Edgard Lima <edgard.lima@indt.org.br>," - " Stefan Kost <ensonic@users.sf.net>"); - - pad_template = - gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - gst_v4l2_object_get_all_caps ()); - gst_element_class_add_pad_template (gstelement_class, pad_template); - gst_object_unref (pad_template); -} - static void gst_v4l2src_class_init (GstV4l2SrcClass * klass) { @@ -264,11 +152,6 @@ gst_v4l2src_class_init (GstV4l2SrcClass * klass) gst_v4l2_object_install_properties_helper (gobject_class, DEFAULT_PROP_DEVICE); - g_object_class_install_property (gobject_class, PROP_QUEUE_SIZE, - g_param_spec_uint ("queue-size", "Queue size", - "Number of buffers to be enqueud in the driver in streaming mode", - GST_V4L2_MIN_BUFFERS, GST_V4L2_MAX_BUFFERS, PROP_DEF_QUEUE_SIZE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_ALWAYS_COPY, g_param_spec_boolean ("always-copy", "Always Copy", "If the buffer will or not be used directly from mmap", @@ -285,6 +168,17 @@ gst_v4l2src_class_init (GstV4l2SrcClass * klass) "Only use every nth frame", 1, G_MAXINT, PROP_DEF_DECIMATE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gst_element_class_set_details_simple (element_class, + "Video (video4linux2) Source", "Source/Video", + "Reads frames from a Video4Linux2 device", + "Edgard Lima <edgard.lima@indt.org.br>, " + "Stefan Kost <ensonic@users.sf.net>"); + + gst_element_class_add_pad_template + (element_class, + gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + gst_v4l2_object_get_all_caps ())); + basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_v4l2src_get_caps); basesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_v4l2src_set_caps); basesrc_class->start = GST_DEBUG_FUNCPTR (gst_v4l2src_start); @@ -294,34 +188,31 @@ gst_v4l2src_class_init (GstV4l2SrcClass * klass) basesrc_class->query = GST_DEBUG_FUNCPTR (gst_v4l2src_query); basesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_v4l2src_fixate); basesrc_class->negotiate = GST_DEBUG_FUNCPTR (gst_v4l2src_negotiate); + basesrc_class->decide_allocation = + GST_DEBUG_FUNCPTR (gst_v4l2src_decide_allocation); + + pushsrc_class->fill = GST_DEBUG_FUNCPTR (gst_v4l2src_fill); + + klass->v4l2_class_devices = NULL; - pushsrc_class->create = GST_DEBUG_FUNCPTR (gst_v4l2src_create); + GST_DEBUG_CATEGORY_INIT (v4l2src_debug, "v4l2src", 0, "V4L2 source element"); } static void -gst_v4l2src_init (GstV4l2Src * v4l2src, GstV4l2SrcClass * klass) +gst_v4l2src_init (GstV4l2Src * v4l2src) { /* fixme: give an update_fps_function */ v4l2src->v4l2object = gst_v4l2_object_new (GST_ELEMENT (v4l2src), V4L2_BUF_TYPE_VIDEO_CAPTURE, DEFAULT_PROP_DEVICE, gst_v4l2_get_input, gst_v4l2_set_input, NULL); - /* number of buffers requested */ - v4l2src->num_buffers = PROP_DEF_QUEUE_SIZE; - - v4l2src->always_copy = PROP_DEF_ALWAYS_COPY; + v4l2src->v4l2object->always_copy = PROP_DEF_ALWAYS_COPY; v4l2src->decimate = PROP_DEF_DECIMATE; - v4l2src->is_capturing = FALSE; - gst_base_src_set_format (GST_BASE_SRC (v4l2src), GST_FORMAT_TIME); gst_base_src_set_live (GST_BASE_SRC (v4l2src), TRUE); - - v4l2src->fps_d = 0; - v4l2src->fps_n = 0; } - static void gst_v4l2src_dispose (GObject * object) { @@ -353,11 +244,8 @@ gst_v4l2src_set_property (GObject * object, if (!gst_v4l2_object_set_property_helper (v4l2src->v4l2object, prop_id, value, pspec)) { switch (prop_id) { - case PROP_QUEUE_SIZE: - v4l2src->num_buffers = g_value_get_uint (value); - break; case PROP_ALWAYS_COPY: - v4l2src->always_copy = g_value_get_boolean (value); + v4l2src->v4l2object->always_copy = g_value_get_boolean (value); break; case PROP_DECIMATE: v4l2src->decimate = g_value_get_int (value); @@ -369,7 +257,6 @@ gst_v4l2src_set_property (GObject * object, } } - static void gst_v4l2src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) @@ -379,11 +266,8 @@ gst_v4l2src_get_property (GObject * object, if (!gst_v4l2_object_get_property_helper (v4l2src->v4l2object, prop_id, value, pspec)) { switch (prop_id) { - case PROP_QUEUE_SIZE: - g_value_set_uint (value, v4l2src->num_buffers); - break; case PROP_ALWAYS_COPY: - g_value_set_boolean (value, v4l2src->always_copy); + g_value_set_boolean (value, v4l2src->v4l2object->always_copy); break; case PROP_DECIMATE: g_value_set_int (value, v4l2src->decimate); @@ -395,9 +279,8 @@ gst_v4l2src_get_property (GObject * object, } } - /* this function is a bit of a last resort */ -static void +static GstCaps * gst_v4l2src_fixate (GstBaseSrc * basesrc, GstCaps * caps) { GstStructure *structure; @@ -405,34 +288,25 @@ gst_v4l2src_fixate (GstBaseSrc * basesrc, GstCaps * caps) GST_DEBUG_OBJECT (basesrc, "fixating caps %" GST_PTR_FORMAT, caps); - for (i = 0; i < gst_caps_get_size (caps); ++i) { - const GValue *v; + caps = gst_caps_make_writable (caps); + for (i = 0; i < gst_caps_get_size (caps); ++i) { structure = gst_caps_get_structure (caps, i); - /* FIXME such sizes? we usually fixate to something in the 320x200 - * range... */ - /* We are fixating to greater possble size (limited to GST_V4L2_MAX_SIZE) + /* We are fixating to a resonable 320x200 resolution and the maximum framerate resolution for that size */ - gst_structure_fixate_field_nearest_int (structure, "width", - GST_V4L2_MAX_SIZE); - gst_structure_fixate_field_nearest_int (structure, "height", - GST_V4L2_MAX_SIZE); + gst_structure_fixate_field_nearest_int (structure, "width", 320); + gst_structure_fixate_field_nearest_int (structure, "height", 200); gst_structure_fixate_field_nearest_fraction (structure, "framerate", G_MAXINT, 1); - - v = gst_structure_get_value (structure, "format"); - if (v && G_VALUE_TYPE (v) != GST_TYPE_FOURCC) { - guint32 fourcc; - - g_return_if_fail (G_VALUE_TYPE (v) == GST_TYPE_LIST); - - fourcc = gst_value_get_fourcc (gst_value_list_get_value (v, 0)); - gst_structure_set (structure, "format", GST_TYPE_FOURCC, fourcc, NULL); - } + gst_structure_fixate_field (structure, "format"); } GST_DEBUG_OBJECT (basesrc, "fixated caps %" GST_PTR_FORMAT, caps); + + caps = GST_BASE_SRC_CLASS (parent_class)->fixate (basesrc, caps); + + return caps; } @@ -445,7 +319,7 @@ gst_v4l2src_negotiate (GstBaseSrc * basesrc) gboolean result = FALSE; /* first see what is possible on our source pad */ - thiscaps = gst_pad_get_caps (GST_BASE_SRC_PAD (basesrc)); + thiscaps = gst_pad_query_caps (GST_BASE_SRC_PAD (basesrc), NULL); GST_DEBUG_OBJECT (basesrc, "caps of src: %" GST_PTR_FORMAT, thiscaps); LOG_CAPS (basesrc, thiscaps); @@ -454,7 +328,7 @@ gst_v4l2src_negotiate (GstBaseSrc * basesrc) goto no_nego_needed; /* get the peer caps */ - peercaps = gst_pad_peer_get_caps (GST_BASE_SRC_PAD (basesrc)); + peercaps = gst_pad_peer_query_caps (GST_BASE_SRC_PAD (basesrc), thiscaps); GST_DEBUG_OBJECT (basesrc, "caps of peer: %" GST_PTR_FORMAT, peercaps); LOG_CAPS (basesrc, peercaps); if (peercaps && !gst_caps_is_any (peercaps)) { @@ -486,11 +360,8 @@ gst_v4l2src_negotiate (GstBaseSrc * basesrc) * resolution strictly bigger then the first peer caps */ if (gst_caps_get_size (icaps) > 1) { GstStructure *s = gst_caps_get_structure (peercaps, 0); - int best = 0; - int twidth, theight; - int width = G_MAXINT, height = G_MAXINT; if (gst_structure_get_int (s, "width", &twidth) @@ -501,7 +372,6 @@ gst_v4l2src_negotiate (GstBaseSrc * basesrc) */ for (i = gst_caps_get_size (icaps) - 1; i >= 0; i--) { GstStructure *is = gst_caps_get_structure (icaps, i); - int w, h; if (gst_structure_get_int (is, "width", &w) @@ -529,12 +399,11 @@ gst_v4l2src_negotiate (GstBaseSrc * basesrc) if (peercaps) gst_caps_unref (peercaps); if (caps) { - caps = gst_caps_make_writable (caps); - gst_caps_truncate (caps); + caps = gst_caps_truncate (caps); /* now fixate */ if (!gst_caps_is_empty (caps)) { - gst_pad_fixate_caps (GST_BASE_SRC_PAD (basesrc), caps); + caps = gst_v4l2src_fixate (basesrc, caps); GST_DEBUG_OBJECT (basesrc, "fixated to: %" GST_PTR_FORMAT, caps); LOG_CAPS (basesrc, caps); @@ -544,8 +413,7 @@ gst_v4l2src_negotiate (GstBaseSrc * basesrc) result = TRUE; } else if (gst_caps_is_fixed (caps)) { /* yay, fixed caps, use those then */ - if (gst_pad_set_caps (GST_BASE_SRC_PAD (basesrc), caps)) - result = TRUE; + result = gst_base_src_set_caps (basesrc, caps); } } gst_caps_unref (caps); @@ -562,14 +430,18 @@ no_nego_needed: } static GstCaps * -gst_v4l2src_get_caps (GstBaseSrc * src) +gst_v4l2src_get_caps (GstBaseSrc * src, GstCaps * filter) { - GstV4l2Src *v4l2src = GST_V4L2SRC (src); + GstV4l2Src *v4l2src; + GstV4l2Object *obj; GstCaps *ret; GSList *walk; GSList *formats; - if (!GST_V4L2_IS_OPEN (v4l2src->v4l2object)) { + v4l2src = GST_V4L2SRC (src); + obj = v4l2src->v4l2object; + + if (!GST_V4L2_IS_OPEN (obj)) { /* FIXME: copy? */ return gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD @@ -579,13 +451,12 @@ gst_v4l2src_get_caps (GstBaseSrc * src) if (v4l2src->probed_caps) return gst_caps_ref (v4l2src->probed_caps); - formats = gst_v4l2_object_get_format_list (v4l2src->v4l2object); + formats = gst_v4l2_object_get_format_list (obj); ret = gst_caps_new_empty (); for (walk = formats; walk; walk = walk->next) { struct v4l2_fmtdesc *format; - GstStructure *template; format = (struct v4l2_fmtdesc *) walk->data; @@ -596,7 +467,7 @@ gst_v4l2src_get_caps (GstBaseSrc * src) GstCaps *tmp; tmp = - gst_v4l2_object_probe_caps_for_format (v4l2src->v4l2object, + gst_v4l2_object_probe_caps_for_format (obj, format->pixelformat, template); if (tmp) gst_caps_append (ret, tmp); @@ -618,57 +489,108 @@ static gboolean gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps) { GstV4l2Src *v4l2src; - gint w = 0, h = 0; - gboolean interlaced; - struct v4l2_fmtdesc *format; - guint fps_n, fps_d; - guint size; + GstV4l2Object *obj; v4l2src = GST_V4L2SRC (src); - - /* if we're not open, punt -- we'll get setcaps'd later via negotiate */ - if (!GST_V4L2_IS_OPEN (v4l2src->v4l2object)) - return FALSE; + obj = v4l2src->v4l2object; /* make sure we stop capturing and dealloc buffers */ - if (GST_V4L2_IS_ACTIVE (v4l2src->v4l2object)) { - /* both will throw an element-error on failure */ - if (!gst_v4l2src_capture_stop (v4l2src)) - return FALSE; - if (!gst_v4l2src_capture_deinit (v4l2src)) - return FALSE; - } - - /* we want our own v4l2 type of fourcc codes */ - if (!gst_v4l2_object_get_caps_info (v4l2src->v4l2object, caps, &format, &w, - &h, &interlaced, &fps_n, &fps_d, &size)) { - GST_INFO_OBJECT (v4l2src, - "can't get capture format from caps %" GST_PTR_FORMAT, caps); + if (!gst_v4l2_object_stop (obj)) return FALSE; - } - GST_DEBUG_OBJECT (v4l2src, "trying to set_capture %dx%d at %d/%d fps, " - "format %s", w, h, fps_n, fps_d, format->description); - - if (!gst_v4l2src_set_capture (v4l2src, format->pixelformat, w, h, - interlaced, fps_n, fps_d)) + if (!gst_v4l2_object_set_format (obj, caps)) /* error already posted */ return FALSE; - if (!gst_v4l2src_capture_init (v4l2src, caps)) - return FALSE; + return TRUE; +} + +static gboolean +gst_v4l2src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query) +{ + GstV4l2Src *src; + GstV4l2Object *obj; + GstBufferPool *pool; + guint size, min, max; + gboolean update; - if (v4l2src->use_mmap) { - v4l2src->get_frame = gst_v4l2src_get_mmap; + src = GST_V4L2SRC (bsrc); + obj = src->v4l2object; + + if (gst_query_get_n_allocation_pools (query) > 0) { + gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); + update = TRUE; } else { - v4l2src->get_frame = gst_v4l2src_get_read; + pool = NULL; + min = max = 0; + size = 0; + update = FALSE; } - if (!gst_v4l2src_capture_start (v4l2src)) - return FALSE; + GST_DEBUG_OBJECT (src, "allocation: size:%u min:%u max:%u pool:%" + GST_PTR_FORMAT, size, min, max, pool); - /* now store the expected output size */ - v4l2src->frame_byte_size = size; + if (min != 0) { + /* if there is a min-buffers suggestion, use it. We add 1 because we need 1 + * buffer extra to capture while the other two buffers are downstream */ + min += 1; + } else { + min = 2; + } + + /* select a pool */ + switch (obj->mode) { + case GST_V4L2_IO_RW: + if (pool == NULL) { + /* no downstream pool, use our own then */ + GST_DEBUG_OBJECT (src, + "read/write mode: no downstream pool, using our own"); + pool = GST_BUFFER_POOL_CAST (obj->pool); + size = obj->sizeimage; + } else { + /* in READ/WRITE mode, prefer a downstream pool because our own pool + * doesn't help much, we have to write to it as well */ + GST_DEBUG_OBJECT (src, "read/write mode: using downstream pool"); + /* use the bigest size, when we use our own pool we can't really do any + * other size than what the hardware gives us but for downstream pools + * we can try */ + size = MAX (size, obj->sizeimage); + } + break; + case GST_V4L2_IO_MMAP: + case GST_V4L2_IO_USERPTR: + /* in streaming mode, prefer our own pool */ + pool = GST_BUFFER_POOL_CAST (obj->pool); + size = obj->sizeimage; + GST_DEBUG_OBJECT (src, + "streaming mode: using our own pool %" GST_PTR_FORMAT, pool); + break; + case GST_V4L2_IO_AUTO: + default: + GST_WARNING_OBJECT (src, "unhandled mode"); + break; + } + + if (pool) { + GstStructure *config; + const GstCaps *caps; + + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL); + gst_buffer_pool_config_set_params (config, caps, size, min, max); + + /* if downstream supports video metadata, add this to the pool config */ + if (gst_query_has_allocation_meta (query, GST_VIDEO_META_API_TYPE)) + gst_buffer_pool_config_add_option (config, + GST_BUFFER_POOL_OPTION_VIDEO_META); + + gst_buffer_pool_set_config (pool, config); + } + + if (update) + gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max); + else + gst_query_add_allocation_pool (query, pool, size, min, max); return TRUE; } @@ -677,35 +599,40 @@ static gboolean gst_v4l2src_query (GstBaseSrc * bsrc, GstQuery * query) { GstV4l2Src *src; - + GstV4l2Object *obj; gboolean res = FALSE; src = GST_V4L2SRC (bsrc); + obj = src->v4l2object; switch (GST_QUERY_TYPE (query)) { case GST_QUERY_LATENCY:{ GstClockTime min_latency, max_latency; + guint32 fps_n, fps_d; /* device must be open */ - if (!GST_V4L2_IS_OPEN (src->v4l2object)) { + if (!GST_V4L2_IS_OPEN (obj)) { GST_WARNING_OBJECT (src, "Can't give latency since device isn't open !"); goto done; } + fps_n = GST_V4L2_FPS_N (obj); + fps_d = GST_V4L2_FPS_D (obj); + /* we must have a framerate */ - if (src->fps_n <= 0 || src->fps_d <= 0) { + if (fps_n <= 0 || fps_d <= 0) { GST_WARNING_OBJECT (src, "Can't give latency since framerate isn't fixated !"); goto done; } /* min latency is the time to capture one frame */ - min_latency = - gst_util_uint64_scale_int (GST_SECOND, src->fps_d, src->fps_n); + min_latency = gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n); /* max latency is total duration of the frame buffer */ - max_latency = src->num_buffers * min_latency; + max_latency = + GST_V4L2_BUFFER_POOL_CAST (obj->pool)->max_buffers * min_latency; GST_DEBUG_OBJECT (bsrc, "report latency min %" GST_TIME_FORMAT " max %" GST_TIME_FORMAT, @@ -741,7 +668,7 @@ gst_v4l2src_start (GstBaseSrc * src) /* activate settings for first frame */ v4l2src->ctrl_time = 0; - gst_object_sync_values (G_OBJECT (src), v4l2src->ctrl_time); + gst_object_sync_values (GST_OBJECT (src), v4l2src->ctrl_time); return TRUE; } @@ -750,41 +677,26 @@ static gboolean gst_v4l2src_unlock (GstBaseSrc * src) { GstV4l2Src *v4l2src = GST_V4L2SRC (src); - - GST_LOG_OBJECT (src, "Flushing"); - gst_poll_set_flushing (v4l2src->v4l2object->poll, TRUE); - - return TRUE; + return gst_v4l2_object_unlock (v4l2src->v4l2object); } static gboolean gst_v4l2src_unlock_stop (GstBaseSrc * src) { GstV4l2Src *v4l2src = GST_V4L2SRC (src); - - GST_LOG_OBJECT (src, "No longer flushing"); - gst_poll_set_flushing (v4l2src->v4l2object->poll, FALSE); - - return TRUE; + return gst_v4l2_object_unlock_stop (v4l2src->v4l2object); } static gboolean gst_v4l2src_stop (GstBaseSrc * src) { GstV4l2Src *v4l2src = GST_V4L2SRC (src); + GstV4l2Object *obj = v4l2src->v4l2object; - if (GST_V4L2_IS_ACTIVE (v4l2src->v4l2object) - && !gst_v4l2src_capture_stop (v4l2src)) - return FALSE; - - if (v4l2src->v4l2object->buffer != NULL) { - if (!gst_v4l2src_capture_deinit (v4l2src)) + if (GST_V4L2_IS_ACTIVE (obj)) { + if (!gst_v4l2_object_stop (obj)) return FALSE; } - - v4l2src->fps_d = 0; - v4l2src->fps_n = 0; - return TRUE; } @@ -793,11 +705,12 @@ gst_v4l2src_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; GstV4l2Src *v4l2src = GST_V4L2SRC (element); + GstV4l2Object *obj = v4l2src->v4l2object; switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: /* open the device */ - if (!gst_v4l2_object_start (v4l2src->v4l2object)) + if (!gst_v4l2_object_open (obj)) return GST_STATE_CHANGE_FAILURE; break; default: @@ -809,7 +722,7 @@ gst_v4l2src_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_READY_TO_NULL: /* close the device */ - if (!gst_v4l2_object_stop (v4l2src->v4l2object)) + if (!gst_v4l2_object_close (obj)) return GST_STATE_CHANGE_FAILURE; if (v4l2src->probed_caps) { @@ -825,228 +738,123 @@ gst_v4l2src_change_state (GstElement * element, GstStateChange transition) } static GstFlowReturn -gst_v4l2src_get_read (GstV4l2Src * v4l2src, GstBuffer ** buf) +gst_v4l2src_fill (GstPushSrc * src, GstBuffer * buf) { - gint amount; - gint ret; - - gint buffersize; - - buffersize = v4l2src->frame_byte_size; - /* In case the size per frame is unknown assume it's a streaming format (e.g. - * mpegts) and grab a reasonable default size instead */ - if (buffersize == 0) - buffersize = GST_BASE_SRC (v4l2src)->blocksize; - - *buf = gst_buffer_new_and_alloc (buffersize); - - do { - ret = gst_poll_wait (v4l2src->v4l2object->poll, GST_CLOCK_TIME_NONE); - if (G_UNLIKELY (ret < 0)) { - if (errno == EBUSY) - goto stopped; - if (errno == ENXIO) { - GST_DEBUG_OBJECT (v4l2src, - "v4l2 device doesn't support polling. Disabling"); - v4l2src->v4l2object->can_poll_device = FALSE; - } else { - if (errno != EAGAIN && errno != EINTR) - goto select_error; - } - } - amount = - v4l2_read (v4l2src->v4l2object->video_fd, GST_BUFFER_DATA (*buf), - buffersize); - if (amount == buffersize) { - break; - } else if (amount == -1) { - if (errno == EAGAIN || errno == EINTR) { - continue; - } else - goto read_error; - } else { - /* short reads can happen if a signal interrupts the read */ - continue; - } - } while (TRUE); - - /* we set the buffer metadata in gst_v4l2src_create() */ - - return GST_FLOW_OK; + GstV4l2Src *v4l2src = GST_V4L2SRC (src); + GstV4l2Object *obj = v4l2src->v4l2object; + GstFlowReturn ret; + GstClock *clock; + GstClockTime timestamp, duration; - /* ERRORS */ -select_error: - { - GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, (NULL), - ("select error %d: %s (%d)", ret, g_strerror (errno), errno)); - return GST_FLOW_ERROR; - } -stopped: - { - GST_DEBUG ("stop called"); - return GST_FLOW_WRONG_STATE; - } -read_error: - { - GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, - (_("Error reading %d bytes from device '%s'."), - buffersize, v4l2src->v4l2object->videodev), GST_ERROR_SYSTEM); +#if 0 + int i; + /* decimate, just capture and throw away frames */ + for (i = 0; i < v4l2src->decimate - 1; i++) { + ret = gst_v4l2_buffer_pool_process (obj, buf); + if (ret != GST_FLOW_OK) { + return ret; + } gst_buffer_unref (*buf); - return GST_FLOW_ERROR; } -} +#endif -static GstFlowReturn -gst_v4l2src_get_mmap (GstV4l2Src * v4l2src, GstBuffer ** buf) -{ - GstBuffer *temp; - GstFlowReturn ret; - guint size; - guint count = 0; + ret = + gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL_CAST (obj->pool), buf); -again: - ret = gst_v4l2src_grab_frame (v4l2src, &temp); if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto done; - - if (v4l2src->frame_byte_size > 0) { - size = GST_BUFFER_SIZE (temp); - - /* if size does not match what we expected, try again */ - if (size != v4l2src->frame_byte_size) { - GST_ELEMENT_WARNING (v4l2src, RESOURCE, READ, - (_("Got unexpected frame size of %u instead of %u."), - size, v4l2src->frame_byte_size), (NULL)); - gst_buffer_unref (temp); - if (count++ > 50) - goto size_error; + goto error; - goto again; - } + /* set buffer metadata */ + GST_BUFFER_OFFSET (buf) = v4l2src->offset++; + GST_BUFFER_OFFSET_END (buf) = v4l2src->offset; + + /* timestamps, LOCK to get clock and base time. */ + /* FIXME: element clock and base_time is rarely changing */ + GST_OBJECT_LOCK (v4l2src); + if ((clock = GST_ELEMENT_CLOCK (v4l2src))) { + /* we have a clock, get base time and ref clock */ + timestamp = GST_ELEMENT (v4l2src)->base_time; + gst_object_ref (clock); + } else { + /* no clock, can't set timestamps */ + timestamp = GST_CLOCK_TIME_NONE; } + GST_OBJECT_UNLOCK (v4l2src); - *buf = temp; -done: - return ret; - - /* ERRORS */ -size_error: - { - GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, - (_("Error reading %d bytes on device '%s'."), - v4l2src->frame_byte_size, v4l2src->v4l2object->videodev), (NULL)); - return GST_FLOW_ERROR; - } -} + duration = obj->duration; -static GstFlowReturn -gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf) -{ - GstV4l2Src *v4l2src = GST_V4L2SRC (src); - int i; - GstFlowReturn ret; + if (G_LIKELY (clock)) { + /* the time now is the time of the clock minus the base time */ + timestamp = gst_clock_get_time (clock) - timestamp; + gst_object_unref (clock); - for (i = 0; i < v4l2src->decimate - 1; i++) { - ret = v4l2src->get_frame (v4l2src, buf); - if (ret != GST_FLOW_OK) { - return ret; + /* if we have a framerate adjust timestamp for frame latency */ + if (GST_CLOCK_TIME_IS_VALID (duration)) { + if (timestamp > duration) + timestamp -= duration; + else + timestamp = 0; } - gst_buffer_unref (*buf); } - ret = v4l2src->get_frame (v4l2src, buf); + /* activate settings for next frame */ + if (GST_CLOCK_TIME_IS_VALID (duration)) { + v4l2src->ctrl_time += duration; + } else { + /* this is not very good (as it should be the next timestamp), + * still good enough for linear fades (as long as it is not -1) + */ + v4l2src->ctrl_time = timestamp; + } + gst_object_sync_values (GST_OBJECT (src), v4l2src->ctrl_time); + GST_INFO_OBJECT (src, "sync to %" GST_TIME_FORMAT, + GST_TIME_ARGS (v4l2src->ctrl_time)); - /* set buffer metadata */ - if (G_LIKELY (ret == GST_FLOW_OK && *buf)) { - GstClock *clock; - GstClockTime timestamp; - - GST_BUFFER_OFFSET (*buf) = v4l2src->offset++; - GST_BUFFER_OFFSET_END (*buf) = v4l2src->offset; - - /* timestamps, LOCK to get clock and base time. */ - /* FIXME: element clock and base_time is rarely changing */ - GST_OBJECT_LOCK (v4l2src); - if ((clock = GST_ELEMENT_CLOCK (v4l2src))) { - /* we have a clock, get base time and ref clock */ - timestamp = GST_ELEMENT (v4l2src)->base_time; - gst_object_ref (clock); - } else { - /* no clock, can't set timestamps */ - timestamp = GST_CLOCK_TIME_NONE; - } - GST_OBJECT_UNLOCK (v4l2src); - - if (G_LIKELY (clock)) { - /* the time now is the time of the clock minus the base time */ - timestamp = gst_clock_get_time (clock) - timestamp; - gst_object_unref (clock); - - /* if we have a framerate adjust timestamp for frame latency */ - if (GST_CLOCK_TIME_IS_VALID (v4l2src->duration)) { - if (timestamp > v4l2src->duration) - timestamp -= v4l2src->duration; - else - timestamp = 0; - } - } + /* FIXME: use the timestamp from the buffer itself! */ + GST_BUFFER_TIMESTAMP (buf) = timestamp; + GST_BUFFER_DURATION (buf) = duration; - /* activate settings for next frame */ - if (GST_CLOCK_TIME_IS_VALID (v4l2src->duration)) { - v4l2src->ctrl_time += v4l2src->duration; - } else { - /* this is not very good (as it should be the next timestamp), - * still good enough for linear fades (as long as it is not -1) - */ - v4l2src->ctrl_time = timestamp; - } - gst_object_sync_values (G_OBJECT (src), v4l2src->ctrl_time); - GST_INFO_OBJECT (src, "sync to %" GST_TIME_FORMAT, - GST_TIME_ARGS (v4l2src->ctrl_time)); + return ret; - /* FIXME: use the timestamp from the buffer itself! */ - GST_BUFFER_TIMESTAMP (*buf) = timestamp; - GST_BUFFER_DURATION (*buf) = v4l2src->duration; + /* ERROR */ +error: + { + GST_ERROR_OBJECT (src, "error processing buffer"); + return ret; } - return ret; } /* GstURIHandler interface */ static GstURIType -gst_v4l2src_uri_get_type (void) +gst_v4l2src_uri_get_type (GType type) { return GST_URI_SRC; } -static gchar ** -gst_v4l2src_uri_get_protocols (void) +static const gchar *const * +gst_v4l2src_uri_get_protocols (GType type) { - static gchar *protocols[] = { (char *) "v4l2", NULL }; + static const gchar *protocols[] = { "v4l2", NULL }; return protocols; } -static const gchar * +static gchar * gst_v4l2src_uri_get_uri (GstURIHandler * handler) { GstV4l2Src *v4l2src = GST_V4L2SRC (handler); if (v4l2src->v4l2object->videodev != NULL) { - gchar uri[256]; - - /* need to return a const string, but also don't want to leak the generated - * string, so just intern it - there's a limited number of video devices - * after all */ - g_snprintf (uri, sizeof (uri), "v4l2://%s", v4l2src->v4l2object->videodev); - return g_intern_string (uri); + return g_strdup_printf ("v4l2://%s", v4l2src->v4l2object->videodev); } - return "v4l2://"; + return g_strdup ("v4l2://"); } static gboolean -gst_v4l2src_uri_set_uri (GstURIHandler * handler, const gchar * uri) +gst_v4l2src_uri_set_uri (GstURIHandler * handler, const gchar * uri, + GError ** error) { GstV4l2Src *v4l2src = GST_V4L2SRC (handler); const gchar *device = DEFAULT_PROP_DEVICE; diff --git a/sys/v4l2/gstv4l2src.h b/sys/v4l2/gstv4l2src.h index 0dd794a711..8ebaa57692 100644 --- a/sys/v4l2/gstv4l2src.h +++ b/sys/v4l2/gstv4l2src.h @@ -45,8 +45,6 @@ G_BEGIN_DECLS typedef struct _GstV4l2Src GstV4l2Src; typedef struct _GstV4l2SrcClass GstV4l2SrcClass; -typedef GstFlowReturn (*GstV4l2SrcGetFunc)(GstV4l2Src * v4l2src, GstBuffer ** buf); - /** * GstV4l2Src: * @@ -62,30 +60,11 @@ struct _GstV4l2Src /* pads */ GstCaps *probed_caps; - /* buffer handling */ - GstV4l2BufferPool *pool; - - guint32 num_buffers; - gboolean use_mmap; - guint32 frame_byte_size; - - /* if the buffer will be or not used from directly mmap */ - gboolean always_copy; - int decimate; - /* True if we want to stop */ - gboolean quit; - gboolean is_capturing; - guint64 offset; - gint fps_d, fps_n; /* framerate if device is open */ - GstClockTime duration; /* duration of one frame */ - GstClockTime ctrl_time; - - GstV4l2SrcGetFunc get_frame; }; struct _GstV4l2SrcClass diff --git a/sys/v4l2/gstv4l2tuner.c b/sys/v4l2/gstv4l2tuner.c index a805396cac..c885dc6740 100644 --- a/sys/v4l2/gstv4l2tuner.c +++ b/sys/v4l2/gstv4l2tuner.c @@ -30,7 +30,6 @@ #include "gstv4l2tuner.h" #include "gstv4l2object.h" #include "v4l2_calls.h" -#include "v4l2src_calls.h" static void gst_v4l2_tuner_channel_class_init (GstV4l2TunerChannelClass * klass); diff --git a/sys/v4l2/gstv4l2tuner.h b/sys/v4l2/gstv4l2tuner.h index 699ca87e10..75d4559e04 100644 --- a/sys/v4l2/gstv4l2tuner.h +++ b/sys/v4l2/gstv4l2tuner.h @@ -179,20 +179,20 @@ interface_as_function ## _tuner_signal_strength (GstTuner * mixer, } \ \ static void \ -interface_as_function ## _tuner_interface_init (GstTunerClass * klass) \ +interface_as_function ## _tuner_interface_init (GstTunerInterface * iface) \ { \ /* default virtual functions */ \ - klass->list_channels = interface_as_function ## _tuner_list_channels; \ - klass->set_channel = interface_as_function ## _tuner_set_channel; \ - klass->get_channel = interface_as_function ## _tuner_get_channel; \ + iface->list_channels = interface_as_function ## _tuner_list_channels; \ + iface->set_channel = interface_as_function ## _tuner_set_channel; \ + iface->get_channel = interface_as_function ## _tuner_get_channel; \ \ - klass->list_norms = interface_as_function ## _tuner_list_norms; \ - klass->set_norm = interface_as_function ## _tuner_set_norm_and_notify; \ - klass->get_norm = interface_as_function ## _tuner_get_norm; \ + iface->list_norms = interface_as_function ## _tuner_list_norms; \ + iface->set_norm = interface_as_function ## _tuner_set_norm_and_notify; \ + iface->get_norm = interface_as_function ## _tuner_get_norm; \ \ - klass->set_frequency = interface_as_function ## _tuner_set_frequency_and_notify; \ - klass->get_frequency = interface_as_function ## _tuner_get_frequency; \ - klass->signal_strength = interface_as_function ## _tuner_signal_strength; \ + iface->set_frequency = interface_as_function ## _tuner_set_frequency_and_notify; \ + iface->get_frequency = interface_as_function ## _tuner_get_frequency; \ + iface->signal_strength = interface_as_function ## _tuner_signal_strength; \ } \ #endif /* __GST_V4L2_TUNER_H__ */ diff --git a/sys/v4l2/gstv4l2xoverlay.c b/sys/v4l2/gstv4l2videooverlay.c index f1c4d5196d..ae9a317918 100644 --- a/sys/v4l2/gstv4l2xoverlay.c +++ b/sys/v4l2/gstv4l2videooverlay.c @@ -1,9 +1,8 @@ /* GStreamer - * * Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net> * 2006 Edgard Lima <edgard.lima@indt.org.br> * - * gstv4l2xoverlay.c: X-based overlay interface implementation for V4L2 + * gstv4l2video_overlay.c: X-based overlay interface implementation for V4L2 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -37,7 +36,7 @@ #include <gst/interfaces/navigation.h> -#include "gstv4l2xoverlay.h" +#include "gstv4l2videooverlay.h" #include "gstv4l2object.h" #include "v4l2_calls.h" @@ -48,21 +47,21 @@ struct _GstV4l2Xv { Display *dpy; gint port, idle_id, event_id; - GMutex *mutex; /* to serialize calls to X11 */ + GMutex mutex; /* to serialize calls to X11 */ }; GST_DEBUG_CATEGORY_STATIC (v4l2xv_debug); #define GST_CAT_DEFAULT v4l2xv_debug void -gst_v4l2_xoverlay_interface_init (GstXOverlayClass * klass) +gst_v4l2_video_overlay_interface_init (GstVideoOverlayInterface * iface) { GST_DEBUG_CATEGORY_INIT (v4l2xv_debug, "v4l2xv", 0, - "V4L2 XOverlay interface debugging"); + "V4L2 GstVideoOverlay interface debugging"); } static void -gst_v4l2_xoverlay_open (GstV4l2Object * v4l2object) +gst_v4l2_video_overlay_open (GstV4l2Object * v4l2object) { struct stat s; GstV4l2Xv *v4l2xv; @@ -134,18 +133,19 @@ gst_v4l2_xoverlay_open (GstV4l2Object * v4l2object) v4l2xv = g_new0 (GstV4l2Xv, 1); v4l2xv->dpy = dpy; v4l2xv->port = id; - v4l2xv->mutex = g_mutex_new (); + g_mutex_init (&v4l2xv->mutex); v4l2xv->idle_id = 0; v4l2xv->event_id = 0; v4l2object->xv = v4l2xv; if (v4l2object->xwindow_id) { - gst_v4l2_xoverlay_set_window_handle (v4l2object, v4l2object->xwindow_id); + gst_v4l2_video_overlay_set_window_handle (v4l2object, + v4l2object->xwindow_id); } } static void -gst_v4l2_xoverlay_close (GstV4l2Object * v4l2object) +gst_v4l2_video_overlay_close (GstV4l2Object * v4l2object) { GstV4l2Xv *v4l2xv = v4l2object->xv; @@ -153,11 +153,11 @@ gst_v4l2_xoverlay_close (GstV4l2Object * v4l2object) return; if (v4l2object->xwindow_id) { - gst_v4l2_xoverlay_set_window_handle (v4l2object, 0); + gst_v4l2_video_overlay_set_window_handle (v4l2object, 0); } XCloseDisplay (v4l2xv->dpy); - g_mutex_free (v4l2xv->mutex); + g_mutex_clear (&v4l2xv->mutex); if (v4l2xv->idle_id) g_source_remove (v4l2xv->idle_id); if (v4l2xv->event_id) @@ -167,17 +167,17 @@ gst_v4l2_xoverlay_close (GstV4l2Object * v4l2object) } void -gst_v4l2_xoverlay_start (GstV4l2Object * v4l2object) +gst_v4l2_video_overlay_start (GstV4l2Object * v4l2object) { if (v4l2object->xwindow_id) { - gst_v4l2_xoverlay_open (v4l2object); + gst_v4l2_video_overlay_open (v4l2object); } } void -gst_v4l2_xoverlay_stop (GstV4l2Object * v4l2object) +gst_v4l2_video_overlay_stop (GstV4l2Object * v4l2object) { - gst_v4l2_xoverlay_close (v4l2object); + gst_v4l2_video_overlay_close (v4l2object); } /* should be called with mutex held */ @@ -200,15 +200,15 @@ get_render_rect (GstV4l2Object * v4l2object, GstVideoRectangle * rect) } gboolean -gst_v4l2_xoverlay_get_render_rect (GstV4l2Object * v4l2object, +gst_v4l2_video_overlay_get_render_rect (GstV4l2Object * v4l2object, GstVideoRectangle * rect) { GstV4l2Xv *v4l2xv = v4l2object->xv; gboolean ret = FALSE; if (v4l2xv) { - g_mutex_lock (v4l2xv->mutex); + g_mutex_lock (&v4l2xv->mutex); ret = get_render_rect (v4l2object, rect); - g_mutex_unlock (v4l2xv->mutex); + g_mutex_unlock (&v4l2xv->mutex); } return ret; } @@ -238,12 +238,12 @@ idle_refresh (gpointer data) GST_LOG_OBJECT (v4l2object->element, "idle refresh"); if (v4l2xv) { - g_mutex_lock (v4l2xv->mutex); + g_mutex_lock (&v4l2xv->mutex); update_geometry (v4l2object); v4l2xv->idle_id = 0; - g_mutex_unlock (v4l2xv->mutex); + g_mutex_unlock (&v4l2xv->mutex); } /* once */ @@ -262,7 +262,7 @@ event_refresh (gpointer data) if (v4l2xv) { XEvent e; - g_mutex_lock (v4l2xv->mutex); + g_mutex_lock (&v4l2xv->mutex); /* If the element supports navigation, collect the relavent input * events and push them upstream as navigation events @@ -289,10 +289,10 @@ event_refresh (gpointer data) if (pointer_moved) { GST_DEBUG_OBJECT (v4l2object->element, "pointer moved over window at %d,%d", pointer_x, pointer_y); - g_mutex_unlock (v4l2xv->mutex); + g_mutex_unlock (&v4l2xv->mutex); gst_navigation_send_mouse_event (GST_NAVIGATION (v4l2object->element), "mouse-move", 0, e.xbutton.x, e.xbutton.y); - g_mutex_lock (v4l2xv->mutex); + g_mutex_lock (&v4l2xv->mutex); } /* We get all events on our window to throw them upstream @@ -303,7 +303,7 @@ event_refresh (gpointer data) KeySym keysym; const char *key_str = NULL; - g_mutex_unlock (v4l2xv->mutex); + g_mutex_unlock (&v4l2xv->mutex); switch (e.type) { case ButtonPress: @@ -324,14 +324,14 @@ event_refresh (gpointer data) break; case KeyPress: case KeyRelease: - g_mutex_lock (v4l2xv->mutex); + g_mutex_lock (&v4l2xv->mutex); keysym = XkbKeycodeToKeysym (v4l2xv->dpy, e.xkey.keycode, 0, 0); if (keysym != NoSymbol) { key_str = XKeysymToString (keysym); } else { key_str = "unknown"; } - g_mutex_unlock (v4l2xv->mutex); + g_mutex_unlock (&v4l2xv->mutex); GST_DEBUG_OBJECT (v4l2object->element, "key %d pressed over window at %d,%d (%s)", e.xkey.keycode, e.xkey.x, e.xkey.y, key_str); @@ -343,7 +343,7 @@ event_refresh (gpointer data) "unhandled X event (%d)", e.type); } - g_mutex_lock (v4l2xv->mutex); + g_mutex_lock (&v4l2xv->mutex); } } @@ -358,7 +358,7 @@ event_refresh (gpointer data) break; } } - g_mutex_unlock (v4l2xv->mutex); + g_mutex_unlock (&v4l2xv->mutex); } /* repeat */ @@ -366,7 +366,8 @@ event_refresh (gpointer data) } void -gst_v4l2_xoverlay_set_window_handle (GstV4l2Object * v4l2object, guintptr id) +gst_v4l2_video_overlay_set_window_handle (GstV4l2Object * v4l2object, + guintptr id) { GstV4l2Xv *v4l2xv; XID xwindow_id = id; @@ -376,12 +377,12 @@ gst_v4l2_xoverlay_set_window_handle (GstV4l2Object * v4l2object, guintptr id) (gulong) xwindow_id); if (!v4l2object->xv && GST_V4L2_IS_OPEN (v4l2object)) - gst_v4l2_xoverlay_open (v4l2object); + gst_v4l2_video_overlay_open (v4l2object); v4l2xv = v4l2object->xv; if (v4l2xv) - g_mutex_lock (v4l2xv->mutex); + g_mutex_lock (&v4l2xv->mutex); if (change) { if (v4l2object->xwindow_id && v4l2xv) { @@ -398,7 +399,7 @@ gst_v4l2_xoverlay_set_window_handle (GstV4l2Object * v4l2object, guintptr id) if (!v4l2xv || xwindow_id == 0) { if (v4l2xv) - g_mutex_unlock (v4l2xv->mutex); + g_mutex_unlock (&v4l2xv->mutex); return; } @@ -416,11 +417,11 @@ gst_v4l2_xoverlay_set_window_handle (GstV4l2Object * v4l2object, guintptr id) if (v4l2xv->idle_id) g_source_remove (v4l2xv->idle_id); v4l2xv->idle_id = g_idle_add (idle_refresh, v4l2object); - g_mutex_unlock (v4l2xv->mutex); + g_mutex_unlock (&v4l2xv->mutex); } /** - * gst_v4l2_xoverlay_prepare_xwindow_id: + * gst_v4l2_video_overlay_prepare_window_handle: * @v4l2object: the v4l2object * @required: %TRUE if display is required (ie. TRUE for v4l2sink, but * FALSE for any other element with optional overlay capabilities) @@ -428,13 +429,16 @@ gst_v4l2_xoverlay_set_window_handle (GstV4l2Object * v4l2object, guintptr id) * Helper function to create a windo if none is set from the application. */ void -gst_v4l2_xoverlay_prepare_xwindow_id (GstV4l2Object * v4l2object, +gst_v4l2_video_overlay_prepare_window_handle (GstV4l2Object * v4l2object, gboolean required) { + GstVideoOverlay *overlay; + if (!GST_V4L2_IS_OVERLAY (v4l2object)) return; - gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (v4l2object->element)); + overlay = GST_VIDEO_OVERLAY (v4l2object->element); + gst_video_overlay_prepare_window_handle (overlay); if (required && !v4l2object->xwindow_id) { GstV4l2Xv *v4l2xv; @@ -443,18 +447,18 @@ gst_v4l2_xoverlay_prepare_xwindow_id (GstV4l2Object * v4l2object, long event_mask; if (!v4l2object->xv && GST_V4L2_IS_OPEN (v4l2object)) - gst_v4l2_xoverlay_open (v4l2object); + gst_v4l2_video_overlay_open (v4l2object); v4l2xv = v4l2object->xv; - /* if xoverlay is not supported, just bail */ + /* if video_overlay is not supported, just bail */ if (!v4l2xv) return; - /* xoverlay is supported, but we don't have a window.. so create one */ + /* video_overlay is supported, but we don't have a window.. so create one */ GST_DEBUG_OBJECT (v4l2object->element, "creating window"); - g_mutex_lock (v4l2xv->mutex); + g_mutex_lock (&v4l2xv->mutex); width = XDisplayWidth (v4l2xv->dpy, DefaultScreen (v4l2xv->dpy)); height = XDisplayHeight (v4l2xv->dpy, DefaultScreen (v4l2xv->dpy)); @@ -479,10 +483,10 @@ gst_v4l2_xoverlay_prepare_xwindow_id (GstV4l2Object * v4l2object, XSync (v4l2xv->dpy, FALSE); - g_mutex_unlock (v4l2xv->mutex); + g_mutex_unlock (&v4l2xv->mutex); GST_DEBUG_OBJECT (v4l2object->element, "got window"); - gst_v4l2_xoverlay_set_window_handle (v4l2object, win); + gst_v4l2_video_overlay_set_window_handle (v4l2object, win); } } diff --git a/sys/v4l2/gstv4l2xoverlay.h b/sys/v4l2/gstv4l2videooverlay.h index 1a09306836..7c044e71d8 100644 --- a/sys/v4l2/gstv4l2xoverlay.h +++ b/sys/v4l2/gstv4l2videooverlay.h @@ -1,9 +1,8 @@ /* GStreamer - * * Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net> * 2006 Edgard Lima <edgard.lima@indt.org.br> * - * gstv4l2xoverlay.h: tv mixer interface implementation for V4L2 + * gstv4l2videooverlay.h: tv mixer interface implementation for V4L2 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,50 +20,50 @@ * Boston, MA 02111-1307, USA. */ -#ifndef __GST_V4L2_X_OVERLAY_H__ -#define __GST_V4L2_X_OVERLAY_H__ +#ifndef __GST_V4L2_VIDEO_OVERLAY_H__ +#define __GST_V4L2_VIDEO_OVERLAY_H__ #include <X11/X.h> #include <gst/gst.h> -#include <gst/interfaces/xoverlay.h> #include <gst/interfaces/navigation.h> #include <gst/video/gstvideosink.h> /* for GstVideoRectange */ +#include <gst/video/videooverlay.h> #include "gstv4l2object.h" G_BEGIN_DECLS -void gst_v4l2_xoverlay_start (GstV4l2Object *v4l2object); -void gst_v4l2_xoverlay_stop (GstV4l2Object *v4l2object); -gboolean gst_v4l2_xoverlay_get_render_rect (GstV4l2Object *v4l2object, +void gst_v4l2_video_overlay_start (GstV4l2Object *v4l2object); +void gst_v4l2_video_overlay_stop (GstV4l2Object *v4l2object); +gboolean gst_v4l2_video_overlay_get_render_rect (GstV4l2Object *v4l2object, GstVideoRectangle *rect); -void gst_v4l2_xoverlay_interface_init (GstXOverlayClass * klass); -void gst_v4l2_xoverlay_set_window_handle (GstV4l2Object * v4l2object, +void gst_v4l2_video_overlay_interface_init (GstVideoOverlayInterface * iface); +void gst_v4l2_video_overlay_set_window_handle (GstV4l2Object * v4l2object, guintptr id); -void gst_v4l2_xoverlay_prepare_xwindow_id (GstV4l2Object * v4l2object, +void gst_v4l2_video_overlay_prepare_window_handle (GstV4l2Object * v4l2object, gboolean required); -#define GST_IMPLEMENT_V4L2_XOVERLAY_METHODS(Type, interface_as_function) \ +#define GST_IMPLEMENT_V4L2_VIDEO_OVERLAY_METHODS(Type, interface_as_function) \ \ static void \ -interface_as_function ## _xoverlay_set_window_handle (GstXOverlay * xoverlay, \ - guintptr id) \ +interface_as_function ## _video_overlay_set_window_handle (GstVideoOverlay * overlay, \ + guintptr id) \ { \ - Type *this = (Type*) xoverlay; \ - gst_v4l2_xoverlay_set_window_handle (this->v4l2object, id); \ + Type *this = (Type*) overlay; \ + gst_v4l2_video_overlay_set_window_handle (this->v4l2object, id); \ } \ \ static void \ -interface_as_function ## _xoverlay_interface_init (GstXOverlayClass * klass) \ +interface_as_function ## _video_overlay_interface_init (GstVideoOverlayInterface * iface) \ { \ /* default virtual functions */ \ - klass->set_window_handle = interface_as_function ## _xoverlay_set_window_handle; \ + iface->set_window_handle = interface_as_function ## _video_overlay_set_window_handle; \ \ - gst_v4l2_xoverlay_interface_init(klass); \ + gst_v4l2_video_overlay_interface_init (iface); \ } \ -#endif /* __GST_V4L2_X_OVERLAY_H__ */ +#endif /* __GST_V4L2_VIDEO_OVERLAY_H__ */ diff --git a/sys/v4l2/gstv4l2vidorient.c b/sys/v4l2/gstv4l2vidorient.c index 1fa47e794f..4dc0da73f2 100644 --- a/sys/v4l2/gstv4l2vidorient.c +++ b/sys/v4l2/gstv4l2vidorient.c @@ -29,7 +29,6 @@ #include "gstv4l2vidorient.h" #include "gstv4l2object.h" #include "v4l2_calls.h" -#include "v4l2src_calls.h" GST_DEBUG_CATEGORY_STATIC (v4l2vo_debug); #define GST_CAT_DEFAULT v4l2vo_debug @@ -43,7 +42,7 @@ GST_DEBUG_CATEGORY_STATIC (v4l2vo_debug); #endif void -gst_v4l2_video_orientation_interface_init (GstVideoOrientationInterface * klass) +gst_v4l2_video_orientation_interface_init (GstVideoOrientationInterface * iface) { GST_DEBUG_CATEGORY_INIT (v4l2vo_debug, "v4l2vo", 0, "V4L2 VideoOrientation interface debugging"); diff --git a/sys/v4l2/gstv4l2vidorient.h b/sys/v4l2/gstv4l2vidorient.h index 39682e2f1a..bf3736f9ba 100644 --- a/sys/v4l2/gstv4l2vidorient.h +++ b/sys/v4l2/gstv4l2vidorient.h @@ -24,13 +24,13 @@ #define __GST_V4L2_VIDORIENT_H__ #include <gst/gst.h> -#include <gst/interfaces/videoorientation.h> +#include <gst/video/videoorientation.h> #include "gstv4l2object.h" G_BEGIN_DECLS -void gst_v4l2_video_orientation_interface_init (GstVideoOrientationInterface * klass); +void gst_v4l2_video_orientation_interface_init (GstVideoOrientationInterface * iface); gboolean gst_v4l2_video_orientation_get_hflip (GstV4l2Object *v4l2object, gboolean *flip); gboolean gst_v4l2_video_orientation_get_vflip (GstV4l2Object *v4l2object, gboolean *flip); @@ -101,17 +101,17 @@ gboolean gst_v4l2_video_orientation_set_vcenter (GstV4l2Object *v4l2object, gint } \ \ static void \ - interface_as_function ## _video_orientation_interface_init (GstVideoOrientationInterface * klass) \ + interface_as_function ## _video_orientation_interface_init (GstVideoOrientationInterface * iface) \ { \ /* default virtual functions */ \ - klass->get_hflip = interface_as_function ## _video_orientation_get_hflip; \ - klass->get_vflip = interface_as_function ## _video_orientation_get_vflip; \ - klass->get_hcenter = interface_as_function ## _video_orientation_get_hcenter; \ - klass->get_vcenter = interface_as_function ## _video_orientation_get_vcenter; \ - klass->set_hflip = interface_as_function ## _video_orientation_set_hflip; \ - klass->set_vflip = interface_as_function ## _video_orientation_set_vflip; \ - klass->set_hcenter = interface_as_function ## _video_orientation_set_hcenter; \ - klass->set_vcenter = interface_as_function ## _video_orientation_set_vcenter; \ + iface->get_hflip = interface_as_function ## _video_orientation_get_hflip; \ + iface->get_vflip = interface_as_function ## _video_orientation_get_vflip; \ + iface->get_hcenter = interface_as_function ## _video_orientation_get_hcenter; \ + iface->get_vcenter = interface_as_function ## _video_orientation_get_vcenter; \ + iface->set_hflip = interface_as_function ## _video_orientation_set_hflip; \ + iface->set_vflip = interface_as_function ## _video_orientation_set_vflip; \ + iface->set_hcenter = interface_as_function ## _video_orientation_set_hcenter; \ + iface->set_vcenter = interface_as_function ## _video_orientation_set_vcenter; \ } #endif /* __GST_V4L2_VIDORIENT_H__ */ diff --git a/sys/v4l2/v4l2_calls.c b/sys/v4l2/v4l2_calls.c index 309bfb668e..62c03ee051 100644 --- a/sys/v4l2/v4l2_calls.c +++ b/sys/v4l2/v4l2_calls.c @@ -46,10 +46,7 @@ #include "gstv4l2colorbalance.h" #include "gstv4l2src.h" - -#ifdef HAVE_EXPERIMENTAL #include "gstv4l2sink.h" -#endif #include "gst/gst-i18n-plugin.h" @@ -465,11 +462,9 @@ gst_v4l2_open (GstV4l2Object * v4l2object) !(v4l2object->vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) goto not_capture; -#ifdef HAVE_EXPERIMENTAL if (GST_IS_V4L2SINK (v4l2object->element) && !(v4l2object->vcap.capabilities & V4L2_CAP_VIDEO_OUTPUT)) goto not_output; -#endif /* create enumerations, posts errors. */ if (!gst_v4l2_fill_lists (v4l2object)) @@ -515,7 +510,6 @@ not_capture: ("Capabilities: 0x%x", v4l2object->vcap.capabilities)); goto error; } -#ifdef HAVE_EXPERIMENTAL not_output: { GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, NOT_FOUND, @@ -524,7 +518,6 @@ not_output: ("Capabilities: 0x%x", v4l2object->vcap.capabilities)); goto error; } -#endif error: { if (GST_V4L2_IS_OPEN (v4l2object)) { diff --git a/sys/v4l2/v4l2_calls.h b/sys/v4l2/v4l2_calls.h index d2e2c723e5..36cf0f3a94 100644 --- a/sys/v4l2/v4l2_calls.h +++ b/sys/v4l2/v4l2_calls.h @@ -40,14 +40,6 @@ # define v4l2_munmap munmap #endif -/* simple check whether the device is open */ -#define GST_V4L2_IS_OPEN(v4l2object) \ - (v4l2object->video_fd > 0) - -/* check whether the device is 'active' */ -#define GST_V4L2_IS_ACTIVE(v4l2object) \ - (v4l2object->buffer != NULL) - #define GST_V4L2_IS_OVERLAY(v4l2object) \ (v4l2object->vcap.capabilities & V4L2_CAP_VIDEO_OVERLAY) diff --git a/sys/v4l2/v4l2src_calls.c b/sys/v4l2/v4l2src_calls.c deleted file mode 100644 index bfa5589601..0000000000 --- a/sys/v4l2/v4l2src_calls.c +++ /dev/null @@ -1,434 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net> - * 2006 Edgard Lima <edgard.lima@indt.org.br> - * - * v4l2src.c - system calls - * - * 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 <stdlib.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <sys/mman.h> -#include <string.h> -#include <errno.h> -#include "v4l2src_calls.h" -#include <sys/time.h> -#include <unistd.h> -#ifdef __sun -/* Needed on older Solaris Nevada builds (72 at least) */ -#include <stropts.h> -#include <sys/ioccom.h> -#endif - -#include "gstv4l2tuner.h" -#include "gstv4l2bufferpool.h" - -#include "gst/gst-i18n-plugin.h" - -#define GST_CAT_DEFAULT v4l2src_debug -GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE); - -/* lalala... */ -#define GST_V4L2_SET_ACTIVE(element) (element)->buffer = GINT_TO_POINTER (-1) -#define GST_V4L2_SET_INACTIVE(element) (element)->buffer = NULL - -/* On some systems MAP_FAILED seems to be missing */ -#ifndef MAP_FAILED -#define MAP_FAILED ((caddr_t) -1) -#endif - - -/* Local functions */ - -static gboolean -gst_v4l2src_buffer_pool_activate (GstV4l2BufferPool * pool, - GstV4l2Src * v4l2src) -{ - GstV4l2Buffer *buf; - - while ((buf = gst_v4l2_buffer_pool_get (pool, FALSE)) != NULL) - if (!gst_v4l2_buffer_pool_qbuf (pool, buf)) - goto queue_failed; - - return TRUE; - - /* ERRORS */ -queue_failed: - { - GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, - (_("Could not enqueue buffers in device '%s'."), - v4l2src->v4l2object->videodev), - ("enqueing buffer %d/%d failed: %s", - buf->vbuffer.index, v4l2src->num_buffers, g_strerror (errno))); - return FALSE; - } -} - -/****************************************************** - * gst_v4l2src_grab_frame (): - * grab a frame for capturing - * return value: GST_FLOW_OK, GST_FLOW_WRONG_STATE or GST_FLOW_ERROR - ******************************************************/ -GstFlowReturn -gst_v4l2src_grab_frame (GstV4l2Src * v4l2src, GstBuffer ** buf) -{ -#define NUM_TRIALS 50 - GstV4l2Object *v4l2object; - GstV4l2BufferPool *pool; - gint32 trials = NUM_TRIALS; - GstBuffer *pool_buffer; - gboolean need_copy; - gint ret; - - v4l2object = v4l2src->v4l2object; - pool = v4l2src->pool; - if (!pool) - goto no_buffer_pool; - - GST_DEBUG_OBJECT (v4l2src, "grab frame"); - - for (;;) { - if (v4l2object->can_poll_device) { - ret = gst_poll_wait (v4l2object->poll, GST_CLOCK_TIME_NONE); - if (G_UNLIKELY (ret < 0)) { - if (errno == EBUSY) - goto stopped; - if (errno == ENXIO) { - GST_DEBUG_OBJECT (v4l2src, - "v4l2 device doesn't support polling. Disabling"); - v4l2object->can_poll_device = FALSE; - } else { - if (errno != EAGAIN && errno != EINTR) - goto select_error; - } - } - } - - pool_buffer = GST_BUFFER (gst_v4l2_buffer_pool_dqbuf (pool)); - if (pool_buffer) - break; - - GST_WARNING_OBJECT (pool->v4l2elem, "trials=%d", trials); - - /* if the sync() got interrupted, we can retry */ - switch (errno) { - case EINVAL: - case ENOMEM: - /* fatal */ - return GST_FLOW_ERROR; - - case EAGAIN: - case EIO: - case EINTR: - default: - /* try again, until too many trials */ - break; - } - - /* check nr. of attempts to capture */ - if (--trials == -1) { - goto too_many_trials; - } - } - - /* if we are handing out the last buffer in the pool, we need to make a - * copy and bring the buffer back in the pool. */ - need_copy = v4l2src->always_copy - || !gst_v4l2_buffer_pool_available_buffers (pool); - - if (G_UNLIKELY (need_copy)) { - if (!v4l2src->always_copy) { - GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, v4l2src, - "running out of buffers, making a copy to reuse current one"); - } - *buf = gst_buffer_copy (pool_buffer); - GST_BUFFER_FLAG_UNSET (*buf, GST_BUFFER_FLAG_READONLY); - /* this will requeue */ - gst_buffer_unref (pool_buffer); - } else { - *buf = pool_buffer; - } - /* we set the buffer metadata in gst_v4l2src_create() */ - - return GST_FLOW_OK; - - /* ERRORS */ -no_buffer_pool: - { - GST_DEBUG ("no buffer pool"); - return GST_FLOW_WRONG_STATE; - } -select_error: - { - GST_ELEMENT_ERROR (pool->v4l2elem, RESOURCE, READ, (NULL), - ("select error %d: %s (%d)", ret, g_strerror (errno), errno)); - return GST_FLOW_ERROR; - } -stopped: - { - GST_DEBUG ("stop called"); - return GST_FLOW_WRONG_STATE; - } -too_many_trials: - { - GST_ELEMENT_ERROR (pool->v4l2elem, RESOURCE, FAILED, - (_("Failed trying to get video frames from device '%s'."), - v4l2object->videodev), - (_("Failed after %d tries. device %s. system error: %s"), - NUM_TRIALS, v4l2object->videodev, g_strerror (errno))); - return GST_FLOW_ERROR; - } -} - -/****************************************************** - * gst_v4l2src_set_capture(): - * set capture parameters - * return value: TRUE on success, FALSE on error - ******************************************************/ -gboolean -gst_v4l2src_set_capture (GstV4l2Src * v4l2src, guint32 pixelformat, - guint32 width, guint32 height, gboolean interlaced, - guint fps_n, guint fps_d) -{ - gint fd = v4l2src->v4l2object->video_fd; - struct v4l2_streamparm stream; - - if (pixelformat == GST_MAKE_FOURCC ('M', 'P', 'E', 'G')) - return TRUE; - - if (!gst_v4l2_object_set_format (v4l2src->v4l2object, pixelformat, width, - height, interlaced)) { - /* error already reported */ - return FALSE; - } - - /* Is there a reason we require the caller to always specify a framerate? */ - GST_DEBUG_OBJECT (v4l2src, "Desired framerate: %u/%u", fps_n, fps_d); - - memset (&stream, 0x00, sizeof (struct v4l2_streamparm)); - stream.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (v4l2_ioctl (fd, VIDIOC_G_PARM, &stream) < 0) { - GST_ELEMENT_WARNING (v4l2src, RESOURCE, SETTINGS, - (_("Could not get parameters on device '%s'"), - v4l2src->v4l2object->videodev), GST_ERROR_SYSTEM); - goto done; - } - - /* We used to skip frame rate setup if the camera was already setup - with the requested frame rate. This breaks some cameras though, - causing them to not output data (several models of Thinkpad cameras - have this problem at least). - So, don't skip. */ - - /* We want to change the frame rate, so check whether we can. Some cheap USB - * cameras don't have the capability */ - if ((stream.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) == 0) { - GST_DEBUG_OBJECT (v4l2src, "Not setting framerate (not supported)"); - goto done; - } - - GST_LOG_OBJECT (v4l2src, "Setting framerate to %u/%u", fps_n, fps_d); - - /* Note: V4L2 wants the frame interval, we have the frame rate */ - stream.parm.capture.timeperframe.numerator = fps_d; - stream.parm.capture.timeperframe.denominator = fps_n; - - /* some cheap USB cam's won't accept any change */ - if (v4l2_ioctl (fd, VIDIOC_S_PARM, &stream) < 0) { - GST_ELEMENT_WARNING (v4l2src, RESOURCE, SETTINGS, - (_("Video input device did not accept new frame rate setting.")), - GST_ERROR_SYSTEM); - goto done; - } - - v4l2src->fps_n = fps_n; - v4l2src->fps_d = fps_d; - - /* if we have a framerate pre-calculate duration */ - if (fps_n > 0 && fps_d > 0) { - v4l2src->duration = gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n); - } else { - v4l2src->duration = GST_CLOCK_TIME_NONE; - } - - GST_INFO_OBJECT (v4l2src, - "Set framerate to %u/%u and duration to %" GST_TIME_FORMAT, fps_n, fps_d, - GST_TIME_ARGS (v4l2src->duration)); -done: - - return TRUE; -} - -/****************************************************** - * gst_v4l2src_capture_init(): - * initialize the capture system - * return value: TRUE on success, FALSE on error - ******************************************************/ -gboolean -gst_v4l2src_capture_init (GstV4l2Src * v4l2src, GstCaps * caps) -{ - GST_DEBUG_OBJECT (v4l2src, "initializing the capture system"); - - GST_V4L2_CHECK_OPEN (v4l2src->v4l2object); - GST_V4L2_CHECK_NOT_ACTIVE (v4l2src->v4l2object); - - if (v4l2src->v4l2object->vcap.capabilities & V4L2_CAP_STREAMING) { - - /* Map the buffers */ - GST_LOG_OBJECT (v4l2src, "initiating buffer pool"); - - if (!(v4l2src->pool = gst_v4l2_buffer_pool_new (GST_ELEMENT (v4l2src), - v4l2src->v4l2object->video_fd, - v4l2src->num_buffers, caps, TRUE, V4L2_BUF_TYPE_VIDEO_CAPTURE))) - goto buffer_pool_new_failed; - - GST_INFO_OBJECT (v4l2src, "capturing buffers via mmap()"); - v4l2src->use_mmap = TRUE; - - if (v4l2src->num_buffers != v4l2src->pool->buffer_count) { - v4l2src->num_buffers = v4l2src->pool->buffer_count; - g_object_notify (G_OBJECT (v4l2src), "queue-size"); - } - - } else if (v4l2src->v4l2object->vcap.capabilities & V4L2_CAP_READWRITE) { - GST_INFO_OBJECT (v4l2src, "capturing buffers via read()"); - v4l2src->use_mmap = FALSE; - v4l2src->pool = NULL; - } else { - goto no_supported_capture_method; - } - - GST_V4L2_SET_ACTIVE (v4l2src->v4l2object); - - return TRUE; - - /* ERRORS */ -buffer_pool_new_failed: - { - GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, - (_("Could not map buffers from device '%s'"), - v4l2src->v4l2object->videodev), - ("Failed to create buffer pool: %s", g_strerror (errno))); - return FALSE; - } -no_supported_capture_method: - { - GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, - (_("The driver of device '%s' does not support any known capture " - "method."), v4l2src->v4l2object->videodev), (NULL)); - return FALSE; - } -} - - -/****************************************************** - * gst_v4l2src_capture_start(): - * start streaming capture - * return value: TRUE on success, FALSE on error - ******************************************************/ -gboolean -gst_v4l2src_capture_start (GstV4l2Src * v4l2src) -{ - GST_DEBUG_OBJECT (v4l2src, "starting the capturing"); - //GST_V4L2_CHECK_OPEN (v4l2src->v4l2object); - GST_V4L2_CHECK_ACTIVE (v4l2src->v4l2object); - - v4l2src->quit = FALSE; - - if (v4l2src->use_mmap) { - if (!gst_v4l2src_buffer_pool_activate (v4l2src->pool, v4l2src)) { - return FALSE; - } - - if (!gst_v4l2_object_start_streaming (v4l2src->v4l2object)) { - return FALSE; - } - } - - v4l2src->is_capturing = TRUE; - - return TRUE; -} - -/****************************************************** - * gst_v4l2src_capture_stop(): - * stop streaming capture - * return value: TRUE on success, FALSE on error - ******************************************************/ -gboolean -gst_v4l2src_capture_stop (GstV4l2Src * v4l2src) -{ - GST_DEBUG_OBJECT (v4l2src, "stopping capturing"); - - if (!GST_V4L2_IS_OPEN (v4l2src->v4l2object)) { - goto done; - } - if (!GST_V4L2_IS_ACTIVE (v4l2src->v4l2object)) { - goto done; - } - - if (v4l2src->use_mmap) { - /* we actually need to sync on all queued buffers but not - * on the non-queued ones */ - if (!gst_v4l2_object_stop_streaming (v4l2src->v4l2object)) { - return FALSE; - } - } - -done: - - /* make an optional pending wait stop */ - v4l2src->quit = TRUE; - v4l2src->is_capturing = FALSE; - - return TRUE; -} - -/****************************************************** - * gst_v4l2src_capture_deinit(): - * deinitialize the capture system - * return value: TRUE on success, FALSE on error - ******************************************************/ -gboolean -gst_v4l2src_capture_deinit (GstV4l2Src * v4l2src) -{ - GST_DEBUG_OBJECT (v4l2src, "deinitting capture system"); - - if (!GST_V4L2_IS_OPEN (v4l2src->v4l2object)) { - return TRUE; - } - if (!GST_V4L2_IS_ACTIVE (v4l2src->v4l2object)) { - return TRUE; - } - - if (v4l2src->pool) { - gst_v4l2_buffer_pool_destroy (v4l2src->pool); - v4l2src->pool = NULL; - } - - GST_V4L2_SET_INACTIVE (v4l2src->v4l2object); - - return TRUE; -} diff --git a/sys/v4l2/v4l2src_calls.h b/sys/v4l2/v4l2src_calls.h deleted file mode 100644 index 709191870e..0000000000 --- a/sys/v4l2/v4l2src_calls.h +++ /dev/null @@ -1,46 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net> - * 2006 Edgard Lima <edgard.lima@indt.org.br> - * - * v4l2src.h - system calls - * - * 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 __V4L2SRC_CALLS_H__ -#define __V4L2SRC_CALLS_H__ - -#include "gstv4l2src.h" -#include "v4l2_calls.h" - -gboolean gst_v4l2src_get_capture (GstV4l2Src * v4l2src); -gboolean gst_v4l2src_set_capture (GstV4l2Src * v4l2src, - guint32 pixelformat, - guint32 width, guint32 height, - gboolean interlaced, - guint32 fps_n, guint32 fps_d); - -gboolean gst_v4l2src_capture_init (GstV4l2Src * v4l2src, GstCaps *caps); -gboolean gst_v4l2src_capture_start (GstV4l2Src * v4l2src); - -GstFlowReturn gst_v4l2src_grab_frame (GstV4l2Src * v4l2src, GstBuffer **buf); - -gboolean gst_v4l2src_capture_stop (GstV4l2Src * v4l2src); -gboolean gst_v4l2src_capture_deinit (GstV4l2Src * v4l2src); - - -#endif /* __V4L2SRC_CALLS_H__ */ diff --git a/sys/waveform/gstwaveformsink.c b/sys/waveform/gstwaveformsink.c index 3ff72a91e7..2c10c61761 100644 --- a/sys/waveform/gstwaveformsink.c +++ b/sys/waveform/gstwaveformsink.c @@ -54,17 +54,18 @@ static void gst_waveform_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_waveform_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static GstCaps *gst_waveform_sink_getcaps (GstBaseSink * bsink); +static GstCaps *gst_waveform_sink_getcaps (GstBaseSink * bsink, + GstCaps * filter); /************************************************************************/ /* GstAudioSink functions */ /************************************************************************/ static gboolean gst_waveform_sink_prepare (GstAudioSink * asink, - GstRingBufferSpec * spec); + GstAudioRingBufferSpec * spec); static gboolean gst_waveform_sink_unprepare (GstAudioSink * asink); static gboolean gst_waveform_sink_open (GstAudioSink * asink); static gboolean gst_waveform_sink_close (GstAudioSink * asink); -static guint gst_waveform_sink_write (GstAudioSink * asink, gpointer data, +static gint gst_waveform_sink_write (GstAudioSink * asink, gpointer data, guint length); static guint gst_waveform_sink_delay (GstAudioSink * asink); static void gst_waveform_sink_reset (GstAudioSink * asink); @@ -73,42 +74,23 @@ static void gst_waveform_sink_reset (GstAudioSink * asink); /* Utils */ /************************************************************************/ GstCaps *gst_waveform_sink_create_caps (gint rate, gint channels, - gint bits_per_sample); + const gchar * format); WAVEHDR *bufferpool_get_buffer (GstWaveFormSink * wfsink, gpointer data, guint length); void CALLBACK waveOutProc (HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2); static GstStaticPadTemplate waveformsink_sink_factory = - GST_STATIC_PAD_TEMPLATE ("sink", +GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-int, " - "signed = (boolean) { TRUE, FALSE }, " - "width = (int) 16, " - "depth = (int) 16, " - "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; " - "audio/x-raw-int, " - "signed = (boolean) { TRUE, FALSE }, " - "width = (int) 8, " - "depth = (int) 8, " + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) { " GST_AUDIO_NE (S16) ", S8 }, " + "layout = (string) interleaved, " "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]")); -GST_BOILERPLATE (GstWaveFormSink, gst_waveform_sink, GstAudioSink, - GST_TYPE_AUDIO_SINK); - -static void -gst_waveform_sink_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_details_simple (element_class, "WaveForm Audio Sink", - "Sink/Audio", - "Output to a sound card via WaveForm API", - "Sebastien Moutte <sebastien@moutte.net>"); - gst_element_class_add_static_pad_template (element_class, - &waveformsink_sink_factory); -} +#define gst_waveform_sink_parent_class parent_class +G_DEFINE_TYPE (GstWaveFormSink, gst_waveform_sink, GST_TYPE_AUDIO_SINK); static void gst_waveform_sink_class_init (GstWaveFormSinkClass * klass) @@ -116,14 +98,14 @@ gst_waveform_sink_class_init (GstWaveFormSinkClass * klass) GObjectClass *gobject_class; GstElementClass *gstelement_class; GstBaseSinkClass *gstbasesink_class; - GstBaseAudioSinkClass *gstbaseaudiosink_class; GstAudioSinkClass *gstaudiosink_class; + GstElementClass *element_class; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; gstbasesink_class = (GstBaseSinkClass *) klass; - gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass; gstaudiosink_class = (GstAudioSinkClass *) klass; + element_class = GST_ELEMENT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); @@ -144,6 +126,14 @@ gst_waveform_sink_class_init (GstWaveFormSinkClass * klass) GST_DEBUG_CATEGORY_INIT (waveformsink_debug, "waveformsink", 0, "Waveform sink"); + + gst_element_class_set_details_simple (element_class, "WaveForm Audio Sink", + "Sink/Audio", + "Output to a sound card via WaveForm API", + "Sebastien Moutte <sebastien@moutte.net>"); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&waveformsink_sink_factory)); } static void @@ -173,8 +163,7 @@ gst_waveform_sink_get_property (GObject * object, guint prop_id, } static void -gst_waveform_sink_init (GstWaveFormSink * wfsink, - GstWaveFormSinkClass * g_class) +gst_waveform_sink_init (GstWaveFormSink * wfsink) { /* initialize members */ wfsink->hwaveout = NULL; @@ -205,7 +194,7 @@ gst_waveform_sink_finalise (GObject * object) } static GstCaps * -gst_waveform_sink_getcaps (GstBaseSink * bsink) +gst_waveform_sink_getcaps (GstBaseSink * bsink, GstCaps * filter) { GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (bsink); MMRESULT mmresult; @@ -232,97 +221,97 @@ gst_waveform_sink_getcaps (GstBaseSink * bsink) /* create a caps for all wave formats supported by the device starting by the best quality format */ if (wocaps.dwFormats & WAVE_FORMAT_96S16) { - caps_temp = gst_waveform_sink_create_caps (96000, 2, 16); + caps_temp = gst_waveform_sink_create_caps (96000, 2, GST_AUDIO_NE (S16)); if (caps_temp) { gst_caps_append (caps, caps_temp); } } if (wocaps.dwFormats & WAVE_FORMAT_96S08) { - caps_temp = gst_waveform_sink_create_caps (96000, 2, 8); + caps_temp = gst_waveform_sink_create_caps (96000, 2, "S8"); if (caps_temp) { gst_caps_append (caps, caps_temp); } } if (wocaps.dwFormats & WAVE_FORMAT_96M16) { - caps_temp = gst_waveform_sink_create_caps (96000, 1, 16); + caps_temp = gst_waveform_sink_create_caps (96000, 1, GST_AUDIO_NE (S16)); if (caps_temp) { gst_caps_append (caps, caps_temp); } } if (wocaps.dwFormats & WAVE_FORMAT_96M08) { - caps_temp = gst_waveform_sink_create_caps (96000, 1, 8); + caps_temp = gst_waveform_sink_create_caps (96000, 1, "S8"); if (caps_temp) { gst_caps_append (caps, caps_temp); } } if (wocaps.dwFormats & WAVE_FORMAT_4S16) { - caps_temp = gst_waveform_sink_create_caps (44100, 2, 16); + caps_temp = gst_waveform_sink_create_caps (44100, 2, GST_AUDIO_NE (S16)); if (caps_temp) { gst_caps_append (caps, caps_temp); } } if (wocaps.dwFormats & WAVE_FORMAT_4S08) { - caps_temp = gst_waveform_sink_create_caps (44100, 2, 8); + caps_temp = gst_waveform_sink_create_caps (44100, 2, "S8"); if (caps_temp) { gst_caps_append (caps, caps_temp); } } if (wocaps.dwFormats & WAVE_FORMAT_4M16) { - caps_temp = gst_waveform_sink_create_caps (44100, 1, 16); + caps_temp = gst_waveform_sink_create_caps (44100, 1, GST_AUDIO_NE (S16)); if (caps_temp) { gst_caps_append (caps, caps_temp); } } if (wocaps.dwFormats & WAVE_FORMAT_4M08) { - caps_temp = gst_waveform_sink_create_caps (44100, 1, 8); + caps_temp = gst_waveform_sink_create_caps (44100, 1, "S8"); if (caps_temp) { gst_caps_append (caps, caps_temp); } } if (wocaps.dwFormats & WAVE_FORMAT_2S16) { - caps_temp = gst_waveform_sink_create_caps (22050, 2, 16); + caps_temp = gst_waveform_sink_create_caps (22050, 2, GST_AUDIO_NE (S16)); if (caps_temp) { gst_caps_append (caps, caps_temp); } } if (wocaps.dwFormats & WAVE_FORMAT_2S08) { - caps_temp = gst_waveform_sink_create_caps (22050, 2, 8); + caps_temp = gst_waveform_sink_create_caps (22050, 2, "S8"); if (caps_temp) { gst_caps_append (caps, caps_temp); } } if (wocaps.dwFormats & WAVE_FORMAT_2M16) { - caps_temp = gst_waveform_sink_create_caps (22050, 1, 16); + caps_temp = gst_waveform_sink_create_caps (22050, 1, GST_AUDIO_NE (S16)); if (caps_temp) { gst_caps_append (caps, caps_temp); } } if (wocaps.dwFormats & WAVE_FORMAT_2M08) { - caps_temp = gst_waveform_sink_create_caps (22050, 1, 8); + caps_temp = gst_waveform_sink_create_caps (22050, 1, "S8"); if (caps_temp) { gst_caps_append (caps, caps_temp); } } if (wocaps.dwFormats & WAVE_FORMAT_1S16) { - caps_temp = gst_waveform_sink_create_caps (11025, 2, 16); + caps_temp = gst_waveform_sink_create_caps (11025, 2, GST_AUDIO_NE (S16)); if (caps_temp) { gst_caps_append (caps, caps_temp); } } if (wocaps.dwFormats & WAVE_FORMAT_1S08) { - caps_temp = gst_waveform_sink_create_caps (11025, 2, 8); + caps_temp = gst_waveform_sink_create_caps (11025, 2, "S8"); if (caps_temp) { gst_caps_append (caps, caps_temp); } } if (wocaps.dwFormats & WAVE_FORMAT_1M16) { - caps_temp = gst_waveform_sink_create_caps (11025, 1, 16); + caps_temp = gst_waveform_sink_create_caps (11025, 1, GST_AUDIO_NE (S16)); if (caps_temp) { gst_caps_append (caps, caps_temp); } } if (wocaps.dwFormats & WAVE_FORMAT_1M08) { - caps_temp = gst_waveform_sink_create_caps (11025, 1, 8); + caps_temp = gst_waveform_sink_create_caps (11025, 1, "S8"); if (caps_temp) { gst_caps_append (caps, caps_temp); } @@ -352,7 +341,7 @@ gst_waveform_sink_open (GstAudioSink * asink) } static gboolean -gst_waveform_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec) +gst_waveform_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec) { GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink); WAVEFORMATEX wfx; @@ -363,14 +352,14 @@ gst_waveform_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec) memset (&wfx, 0, sizeof (wfx)); wfx.cbSize = 0; wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nChannels = spec->channels; - wfx.nSamplesPerSec = spec->rate; - wfx.wBitsPerSample = (spec->bytes_per_sample * 8) / wfx.nChannels; - wfx.nBlockAlign = spec->bytes_per_sample; + wfx.nChannels = spec->info.channels; + wfx.nSamplesPerSec = spec->info.rate; + wfx.wBitsPerSample = (spec->info.bpf * 8) / wfx.nChannels; + wfx.nBlockAlign = spec->info.bpf; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; /* save bytes per sample to use it in delay */ - wfsink->bytes_per_sample = spec->bytes_per_sample; + wfsink->bytes_per_sample = spec->info.bpf; /* open the default audio device with the given caps */ mmresult = waveOutOpen (&wfsink->hwaveout, WAVE_MAPPER, @@ -449,7 +438,7 @@ gst_waveform_sink_close (GstAudioSink * asink) return TRUE; } -static guint +static gint gst_waveform_sink_write (GstAudioSink * asink, gpointer data, guint length) { GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink); @@ -554,15 +543,13 @@ gst_waveform_sink_reset (GstAudioSink * asink) } GstCaps * -gst_waveform_sink_create_caps (gint rate, gint channels, gint bits_per_sample) +gst_waveform_sink_create_caps (gint rate, gint channels, const gchar * format) { GstCaps *caps = NULL; - caps = gst_caps_new_simple ("audio/x-raw-int", - "width", G_TYPE_INT, bits_per_sample, - "depth", G_TYPE_INT, bits_per_sample, - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "signed", G_TYPE_BOOLEAN, TRUE, + caps = gst_caps_new_simple ("audio/x-raw", + "format", G_TYPE_STRING, format, + "layout", G_TYPE_STRING, "interleaved", "channels", G_TYPE_INT, channels, "rate", G_TYPE_INT, rate, NULL); return caps; } diff --git a/sys/ximage/Makefile.am b/sys/ximage/Makefile.am index db3ab228df..553226e30a 100644 --- a/sys/ximage/Makefile.am +++ b/sys/ximage/Makefile.am @@ -7,7 +7,7 @@ libgstximagesrc_la_CFLAGS = \ $(GST_CFLAGS) \ $(X_CFLAGS) $(XFIXES_CFLAGS) $(XDAMAGE_CFLAGS) libgstximagesrc_la_LIBADD = \ - $(GST_PLUGINS_BASE_LIBS) \ + $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_MAJORMINOR) \ $(GST_BASE_LIBS) \ $(X_LIBS) $(XSHM_LIBS) $(XFIXES_LIBS) $(XDAMAGE_LIBS) libgstximagesrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) diff --git a/sys/ximage/gstximagesrc.c b/sys/ximage/gstximagesrc.c index 974031295d..0ec9688b4a 100644 --- a/sys/ximage/gstximagesrc.c +++ b/sys/ximage/gstximagesrc.c @@ -30,7 +30,7 @@ * <refsect2> * <title>Example pipelines</title> * |[ - * gst-launch ximagesrc ! video/x-raw-rgb,framerate=5/1 ! ffmpegcolorspace ! theoraenc ! oggmux ! filesink location=desktop.ogg + * gst-launch ximagesrc ! video/x-raw,framerate=5/1 ! ffmpegcolorspace ! theoraenc ! oggmux ! filesink location=desktop.ogg * ]| Encodes your X display to an Ogg theora video at 5 frames per second. * </refsect2> */ @@ -48,6 +48,7 @@ #include <gst/gst.h> #include <gst/gst-i18n-plugin.h> +#include <gst/video/video.h> #include "gst/glib-compat-private.h" @@ -56,7 +57,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_debug_ximage_src); static GstStaticPadTemplate t = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-raw-rgb, " + GST_STATIC_CAPS ("video/x-raw, " "framerate = (fraction) [ 0, MAX ], " "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ], " "pixel-aspect-ratio = (fraction) [ 0, MAX ]")); @@ -77,35 +78,35 @@ enum PROP_XNAME, }; -GST_BOILERPLATE (GstXImageSrc, gst_ximage_src, GstPushSrc, GST_TYPE_PUSH_SRC); +#define gst_ximage_src_parent_class parent_class +G_DEFINE_TYPE (GstXImageSrc, gst_ximage_src, GST_TYPE_PUSH_SRC); -static void gst_ximage_src_fixate (GstPad * pad, GstCaps * caps); +static GstCaps *gst_ximage_src_fixate (GstBaseSrc * bsrc, GstCaps * caps); static void gst_ximage_src_clear_bufpool (GstXImageSrc * ximagesrc); /* Called when a buffer is returned from the pipeline */ static void -gst_ximage_src_return_buf (GstXImageSrc * ximagesrc, - GstXImageSrcBuffer * ximage) +gst_ximage_src_return_buf (GstXImageSrc * ximagesrc, GstBuffer * ximage) { + GstMetaXImage *meta = GST_META_XIMAGE_GET (ximage); + /* If our geometry changed we can't reuse that image. */ - if ((ximage->width != ximagesrc->width) || - (ximage->height != ximagesrc->height)) { + if ((meta->width != ximagesrc->width) || (meta->height != ximagesrc->height)) { GST_DEBUG_OBJECT (ximagesrc, "destroy image %p as its size changed %dx%d vs current %dx%d", - ximage, ximage->width, ximage->height, - ximagesrc->width, ximagesrc->height); - g_mutex_lock (ximagesrc->x_lock); + ximage, meta->width, meta->height, ximagesrc->width, ximagesrc->height); + g_mutex_lock (&ximagesrc->x_lock); gst_ximageutil_ximage_destroy (ximagesrc->xcontext, ximage); - g_mutex_unlock (ximagesrc->x_lock); + g_mutex_unlock (&ximagesrc->x_lock); } else { /* In that case we can reuse the image and add it to our image pool. */ GST_LOG_OBJECT (ximagesrc, "recycling image %p in pool", ximage); /* need to increment the refcount again to recycle */ - gst_buffer_ref (GST_BUFFER (ximage)); - g_mutex_lock (ximagesrc->pool_lock); + gst_buffer_ref (ximage); + g_mutex_lock (&ximagesrc->pool_lock); GST_BUFFER_FLAGS (GST_BUFFER (ximage)) = 0; /* clear out any flags from the previous use */ ximagesrc->buffer_pool = g_slist_prepend (ximagesrc->buffer_pool, ximage); - g_mutex_unlock (ximagesrc->pool_lock); + g_mutex_unlock (&ximagesrc->pool_lock); } } @@ -146,10 +147,10 @@ gst_ximage_src_open_display (GstXImageSrc * s, const gchar * name) if (s->xcontext != NULL) return TRUE; - g_mutex_lock (s->x_lock); + g_mutex_lock (&s->x_lock); s->xcontext = ximageutil_xcontext_get (GST_ELEMENT (s), name); if (s->xcontext == NULL) { - g_mutex_unlock (s->x_lock); + g_mutex_unlock (&s->x_lock); GST_ELEMENT_ERROR (s, RESOURCE, OPEN_READ, ("Could not open X display for reading"), ("NULL returned from getting xcontext")); @@ -259,7 +260,7 @@ use_root_window: #endif #endif - g_mutex_unlock (s->x_lock); + g_mutex_unlock (&s->x_lock); if (s->xcontext == NULL) return FALSE; @@ -301,7 +302,7 @@ gst_ximage_src_stop (GstBaseSrc * basesrc) #endif if (src->xcontext) { - g_mutex_lock (src->x_lock); + g_mutex_lock (&src->x_lock); #ifdef HAVE_XDAMAGE if (src->damage_copy_gc != None) { @@ -320,7 +321,7 @@ gst_ximage_src_stop (GstBaseSrc * basesrc) ximageutil_xcontext_clear (src->xcontext); src->xcontext = NULL; - g_mutex_unlock (src->x_lock); + g_mutex_unlock (&src->x_lock); } return TRUE; @@ -433,35 +434,47 @@ composite_pixel (GstXContext * xcontext, guchar * dest, guchar * src) } #endif +#ifdef HAVE_XDAMAGE +static void +copy_buffer (GstBuffer * dest, GstBuffer * src) +{ + GstMapInfo map; + + gst_buffer_map (src, &map, GST_MAP_READ); + gst_buffer_fill (dest, 0, map.data, map.size); + gst_buffer_unmap (src, &map); +} +#endif + /* Retrieve an XImageSrcBuffer, preferably from our * pool of existing images and populate it from the window */ -static GstXImageSrcBuffer * +static GstBuffer * gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc) { - GstXImageSrcBuffer *ximage = NULL; + GstBuffer *ximage = NULL; + GstMetaXImage *meta; - g_mutex_lock (ximagesrc->pool_lock); + g_mutex_lock (&ximagesrc->pool_lock); while (ximagesrc->buffer_pool != NULL) { ximage = ximagesrc->buffer_pool->data; - if ((ximage->width != ximagesrc->width) || - (ximage->height != ximagesrc->height)) { + meta = GST_META_XIMAGE_GET (ximage); + + if ((meta->width != ximagesrc->width) || + (meta->height != ximagesrc->height)) { gst_ximage_buffer_free (ximage); } ximagesrc->buffer_pool = g_slist_delete_link (ximagesrc->buffer_pool, ximagesrc->buffer_pool); } - g_mutex_unlock (ximagesrc->pool_lock); + g_mutex_unlock (&ximagesrc->pool_lock); if (ximage == NULL) { - GstXContext *xcontext; - GstCaps *caps = NULL; - GST_DEBUG_OBJECT (ximagesrc, "creating image (%dx%d)", ximagesrc->width, ximagesrc->height); - g_mutex_lock (ximagesrc->x_lock); + g_mutex_lock (&ximagesrc->x_lock); ximage = gst_ximageutil_ximage_new (ximagesrc->xcontext, GST_ELEMENT (ximagesrc), ximagesrc->width, ximagesrc->height, (BufferReturnFunc) (gst_ximage_src_return_buf)); @@ -469,34 +482,17 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc) GST_ELEMENT_ERROR (ximagesrc, RESOURCE, WRITE, (NULL), ("could not create a %dx%d ximage", ximagesrc->width, ximagesrc->height)); - g_mutex_unlock (ximagesrc->x_lock); + g_mutex_unlock (&ximagesrc->x_lock); return NULL; } - xcontext = ximagesrc->xcontext; - - - caps = gst_caps_new_simple ("video/x-raw-rgb", - "bpp", G_TYPE_INT, xcontext->bpp, - "depth", G_TYPE_INT, xcontext->depth, - "endianness", G_TYPE_INT, xcontext->endianness, - "red_mask", G_TYPE_INT, xcontext->r_mask_output, - "green_mask", G_TYPE_INT, xcontext->g_mask_output, - "blue_mask", G_TYPE_INT, xcontext->b_mask_output, - "width", G_TYPE_INT, ximagesrc->width, - "height", G_TYPE_INT, ximagesrc->height, - "framerate", GST_TYPE_FRACTION, ximagesrc->fps_n, ximagesrc->fps_d, - "pixel-aspect-ratio", GST_TYPE_FRACTION, - gst_value_get_fraction_numerator (xcontext->par), - gst_value_get_fraction_denominator (xcontext->par), NULL); - - gst_buffer_set_caps (GST_BUFFER (ximage), caps); - g_mutex_unlock (ximagesrc->x_lock); - - gst_caps_unref (caps); + g_mutex_unlock (&ximagesrc->x_lock); } g_return_val_if_fail (GST_IS_XIMAGE_SRC (ximagesrc), NULL); + + meta = GST_META_XIMAGE_GET (ximage); + #ifdef HAVE_XDAMAGE if (ximagesrc->have_xdamage && ximagesrc->use_damage && ximagesrc->last_ximage != NULL) { @@ -526,11 +522,9 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc) if (!have_frame) { GST_LOG_OBJECT (ximagesrc, - "Copying from last frame ximage->size: %d", - GST_BUFFER_SIZE (GST_BUFFER (ximage))); - memcpy (GST_BUFFER_DATA (GST_BUFFER (ximage)), - GST_BUFFER_DATA (GST_BUFFER (ximagesrc->last_ximage)), - GST_BUFFER_SIZE (GST_BUFFER (ximage))); + "Copying from last frame ximage->size: %" G_GSIZE_FORMAT, + gst_buffer_get_size (ximage)); + copy_buffer (ximage, ximagesrc->last_ximage); have_frame = TRUE; } for (i = 0; i < nrects; i++) { @@ -569,7 +563,7 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc) startx, starty, width, height); XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow, startx, starty, width, height, AllPlanes, ZPixmap, - ximage->ximage, startx - ximagesrc->startx, + meta->ximage, startx - ximagesrc->startx, starty - ximagesrc->starty); } } else { @@ -581,7 +575,7 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc) XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow, rects[i].x, rects[i].y, rects[i].width, rects[i].height, - AllPlanes, ZPixmap, ximage->ximage, rects[i].x, rects[i].y); + AllPlanes, ZPixmap, meta->ximage, rects[i].x, rects[i].y); } } free (rects); @@ -590,11 +584,9 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc) } while (XPending (ximagesrc->xcontext->disp)); if (!have_frame) { GST_LOG_OBJECT (ximagesrc, - "Copying from last frame ximage->size: %d", - GST_BUFFER_SIZE (GST_BUFFER (ximage))); - memcpy (GST_BUFFER_DATA (GST_BUFFER (ximage)), - GST_BUFFER_DATA (GST_BUFFER (ximagesrc->last_ximage)), - GST_BUFFER_SIZE (GST_BUFFER (ximage))); + "Copying from last frame ximage->size: %" G_GSIZE_FORMAT, + gst_buffer_get_size (ximage)); + copy_buffer (ximage, ximagesrc->last_ximage); } #ifdef HAVE_XFIXES /* re-get area where last mouse pointer was but only if in our clipping @@ -643,14 +635,14 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc) GST_DEBUG_OBJECT (ximagesrc, "Removing cursor from %d,%d", x, y); XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow, startx, starty, iwidth, iheight, AllPlanes, ZPixmap, - ximage->ximage, startx - ximagesrc->startx, + meta->ximage, startx - ximagesrc->startx, starty - ximagesrc->starty); } } else { GST_DEBUG_OBJECT (ximagesrc, "Removing cursor from %d,%d", x, y); XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow, - x, y, width, height, AllPlanes, ZPixmap, ximage->ximage, x, y); + x, y, width, height, AllPlanes, ZPixmap, meta->ximage, x, y); } } #endif @@ -663,7 +655,7 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc) if (ximagesrc->xcontext->use_xshm) { GST_DEBUG_OBJECT (ximagesrc, "Retrieving screen using XShm"); XShmGetImage (ximagesrc->xcontext->disp, ximagesrc->xwindow, - ximage->ximage, ximagesrc->startx, ximagesrc->starty, AllPlanes); + meta->ximage, ximagesrc->startx, ximagesrc->starty, AllPlanes); } else #endif /* HAVE_XSHM */ @@ -672,9 +664,9 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc) if (ximagesrc->remote) { XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow, ximagesrc->startx, ximagesrc->starty, ximagesrc->width, - ximagesrc->height, AllPlanes, ZPixmap, ximage->ximage, 0, 0); + ximagesrc->height, AllPlanes, ZPixmap, meta->ximage, 0, 0); } else { - ximage->ximage = + meta->ximage = XGetImage (ximagesrc->xcontext->disp, ximagesrc->xwindow, ximagesrc->startx, ximagesrc->starty, ximagesrc->width, ximagesrc->height, AllPlanes, ZPixmap); @@ -754,7 +746,7 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc) (guint8 *) & (ximagesrc->cursor_image->pixels[((j - cy) * ximagesrc->cursor_image->width + (i - cx))]); dest = - (guint8 *) & (ximage->ximage->data[((j - + (guint8 *) & (meta->ximage->data[((j - ximagesrc->starty) * ximagesrc->width + (i - ximagesrc->startx)) * (ximagesrc->xcontext->bpp / 8)]); @@ -770,9 +762,9 @@ gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc) #ifdef HAVE_XDAMAGE if (ximagesrc->have_xdamage && ximagesrc->use_damage) { /* need to ref ximage to put in last_ximage */ - gst_buffer_ref (GST_BUFFER (ximage)); + gst_buffer_ref (ximage); if (ximagesrc->last_ximage) { - gst_buffer_unref (GST_BUFFER (ximagesrc->last_ximage)); + gst_buffer_unref (ximagesrc->last_ximage); } ximagesrc->last_ximage = ximage; GST_LOG_OBJECT (ximagesrc, "reffing current buffer for last_ximage"); @@ -785,7 +777,7 @@ static GstFlowReturn gst_ximage_src_create (GstPushSrc * bs, GstBuffer ** buf) { GstXImageSrc *s = GST_XIMAGE_SRC (bs); - GstXImageSrcBuffer *image; + GstBuffer *image; GstClockTime base_time; GstClockTime next_capture_ts; GstClockTime dur; @@ -847,7 +839,7 @@ gst_ximage_src_create (GstPushSrc * bs, GstBuffer ** buf) if (ret == GST_CLOCK_UNSCHEDULED) { /* Got woken up by the unlock function */ GST_OBJECT_UNLOCK (s); - return GST_FLOW_WRONG_STATE; + return GST_FLOW_FLUSHING; } /* Duration is a complete 1/fps frame duration */ dur = gst_util_uint64_scale_int (GST_SECOND, s->fps_d, s->fps_n); @@ -869,7 +861,7 @@ gst_ximage_src_create (GstPushSrc * bs, GstBuffer ** buf) if (!image) return GST_FLOW_ERROR; - *buf = GST_BUFFER (image); + *buf = image; GST_BUFFER_TIMESTAMP (*buf) = next_capture_ts; GST_BUFFER_DURATION (*buf) = dur; @@ -985,30 +977,16 @@ gst_ximage_src_get_property (GObject * object, guint prop_id, GValue * value, static void gst_ximage_src_clear_bufpool (GstXImageSrc * ximagesrc) { - g_mutex_lock (ximagesrc->pool_lock); + g_mutex_lock (&ximagesrc->pool_lock); while (ximagesrc->buffer_pool != NULL) { - GstXImageSrcBuffer *ximage = ximagesrc->buffer_pool->data; + GstBuffer *ximage = ximagesrc->buffer_pool->data; gst_ximage_buffer_free (ximage); ximagesrc->buffer_pool = g_slist_delete_link (ximagesrc->buffer_pool, ximagesrc->buffer_pool); } - g_mutex_unlock (ximagesrc->pool_lock); -} - -static void -gst_ximage_src_base_init (gpointer g_class) -{ - GstElementClass *ec = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_details_simple (ec, "Ximage video source", - "Source/Video", - "Creates a screenshot video stream", - "Lutz Mueller <lutz@users.sourceforge.net>, " - "Jan Schmidt <thaytan@mad.scientist.com>, " - "Zaheer Merali <zaheerabbas at merali dot org>"); - gst_element_class_add_static_pad_template (ec, &t); + g_mutex_unlock (&ximagesrc->pool_lock); } static void @@ -1029,18 +1007,19 @@ gst_ximage_src_finalize (GObject * object) ximageutil_xcontext_clear (src->xcontext); g_free (src->xname); - g_mutex_free (src->pool_lock); - g_mutex_free (src->x_lock); + g_mutex_clear (&src->pool_lock); + g_mutex_clear (&src->x_lock); G_OBJECT_CLASS (parent_class)->finalize (object); } static GstCaps * -gst_ximage_src_get_caps (GstBaseSrc * bs) +gst_ximage_src_get_caps (GstBaseSrc * bs, GstCaps * filter) { GstXImageSrc *s = GST_XIMAGE_SRC (bs); GstXContext *xcontext; gint width, height; + GstVideoFormat format; if ((!s->xcontext) && (!gst_ximage_src_open_display (s, s->display_name))) return @@ -1094,13 +1073,14 @@ gst_ximage_src_get_caps (GstBaseSrc * bs) s->endy = height - 1; } GST_DEBUG ("width = %d, height=%d", width, height); - return gst_caps_new_simple ("video/x-raw-rgb", - "bpp", G_TYPE_INT, xcontext->bpp, - "depth", G_TYPE_INT, xcontext->depth, - "endianness", G_TYPE_INT, xcontext->endianness, - "red_mask", G_TYPE_INT, xcontext->r_mask_output, - "green_mask", G_TYPE_INT, xcontext->g_mask_output, - "blue_mask", G_TYPE_INT, xcontext->b_mask_output, + + format = + gst_video_format_from_masks (xcontext->depth, xcontext->bpp, + xcontext->endianness, xcontext->r_mask_output, xcontext->g_mask_output, + xcontext->b_mask_output, 0); + + return gst_caps_new_simple ("video/x-raw", + "format", G_TYPE_STRING, gst_video_format_to_string (format), "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, "framerate", GST_TYPE_FRACTION_RANGE, 1, G_MAXINT, G_MAXINT, 1, @@ -1134,23 +1114,29 @@ gst_ximage_src_set_caps (GstBaseSrc * bs, GstCaps * caps) return TRUE; } -static void -gst_ximage_src_fixate (GstPad * pad, GstCaps * caps) +static GstCaps * +gst_ximage_src_fixate (GstBaseSrc * bsrc, GstCaps * caps) { gint i; GstStructure *structure; + caps = gst_caps_make_writable (caps); + for (i = 0; i < gst_caps_get_size (caps); ++i) { structure = gst_caps_get_structure (caps, i); gst_structure_fixate_field_nearest_fraction (structure, "framerate", 25, 1); } + caps = GST_BASE_SRC_CLASS (parent_class)->fixate (bsrc, caps); + + return caps; } static void gst_ximage_src_class_init (GstXImageSrcClass * klass) { GObjectClass *gc = G_OBJECT_CLASS (klass); + GstElementClass *ec = GST_ELEMENT_CLASS (klass); GstBaseSrcClass *bc = GST_BASE_SRC_CLASS (klass); GstPushSrcClass *push_class = GST_PUSH_SRC_CLASS (klass); @@ -1241,7 +1227,6 @@ gst_ximage_src_class_init (GstXImageSrcClass * klass) g_param_spec_boolean ("remote", "Remote dispay", "Whether the display is remote", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /** * GstXImageSrc:xid * @@ -1253,7 +1238,6 @@ gst_ximage_src_class_init (GstXImageSrcClass * klass) g_param_spec_uint64 ("xid", "Window XID", "Window XID to capture from", 0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /** * GstXImageSrc:xname * @@ -1266,26 +1250,31 @@ gst_ximage_src_class_init (GstXImageSrcClass * klass) "Window name to capture from", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - parent_class = g_type_class_peek_parent (klass); + gst_element_class_set_details_simple (ec, "Ximage video source", + "Source/Video", + "Creates a screenshot video stream", + "Lutz Mueller <lutz@users.sourceforge.net>, " + "Jan Schmidt <thaytan@mad.scientist.com>, " + "Zaheer Merali <zaheerabbas at merali dot org>"); + gst_element_class_add_pad_template (ec, gst_static_pad_template_get (&t)); - push_class->create = gst_ximage_src_create; + bc->fixate = gst_ximage_src_fixate; bc->get_caps = gst_ximage_src_get_caps; bc->set_caps = gst_ximage_src_set_caps; bc->start = gst_ximage_src_start; bc->stop = gst_ximage_src_stop; bc->unlock = gst_ximage_src_unlock; + push_class->create = gst_ximage_src_create; } static void -gst_ximage_src_init (GstXImageSrc * ximagesrc, GstXImageSrcClass * klass) +gst_ximage_src_init (GstXImageSrc * ximagesrc) { gst_base_src_set_format (GST_BASE_SRC (ximagesrc), GST_FORMAT_TIME); gst_base_src_set_live (GST_BASE_SRC (ximagesrc), TRUE); - gst_pad_set_fixatecaps_function (GST_BASE_SRC_PAD (ximagesrc), - gst_ximage_src_fixate); - ximagesrc->pool_lock = g_mutex_new (); - ximagesrc->x_lock = g_mutex_new (); + g_mutex_init (&ximagesrc->pool_lock); + g_mutex_init (&ximagesrc->x_lock); ximagesrc->show_pointer = TRUE; ximagesrc->use_damage = TRUE; ximagesrc->startx = 0; diff --git a/sys/ximage/gstximagesrc.h b/sys/ximage/gstximagesrc.h index e58513c1ec..7f5275a85a 100644 --- a/sys/ximage/gstximagesrc.h +++ b/sys/ximage/gstximagesrc.h @@ -69,10 +69,10 @@ struct _GstXImageSrc gint64 last_frame_no; /* Protect X Windows calls */ - GMutex *x_lock; + GMutex x_lock; /* Gathered pool of emitted buffers */ - GMutex *pool_lock; + GMutex pool_lock; GSList *buffer_pool; /* XFixes and XDamage support */ @@ -99,7 +99,7 @@ struct _GstXImageSrc int damage_event_base; XserverRegion damage_region; GC damage_copy_gc; - GstXImageSrcBuffer *last_ximage; + GstBuffer *last_ximage; #endif }; diff --git a/sys/ximage/ximageutil.c b/sys/ximage/ximageutil.c index 2fac09a231..907aa3aefd 100644 --- a/sys/ximage/ximageutil.c +++ b/sys/ximage/ximageutil.c @@ -23,6 +23,33 @@ #include "ximageutil.h" +GType +gst_meta_ximage_api_get_type (void) +{ + static volatile GType type; + static const gchar *tags[] = { "memory", NULL }; + + if (g_once_init_enter (&type)) { + GType _type = gst_meta_api_type_register ("GstMetaXImageSrcAPI", tags); + g_once_init_leave (&type, _type); + } + return type; +} + +const GstMetaInfo * +gst_meta_ximage_get_info (void) +{ + static const GstMetaInfo *meta_ximage_info = NULL; + + if (meta_ximage_info == NULL) { + meta_ximage_info = + gst_meta_register (gst_meta_ximage_api_get_type (), "GstMetaXImageSrc", + sizeof (GstMetaXImage), (GstMetaInitFunction) NULL, + (GstMetaFreeFunction) NULL, (GstMetaTransformFunction) NULL); + } + return meta_ximage_info; +} + #ifdef HAVE_XSHM static gboolean error_caught = FALSE; @@ -296,108 +323,70 @@ ximageutil_calculate_pixel_aspect_ratio (GstXContext * xcontext) gst_value_get_fraction_denominator (xcontext->par)); } -static GstBufferClass *ximagesrc_buffer_parent_class = NULL; - static void -gst_ximagesrc_buffer_finalize (GstXImageSrcBuffer * ximage) +gst_ximagesrc_buffer_dispose (GstBuffer * ximage) { GstElement *parent; + GstMetaXImage *meta; g_return_if_fail (ximage != NULL); - parent = ximage->parent; + meta = GST_META_XIMAGE_GET (ximage); + + parent = meta->parent; if (parent == NULL) { g_warning ("XImageSrcBuffer->ximagesrc == NULL"); goto beach; } - if (ximage->return_func) - ximage->return_func (parent, ximage); + if (meta->return_func) + meta->return_func (parent, ximage); beach: - - GST_MINI_OBJECT_CLASS (ximagesrc_buffer_parent_class)->finalize - (GST_MINI_OBJECT (ximage)); - return; } void -gst_ximage_buffer_free (GstXImageSrcBuffer * ximage) +gst_ximage_buffer_free (GstBuffer * ximage) { - /* make sure it is not recycled */ - ximage->width = -1; - ximage->height = -1; - gst_buffer_unref (GST_BUFFER (ximage)); -} + GstMetaXImage *meta; -static void -gst_ximagesrc_buffer_init (GstXImageSrcBuffer * ximage_buffer, gpointer g_class) -{ -#ifdef HAVE_XSHM - ximage_buffer->SHMInfo.shmaddr = ((void *) -1); - ximage_buffer->SHMInfo.shmid = -1; -#endif -} - -static void -gst_ximagesrc_buffer_class_init (gpointer g_class, gpointer class_data) -{ - GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class); - - ximagesrc_buffer_parent_class = g_type_class_peek_parent (g_class); + meta = GST_META_XIMAGE_GET (ximage); - mini_object_class->finalize = (GstMiniObjectFinalizeFunction) - gst_ximagesrc_buffer_finalize; -} - -static GType -gst_ximagesrc_buffer_get_type (void) -{ - static GType _gst_ximagesrc_buffer_type; - - if (G_UNLIKELY (_gst_ximagesrc_buffer_type == 0)) { - static const GTypeInfo ximagesrc_buffer_info = { - sizeof (GstBufferClass), - NULL, - NULL, - gst_ximagesrc_buffer_class_init, - NULL, - NULL, - sizeof (GstXImageSrcBuffer), - 0, - (GInstanceInitFunc) gst_ximagesrc_buffer_init, - NULL - }; - _gst_ximagesrc_buffer_type = g_type_register_static (GST_TYPE_BUFFER, - "GstXImageSrcBuffer", &ximagesrc_buffer_info, 0); - } - return _gst_ximagesrc_buffer_type; + /* make sure it is not recycled */ + meta->width = -1; + meta->height = -1; + gst_buffer_unref (ximage); } /* This function handles GstXImageSrcBuffer creation depending on XShm availability */ -GstXImageSrcBuffer * +GstBuffer * gst_ximageutil_ximage_new (GstXContext * xcontext, GstElement * parent, int width, int height, BufferReturnFunc return_func) { - GstXImageSrcBuffer *ximage = NULL; + GstBuffer *ximage = NULL; + GstMetaXImage *meta; gboolean succeeded = FALSE; - ximage = - (GstXImageSrcBuffer *) gst_mini_object_new (GST_TYPE_XIMAGESRC_BUFFER); + ximage = gst_buffer_new (); + GST_MINI_OBJECT_CAST (ximage)->dispose = + (GstMiniObjectDisposeFunction) gst_ximagesrc_buffer_dispose; - ximage->width = width; - ximage->height = height; + meta = GST_META_XIMAGE_ADD (ximage); + meta->width = width; + meta->height = height; #ifdef HAVE_XSHM + meta->SHMInfo.shmaddr = ((void *) -1); + meta->SHMInfo.shmid = -1; + if (xcontext->use_xshm) { - ximage->ximage = XShmCreateImage (xcontext->disp, + meta->ximage = XShmCreateImage (xcontext->disp, xcontext->visual, xcontext->depth, - ZPixmap, NULL, &ximage->SHMInfo, ximage->width, ximage->height); - if (!ximage->ximage) { + ZPixmap, NULL, &meta->SHMInfo, meta->width, meta->height); + if (!meta->ximage) { GST_WARNING_OBJECT (parent, - "could not XShmCreateImage a %dx%d image", - ximage->width, ximage->height); + "could not XShmCreateImage a %dx%d image", meta->width, meta->height); /* Retry without XShm */ xcontext->use_xshm = FALSE; @@ -405,24 +394,23 @@ gst_ximageutil_ximage_new (GstXContext * xcontext, } /* we have to use the returned bytes_per_line for our shm size */ - ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height; - ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size, - IPC_CREAT | 0777); - if (ximage->SHMInfo.shmid == -1) + meta->size = meta->ximage->bytes_per_line * meta->ximage->height; + meta->SHMInfo.shmid = shmget (IPC_PRIVATE, meta->size, IPC_CREAT | 0777); + if (meta->SHMInfo.shmid == -1) goto beach; - ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, 0, 0); - if (ximage->SHMInfo.shmaddr == ((void *) -1)) + meta->SHMInfo.shmaddr = shmat (meta->SHMInfo.shmid, 0, 0); + if (meta->SHMInfo.shmaddr == ((void *) -1)) goto beach; /* Delete the SHM segment. It will actually go away automatically * when we detach now */ - shmctl (ximage->SHMInfo.shmid, IPC_RMID, 0); + shmctl (meta->SHMInfo.shmid, IPC_RMID, 0); - ximage->ximage->data = ximage->SHMInfo.shmaddr; - ximage->SHMInfo.readOnly = FALSE; + meta->ximage->data = meta->SHMInfo.shmaddr; + meta->SHMInfo.readOnly = FALSE; - if (XShmAttach (xcontext->disp, &ximage->SHMInfo) == 0) + if (XShmAttach (xcontext->disp, &meta->SHMInfo) == 0) goto beach; XSync (xcontext->disp, FALSE); @@ -430,27 +418,28 @@ gst_ximageutil_ximage_new (GstXContext * xcontext, no_xshm: #endif /* HAVE_XSHM */ { - ximage->ximage = XCreateImage (xcontext->disp, + meta->ximage = XCreateImage (xcontext->disp, xcontext->visual, xcontext->depth, - ZPixmap, 0, NULL, ximage->width, ximage->height, xcontext->bpp, 0); - if (!ximage->ximage) + ZPixmap, 0, NULL, meta->width, meta->height, xcontext->bpp, 0); + if (!meta->ximage) goto beach; /* we have to use the returned bytes_per_line for our image size */ - ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height; - ximage->ximage->data = g_malloc (ximage->size); + meta->size = meta->ximage->bytes_per_line * meta->ximage->height; + meta->ximage->data = g_malloc (meta->size); XSync (xcontext->disp, FALSE); } succeeded = TRUE; - GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data; - GST_BUFFER_SIZE (ximage) = ximage->size; + gst_buffer_take_memory (ximage, -1, + gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE, meta->ximage->data, + meta->size, 0, meta->size, NULL, NULL)); /* Keep a ref to our src */ - ximage->parent = gst_object_ref (parent); - ximage->return_func = return_func; + meta->parent = gst_object_ref (parent); + meta->return_func = return_func; beach: if (!succeeded) { gst_ximage_buffer_free (ximage); @@ -462,9 +451,12 @@ beach: /* This function destroys a GstXImageBuffer handling XShm availability */ void -gst_ximageutil_ximage_destroy (GstXContext * xcontext, - GstXImageSrcBuffer * ximage) +gst_ximageutil_ximage_destroy (GstXContext * xcontext, GstBuffer * ximage) { + GstMetaXImage *meta; + + meta = GST_META_XIMAGE_GET (ximage); + /* We might have some buffers destroyed after changing state to NULL */ if (!xcontext) goto beach; @@ -473,28 +465,28 @@ gst_ximageutil_ximage_destroy (GstXContext * xcontext, #ifdef HAVE_XSHM if (xcontext->use_xshm) { - if (ximage->SHMInfo.shmaddr != ((void *) -1)) { - XShmDetach (xcontext->disp, &ximage->SHMInfo); + if (meta->SHMInfo.shmaddr != ((void *) -1)) { + XShmDetach (xcontext->disp, &meta->SHMInfo); XSync (xcontext->disp, 0); - shmdt (ximage->SHMInfo.shmaddr); + shmdt (meta->SHMInfo.shmaddr); } - if (ximage->ximage) - XDestroyImage (ximage->ximage); + if (meta->ximage) + XDestroyImage (meta->ximage); } else #endif /* HAVE_XSHM */ { - if (ximage->ximage) { - XDestroyImage (ximage->ximage); + if (meta->ximage) { + XDestroyImage (meta->ximage); } } XSync (xcontext->disp, FALSE); beach: - if (ximage->parent) { + if (meta->parent) { /* Release the ref to our parent */ - gst_object_unref (ximage->parent); - ximage->parent = NULL; + gst_object_unref (meta->parent); + meta->parent = NULL; } return; diff --git a/sys/ximage/ximageutil.h b/sys/ximage/ximageutil.h index 517fc8e06a..aa03e7a857 100644 --- a/sys/ximage/ximageutil.h +++ b/sys/ximage/ximageutil.h @@ -43,7 +43,7 @@ G_BEGIN_DECLS typedef struct _GstXContext GstXContext; typedef struct _GstXWindow GstXWindow; typedef struct _GstXImage GstXImage; -typedef struct _GstXImageSrcBuffer GstXImageSrcBuffer; +typedef struct _GstMetaXImage GstMetaXImage; /* Global X Context stuff */ /** @@ -130,20 +130,20 @@ void ximageutil_calculate_pixel_aspect_ratio (GstXContext * xcontext); /* custom ximagesrc buffer, copied from ximagesink */ /* BufferReturnFunc is called when a buffer is finalised */ -typedef void (*BufferReturnFunc) (GstElement *parent, GstXImageSrcBuffer *buf); +typedef void (*BufferReturnFunc) (GstElement *parent, GstBuffer *buf); /** - * GstXImageSrcBuffer: + * GstMetaXImage: * @parent: a reference to the element we belong to * @ximage: the XImage of this buffer * @width: the width in pixels of XImage @ximage * @height: the height in pixels of XImage @ximage * @size: the size in bytes of XImage @ximage * - * Subclass of #GstBuffer containing additional information about an XImage. + * Extra data attached to buffers containing additional information about an XImage. */ -struct _GstXImageSrcBuffer { - GstBuffer buffer; +struct _GstMetaXImage { + GstMeta meta; /* Reference to the ximagesrc we belong to */ GstElement *parent; @@ -156,26 +156,23 @@ struct _GstXImageSrcBuffer { gint width, height; size_t size; - + BufferReturnFunc return_func; }; +GType gst_meta_ximage_api_get_type (void); +const GstMetaInfo * gst_meta_ximage_get_info (void); +#define GST_META_XIMAGE_GET(buf) ((GstMetaXImage *)gst_buffer_get_meta(buf,gst_meta_ximage_api_get_type())) +#define GST_META_XIMAGE_ADD(buf) ((GstMetaXImage *)gst_buffer_add_meta(buf,gst_meta_ximage_get_info(),NULL)) -GstXImageSrcBuffer *gst_ximageutil_ximage_new (GstXContext *xcontext, +GstBuffer *gst_ximageutil_ximage_new (GstXContext *xcontext, GstElement *parent, int width, int height, BufferReturnFunc return_func); void gst_ximageutil_ximage_destroy (GstXContext *xcontext, - GstXImageSrcBuffer * ximage); + GstBuffer * ximage); /* Call to manually release a buffer */ -void gst_ximage_buffer_free (GstXImageSrcBuffer *ximage); - -#define GST_TYPE_XIMAGESRC_BUFFER (gst_ximagesrc_buffer_get_type()) -#define GST_IS_XIMAGESRC_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XIMAGESRC_BUFFER)) -#define GST_IS_XIMAGESRC_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_XIMAGESRC_BUFFER)) -#define GST_XIMAGESRC_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XIMAGESRC_BUFFER, GstXImageSrcBuffer)) -#define GST_XIMAGESRC_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_XIMAGESRC_BUFFER, GstXImageSrcBufferClass)) -#define GST_XIMAGESRC_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_XIMAGESRC_BUFFER, GstXImageSrcBufferClass)) +void gst_ximage_buffer_free (GstBuffer *ximage); G_END_DECLS |