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