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