From d21f04ca6fb0e4d3f912834542aa13d0d79fdf94 Mon Sep 17 00:00:00 2001 From: David Schleef Date: Thu, 25 Mar 2004 02:43:48 +0000 Subject: [PATCH] sys/oss/gstosselement.c: Add code to handle rate probing (bug #120883) Original commit message from CVS: * sys/oss/gstosselement.c: (gst_osselement_sync_parms), (gst_osselement_close_audio), (gst_osselement_probe_caps), (gst_osselement_get_format_structure), (gst_osselement_rate_probe_check), (gst_osselement_rate_add_range), (gst_osselement_rate_check_rate), (gst_osselement_rate_add_rate), (gst_osselement_rate_int_compare): Add code to handle rate probing (bug #120883) * sys/oss/gstosselement.h: same * sys/oss/gstosssink.c: (gst_osssink_init), (gst_osssink_getcaps): Use rate probing provided by osselement. * sys/oss/gstosssrc.c: (gst_osssrc_init), (gst_osssrc_getcaps): same --- ChangeLog | 14 ++ sys/oss/gstosselement.c | 339 +++++++++++++++++++++++++++++++++++++++++++++++- sys/oss/gstosselement.h | 2 + sys/oss/gstosssink.c | 21 ++- sys/oss/gstosssrc.c | 24 +++- 5 files changed, 396 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4cf6f8e..dda4d5b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2004-03-24 David Schleef + + * sys/oss/gstosselement.c: (gst_osselement_sync_parms), + (gst_osselement_close_audio), (gst_osselement_probe_caps), + (gst_osselement_get_format_structure), + (gst_osselement_rate_probe_check), (gst_osselement_rate_add_range), + (gst_osselement_rate_check_rate), (gst_osselement_rate_add_rate), + (gst_osselement_rate_int_compare): Add code to handle rate probing + (bug #120883) + * sys/oss/gstosselement.h: same + * sys/oss/gstosssink.c: (gst_osssink_init), (gst_osssink_getcaps): + Use rate probing provided by osselement. + * sys/oss/gstosssrc.c: (gst_osssrc_init), (gst_osssrc_getcaps): same + 2004-03-24 Ronald Bultje * ext/xvid/gstxvidenc.c: (gst_xvidenc_set_property), diff --git a/sys/oss/gstosselement.c b/sys/oss/gstosselement.c index ad609af..78215d5 100644 --- a/sys/oss/gstosselement.c +++ b/sys/oss/gstosselement.c @@ -642,7 +642,7 @@ gst_osselement_sync_parms (GstOssElement * oss) g_warning ("couldn't set the right number of channels (wanted %d, got %d), enjoy the tone difference", target_channels, oss->channels); - if (target_rate != oss->rate) + if (target_rate < oss->rate - 1 || target_rate > oss->rate + 1) g_warning ("couldn't set the right sample rate (wanted %d, got %d), enjoy the speed difference", target_rate, oss->rate); @@ -806,6 +806,10 @@ static void gst_osselement_close_audio (GstOssElement * oss) { gst_ossmixer_free_list (oss); + if (oss->probed_caps) { + gst_caps_free (oss->probed_caps); + oss->probed_caps = NULL; + } if (oss->fd < 0) return; @@ -967,3 +971,336 @@ gst_osselement_change_state (GstElement * element) return GST_STATE_SUCCESS; } + + +/* rate probing code */ + + +#if 0 +#include +#include +#include +#include +#include +#include +#include +#endif + +typedef struct _GstOssProbe GstOssProbe; +struct _GstOssProbe +{ + int fd; + int format; + int n_channels; + GArray *rates; + int min; + int max; +}; + +typedef struct _GstOssRange GstOssRange; +struct _GstOssRange +{ + int min; + int max; +}; + +static GstStructure *gst_osselement_get_format_structure (unsigned int + format_bit); +static gboolean gst_osselement_rate_probe_check (GstOssProbe * probe); +static int gst_osselement_rate_check_rate (GstOssProbe * probe, int irate); +static void gst_osselement_rate_add_range (GQueue * queue, int min, int max); +static void gst_osselement_rate_add_rate (GArray * array, int rate); +static int gst_osselement_rate_int_compare (gconstpointer a, gconstpointer b); + +void +gst_osselement_probe_caps (GstOssElement * oss) +{ + GstOssProbe *probe; + int i; + gboolean ret; + GstStructure *structure; + unsigned int format_bit; + unsigned int format_mask; + GstCaps *caps; + + if (oss->probed_caps != NULL) + return; + if (oss->fd == -1) + return; + + /* FIXME test make sure we're not currently playing */ + /* FIXME test both mono and stereo */ + + format_mask = AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_S8 | + AFMT_U16_LE | AFMT_U16_BE; + format_mask &= oss->caps; + + caps = gst_caps_new_empty (); + + /* assume that the most significant bit of format_mask is 0 */ + for (format_bit = 1; format_bit < format_mask; format_bit <<= 1) { + if (format_bit & format_mask) { + GValue rate_value = { 0 }; + + probe = g_new0 (GstOssProbe, 1); + probe->fd = oss->fd; + probe->format = format_bit; + probe->n_channels = 2; + + ret = gst_osselement_rate_probe_check (probe); + if (probe->min == -1 || probe->max == -1) { + g_array_free (probe->rates, TRUE); + g_free (probe); + continue; + } + + if (ret) { + GValue value = { 0 }; + + g_array_sort (probe->rates, gst_osselement_rate_int_compare); + + g_value_init (&rate_value, GST_TYPE_LIST); + g_value_init (&value, G_TYPE_INT); + + for (i = 0; i < probe->rates->len; i++) { + g_value_set_int (&value, g_array_index (probe->rates, int, i)); + + gst_value_list_append_value (&rate_value, &value); + } + + g_value_unset (&value); + } else { + /* one big range */ + g_value_init (&rate_value, GST_TYPE_INT_RANGE); + gst_value_set_int_range (&rate_value, probe->min, probe->max); + } + + g_array_free (probe->rates, TRUE); + g_free (probe); + + structure = gst_osselement_get_format_structure (format_bit); + gst_structure_set (structure, "channels", GST_TYPE_INT_RANGE, 1, 2, NULL); + gst_structure_set_value (structure, "rate", &rate_value); + g_value_unset (&rate_value); + + gst_caps_append_structure (caps, structure); + } + } + + GST_DEBUG ("probed caps: %" GST_PTR_FORMAT, caps); + oss->probed_caps = caps; +} + +static GstStructure * +gst_osselement_get_format_structure (unsigned int format_bit) +{ + GstStructure *structure; + int endianness; + gboolean sign; + int width; + + switch (format_bit) { + case AFMT_U8: + endianness = 0; + sign = FALSE; + width = 8; + break; + case AFMT_S16_LE: + endianness = G_LITTLE_ENDIAN; + sign = TRUE; + width = 16; + break; + case AFMT_S16_BE: + endianness = G_BIG_ENDIAN; + sign = TRUE; + width = 16; + break; + case AFMT_S8: + endianness = 0; + sign = TRUE; + width = 8; + break; + case AFMT_U16_LE: + endianness = G_LITTLE_ENDIAN; + sign = FALSE; + width = 16; + break; + case AFMT_U16_BE: + endianness = G_BIG_ENDIAN; + sign = FALSE; + width = 16; + 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); + } + + return structure; +} + +static gboolean +gst_osselement_rate_probe_check (GstOssProbe * probe) +{ + GstOssRange *range; + GQueue *ranges; + int exact_rates = 0; + gboolean checking_exact_rates = TRUE; + int n_checks = 0; + gboolean result = TRUE; + + ranges = g_queue_new (); + + probe->rates = g_array_new (FALSE, FALSE, sizeof (int)); + + probe->min = gst_osselement_rate_check_rate (probe, 1000); + n_checks++; + probe->max = gst_osselement_rate_check_rate (probe, 100000); + n_checks++; + if (probe->min == -1 || probe->max == -1) { + GST_DEBUG ("unexpected check_rate error"); + return FALSE; + } + gst_osselement_rate_add_range (ranges, probe->min + 1, probe->max - 1); + + while ((range = g_queue_pop_head (ranges))) { + int min1; + int max1; + int mid; + int mid_ret; + + GST_DEBUG ("checking [%d,%d]\n", range->min, range->max); + + mid = (range->min + range->max) / 2; + mid_ret = gst_osselement_rate_check_rate (probe, mid); + if (mid_ret == -1) { + /* FIXME ioctl returned an error. do something */ + GST_DEBUG ("unexpected check_rate error"); + } + n_checks++; + + if (mid == mid_ret && checking_exact_rates) { + int max_exact_matches = 20; + + exact_rates++; + if (exact_rates > max_exact_matches) { + GST_DEBUG ("got %d exact rates, assuming all are exact\n", + max_exact_matches); + result = FALSE; + g_free (range); + break; + } + } else { + checking_exact_rates = FALSE; + } + + /* Assume that the rate is arithmetically rounded to the nearest + * supported rate. */ + if (mid == mid_ret) { + min1 = mid - 1; + max1 = mid + 1; + } else { + if (mid < mid_ret) { + min1 = mid - (mid_ret - mid); + max1 = mid_ret + 1; + } else { + min1 = mid_ret - 1; + max1 = mid + (mid - mid_ret); + } + } + + gst_osselement_rate_add_range (ranges, range->min, min1); + gst_osselement_rate_add_range (ranges, max1, range->max); + + g_free (range); + } + + while ((range = g_queue_pop_head (ranges))) { + g_free (range); + } + g_queue_free (ranges); + + return result; +} + +static void +gst_osselement_rate_add_range (GQueue * queue, int min, int max) +{ + if (min <= max) { + GstOssRange *range = g_new0 (GstOssRange, 1); + + range->min = min; + range->max = max; + + g_queue_push_tail (queue, range); + /* push_head also works, but has different probing behavior */ + /*g_queue_push_head (queue, range); */ + } +} + +static int +gst_osselement_rate_check_rate (GstOssProbe * probe, int irate) +{ + int rate; + int format; + int n_channels; + int ret; + + rate = irate; + format = probe->format; + n_channels = probe->n_channels; + + ret = ioctl (probe->fd, SNDCTL_DSP_SETFMT, &format); + if (ret < 0) + return -1; + ret = ioctl (probe->fd, SNDCTL_DSP_CHANNELS, &n_channels); + if (ret < 0) + return -1; + ret = ioctl (probe->fd, SNDCTL_DSP_SPEED, &rate); + if (ret < 0) + return -1; + + GST_DEBUG ("rate %d -> %d\n", irate, rate); + + if (rate == irate - 1 || rate == irate + 1) { + rate = irate; + } + gst_osselement_rate_add_rate (probe->rates, rate); + return rate; +} + +static void +gst_osselement_rate_add_rate (GArray * array, int rate) +{ + int i; + int val; + + for (i = 0; i < array->len; i++) { + val = g_array_index (array, int, i); + + if (val == rate) + return; + } + GST_DEBUG ("supported rate: %d\n", rate); + g_array_append_val (array, rate); +} + +static int +gst_osselement_rate_int_compare (gconstpointer a, gconstpointer b) +{ + const int *va = (const int *) a; + const int *vb = (const int *) b; + + if (*va < *vb) + return -1; + if (*va > *vb) + return 1; + return 0; +} diff --git a/sys/oss/gstosselement.h b/sys/oss/gstosselement.h index 3ce84a2..9c30187 100644 --- a/sys/oss/gstosselement.h +++ b/sys/oss/gstosselement.h @@ -73,6 +73,7 @@ struct _GstOssElement guint64 fragment_time; gint fragment_size; GstOssOpenMode mode; + GstCaps *probed_caps; /* stats bytes per *second* */ guint bps; @@ -118,6 +119,7 @@ gboolean gst_osselement_convert (GstOssElement *oss, gint64 src_value, GstFormat *dest_format, gint64 *dest_value); +void gst_osselement_probe_caps (GstOssElement *oss); G_END_DECLS diff --git a/sys/oss/gstosssink.c b/sys/oss/gstosssink.c index 9d06b06..28a8c44 100644 --- a/sys/oss/gstosssink.c +++ b/sys/oss/gstosssink.c @@ -59,6 +59,7 @@ static gboolean gst_osssink_sink_query (GstPad * pad, GstQueryType type, GstFormat * format, gint64 * value); static GstCaps *gst_osssink_sink_fixate (GstPad * pad, const GstCaps * caps); +static GstCaps *gst_osssink_getcaps (GstPad * pad); static GstPadLinkReturn gst_osssink_sinkconnect (GstPad * pad, const GstCaps * caps); @@ -96,7 +97,7 @@ GST_STATIC_PAD_TEMPLATE ("sink", "signed = (boolean) { TRUE, FALSE }, " "width = (int) { 8, 16 }, " "depth = (int) { 8, 16 }, " - "rate = (int) [ 1000, 48000 ], " "channels = (int) [ 1, 2 ]") + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]") ); static GstElementClass *parent_class = NULL; @@ -202,6 +203,7 @@ gst_osssink_init (GstOssSink * osssink) (&osssink_sink_factory), "sink"); gst_element_add_pad (GST_ELEMENT (osssink), osssink->sinkpad); gst_pad_set_link_function (osssink->sinkpad, gst_osssink_sinkconnect); + gst_pad_set_getcaps_function (osssink->sinkpad, gst_osssink_getcaps); gst_pad_set_fixate_function (osssink->sinkpad, gst_osssink_sink_fixate); gst_pad_set_convert_function (osssink->sinkpad, gst_osssink_convert); gst_pad_set_query_function (osssink->sinkpad, gst_osssink_sink_query); @@ -256,6 +258,23 @@ gst_osssink_sink_fixate (GstPad * pad, const GstCaps * caps) return NULL; } +static GstCaps * +gst_osssink_getcaps (GstPad * pad) +{ + GstOssSink *osssink = GST_OSSSINK (gst_pad_get_parent (pad)); + GstCaps *caps; + + gst_osselement_probe_caps (GST_OSSELEMENT (osssink)); + + if (GST_OSSELEMENT (osssink)->probed_caps == NULL) { + caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); + } else { + caps = gst_caps_copy (GST_OSSELEMENT (osssink)->probed_caps); + } + + return caps; +} + static GstPadLinkReturn gst_osssink_sinkconnect (GstPad * pad, const GstCaps * caps) { diff --git a/sys/oss/gstosssrc.c b/sys/oss/gstosssrc.c index b35dbfc..1ff3ee4 100644 --- a/sys/oss/gstosssrc.c +++ b/sys/oss/gstosssrc.c @@ -66,7 +66,7 @@ static GstStaticPadTemplate osssrc_src_factory = GST_STATIC_PAD_TEMPLATE ("src", "signed = (boolean) { TRUE, FALSE }, " "width = (int) { 8, 16 }, " "depth = (int) { 8, 16 }, " - "rate = (int) [ 1000, 48000 ], " "channels = (int) [ 1, 2 ]") + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]") ); static void gst_osssrc_base_init (gpointer g_class); @@ -76,6 +76,7 @@ static void gst_osssrc_dispose (GObject * object); static GstPadLinkReturn gst_osssrc_srcconnect (GstPad * pad, const GstCaps * caps); +static GstCaps *gst_osssrc_getcaps (GstPad * pad); static const GstFormat *gst_osssrc_get_formats (GstPad * pad); static gboolean gst_osssrc_convert (GstPad * pad, GstFormat src_format, gint64 src_value, @@ -176,6 +177,7 @@ gst_osssrc_init (GstOssSrc * osssrc) gst_pad_new_from_template (gst_static_pad_template_get (&osssrc_src_factory), "src"); gst_pad_set_get_function (osssrc->srcpad, gst_osssrc_get); + gst_pad_set_getcaps_function (osssrc->srcpad, gst_osssrc_getcaps); gst_pad_set_link_function (osssrc->srcpad, gst_osssrc_srcconnect); gst_pad_set_convert_function (osssrc->srcpad, gst_osssrc_convert); gst_pad_set_formats_function (osssrc->srcpad, gst_osssrc_get_formats); @@ -184,7 +186,6 @@ gst_osssrc_init (GstOssSrc * osssrc) gst_pad_set_query_function (osssrc->srcpad, gst_osssrc_src_query); gst_pad_set_query_type_function (osssrc->srcpad, gst_osssrc_get_query_types); - gst_element_add_pad (GST_ELEMENT (osssrc), osssrc->srcpad); osssrc->buffersize = 4096; @@ -208,6 +209,25 @@ gst_osssrc_dispose (GObject * object) G_OBJECT_CLASS (parent_class)->dispose (object); } +static GstCaps * +gst_osssrc_getcaps (GstPad * pad) +{ + GstOssSrc *src; + GstCaps *caps; + + src = GST_OSSSRC (gst_pad_get_parent (pad)); + + gst_osselement_probe_caps (GST_OSSELEMENT (src)); + + if (GST_OSSELEMENT (src)->probed_caps == NULL) { + caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); + } else { + caps = gst_caps_copy (GST_OSSELEMENT (src)->probed_caps); + } + + return caps; +} + static GstPadLinkReturn gst_osssrc_srcconnect (GstPad * pad, const GstCaps * caps) { -- 2.7.4