--- libavformat/tcp.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+)
diff --git a/libavformat/tcp.c b/libavformat/tcp.c index c105479..9d4fe3d 100644 --- a/libavformat/tcp.c +++ b/libavformat/tcp.c @@ -31,11 +31,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; @@ -50,6 +54,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 }, { NULL } @@ -62,6 +67,210 @@ 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 int64_t tcp_gettime() +{ +#if defined(HAVE_PTHREAD_COND_TIMEWAIT_MONOTONIC_NP) && HAVE_PTHREAD_COND_TIMEWAIT_MONOTONIC_NP + if (av_gettime_relative_is_monotonic()) + return av_gettime_relative(); + else + return av_gettime(); +#else + return av_gettime(); +#endif +} + +static int tcp_cond_timewait(pthread_cond_t * cond, pthread_mutex_t * mutex, + const struct timespec * tv) +{ +#if defined(HAVE_PTHREAD_COND_TIMEWAIT_MONOTONIC_NP) && HAVE_PTHREAD_COND_TIMEWAIT_MONOTONIC_NP + if (av_gettime_relative_is_monotonic()) { + return pthread_cond_timedwait_monotonic_np(cond, mutex, tv); + } else { + return pthread_cond_timedwait(cond, mutex, tv); + } +#else + return pthread_cond_timedwait(cond, mutex, tv); +#endif +} + +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 = tcp_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 }; + tcp_cond_timewait(&req->cond, &req->mutex, &tv); + if (ret == 0 || ret != ETIMEDOUT) + break; + + if (ff_check_interrupt(&req->interrupt_callback)) + return AVERROR_EXIT; + + now = tcp_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; +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) { @@ -108,10 +317,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) _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel