]> git.sesse.net Git - cubemap/commitdiff
Add ?frag=header for getting a HLS fragment of the header only (useful for fMP4).
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 4 Apr 2018 20:46:20 +0000 (22:46 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Fri, 6 Apr 2018 17:13:56 +0000 (19:13 +0200)
client.h
server.cpp

index f1d14e65cf2cbc321770d069aeed8f93fc087f94..e558bd0e4e8fa47454bbb7d87bf2802a00973431 100644 (file)
--- a/client.h
+++ b/client.h
@@ -83,12 +83,18 @@ struct Client {
        // -1 means we want to send from the end of the backlog (the normal case),
        // although only at a keyframe.
        // -2 means we want to send from the _beginning_ of the backlog.
        // -1 means we want to send from the end of the backlog (the normal case),
        // although only at a keyframe.
        // -2 means we want to send from the _beginning_ of the backlog.
+       // -3 means we sent the header only.
+       static const size_t STREAM_POS_AT_END = -1;
+       static const size_t STREAM_POS_AT_START = -2;
+       static const size_t STREAM_POS_HEADER_ONLY = -3;
+
        // Once we go into WAITING_FOR_KEYFRAME, PREBUFFERING or SENDING_DATA,
        // these negative values will be translated to real numbers.
        size_t stream_pos = 0;
 
        // Position at which to end the stream (one-past-the-end, used for fragments).
        // -1 means never to end; this is the common case.
        // Once we go into WAITING_FOR_KEYFRAME, PREBUFFERING or SENDING_DATA,
        // these negative values will be translated to real numbers.
        size_t stream_pos = 0;
 
        // Position at which to end the stream (one-past-the-end, used for fragments).
        // -1 means never to end; this is the common case.
+       static const size_t STREAM_POS_NO_END = -1;
        size_t stream_pos_end = 0;
 
        // Number of bytes we've sent of data. Only relevant for SENDING_DATA.
        size_t stream_pos_end = 0;
 
        // Number of bytes we've sent of data. Only relevant for SENDING_DATA.
index e01c9f4026e3cde07b8e4181a5345f038486aae8..3107ead689e0e35b287cf9a0a9a6e65772846e01 100644 (file)
@@ -538,7 +538,7 @@ sending_header_or_short_response_again:
                }
 
                Stream *stream = client->stream;
                }
 
                Stream *stream = client->stream;
-               if (client->stream_pos == size_t(-2)) {
+               if (client->stream_pos == Client::STREAM_POS_AT_START) {
                        // Start sending from the beginning of the backlog.
                        client->stream_pos = min<size_t>(
                            stream->bytes_received - stream->backlog_size,
                        // Start sending from the beginning of the backlog.
                        client->stream_pos = min<size_t>(
                            stream->bytes_received - stream->backlog_size,
@@ -557,8 +557,8 @@ sending_header_or_short_response_again:
                        // sending immediately as we get the next keyframe block.
                        // Note that this is functionally identical to the next if branch,
                        // except that we save a binary search.
                        // sending immediately as we get the next keyframe block.
                        // Note that this is functionally identical to the next if branch,
                        // except that we save a binary search.
-                       assert(client->stream_pos == size_t(-1));
-                       assert(client->stream_pos_end == size_t(-1));
+                       assert(client->stream_pos == Client::STREAM_POS_AT_END);
+                       assert(client->stream_pos_end == Client::STREAM_POS_NO_END);
                        client->stream_pos = stream->bytes_received;
                        client->state = Client::WAITING_FOR_KEYFRAME;
                } else {
                        client->stream_pos = stream->bytes_received;
                        client->state = Client::WAITING_FOR_KEYFRAME;
                } else {
@@ -568,8 +568,8 @@ sending_header_or_short_response_again:
                        // so that we are conservative and never add extra latency over just
                        // waiting (assuming CBR or nearly so); otherwise, we could want e.g.
                        // 100 kB prebuffer but end up sending a 10 MB GOP.
                        // so that we are conservative and never add extra latency over just
                        // waiting (assuming CBR or nearly so); otherwise, we could want e.g.
                        // 100 kB prebuffer but end up sending a 10 MB GOP.
-                       assert(client->stream_pos == size_t(-1));
-                       assert(client->stream_pos_end == size_t(-1));
+                       assert(client->stream_pos == Client::STREAM_POS_AT_END);
+                       assert(client->stream_pos_end == Client::STREAM_POS_NO_END);
                        deque<size_t>::const_iterator starting_point_it =
                                lower_bound(stream->suitable_starting_points.begin(),
                                            stream->suitable_starting_points.end(),
                        deque<size_t>::const_iterator starting_point_it =
                                lower_bound(stream->suitable_starting_points.begin(),
                                            stream->suitable_starting_points.end(),
@@ -622,7 +622,7 @@ sending_data:
 
 sending_data_again:
                size_t bytes_to_send;
 
 sending_data_again:
                size_t bytes_to_send;
-               if (client->stream_pos_end == size_t(-1)) {
+               if (client->stream_pos_end == Client::STREAM_POS_NO_END) {
                         bytes_to_send = stream->bytes_received - client->stream_pos;
                } else {
                         bytes_to_send = client->stream_pos_end - client->stream_pos;
                         bytes_to_send = stream->bytes_received - client->stream_pos;
                } else {
                         bytes_to_send = client->stream_pos_end - client->stream_pos;
@@ -670,7 +670,7 @@ sending_data_again:
                client->stream_pos += ret;
                client->bytes_sent += ret;
 
                client->stream_pos += ret;
                client->bytes_sent += ret;
 
-               assert(client->stream_pos_end == size_t(-1) || client->stream_pos <= client->stream_pos_end);
+               assert(client->stream_pos_end == Client::STREAM_POS_NO_END || client->stream_pos <= client->stream_pos_end);
                if (client->stream_pos == client->stream_pos_end) {
                        goto sending_data_again;  // Will see that bytes_to_send == 0 and end.
                } else if (client->stream_pos == stream->bytes_received) {
                if (client->stream_pos == client->stream_pos_end) {
                        goto sending_data_again;  // Will see that bytes_to_send == 0 and end.
                } else if (client->stream_pos == stream->bytes_received) {
@@ -829,7 +829,7 @@ void Server::skip_lost_data(Client *client)
                client->bytes_lost += bytes_lost;
                ++client->num_loss_events;
                if (!client->close_after_response) {
                client->bytes_lost += bytes_lost;
                ++client->num_loss_events;
                if (!client->close_after_response) {
-                       assert(client->stream_pos_end != size_t(-1));
+                       assert(client->stream_pos_end != Client::STREAM_POS_NO_END);
 
                        // We've already sent a Content-length, so we can't just skip data.
                        // Close the connection immediately and hope the other side
 
                        // We've already sent a Content-length, so we can't just skip data.
                        // Close the connection immediately and hope the other side
@@ -873,35 +873,42 @@ int Server::parse_request(Client *client)
        string url = request_tokens[1];
        client->url = url;
        if (url.size() > 8 && url.find("?backlog") == url.size() - 8) {
        string url = request_tokens[1];
        client->url = url;
        if (url.size() > 8 && url.find("?backlog") == url.size() - 8) {
-               client->stream_pos = -2;
+               client->stream_pos = Client::STREAM_POS_AT_START;
                url = url.substr(0, url.size() - 8);
        } else {
                size_t pos = url.find("?frag=");
                if (pos != string::npos) {
                        // Parse an endpoint of the type /stream.mp4?frag=1234-5678.
                        const char *ptr = url.c_str() + pos + 6;
                url = url.substr(0, url.size() - 8);
        } else {
                size_t pos = url.find("?frag=");
                if (pos != string::npos) {
                        // Parse an endpoint of the type /stream.mp4?frag=1234-5678.
                        const char *ptr = url.c_str() + pos + 6;
-                       char *endptr;
-                       long long frag_start = strtol(ptr, &endptr, 10);
-                       if (ptr == endptr || frag_start < 0 || frag_start == LLONG_MAX) {
-                               return 400;  // Bad request.
-                       }
-                       if (*endptr != '-') {
-                               return 400;  // Bad request.
-                       }
-                       ptr = endptr + 1;
 
 
-                       long long frag_end = strtol(ptr, &endptr, 10);
-                       if (ptr == endptr || frag_end < frag_start || frag_end == LLONG_MAX) {
-                               return 400;  // Bad request.
-                       }
+                       // "?frag=header" is special.
+                       if (strcmp(ptr, "header") == 0) {
+                               client->stream_pos = Client::STREAM_POS_HEADER_ONLY;
+                               client->stream_pos_end = -1;
+                       } else {
+                               char *endptr;
+                               long long frag_start = strtol(ptr, &endptr, 10);
+                               if (ptr == endptr || frag_start < 0 || frag_start == LLONG_MAX) {
+                                       return 400;  // Bad request.
+                               }
+                               if (*endptr != '-') {
+                                       return 400;  // Bad request.
+                               }
+                               ptr = endptr + 1;
 
 
-                       if (*endptr != '\0') {
-                               return 400;  // Bad request.
-                       }
+                               long long frag_end = strtol(ptr, &endptr, 10);
+                               if (ptr == endptr || frag_end < frag_start || frag_end == LLONG_MAX) {
+                                       return 400;  // Bad request.
+                               }
 
 
+                               if (*endptr != '\0') {
+                                       return 400;  // Bad request.
+                               }
+
+                               client->stream_pos = frag_start;
+                               client->stream_pos_end = frag_end;
+                       }
                        url = url.substr(0, pos);
                        url = url.substr(0, pos);
-                       client->stream_pos = frag_start;
-                       client->stream_pos_end = frag_end;
                } else {
                        client->stream_pos = -1;
                        client->stream_pos_end = -1;
                } else {
                        client->stream_pos = -1;
                        client->stream_pos_end = -1;
@@ -941,7 +948,7 @@ int Server::parse_request(Client *client)
                return 503;  // Service unavailable.
        }
 
                return 503;  // Service unavailable.
        }
 
-       if (client->stream_pos_end == size_t(-1)) {
+       if (client->stream_pos_end == Client::STREAM_POS_NO_END) {
                // This stream won't end, so we don't have a content-length,
                // and can just as well tell the client it's Connection: close
                // (otherwise, we'd have to implement chunking TE for no good reason).
                // This stream won't end, so we don't have a content-length,
                // and can just as well tell the client it's Connection: close
                // (otherwise, we'd have to implement chunking TE for no good reason).
@@ -977,7 +984,11 @@ void Server::construct_header(Client *client)
 {
        Stream *stream = client->stream;
        client->header_or_short_response = stream->http_header;
 {
        Stream *stream = client->stream;
        client->header_or_short_response = stream->http_header;
-       if (client->stream_pos_end != size_t(-1)) {
+       if (client->stream_pos == Client::STREAM_POS_HEADER_ONLY) {
+               char buf[64];
+               snprintf(buf, sizeof(buf), "Content-length: %zu\r\n", stream->stream_header.size());
+               client->header_or_short_response.append(buf);
+       } else if (client->stream_pos_end != Client::STREAM_POS_NO_END) {
                char buf[64];
                snprintf(buf, sizeof(buf), "Content-length: %zu\r\n", client->stream_pos_end - client->stream_pos);
                client->header_or_short_response.append(buf);
                char buf[64];
                snprintf(buf, sizeof(buf), "Content-length: %zu\r\n", client->stream_pos_end - client->stream_pos);
                client->header_or_short_response.append(buf);
@@ -1009,12 +1020,17 @@ void Server::construct_header(Client *client)
        } else {
                assert(false);
        }
        } else {
                assert(false);
        }
-       if (client->stream_pos_end == size_t(-1)) {  // Fragments don't contain stream headers.
+       if (client->stream_pos == Client::STREAM_POS_HEADER_ONLY) {
+               client->state = Client::SENDING_SHORT_RESPONSE;
                client->header_or_short_response.append(stream->stream_header);
                client->header_or_short_response.append(stream->stream_header);
+       } else {
+               client->state = Client::SENDING_HEADER;
+               if (client->stream_pos_end == Client::STREAM_POS_NO_END) {  // Fragments don't contain stream headers.
+                       client->header_or_short_response.append(stream->stream_header);
+               }
        }
 
        // Switch states.
        }
 
        // Switch states.
-       client->state = Client::SENDING_HEADER;
        change_epoll_events(client, EPOLLOUT | EPOLLET | EPOLLRDHUP);
 }
        
        change_epoll_events(client, EPOLLOUT | EPOLLET | EPOLLRDHUP);
 }