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