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