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