[FFmpeg-devel] [GSoC] Proof-of-concept HTTP Server
Stephan Holljes
klaxa1337 at googlemail.com
Sat Mar 21 23:00:09 CET 2015
Hi,
this is a patch for a proof-of-concept for an http server.
Usage on the server side:
ffmpeg -i test.mp3 -c copy -listen 1 -f mp3 http://0.0.0.0
Usage on the client side:
ffplay http://localhost:8080
I looked at tls.c and tcp.c and copied parts of the code.
Please comment.
Regards,
Stephan Holljes
---
libavformat/http.c | 113
++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 83 insertions(+), 30 deletions(-)
diff --git a/libavformat/http.c b/libavformat/http.c
index da3c9be..d61e4e2 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -96,8 +96,12 @@ typedef struct HTTPContext {
int send_expect_100;
char *method;
int reconnect;
+ int listen;
+ int fd;
+ int header_sent;
} HTTPContext;
+
#define OFFSET(x) offsetof(HTTPContext, x)
#define D AV_OPT_FLAG_DECODING_PARAM
#define E AV_OPT_FLAG_ENCODING_PARAM
@@ -127,6 +131,7 @@ static const AVOption options[] = {
{ "end_offset", "try to limit the request to bytes preceding this
offset", OFFSET(end_off), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D
},
{ "method", "Override the HTTP method", OFFSET(method),
AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
{ "reconnect", "auto reconnect after disconnect before EOF",
OFFSET(reconnect), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, D },
+ { "listen", "listen on HTTP", OFFSET(listen), AV_OPT_TYPE_INT, { .i64
= 0 }, 0, 1, D },
{ NULL }
};
@@ -299,8 +304,10 @@ int ff_http_averror(int status_code, int
default_averror)
static int http_open(URLContext *h, const char *uri, int flags,
AVDictionary **options)
{
+ struct addrinfo hints = { 0 }, *ai;
HTTPContext *s = h->priv_data;
- int ret;
+ int ret = -1, fd;
+ char portstr[] = "8080"; // allow non-root users for now
if( s->seekable == 1 )
h->is_streamed = 0;
@@ -320,11 +327,39 @@ static int http_open(URLContext *h, const char *uri,
int flags,
av_log(h, AV_LOG_WARNING,
"No trailing CRLF found in HTTP header.\n");
}
+ if (s->listen) {
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags |= AI_PASSIVE;
+ ret = getaddrinfo(NULL, portstr, &hints, &ai);
+ if (ret) {
+ av_log(h, AV_LOG_ERROR, "borked");
+ return AVERROR(EIO);
+ }
+ fd = ff_socket(ai->ai_family,
+ ai->ai_socktype,
+ ai->ai_protocol);
+ if (fd < 0) {
+ ret = ff_neterrno();
+ freeaddrinfo(ai);
+ return -1;
+ }
- ret = http_open_cnx(h, options);
- if (ret < 0)
- av_dict_free(&s->chained_options);
- return ret;
+ fd = ff_listen_bind(fd, ai->ai_addr, ai->ai_addrlen, -1, h);
+ if (fd < 0) {
+ freeaddrinfo(ai);
+ return fd;
+ }
+ h->is_streamed = 1;
+ s->fd = fd;
+ freeaddrinfo(ai);
+ return 0;
+ } else {
+ ret = http_open_cnx(h, options);
+ if (ret < 0)
+ av_dict_free(&s->chained_options);
+ return ret;
+ }
}
static int http_getc(HTTPContext *s)
@@ -1102,25 +1137,40 @@ static int http_write(URLContext *h, const uint8_t
*buf, int size)
char temp[11] = ""; /* 32-bit hex + CRLF + nul */
int ret;
char crlf[] = "\r\n";
+ char header[] = "HTTP 200 OK\r\n\r\n";
HTTPContext *s = h->priv_data;
+ if (!s->listen) {
+ if (!s->chunked_post) {
+ /* non-chunked data is sent without any special encoding */
+ return ffurl_write(s->hd, buf, size);
+ }
- if (!s->chunked_post) {
- /* non-chunked data is sent without any special encoding */
- return ffurl_write(s->hd, buf, size);
- }
-
- /* silently ignore zero-size data since chunk encoding that would
- * signal EOF */
- if (size > 0) {
- /* upload data using chunked encoding */
- snprintf(temp, sizeof(temp), "%x\r\n", size);
+ /* silently ignore zero-size data since chunk encoding that would
+ * signal EOF */
+ if (size > 0) {
+ /* upload data using chunked encoding */
+ snprintf(temp, sizeof(temp), "%x\r\n", size);
- if ((ret = ffurl_write(s->hd, temp, strlen(temp))) < 0 ||
- (ret = ffurl_write(s->hd, buf, size)) < 0 ||
- (ret = ffurl_write(s->hd, crlf, sizeof(crlf) - 1)) < 0)
- return ret;
+ if ((ret = ffurl_write(s->hd, temp, strlen(temp))) < 0 ||
+ (ret = ffurl_write(s->hd, buf, size)) < 0 ||
+ (ret = ffurl_write(s->hd, crlf, sizeof(crlf) - 1)) < 0)
+ return ret;
+ }
+ return size;
+ } else {
+ if (!s->header_sent) {
+ ret = send(s->fd, header, sizeof(header), MSG_NOSIGNAL);
+ s->header_sent = 1;
+ return ret < 0 ? ff_neterrno() : ret;
+ }
+ if (size > 0) {
+ ret = send(s->fd, buf, size, MSG_NOSIGNAL);
+ return ret < 0 ? ff_neterrno() : ret;
+ } else {
+ ret = -1;
+ }
+ return ret;
}
- return size;
}
static int http_shutdown(URLContext *h, int flags)
@@ -1143,20 +1193,23 @@ static int http_close(URLContext *h)
{
int ret = 0;
HTTPContext *s = h->priv_data;
-
+ if (!s->listen) {
#if CONFIG_ZLIB
- inflateEnd(&s->inflate_stream);
- av_freep(&s->inflate_buffer);
+ inflateEnd(&s->inflate_stream);
+ av_freep(&s->inflate_buffer);
#endif /* CONFIG_ZLIB */
- if (!s->end_chunked_post)
- /* Close the write direction by sending the end of chunked
encoding. */
- ret = http_shutdown(h, h->flags);
+ if (!s->end_chunked_post)
+ /* Close the write direction by sending the end of chunked
encoding. */
+ ret = http_shutdown(h, h->flags);
- if (s->hd)
- ffurl_closep(&s->hd);
- av_dict_free(&s->chained_options);
- return ret;
+ if (s->hd)
+ ffurl_closep(&s->hd);
+ av_dict_free(&s->chained_options);
+ return ret;
+ } else {
+ return shutdown(s->fd, SHUT_RDWR);
+ }
}
static int64_t http_seek_internal(URLContext *h, int64_t off, int whence,
int force_reconnect)
More information about the ffmpeg-devel
mailing list