style cleanup
[blender.git] / source / blender / blenkernel / intern / writeframeserver.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Copyright (c) 2006 Peter Schlaile
19  *
20  * Contributor(s):
21  *
22  * ***** END GPL LICENSE BLOCK *****
23  */
24
25 /** \file blender/blenkernel/intern/writeframeserver.c
26  *  \ingroup bke
27  *
28  * Frameserver
29  * Makes Blender accessible from TMPGenc directly using VFAPI (you can
30  * use firefox too ;-)
31  */
32
33 #ifdef WITH_FRAMESERVER
34
35 #include <string.h>
36 #include <stdio.h>
37
38 #if defined(_WIN32)
39 #include <winsock2.h>
40 #include <windows.h>
41 #include <winbase.h>
42 #include <direct.h>
43 #else
44 #include <sys/time.h>
45 #include <sys/socket.h>
46 #include <sys/types.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 #include <net/if.h>
50 #include <netdb.h>
51 #include <sys/ioctl.h>
52 #include <errno.h>
53 #include <unistd.h>
54 #include <sys/un.h>
55 #include <fcntl.h>
56 #endif
57
58 #include <stdlib.h>
59
60 #include "DNA_userdef_types.h"
61
62 #include "BLI_utildefines.h"
63
64 #include "BKE_writeframeserver.h"
65 #include "BKE_global.h"
66 #include "BKE_report.h"
67
68 #include "DNA_scene_types.h"
69
70 static int sock;
71 static int connsock;
72 static int write_ppm;
73 static int render_width;
74 static int render_height;
75
76
77 #if defined(_WIN32)
78 static int startup_socket_system(void)
79 {
80         WSADATA wsa;
81         return (WSAStartup(MAKEWORD(2, 0), &wsa) == 0);
82 }
83
84 static void shutdown_socket_system(void)
85 {
86         WSACleanup();
87 }
88 static int select_was_interrupted_by_signal(void)
89 {
90         return (WSAGetLastError() == WSAEINTR);
91 }
92 #else
93 static int startup_socket_system(void)
94 {
95         return 1;
96 }
97
98 static void shutdown_socket_system(void)
99 {
100 }
101
102 static int select_was_interrupted_by_signal(void)
103 {
104         return (errno == EINTR);
105 }
106
107 static int closesocket(int fd)
108 {
109         return close(fd);
110 }
111 #endif
112
113 int BKE_frameserver_start(struct Scene *scene, RenderData *UNUSED(rd), int rectx, int recty, ReportList *reports)
114 {
115         struct sockaddr_in addr;
116         int arg = 1;
117         
118         (void)scene; /* unused */
119
120         if (!startup_socket_system()) {
121                 BKE_report(reports, RPT_ERROR, "Can't startup socket system");
122                 return 0;
123         }
124
125         if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
126                 shutdown_socket_system();
127                 BKE_report(reports, RPT_ERROR, "Can't open socket");
128                 return 0;
129         }
130
131         setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &arg, sizeof(arg));
132
133         addr.sin_family = AF_INET;
134         addr.sin_port = htons(U.frameserverport);
135         addr.sin_addr.s_addr = INADDR_ANY;
136
137         if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
138                 shutdown_socket_system();
139                 BKE_report(reports, RPT_ERROR, "Can't bind to socket");
140                 return 0;
141         }
142
143         if (listen(sock, SOMAXCONN) < 0) {
144                 shutdown_socket_system();
145                 BKE_report(reports, RPT_ERROR, "Can't establish listen backlog");
146                 return 0;
147         }
148         connsock = -1;
149
150         render_width = rectx;
151         render_height = recty;
152
153         return 1;
154 }
155
156 static char index_page[] =
157 "HTTP/1.1 200 OK\r\n"
158 "Content-Type: text/html\r\n"
159 "\r\n"
160 "<html><head><title>Blender Frameserver</title></head>\n"
161 "<body><pre>\n"
162 "<H2>Blender Frameserver</H2>\n"
163 "<A HREF=info.txt>Render Info</A><br>\n"
164 "<A HREF=close.txt>Stop Rendering</A><br>\n"
165 "\n"
166 "Images can be found here\n"
167 "\n"
168 "images/ppm/%d.ppm\n"
169 "\n"
170 "</pre></body></html>\n";
171
172 static char good_bye[] =
173 "HTTP/1.1 200 OK\r\n"
174 "Content-Type: text/html\r\n"
175 "\r\n"
176 "<html><head><title>Blender Frameserver</title></head>\n"
177 "<body><pre>\n"
178 "Render stopped. Goodbye</pre></body></html>";
179
180 static int safe_write(char *s, int tosend)
181 {
182         int total = tosend;
183         do {
184                 int got = send(connsock, s, tosend, 0);
185                 if (got < 0) {
186                         return got;
187                 }
188                 tosend -= got;
189                 s += got;
190         } while (tosend > 0);
191
192         return total;
193 }
194
195 static int safe_puts(char *s)
196 {
197         return safe_write(s, strlen(s));
198 }
199
200 static int handle_request(RenderData *rd, char *req)
201 {
202         char *p;
203         char *path;
204         int pathlen;
205
206         if (memcmp(req, "GET ", 4) != 0) {
207                 return -1;
208         }
209            
210         p = req + 4;
211         path = p;
212
213         while (*p != ' ' && *p) p++;
214
215         *p = 0;
216
217         if (strcmp(path, "/index.html") == 0 || strcmp(path, "/") == 0) {
218                 safe_puts(index_page);
219                 return -1;
220         }
221
222         write_ppm = 0;
223         pathlen = strlen(path);
224
225         if (pathlen > 12 && memcmp(path, "/images/ppm/", 12) == 0) {
226                 write_ppm = 1;
227                 return atoi(path + 12);
228         }
229         if (strcmp(path, "/info.txt") == 0) {
230                 char buf[4096];
231
232                 sprintf(buf,
233                         "HTTP/1.1 200 OK\r\n"
234                         "Content-Type: text/html\r\n"
235                         "\r\n"
236                         "start %d\n"
237                         "end %d\n"
238                         "width %d\n"
239                         "height %d\n"
240                         "rate %d\n"
241                         "ratescale %d\n",
242                         rd->sfra,
243                         rd->efra,
244                         render_width,
245                         render_height,
246                         rd->frs_sec,
247                         1
248                         );
249
250                 safe_puts(buf);
251                 return -1;
252         }
253         if (strcmp(path, "/close.txt") == 0) {
254                 safe_puts(good_bye);
255                 G.afbreek = 1; /* Abort render */
256                 return -1;
257         }
258         return -1;
259 }
260
261 int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports))
262 {
263         fd_set readfds;
264         struct timeval tv;
265         struct sockaddr_in addr;
266         int len, rval;
267 #ifdef FREE_WINDOWS
268         int socklen;
269 #else
270         unsigned int socklen;
271 #endif
272         char buf[4096];
273
274         if (connsock != -1) {
275                 closesocket(connsock);
276                 connsock = -1;
277         }
278
279         tv.tv_sec = 1;
280         tv.tv_usec = 0;
281
282         FD_ZERO(&readfds);
283         FD_SET(sock, &readfds);
284
285         rval = select(sock + 1, &readfds, NULL, NULL, &tv);
286         if (rval < 0) {
287                 return -1;
288         }
289
290         if (rval == 0) { /* nothing to be done */
291                 return -1;
292         }
293
294         socklen = sizeof(addr);
295
296         if ((connsock = accept(sock, (struct sockaddr *)&addr, &socklen)) < 0) {
297                 return -1;
298         }
299
300         FD_ZERO(&readfds);
301         FD_SET(connsock, &readfds);
302
303         for (;;) {
304                 /* give 10 seconds for telnet testing... */
305                 tv.tv_sec = 10;
306                 tv.tv_usec = 0;
307
308                 rval = select(connsock + 1, &readfds, NULL, NULL, &tv);
309                 if (rval > 0) {
310                         break;
311                 }
312                 else if (rval == 0) {
313                         return -1;
314                 }
315                 else if (rval < 0) {
316                         if (!select_was_interrupted_by_signal()) {
317                                 return -1;
318                         }
319                 }
320         }
321
322         len = recv(connsock, buf, sizeof(buf) - 1, 0);
323
324         if (len < 0) {
325                 return -1;
326         }
327
328         buf[len] = 0;
329
330         return handle_request(rd, buf);
331 }
332
333 static void serve_ppm(int *pixels, int rectx, int recty)
334 {
335         unsigned char *rendered_frame;
336         unsigned char *row = (unsigned char *) malloc(render_width * 3);
337         int y;
338         char header[1024];
339
340         sprintf(header,
341                 "HTTP/1.1 200 OK\r\n"
342                 "Content-Type: image/ppm\r\n"
343                 "Connection: close\r\n"
344                 "\r\n"
345                 "P6\n"
346                 "# Creator: blender frameserver v0.0.1\n"
347                 "%d %d\n"
348                 "255\n",
349                 rectx, recty);
350
351         safe_puts(header);
352
353         rendered_frame = (unsigned char *)pixels;
354
355         for (y = recty - 1; y >= 0; y--) {
356                 unsigned char *target = row;
357                 unsigned char *src = rendered_frame + rectx * 4 * y;
358                 unsigned char *end = src + rectx * 4;
359                 while (src != end) {
360                         target[2] = src[2];
361                         target[1] = src[1];
362                         target[0] = src[0];
363                         
364                         target += 3;
365                         src += 4;
366                 }
367                 safe_write((char *)row, 3 * rectx);
368         }
369         free(row);
370         closesocket(connsock);
371         connsock = -1;
372 }
373
374 int BKE_frameserver_append(RenderData *UNUSED(rd), int UNUSED(start_frame), int frame, int *pixels,
375                            int rectx, int recty, ReportList *UNUSED(reports))
376 {
377         fprintf(stderr, "Serving frame: %d\n", frame);
378         if (write_ppm) {
379                 serve_ppm(pixels, rectx, recty);
380         }
381         if (connsock != -1) {
382                 closesocket(connsock);
383                 connsock = -1;
384         }
385
386         return 1;
387 }
388
389 void BKE_frameserver_end(void)
390 {
391         if (connsock != -1) {
392                 closesocket(connsock);
393                 connsock = -1;
394         }
395         closesocket(sock);
396         shutdown_socket_system();
397 }
398
399 #endif /* WITH_FRAMESERVER */