gstaudioaggregator: expose output-buffer-duration-fraction

The code for this is mostly lifted from audiobuffersplit, it
allows use cases such as keeping the buffers output by compositor
on one branch and audiomixer on another perfectly aligned, by
requiring the compositor to output a n/d frame rate, and setting
output-buffer-duration to d/n on the audiomixer.

The old output-buffer-duration property now simply maps to its
fractional counterpart, the last set property wins.
This commit is contained in:
Mathieu Duponchelle 2019-05-14 01:56:58 +02:00
parent d90ac65af1
commit 31ac4f4665

View file

@ -210,8 +210,8 @@ gst_audio_aggregator_convert_pad_update_converter (GstAudioAggregatorConvertPad
aaggcpad->priv->converter =
gst_audio_converter_new (GST_AUDIO_CONVERTER_FLAG_NONE,
in_info, out_info,
aaggcpad->priv->converter_config ? gst_structure_copy (aaggcpad->
priv->converter_config) : NULL);
aaggcpad->priv->converter_config ? gst_structure_copy (aaggcpad->priv->
converter_config) : NULL);
}
aaggcpad->priv->converter_config_changed = FALSE;
@ -368,10 +368,17 @@ struct _GstAudioAggregatorPrivate
/* All three properties are unprotected, can't be modified while streaming */
/* Size in frames that is output per buffer */
GstClockTime output_buffer_duration;
GstClockTime alignment_threshold;
GstClockTime discont_wait;
gint output_buffer_duration_n;
gint output_buffer_duration_d;
guint samples_per_buffer;
guint error_per_buffer;
guint accumulated_error;
guint current_blocksize;
/* Protected by srcpad stream clock */
/* Output buffer starting at offset containing blocksize frames (calculated
* from output_buffer_duration) */
@ -424,6 +431,8 @@ static GstCaps *gst_audio_aggregator_fixate_src_caps (GstAggregator * agg,
#define DEFAULT_OUTPUT_BUFFER_DURATION (10 * GST_MSECOND)
#define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND)
#define DEFAULT_DISCONT_WAIT (1 * GST_SECOND)
#define DEFAULT_OUTPUT_BUFFER_DURATION_N (1)
#define DEFAULT_OUTPUT_BUFFER_DURATION_D (100)
enum
{
@ -431,6 +440,7 @@ enum
PROP_OUTPUT_BUFFER_DURATION,
PROP_ALIGNMENT_THRESHOLD,
PROP_DISCONT_WAIT,
PROP_OUTPUT_BUFFER_DURATION_FRACTION,
};
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstAudioAggregator, gst_audio_aggregator,
@ -448,6 +458,79 @@ gst_audio_aggregator_convert_buffer (GstAudioAggregator * aagg, GstPad * pad,
return klass->convert_buffer (aaggpad, in_info, out_info, buffer);
}
static void
gst_audio_aggregator_translate_output_buffer_duration (GstAudioAggregator *
aagg, GstClockTime duration)
{
gint gcd;
aagg->priv->output_buffer_duration_n = duration;
aagg->priv->output_buffer_duration_d = GST_SECOND;
gcd = gst_util_greatest_common_divisor (aagg->priv->output_buffer_duration_n,
aagg->priv->output_buffer_duration_d);
if (gcd) {
aagg->priv->output_buffer_duration_n /= gcd;
aagg->priv->output_buffer_duration_d /= gcd;
}
}
static gboolean
gst_audio_aggregator_update_samples_per_buffer (GstAudioAggregator * aagg)
{
gboolean ret = TRUE;
GstAudioAggregatorPad *srcpad =
GST_AUDIO_AGGREGATOR_PAD (GST_AGGREGATOR_SRC_PAD (aagg));
if (!srcpad->info.finfo
|| GST_AUDIO_INFO_FORMAT (&srcpad->info) == GST_AUDIO_FORMAT_UNKNOWN) {
ret = FALSE;
goto out;
}
aagg->priv->samples_per_buffer =
(((guint64) GST_AUDIO_INFO_RATE (&srcpad->info)) *
aagg->priv->output_buffer_duration_n) /
aagg->priv->output_buffer_duration_d;
if (aagg->priv->samples_per_buffer == 0) {
ret = FALSE;
goto out;
}
aagg->priv->error_per_buffer =
(((guint64) GST_AUDIO_INFO_RATE (&srcpad->info)) *
aagg->priv->output_buffer_duration_n) %
aagg->priv->output_buffer_duration_d;
aagg->priv->accumulated_error = 0;
GST_DEBUG_OBJECT (aagg, "Buffer duration: %u/%u",
aagg->priv->output_buffer_duration_n,
aagg->priv->output_buffer_duration_d);
GST_DEBUG_OBJECT (aagg, "Samples per buffer: %u (error: %u/%u)",
aagg->priv->samples_per_buffer, aagg->priv->error_per_buffer,
aagg->priv->output_buffer_duration_d);
out:
return ret;
}
static void
gst_audio_aggregator_recalculate_latency (GstAudioAggregator * aagg)
{
guint64 latency = gst_util_uint64_scale_int (GST_SECOND,
aagg->priv->output_buffer_duration_n,
aagg->priv->output_buffer_duration_d);
gst_aggregator_set_latency (GST_AGGREGATOR (aagg), latency, latency);
GST_OBJECT_LOCK (aagg);
/* Force recalculating in aggregate */
aagg->priv->samples_per_buffer = 0;
GST_OBJECT_UNLOCK (aagg);
}
static void
gst_audio_aggregator_class_init (GstAudioAggregatorClass * klass)
{
@ -489,6 +572,16 @@ gst_audio_aggregator_class_init (GstAudioAggregatorClass * klass)
G_MAXUINT64, DEFAULT_OUTPUT_BUFFER_DURATION,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_OUTPUT_BUFFER_DURATION_FRACTION,
gst_param_spec_fraction ("output-buffer-duration-fraction",
"Output buffer duration fraction",
"Output block size in nanoseconds, expressed as a fraction", 1,
G_MAXINT, G_MAXINT, 1, DEFAULT_OUTPUT_BUFFER_DURATION_N,
DEFAULT_OUTPUT_BUFFER_DURATION_D,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
GST_PARAM_MUTABLE_READY));
g_object_class_install_property (gobject_class, PROP_ALIGNMENT_THRESHOLD,
g_param_spec_uint64 ("alignment-threshold", "Alignment Threshold",
"Timestamp alignment threshold in nanoseconds", 0,
@ -510,14 +603,14 @@ gst_audio_aggregator_init (GstAudioAggregator * aagg)
g_mutex_init (&aagg->priv->mutex);
aagg->priv->output_buffer_duration = DEFAULT_OUTPUT_BUFFER_DURATION;
aagg->priv->alignment_threshold = DEFAULT_ALIGNMENT_THRESHOLD;
aagg->priv->discont_wait = DEFAULT_DISCONT_WAIT;
aagg->current_caps = NULL;
gst_audio_aggregator_translate_output_buffer_duration (aagg,
DEFAULT_OUTPUT_BUFFER_DURATION);
gst_audio_aggregator_recalculate_latency (aagg);
gst_aggregator_set_latency (GST_AGGREGATOR (aagg),
aagg->priv->output_buffer_duration, aagg->priv->output_buffer_duration);
aagg->current_caps = NULL;
}
static void
@ -540,10 +633,10 @@ gst_audio_aggregator_set_property (GObject * object, guint prop_id,
switch (prop_id) {
case PROP_OUTPUT_BUFFER_DURATION:
aagg->priv->output_buffer_duration = g_value_get_uint64 (value);
gst_aggregator_set_latency (GST_AGGREGATOR (aagg),
aagg->priv->output_buffer_duration,
aagg->priv->output_buffer_duration);
gst_audio_aggregator_translate_output_buffer_duration (aagg,
g_value_get_uint64 (value));
g_object_notify (object, "output-buffer-duration-fraction");
gst_audio_aggregator_recalculate_latency (aagg);
break;
case PROP_ALIGNMENT_THRESHOLD:
aagg->priv->alignment_threshold = g_value_get_uint64 (value);
@ -551,6 +644,14 @@ gst_audio_aggregator_set_property (GObject * object, guint prop_id,
case PROP_DISCONT_WAIT:
aagg->priv->discont_wait = g_value_get_uint64 (value);
break;
case PROP_OUTPUT_BUFFER_DURATION_FRACTION:
aagg->priv->output_buffer_duration_n =
gst_value_get_fraction_numerator (value);
aagg->priv->output_buffer_duration_d =
gst_value_get_fraction_denominator (value);
g_object_notify (object, "output-buffer-duration");
gst_audio_aggregator_recalculate_latency (aagg);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -565,7 +666,9 @@ gst_audio_aggregator_get_property (GObject * object, guint prop_id,
switch (prop_id) {
case PROP_OUTPUT_BUFFER_DURATION:
g_value_set_uint64 (value, aagg->priv->output_buffer_duration);
g_value_set_uint64 (value, gst_util_uint64_scale_int (GST_SECOND,
aagg->priv->output_buffer_duration_n,
aagg->priv->output_buffer_duration_d));
break;
case PROP_ALIGNMENT_THRESHOLD:
g_value_set_uint64 (value, aagg->priv->alignment_threshold);
@ -573,6 +676,10 @@ gst_audio_aggregator_get_property (GObject * object, guint prop_id,
case PROP_DISCONT_WAIT:
g_value_set_uint64 (value, aagg->priv->discont_wait);
break;
case PROP_OUTPUT_BUFFER_DURATION_FRACTION:
gst_value_set_fraction (value, aagg->priv->output_buffer_duration_n,
aagg->priv->output_buffer_duration_d);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -866,8 +973,8 @@ gst_audio_aggregator_negotiated_src_caps (GstAggregator * agg, GstCaps * caps)
gst_audio_aggregator_update_converters (aagg, &info, &old_info);
if (srcpad_klass->update_conversion_info)
srcpad_klass->
update_conversion_info (GST_AUDIO_AGGREGATOR_PAD (agg->srcpad));
srcpad_klass->update_conversion_info (GST_AUDIO_AGGREGATOR_PAD (agg->
srcpad));
if (aagg->priv->current_buffer) {
GstBuffer *converted;
@ -878,6 +985,9 @@ gst_audio_aggregator_negotiated_src_caps (GstAggregator * agg, GstCaps * caps)
gst_buffer_unref (aagg->priv->current_buffer);
aagg->priv->current_buffer = converted;
}
/* Force recalculating in aggregate */
aagg->priv->samples_per_buffer = 0;
}
GST_OBJECT_UNLOCK (aagg);
@ -1180,8 +1290,8 @@ gst_audio_aggregator_src_query (GstAggregator * agg, GstQuery * query)
switch (format) {
case GST_FORMAT_TIME:
gst_query_set_position (query, format,
gst_segment_to_stream_time (&GST_AGGREGATOR_PAD (agg->
srcpad)->segment, GST_FORMAT_TIME,
gst_segment_to_stream_time (&GST_AGGREGATOR_PAD (agg->srcpad)->
segment, GST_FORMAT_TIME,
GST_AGGREGATOR_PAD (agg->srcpad)->segment.position));
res = TRUE;
break;
@ -1247,6 +1357,7 @@ gst_audio_aggregator_reset (GstAudioAggregator * aagg)
gst_audio_info_init (&GST_AUDIO_AGGREGATOR_PAD (agg->srcpad)->info);
gst_caps_replace (&aagg->current_caps, NULL);
gst_buffer_replace (&aagg->priv->current_buffer, NULL);
aagg->priv->accumulated_error = 0;
GST_OBJECT_UNLOCK (aagg);
GST_AUDIO_AGGREGATOR_UNLOCK (aagg);
}
@ -1280,6 +1391,7 @@ gst_audio_aggregator_flush (GstAggregator * agg)
GST_OBJECT_LOCK (aagg);
GST_AGGREGATOR_PAD (agg->srcpad)->segment.position = -1;
aagg->priv->offset = -1;
aagg->priv->accumulated_error = 0;
gst_buffer_replace (&aagg->priv->current_buffer, NULL);
GST_OBJECT_UNLOCK (aagg);
GST_AUDIO_AGGREGATOR_UNLOCK (aagg);
@ -1698,6 +1810,15 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout)
GST_AUDIO_AGGREGATOR_LOCK (aagg);
GST_OBJECT_LOCK (agg);
if (aagg->priv->samples_per_buffer == 0) {
if (!gst_audio_aggregator_update_samples_per_buffer (aagg)) {
GST_ERROR_OBJECT (aagg,
"Failed to calculate the number of samples per buffer");
GST_OBJECT_UNLOCK (agg);
goto not_negotiated;
}
}
/* Update position from the segment start/stop if needed */
if (agg_segment->position == -1) {
if (agg_segment->rate > 0.0)
@ -1706,16 +1827,31 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout)
agg_segment->position = agg_segment->stop;
}
rate = GST_AUDIO_INFO_RATE (&srcpad->info);
bpf = GST_AUDIO_INFO_BPF (&srcpad->info);
if (G_UNLIKELY (srcpad->info.finfo->format == GST_AUDIO_FORMAT_UNKNOWN)) {
if (timeout) {
GstClockTime output_buffer_duration;
GST_DEBUG_OBJECT (aagg,
"Got timeout before receiving any caps, don't output anything");
blocksize = aagg->priv->samples_per_buffer;
if (aagg->priv->error_per_buffer + aagg->priv->accumulated_error >=
aagg->priv->output_buffer_duration_d)
blocksize += 1;
aagg->priv->accumulated_error =
(aagg->priv->accumulated_error +
aagg->priv->error_per_buffer) % aagg->priv->output_buffer_duration_d;
output_buffer_duration =
gst_util_uint64_scale (blocksize, GST_SECOND, rate);
/* Advance position */
if (agg_segment->rate > 0.0)
agg_segment->position += aagg->priv->output_buffer_duration;
else if (agg_segment->position > aagg->priv->output_buffer_duration)
agg_segment->position -= aagg->priv->output_buffer_duration;
agg_segment->position += output_buffer_duration;
else if (agg_segment->position > output_buffer_duration)
agg_segment->position -= output_buffer_duration;
else
agg_segment->position = 0;
@ -1728,9 +1864,6 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout)
}
}
rate = GST_AUDIO_INFO_RATE (&srcpad->info);
bpf = GST_AUDIO_INFO_BPF (&srcpad->info);
if (aagg->priv->offset == -1) {
aagg->priv->offset =
gst_util_uint64_scale (agg_segment->position - agg_segment->start, rate,
@ -1739,9 +1872,29 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout)
aagg->priv->offset);
}
blocksize = gst_util_uint64_scale (aagg->priv->output_buffer_duration,
rate, GST_SECOND);
blocksize = MAX (1, blocksize);
if (aagg->priv->current_buffer == NULL) {
blocksize = aagg->priv->samples_per_buffer;
if (aagg->priv->error_per_buffer + aagg->priv->accumulated_error >=
aagg->priv->output_buffer_duration_d)
blocksize += 1;
aagg->priv->current_blocksize = blocksize;
aagg->priv->accumulated_error =
(aagg->priv->accumulated_error +
aagg->priv->error_per_buffer) % aagg->priv->output_buffer_duration_d;
GST_OBJECT_UNLOCK (agg);
aagg->priv->current_buffer =
GST_AUDIO_AGGREGATOR_GET_CLASS (aagg)->create_output_buffer (aagg,
blocksize);
/* Be careful, some things could have changed ? */
GST_OBJECT_LOCK (agg);
GST_BUFFER_FLAG_SET (aagg->priv->current_buffer, GST_BUFFER_FLAG_GAP);
} else {
blocksize = aagg->priv->current_blocksize;
}
/* FIXME: Reverse mixing does not work at all yet */
if (agg_segment->rate > 0.0) {
@ -1755,15 +1908,6 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout)
agg_segment->start + gst_util_uint64_scale (next_offset, GST_SECOND,
rate);
if (aagg->priv->current_buffer == NULL) {
GST_OBJECT_UNLOCK (agg);
aagg->priv->current_buffer =
GST_AUDIO_AGGREGATOR_GET_CLASS (aagg)->create_output_buffer (aagg,
blocksize);
/* Be careful, some things could have changed ? */
GST_OBJECT_LOCK (agg);
GST_BUFFER_FLAG_SET (aagg->priv->current_buffer, GST_BUFFER_FLAG_GAP);
}
outbuf = aagg->priv->current_buffer;
GST_LOG_OBJECT (agg,