/** * gst_task_join: * @task: The #GstTask to join * * Joins @task. After this call, it is safe to unref the task * and clean up the lock set with gst_task_set_lock(). * * The task will automatically be stopped with this call. * * This function cannot be called from within a task function as this * would cause a deadlock. The function will detect this and print a * g_warning. * * Returns: %TRUE if the task could be joined. * * MT safe. */ gboolean gst_task_join (GstTask * task) { GThread *tself; GstTaskPrivate *priv; gpointer id; GstTaskPool *pool = NULL; priv = task->priv; g_return_val_if_fail (GST_IS_TASK (task), FALSE); tself = g_thread_self (); GST_DEBUG_OBJECT (task, "Joining task %p, thread %p", task, tself); /* we don't use a real thread join here because we are using * thread pools */ GST_OBJECT_LOCK (task); if (G_UNLIKELY (tself == task->abidata.ABI.thread)) goto joining_self; task->state = GST_TASK_STOPPED; /* signal the state change for when it was blocked in PAUSED. */ GST_TASK_SIGNAL (task); /* we set the running flag when pushing the task on the thread pool. * This means that the task function might not be called when we try * to join it here. */ while (G_LIKELY (task->running)) GST_TASK_WAIT (task); /* clean the thread */ task->abidata.ABI.thread = NULL; /* get the id and pool to join */ pool = priv->pool_id; id = priv->id; priv->pool_id = NULL; priv->id = NULL; GST_OBJECT_UNLOCK (task); if (pool) { if (id) gst_task_pool_join (pool, id); gst_object_unref (pool); } GST_DEBUG_OBJECT (task, "Joined task %p", task); return TRUE; /* ERRORS */ joining_self: { GST_WARNING_OBJECT (task, "trying to join task from its thread"); GST_OBJECT_UNLOCK (task); g_warning ("\nTrying to join task %p from its thread would deadlock.\n" "You cannot change the state of an element from its streaming\n" "thread. Use g_idle_add() or post a GstMessage on the bus to\n" "schedule the state change from the main thread.\n", task); return FALSE; } }
EXPORT_C #endif gboolean gst_task_stop (GstTask * task) { GstTaskClass *tclass; GstTaskState old; g_return_val_if_fail (GST_IS_TASK (task), FALSE); tclass = GST_TASK_GET_CLASS (task); GST_DEBUG_OBJECT (task, "Stopping task %p", task); GST_OBJECT_LOCK (task); old = task->state; task->state = GST_TASK_STOPPED; switch (old) { case GST_TASK_STOPPED: break; case GST_TASK_PAUSED: GST_TASK_SIGNAL (task); break; case GST_TASK_STARTED: break; } GST_OBJECT_UNLOCK (task); return TRUE; }
static gboolean gst_hls_demux_get_next_fragment (GstHLSDemux * demux, gboolean retry) { GstBuffer *buf; guint avail; const gchar *next_fragment_uri; GstClockTime duration; gboolean discont; if (!gst_m3u8_client_get_next_fragment (demux->client, &discont, &next_fragment_uri, &duration)) { GST_INFO_OBJECT (demux, "This playlist doesn't contain more fragments"); demux->end_of_playlist = TRUE; GST_TASK_SIGNAL (demux->task); return FALSE; } GST_INFO_OBJECT (demux, "Fetching next fragment %s", next_fragment_uri); if (!gst_hls_demux_fetch_location (demux, next_fragment_uri)) return FALSE; avail = gst_adapter_available (demux->download); buf = gst_adapter_take_buffer (demux->download, avail); GST_BUFFER_DURATION (buf) = duration; if (G_UNLIKELY (demux->input_caps == NULL)) { demux->input_caps = gst_type_find_helper_for_buffer (NULL, buf, NULL); if (demux->input_caps) { gst_pad_set_caps (demux->srcpad, demux->input_caps); GST_INFO_OBJECT (demux, "Input source caps: %" GST_PTR_FORMAT, demux->input_caps); } } if (discont) { GST_DEBUG_OBJECT (demux, "Marking fragment as discontinuous"); GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); } g_queue_push_tail (demux->queue, buf); GST_TASK_SIGNAL (demux->task); gst_adapter_clear (demux->download); return TRUE; }
static void gst_hls_demux_stop (GstHLSDemux * demux) { gst_uri_downloader_cancel (demux->downloader); if (GST_TASK_STATE (demux->updates_task) != GST_TASK_STOPPED) { demux->stop_stream_task = TRUE; gst_task_stop (demux->updates_task); GST_TASK_SIGNAL (demux->updates_task); } if (GST_TASK_STATE (demux->stream_task) != GST_TASK_STOPPED) gst_task_stop (demux->stream_task); }
/** * gst_task_set_state: * @task: a #GstTask * @state: the new task state * * Sets the state of @task to @state. * * The @task must have a lock associated with it using * gst_task_set_lock() when going to GST_TASK_STARTED or GST_TASK_PAUSED or * this function will return %FALSE. * * MT safe. * * Returns: %TRUE if the state could be changed. * * Since: 0.10.24 */ gboolean gst_task_set_state (GstTask * task, GstTaskState state) { GstTaskState old; gboolean res = TRUE; g_return_val_if_fail (GST_IS_TASK (task), FALSE); GST_DEBUG_OBJECT (task, "Changing task %p to state %d", task, state); GST_OBJECT_LOCK (task); if (state != GST_TASK_STOPPED) if (G_UNLIKELY (GST_TASK_GET_LOCK (task) == NULL)) goto no_lock; /* if the state changed, do our thing */ old = task->state; if (old != state) { task->state = state; switch (old) { case GST_TASK_STOPPED: /* If the task already has a thread scheduled we don't have to do * anything. */ if (G_UNLIKELY (!task->running)) res = start_task (task); break; case GST_TASK_PAUSED: /* when we are paused, signal to go to the new state */ GST_TASK_SIGNAL (task); break; case GST_TASK_STARTED: /* if we were started, we'll go to the new state after the next * iteration. */ break; } } GST_OBJECT_UNLOCK (task); return res; /* ERRORS */ no_lock: { GST_WARNING_OBJECT (task, "state %d set on task without a lock", state); GST_OBJECT_UNLOCK (task); g_warning ("task without a lock can't be set to state %d", state); return FALSE; } }
EXPORT_C #endif gboolean gst_task_start (GstTask * task) { GstTaskState old; g_return_val_if_fail (GST_IS_TASK (task), FALSE); GST_DEBUG_OBJECT (task, "Starting task %p", task); GST_OBJECT_LOCK (task); if (G_UNLIKELY (GST_TASK_GET_LOCK (task) == NULL)) goto no_lock; old = task->state; task->state = GST_TASK_STARTED; switch (old) { case GST_TASK_STOPPED: { GstTaskClass *tclass; /* If the task already has a thread scheduled we don't have to do * anything. */ if (task->running) break; /* new task, push on threadpool. We ref before so * that it remains alive while on the threadpool. */ gst_object_ref (task); /* mark task as running so that a join will wait until we schedule * and exit the task function. */ task->running = TRUE; tclass = GST_TASK_GET_CLASS (task); g_static_mutex_lock (&pool_lock); g_thread_pool_push (tclass->pool, task, NULL); g_static_mutex_unlock (&pool_lock); break; } case GST_TASK_PAUSED: /* PAUSE to PLAY, signal */ GST_TASK_SIGNAL (task); break; case GST_TASK_STARTED: /* was OK */ break; } GST_OBJECT_UNLOCK (task); return TRUE; /* ERRORS */ no_lock: { GST_WARNING_OBJECT (task, "starting task without a lock"); GST_OBJECT_UNLOCK (task); g_warning ("starting task without a lock"); return FALSE; } }
static void gst_task_func (GstTask * task, GstTaskClass * tclass) { GStaticRecMutex *lock; GThread *tself; tself = g_thread_self (); GST_DEBUG ("Entering task %p, thread %p", task, tself); /* we have to grab the lock to get the mutex. We also * mark our state running so that nobody can mess with * the mutex. */ GST_OBJECT_LOCK (task); if (task->state == GST_TASK_STOPPED) goto exit; lock = GST_TASK_GET_LOCK (task); if (G_UNLIKELY (lock == NULL)) goto no_lock; task->abidata.ABI.thread = tself; GST_OBJECT_UNLOCK (task); /* locking order is TASK_LOCK, LOCK */ g_static_rec_mutex_lock (lock); GST_OBJECT_LOCK (task); while (G_LIKELY (task->state != GST_TASK_STOPPED)) { while (G_UNLIKELY (task->state == GST_TASK_PAUSED)) { gint t; t = g_static_rec_mutex_unlock_full (lock); if (t <= 0) { g_warning ("wrong STREAM_LOCK count %d", t); } GST_TASK_SIGNAL (task); GST_TASK_WAIT (task); GST_OBJECT_UNLOCK (task); /* locking order.. */ if (t > 0) g_static_rec_mutex_lock_full (lock, t); GST_OBJECT_LOCK (task); if (G_UNLIKELY (task->state == GST_TASK_STOPPED)) goto done; } GST_OBJECT_UNLOCK (task); task->func (task->data); GST_OBJECT_LOCK (task); } done: GST_OBJECT_UNLOCK (task); g_static_rec_mutex_unlock (lock); GST_OBJECT_LOCK (task); task->abidata.ABI.thread = NULL; exit: /* now we allow messing with the lock again by setting the running flag to * FALSE. Together with the SIGNAL this is the sign for the _join() to * complete. * Note that we still have not dropped the final ref on the task. We could * check here if there is a pending join() going on and drop the last ref * before releasing the lock as we can be sure that a ref is held by the * caller of the join(). */ task->running = FALSE; GST_TASK_SIGNAL (task); GST_OBJECT_UNLOCK (task); GST_DEBUG ("Exit task %p, thread %p", task, g_thread_self ()); gst_object_unref (task); return; no_lock: { g_warning ("starting task without a lock"); goto exit; } }
static void gst_task_func (GstTask * task) { GStaticRecMutex *lock; GThread *tself; GstTaskPrivate *priv; priv = task->priv; tself = g_thread_self (); GST_DEBUG ("Entering task %p, thread %p", task, tself); /* we have to grab the lock to get the mutex. We also * mark our state running so that nobody can mess with * the mutex. */ GST_OBJECT_LOCK (task); if (task->state == GST_TASK_STOPPED) goto exit; lock = GST_TASK_GET_LOCK (task); if (G_UNLIKELY (lock == NULL)) goto no_lock; task->abidata.ABI.thread = tself; /* only update the priority when it was changed */ if (priv->prio_set) g_thread_set_priority (tself, priv->priority); GST_OBJECT_UNLOCK (task); /* fire the enter_thread callback when we need to */ if (priv->thr_callbacks.enter_thread) priv->thr_callbacks.enter_thread (task, tself, priv->thr_user_data); /* locking order is TASK_LOCK, LOCK */ g_static_rec_mutex_lock (lock); GST_OBJECT_LOCK (task); /* configure the thread name now */ gst_task_configure_name (task); while (G_LIKELY (task->state != GST_TASK_STOPPED)) { while (G_UNLIKELY (task->state == GST_TASK_PAUSED)) { gint t; t = g_static_rec_mutex_unlock_full (lock); if (t <= 0) { g_warning ("wrong STREAM_LOCK count %d", t); } GST_TASK_SIGNAL (task); GST_TASK_WAIT (task); GST_OBJECT_UNLOCK (task); /* locking order.. */ if (t > 0) g_static_rec_mutex_lock_full (lock, t); GST_OBJECT_LOCK (task); if (G_UNLIKELY (task->state == GST_TASK_STOPPED)) goto done; } GST_OBJECT_UNLOCK (task); task->func (task->data); GST_OBJECT_LOCK (task); } done: GST_OBJECT_UNLOCK (task); g_static_rec_mutex_unlock (lock); GST_OBJECT_LOCK (task); task->abidata.ABI.thread = NULL; exit: if (priv->thr_callbacks.leave_thread) { /* fire the leave_thread callback when we need to. We need to do this before * we signal the task and with the task lock released. */ GST_OBJECT_UNLOCK (task); priv->thr_callbacks.leave_thread (task, tself, priv->thr_user_data); GST_OBJECT_LOCK (task); } else { /* restore normal priority when releasing back into the pool, we will not * touch the priority when a custom callback has been installed. */ g_thread_set_priority (tself, G_THREAD_PRIORITY_NORMAL); } /* now we allow messing with the lock again by setting the running flag to * FALSE. Together with the SIGNAL this is the sign for the _join() to * complete. * Note that we still have not dropped the final ref on the task. We could * check here if there is a pending join() going on and drop the last ref * before releasing the lock as we can be sure that a ref is held by the * caller of the join(). */ task->running = FALSE; GST_TASK_SIGNAL (task); GST_OBJECT_UNLOCK (task); GST_DEBUG ("Exit task %p, thread %p", task, g_thread_self ()); gst_object_unref (task); return; no_lock: { g_warning ("starting task without a lock"); goto exit; } }
static gboolean gst_hls_demux_get_next_fragment (GstHLSDemux * demux, gboolean caching) { GstFragment *download; const gchar *next_fragment_uri; GstClockTime duration; GstClockTime timestamp; GstBuffer *buf; gboolean discont; if (!gst_m3u8_client_get_next_fragment (demux->client, &discont, &next_fragment_uri, &duration, ×tamp)) { GST_INFO_OBJECT (demux, "This playlist doesn't contain more fragments"); demux->end_of_playlist = TRUE; gst_task_start (demux->stream_task); return FALSE; } GST_INFO_OBJECT (demux, "Fetching next fragment %s", next_fragment_uri); download = gst_uri_downloader_fetch_uri (demux->downloader, next_fragment_uri); if (download == NULL) goto error; buf = gst_fragment_get_buffer (download); GST_BUFFER_DURATION (buf) = duration; GST_BUFFER_PTS (buf) = timestamp; /* We actually need to do this every time we switch bitrate */ if (G_UNLIKELY (demux->do_typefind)) { GstCaps *caps = gst_fragment_get_caps (download); if (!demux->input_caps || !gst_caps_is_equal (caps, demux->input_caps)) { gst_caps_replace (&demux->input_caps, caps); /* gst_pad_set_caps (demux->srcpad, demux->input_caps); */ GST_INFO_OBJECT (demux, "Input source caps: %" GST_PTR_FORMAT, demux->input_caps); demux->do_typefind = FALSE; } gst_caps_unref (caps); } else { gst_fragment_set_caps (download, demux->input_caps); } if (discont) { GST_DEBUG_OBJECT (demux, "Marking fragment as discontinuous"); GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); } g_queue_push_tail (demux->queue, download); if (!caching) { GST_TASK_SIGNAL (demux->updates_task); gst_task_start (demux->stream_task); } return TRUE; error: { gst_hls_demux_stop (demux); return FALSE; } }