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