static GstFlowReturn vorbis_parse_queue_buffer (GstVorbisParse * parse, GstBuffer * buf) { GstFlowReturn ret = GST_FLOW_OK; long blocksize; ogg_packet packet; buf = gst_buffer_make_metadata_writable (buf); packet.packet = GST_BUFFER_DATA (buf); packet.bytes = GST_BUFFER_SIZE (buf); packet.granulepos = GST_BUFFER_OFFSET_END (buf); packet.packetno = parse->packetno + parse->buffer_queue->length; packet.e_o_s = 0; blocksize = vorbis_packet_blocksize (&parse->vi, &packet); /* temporarily store the sample count in OFFSET -- we overwrite this later */ if (parse->prev_blocksize < 0) GST_BUFFER_OFFSET (buf) = 0; else GST_BUFFER_OFFSET (buf) = (blocksize + parse->prev_blocksize) / 4; parse->prev_blocksize = blocksize; g_queue_push_tail (parse->buffer_queue, buf); if (GST_BUFFER_OFFSET_END_IS_VALID (buf)) ret = vorbis_parse_drain_queue (parse, GST_BUFFER_OFFSET_END (buf)); return ret; }
GstFlowReturn got_data(GstPad *pad, GstObject *parent, GstBuffer *buf) { GstTfImpl *This = gst_pad_get_element_private(pad); IMediaSample *sample = (IMediaSample *) gst_mini_object_get_qdata(GST_MINI_OBJECT(buf), g_quark_from_static_string(media_quark_string)); REFERENCE_TIME tStart, tStop; HRESULT hr; TRACE("%p, %p\n", pad, buf); if(!sample){ GstMapInfo info; BYTE *ptr; gst_buffer_map(buf, &info, GST_MAP_READ); hr = BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin*)This->tf.ppPins[1], &sample, NULL, NULL, 0); if (FAILED(hr)) { ERR("Could not get output buffer: %08x\n", hr); return GST_FLOW_FLUSHING; } IMediaSample_SetActualDataLength(sample, info.size); IMediaSample_GetPointer(sample, &ptr); memcpy(ptr, info.data, info.size); gst_buffer_unmap(buf, &info); } if (GST_BUFFER_PTS_IS_VALID(buf) && GST_BUFFER_DURATION_IS_VALID(buf)) { tStart = buf->pts / 100; tStop = tStart + buf->duration / 100; IMediaSample_SetTime(sample, &tStart, &tStop); } else IMediaSample_SetTime(sample, NULL, NULL); if (GST_BUFFER_OFFSET_IS_VALID(buf) && GST_BUFFER_OFFSET_END_IS_VALID(buf)) { tStart = buf->offset / 100; tStop = buf->offset_end / 100; IMediaSample_SetMediaTime(sample, &tStart, &tStop); } else IMediaSample_SetMediaTime(sample, NULL, NULL); IMediaSample_SetDiscontinuity(sample, GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_DISCONT)); IMediaSample_SetPreroll(sample, GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_LIVE)); IMediaSample_SetSyncPoint(sample, !GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT)); IMediaSample_SetActualDataLength(sample, gst_buffer_get_size(buf)); hr = BaseOutputPinImpl_Deliver((BaseOutputPin*)This->tf.ppPins[1], sample); IMediaSample_Release(sample); gst_buffer_unref(buf); if (FAILED(hr)) return GST_FLOW_FLUSHING; return GST_FLOW_OK; }
/** * gst_buffer_span: * @buf1: the first source #GstBuffer to merge. * @offset: the offset in the first buffer from where the new * buffer should start. * @buf2: the second source #GstBuffer to merge. * @len: the total length of the new buffer. * * Creates a new buffer that consists of part of buf1 and buf2. * Logically, buf1 and buf2 are concatenated into a single larger * buffer, and a new buffer is created at the given offset inside * this space, with a given length. * * If the two source buffers are children of the same larger buffer, * and are contiguous, the new buffer will be a child of the shared * parent, and thus no copying is necessary. you can use * gst_buffer_is_span_fast() to determine if a memcpy will be needed. * * MT safe. * Returns: the new #GstBuffer that spans the two source buffers. * Returns NULL if the arguments are invalid. */ GstBuffer * gst_buffer_span (GstBuffer * buf1, guint32 offset, GstBuffer * buf2, guint32 len) { GstBuffer *newbuf; g_return_val_if_fail (buf1 != NULL && buf2 != NULL, NULL); g_return_val_if_fail (buf1->mini_object.refcount > 0, NULL); g_return_val_if_fail (buf2->mini_object.refcount > 0, NULL); g_return_val_if_fail (len > 0, NULL); g_return_val_if_fail (len <= buf1->size + buf2->size - offset, NULL); /* if the two buffers have the same parent and are adjacent */ if (gst_buffer_is_span_fast (buf1, buf2)) { GstBuffer *parent = GST_SUBBUFFER_CAST (buf1)->parent; /* we simply create a subbuffer of the common parent */ newbuf = gst_buffer_create_sub (parent, buf1->data - parent->data + offset, len); } else { GST_CAT_DEBUG (GST_CAT_BUFFER, "slow path taken while spanning buffers %p and %p", buf1, buf2); /* otherwise we simply have to brute-force copy the buffers */ newbuf = gst_buffer_new_and_alloc (len); /* copy the first buffer's data across */ memcpy (newbuf->data, buf1->data + offset, buf1->size - offset); /* copy the second buffer's data across */ memcpy (newbuf->data + (buf1->size - offset), buf2->data, len - (buf1->size - offset)); } /* if the offset is 0, the new buffer has the same timestamp as buf1 */ if (offset == 0) { GST_BUFFER_OFFSET (newbuf) = GST_BUFFER_OFFSET (buf1); GST_BUFFER_TIMESTAMP (newbuf) = GST_BUFFER_TIMESTAMP (buf1); /* if we completely merged the two buffers (appended), we can * calculate the duration too. Also make sure we's not messing with * invalid DURATIONS */ if (buf1->size + buf2->size == len) { if (GST_BUFFER_DURATION_IS_VALID (buf1) && GST_BUFFER_DURATION_IS_VALID (buf2)) { /* add duration */ GST_BUFFER_DURATION (newbuf) = GST_BUFFER_DURATION (buf1) + GST_BUFFER_DURATION (buf2); } if (GST_BUFFER_OFFSET_END_IS_VALID (buf2)) { /* add offset_end */ GST_BUFFER_OFFSET_END (newbuf) = GST_BUFFER_OFFSET_END (buf2); } } } return newbuf; }
static GstFlowReturn theora_parse_drain_queue_prematurely (GstTheoraParse * parse) { GstFlowReturn ret = GST_FLOW_OK; /* got an EOS event, make sure to push out any buffers that were in the queue * -- won't normally be the case, but this catches the * didn't-get-a-granulepos-on-the-last-packet case. Assuming a continuous * stream. */ GST_DEBUG_OBJECT (parse, "got EOS, draining queue"); /* if we get an eos before pushing the streamheaders, drain our events before * eos */ theora_parse_drain_event_queue (parse); while (!g_queue_is_empty (parse->buffer_queue)) { GstBuffer *buf; buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue)); parse->prev_frame++; if (is_keyframe (buf)) /* we have a keyframe */ parse->prev_keyframe = parse->prev_frame; else GST_BUFFER_FLAGS (buf) |= GST_BUFFER_FLAG_DELTA_UNIT; if (parse->prev_keyframe < 0) { if (GST_BUFFER_OFFSET_END_IS_VALID (buf)) { parse_granulepos (parse, GST_BUFFER_OFFSET_END (buf), &parse->prev_keyframe, NULL); } else { /* No previous keyframe known; can't extract one from this frame. That * means we can't do any valid output for this frame, just continue to * the next frame. */ gst_buffer_unref (buf); continue; } } ret = theora_parse_push_buffer (parse, buf, parse->prev_keyframe, parse->prev_frame); if (ret != GST_FLOW_OK) goto done; } done: return ret; }
static GstFlowReturn theora_parse_queue_buffer (GstTheoraParse * parse, GstBuffer * buf) { GstFlowReturn ret = GST_FLOW_OK; buf = gst_buffer_make_writable (buf); g_queue_push_tail (parse->buffer_queue, buf); if (GST_BUFFER_OFFSET_END_IS_VALID (buf)) { if (parse->prev_keyframe < 0) { parse_granulepos (parse, GST_BUFFER_OFFSET_END (buf), &parse->prev_keyframe, NULL); } ret = theora_parse_drain_queue (parse, GST_BUFFER_OFFSET_END (buf)); } return ret; }
static GstFlowReturn vorbis_parse_queue_buffer (GstVorbisParse * parse, GstBuffer * buf) { GstFlowReturn ret = GST_FLOW_OK; long blocksize; ogg_packet packet; GstMapInfo map; buf = gst_buffer_make_writable (buf); gst_buffer_map (buf, &map, GST_MAP_READ); packet.packet = map.data; packet.bytes = map.size; GST_DEBUG ("%p, %" G_GSIZE_FORMAT, map.data, map.size); packet.granulepos = GST_BUFFER_OFFSET_END (buf); packet.packetno = parse->packetno + parse->buffer_queue->length; packet.e_o_s = 0; blocksize = vorbis_packet_blocksize (&parse->vi, &packet); gst_buffer_unmap (buf, &map); /* temporarily store the sample count in OFFSET -- we overwrite this later */ if (parse->prev_blocksize < 0) GST_BUFFER_OFFSET (buf) = 0; else GST_BUFFER_OFFSET (buf) = (blocksize + parse->prev_blocksize) / 4; parse->prev_blocksize = blocksize; g_queue_push_tail (parse->buffer_queue, buf); if (GST_BUFFER_OFFSET_END_IS_VALID (buf)) ret = vorbis_parse_drain_queue (parse, GST_BUFFER_OFFSET_END (buf)); return ret; }
static GstFlowReturn got_data(GstPad *pad, GstBuffer *buf) { GstTfImpl *This = gst_pad_get_element_private(pad); IMediaSample *sample = GST_APP_BUFFER(buf)->priv; REFERENCE_TIME tStart, tStop; HRESULT hr; if (GST_BUFFER_TIMESTAMP_IS_VALID(buf) && GST_BUFFER_DURATION_IS_VALID(buf)) { tStart = buf->timestamp / 100; tStop = tStart + buf->duration / 100; IMediaSample_SetTime(sample, &tStart, &tStop); } else IMediaSample_SetTime(sample, NULL, NULL); if (GST_BUFFER_OFFSET_IS_VALID(buf) && GST_BUFFER_OFFSET_END_IS_VALID(buf)) { tStart = buf->offset / 100; tStop = buf->offset_end / 100; IMediaSample_SetMediaTime(sample, &tStart, &tStop); } else IMediaSample_SetMediaTime(sample, NULL, NULL); IMediaSample_SetDiscontinuity(sample, GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_DISCONT)); IMediaSample_SetPreroll(sample, GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_PREROLL)); IMediaSample_SetSyncPoint(sample, !GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT)); IMediaSample_SetActualDataLength(sample, GST_BUFFER_SIZE(buf)); hr = BaseOutputPinImpl_Deliver((BaseOutputPin*)This->tf.ppPins[1], sample); gst_buffer_unref(buf); if (FAILED(hr)) return GST_FLOW_WRONG_STATE; if (hr != S_OK) return GST_FLOW_RESEND; return GST_FLOW_OK; }
static void do_perfect_stream_test (guint rate, guint width, gdouble drop_probability, gdouble inject_probability) { GstElement *pipe, *src, *conv, *filter, *injector, *audiorate, *sink; GstMessage *msg; GstCaps *caps; GstPad *srcpad; GList *l, *bufs = NULL; GstClockTime next_time = GST_CLOCK_TIME_NONE; guint64 next_offset = GST_BUFFER_OFFSET_NONE; caps = gst_caps_new_simple ("audio/x-raw-int", "rate", G_TYPE_INT, rate, "width", G_TYPE_INT, width, NULL); GST_INFO ("-------- drop=%.0f%% caps = %" GST_PTR_FORMAT " ---------- ", drop_probability * 100.0, caps); g_assert (drop_probability >= 0.0 && drop_probability <= 1.0); g_assert (inject_probability >= 0.0 && inject_probability <= 1.0); g_assert (width > 0 && (width % 8) == 0); pipe = gst_pipeline_new ("pipeline"); fail_unless (pipe != NULL); src = gst_element_factory_make ("audiotestsrc", "audiotestsrc"); fail_unless (src != NULL); g_object_set (src, "num-buffers", 100, NULL); conv = gst_element_factory_make ("audioconvert", "audioconvert"); fail_unless (conv != NULL); filter = gst_element_factory_make ("capsfilter", "capsfilter"); fail_unless (filter != NULL); g_object_set (filter, "caps", caps, NULL); injector_inject_probability = inject_probability; injector = GST_ELEMENT (g_object_new (test_injector_get_type (), NULL)); srcpad = gst_element_get_pad (injector, "src"); fail_unless (srcpad != NULL); gst_pad_add_buffer_probe (srcpad, G_CALLBACK (probe_cb), &drop_probability); gst_object_unref (srcpad); audiorate = gst_element_factory_make ("audiorate", "audiorate"); fail_unless (audiorate != NULL); sink = gst_element_factory_make ("fakesink", "fakesink"); fail_unless (sink != NULL); g_object_set (sink, "signal-handoffs", TRUE, NULL); g_signal_connect (sink, "handoff", G_CALLBACK (got_buf), &bufs); gst_bin_add_many (GST_BIN (pipe), src, conv, filter, injector, audiorate, sink, NULL); gst_element_link_many (src, conv, filter, injector, audiorate, sink, NULL); fail_unless_equals_int (gst_element_set_state (pipe, GST_STATE_PLAYING), GST_STATE_CHANGE_ASYNC); fail_unless_equals_int (gst_element_get_state (pipe, NULL, NULL, -1), GST_STATE_CHANGE_SUCCESS); msg = gst_bus_poll (GST_ELEMENT_BUS (pipe), GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1); fail_unless_equals_string (GST_MESSAGE_TYPE_NAME (msg), "eos"); for (l = bufs; l != NULL; l = l->next) { GstBuffer *buf = GST_BUFFER (l->data); guint num_samples; fail_unless (GST_BUFFER_TIMESTAMP_IS_VALID (buf)); fail_unless (GST_BUFFER_DURATION_IS_VALID (buf)); fail_unless (GST_BUFFER_OFFSET_IS_VALID (buf)); fail_unless (GST_BUFFER_OFFSET_END_IS_VALID (buf)); GST_LOG ("buffer: ts=%" GST_TIME_FORMAT ", end_ts=%" GST_TIME_FORMAT " off=%" G_GINT64_FORMAT ", end_off=%" G_GINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf), GST_BUFFER_OFFSET_END (buf)); if (GST_CLOCK_TIME_IS_VALID (next_time)) { fail_unless_equals_uint64 (next_time, GST_BUFFER_TIMESTAMP (buf)); } if (next_offset != GST_BUFFER_OFFSET_NONE) { fail_unless_equals_uint64 (next_offset, GST_BUFFER_OFFSET (buf)); } /* check buffer size for sanity */ fail_unless_equals_int (GST_BUFFER_SIZE (buf) % (width / 8), 0); /* check there is actually as much data as there should be */ num_samples = GST_BUFFER_OFFSET_END (buf) - GST_BUFFER_OFFSET (buf); fail_unless_equals_int (GST_BUFFER_SIZE (buf), num_samples * (width / 8)); next_time = GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf); next_offset = GST_BUFFER_OFFSET_END (buf); } gst_message_unref (msg); gst_element_set_state (pipe, GST_STATE_NULL); gst_object_unref (pipe); g_list_foreach (bufs, (GFunc) gst_mini_object_unref, NULL); g_list_free (bufs); gst_caps_unref (caps); }
static GstFlowReturn test_injector_chain (GstPad * pad, GstBuffer * buf) { GstFlowReturn ret; GstPad *srcpad; srcpad = gst_element_get_pad (GST_ELEMENT (GST_PAD_PARENT (pad)), "src"); /* since we're increasing timestamp/offsets, push this one first */ GST_LOG (" passing buffer [t=%" GST_TIME_FORMAT "-%" GST_TIME_FORMAT "], offset=%" G_GINT64_FORMAT ", offset_end=%" G_GINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf), GST_BUFFER_OFFSET_END (buf)); gst_buffer_ref (buf); ret = gst_pad_push (srcpad, buf); if (g_random_double () < injector_inject_probability) { GstBuffer *ibuf; ibuf = gst_buffer_copy (buf); if (GST_BUFFER_OFFSET_IS_VALID (buf) && GST_BUFFER_OFFSET_END_IS_VALID (buf)) { guint64 delta; delta = GST_BUFFER_OFFSET_END (buf) - GST_BUFFER_OFFSET (buf); GST_BUFFER_OFFSET (ibuf) += delta / 4; GST_BUFFER_OFFSET_END (ibuf) += delta / 4; } else { GST_BUFFER_OFFSET (ibuf) = GST_BUFFER_OFFSET_NONE; GST_BUFFER_OFFSET_END (ibuf) = GST_BUFFER_OFFSET_NONE; } if (GST_BUFFER_TIMESTAMP_IS_VALID (buf) && GST_BUFFER_DURATION_IS_VALID (buf)) { GstClockTime delta; delta = GST_BUFFER_DURATION (buf); GST_BUFFER_TIMESTAMP (ibuf) += delta / 4; } else { GST_BUFFER_TIMESTAMP (ibuf) = GST_CLOCK_TIME_NONE; GST_BUFFER_DURATION (ibuf) = GST_CLOCK_TIME_NONE; } if (GST_BUFFER_TIMESTAMP_IS_VALID (ibuf) || GST_BUFFER_OFFSET_IS_VALID (ibuf)) { GST_LOG ("injecting buffer [t=%" GST_TIME_FORMAT "-%" GST_TIME_FORMAT "], offset=%" G_GINT64_FORMAT ", offset_end=%" G_GINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (ibuf)), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (ibuf) + GST_BUFFER_DURATION (ibuf)), GST_BUFFER_OFFSET (ibuf), GST_BUFFER_OFFSET_END (ibuf)); if (gst_pad_push (srcpad, ibuf) != GST_FLOW_OK) { /* ignore return value */ } } else { GST_WARNING ("couldn't inject buffer, no incoming timestamps or offsets"); gst_buffer_unref (ibuf); } } gst_buffer_unref (buf); return ret; }
EXPORT_C #endif GstBuffer * gst_audio_buffer_clip (GstBuffer * buffer, GstSegment * segment, gint rate, gint frame_size) { GstBuffer *ret; GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE; guint64 offset = GST_BUFFER_OFFSET_NONE, offset_end = GST_BUFFER_OFFSET_NONE; guint8 *data; guint size; gboolean change_duration = TRUE, change_offset = TRUE, change_offset_end = TRUE; g_return_val_if_fail (segment->format == GST_FORMAT_TIME || segment->format == GST_FORMAT_DEFAULT, buffer); g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) /* No timestamp - assume the buffer is completely in the segment */ return buffer; /* Get copies of the buffer metadata to change later. * Calculate the missing values for the calculations, * they won't be changed later though. */ data = GST_BUFFER_DATA (buffer); size = GST_BUFFER_SIZE (buffer); timestamp = GST_BUFFER_TIMESTAMP (buffer); if (GST_BUFFER_DURATION_IS_VALID (buffer)) { duration = GST_BUFFER_DURATION (buffer); } else { change_duration = FALSE; duration = gst_util_uint64_scale (size / frame_size, GST_SECOND, rate); } if (GST_BUFFER_OFFSET_IS_VALID (buffer)) { offset = GST_BUFFER_OFFSET (buffer); } else { change_offset = FALSE; offset = 0; } if (GST_BUFFER_OFFSET_END_IS_VALID (buffer)) { offset_end = GST_BUFFER_OFFSET_END (buffer); } else { change_offset_end = FALSE; offset_end = offset + size / frame_size; } if (segment->format == GST_FORMAT_TIME) { /* Handle clipping for GST_FORMAT_TIME */ gint64 start, stop, cstart, cstop, diff; start = timestamp; stop = timestamp + duration; if (gst_segment_clip (segment, GST_FORMAT_TIME, start, stop, &cstart, &cstop)) { diff = cstart - start; if (diff > 0) { timestamp = cstart; if (change_duration) duration -= diff; diff = gst_util_uint64_scale (diff, rate, GST_SECOND); if (change_offset) offset += diff; data += diff * frame_size; size -= diff * frame_size; } diff = stop - cstop; if (diff > 0) { /* duration is always valid if stop is valid */ duration -= diff; diff = gst_util_uint64_scale (diff, rate, GST_SECOND); if (change_offset_end) offset_end -= diff; size -= diff * frame_size; } } else { gst_buffer_unref (buffer); return NULL; } } else { /* Handle clipping for GST_FORMAT_DEFAULT */ gint64 start, stop, cstart, cstop, diff; g_return_val_if_fail (GST_BUFFER_OFFSET_IS_VALID (buffer), buffer); start = offset; stop = offset_end; if (gst_segment_clip (segment, GST_FORMAT_DEFAULT, start, stop, &cstart, &cstop)) { diff = cstart - start; if (diff > 0) { offset = cstart; timestamp = gst_util_uint64_scale (cstart, GST_SECOND, rate); if (change_duration) duration -= gst_util_uint64_scale (diff, GST_SECOND, rate); data += diff * frame_size; size -= diff * frame_size; } diff = stop - cstop; if (diff > 0) { offset_end = cstop; if (change_duration) duration -= gst_util_uint64_scale (diff, GST_SECOND, rate); size -= diff * frame_size; } } else { gst_buffer_unref (buffer); return NULL; } } /* Get a metadata writable buffer and apply all changes */ ret = gst_buffer_make_metadata_writable (buffer); GST_BUFFER_TIMESTAMP (ret) = timestamp; GST_BUFFER_SIZE (ret) = size; GST_BUFFER_DATA (ret) = data; if (change_duration) GST_BUFFER_DURATION (ret) = duration; if (change_offset) GST_BUFFER_OFFSET (ret) = offset; if (change_offset_end) GST_BUFFER_OFFSET_END (ret) = offset_end; return ret; }
static GstFlowReturn gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf) { GstV4l2Src *v4l2src = GST_V4L2SRC (src); GstV4l2Object *obj = v4l2src->v4l2object; GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL_CAST (obj->pool); GstFlowReturn ret; GstClock *clock; GstClockTime abs_time, base_time, timestamp, duration; GstClockTime delay; GstMessage *qos_msg; do { ret = GST_BASE_SRC_CLASS (parent_class)->alloc (GST_BASE_SRC (src), 0, obj->info.size, buf); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto alloc_failed; ret = gst_v4l2_buffer_pool_process (pool, buf); } while (ret == GST_V4L2_FLOW_CORRUPTED_BUFFER); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto error; timestamp = GST_BUFFER_TIMESTAMP (*buf); duration = obj->duration; /* 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 */ base_time = GST_ELEMENT (v4l2src)->base_time; gst_object_ref (clock); } else { /* no clock, can't set timestamps */ base_time = GST_CLOCK_TIME_NONE; } GST_OBJECT_UNLOCK (v4l2src); /* sample pipeline clock */ if (clock) { abs_time = gst_clock_get_time (clock); gst_object_unref (clock); } else { abs_time = GST_CLOCK_TIME_NONE; } retry: if (!v4l2src->has_bad_timestamp && timestamp != GST_CLOCK_TIME_NONE) { struct timespec now; GstClockTime gstnow; /* v4l2 specs say to use the system time although many drivers switched to * the more desirable monotonic time. We first try to use the monotonic time * and see how that goes */ clock_gettime (CLOCK_MONOTONIC, &now); gstnow = GST_TIMESPEC_TO_TIME (now); if (timestamp > gstnow || (gstnow - timestamp) > (10 * GST_SECOND)) { GTimeVal now; /* very large diff, fall back to system time */ g_get_current_time (&now); gstnow = GST_TIMEVAL_TO_TIME (now); } /* Detect buggy drivers here, and stop using their timestamp. Failing any * of these condition would imply a very buggy driver: * - Timestamp in the future * - Timestamp is going backward compare to last seen timestamp * - Timestamp is jumping forward for less then a frame duration * - Delay is bigger then the actual timestamp * */ if (timestamp > gstnow) { GST_WARNING_OBJECT (v4l2src, "Timestamp in the future detected, ignoring driver timestamps"); v4l2src->has_bad_timestamp = TRUE; goto retry; } if (v4l2src->last_timestamp > timestamp) { GST_WARNING_OBJECT (v4l2src, "Timestamp going backward, ignoring driver timestamps"); v4l2src->has_bad_timestamp = TRUE; goto retry; } delay = gstnow - timestamp; if (delay > timestamp) { GST_WARNING_OBJECT (v4l2src, "Timestamp does not correlate with any clock, ignoring driver timestamps"); v4l2src->has_bad_timestamp = TRUE; goto retry; } /* Save last timestamp for sanity checks */ v4l2src->last_timestamp = timestamp; GST_DEBUG_OBJECT (v4l2src, "ts: %" GST_TIME_FORMAT " now %" GST_TIME_FORMAT " delay %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp), GST_TIME_ARGS (gstnow), GST_TIME_ARGS (delay)); } else { /* we assume 1 frame latency otherwise */ if (GST_CLOCK_TIME_IS_VALID (duration)) delay = duration; else delay = 0; } /* set buffer metadata */ if (G_LIKELY (abs_time != GST_CLOCK_TIME_NONE)) { /* the time now is the time of the clock minus the base time */ timestamp = abs_time - base_time; /* adjust for delay in the device */ if (timestamp > delay) timestamp -= delay; else timestamp = 0; } else { timestamp = GST_CLOCK_TIME_NONE; } /* 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 " out ts %" GST_TIME_FORMAT, GST_TIME_ARGS (v4l2src->ctrl_time), GST_TIME_ARGS (timestamp)); /* use generated offset values only if there are not already valid ones * set by the v4l2 device */ if (!GST_BUFFER_OFFSET_IS_VALID (*buf) || !GST_BUFFER_OFFSET_END_IS_VALID (*buf)) { GST_BUFFER_OFFSET (*buf) = v4l2src->offset++; GST_BUFFER_OFFSET_END (*buf) = v4l2src->offset; } else { /* adjust raw v4l2 device sequence, will restart at null in case of renegotiation * (streamoff/streamon) */ GST_BUFFER_OFFSET (*buf) += v4l2src->renegotiation_adjust; GST_BUFFER_OFFSET_END (*buf) += v4l2src->renegotiation_adjust; /* check for frame loss with given (from v4l2 device) buffer offset */ if ((v4l2src->offset != 0) && (GST_BUFFER_OFFSET (*buf) != (v4l2src->offset + 1))) { guint64 lost_frame_count = GST_BUFFER_OFFSET (*buf) - v4l2src->offset - 1; GST_WARNING_OBJECT (v4l2src, "lost frames detected: count = %" G_GUINT64_FORMAT " - ts: %" GST_TIME_FORMAT, lost_frame_count, GST_TIME_ARGS (timestamp)); qos_msg = gst_message_new_qos (GST_OBJECT_CAST (v4l2src), TRUE, GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE, timestamp, GST_CLOCK_TIME_IS_VALID (duration) ? lost_frame_count * duration : GST_CLOCK_TIME_NONE); gst_element_post_message (GST_ELEMENT_CAST (v4l2src), qos_msg); } v4l2src->offset = GST_BUFFER_OFFSET (*buf); } GST_BUFFER_TIMESTAMP (*buf) = timestamp; GST_BUFFER_DURATION (*buf) = duration; return ret; /* ERROR */ alloc_failed: { if (ret != GST_FLOW_FLUSHING) GST_ELEMENT_ERROR (src, RESOURCE, NO_SPACE_LEFT, ("Failed to allocate a buffer"), (NULL)); return ret; } error: { if (ret == GST_V4L2_FLOW_LAST_BUFFER) { GST_ELEMENT_ERROR (src, RESOURCE, FAILED, ("Driver returned a buffer with no payload, this most likely " "indicate a bug in the driver."), (NULL)); ret = GST_FLOW_ERROR; } else { GST_DEBUG_OBJECT (src, "error processing buffer %d (%s)", ret, gst_flow_get_name (ret)); } return ret; } }
/** * gst_audio_buffer_clip: * @buffer: (transfer full): The buffer to clip. * @segment: Segment in %GST_FORMAT_TIME or %GST_FORMAT_DEFAULT to which * the buffer should be clipped. * @rate: sample rate. * @bpf: size of one audio frame in bytes. This is the size of one sample * * channels. * * Clip the buffer to the given %GstSegment. * * After calling this function the caller does not own a reference to * @buffer anymore. * * Returns: (transfer full): %NULL if the buffer is completely outside the configured segment, * otherwise the clipped buffer is returned. * * If the buffer has no timestamp, it is assumed to be inside the segment and * is not clipped */ GstBuffer * gst_audio_buffer_clip (GstBuffer * buffer, GstSegment * segment, gint rate, gint bpf) { GstBuffer *ret; GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE; guint64 offset = GST_BUFFER_OFFSET_NONE, offset_end = GST_BUFFER_OFFSET_NONE; gsize trim, size, osize; gboolean change_duration = TRUE, change_offset = TRUE, change_offset_end = TRUE; g_return_val_if_fail (segment->format == GST_FORMAT_TIME || segment->format == GST_FORMAT_DEFAULT, buffer); g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) /* No timestamp - assume the buffer is completely in the segment */ return buffer; /* Get copies of the buffer metadata to change later. * Calculate the missing values for the calculations, * they won't be changed later though. */ trim = 0; osize = size = gst_buffer_get_size (buffer); /* no data, nothing to clip */ if (!size) return buffer; timestamp = GST_BUFFER_TIMESTAMP (buffer); GST_DEBUG ("timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); if (GST_BUFFER_DURATION_IS_VALID (buffer)) { duration = GST_BUFFER_DURATION (buffer); } else { change_duration = FALSE; duration = gst_util_uint64_scale (size / bpf, GST_SECOND, rate); } if (GST_BUFFER_OFFSET_IS_VALID (buffer)) { offset = GST_BUFFER_OFFSET (buffer); } else { change_offset = FALSE; offset = 0; } if (GST_BUFFER_OFFSET_END_IS_VALID (buffer)) { offset_end = GST_BUFFER_OFFSET_END (buffer); } else { change_offset_end = FALSE; offset_end = offset + size / bpf; } if (segment->format == GST_FORMAT_TIME) { /* Handle clipping for GST_FORMAT_TIME */ guint64 start, stop, cstart, cstop, diff; start = timestamp; stop = timestamp + duration; if (gst_segment_clip (segment, GST_FORMAT_TIME, start, stop, &cstart, &cstop)) { diff = cstart - start; if (diff > 0) { timestamp = cstart; if (change_duration) duration -= diff; diff = gst_util_uint64_scale (diff, rate, GST_SECOND); if (change_offset) offset += diff; trim += diff * bpf; size -= diff * bpf; } diff = stop - cstop; if (diff > 0) { /* duration is always valid if stop is valid */ duration -= diff; diff = gst_util_uint64_scale (diff, rate, GST_SECOND); if (change_offset_end) offset_end -= diff; size -= diff * bpf; } } else { gst_buffer_unref (buffer); return NULL; } } else { /* Handle clipping for GST_FORMAT_DEFAULT */ guint64 start, stop, cstart, cstop, diff; g_return_val_if_fail (GST_BUFFER_OFFSET_IS_VALID (buffer), buffer); start = offset; stop = offset_end; if (gst_segment_clip (segment, GST_FORMAT_DEFAULT, start, stop, &cstart, &cstop)) { diff = cstart - start; if (diff > 0) { offset = cstart; timestamp = gst_util_uint64_scale (cstart, GST_SECOND, rate); if (change_duration) duration -= gst_util_uint64_scale (diff, GST_SECOND, rate); trim += diff * bpf; size -= diff * bpf; } diff = stop - cstop; if (diff > 0) { offset_end = cstop; if (change_duration) duration -= gst_util_uint64_scale (diff, GST_SECOND, rate); size -= diff * bpf; } } else { gst_buffer_unref (buffer); return NULL; } } if (trim == 0 && size == osize) { ret = buffer; if (GST_BUFFER_TIMESTAMP (ret) != timestamp) { ret = gst_buffer_make_writable (ret); GST_BUFFER_TIMESTAMP (ret) = timestamp; } if (GST_BUFFER_DURATION (ret) != duration) { ret = gst_buffer_make_writable (ret); GST_BUFFER_DURATION (ret) = duration; } } else { /* Get a writable buffer and apply all changes */ GST_DEBUG ("trim %" G_GSIZE_FORMAT " size %" G_GSIZE_FORMAT, trim, size); ret = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, trim, size); gst_buffer_unref (buffer); GST_DEBUG ("timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); GST_BUFFER_TIMESTAMP (ret) = timestamp; if (change_duration) GST_BUFFER_DURATION (ret) = duration; if (change_offset) GST_BUFFER_OFFSET (ret) = offset; if (change_offset_end) GST_BUFFER_OFFSET_END (ret) = offset_end; } return ret; }
void FarsightChannel::OnFakeSinkHandoff(GstElement *fakesink, GstBuffer *buffer, GstPad *pad, gpointer user_data) { FarsightChannel* self = (FarsightChannel*)user_data; static GStaticMutex mutex = G_STATIC_MUTEX_INIT; g_static_mutex_lock (&mutex); gst_buffer_ref(buffer); int rate = 0; int channels = 0; int width = 0; GstCaps *caps; GstStructure *structure; caps = gst_buffer_get_caps(buffer); structure = gst_caps_get_structure(caps, 0); gst_structure_get_int(structure, "rate", &rate); gst_structure_get_int(structure, "channels", &channels); gst_structure_get_int(structure, "width", &width); gst_caps_unref(caps); if (GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_PREROLL)) { LogInfo("Drop fakesink buffer: Preroll audio data packet."); gst_buffer_unref(buffer); g_static_mutex_unlock (&mutex); return; } if (GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_GAP)) { LogInfo("Drop fakesink buffer: Caps audio data packet."); gst_buffer_unref(buffer); g_static_mutex_unlock (&mutex); return; } if (GST_BUFFER_DURATION(buffer) == 0) { LogInfo("Drop fakesink buffer: Got audio data packet with 0 duration"); gst_buffer_unref(buffer); g_static_mutex_unlock (&mutex); return; } if (GST_BUFFER_IS_DISCONT(buffer)) { LogInfo("Drop fakesink buffer: Got disconnect audio data packet."); gst_buffer_unref(buffer); g_static_mutex_unlock (&mutex); return; } if (GST_BUFFER_OFFSET_IS_VALID(buffer)) { guint64 offset = GST_BUFFER_OFFSET (buffer); } if (GST_BUFFER_OFFSET_END_IS_VALID(buffer)) { guint64 offset = GST_BUFFER_OFFSET_END(buffer); } u8* data = GST_BUFFER_DATA(buffer); u32 size = GST_BUFFER_SIZE(buffer); self->HandleAudioData(data, size, rate, width, channels); gst_buffer_unref(buffer); g_static_mutex_unlock (&mutex); }
static GstFlowReturn celt_dec_chain_parse_data (GstCeltDec * dec, GstBuffer * buf, GstClockTime timestamp, GstClockTime duration) { GstFlowReturn res = GST_FLOW_OK; gint size; guint8 *data; GstBuffer *outbuf; gint16 *out_data; gint error = CELT_OK; if (timestamp != -1) { dec->segment.last_stop = timestamp; dec->granulepos = -1; } if (buf) { data = GST_BUFFER_DATA (buf); size = GST_BUFFER_SIZE (buf); GST_DEBUG_OBJECT (dec, "received buffer of size %u", size); if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf) && GST_BUFFER_OFFSET_END_IS_VALID (buf)) { dec->granulepos = GST_BUFFER_OFFSET_END (buf); GST_DEBUG_OBJECT (dec, "Taking granulepos from upstream: %" G_GUINT64_FORMAT, dec->granulepos); } /* copy timestamp */ } else { /* concealment data, pass NULL as the bits parameters */ GST_DEBUG_OBJECT (dec, "creating concealment data"); data = NULL; size = 0; } res = gst_pad_alloc_buffer_and_set_caps (dec->srcpad, GST_BUFFER_OFFSET_NONE, dec->frame_size * dec->header.nb_channels * 2, GST_PAD_CAPS (dec->srcpad), &outbuf); if (res != GST_FLOW_OK) { GST_DEBUG_OBJECT (dec, "buf alloc flow: %s", gst_flow_get_name (res)); return res; } out_data = (gint16 *) GST_BUFFER_DATA (outbuf); GST_LOG_OBJECT (dec, "decoding frame"); error = celt_decode (dec->state, data, size, out_data); if (error != CELT_OK) { GST_WARNING_OBJECT (dec, "Decoding error: %d", error); return GST_FLOW_ERROR; } if (dec->granulepos == -1) { if (dec->segment.format != GST_FORMAT_TIME) { GST_WARNING_OBJECT (dec, "segment not initialized or not TIME format"); dec->granulepos = dec->frame_size; } else { dec->granulepos = gst_util_uint64_scale_int (dec->segment.last_stop, dec->header.sample_rate, GST_SECOND) + dec->frame_size; } GST_DEBUG_OBJECT (dec, "granulepos=%" G_GINT64_FORMAT, dec->granulepos); } GST_BUFFER_OFFSET (outbuf) = dec->granulepos - dec->frame_size; GST_BUFFER_OFFSET_END (outbuf) = dec->granulepos; GST_BUFFER_TIMESTAMP (outbuf) = gst_util_uint64_scale_int (dec->granulepos - dec->frame_size, GST_SECOND, dec->header.sample_rate); GST_BUFFER_DURATION (outbuf) = dec->frame_duration; dec->granulepos += dec->frame_size; dec->segment.last_stop += dec->frame_duration; GST_LOG_OBJECT (dec, "pushing buffer with ts=%" GST_TIME_FORMAT ", dur=%" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), GST_TIME_ARGS (dec->frame_duration)); res = gst_pad_push (dec->srcpad, outbuf); if (res != GST_FLOW_OK) GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (res)); return res; }