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.
This commit is contained in:
@@ -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);
|
||||
|
||||
50
source/blender/blenkernel/BKE_writeframeserver.h
Normal file
50
source/blender/blenkernel/BKE_writeframeserver.h
Normal file
@@ -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
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
362
source/blender/blenkernel/intern/writeframeserver.c
Normal file
362
source/blender/blenkernel/intern/writeframeserver.c
Normal file
@@ -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 <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#include <winbase.h>
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/un.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#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 <config.h>
|
||||
#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"
|
||||
"<html><head><title>Blender Frameserver</title></head>\n"
|
||||
"<body><pre>\n"
|
||||
"<H2>Blender Frameserver</H2>\n"
|
||||
"<A HREF=info.txt>Render Info</A><br>\n"
|
||||
"<A HREF=close.txt>Stop Rendering</A><br>\n"
|
||||
"\n"
|
||||
"Images can be found here\n"
|
||||
"\n"
|
||||
"images/ppm/%d.ppm\n"
|
||||
"\n"
|
||||
"</pre></body></html>\n";
|
||||
|
||||
static char good_bye[]
|
||||
= "HTTP/1.1 200 OK\n"
|
||||
"Content-Type: text/html\n\n"
|
||||
"<html><head><title>Blender Frameserver</title></head>\n"
|
||||
"<body><pre>\n"
|
||||
"Render stopped. Goodbye</pre></body></html>";
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user