]> git.sesse.net Git - cubemap/blobdiff - server.cpp
Use CLOCK_MONOTONIC_COARSE in httpinput.cpp; we do not need precise timing here.
[cubemap] / server.cpp
index 2f7a3327079af265a0faf2469b9b8b29c2f9a308..e99dbeaa89b311788a9c0fc4eb132a667db3f5b1 100644 (file)
@@ -317,6 +317,13 @@ void Server::set_backlog_size(int stream_index, size_t new_size)
        assert(stream_index >= 0 && stream_index < ssize_t(streams.size()));
        streams[stream_index]->set_backlog_size(new_size);
 }
+
+void Server::set_prebuffering_bytes(int stream_index, size_t new_amount)
+{
+       MutexLock lock(&mutex);
+       assert(stream_index >= 0 && stream_index < ssize_t(streams.size()));
+       streams[stream_index]->prebuffering_bytes = new_amount;
+}
        
 void Server::set_encoding(int stream_index, Stream::Encoding encoding)
 {
@@ -330,6 +337,17 @@ void Server::set_header(int stream_index, const string &http_header, const strin
        MutexLock lock(&mutex);
        assert(stream_index >= 0 && stream_index < ssize_t(streams.size()));
        streams[stream_index]->http_header = http_header;
+
+       if (stream_header != streams[stream_index]->stream_header) {
+               // We cannot start at any of the older starting points anymore,
+               // since they'd get the wrong header for the stream (not to mention
+               // that a changed header probably means the stream restarted,
+               // which means any client starting on the old one would probably
+               // stop playing properly at the change point). Next block
+               // should be a suitable starting point (if not, something is
+               // pretty strange), so it will fill up again soon enough.
+               streams[stream_index]->suitable_starting_points.clear();
+       }
        streams[stream_index]->stream_header = stream_header;
 }
        
@@ -451,43 +469,68 @@ sending_header_or_error_again:
                        return;
                }
 
-               // Start sending from the first keyframe we get. In other
-               // words, we won't send any of the backlog, but we'll start
-               // sending immediately as we get the next keyframe block.
-               // This is postcondition #3.
+               Stream *stream = client->stream;
                if (client->stream_pos == size_t(-2)) {
+                       // Start sending from the beginning of the backlog.
                        client->stream_pos = std::min<size_t>(
-                           client->stream->bytes_received - client->stream->backlog_size,
+                           stream->bytes_received - stream->backlog_size,
                            0);
                        client->state = Client::SENDING_DATA;
-               } else {
-                       // client->stream_pos should be -1, but it might not be,
-                       // if we have clients from an older version.
-                       client->stream_pos = client->stream->bytes_received;
+                       goto sending_data;
+               } else if (stream->prebuffering_bytes == 0) {
+                       // Start sending from the first keyframe we get. In other
+                       // words, we won't send any of the backlog, but we'll start
+                       // 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.
+                       client->stream_pos = stream->bytes_received;
                        client->state = Client::WAITING_FOR_KEYFRAME;
+               } else {
+                       // We're not going to send anything to the client before we have
+                       // N bytes. However, this wait might be boring; we can just as well
+                       // use it to send older data if we have it. We use lower_bound()
+                       // 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.
+                       deque<size_t>::const_iterator starting_point_it =
+                               lower_bound(stream->suitable_starting_points.begin(),
+                                           stream->suitable_starting_points.end(),
+                                           stream->bytes_received - stream->prebuffering_bytes);
+                       if (starting_point_it == stream->suitable_starting_points.end()) {
+                               // None found. Just put us at the end, and then wait for the
+                               // first keyframe to appear.
+                               client->stream_pos = stream->bytes_received;
+                               client->state = Client::WAITING_FOR_KEYFRAME;
+                       } else {
+                               client->stream_pos = *starting_point_it;
+                               client->state = Client::PREBUFFERING;
+                               goto prebuffering;
+                       }
                }
-               client->stream->put_client_to_sleep(client);
-               return;
+               // Fall through.
        }
        case Client::WAITING_FOR_KEYFRAME: {
                Stream *stream = client->stream;
-               if (ssize_t(client->stream_pos) > stream->last_suitable_starting_point) {
+               if (stream->suitable_starting_points.empty() ||
+                   client->stream_pos > stream->suitable_starting_points.back()) {
                        // We haven't received a keyframe since this stream started waiting,
                        // so keep on waiting for one.
                        // This is postcondition #3.
                        stream->put_client_to_sleep(client);
                        return;
                }
-               client->stream_pos = stream->last_suitable_starting_point;
+               client->stream_pos = stream->suitable_starting_points.back();
                client->state = Client::PREBUFFERING;
                // Fall through.
        }
        case Client::PREBUFFERING: {
+prebuffering:
                Stream *stream = client->stream;
                size_t bytes_to_send = stream->bytes_received - client->stream_pos;
                assert(bytes_to_send <= stream->backlog_size);
                if (bytes_to_send < stream->prebuffering_bytes) {
                        // We don't have enough bytes buffered to start this client yet.
+                       // This is postcondition #3.
                        stream->put_client_to_sleep(client);
                        return;
                }
@@ -495,6 +538,7 @@ sending_header_or_error_again:
                // Fall through.
        }
        case Client::SENDING_DATA: {
+sending_data:
                skip_lost_data(client);
                Stream *stream = client->stream;