QosMessagePtr QosMessage::create(const ObjectPtr & source, bool live, quint64 runningTime, quint64 streamTime, quint64 timestamp, quint64 duration) { GstMessage *m = gst_message_new_qos(source, live, runningTime, streamTime, timestamp, duration); return QosMessagePtr::wrap(m, false); }
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; } }
static GstFlowReturn theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet, GstClockTime outtime, GstClockTime outdur) { /* normal data packet */ th_ycbcr_buffer buf; GstBuffer *out; gboolean keyframe; GstFlowReturn result; ogg_int64_t gp; if (G_UNLIKELY (!dec->have_header)) goto not_initialized; /* get timestamp and durations */ if (outtime == -1) outtime = dec->last_timestamp; if (outdur == -1) outdur = gst_util_uint64_scale_int (GST_SECOND, dec->info.fps_denominator, dec->info.fps_numerator); /* calculate expected next timestamp */ if (outtime != -1 && outdur != -1) dec->last_timestamp = outtime + outdur; /* the second most significant bit of the first data byte is cleared * for keyframes. We can only check it if it's not a zero-length packet. */ keyframe = packet->bytes && ((packet->packet[0] & 0x40) == 0); if (G_UNLIKELY (keyframe)) { GST_DEBUG_OBJECT (dec, "we have a keyframe"); dec->need_keyframe = FALSE; } else if (G_UNLIKELY (dec->need_keyframe)) { goto dropping; } GST_DEBUG_OBJECT (dec, "parsing data packet"); /* this does the decoding */ if (G_UNLIKELY (th_decode_packetin (dec->decoder, packet, &gp) < 0)) goto decode_error; if (outtime != -1) { gboolean need_skip; GstClockTime running_time; GstClockTime earliest_time; gdouble proportion; /* qos needs to be done on running time */ running_time = gst_segment_to_running_time (&dec->segment, GST_FORMAT_TIME, outtime); GST_OBJECT_LOCK (dec); proportion = dec->proportion; earliest_time = dec->earliest_time; /* check for QoS, don't perform the last steps of getting and * pushing the buffers that are known to be late. */ need_skip = earliest_time != -1 && running_time <= earliest_time; GST_OBJECT_UNLOCK (dec); if (need_skip) { GstMessage *qos_msg; guint64 stream_time; gint64 jitter; GST_DEBUG_OBJECT (dec, "skipping decoding: qostime %" GST_TIME_FORMAT " <= %" GST_TIME_FORMAT, GST_TIME_ARGS (running_time), GST_TIME_ARGS (earliest_time)); dec->dropped++; stream_time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, outtime); jitter = GST_CLOCK_DIFF (running_time, earliest_time); qos_msg = gst_message_new_qos (GST_OBJECT_CAST (dec), FALSE, running_time, stream_time, outtime, outdur); gst_message_set_qos_values (qos_msg, jitter, proportion, 1000000); gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS, dec->processed, dec->dropped); gst_element_post_message (GST_ELEMENT_CAST (dec), qos_msg); goto dropping_qos; } } /* this does postprocessing and set up the decoded frame * pointers in our yuv variable */ if (G_UNLIKELY (th_decode_ycbcr_out (dec->decoder, buf) < 0)) goto no_yuv; if (G_UNLIKELY ((buf[0].width != dec->info.frame_width) || (buf[0].height != dec->info.frame_height))) goto wrong_dimensions; result = theora_handle_image (dec, buf, &out); if (result != GST_FLOW_OK) return result; GST_BUFFER_OFFSET (out) = dec->frame_nr; if (dec->frame_nr != -1) dec->frame_nr++; GST_BUFFER_OFFSET_END (out) = dec->frame_nr; GST_BUFFER_TIMESTAMP (out) = outtime; GST_BUFFER_DURATION (out) = outdur; dec->processed++; if (dec->segment.rate >= 0.0) result = theora_dec_push_forward (dec, out); else result = theora_dec_push_reverse (dec, out); return result; /* ERRORS */ not_initialized: { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, (NULL), ("no header sent yet")); return GST_FLOW_ERROR; } dropping: { GST_WARNING_OBJECT (dec, "dropping frame because we need a keyframe"); dec->discont = TRUE; return GST_FLOW_OK; } dropping_qos: { if (dec->frame_nr != -1) dec->frame_nr++; dec->discont = TRUE; GST_WARNING_OBJECT (dec, "dropping frame because of QoS"); return GST_FLOW_OK; } decode_error: { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, (NULL), ("theora decoder did not decode data packet")); return GST_FLOW_ERROR; } no_yuv: { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, (NULL), ("couldn't read out YUV image")); return GST_FLOW_ERROR; } wrong_dimensions: { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, FORMAT, (NULL), ("dimensions of image do not match header")); return GST_FLOW_ERROR; } }