static GstFlowReturn vorbis_dec_push_forward (GstVorbisDec * dec, GstBuffer * buf) { GstFlowReturn result; /* clip */ if (!(buf = gst_audio_buffer_clip (buf, &dec->segment, dec->vi.rate, dec->vi.channels * dec->width))) { GST_LOG_OBJECT (dec, "clipped buffer"); return GST_FLOW_OK; } if (dec->discont) { GST_LOG_OBJECT (dec, "setting DISCONT"); GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); dec->discont = FALSE; } GST_DEBUG_OBJECT (dec, "pushing time %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_TIME_ARGS (GST_BUFFER_DURATION (buf))); result = gst_pad_push (dec->srcpad, buf); return result; }
static GstFlowReturn gst_a52dec_push (GstA52Dec * a52dec, GstPad * srcpad, int flags, sample_t * samples, GstClockTime timestamp) { GstBuffer *buf; int chans, n, c; GstFlowReturn result; flags &= (A52_CHANNEL_MASK | A52_LFE); chans = gst_a52dec_channels (flags, NULL); if (!chans) { GST_ELEMENT_ERROR (GST_ELEMENT (a52dec), STREAM, DECODE, (NULL), ("invalid channel flags: %d", flags)); return GST_FLOW_ERROR; } result = gst_pad_alloc_buffer_and_set_caps (srcpad, 0, 256 * chans * (SAMPLE_WIDTH / 8), GST_PAD_CAPS (srcpad), &buf); if (result != GST_FLOW_OK) return result; for (n = 0; n < 256; n++) { for (c = 0; c < chans; c++) { ((sample_t *) GST_BUFFER_DATA (buf))[n * chans + c] = samples[c * 256 + n]; } } GST_BUFFER_TIMESTAMP (buf) = timestamp; GST_BUFFER_DURATION (buf) = 256 * GST_SECOND / a52dec->sample_rate; result = GST_FLOW_OK; if ((buf = gst_audio_buffer_clip (buf, &a52dec->segment, a52dec->sample_rate, (SAMPLE_WIDTH / 8) * chans))) { /* set discont when needed */ if (a52dec->discont) { GST_LOG_OBJECT (a52dec, "marking DISCONT"); GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); a52dec->discont = FALSE; } if (a52dec->segment.rate > 0.0) { GST_DEBUG_OBJECT (a52dec, "Pushing buffer with ts %" GST_TIME_FORMAT " duration %" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_TIME_ARGS (GST_BUFFER_DURATION (buf))); result = gst_pad_push (srcpad, buf); } else { /* reverse playback, queue frame till later when we get a discont. */ GST_DEBUG_OBJECT (a52dec, "queued frame"); a52dec->queued = g_list_prepend (a52dec->queued, buf); } } return result; }
static GstBuffer * gst_audio_aggregator_do_clip (GstAggregator * agg, GstAggregatorPad * bpad, GstBuffer * buffer) { GstAudioAggregatorPad *pad = GST_AUDIO_AGGREGATOR_PAD (bpad); gint rate, bpf; rate = GST_AUDIO_INFO_RATE (&pad->info); bpf = GST_AUDIO_INFO_BPF (&pad->info); GST_OBJECT_LOCK (bpad); buffer = gst_audio_buffer_clip (buffer, &bpad->segment, rate, bpf); GST_OBJECT_UNLOCK (bpad); return buffer; }
static GstFlowReturn gst_wavpack_dec_chain (GstPad * pad, GstBuffer * buf) { GstWavpackDec *dec; GstBuffer *outbuf; GstFlowReturn ret = GST_FLOW_OK; WavpackHeader wph; int32_t decoded, unpacked_size; gboolean format_changed; dec = GST_WAVPACK_DEC (GST_PAD_PARENT (pad)); /* check input, we only accept framed input with complete chunks */ if (GST_BUFFER_SIZE (buf) < sizeof (WavpackHeader)) goto input_not_framed; if (!gst_wavpack_read_header (&wph, GST_BUFFER_DATA (buf))) goto invalid_header; if (GST_BUFFER_SIZE (buf) < wph.ckSize + 4 * 1 + 4) goto input_not_framed; if (!(wph.flags & INITIAL_BLOCK)) goto input_not_framed; dec->wv_id.buffer = GST_BUFFER_DATA (buf); dec->wv_id.length = GST_BUFFER_SIZE (buf); dec->wv_id.position = 0; /* create a new wavpack context if there is none yet but if there * was already one (i.e. caps were set on the srcpad) check whether * the new one has the same caps */ if (!dec->context) { gchar error_msg[80]; dec->context = WavpackOpenFileInputEx (dec->stream_reader, &dec->wv_id, NULL, error_msg, OPEN_STREAMING, 0); if (!dec->context) { GST_WARNING ("Couldn't decode buffer: %s", error_msg); dec->error_count++; if (dec->error_count <= WAVPACK_DEC_MAX_ERRORS) { goto out; /* just return OK for now */ } else { goto decode_error; } } } g_assert (dec->context != NULL); dec->error_count = 0; format_changed = (dec->sample_rate != WavpackGetSampleRate (dec->context)) || (dec->channels != WavpackGetNumChannels (dec->context)) || (dec->depth != WavpackGetBitsPerSample (dec->context)) || #ifdef WAVPACK_OLD_API (dec->channel_mask != dec->context->config.channel_mask); #else (dec->channel_mask != WavpackGetChannelMask (dec->context)); #endif if (!GST_PAD_CAPS (dec->srcpad) || format_changed) { GstCaps *caps; gint channel_mask; dec->sample_rate = WavpackGetSampleRate (dec->context); dec->channels = WavpackGetNumChannels (dec->context); dec->depth = WavpackGetBitsPerSample (dec->context); caps = gst_caps_new_simple ("audio/x-raw-int", "rate", G_TYPE_INT, dec->sample_rate, "channels", G_TYPE_INT, dec->channels, "depth", G_TYPE_INT, dec->depth, "width", G_TYPE_INT, 32, "endianness", G_TYPE_INT, G_BYTE_ORDER, "signed", G_TYPE_BOOLEAN, TRUE, NULL); #ifdef WAVPACK_OLD_API channel_mask = dec->context->config.channel_mask; #else channel_mask = WavpackGetChannelMask (dec->context); #endif if (channel_mask == 0) channel_mask = gst_wavpack_get_default_channel_mask (dec->channels); dec->channel_mask = channel_mask; /* Only set the channel layout for more than two channels * otherwise things break unfortunately */ if (channel_mask != 0 && dec->channels > 2) if (!gst_wavpack_set_channel_layout (caps, channel_mask)) GST_WARNING_OBJECT (dec, "Failed to set channel layout"); GST_DEBUG_OBJECT (dec, "setting caps %" GST_PTR_FORMAT, caps); /* should always succeed */ gst_pad_set_caps (dec->srcpad, caps); gst_caps_unref (caps); /* send GST_TAG_AUDIO_CODEC and GST_TAG_BITRATE tags before something * is decoded or after the format has changed */ gst_wavpack_dec_post_tags (dec); } /* alloc output buffer */ unpacked_size = 4 * wph.block_samples * dec->channels; ret = gst_pad_alloc_buffer (dec->srcpad, GST_BUFFER_OFFSET (buf), unpacked_size, GST_PAD_CAPS (dec->srcpad), &outbuf); if (ret != GST_FLOW_OK) goto out; gst_buffer_copy_metadata (outbuf, buf, GST_BUFFER_COPY_TIMESTAMPS); /* If we got a DISCONT buffer forward the flag. Nothing else * has to be done as libwavpack doesn't store state between * Wavpack blocks */ if (GST_BUFFER_IS_DISCONT (buf) || dec->next_block_index != wph.block_index) GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); dec->next_block_index = wph.block_index + wph.block_samples; /* decode */ decoded = WavpackUnpackSamples (dec->context, (int32_t *) GST_BUFFER_DATA (outbuf), wph.block_samples); if (decoded != wph.block_samples) goto decode_error; if ((outbuf = gst_audio_buffer_clip (outbuf, &dec->segment, dec->sample_rate, 4 * dec->channels))) { GST_LOG_OBJECT (dec, "pushing buffer with time %" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf))); ret = gst_pad_push (dec->srcpad, outbuf); } out: if (G_UNLIKELY (ret != GST_FLOW_OK)) { GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (ret)); } gst_buffer_unref (buf); return ret; /* ERRORS */ input_not_framed: { GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Expected framed input")); gst_buffer_unref (buf); return GST_FLOW_ERROR; } invalid_header: { GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Invalid wavpack header")); gst_buffer_unref (buf); return GST_FLOW_ERROR; } decode_error: { const gchar *reason = "unknown"; if (dec->context) { #ifdef WAVPACK_OLD_API reason = dec->context->error_message; #else reason = WavpackGetErrorMessage (dec->context); #endif } else { reason = "couldn't create decoder context"; } GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Failed to decode wavpack stream: %s", reason)); gst_buffer_unref (outbuf); gst_buffer_unref (buf); return GST_FLOW_ERROR; } }
static GstFlowReturn gst_audio_segment_clip_clip_buffer (GstSegmentClip * base, GstBuffer * buffer, GstBuffer ** outbuf) { GstAudioSegmentClip *self = GST_AUDIO_SEGMENT_CLIP (base); GstSegment *segment = &base->segment; GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buffer); GstClockTime duration = GST_BUFFER_DURATION (buffer); guint64 offset = GST_BUFFER_OFFSET (buffer); guint64 offset_end = GST_BUFFER_OFFSET_END (buffer); guint size = gst_buffer_get_size (buffer); if (!self->rate || !self->framesize) { GST_ERROR_OBJECT (self, "Not negotiated yet"); gst_buffer_unref (buffer); return GST_FLOW_NOT_NEGOTIATED; } if (segment->format != GST_FORMAT_DEFAULT && segment->format != GST_FORMAT_TIME) { GST_DEBUG_OBJECT (self, "Unsupported segment format %s", gst_format_get_name (segment->format)); *outbuf = buffer; return GST_FLOW_OK; } if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) { GST_WARNING_OBJECT (self, "Buffer without valid timestamp"); *outbuf = buffer; return GST_FLOW_OK; } *outbuf = gst_audio_buffer_clip (buffer, segment, self->rate, self->framesize); if (!*outbuf) { GST_DEBUG_OBJECT (self, "Buffer outside the configured segment"); /* Now return unexpected if we're before/after the end */ if (segment->format == GST_FORMAT_TIME) { if (segment->rate >= 0) { if (segment->stop != -1 && timestamp >= segment->stop) return GST_FLOW_EOS; } else { if (!GST_CLOCK_TIME_IS_VALID (duration)) duration = gst_util_uint64_scale_int (size, GST_SECOND, self->framesize * self->rate); if (segment->start != -1 && timestamp + duration <= segment->start) return GST_FLOW_EOS; } } else { if (segment->rate >= 0) { if (segment->stop != -1 && offset != -1 && offset >= segment->stop) return GST_FLOW_EOS; } else if (offset != -1 || offset_end != -1) { if (offset_end == -1) offset_end = offset + size / self->framesize; if (segment->start != -1 && offset_end <= segment->start) return GST_FLOW_EOS; } } } return GST_FLOW_OK; }
static GstFlowReturn gst_timecodewait_asink_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) { GstClockTime timestamp; GstTimeCodeWait *self = GST_TIMECODEWAIT (parent); GstClockTime current_running_time; GstClockTime video_running_time = GST_CLOCK_TIME_NONE; GstClockTime duration; GstClockTime running_time_at_end = GST_CLOCK_TIME_NONE; gint asign, vsign = 1, esign = 1; timestamp = GST_BUFFER_TIMESTAMP (inbuf); if (timestamp == GST_CLOCK_TIME_NONE) { gst_buffer_unref (inbuf); return GST_FLOW_ERROR; } g_mutex_lock (&self->mutex); self->asegment.position = timestamp; asign = gst_segment_to_running_time_full (&self->asegment, GST_FORMAT_TIME, self->asegment.position, ¤t_running_time); if (asign == 0) { g_mutex_unlock (&self->mutex); gst_buffer_unref (inbuf); GST_ERROR_OBJECT (self, "Could not get current running time"); return GST_FLOW_ERROR; } if (self->vsegment.format == GST_FORMAT_TIME) { vsign = gst_segment_to_running_time_full (&self->vsegment, GST_FORMAT_TIME, self->vsegment.position, &video_running_time); if (vsign == 0) { video_running_time = GST_CLOCK_TIME_NONE; } } while (!(self->video_eos_flag || self->audio_flush_flag || self->shutdown_flag) && (video_running_time == GST_CLOCK_TIME_NONE || gst_timecodewait_compare_guint64_with_signs (asign, current_running_time, vsign, video_running_time) == 1) && self->running_time_of_timecode == GST_CLOCK_TIME_NONE) { g_cond_wait (&self->cond, &self->mutex); vsign = gst_segment_to_running_time_full (&self->vsegment, GST_FORMAT_TIME, self->vsegment.position, &video_running_time); if (vsign == 0) { video_running_time = GST_CLOCK_TIME_NONE; } } if (self->audio_flush_flag || self->shutdown_flag) { GST_DEBUG_OBJECT (self, "Shutting down, ignoring frame"); gst_buffer_unref (inbuf); return GST_FLOW_FLUSHING; } duration = GST_BUFFER_DURATION (inbuf); if (duration != GST_CLOCK_TIME_NONE) { esign = gst_segment_to_running_time_full (&self->asegment, GST_FORMAT_TIME, self->asegment.position + duration, &running_time_at_end); if (esign == 0) { g_mutex_unlock (&self->mutex); GST_ERROR_OBJECT (self, "Could not get running time at end"); gst_buffer_unref (inbuf); return GST_FLOW_ERROR; } } if (self->running_time_of_timecode == GST_CLOCK_TIME_NONE || gst_timecodewait_compare_guint64_with_signs (esign, running_time_at_end, 1, self->running_time_of_timecode) == -1) { GST_DEBUG_OBJECT (self, "Dropped an audio buf at %" GST_TIME_FORMAT " with timecode %" GST_TIME_FORMAT " video timecode %" GST_TIME_FORMAT, GST_TIME_ARGS (current_running_time), GST_TIME_ARGS (self->running_time_of_timecode), GST_TIME_ARGS (video_running_time)); gst_buffer_unref (inbuf); inbuf = NULL; } else if (current_running_time < self->running_time_of_timecode && running_time_at_end > self->running_time_of_timecode) { GstSegment asegment2 = self->asegment; gst_segment_set_running_time (&asegment2, GST_FORMAT_TIME, self->running_time_of_timecode); inbuf = gst_audio_buffer_clip (inbuf, &asegment2, self->ainfo.rate, self->ainfo.bpf); } g_mutex_unlock (&self->mutex); if (inbuf) return gst_pad_push (self->asrcpad, inbuf); else return GST_FLOW_OK; }
void test_buffer_clipping_samples() { GstSegment s; GstBuffer *buf; GstBuffer *ret; guint8 *data; xmlfile = "test_buffer_clipping_samples"; std_log(LOG_FILENAME_LINE, "Test Started test_buffer_clipping_samples"); /* Clip start and end */ buf = gst_buffer_new (); data = (guint8 *) g_malloc (1000); GST_BUFFER_SIZE (buf) = 1000; GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = data; gst_segment_init (&s, GST_FORMAT_DEFAULT); gst_segment_set_newsegment (&s, FALSE, 1.0, GST_FORMAT_DEFAULT, 400, 800, 400); GST_BUFFER_TIMESTAMP (buf) = 2 * GST_SECOND; GST_BUFFER_DURATION (buf) = 10 * GST_SECOND; GST_BUFFER_OFFSET (buf) = 200; GST_BUFFER_OFFSET_END (buf) = 1200; ret = gst_audio_buffer_clip (buf, &s, 100, 1); fail_unless (ret != NULL); fail_unless (GST_BUFFER_TIMESTAMP (ret) == 4 * GST_SECOND); fail_unless (GST_BUFFER_DURATION (ret) == 4 * GST_SECOND); fail_unless (GST_BUFFER_OFFSET (ret) == 400); fail_unless (GST_BUFFER_OFFSET_END (ret) == 800); fail_unless (GST_BUFFER_DATA (ret) == data + 200); fail_unless (GST_BUFFER_SIZE (ret) == 400); gst_buffer_unref (ret); /* Clip only start */ buf = gst_buffer_new (); data = (guint8 *) g_malloc (1000); GST_BUFFER_SIZE (buf) = 1000; GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = data; gst_segment_init (&s, GST_FORMAT_DEFAULT); gst_segment_set_newsegment (&s, FALSE, 1.0, GST_FORMAT_DEFAULT, 400, 1200, 400); GST_BUFFER_TIMESTAMP (buf) = 2 * GST_SECOND; GST_BUFFER_DURATION (buf) = 10 * GST_SECOND; GST_BUFFER_OFFSET (buf) = 200; GST_BUFFER_OFFSET_END (buf) = 1200; ret = gst_audio_buffer_clip (buf, &s, 100, 1); fail_unless (ret != NULL); fail_unless (GST_BUFFER_TIMESTAMP (ret) == 4 * GST_SECOND); fail_unless (GST_BUFFER_DURATION (ret) == 8 * GST_SECOND); fail_unless (GST_BUFFER_OFFSET (ret) == 400); fail_unless (GST_BUFFER_OFFSET_END (ret) == 1200); fail_unless (GST_BUFFER_DATA (ret) == data + 200); fail_unless (GST_BUFFER_SIZE (ret) == 800); gst_buffer_unref (ret); /* Clip only stop */ buf = gst_buffer_new (); data = (guint8 *) g_malloc (1000); GST_BUFFER_SIZE (buf) = 1000; GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = data; gst_segment_init (&s, GST_FORMAT_DEFAULT); gst_segment_set_newsegment (&s, FALSE, 1.0, GST_FORMAT_DEFAULT, 200, 1000, 200); GST_BUFFER_TIMESTAMP (buf) = 2 * GST_SECOND; GST_BUFFER_DURATION (buf) = 10 * GST_SECOND; GST_BUFFER_OFFSET (buf) = 200; GST_BUFFER_OFFSET_END (buf) = 1200; ret = gst_audio_buffer_clip (buf, &s, 100, 1); fail_unless (ret != NULL); fail_unless (GST_BUFFER_TIMESTAMP (ret) == 2 * GST_SECOND); fail_unless (GST_BUFFER_DURATION (ret) == 8 * GST_SECOND); fail_unless (GST_BUFFER_OFFSET (ret) == 200); fail_unless (GST_BUFFER_OFFSET_END (ret) == 1000); fail_unless (GST_BUFFER_DATA (ret) == data); fail_unless (GST_BUFFER_SIZE (ret) == 800); gst_buffer_unref (ret); /* Buffer outside segment */ buf = gst_buffer_new (); data = (guint8 *) g_malloc (1000); GST_BUFFER_SIZE (buf) = 1000; GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = data; gst_segment_init (&s, GST_FORMAT_DEFAULT); gst_segment_set_newsegment (&s, FALSE, 1.0, GST_FORMAT_DEFAULT, 1200, 2000, 1200); GST_BUFFER_TIMESTAMP (buf) = 2 * GST_SECOND; GST_BUFFER_DURATION (buf) = 10 * GST_SECOND; GST_BUFFER_OFFSET (buf) = 200; GST_BUFFER_OFFSET_END (buf) = 1200; ret = gst_audio_buffer_clip (buf, &s, 100, 1); fail_unless (ret == NULL); /* Clip start and end but don't touch duration and offset_end */ buf = gst_buffer_new (); data = (guint8 *) g_malloc (1000); GST_BUFFER_SIZE (buf) = 1000; GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = data; gst_segment_init (&s, GST_FORMAT_DEFAULT); gst_segment_set_newsegment (&s, FALSE, 1.0, GST_FORMAT_DEFAULT, 400, 800, 400); GST_BUFFER_TIMESTAMP (buf) = 2 * GST_SECOND; GST_BUFFER_DURATION (buf) = GST_CLOCK_TIME_NONE; GST_BUFFER_OFFSET (buf) = 200; GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE; ret = gst_audio_buffer_clip (buf, &s, 100, 1); fail_unless (ret != NULL); fail_unless (GST_BUFFER_TIMESTAMP (ret) == 4 * GST_SECOND); fail_unless (GST_BUFFER_DURATION (ret) == GST_CLOCK_TIME_NONE); fail_unless (GST_BUFFER_OFFSET (ret) == 400); fail_unless (GST_BUFFER_OFFSET_END (ret) == GST_BUFFER_OFFSET_NONE); fail_unless (GST_BUFFER_DATA (ret) == data + 200); fail_unless (GST_BUFFER_SIZE (ret) == 400); gst_buffer_unref (ret); /* If the buffer has no offset it should assert() * FIXME: check if return value is the same as the input buffer. * probably can't be done because the assert() does a SIGABRT. */ buf = gst_buffer_new (); data = (guint8 *) g_malloc (1000); GST_BUFFER_SIZE (buf) = 1000; GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = data; gst_segment_init (&s, GST_FORMAT_DEFAULT); gst_segment_set_newsegment (&s, FALSE, 1.0, GST_FORMAT_DEFAULT, 0, 10, 0); GST_BUFFER_TIMESTAMP (buf) = 0 * GST_SECOND; GST_BUFFER_DURATION (buf) = GST_CLOCK_TIME_NONE; GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE; GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE; ASSERT_CRITICAL (ret = gst_audio_buffer_clip (buf, &s, 100, 1)); gst_buffer_unref (buf); std_log(LOG_FILENAME_LINE, "Test Successful"); create_xml(0); }
static GstFlowReturn gst_live_live_adder_chain (GstPad * pad, GstBuffer * buffer) { GstLiveAdder *adder = GST_LIVE_ADDER (gst_pad_get_parent_element (pad)); GstLiveAdderPadPrivate *padprivate = NULL; GstFlowReturn ret = GST_FLOW_OK; GList *item = NULL; GstClockTime skip = 0; gint64 drift = 0; /* Positive if new buffer after old buffer */ GST_OBJECT_LOCK (adder); ret = adder->srcresult; GST_DEBUG ("Incoming buffer time:%" GST_TIME_FORMAT " duration:%" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), GST_TIME_ARGS (GST_BUFFER_DURATION (buffer))); if (ret != GST_FLOW_OK) { GST_DEBUG_OBJECT (adder, "Passing non-ok result from src: %s", gst_flow_get_name (ret)); gst_buffer_unref (buffer); goto out; } padprivate = gst_pad_get_element_private (pad); if (!padprivate) { ret = GST_FLOW_NOT_LINKED; gst_buffer_unref (buffer); goto out; } if (padprivate->eos) { GST_DEBUG_OBJECT (adder, "Received buffer after EOS"); ret = GST_FLOW_UNEXPECTED; gst_buffer_unref (buffer); goto out; } if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) goto invalid_timestamp; if (padprivate->segment.format == GST_FORMAT_UNDEFINED) { GST_WARNING_OBJECT (adder, "No new-segment received," " initializing segment with time 0..-1"); gst_segment_init (&padprivate->segment, GST_FORMAT_TIME); gst_segment_set_newsegment (&padprivate->segment, FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0); } if (padprivate->segment.format != GST_FORMAT_TIME) goto invalid_segment; buffer = gst_buffer_make_metadata_writable (buffer); drift = GST_BUFFER_TIMESTAMP (buffer) - padprivate->expected_timestamp; /* Just see if we receive invalid timestamp/durations */ if (GST_CLOCK_TIME_IS_VALID (padprivate->expected_timestamp) && !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT) && (drift != 0)) { GST_LOG_OBJECT (adder, "Timestamp discontinuity without the DISCONT flag set" " (expected %" GST_TIME_FORMAT ", got %" GST_TIME_FORMAT " drift:%" G_GINT64_FORMAT "ms)", GST_TIME_ARGS (padprivate->expected_timestamp), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), drift / GST_MSECOND); /* We accept drifts of 10ms */ if (ABS (drift) < (10 * GST_MSECOND)) { GST_DEBUG ("Correcting minor drift"); GST_BUFFER_TIMESTAMP (buffer) = padprivate->expected_timestamp; } } /* If there is no duration, lets set one */ if (!GST_BUFFER_DURATION_IS_VALID (buffer)) { GST_BUFFER_DURATION (buffer) = gst_audio_duration_from_pad_buffer (pad, buffer); padprivate->expected_timestamp = GST_CLOCK_TIME_NONE; } else { padprivate->expected_timestamp = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer); } /* * Lets clip the buffer to the segment (so we don't have to worry about * cliping afterwards). * This should also guarantee us that we'll have valid timestamps and * durations afterwards */ buffer = gst_audio_buffer_clip (buffer, &padprivate->segment, adder->rate, adder->bps); /* buffer can be NULL if it's completely outside of the segment */ if (!buffer) { GST_DEBUG ("Buffer completely outside of configured segment, dropping it"); goto out; } /* * Make sure all incoming buffers share the same timestamping */ GST_BUFFER_TIMESTAMP (buffer) = gst_segment_to_running_time (&padprivate->segment, padprivate->segment.format, GST_BUFFER_TIMESTAMP (buffer)); if (GST_CLOCK_TIME_IS_VALID (adder->next_timestamp) && GST_BUFFER_TIMESTAMP (buffer) < adder->next_timestamp) { if (GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) < adder->next_timestamp) { GST_DEBUG_OBJECT (adder, "Buffer is late, dropping (ts: %" GST_TIME_FORMAT " duration: %" GST_TIME_FORMAT ")", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), GST_TIME_ARGS (GST_BUFFER_DURATION (buffer))); gst_buffer_unref (buffer); goto out; } else { skip = adder->next_timestamp - GST_BUFFER_TIMESTAMP (buffer); GST_DEBUG_OBJECT (adder, "Buffer is partially late, skipping %" GST_TIME_FORMAT, GST_TIME_ARGS (skip)); } } /* If our new buffer's head is higher than the queue's head, lets wake up, * we may not have to wait for as long */ if (adder->clock_id && g_queue_peek_head (adder->buffers) != NULL && GST_BUFFER_TIMESTAMP (buffer) + skip < GST_BUFFER_TIMESTAMP (g_queue_peek_head (adder->buffers))) gst_clock_id_unschedule (adder->clock_id); for (item = g_queue_peek_head_link (adder->buffers); item; item = g_list_next (item)) { GstBuffer *oldbuffer = item->data; GstClockTime old_skip = 0; GstClockTime mix_duration = 0; GstClockTime mix_start = 0; GstClockTime mix_end = 0; /* We haven't reached our place yet */ if (GST_BUFFER_TIMESTAMP (buffer) + skip >= GST_BUFFER_TIMESTAMP (oldbuffer) + GST_BUFFER_DURATION (oldbuffer)) continue; /* We're past our place, lets insert ouselves here */ if (GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) <= GST_BUFFER_TIMESTAMP (oldbuffer)) break; /* if we reach this spot, we have overlap, so we must mix */ /* First make a subbuffer with the non-overlapping part */ if (GST_BUFFER_TIMESTAMP (buffer) + skip < GST_BUFFER_TIMESTAMP (oldbuffer)) { GstBuffer *subbuffer = NULL; GstClockTime subbuffer_duration = GST_BUFFER_TIMESTAMP (oldbuffer) - (GST_BUFFER_TIMESTAMP (buffer) + skip); subbuffer = gst_buffer_create_sub (buffer, gst_live_adder_length_from_duration (adder, skip), gst_live_adder_length_from_duration (adder, subbuffer_duration)); GST_BUFFER_TIMESTAMP (subbuffer) = GST_BUFFER_TIMESTAMP (buffer) + skip; GST_BUFFER_DURATION (subbuffer) = subbuffer_duration; skip += subbuffer_duration; g_queue_insert_before (adder->buffers, item, subbuffer); } /* Now we are on the overlapping part */ oldbuffer = gst_buffer_make_writable (oldbuffer); item->data = oldbuffer; old_skip = GST_BUFFER_TIMESTAMP (buffer) + skip - GST_BUFFER_TIMESTAMP (oldbuffer); mix_start = GST_BUFFER_TIMESTAMP (oldbuffer) + old_skip; if (GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) < GST_BUFFER_TIMESTAMP (oldbuffer) + GST_BUFFER_DURATION (oldbuffer)) mix_end = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer); else mix_end = GST_BUFFER_TIMESTAMP (oldbuffer) + GST_BUFFER_DURATION (oldbuffer); mix_duration = mix_end - mix_start; adder->func (GST_BUFFER_DATA (oldbuffer) + gst_live_adder_length_from_duration (adder, old_skip), GST_BUFFER_DATA (buffer) + gst_live_adder_length_from_duration (adder, skip), gst_live_adder_length_from_duration (adder, mix_duration)); skip += mix_duration; } g_cond_broadcast (adder->not_empty_cond); if (skip == GST_BUFFER_DURATION (buffer)) { gst_buffer_unref (buffer); } else { if (skip) { GstClockTime subbuffer_duration = GST_BUFFER_DURATION (buffer) - skip; GstClockTime subbuffer_ts = GST_BUFFER_TIMESTAMP (buffer) + skip; GstBuffer *new_buffer = gst_buffer_create_sub (buffer, gst_live_adder_length_from_duration (adder, skip), gst_live_adder_length_from_duration (adder, subbuffer_duration)); gst_buffer_unref (buffer); buffer = new_buffer; GST_BUFFER_TIMESTAMP (buffer) = subbuffer_ts; GST_BUFFER_DURATION (buffer) = subbuffer_duration; } if (item) g_queue_insert_before (adder->buffers, item, buffer); else g_queue_push_tail (adder->buffers, buffer); } out: GST_OBJECT_UNLOCK (adder); gst_object_unref (adder); return ret; invalid_timestamp: GST_OBJECT_UNLOCK (adder); gst_buffer_unref (buffer); GST_ELEMENT_ERROR (adder, STREAM, FAILED, ("Buffer without a valid timestamp received"), ("Invalid timestamp received on buffer")); return GST_FLOW_ERROR; invalid_segment: { const gchar *format = gst_format_get_name (padprivate->segment.format); GST_OBJECT_UNLOCK (adder); gst_buffer_unref (buffer); GST_ELEMENT_ERROR (adder, STREAM, FAILED, ("This element only supports TIME segments, received other type"), ("Received a segment of type %s, only support time segment", format)); return GST_FLOW_ERROR; } }