]> git.sesse.net Git - cubemap/blob - stream.cpp
83257e9046d3a9cbbd29756f100bedaed069df45
[cubemap] / stream.cpp
1 #include <assert.h>
2 #include <errno.h>
3 #include <netinet/in.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include <string>
8 #include <vector>
9
10 #include "log.h"
11 #include "metacube.h"
12 #include "state.pb.h"
13 #include "stream.h"
14 #include "util.h"
15
16 using namespace std;
17
18 Stream::Stream(const string &stream_id, size_t backlog_size, Encoding encoding)
19         : stream_id(stream_id),
20           encoding(encoding),
21           data_fd(make_tempfile("")),
22           backlog_size(backlog_size),
23           bytes_received(0),
24           mark_pool(NULL)
25 {
26         if (data_fd == -1) {
27                 exit(1);
28         }
29 }
30
31 Stream::~Stream()
32 {
33         if (data_fd != -1) {
34                 int ret;
35                 do {
36                         ret = close(data_fd);
37                 } while (ret == -1 && errno == EINTR);
38                 if (ret == -1) {
39                         log_perror("close");
40                 }
41         }
42 }
43
44 Stream::Stream(const StreamProto &serialized, int data_fd)
45         : stream_id(serialized.stream_id()),
46           http_header(serialized.http_header()),
47           stream_header(serialized.stream_header()),
48           encoding(Stream::STREAM_ENCODING_RAW),  // Will be changed later.
49           data_fd(data_fd),
50           backlog_size(serialized.backlog_size()),
51           bytes_received(serialized.bytes_received()),
52           mark_pool(NULL)
53 {
54         if (data_fd == -1) {
55                 exit(1);
56         }
57
58         // Split old-style headers into HTTP and video headers.
59         if (!serialized.header().empty()) {
60                 string header = serialized.header();
61                 size_t split = header.find("\r\n\r\n");
62                 if (split == string::npos) {
63                         http_header = header;
64                         stream_header = "";
65                 } else {
66                         http_header = header.substr(0, split + 2);  // Split off the second \r\n.
67                         stream_header = header.substr(split, string::npos);
68                 }
69         }
70 }
71
72 StreamProto Stream::serialize()
73 {
74         StreamProto serialized;
75         serialized.set_http_header(http_header);
76         serialized.set_stream_header(stream_header);
77         serialized.add_data_fds(data_fd);
78         serialized.set_backlog_size(backlog_size);
79         serialized.set_bytes_received(bytes_received);
80         serialized.set_stream_id(stream_id);
81         data_fd = -1;
82         return serialized;
83 }
84         
85 void Stream::set_backlog_size(size_t new_size)
86 {
87         if (backlog_size == new_size) {
88                 return;
89         }
90
91         string existing_data;
92         if (!read_tempfile_and_close(data_fd, &existing_data)) {
93                 exit(1);
94         }
95
96         // Unwrap the data so it's no longer circular.
97         if (bytes_received <= backlog_size) {
98                 existing_data.resize(bytes_received);
99         } else {
100                 size_t pos = bytes_received % backlog_size;
101                 existing_data = existing_data.substr(pos, string::npos) +
102                         existing_data.substr(0, pos);
103         }
104
105         // See if we need to discard data.
106         if (new_size < existing_data.size()) {
107                 size_t to_discard = existing_data.size() - new_size;
108                 existing_data = existing_data.substr(to_discard, string::npos);
109         }
110
111         // Create a new, empty data file.
112         data_fd = make_tempfile("");
113         if (data_fd == -1) {
114                 exit(1);
115         }
116         backlog_size = new_size;
117
118         // Now cheat a bit by rewinding, and adding all the old data back.
119         bytes_received -= existing_data.size();
120         add_data(existing_data.data(), existing_data.size());
121 }
122
123 void Stream::put_client_to_sleep(Client *client)
124 {
125         sleeping_clients.push_back(client);
126 }
127
128 void Stream::add_data(const char *data, ssize_t bytes)
129 {
130         if (encoding == Stream::STREAM_ENCODING_RAW) {
131                 add_data_raw(data, bytes);
132         } else if (encoding == STREAM_ENCODING_METACUBE) {
133                 metacube_block_header hdr;
134                 memcpy(hdr.sync, METACUBE_SYNC, sizeof(hdr.sync));
135                 hdr.size = htonl(bytes);
136                 hdr.flags = htonl(0);
137
138                 char *block = new char[bytes + sizeof(hdr)];
139                 memcpy(block, &hdr, sizeof(hdr));
140                 memcpy(block + sizeof(hdr), data, bytes);
141                 add_data_raw(block, bytes + sizeof(hdr));
142                 delete[] block;
143         } else {
144                 assert(false);
145         }
146 }
147
148 void Stream::add_data_raw(const char *data, ssize_t bytes)
149 {
150         size_t pos = bytes_received % backlog_size;
151         bytes_received += bytes;
152
153         if (pos + bytes > backlog_size) {
154                 ssize_t to_copy = backlog_size - pos;
155                 while (to_copy > 0) {
156                         int ret = pwrite(data_fd, data, to_copy, pos);
157                         if (ret == -1 && errno == EINTR) {
158                                 continue;
159                         }
160                         if (ret == -1) {
161                                 log_perror("pwrite");
162                                 // Dazed and confused, but trying to continue...
163                                 break;
164                         }
165                         pos += ret;
166                         data += ret;
167                         to_copy -= ret;
168                         bytes -= ret;
169                 }
170                 pos = 0;
171         }
172
173         while (bytes > 0) {
174                 int ret = pwrite(data_fd, data, bytes, pos);
175                 if (ret == -1 && errno == EINTR) {
176                         continue;
177                 }
178                 if (ret == -1) {
179                         log_perror("pwrite");
180                         // Dazed and confused, but trying to continue...
181                         break;
182                 }
183                 pos += ret;
184                 data += ret;
185                 bytes -= ret;
186         }
187 }
188
189 void Stream::wake_up_all_clients()
190 {
191         if (to_process.empty()) {
192                 swap(sleeping_clients, to_process);
193         } else {
194                 to_process.insert(to_process.end(), sleeping_clients.begin(), sleeping_clients.end());
195                 sleeping_clients.clear();
196         }
197 }