[FFmpeg-devel] [PATCH 1/1] avformat/tcp: support timeout for getaddrinfo For fixing stucking when resolving addrinfo
Xinzheng Zhang
zhangxzheng at gmail.com
Tue May 31 13:05:40 CEST 2016
From: xinzhengzhang <zhangxzheng at gmail.com>
---
libavformat/tcp.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 194 insertions(+)
diff --git a/libavformat/tcp.c b/libavformat/tcp.c
index 4ac061a..dc3e0bd 100644
--- a/libavformat/tcp.c
+++ b/libavformat/tcp.c
@@ -32,11 +32,15 @@
#if HAVE_POLL_H
#include <poll.h>
#endif
+#if HAVE_PTHREADS
+#include <pthread.h>
+#endif
typedef struct TCPContext {
const AVClass *class;
int fd;
int listen;
+ int addrinfo_timeout;
int open_timeout;
int rw_timeout;
int listen_timeout;
@@ -52,6 +56,7 @@ static const AVOption options[] = {
{ "listen", "Listen for incoming connections", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, .flags = D|E },
{ "timeout", "set timeout (in microseconds) of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
{ "listen_timeout", "Connection awaiting timeout (in milliseconds)", OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
+ { "addrinfo_timeout", "set timeout (in microseconds) for getaddrinfo()", OFFSET(addrinfo_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
{ "send_buffer_size", "Socket send buffer size (in bytes)", OFFSET(send_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
{ "recv_buffer_size", "Socket receive buffer size (in bytes)", OFFSET(recv_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
{ "ijkapplication", "AVApplicationContext", OFFSET(app_ctx), AV_OPT_TYPE_INT64, { .i64 = 0 }, INT64_MIN, INT64_MAX, .flags = D },
@@ -65,6 +70,189 @@ static const AVClass tcp_class = {
.version = LIBAVUTIL_VERSION_INT,
};
+#ifdef HAVE_PTHREADS
+
+typedef struct TCPAddrinfoRequest
+{
+ AVBufferRef *buffer;
+
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+
+ int64_t timeout; // in microseconds;
+ AVIOInterruptCB interrupt_callback;
+
+ char *hostname;
+ char *servname;
+ struct addrinfo hints;
+ struct addrinfo *res;
+
+ volatile int finished;
+ int ret;
+} TCPAddrinfoRequest;
+
+static void tcp_getaddrinfo_request_free(TCPAddrinfoRequest *req)
+{
+ freeaddrinfo(req->res);
+
+ av_freep(&req->servname);
+ av_freep(&req->hostname);
+ pthread_cond_destroy(&req->cond);
+ pthread_mutex_destroy(&req->mutex);
+ av_freep(&req);
+}
+
+static void tcp_getaddrinfo_request_free_buffer(void *opaque, uint8_t *data)
+{
+ TCPAddrinfoRequest *req = (TCPAddrinfoRequest *)opaque;
+ tcp_getaddrinfo_request_free(req);
+}
+
+static int tcp_getaddrinfo_request_create(TCPAddrinfoRequest **request,
+ const char *hostname,
+ const char *servname,
+ const struct addrinfo *hints,
+ int64_t timeout,
+ const AVIOInterruptCB *int_cb)
+{
+ TCPAddrinfoRequest *req = (TCPAddrinfoRequest *) av_mallocz(sizeof(TCPAddrinfoRequest));
+ if (!req)
+ return AVERROR(ENOMEM);
+
+ if (pthread_mutex_init(&req->mutex, NULL)) {
+ av_freep(&req);
+ return AVERROR(ENOMEM);
+ }
+
+ if (pthread_cond_init(&req->cond, NULL)) {
+ pthread_mutex_destroy(&req->mutex);
+ av_freep(&req);
+ return AVERROR(ENOMEM);
+ }
+
+ req->timeout = timeout;
+ req->interrupt_callback = *int_cb;
+
+ if (hostname && *hostname) {
+ req->hostname = av_strdup(hostname);
+ if (!req->hostname)
+ goto fail;
+ }
+
+ if (servname) {
+ req->servname = av_strdup(servname);
+ if (!req->hostname)
+ goto fail;
+ }
+
+ if (hints) {
+ req->hints.ai_family = hints->ai_family;
+ req->hints.ai_socktype = hints->ai_socktype;
+ req->hints.ai_protocol = hints->ai_protocol;
+ req->hints.ai_flags = hints->ai_flags;
+ }
+
+ req->buffer = av_buffer_create(NULL, 0, tcp_getaddrinfo_request_free_buffer, req, 0);
+ if (!req->buffer)
+ goto fail;
+
+ *request = req;
+ return 0;
+fail:
+ tcp_getaddrinfo_request_free(req);
+ return AVERROR(ENOMEM);
+}
+
+static void *tcp_getaddrinfo_worker(void *arg)
+{
+ TCPAddrinfoRequest *req = arg;
+
+ req->ret = getaddrinfo(req->hostname, req->servname, &req->hints, &req->res);
+
+ pthread_mutex_lock(&req->mutex);
+ req->finished = 1;
+ pthread_cond_signal(&req->cond);
+ pthread_mutex_unlock(&req->mutex);
+
+ av_buffer_unref(&req->buffer);
+ return NULL;
+}
+
+static int tcp_getaddrinfo_nonblock(const char *hostname, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res,
+ int64_t timeout,
+ const AVIOInterruptCB *int_cb)
+{
+ int ret;
+ int64_t start;
+ int64_t now;
+ AVBufferRef *req_ref = NULL;
+ TCPAddrinfoRequest *req = NULL;
+ pthread_t work_thread;
+
+ if (hostname && !hostname[0])
+ hostname = NULL;
+
+ if (timeout <= 0)
+ return getaddrinfo(hostname, servname, hints, res);
+
+ ret = tcp_getaddrinfo_request_create(&req, hostname, servname, hints, timeout, int_cb);
+ if (ret)
+ goto fail;
+
+ req_ref = av_buffer_ref(req->buffer);
+ if (ret)
+ goto fail;
+
+ /* FIXME: using a thread pool would be better. */
+ ret = pthread_create(&work_thread, NULL, tcp_getaddrinfo_worker, req);
+ if (ret) {
+ ret = AVERROR(ret);
+ goto fail;
+ }
+
+ pthread_detach(work_thread);
+
+ start = av_gettime();
+ now = start;
+
+ pthread_mutex_lock(&req->mutex);
+ while (!req->finished && start + timeout > now) {
+ int64_t wait_time = now + 100000;
+ struct timespec tv = { .tv_sec = wait_time / 1000000,
+ .tv_nsec = (wait_time % 1000000) * 1000 };
+#if HAVE_PTHREAD_COND_TIMEWAIT_MONOTONIC_NP
+ ret = pthread_cond_timedwait_monotonic_np(&req->cond, &req->mutex, &tv);
+else
+ ret = pthread_cond_timedwait(&req->cond, &req->mutex, &tv);
+#endif
+ if (ret == 0 || ret != ETIMEDOUT)
+ break;
+
+ if (ff_check_interrupt(&req->interrupt_callback))
+ return AVERROR_EXIT;
+
+ now = av_gettime();
+ }
+ pthread_mutex_unlock(&req->mutex);
+
+ if (!req->finished && ret != 0) {
+ ret = AVERROR(ret);
+ goto fail;
+ }
+
+ ret = req->ret;
+ *res = req->res;
+ req->res = NULL;
+ av_buffer_unref(&req_ref);
+ return ret;
+fail:
+ av_buffer_unref(&req_ref);
+ return ret;
+}
+
+#endif
+
/* return non zero if error */
static int tcp_open(URLContext *h, const char *uri, int flags)
{
@@ -111,10 +299,16 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
snprintf(portstr, sizeof(portstr), "%d", port);
if (s->listen)
hints.ai_flags |= AI_PASSIVE;
+#ifdef HAVE_PTHREADS
+ ret = tcp_getaddrinfo_nonblock(hostname, portstr, &hints, &ai, s->addrinfo_timeout, &h->interrupt_callback);
+#else
+ if (s->addrinfo_timeout > 0)
+ av_log(h, AV_LOG_WARNING, "Ignore addrinfo_timeout without pthreads support.\n")
if (!hostname[0])
ret = getaddrinfo(NULL, portstr, &hints, &ai);
else
ret = getaddrinfo(hostname, portstr, &hints, &ai);
+#endif
if (ret) {
av_log(h, AV_LOG_ERROR,
"Failed to resolve hostname %s: %s\n",
--
2.6.4 (Apple Git-63)
More information about the ffmpeg-devel
mailing list