static gboolean gst_curl_base_sink_transfer_set_options_unlocked (GstCurlBaseSink * sink) { GstCurlBaseSinkClass *klass = GST_CURL_BASE_SINK_GET_CLASS (sink); CURLcode res; if (!gst_curl_base_sink_transfer_set_common_options_unlocked (sink)) { return FALSE; } /* authentication settings */ if (sink->user != NULL && strlen (sink->user)) { res = curl_easy_setopt (sink->curl, CURLOPT_USERNAME, sink->user); if (res != CURLE_OK) { sink->error = g_strdup_printf ("failed to set user name: %s", curl_easy_strerror (res)); return FALSE; } res = curl_easy_setopt (sink->curl, CURLOPT_PASSWORD, sink->passwd); if (res != CURLE_OK) { sink->error = g_strdup_printf ("failed to set password: %s", curl_easy_strerror (res)); return FALSE; } } if (klass->set_options_unlocked) { return klass->set_options_unlocked (sink); } else { return FALSE; } }
static gboolean gst_curl_base_sink_event (GstBaseSink * bsink, GstEvent * event) { GstCurlBaseSink *sink = GST_CURL_BASE_SINK (bsink); GstCurlBaseSinkClass *klass = GST_CURL_BASE_SINK_GET_CLASS (sink); switch (event->type) { case GST_EVENT_EOS: GST_DEBUG_OBJECT (sink, "received EOS"); gst_curl_base_sink_transfer_thread_close (sink); gst_curl_base_sink_wait_for_response (sink); break; case GST_EVENT_CAPS: if (klass->set_mime_type) { GstCaps *caps; gst_event_parse_caps (event, &caps); klass->set_mime_type (sink, caps); } break; default: break; } return GST_BASE_SINK_CLASS (parent_class)->event (bsink, event); }
static size_t gst_curl_base_sink_transfer_read_cb (void *curl_ptr, size_t size, size_t nmemb, void *stream) { GstCurlBaseSink *sink; GstCurlBaseSinkClass *klass; size_t max_bytes_to_send; size_t bytes_to_send; guint last_chunk = 0; sink = (GstCurlBaseSink *) stream; klass = GST_CURL_BASE_SINK_GET_CLASS (sink); max_bytes_to_send = size * nmemb; /* wait for data to come available, if new file or thread close is set * then zero will be returned to indicate end of current transfer */ GST_OBJECT_LOCK (sink); if (gst_curl_base_sink_wait_for_data_unlocked (sink) == FALSE) { if (gst_curl_base_sink_has_buffered_data_unlocked (sink) && sink->transfer_thread_close) { GST_WARNING_OBJECT (sink, "discarding render data due to thread close flag"); GST_OBJECT_UNLOCK (sink); return CURL_READFUNC_ABORT; } if (klass->flush_data_unlocked) { bytes_to_send = klass->flush_data_unlocked (sink, curl_ptr, max_bytes_to_send, sink->new_file, sink->transfer_thread_close); GST_OBJECT_UNLOCK (sink); return bytes_to_send; } GST_OBJECT_UNLOCK (sink); GST_LOG ("returning 0, no more data to send in this file"); return 0; } GST_OBJECT_UNLOCK (sink); bytes_to_send = klass->transfer_data_buffer (sink, curl_ptr, max_bytes_to_send, &last_chunk); /* the last data chunk */ if (last_chunk) { gst_curl_base_sink_data_sent_notify (sink); } return bytes_to_send; }
static gboolean gst_curl_base_sink_has_buffered_data_unlocked (GstCurlBaseSink * sink) { GstCurlBaseSinkClass *klass; gboolean res = FALSE; klass = GST_CURL_BASE_SINK_GET_CLASS (sink); if (klass->has_buffered_data_unlocked) res = klass->has_buffered_data_unlocked (sink); return res; }
static gboolean gst_curl_base_sink_transfer_set_options_unlocked (GstCurlBaseSink * sink) { gboolean res = FALSE; GstCurlBaseSinkClass *klass = GST_CURL_BASE_SINK_GET_CLASS (sink); gst_curl_base_sink_transfer_set_common_options_unlocked (sink); /* authentication settings */ if (sink->user != NULL && strlen (sink->user)) { curl_easy_setopt (sink->curl, CURLOPT_USERNAME, sink->user); curl_easy_setopt (sink->curl, CURLOPT_PASSWORD, sink->passwd); } if (klass->set_options_unlocked) { res = klass->set_options_unlocked (sink); } return res; }
static size_t gst_curl_base_sink_transfer_write_cb (void G_GNUC_UNUSED * ptr, size_t size, size_t nmemb, void G_GNUC_UNUSED * stream) { GstCurlBaseSink *sink; GstCurlBaseSinkClass *klass; size_t realsize = size * nmemb; sink = (GstCurlBaseSink *) stream; klass = GST_CURL_BASE_SINK_GET_CLASS (sink); if (klass->transfer_verify_response_code) { if (!klass->transfer_verify_response_code (sink)) { GST_DEBUG_OBJECT (sink, "response error"); GST_OBJECT_LOCK (sink); sink->flow_ret = GST_FLOW_ERROR; GST_OBJECT_UNLOCK (sink); } } GST_DEBUG ("response %s", (gchar *) ptr); return realsize; }
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 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); /* 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; }