From 1ce9e196f7c4b76da9a5cf3c789fe6ff2cada2e2 Mon Sep 17 00:00:00 2001 From: Peter Schlaile Date: Sun, 5 Feb 2006 19:12:25 +0000 Subject: Adds support for frameserver rendering to blender. This is done by integrating a mini-webserver (around 300 lines of code) into blender. Using the VFAPI-plugin in contrib/windows it enables blender to directly feed its output into TMPGEnc, a commercial high quality MPEG-Encoder. Since it is a mini-webserver, you can probably easily use it for other interfacing purposes. --- source/blender/blenkernel/BKE_writeavi.h | 1 + source/blender/blenkernel/BKE_writeframeserver.h | 50 +++ source/blender/blenkernel/SConscript | 3 + source/blender/blenkernel/intern/Makefile | 5 + source/blender/blenkernel/intern/image.c | 12 +- source/blender/blenkernel/intern/writeavi.c | 20 ++ .../blender/blenkernel/intern/writeframeserver.c | 362 +++++++++++++++++++++ 7 files changed, 448 insertions(+), 5 deletions(-) create mode 100644 source/blender/blenkernel/BKE_writeframeserver.h create mode 100644 source/blender/blenkernel/intern/writeframeserver.c (limited to 'source') diff --git a/source/blender/blenkernel/BKE_writeavi.h b/source/blender/blenkernel/BKE_writeavi.h index 43036612339..6faa5f44878 100644 --- a/source/blender/blenkernel/BKE_writeavi.h +++ b/source/blender/blenkernel/BKE_writeavi.h @@ -49,6 +49,7 @@ typedef struct bMovieHandle { void (*start_movie)(struct RenderData *rd, int rectx, int recty); void (*append_movie)(int frame, int *pixels, int rectx, int recty); void (*end_movie)(void); + int (*get_next_frame)(void); /* can be null */ } bMovieHandle; bMovieHandle *BKE_get_movie_handle(int imtype); diff --git a/source/blender/blenkernel/BKE_writeframeserver.h b/source/blender/blenkernel/BKE_writeframeserver.h new file mode 100644 index 00000000000..d8414e51f43 --- /dev/null +++ b/source/blender/blenkernel/BKE_writeframeserver.h @@ -0,0 +1,50 @@ +/** + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#ifndef BKE_WRITEFRAMESERVER_H +#define BKE_WRITEFRAMESERVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct RenderData; + +extern void start_frameserver(struct RenderData *rd, int rectx, int recty); +extern void end_frameserver(void); +extern void append_frameserver(int frame, int *pixels, int rectx, int recty); +extern int frameserver_loop(); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/source/blender/blenkernel/SConscript b/source/blender/blenkernel/SConscript index aaee2409d89..a5128a33c3e 100644 --- a/source/blender/blenkernel/SConscript +++ b/source/blender/blenkernel/SConscript @@ -20,6 +20,9 @@ if env['WITH_BF_INTERNATIONAL'] == 1: if env['WITH_BF_OPENEXR'] == 1: defs += ' WITH_OPENEXR' +if env['WITH_BF_FFMPEG'] == 1: + defs += ' WITH_FFMPEG' + if env['WITH_BF_QUICKTIME'] == 1: defs += ' WITH_QUICKTIME' incs += ' ' + env['BF_QUICKTIME_INC'] diff --git a/source/blender/blenkernel/intern/Makefile b/source/blender/blenkernel/intern/Makefile index c173051e862..2605541b942 100644 --- a/source/blender/blenkernel/intern/Makefile +++ b/source/blender/blenkernel/intern/Makefile @@ -66,6 +66,7 @@ CPPFLAGS += -I../../render/extern/include # for sound #CPPFLAGS += -I../../../kernel/gen_system +CPPFLAGS += $(NAN_SDLCFLAGS) CPPFLAGS += -I$(NAN_IKSOLVER)/include CPPFLAGS += -I$(NAN_DECIMATION)/include @@ -83,6 +84,10 @@ ifeq ($(WITH_FREETYPE2), true) CPPFLAGS += -I$(NAN_FREETYPE)/include/freetype2 endif +ifeq ($(WITH_FFMPEG),true) + CPPFLAGS += -DWITH_FFMPEG +endif + ifeq ($(WITH_OPENEXR), true) CPPFLAGS += -DWITH_OPENEXR endif diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index addb4378ce9..4a2f87cf820 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -279,11 +279,13 @@ void free_unused_animimages() int BKE_imtype_is_movie(int imtype) { switch(imtype) { - case R_MOVIE: - case R_AVIRAW: - case R_AVIJPEG: - case R_AVICODEC: - case R_QUICKTIME: + case R_MOVIE: + case R_AVIRAW: + case R_AVIJPEG: + case R_AVICODEC: + case R_QUICKTIME: + case R_FFMPEG: + case R_FRAMESERVER: return 1; } return 0; diff --git a/source/blender/blenkernel/intern/writeavi.c b/source/blender/blenkernel/intern/writeavi.c index eb559df89e7..8c39e5f46c9 100644 --- a/source/blender/blenkernel/intern/writeavi.c +++ b/source/blender/blenkernel/intern/writeavi.c @@ -57,6 +57,12 @@ #include "BIF_writeavicodec.h" #endif +#ifdef WITH_FFMPEG +#include "BKE_writeffmpeg.h" +#endif + +#include "BKE_writeframeserver.h" + bMovieHandle *BKE_get_movie_handle(int imtype) { static bMovieHandle mh; @@ -65,6 +71,7 @@ bMovieHandle *BKE_get_movie_handle(int imtype) mh.start_movie= start_avi; mh.append_movie= append_avi; mh.end_movie= end_avi; + mh.get_next_frame = 0; /* do the platform specific handles */ #ifdef __sgi @@ -86,6 +93,19 @@ bMovieHandle *BKE_get_movie_handle(int imtype) mh.end_movie= end_qt; } #endif +#ifdef WITH_FFMPEG + if (imtype == R_FFMPEG) { + mh.start_movie = start_ffmpeg; + mh.append_movie = append_ffmpeg; + mh.end_movie = end_ffmpeg; + } +#endif + if (imtype == R_FRAMESERVER) { + mh.start_movie = start_frameserver; + mh.append_movie = append_frameserver; + mh.end_movie = end_frameserver; + mh.get_next_frame = frameserver_loop; + } return &mh; } diff --git a/source/blender/blenkernel/intern/writeframeserver.c b/source/blender/blenkernel/intern/writeframeserver.c new file mode 100644 index 00000000000..75bf0750766 --- /dev/null +++ b/source/blender/blenkernel/intern/writeframeserver.c @@ -0,0 +1,362 @@ +/* + * Frameserver + * Makes Blender accessible from TMPGenc directly using VFAPI (you can + * use firefox too ;-) + * + * Copyright (c) 2006 Peter Schlaile + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + + +#include +#include + +#if defined(_WIN32) +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include + +#include "MEM_guardedalloc.h" +#include "BLI_blenlib.h" +#include "DNA_userdef_types.h" + +#include "BKE_bad_level_calls.h" +#include "BKE_global.h" + +#include "IMB_imbuf_types.h" +#include "IMB_imbuf.h" + +#include "DNA_scene_types.h" +#include "blendef.h" + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* + Big red FIXME: + + You can't simply press escape to stop the frameserver, since somehow + the escape signal handling does not work, when you wait for a connection. + + You have to point your favorite webbrowser to + + blenderserver:port + + and click on "Stop Rendering" + + It does help, if you start blender using "-p 0 0 800 600" e.g... + +*/ + +static int sock; +static int connsock; +static int write_ppm; +static int render_width; +static int render_height; + + +#if !defined(_WIN32) +static inline int closesocket(int fd) { + return close(fd); +} +#endif + +void start_frameserver(RenderData *rd, int rectx, int recty) +{ + struct sockaddr_in addr; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + G.afbreek = 1; /* Abort render */ + error("Can't open socket"); + return; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(U.frameserverport); + addr.sin_addr.s_addr = INADDR_ANY; + + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + G.afbreek = 1; /* Abort render */ + error("Can't bind to socket"); + return; + } + + if (listen(sock, SOMAXCONN) < 0) { + G.afbreek = 1; /* Abort render */ + error("Can't establish listen backlog"); + return; + } + connsock = -1; + + render_width = rectx; + render_height = recty; +} + +static char index_page[] += +"HTTP/1.1 200 OK\n" +"Content-Type: text/html\n\n" +"Blender Frameserver\n" +"
\n"
+"

Blender Frameserver

\n" +"Render Info
\n" +"Stop Rendering
\n" +"\n" +"Images can be found here\n" +"\n" +"images/ppm/%d.ppm\n" +"\n" +"
\n"; + +static char good_bye[] += "HTTP/1.1 200 OK\n" +"Content-Type: text/html\n\n" +"Blender Frameserver\n" +"
\n"
+"Render stopped. Goodbye
"; + +static int safe_write(char * s, int tosend) +{ + int total = tosend; + do { + int got = send(connsock, s, tosend, 0); + if (got < 0) { + return got; + } + tosend -= got; + s += got; + } while (tosend > 0); + + return total; +} + +static int safe_puts(char * s) +{ + return safe_write(s, strlen(s)); +} + +static int handle_request(char * req) +{ + char * p; + char * path; + + if (strlen(req) < 20) { + return -1; + } + + if (memcmp(req, "GET ", 4) != 0) { + return -1; + } + + p = req + 4; + path = p; + + while (*p != ' ' && *p) p++; + + *p = 0; + + if (strcmp(path, "/index.html") == 0 + || strcmp(path, "/") == 0) { + safe_puts(index_page); + return -1; + } + + write_ppm = 0; + + if (memcmp(path, "/images/ppm/", 12) == 0) { + write_ppm = 1; + return atoi(path + 12); + } + if (strcmp(path, "/info.txt") == 0) { + char buf[4096]; + + sprintf(buf, + "HTTP/1.1 200 OK\n" + "Content-Type: text/html\n\n" + "start %d\n" + "end %d\n" + "width %d\n" + "height %d\n" + "rate %d\n" + "ratescale %d\n", + G.scene->r.sfra, + G.scene->r.efra, + render_width, + render_height, + G.scene->r.frs_sec, + 1 + ); + + safe_puts(buf); + return -1; + } + if (strcmp(path, "/close.txt") == 0) { + safe_puts(good_bye); + G.afbreek = 1; /* Abort render */ + return -1; + } + return -1; +} + +int frameserver_loop() +{ + fd_set readfds; +#if !defined(_WIN32) + struct timeval tv; +#endif + struct sockaddr_in addr; + int len; + char buf[4096]; + int rval; + + if (connsock != -1) { + closesocket(connsock); + connsock = -1; + } + +#if !defined(_WIN32) + /* FIXME: Don't know, how to wait for socket on Windows ... */ + + tv.tv_sec = 1; + tv.tv_usec = 0; + + FD_ZERO(&readfds); + FD_SET(sock, &readfds); + + rval = select(sock + 1, &readfds, NULL, NULL, &tv); + if (rval < 0) { + return -1; + } + + if (rval == 0) { /* nothing to be done */ + return -1; + } +#endif + + len = sizeof(addr); + + if ((connsock = accept(sock, (struct sockaddr *)&addr, &len)) < 0) { + return -1; + } + +#if !defined(_WIN32) + /* FIXME: Don't know, how to wait for socket on Windows ... */ + + FD_ZERO(&readfds); + FD_SET(connsock, &readfds); + + for (;;) { + /* give 10 seconds for telnet testing... */ + tv.tv_sec = 10; + tv.tv_usec = 0; + + rval = select(connsock + 1, &readfds, NULL, NULL, &tv); + if (rval > 0) { + break; + } else if (rval == 0) { + return -1; + } else if (rval < 0) { + if (errno != EINTR) { + return -1; + } + } + } +#endif + + len = recv(connsock, buf, 4095, 0); + + if (len < 0) { + return -1; + } + + buf[len] = 0; + + return handle_request(buf); +} + +static void serve_ppm(int *pixels, int rectx, int recty) +{ + unsigned char* rendered_frame; + unsigned char* row = (unsigned char*) malloc(render_width * 3); + int y; + char header[1024]; + + sprintf(header, + "HTTP/1.1 200 OK\n" + "Content-Type: image/ppm\n" + "Connection: close\n\n" + "P6\n" + "# Creator: blender frameserver v0.0.1\n" + "%d %d\n" + "255\n", + rectx, recty); + + safe_puts(header); + + rendered_frame = pixels; + + for (y = recty - 1; y >= 0; y--) { + uint8_t* target = row; + uint8_t* src = rendered_frame + rectx * 4 * y; + uint8_t* end = src + rectx * 4; + while (src != end) { + target[2] = src[2]; + target[1] = src[1]; + target[0] = src[0]; + + target += 3; + src += 4; + } + safe_write(row, 3 * rectx); + } + free(row); + closesocket(connsock); + connsock = -1; +} + +void append_frameserver(int frame, int *pixels, int rectx, int recty) +{ + fprintf(stderr, "Serving frame: %d\n", frame); + if (write_ppm) { + serve_ppm(pixels, rectx, recty); + } + if (connsock != -1) { + closesocket(connsock); + connsock = -1; + } +} + +void end_frameserver() +{ + if (connsock != -1) { + closesocket(connsock); + connsock = -1; + } + closesocket(sock); +} + -- cgit v1.2.3