diff --git a/gst/audioresample/gstaudioresample.c b/gst/audioresample/gstaudioresample.c index d3b465adfb..a14bf6d9fe 100644 --- a/gst/audioresample/gstaudioresample.c +++ b/gst/audioresample/gstaudioresample.c @@ -223,6 +223,7 @@ gst_audio_resample_init (GstAudioResample * resample, resample->quality = SPEEX_RESAMPLER_QUALITY_DEFAULT; + gst_base_transform_set_gap_aware (trans, TRUE); gst_pad_set_query_function (trans->srcpad, gst_audio_resample_query); gst_pad_set_query_type_function (trans->srcpad, gst_audio_resample_query_type); @@ -236,6 +237,8 @@ gst_audio_resample_start (GstBaseTransform * base) resample->need_discont = TRUE; + resample->count_gap = 0; + resample->count_nongap = 0; resample->t0 = GST_CLOCK_TIME_NONE; resample->in_offset0 = GST_BUFFER_OFFSET_NONE; resample->out_offset0 = GST_BUFFER_OFFSET_NONE; @@ -385,8 +388,6 @@ gst_audio_resample_init_state (GstAudioResample * resample, gint width, return NULL; } - funcs->skip_zeros (ret); - return ret; } @@ -780,18 +781,48 @@ gst_audio_resample_workspace_realloc (guint8 ** workspace, guint * size, return *workspace; } +/* Push history_len zeros into the filter, but discard the output. */ static void -gst_audio_resample_push_drain (GstAudioResample * resample) +gst_audio_resample_dump_drain (GstAudioResample * resample, guint history_len) +{ + gint outsize; + guint in_len, in_processed; + guint out_len, out_processed; + guint num, den; + void *buf; + + g_assert (resample->state != NULL); + + resample->funcs->get_ratio (resample->state, &num, &den); + + in_len = in_processed = history_len; + out_processed = out_len = + gst_util_uint64_scale_int_ceil (history_len, den, num); + outsize = out_len * resample->channels * (resample->funcs->width / 8); + + if (out_len == 0) + return; + + buf = g_malloc (outsize); + resample->funcs->process (resample->state, NULL, &in_processed, buf, + &out_processed); + g_free (buf); + + g_assert (in_len == in_processed); +} + +static void +gst_audio_resample_push_drain (GstAudioResample * resample, guint history_len) { GstBuffer *outbuf; GstFlowReturn res; gint outsize; - guint history_len, out_len, out_processed; + guint in_len, in_processed; + guint out_len, out_processed; gint err; guint num, den; - if (!resample->state) - return; + g_assert (resample->state != NULL); /* Don't drain samples if we were reset. */ if (!GST_CLOCK_TIME_IS_VALID (resample->t0)) @@ -799,11 +830,14 @@ gst_audio_resample_push_drain (GstAudioResample * resample) resample->funcs->get_ratio (resample->state, &num, &den); - history_len = resample->funcs->get_input_latency (resample->state); + in_len = in_processed = history_len; out_len = out_processed = gst_util_uint64_scale_int_ceil (history_len, den, num); outsize = out_len * resample->channels * (resample->width / 8); + if (out_len == 0) + return; + res = gst_pad_alloc_buffer_and_set_caps (GST_BASE_TRANSFORM_SRC_PAD (resample), GST_BUFFER_OFFSET_NONE, outsize, @@ -824,7 +858,7 @@ gst_audio_resample_push_drain (GstAudioResample * resample) } /* process */ - err = resample->funcs->process (resample->state, NULL, &history_len, + err = resample->funcs->process (resample->state, NULL, &in_processed, resample->tmp_out, &out_processed); /* convert output format */ @@ -832,7 +866,7 @@ gst_audio_resample_push_drain (GstAudioResample * resample) GST_BUFFER_DATA (outbuf), out_processed, TRUE); } else { /* don't need to convert data format; process */ - err = resample->funcs->process (resample->state, NULL, &history_len, + err = resample->funcs->process (resample->state, NULL, &in_processed, GST_BUFFER_DATA (outbuf), &out_processed); } @@ -847,12 +881,6 @@ gst_audio_resample_push_drain (GstAudioResample * resample) return; } - if (G_UNLIKELY (out_processed == 0)) { - GST_WARNING_OBJECT (resample, "Failed to get drain, dropping buffer"); - gst_buffer_unref (outbuf); - return; - } - if (GST_CLOCK_TIME_IS_VALID (resample->t0)) { GST_BUFFER_OFFSET (outbuf) = resample->next_out_offset; GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + out_processed; @@ -864,7 +892,7 @@ gst_audio_resample_push_drain (GstAudioResample * resample) resample->out_offset0, GST_SECOND, resample->outrate) - GST_BUFFER_TIMESTAMP (outbuf); resample->next_out_offset += out_processed; - resample->next_in_offset += 0; + resample->next_in_offset += history_len; } else { GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE; GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE; @@ -872,6 +900,12 @@ gst_audio_resample_push_drain (GstAudioResample * resample) GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE; } + if (G_UNLIKELY (out_processed == 0 && in_len * den > num)) { + GST_WARNING_OBJECT (resample, "Failed to get drain, dropping buffer"); + gst_buffer_unref (outbuf); + return; + } + GST_BUFFER_SIZE (outbuf) = out_processed * resample->channels * (resample->width / 8); @@ -900,6 +934,11 @@ gst_audio_resample_event (GstBaseTransform * base, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_STOP: gst_audio_resample_reset_state (resample); + if (resample->state) + resample->count_gap = resample->funcs->get_filt_len (resample->state); + else + resample->count_gap = 0; + resample->count_nongap = 0; resample->t0 = GST_CLOCK_TIME_NONE; resample->in_offset0 = GST_BUFFER_OFFSET_NONE; resample->out_offset0 = GST_BUFFER_OFFSET_NONE; @@ -908,8 +947,14 @@ gst_audio_resample_event (GstBaseTransform * base, GstEvent * event) resample->need_discont = TRUE; break; case GST_EVENT_NEWSEGMENT: - gst_audio_resample_push_drain (resample); + if (resample->state) + gst_audio_resample_push_drain (resample, resample->count_nongap); gst_audio_resample_reset_state (resample); + if (resample->state) + resample->count_gap = resample->funcs->get_filt_len (resample->state); + else + resample->count_gap = 0; + resample->count_nongap = 0; resample->t0 = GST_CLOCK_TIME_NONE; resample->in_offset0 = GST_BUFFER_OFFSET_NONE; resample->out_offset0 = GST_BUFFER_OFFSET_NONE; @@ -918,7 +963,8 @@ gst_audio_resample_event (GstBaseTransform * base, GstEvent * event) resample->need_discont = TRUE; break; case GST_EVENT_EOS: - gst_audio_resample_push_drain (resample); + if (resample->state) + gst_audio_resample_push_drain (resample, resample->count_nongap); gst_audio_resample_reset_state (resample); break; default: @@ -933,6 +979,9 @@ gst_audio_resample_check_discont (GstAudioResample * resample, GstBuffer * buf) { guint64 offset; guint64 delta; + guint filt_len = resample->funcs->get_filt_len (resample->state); + guint64 delay = + gst_util_uint64_scale_round (filt_len, GST_SECOND, 2 * resample->inrate); /* is the incoming buffer a discontinuity? */ if (G_UNLIKELY (GST_BUFFER_IS_DISCONT (buf))) @@ -949,7 +998,7 @@ gst_audio_resample_check_discont (GstAudioResample * resample, GstBuffer * buf) offset = resample->in_offset0 + gst_util_uint64_scale_int_round (GST_BUFFER_TIMESTAMP (buf) - - resample->t0, resample->inrate, GST_SECOND); + resample->t0 - delay, resample->inrate, GST_SECOND); /* many elements generate imperfect streams due to rounding errors, so we * permit a small error (up to one sample) without triggering a filter @@ -972,7 +1021,7 @@ gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf, { guint32 in_len, in_processed; guint32 out_len, out_processed; - gint err; + guint filt_len = resample->funcs->get_filt_len (resample->state); in_len = GST_BUFFER_SIZE (inbuf) / resample->channels; out_len = GST_BUFFER_SIZE (outbuf) / resample->channels; @@ -983,47 +1032,93 @@ gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf, in_processed = in_len; out_processed = out_len; - if (resample->funcs->width != resample->width) { - /* need to convert data format for processing; ensure we have enough - * workspace available */ - if (!gst_audio_resample_workspace_realloc (&resample->tmp_in, - &resample->tmp_in_size, in_len * resample->channels * - (resample->funcs->width / 8)) || - !gst_audio_resample_workspace_realloc (&resample->tmp_out, - &resample->tmp_out_size, out_len * resample->channels * - (resample->funcs->width / 8))) { - GST_ERROR_OBJECT (resample, "failed to allocate workspace"); - return GST_FLOW_ERROR; + if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { + resample->count_nongap = 0; + if (resample->count_gap < filt_len) { + guint zeros_to_push; + if (in_len >= filt_len - resample->count_gap) + zeros_to_push = filt_len - resample->count_gap; + else + zeros_to_push = in_len; + + gst_audio_resample_push_drain (resample, zeros_to_push); + in_len -= zeros_to_push; + resample->count_gap += zeros_to_push; } - /* convert input */ - gst_audio_resample_convert_buffer (resample, GST_BUFFER_DATA (inbuf), - resample->tmp_in, in_len, FALSE); + { + guint num, den; + resample->funcs->get_ratio (resample->state, &num, &den); + out_processed = + gst_util_uint64_scale_int_ceil (resample->next_in_offset - + resample->in_offset0 + in_len, den, + num) - resample->next_out_offset + resample->out_offset0; - /* process */ - err = resample->funcs->process (resample->state, - resample->tmp_in, &in_processed, resample->tmp_out, &out_processed); + memset (GST_BUFFER_DATA (outbuf), 0, GST_BUFFER_SIZE (outbuf)); + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP); + resample->count_gap += in_len; + in_processed = in_len; + } + } else { /* not a gap */ - /* convert output */ - gst_audio_resample_convert_buffer (resample, resample->tmp_out, - GST_BUFFER_DATA (outbuf), out_processed, TRUE); - } else { - /* no format conversion required; process */ - err = resample->funcs->process (resample->state, - GST_BUFFER_DATA (inbuf), &in_processed, - GST_BUFFER_DATA (outbuf), &out_processed); + gint err; + + if (resample->count_gap > filt_len) { + /* push in enough zeros to restore the filter to the right offset */ + guint num, den; + resample->funcs->get_ratio (resample->state, &num, &den); + gst_audio_resample_dump_drain (resample, + (resample->count_gap - filt_len) % num); + } + resample->count_gap = 0; + if (resample->count_nongap < filt_len) { + resample->count_nongap += in_len; + if (resample->count_nongap > filt_len) + resample->count_nongap = filt_len; + } + + if (resample->funcs->width != resample->width) { + /* need to convert data format for processing; ensure we have enough + * workspace available */ + if (!gst_audio_resample_workspace_realloc (&resample->tmp_in, + &resample->tmp_in_size, in_len * resample->channels * + (resample->funcs->width / 8)) || + !gst_audio_resample_workspace_realloc (&resample->tmp_out, + &resample->tmp_out_size, out_len * resample->channels * + (resample->funcs->width / 8))) { + GST_ERROR_OBJECT (resample, "failed to allocate workspace"); + return GST_FLOW_ERROR; + } + + /* convert input */ + gst_audio_resample_convert_buffer (resample, GST_BUFFER_DATA (inbuf), + resample->tmp_in, in_len, FALSE); + + /* process */ + err = resample->funcs->process (resample->state, + resample->tmp_in, &in_processed, resample->tmp_out, &out_processed); + + /* convert output */ + gst_audio_resample_convert_buffer (resample, resample->tmp_out, + GST_BUFFER_DATA (outbuf), out_processed, TRUE); + } else { + /* no format conversion required; process */ + err = resample->funcs->process (resample->state, + GST_BUFFER_DATA (inbuf), &in_processed, + GST_BUFFER_DATA (outbuf), &out_processed); + } + + if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) { + GST_ERROR_OBJECT (resample, "Failed to convert data: %s", + resample->funcs->strerror (err)); + return GST_FLOW_ERROR; + } } /* If we wrote more than allocated something is really wrong now and we * should better abort immediately */ g_assert (out_len >= out_processed); - if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) { - GST_ERROR_OBJECT (resample, "Failed to convert data: %s", - resample->funcs->strerror (err)); - return GST_FLOW_ERROR; - } - if (G_UNLIKELY (in_len != in_processed)) { GST_WARNING_OBJECT (resample, "converted %d of %d input samples", in_processed, in_len); @@ -1108,7 +1203,11 @@ gst_audio_resample_transform (GstBaseTransform * base, GstBuffer * inbuf, /* resync the timestamp and offset counters if possible */ if (GST_BUFFER_TIMESTAMP_IS_VALID (inbuf) && GST_BUFFER_OFFSET_IS_VALID (inbuf)) { - resample->t0 = GST_BUFFER_TIMESTAMP (inbuf); + guint filt_len = resample->funcs->get_filt_len (resample->state); + guint64 delay = gst_util_uint64_scale_round (filt_len, GST_SECOND, + 2 * resample->inrate); + resample->count_gap = resample->funcs->get_filt_len (resample->state); + resample->t0 = GST_BUFFER_TIMESTAMP (inbuf) - delay; resample->in_offset0 = GST_BUFFER_OFFSET (inbuf); resample->out_offset0 = gst_util_uint64_scale_int_round (resample->in_offset0, diff --git a/gst/audioresample/gstaudioresample.h b/gst/audioresample/gstaudioresample.h index 2f41b2cecb..3f8d71d824 100644 --- a/gst/audioresample/gstaudioresample.h +++ b/gst/audioresample/gstaudioresample.h @@ -64,6 +64,9 @@ struct _GstAudioResample { guint64 next_in_offset; guint64 next_out_offset; + guint count_gap; + guint count_nongap; + gint channels; gint inrate; gint outrate; diff --git a/gst/audioresample/resample.c b/gst/audioresample/resample.c index 2cfd5442e3..7d42f0e0fd 100644 --- a/gst/audioresample/resample.c +++ b/gst/audioresample/resample.c @@ -1310,6 +1310,12 @@ speex_resampler_get_output_latency (SpeexResamplerState * st) (st->num_rate >> 1)) / st->num_rate; } +EXPORT int +speex_resampler_get_filt_len (SpeexResamplerState * st) +{ + return st->filt_len; +} + EXPORT int speex_resampler_skip_zeros (SpeexResamplerState * st) { diff --git a/gst/audioresample/speex_resampler.h b/gst/audioresample/speex_resampler.h index 9dc02806a6..894b380f80 100644 --- a/gst/audioresample/speex_resampler.h +++ b/gst/audioresample/speex_resampler.h @@ -73,6 +73,7 @@ #define speex_resampler_get_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_stride) #define speex_resampler_get_input_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_latency) #define speex_resampler_get_output_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_latency) +#define speex_resampler_get_filt_len CAT_PREFIX(RANDOM_PREFIX,_resampler_get_filt_len) #define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros) #define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem) #define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror) @@ -333,6 +334,11 @@ int speex_resampler_get_input_latency(SpeexResamplerState *st); */ int speex_resampler_get_output_latency(SpeexResamplerState *st); +/** Get the length of the filter in input samples. + * @param st Resampler state + */ +int speex_resampler_get_filt_len(SpeexResamplerState *st); + /** Make sure that the first samples to go out of the resamplers don't have * leading zeros. This is only useful before starting to use a newly created * resampler. It is recommended to use that when resampling an audio file, as diff --git a/gst/audioresample/speex_resampler_wrapper.h b/gst/audioresample/speex_resampler_wrapper.h index 36d444f879..08a82bc105 100644 --- a/gst/audioresample/speex_resampler_wrapper.h +++ b/gst/audioresample/speex_resampler_wrapper.h @@ -52,6 +52,7 @@ typedef struct { void (*get_ratio) (SpeexResamplerState * st, guint32 * ratio_num, guint32 * ratio_den); int (*get_input_latency) (SpeexResamplerState * st); + int (*get_filt_len) (SpeexResamplerState * st); int (*set_quality) (SpeexResamplerState * st, gint quality); int (*reset_mem) (SpeexResamplerState * st); int (*skip_zeros) (SpeexResamplerState * st); @@ -71,6 +72,7 @@ void resample_float_resampler_get_rate (SpeexResamplerState * st, void resample_float_resampler_get_ratio (SpeexResamplerState * st, guint32 * ratio_num, guint32 * ratio_den); int resample_float_resampler_get_input_latency (SpeexResamplerState * st); +int resample_float_resampler_get_filt_len (SpeexResamplerState * st); int resample_float_resampler_set_quality (SpeexResamplerState * st, gint quality); int resample_float_resampler_reset_mem (SpeexResamplerState * st); int resample_float_resampler_skip_zeros (SpeexResamplerState * st); @@ -85,6 +87,7 @@ static const SpeexResampleFuncs float_funcs = resample_float_resampler_get_rate, resample_float_resampler_get_ratio, resample_float_resampler_get_input_latency, + resample_float_resampler_get_filt_len, resample_float_resampler_set_quality, resample_float_resampler_reset_mem, resample_float_resampler_skip_zeros, @@ -104,6 +107,7 @@ void resample_double_resampler_get_rate (SpeexResamplerState * st, void resample_double_resampler_get_ratio (SpeexResamplerState * st, guint32 * ratio_num, guint32 * ratio_den); int resample_double_resampler_get_input_latency (SpeexResamplerState * st); +int resample_double_resampler_get_filt_len (SpeexResamplerState * st); int resample_double_resampler_set_quality (SpeexResamplerState * st, gint quality); int resample_double_resampler_reset_mem (SpeexResamplerState * st); int resample_double_resampler_skip_zeros (SpeexResamplerState * st); @@ -118,6 +122,7 @@ static const SpeexResampleFuncs double_funcs = resample_double_resampler_get_rate, resample_double_resampler_get_ratio, resample_double_resampler_get_input_latency, + resample_double_resampler_get_filt_len, resample_double_resampler_set_quality, resample_double_resampler_reset_mem, resample_double_resampler_skip_zeros, @@ -137,6 +142,7 @@ void resample_int_resampler_get_rate (SpeexResamplerState * st, void resample_int_resampler_get_ratio (SpeexResamplerState * st, guint32 * ratio_num, guint32 * ratio_den); int resample_int_resampler_get_input_latency (SpeexResamplerState * st); +int resample_int_resampler_get_filt_len (SpeexResamplerState * st); int resample_int_resampler_set_quality (SpeexResamplerState * st, gint quality); int resample_int_resampler_reset_mem (SpeexResamplerState * st); int resample_int_resampler_skip_zeros (SpeexResamplerState * st); @@ -151,6 +157,7 @@ static const SpeexResampleFuncs int_funcs = resample_int_resampler_get_rate, resample_int_resampler_get_ratio, resample_int_resampler_get_input_latency, + resample_int_resampler_get_filt_len, resample_int_resampler_set_quality, resample_int_resampler_reset_mem, resample_int_resampler_skip_zeros,