static gpointer gst_curl_base_sink_transfer_thread_func (gpointer data) { GstCurlBaseSink *sink = (GstCurlBaseSink *) data; GstCurlBaseSinkClass *klass = GST_CURL_BASE_SINK_GET_CLASS (sink); GstFlowReturn ret; gboolean data_available; GST_LOG ("transfer thread started"); GST_OBJECT_LOCK (sink); if (!gst_curl_base_sink_transfer_setup_unlocked (sink)) { /* no need to set sink->error, as it is set by the called function */ sink->flow_ret = GST_FLOW_ERROR; goto done; } while (!sink->transfer_thread_close && sink->flow_ret == GST_FLOW_OK) { /* we are working on a new file, clearing flag and setting a new file * name */ sink->new_file = FALSE; /* wait for data to arrive for this new file, if we get a new file name * again before getting data we will simply skip transferring anything * for this file and go directly to the new file */ data_available = gst_curl_base_sink_wait_for_data_unlocked (sink); if (data_available) { if (G_UNLIKELY (!klass->set_protocol_dynamic_options_unlocked (sink))) { sink->error = g_strdup ("unexpected state"); sink->flow_ret = GST_FLOW_ERROR; goto done; } } /* stay unlocked while handling the actual transfer */ GST_OBJECT_UNLOCK (sink); if (data_available) { GST_LOG ("have data"); if (!gst_curl_base_sink_is_live (sink)) { /* prepare transfer if needed */ if (klass->prepare_transfer) { GST_OBJECT_LOCK (sink); if (!klass->prepare_transfer (sink)) { sink->flow_ret = GST_FLOW_ERROR; goto done; } GST_OBJECT_UNLOCK (sink); } GST_LOG ("adding handle"); curl_multi_add_handle (sink->multi_handle, sink->curl); } /* Start driving the transfer. */ klass->handle_transfer (sink); /* easy handle will be possibly re-used for next transfer, thus it needs * to be removed from the multi stack and re-added again */ if (!gst_curl_base_sink_is_live (sink)) { GST_LOG ("removing handle"); curl_multi_remove_handle (sink->multi_handle, sink->curl); } } else { GST_LOG ("have no data yet"); } /* lock again before looping to check the thread closed flag */ GST_OBJECT_LOCK (sink); } if (sink->is_live) { GST_LOG ("removing handle"); curl_multi_remove_handle (sink->multi_handle, sink->curl); } done: /* extract the error code so the lock does not have to be * taken when calling the functions below that take the lock * on their own */ ret = sink->flow_ret; GST_OBJECT_UNLOCK (sink); /* if there is a flow error, always notify the render function so it * can return the flow error up along the pipeline. as an error has * occurred there is no response to receive, so notify the event function * so it doesn't block indefinitely waiting for a response. */ if (ret != GST_FLOW_OK) { gst_curl_base_sink_data_sent_notify (sink); gst_curl_base_sink_got_response_notify (sink); } GST_DEBUG ("exit thread func - transfer thread close flag: %d", sink->transfer_thread_close); return NULL; }
static void handle_transfer (GstCurlBaseSink * sink) { GstCurlBaseSinkClass *klass = GST_CURL_BASE_SINK_GET_CLASS (sink); gint retval; gint activated_fds; gint running_handles; gint timeout; CURLMcode m_code; CURLcode e_code; GST_OBJECT_LOCK (sink); timeout = sink->timeout; GST_OBJECT_UNLOCK (sink); GST_DEBUG_OBJECT (sink, "handling transfers"); /* Receiving CURLM_CALL_MULTI_PERFORM means that libcurl may have more data available to send or receive - call simply curl_multi_perform before poll() on more actions */ do { m_code = curl_multi_perform (sink->multi_handle, &running_handles); } while (m_code == CURLM_CALL_MULTI_PERFORM); GST_DEBUG_OBJECT (sink, "running handles: %d", running_handles); while (running_handles && (m_code == CURLM_OK)) { if (klass->transfer_prepare_poll_wait) { klass->transfer_prepare_poll_wait (sink); } activated_fds = gst_poll_wait (sink->fdset, timeout * GST_SECOND); if (G_UNLIKELY (activated_fds == -1)) { if (errno == EAGAIN || errno == EINTR) { GST_DEBUG_OBJECT (sink, "interrupted by signal"); } else if (errno == EBUSY) { GST_DEBUG_OBJECT (sink, "poll stopped"); retval = GST_FLOW_EOS; GST_OBJECT_LOCK (sink); if (gst_curl_base_sink_has_buffered_data_unlocked (sink)) GST_WARNING_OBJECT (sink, "discarding render data due to thread close flag"); GST_OBJECT_UNLOCK (sink); goto fail; } else { sink->error = g_strdup_printf ("poll failed: %s", g_strerror (errno)); retval = GST_FLOW_ERROR; goto fail; } } else if (G_UNLIKELY (activated_fds == 0)) { sink->error = g_strdup_printf ("poll timed out after %" GST_TIME_FORMAT, GST_TIME_ARGS (timeout * GST_SECOND)); retval = GST_FLOW_ERROR; goto fail; } /* readable/writable sockets */ do { m_code = curl_multi_perform (sink->multi_handle, &running_handles); } while (m_code == CURLM_CALL_MULTI_PERFORM); GST_DEBUG_OBJECT (sink, "running handles: %d", running_handles); } if (m_code != CURLM_OK) { sink->error = g_strdup_printf ("failed to write data: %s", curl_multi_strerror (m_code)); retval = GST_FLOW_ERROR; goto fail; } /* problems still might have occurred on individual transfers even when * curl_multi_perform returns CURLM_OK */ if ((e_code = gst_curl_base_sink_transfer_check (sink)) != CURLE_OK) { sink->error = g_strdup_printf ("failed to transfer data: %s", curl_easy_strerror (e_code)); retval = GST_FLOW_ERROR; goto fail; } gst_curl_base_sink_got_response_notify (sink); GST_OBJECT_LOCK (sink); if (sink->socket_type == CURLSOCKTYPE_ACCEPT) { /* FIXME: remove this again once we can depend on libcurl > 7.44.0, * see https://github.com/bagder/curl/issues/405. */ if (G_UNLIKELY (sink->fd.fd < 0)) { sink->error = g_strdup_printf ("unknown error"); retval = GST_FLOW_ERROR; GST_OBJECT_UNLOCK (sink); goto fail; } if (!gst_poll_remove_fd (sink->fdset, &sink->fd)) { sink->error = g_strdup_printf ("failed to remove fd"); retval = GST_FLOW_ERROR; GST_OBJECT_UNLOCK (sink); goto fail; } sink->fd.fd = -1; } GST_OBJECT_UNLOCK (sink); return; fail: GST_OBJECT_LOCK (sink); if (sink->flow_ret == GST_FLOW_OK) { sink->flow_ret = retval; } GST_OBJECT_UNLOCK (sink); return; }
static void handle_transfer (GstCurlBaseSink * sink) { GstCurlBaseSinkClass *klass = GST_CURL_BASE_SINK_GET_CLASS (sink); gint retval; gint activated_fds; gint running_handles; gint timeout; CURLMcode m_code; CURLcode e_code; GST_OBJECT_LOCK (sink); timeout = sink->timeout; GST_OBJECT_UNLOCK (sink); /* Receiving CURLM_CALL_MULTI_PERFORM means that libcurl may have more data available to send or receive - call simply curl_multi_perform before poll() on more actions */ do { m_code = curl_multi_perform (sink->multi_handle, &running_handles); } while (m_code == CURLM_CALL_MULTI_PERFORM); while (running_handles && (m_code == CURLM_OK)) { if (klass->transfer_prepare_poll_wait) { klass->transfer_prepare_poll_wait (sink); } activated_fds = gst_poll_wait (sink->fdset, timeout * GST_SECOND); if (G_UNLIKELY (activated_fds == -1)) { if (errno == EAGAIN || errno == EINTR) { GST_DEBUG_OBJECT (sink, "interrupted by signal"); } else if (errno == EBUSY) { GST_DEBUG_OBJECT (sink, "poll stopped"); retval = GST_FLOW_EOS; goto fail; } else { GST_DEBUG_OBJECT (sink, "poll failed: %s", g_strerror (errno)); GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, ("poll failed"), (NULL)); retval = GST_FLOW_ERROR; goto fail; } } else if (G_UNLIKELY (activated_fds == 0)) { GST_DEBUG_OBJECT (sink, "poll timed out"); GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, ("poll timed out"), (NULL)); retval = GST_FLOW_ERROR; goto fail; } /* readable/writable sockets */ do { m_code = curl_multi_perform (sink->multi_handle, &running_handles); } while (m_code == CURLM_CALL_MULTI_PERFORM); } if (m_code != CURLM_OK) { GST_DEBUG_OBJECT (sink, "curl multi error"); GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, ("%s", curl_multi_strerror (m_code)), (NULL)); retval = GST_FLOW_ERROR; goto fail; } /* problems still might have occurred on individual transfers even when * curl_multi_perform returns CURLM_OK */ if ((e_code = gst_curl_base_sink_transfer_check (sink)) != CURLE_OK) { GST_DEBUG_OBJECT (sink, "curl easy error"); GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, ("%s", curl_easy_strerror (e_code)), (NULL)); retval = GST_FLOW_ERROR; goto fail; } gst_curl_base_sink_got_response_notify (sink); return; fail: GST_OBJECT_LOCK (sink); if (sink->flow_ret == GST_FLOW_OK) { sink->flow_ret = retval; } GST_OBJECT_UNLOCK (sink); return; }