From: Steinar H. Gunderson Date: Wed, 4 Apr 2018 20:46:20 +0000 (+0200) Subject: Add ?frag=header for getting a HLS fragment of the header only (useful for fMP4). X-Git-Tag: 1.4.0~26 X-Git-Url: https://git.sesse.net/?p=cubemap;a=commitdiff_plain;h=c039416ed5102c0c37298334bc009dc891038db9 Add ?frag=header for getting a HLS fragment of the header only (useful for fMP4). --- diff --git a/client.h b/client.h index f1d14e6..e558bd0 100644 --- 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. + // -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. + 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. diff --git a/server.cpp b/server.cpp index e01c9f4..3107ead 100644 --- a/server.cpp +++ b/server.cpp @@ -538,7 +538,7 @@ sending_header_or_short_response_again: } 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( 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. - 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 { @@ -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. - 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::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; - 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; @@ -670,7 +670,7 @@ sending_data_again: 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) { @@ -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) { - 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 @@ -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) { - 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; - 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); - client->stream_pos = frag_start; - client->stream_pos_end = frag_end; } else { client->stream_pos = -1; client->stream_pos_end = -1; @@ -941,7 +948,7 @@ int Server::parse_request(Client *client) 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). @@ -977,7 +984,11 @@ void Server::construct_header(Client *client) { 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); @@ -1009,12 +1020,17 @@ void Server::construct_header(Client *client) } 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); + } 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. - client->state = Client::SENDING_HEADER; change_epoll_events(client, EPOLLOUT | EPOLLET | EPOLLRDHUP); }