static gboolean gst_hls_demux_switch_playlist (GstHLSDemux * demux) { GTimeVal now; gint64 diff, limit; g_get_current_time (&now); if (!demux->client->main->lists) return TRUE; /* compare the time when the fragment was downloaded with the time when it was * scheduled */ diff = (GST_TIMEVAL_TO_TIME (demux->next_update) - GST_TIMEVAL_TO_TIME (now)); limit = demux->client->current->targetduration * GST_SECOND * demux->bitrate_switch_tol; GST_DEBUG ("diff:%s%" GST_TIME_FORMAT ", limit:%" GST_TIME_FORMAT, diff < 0 ? "-" : " ", GST_TIME_ARGS (ABS (diff)), GST_TIME_ARGS (limit)); /* if we are on time switch to a higher bitrate */ if (diff > limit) { gst_hls_demux_change_playlist (demux, TRUE); } else if (diff < 0) { /* if the client is too slow wait until it has accumulated a certain delay to * switch to a lower bitrate */ demux->accumulated_delay -= diff; if (demux->accumulated_delay >= limit) { gst_hls_demux_change_playlist (demux, FALSE); } else if (demux->accumulated_delay < 0) { demux->accumulated_delay = 0; } } return TRUE; }
/** * gst_rtsp_session_next_timeout: * @session: a #GstRTSPSession * @now: the current system time * * Get the amount of milliseconds till the session will expire. * * Returns: the amount of milliseconds since the session will time out. */ gint gst_rtsp_session_next_timeout (GstRTSPSession * session, GTimeVal * now) { GstRTSPSessionPrivate *priv; gint res; GstClockTime last_access, now_ns; g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1); g_return_val_if_fail (now != NULL, -1); priv = session->priv; g_mutex_lock (&priv->lock); if (g_atomic_int_get (&priv->expire_count) != 0) { /* touch session when the expire count is not 0 */ g_get_current_time (&priv->last_access); } last_access = GST_TIMEVAL_TO_TIME (priv->last_access); /* add timeout allow for 5 seconds of extra time */ last_access += priv->timeout * GST_SECOND + (5 * GST_SECOND); g_mutex_unlock (&priv->lock); now_ns = GST_TIMEVAL_TO_TIME (*now); if (last_access > now_ns) res = GST_TIME_AS_MSECONDS (last_access - now_ns); else res = 0; return res; }
static gboolean gst_hls_demux_switch_playlist (GstHLSDemux * demux) { GTimeVal now; GstClockTime diff; gsize size; gint bitrate; GstFragment *fragment = g_queue_peek_tail (demux->queue); GstBuffer *buffer; GST_M3U8_CLIENT_LOCK (demux->client); if (!demux->client->main->lists) { GST_M3U8_CLIENT_UNLOCK (demux->client); return TRUE; } GST_M3U8_CLIENT_UNLOCK (demux->client); /* compare the time when the fragment was downloaded with the time when it was * scheduled */ g_get_current_time (&now); diff = (GST_TIMEVAL_TO_TIME (now) - GST_TIMEVAL_TO_TIME (demux->next_update)); buffer = gst_fragment_get_buffer (fragment); size = gst_buffer_get_size (buffer); bitrate = (size * 8) / ((double) diff / GST_SECOND); GST_DEBUG ("Downloaded %d bytes in %" GST_TIME_FORMAT ". Bitrate is : %d", size, GST_TIME_ARGS (diff), bitrate); gst_buffer_unref (buffer); return gst_hls_demux_change_playlist (demux, bitrate * demux->bitrate_limit); }
static gboolean gst_transcoder_adjust_wait_time (GstClock * sync_clock, GstClockTime time, GstClockID id, GstCpuThrottlingClock * self) { struct rusage ru; float delta_usage, usage, coef; GstCpuThrottlingClockPrivate *priv = self->priv; getrusage (RUSAGE_SELF, &ru); delta_usage = GST_TIMEVAL_TO_TIME (ru.ru_utime) - GST_TIMEVAL_TO_TIME (self->priv->last_usage.ru_utime); usage = ((float) delta_usage / self->priv->time_between_evals * 100) / g_get_num_processors (); self->priv->last_usage = ru; coef = GST_MSECOND / 10; if (usage < (gfloat) priv->wanted_cpu_usage) { coef = -coef; } priv->current_wait_time = CLAMP (0, (GstClockTime) priv->current_wait_time + coef, GST_SECOND); GST_DEBUG_OBJECT (self, "Avg is %f (wanted %d) => %" GST_TIME_FORMAT, usage, self->priv->wanted_cpu_usage, GST_TIME_ARGS (priv->current_wait_time)); return TRUE; }
static void gst_pulsesrc_stream_latency_update_cb (pa_stream * s, void *userdata) { const pa_timing_info *info; pa_usec_t source_usec; info = pa_stream_get_timing_info (s); if (!info) { GST_LOG_OBJECT (GST_PULSESRC_CAST (userdata), "latency update (information unknown)"); return; } #ifdef HAVE_PULSE_0_9_11 source_usec = info->configured_source_usec; #else source_usec = 0; #endif GST_LOG_OBJECT (GST_PULSESRC_CAST (userdata), "latency_update, %" G_GUINT64_FORMAT ", %d:%" G_GINT64_FORMAT ", %d:%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT, GST_TIMEVAL_TO_TIME (info->timestamp), info->write_index_corrupt, info->write_index, info->read_index_corrupt, info->read_index, info->source_usec, source_usec); }
static void gst_multiudpsink_add_internal (GstMultiUDPSink * sink, const gchar * host, gint port, gboolean lock) { GstUDPClient *client; GstUDPClient udpclient; GTimeVal now; GList *find; udpclient.host = (gchar *) host; udpclient.port = port; GST_DEBUG_OBJECT (sink, "adding client on host %s, port %d", host, port); if (lock) g_mutex_lock (&sink->client_lock); find = g_list_find_custom (sink->clients, &udpclient, (GCompareFunc) client_compare); if (find) { client = (GstUDPClient *) find->data; GST_DEBUG_OBJECT (sink, "found %d existing clients with host %s, port %d", client->refcount, host, port); client->refcount++; } else { client = create_client (sink, host, port); if (!client) goto error; g_get_current_time (&now); client->connect_time = GST_TIMEVAL_TO_TIME (now); if (sink->used_socket) gst_multiudpsink_configure_client (sink, client); GST_DEBUG_OBJECT (sink, "add client with host %s, port %d", host, port); sink->clients = g_list_prepend (sink->clients, client); } if (lock) g_mutex_unlock (&sink->client_lock); g_signal_emit (G_OBJECT (sink), gst_multiudpsink_signals[SIGNAL_CLIENT_ADDED], 0, host, port); GST_DEBUG_OBJECT (sink, "added client on host %s, port %d", host, port); return; /* ERRORS */ error: { GST_DEBUG_OBJECT (sink, "did not add client on host %s, port %d", host, port); if (lock) g_mutex_unlock (&sink->client_lock); return; } }
void gst_multiudpsink_remove (GstMultiUDPSink * sink, const gchar * host, gint port) { GList *find; GstUDPClient udpclient; GstUDPClient *client; GTimeVal now; udpclient.host = (gchar *) host; udpclient.port = port; g_mutex_lock (sink->client_lock); find = g_list_find_custom (sink->clients, &udpclient, (GCompareFunc) client_compare); if (!find) goto not_found; client = (GstUDPClient *) find->data; GST_DEBUG_OBJECT (sink, "found %d clients with host %s, port %d", client->refcount, host, port); client->refcount--; if (client->refcount == 0) { GST_DEBUG_OBJECT (sink, "remove client with host %s, port %d", host, port); g_get_current_time (&now); client->disconnect_time = GST_TIMEVAL_TO_TIME (now); if (*(client->sock) != -1 && sink->auto_multicast && gst_udp_is_multicast (&client->theiraddr)) gst_udp_leave_group (*(client->sock), &client->theiraddr); /* Unlock to emit signal before we delete the actual client */ g_mutex_unlock (sink->client_lock); g_signal_emit (G_OBJECT (sink), gst_multiudpsink_signals[SIGNAL_CLIENT_REMOVED], 0, host, port); g_mutex_lock (sink->client_lock); sink->clients = g_list_delete_link (sink->clients, find); free_client (client); } g_mutex_unlock (sink->client_lock); return; /* ERRORS */ not_found: { g_mutex_unlock (sink->client_lock); GST_WARNING_OBJECT (sink, "client at host %s, port %d not found", host, port); return; } }
GstClockTime toGstClockTime(float time) { // Extract the integer part of the time (seconds) and the fractional part (microseconds). Attempt to // round the microseconds so no floating point precision is lost and we can perform an accurate seek. float seconds; float microSeconds = modff(time, &seconds) * 1000000; GTimeVal timeValue; timeValue.tv_sec = static_cast<glong>(seconds); timeValue.tv_usec = static_cast<glong>(roundf(microSeconds / 10000) * 10000); return GST_TIMEVAL_TO_TIME(timeValue); }
static GstFlowReturn gst_imx_v4l2_buffer_pool_acquire_buffer(GstBufferPool *bpool, GstBuffer **buffer, G_GNUC_UNUSED GstBufferPoolAcquireParams *params) { GstImxV4l2BufferPool *pool = GST_IMX_V4L2_BUFFER_POOL(bpool); struct v4l2_buffer vbuffer; GstBuffer *buf; GstImxV4l2Meta *meta; if (GST_BUFFER_POOL_IS_FLUSHING(bpool)) return GST_FLOW_FLUSHING; memset(&vbuffer, 0, sizeof(vbuffer)); vbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vbuffer.memory = V4L2_MEMORY_MMAP; if (ioctl(GST_IMX_FD_OBJECT_GET_FD(pool->fd_obj_v4l), VIDIOC_DQBUF, &vbuffer) < 0) { GST_ERROR_OBJECT(pool, "VIDIOC_DQBUF failed: %s", g_strerror(errno)); return GST_FLOW_ERROR; } buf = pool->buffers[vbuffer.index]; GST_DEBUG_OBJECT(pool, "dqbuf %u %p", vbuffer.index, (gpointer)buf); pool->buffers[vbuffer.index] = NULL; g_assert(buf); meta = GST_IMX_V4L2_META_GET(buf); g_assert(meta); gst_buffer_remove_all_memory(buf); gst_buffer_append_memory(buf, gst_memory_new_wrapped(0, meta->mem, meta->vbuffer.length, 0, vbuffer.bytesused, NULL, NULL)); GST_BUFFER_TIMESTAMP(buf) = GST_TIMEVAL_TO_TIME(vbuffer.timestamp); *buffer = buf; return GST_FLOW_OK; }
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; 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; } if (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 (gstnow < timestamp && (timestamp - gstnow) > (10 * GST_SECOND)) { GTimeVal now; /* very large diff, fall back to system time */ g_get_current_time (&now); gstnow = GST_TIMEVAL_TO_TIME (now); } if (gstnow > timestamp) { delay = gstnow - timestamp; } else { delay = 0; } 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 */ GST_BUFFER_OFFSET (*buf) = v4l2src->offset++; GST_BUFFER_OFFSET_END (*buf) = v4l2src->offset; 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)); 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 gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer) { GstFlowReturn res; GstBuffer *outbuf; struct v4l2_buffer vbuffer; GstV4l2Object *obj = pool->obj; GstClockTime timestamp; if (obj->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { /* select works for input devices when data is available. According to the * specs we can also poll to find out when a frame has been displayed but * that just seems to lock up here */ if ((res = gst_v4l2_object_poll (obj)) != GST_FLOW_OK) goto poll_error; } memset (&vbuffer, 0x00, sizeof (vbuffer)); vbuffer.type = obj->type; vbuffer.memory = V4L2_MEMORY_MMAP; GST_LOG_OBJECT (pool, "doing DQBUF"); if (v4l2_ioctl (pool->video_fd, VIDIOC_DQBUF, &vbuffer) < 0) goto error; /* get our GstBuffer with that index from the pool, if the buffer was * outstanding we have a serious problem. */ outbuf = pool->buffers[vbuffer.index]; if (outbuf == NULL) goto no_buffer; /* mark the buffer outstanding */ pool->buffers[vbuffer.index] = NULL; pool->num_queued--; timestamp = GST_TIMEVAL_TO_TIME (vbuffer.timestamp); GST_LOG_OBJECT (pool, "dequeued buffer %p seq:%d (ix=%d), used %d, flags %08x, ts %" GST_TIME_FORMAT ", pool-queued=%d, buffer=%p", outbuf, vbuffer.sequence, vbuffer.index, vbuffer.bytesused, vbuffer.flags, GST_TIME_ARGS (timestamp), pool->num_queued, outbuf); /* set top/bottom field first if v4l2_buffer has the information */ if (vbuffer.field == V4L2_FIELD_INTERLACED_TB) { GST_BUFFER_FLAG_SET (outbuf, GST_VIDEO_BUFFER_FLAG_TFF); } if (vbuffer.field == V4L2_FIELD_INTERLACED_BT) { GST_BUFFER_FLAG_UNSET (outbuf, GST_VIDEO_BUFFER_FLAG_TFF); } /* this can change at every frame, esp. with jpeg */ if (obj->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) gst_buffer_resize (outbuf, 0, vbuffer.bytesused); else gst_buffer_resize (outbuf, 0, vbuffer.length); GST_BUFFER_TIMESTAMP (outbuf) = timestamp; *buffer = outbuf; return GST_FLOW_OK; /* ERRORS */ poll_error: { GST_DEBUG_OBJECT (pool, "poll error %s", gst_flow_get_name (res)); return res; } error: { GST_WARNING_OBJECT (pool, "problem dequeuing frame %d (ix=%d), pool-ct=%d, buf.flags=%d", vbuffer.sequence, vbuffer.index, GST_MINI_OBJECT_REFCOUNT (pool), vbuffer.flags); switch (errno) { case EAGAIN: GST_WARNING_OBJECT (pool, "Non-blocking I/O has been selected using O_NONBLOCK and" " no buffer was in the outgoing queue. device %s", obj->videodev); break; case EINVAL: GST_ERROR_OBJECT (pool, "The buffer type is not supported, or the index is out of bounds, " "or no buffers have been allocated yet, or the userptr " "or length are invalid. device %s", obj->videodev); break; case ENOMEM: GST_ERROR_OBJECT (pool, "insufficient memory to enqueue a user pointer buffer"); break; case EIO: GST_INFO_OBJECT (pool, "VIDIOC_DQBUF failed due to an internal error." " Can also indicate temporary problems like signal loss." " Note the driver might dequeue an (empty) buffer despite" " returning an error, or even stop capturing." " device %s", obj->videodev); /* have we de-queued a buffer ? */ if (!(vbuffer.flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE))) { GST_DEBUG_OBJECT (pool, "reenqueing buffer"); /* FIXME ... should we do something here? */ } break; case EINTR: GST_WARNING_OBJECT (pool, "could not sync on a buffer on device %s", obj->videodev); break; default: GST_WARNING_OBJECT (pool, "Grabbing frame got interrupted on %s unexpectedly. %d: %s.", obj->videodev, errno, g_strerror (errno)); break; } return GST_FLOW_ERROR; } no_buffer: { GST_ERROR_OBJECT (pool, "No free buffer found in the pool at index %d.", vbuffer.index); return GST_FLOW_ERROR; } }
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_bus_timed_pop_filtered: * @bus: a #GstBus to pop from * @timeout: a timeout in nanoseconds, or GST_CLOCK_TIME_NONE to wait forever * @types: message types to take into account, GST_MESSAGE_ANY for any type * * Get a message from the bus whose type matches the message type mask @types, * waiting up to the specified timeout (and discarding any messages that do not * match the mask provided). * * If @timeout is 0, this function behaves like gst_bus_pop_filtered(). If * @timeout is #GST_CLOCK_TIME_NONE, this function will block forever until a * matching message was posted on the bus. * * Returns: (transfer full) (nullable): a #GstMessage matching the * filter in @types, or %NULL if no matching message was found on * the bus until the timeout expired. The message is taken from * the bus and needs to be unreffed with gst_message_unref() after * usage. * * MT safe. */ GstMessage * gst_bus_timed_pop_filtered (GstBus * bus, GstClockTime timeout, GstMessageType types) { GstMessage *message; GTimeVal now, then; gboolean first_round = TRUE; GstClockTime elapsed = 0; g_return_val_if_fail (GST_IS_BUS (bus), NULL); g_return_val_if_fail (types != 0, NULL); g_return_val_if_fail (timeout == 0 || bus->priv->poll != NULL, NULL); g_mutex_lock (&bus->priv->queue_lock); while (TRUE) { gint ret; GST_LOG_OBJECT (bus, "have %d messages", gst_atomic_queue_length (bus->priv->queue)); while ((message = gst_atomic_queue_pop (bus->priv->queue))) { if (bus->priv->poll) { while (!gst_poll_read_control (bus->priv->poll)) { if (errno == EWOULDBLOCK) { /* Retry, this can happen if pushing to the queue has finished, * popping here succeeded but writing control did not finish * before we got to this line. */ /* Give other threads the chance to do something */ g_thread_yield (); continue; } else { /* This is a real error and means that either the bus is in an * inconsistent state, or the GstPoll is invalid. GstPoll already * prints a critical warning about this, no need to do that again * ourselves */ break; } } } GST_DEBUG_OBJECT (bus, "got message %p, %s from %s, type mask is %u", message, GST_MESSAGE_TYPE_NAME (message), GST_MESSAGE_SRC_NAME (message), (guint) types); if ((GST_MESSAGE_TYPE (message) & types) != 0) { /* Extra check to ensure extended types don't get matched unless * asked for */ if ((!GST_MESSAGE_TYPE_IS_EXTENDED (message)) || (types & GST_MESSAGE_EXTENDED)) { /* exit the loop, we have a message */ goto beach; } } GST_DEBUG_OBJECT (bus, "discarding message, does not match mask"); gst_message_unref (message); message = NULL; } /* no need to wait, exit loop */ if (timeout == 0) break; else if (timeout != GST_CLOCK_TIME_NONE) { if (first_round) { g_get_current_time (&then); first_round = FALSE; } else { g_get_current_time (&now); elapsed = GST_TIMEVAL_TO_TIME (now) - GST_TIMEVAL_TO_TIME (then); if (elapsed > timeout) break; } } /* only here in timeout case */ g_assert (bus->priv->poll); g_mutex_unlock (&bus->priv->queue_lock); ret = gst_poll_wait (bus->priv->poll, timeout - elapsed); g_mutex_lock (&bus->priv->queue_lock); if (ret == 0) { GST_INFO_OBJECT (bus, "timed out, breaking loop"); break; } else { GST_INFO_OBJECT (bus, "we got woken up, recheck for message"); } } beach: g_mutex_unlock (&bus->priv->queue_lock); return message; }
static GstFlowReturn gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer) { GstFlowReturn res; GstBuffer *outbuf; GstV4l2Object *obj = pool->obj; GstClockTime timestamp; GstV4l2MemoryGroup *group; gint i; if ((res = gst_v4l2_buffer_pool_poll (pool)) != GST_FLOW_OK) goto poll_failed; GST_LOG_OBJECT (pool, "dequeueing a buffer"); group = gst_v4l2_allocator_dqbuf (pool->vallocator); if (group == NULL) goto dqbuf_failed; /* get our GstBuffer with that index from the pool, if the buffer was * outstanding we have a serious problem. */ outbuf = pool->buffers[group->buffer.index]; if (outbuf == NULL) goto no_buffer; /* mark the buffer outstanding */ pool->buffers[group->buffer.index] = NULL; if (g_atomic_int_dec_and_test (&pool->num_queued)) { GST_OBJECT_LOCK (pool); pool->empty = TRUE; GST_OBJECT_UNLOCK (pool); } timestamp = GST_TIMEVAL_TO_TIME (group->buffer.timestamp); #ifndef GST_DISABLE_GST_DEBUG for (i = 0; i < group->n_mem; i++) { GST_LOG_OBJECT (pool, "dequeued buffer %p seq:%d (ix=%d), mem %p used %d, plane=%d, flags %08x, ts %" GST_TIME_FORMAT ", pool-queued=%d, buffer=%p", outbuf, group->buffer.sequence, group->buffer.index, group->mem[i], group->planes[i].bytesused, i, group->buffer.flags, GST_TIME_ARGS (timestamp), pool->num_queued, outbuf); } #endif /* set top/bottom field first if v4l2_buffer has the information */ switch (group->buffer.field) { case V4L2_FIELD_NONE: GST_BUFFER_FLAG_UNSET (outbuf, GST_VIDEO_BUFFER_FLAG_INTERLACED); GST_BUFFER_FLAG_UNSET (outbuf, GST_VIDEO_BUFFER_FLAG_TFF); break; case V4L2_FIELD_INTERLACED_TB: GST_BUFFER_FLAG_SET (outbuf, GST_VIDEO_BUFFER_FLAG_INTERLACED); GST_BUFFER_FLAG_SET (outbuf, GST_VIDEO_BUFFER_FLAG_TFF); break; case V4L2_FIELD_INTERLACED_BT: GST_BUFFER_FLAG_SET (outbuf, GST_VIDEO_BUFFER_FLAG_INTERLACED); GST_BUFFER_FLAG_UNSET (outbuf, GST_VIDEO_BUFFER_FLAG_TFF); break; default: GST_BUFFER_FLAG_UNSET (outbuf, GST_VIDEO_BUFFER_FLAG_INTERLACED); GST_BUFFER_FLAG_UNSET (outbuf, GST_VIDEO_BUFFER_FLAG_TFF); GST_FIXME_OBJECT (pool, "Unhandled enum v4l2_field %d - treating as progressive", group->buffer.field); } if (GST_VIDEO_INFO_FORMAT (&obj->info) == GST_VIDEO_FORMAT_ENCODED) { if (group->buffer.flags & V4L2_BUF_FLAG_KEYFRAME) GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); else GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); } GST_BUFFER_TIMESTAMP (outbuf) = timestamp; *buffer = outbuf; return GST_FLOW_OK; /* ERRORS */ poll_failed: { GST_DEBUG_OBJECT (pool, "poll error %s", gst_flow_get_name (res)); return res; } dqbuf_failed: { return GST_FLOW_ERROR; } no_buffer: { GST_ERROR_OBJECT (pool, "No free buffer found in the pool at index %d.", group->buffer.index); return GST_FLOW_ERROR; } }
void gst_multiudpsink_remove (GstMultiUDPSink * sink, const gchar * host, gint port) { GList *find; GstUDPClient udpclient; GstUDPClient *client; GTimeVal now; udpclient.host = (gchar *) host; udpclient.port = port; g_mutex_lock (&sink->client_lock); find = g_list_find_custom (sink->clients, &udpclient, (GCompareFunc) client_compare); if (!find) goto not_found; client = (GstUDPClient *) find->data; GST_DEBUG_OBJECT (sink, "found %d clients with host %s, port %d", client->refcount, host, port); client->refcount--; if (client->refcount == 0) { GInetSocketAddress *saddr = G_INET_SOCKET_ADDRESS (client->addr); GInetAddress *addr = g_inet_socket_address_get_address (saddr); GSocketFamily family = g_socket_address_get_family (G_SOCKET_ADDRESS (saddr)); GSocket *socket; /* Select socket to send from for this address */ if (family == G_SOCKET_FAMILY_IPV6 || !sink->used_socket) socket = sink->used_socket_v6; else socket = sink->used_socket; GST_DEBUG_OBJECT (sink, "remove client with host %s, port %d", host, port); g_get_current_time (&now); client->disconnect_time = GST_TIMEVAL_TO_TIME (now); if (socket && sink->auto_multicast && g_inet_address_get_is_multicast (addr)) { GError *err = NULL; if (!g_socket_leave_multicast_group (socket, addr, FALSE, sink->multi_iface, &err)) { GST_DEBUG_OBJECT (sink, "Failed to leave multicast group: %s", err->message); g_clear_error (&err); } } /* Unlock to emit signal before we delete the actual client */ g_mutex_unlock (&sink->client_lock); g_signal_emit (G_OBJECT (sink), gst_multiudpsink_signals[SIGNAL_CLIENT_REMOVED], 0, host, port); g_mutex_lock (&sink->client_lock); sink->clients = g_list_delete_link (sink->clients, find); free_client (client); } g_mutex_unlock (&sink->client_lock); return; /* ERRORS */ not_found: { g_mutex_unlock (&sink->client_lock); GST_WARNING_OBJECT (sink, "client at host %s, port %d not found", host, port); return; } }
static void do_stats (GstTracer * obj, guint64 ts) { GstRUsageTracer *self = GST_RUSAGE_TRACER_CAST (obj); GstThreadStats *stats; gpointer thread_id = g_thread_self (); guint avg_cpuload, cur_cpuload; struct rusage ru; GstClockTime tproc = G_GUINT64_CONSTANT (0); GstClockTime tthread = G_GUINT64_CONSTANT (0); GstClockTime dts, dtproc; #ifdef HAVE_CLOCK_GETTIME { struct timespec now; if (!clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &now)) { tproc = GST_TIMESPEC_TO_TIME (now); } else { GST_WARNING_OBJECT (obj, "clock_gettime (CLOCK_PROCESS_CPUTIME_ID,...) failed: %s", g_strerror (errno)); getrusage (RUSAGE_SELF, &ru); tproc = GST_TIMEVAL_TO_TIME (ru.ru_utime) + GST_TIMEVAL_TO_TIME (ru.ru_stime); } /* cpu time per thread */ if (!clock_gettime (CLOCK_THREAD_CPUTIME_ID, &now)) { tthread = GST_TIMESPEC_TO_TIME (now); } else { GST_WARNING_OBJECT (obj, "clock_gettime (CLOCK_THREAD_CPUTIME_ID,...) failed: %s", g_strerror (errno)); #ifdef RUSAGE_THREAD getrusage (RUSAGE_THREAD, &ru); tthread = GST_TIMEVAL_TO_TIME (ru.ru_utime) + GST_TIMEVAL_TO_TIME (ru.ru_stime); #endif } } #else getrusage (RUSAGE_SELF, &ru); tproc = GST_TIMEVAL_TO_TIME (ru.ru_utime) + GST_TIMEVAL_TO_TIME (ru.ru_stime); #ifdef RUSAGE_THREAD getrusage (RUSAGE_THREAD, &ru); tthread = GST_TIMEVAL_TO_TIME (ru.ru_utime) + GST_TIMEVAL_TO_TIME (ru.ru_stime); #endif #endif /* get stats record for current thread */ if (!(stats = g_hash_table_lookup (self->threads, thread_id))) { stats = g_slice_new0 (GstThreadStats); stats->tvs_thread = make_trace_values (GST_SECOND); g_hash_table_insert (self->threads, thread_id, stats); } stats->tthread = tthread; /* Calibrate ts for the process and main thread. For tthread[main] and tproc * the time is larger than ts, as our base-ts is taken after the process has * started. */ if (G_UNLIKELY (thread_id == self->main_thread_id)) { self->main_thread_id = NULL; /* when the registry gets updated, the tproc is less than the debug time ? */ /* TODO(ensonic): we still see cases where tproc overtakes ts, especially * when with sync=false, can this be due to multiple cores in use? */ if (tproc > ts) { self->tproc_base = tproc - ts; GST_DEBUG ("rusage: calibrating by %" G_GUINT64_FORMAT ", thread: %" G_GUINT64_FORMAT ", proc: %" G_GUINT64_FORMAT, self->tproc_base, stats->tthread, tproc); stats->tthread -= self->tproc_base; } } /* we always need to correct proc time */ tproc -= self->tproc_base; /* FIXME: how can we take cpu-frequency scaling into account? * - looking at /sys/devices/system/cpu/cpu0/cpufreq/ * scale_factor=scaling_max_freq/scaling_cur_freq * - as a workaround we can switch it via /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor * cpufreq-selector -g performance * cpufreq-selector -g ondemand */ /* *INDENT-OFF* */ avg_cpuload = (guint) gst_util_uint64_scale (stats->tthread, G_GINT64_CONSTANT (1000), ts); update_trace_value (stats->tvs_thread, ts, stats->tthread, &dts, &dtproc); cur_cpuload = (guint) gst_util_uint64_scale (dtproc, G_GINT64_CONSTANT (1000), dts); gst_tracer_record_log (tr_thread, (guint64) (guintptr) thread_id, ts, MIN (avg_cpuload, 1000), MIN (cur_cpuload, 1000), stats->tthread); avg_cpuload = (guint) gst_util_uint64_scale (tproc / num_cpus, G_GINT64_CONSTANT (1000), ts); G_LOCK (_proc); update_trace_value (self->tvs_proc, ts, tproc, &dts, &dtproc); G_UNLOCK (_proc); cur_cpuload = (guint) gst_util_uint64_scale (dtproc / num_cpus, G_GINT64_CONSTANT (1000), dts); gst_tracer_record_log (tr_proc, (guint64) getpid (), ts, MIN (avg_cpuload, 1000), MIN (cur_cpuload, 1000), tproc); /* *INDENT-ON* */ }
/** * gst_bus_timed_pop_filtered: * @bus: a #GstBus to pop from * @timeout: a timeout in nanoseconds, or GST_CLOCK_TIME_NONE to wait forever * @types: message types to take into account, GST_MESSAGE_ANY for any type * * Get a message from the bus whose type matches the message type mask @types, * waiting up to the specified timeout (and discarding any messages that do not * match the mask provided). * * If @timeout is 0, this function behaves like gst_bus_pop_filtered(). If * @timeout is #GST_CLOCK_TIME_NONE, this function will block forever until a * matching message was posted on the bus. * * Returns: (transfer full) (nullable): a #GstMessage matching the * filter in @types, or %NULL if no matching message was found on * the bus until the timeout expired. The message is taken from * the bus and needs to be unreffed with gst_message_unref() after * usage. * * MT safe. */ GstMessage * gst_bus_timed_pop_filtered (GstBus * bus, GstClockTime timeout, GstMessageType types) { GstMessage *message; GTimeVal now, then; gboolean first_round = TRUE; GstClockTime elapsed = 0; g_return_val_if_fail (GST_IS_BUS (bus), NULL); g_return_val_if_fail (types != 0, NULL); g_return_val_if_fail (timeout == 0 || bus->priv->poll != NULL, NULL); g_mutex_lock (&bus->priv->queue_lock); while (TRUE) { gint ret; GST_LOG_OBJECT (bus, "have %d messages", gst_atomic_queue_length (bus->priv->queue)); while ((message = gst_atomic_queue_pop (bus->priv->queue))) { if (bus->priv->poll) gst_poll_read_control (bus->priv->poll); GST_DEBUG_OBJECT (bus, "got message %p, %s from %s, type mask is %u", message, GST_MESSAGE_TYPE_NAME (message), GST_MESSAGE_SRC_NAME (message), (guint) types); if ((GST_MESSAGE_TYPE (message) & types) != 0) { /* Extra check to ensure extended types don't get matched unless * asked for */ if ((!GST_MESSAGE_TYPE_IS_EXTENDED (message)) || (types & GST_MESSAGE_EXTENDED)) { /* exit the loop, we have a message */ goto beach; } } GST_DEBUG_OBJECT (bus, "discarding message, does not match mask"); gst_message_unref (message); message = NULL; } /* no need to wait, exit loop */ if (timeout == 0) break; else if (timeout != GST_CLOCK_TIME_NONE) { if (first_round) { g_get_current_time (&then); first_round = FALSE; } else { g_get_current_time (&now); elapsed = GST_TIMEVAL_TO_TIME (now) - GST_TIMEVAL_TO_TIME (then); if (elapsed > timeout) break; } } /* only here in timeout case */ g_assert (bus->priv->poll); g_mutex_unlock (&bus->priv->queue_lock); ret = gst_poll_wait (bus->priv->poll, timeout - elapsed); g_mutex_lock (&bus->priv->queue_lock); if (ret == 0) { GST_INFO_OBJECT (bus, "timed out, breaking loop"); break; } else { GST_INFO_OBJECT (bus, "we got woken up, recheck for message"); } } beach: g_mutex_unlock (&bus->priv->queue_lock); return message; }
static gboolean do_seek (GtkWidget * widget, gboolean flush, gboolean segment) { gint64 start, stop; gboolean res = FALSE; GstEvent *s_event; gdouble rate; GTimeVal tv; gboolean valid; gdouble new_range; if (segment) new_range = gtk_range_get_value (GTK_RANGE (widget)); else { new_range = (gdouble) RANGE_PREC; cur_time = -1; } valid = prev_time != -1; GST_DEBUG ("flush %d, segment %d, valid %d", flush, segment, valid); if (new_range == cur_range) return FALSE; prev_time = cur_time; prev_range = cur_range; cur_range = new_range; g_get_current_time (&tv); cur_time = GST_TIMEVAL_TO_TIME (tv); if (!valid) return FALSE; GST_DEBUG ("cur: %lf, %" GST_TIME_FORMAT, cur_range, GST_TIME_ARGS (cur_time)); GST_DEBUG ("prev: %lf, %" GST_TIME_FORMAT, prev_range, GST_TIME_ARGS (prev_time)); diff = cur_time - prev_time; GST_DEBUG ("diff: %" GST_TIME_FORMAT, GST_TIME_ARGS (diff)); start = prev_range * duration / RANGE_PREC; /* play 50 milliseconds */ stop = segment ? cur_range * duration / RANGE_PREC : duration; if (start == stop) return FALSE; if (segment) rate = (stop - start) / (gdouble) diff; else rate = cur_speed; if (start > stop) { gint64 tmp; tmp = start; start = stop; stop = tmp; } if (rate == 0.0) return TRUE; GST_DEBUG ("seek to %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT ", rate %lf" " on element %s", GST_TIME_ARGS (start), GST_TIME_ARGS (stop), rate, GST_ELEMENT_NAME (pipeline)); s_event = gst_event_new_seek (rate, GST_FORMAT_TIME, (flush ? GST_SEEK_FLAG_FLUSH : 0) | (segment ? GST_SEEK_FLAG_SEGMENT : 0), GST_SEEK_TYPE_SET, start, GST_SEEK_TYPE_SET, stop); res = gst_element_send_event (pipeline, s_event); if (!res) g_print ("seek failed\n"); gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); return TRUE; }