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