Vlad: Home:   Demo User Thu, 24 Jul 2008 19:00:51
Home
About
    Reading
    Shooting
    Pictures
Forums
Software
    OSSWEB
    OSSMON
    Maverix
    LMBOX
    Naviserver
News
Weather

This is a little modules that makes embedding VLc with SDL applications much easier. The basic idea is to have VLc perform decoding and rendering video and pass final frames to us as RGB pictures. Our applicarton will just blit those pictures with our screen or other surfaces. Currently VLC already has SDL module but this module assume full control but we want just little embedded video window without implementing full media player functionality.

First is VLC vout module, the only thing it does is to register itself as vout plugin and take output picture parameters from our application. With parameters we pass display callback function which will be called by VLc for every frame, then it is up to us where and how we want that frame displayed.

Source code for the plugin follows below:


/*****************************************************************************
* vlc_vout_embed.c: Embedded video output display method
*****************************************************************************
* Copyright (C) 1998-2001 the VideoLAN team
* $Id: embed.c 16987 2006-10-08 12:54:12Z vlad $
*
* Authors: Vlad Seryakov 
*
* 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.
*
* 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include                                                  /* ENOMEM */
#include                                                 /* free() */
#include                                             /* strerror() */
#include 
#include 
#include 
#include 
#define MAX_DIRECTBUFFERS 3
/*****************************************************************************
* vout_sys_t: video output method descriptor
*****************************************************************************
* This structure is part of the video output thread descriptor.
* It describes the video picture properties of an output thread.
*****************************************************************************/
struct vout_sys_t
{
int changed;             // needs to be set to 0 after init
int x;                   // x position of the video picture
int y;                   // y position of the video picture
int w;                   // width of the target surface
int h;                   // height of the target surface
int bpp;                 // bits per pixel
int pitch;               // target image pitch (size of one line)
int aspect;              // final aspect ratio
unsigned int rmask;
unsigned int gmask;
unsigned int bmask;
// This routine will be called with rendered picture for actual display,
// arg is pointer to this structure which may have more fields below
// If pixels == NULL, it means end of stream
int (*callback)(void *arg, void *pixels);
};
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int  Open      (vlc_object_t *);
static void Close     (vlc_object_t *);
static int  Init      (vout_thread_t *);
static void End       (vout_thread_t *);
static void Display   (vout_thread_t *, picture_t *);
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_shortname("Embed");
set_category(CAT_VIDEO);
set_subcategory(SUBCAT_VIDEO_VOUT);
set_description(_("Embedded RGB video output"));
set_capability("video output", 200);
add_shortcut("embed");
set_callbacks(Open, Close);
var_Create(p_module->p_libvlc, "embed", VLC_VAR_MUTEX);
vlc_module_end();
/*****************************************************************************
* OpenVideo: allocate video thread output method
*****************************************************************************/
static int Open (vlc_object_t *p_this)
{
vout_thread_t *p_vout = (vout_thread_t *)p_this;
vlc_value_t lockval, val;
var_Get(p_this->p_libvlc, "embed", &lockval);
vlc_mutex_lock(lockval.p_address);
p_vout->pf_init = Init;
p_vout->pf_end = End;
p_vout->pf_manage = NULL;
p_vout->pf_render = NULL;
p_vout->pf_display = Display;
var_Get(p_vout->p_vlc, "drawable", &val);
p_vout->p_sys = (void*)val.i_int;
p_vout->p_sys->changed = 0;
vlc_mutex_unlock(lockval.p_address);
msg_Warn(p_vout, "open: %dx%d, bpp=%d, pitch=%d", 
p_vout->p_sys->w, p_vout->p_sys->h, p_vout->p_sys->bpp, p_vout->p_sys->pitch);
return VLC_SUCCESS;
}
/*****************************************************************************
* Init: initialize video thread output method
*****************************************************************************/
static int Init(vout_thread_t *p_vout)
{
int i, x, y, w, h;
picture_t *p_pic;
I_OUTPUTPICTURES = 0;
switch (p_vout->p_sys->bpp) {
case 15:
p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5');
break;
case 16:
p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6');
break;
case 24:
p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4');
break;
case 32:
p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
break;
default:
msg_Err(p_vout, "unknown screen depth %i", p_vout->p_sys->bpp);
return VLC_EGENERIC;
}
p_vout->output.i_width  = p_vout->p_sys->w;
p_vout->output.i_height = p_vout->p_sys->h;
p_vout->output.i_aspect = p_vout->render.i_aspect;
p_vout->output.i_rmask = p_vout->p_sys->rmask;
p_vout->output.i_gmask = p_vout->p_sys->gmask;
p_vout->output.i_bmask = p_vout->p_sys->bmask;
// Calculate picture size and correct aspect ratio
vout_PlacePicture(p_vout, p_vout->p_sys->w, p_vout->p_sys->h, &x, &y, &w, &h);
p_vout->fmt_out.i_x_offset = p_vout->p_sys->x = x;
p_vout->fmt_out.i_y_offset = p_vout->p_sys->y = y;
p_vout->output.i_width = p_vout->fmt_out.i_width = p_vout->p_sys->w = p_vout->fmt_out.i_visible_width = w;
p_vout->output.i_height = p_vout->fmt_out.i_height = p_vout->p_sys->h = p_vout->fmt_out.i_visible_height = h;
p_vout->output.i_aspect = p_vout->fmt_out.i_aspect = p_vout->p_sys->aspect = 
p_vout->fmt_out.i_width * VOUT_ASPECT_FACTOR / p_vout->fmt_out.i_height;
msg_Info(p_vout, "init: %dx%d, bpp=%d, pitch=%d, orig=%dx%d/%d, out=%dx%d/%d, vis=%dx%d pos=%d/%d",
p_vout->p_sys->w, p_vout->p_sys->h, p_vout->p_sys->bpp, p_vout->p_sys->pitch,
p_vout->render.i_width, p_vout->render.i_height, p_vout->render.i_aspect,
p_vout->output.i_width, p_vout->output.i_height, p_vout->output.i_aspect,
p_vout->fmt_out.i_visible_width, p_vout->fmt_out.i_visible_height,
p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset);
while (I_OUTPUTPICTURES < MAX_DIRECTBUFFERS) {
p_pic = NULL;
/* Find an empty picture slot */
for (i = 0; i < VOUT_MAX_PICTURES; i++) {
if (p_vout->p_picture[i].i_status == FREE_PICTURE) {
p_pic = p_vout->p_picture + i;
break;
}
}
if (p_pic == NULL) {
break;
}
switch (p_vout->p_sys->bpp) {
case 15:
case 16:
p_pic->p->i_pixel_pitch = 2;
break;
case 24:
case 32:
p_pic->p->i_pixel_pitch = 4;
break;
}
p_pic->p->i_pitch = p_vout->p_sys->pitch;
p_pic->p->p_pixels = malloc(p_vout->p_sys->h * p_vout->p_sys->pitch);
p_pic->p->i_lines = p_vout->p_sys->h;
p_pic->p->i_visible_lines = p_vout->p_sys->h;
p_pic->p->i_visible_pitch = p_pic->p->i_pixel_pitch * p_vout->p_sys->w;
p_pic->i_planes = 1;
p_pic->i_status = DESTROYED_PICTURE;
p_pic->i_type = DIRECT_PICTURE;
PP_OUTPUTPICTURE[I_OUTPUTPICTURES] = p_pic;
I_OUTPUTPICTURES++;
}
return VLC_SUCCESS;
}
/*****************************************************************************
* End: terminate video thread output method
*****************************************************************************/
static void End(vout_thread_t *p_vout)
{
int i;
for (i = 0; i <= I_OUTPUTPICTURES - 1; i++) {
free(PP_OUTPUTPICTURE[i]->p->p_pixels);
}
msg_Warn(p_vout, "end: %dx%d, bpp=%d, pitch=%d", 
p_vout->p_sys->w, p_vout->p_sys->h, p_vout->p_sys->bpp, p_vout->p_sys->pitch);
}
/*****************************************************************************
* CloseVideo: destroy video thread output method
*****************************************************************************/
static void Close (vlc_object_t *p_this)
{
vout_thread_t *p_vout = (vout_thread_t *)p_this;
msg_Info(p_vout, "close: %dx%d, bpp=%d, pitch=%d", 
p_vout->p_sys->w, p_vout->p_sys->h, p_vout->p_sys->bpp, p_vout->p_sys->pitch);
(*p_vout->p_sys->callback)(p_vout->p_sys, NULL);
}
/*****************************************************************************
* Display: displays previously rendered output
*****************************************************************************/
static void Display(vout_thread_t *p_vout, picture_t *p_pic)
{
switch ((*p_vout->p_sys->callback)(p_vout->p_sys, p_pic->p->p_pixels)) {
case 1:
p_vout->i_changes |= (VOUT_SIZE_CHANGE|VOUT_ASPECT_CHANGE);
p_vout->i_window_width = p_vout->p_sys->w;
p_vout->i_window_height = p_vout->p_sys->h;
msg_Info(p_vout, "changed: %dx%dx%dx%d, bpp=%d, pitch=%d, aspect=%d", 
p_vout->p_sys->x, p_vout->p_sys->y, p_vout->p_sys->w, p_vout->p_sys->h, 
p_vout->p_sys->bpp, p_vout->p_sys->pitch, p_vout->output.i_aspect);
break;
}
}

To show how this plugin can be used, small SDL example is provided as well, this is very basic video player. When it registers VLC object, it passes plugin path as current directory, this trick will allow VLC to load our new plugin without installing it into VLC system directory.

In this example we render video frames directly into screen but it is very easy to create another SDL surface and blit video frames there and then do some overlays and finaly blit everything into the screen surface. I only show how resize works by changing surface on the fly and telling VLC to re-open itself with new dimensions.


 
/*
*  Author: Vlad Seryakov vlad@mpowermedia.net
*  April 2007
*
*/
#include "SDL.h"
#include "SDL_thread.h"
#include "SDL_mutex.h"
#include "SDL_syswm.h"
#include "SDL_error.h"
#include "SDL_video.h"
#include "SDL_endian.h"
#include 
// Param structure for custom embed VLC vout module which renders RGB frames
// directly into pixel buffer
typedef struct voutEmbed {
int changed;                           // To signal changes to the window or dimensions
int x;                                 // Where to place frame
int y;
int w;                                 // Dimensions of the frame
int h;
int bpp;                               // Bits per pixel
int pitch;                             // Size of one frame line
int aspect;                            // Picture aspect
unsigned int rmask;
unsigned int gmask;
unsigned int bmask;
int (*display)(void *arg, void *pixels);
} voutEmbed;
static void setup(voutEmbed *param);
static int display(void *arg, void *pixels);
SDL_Surface *screen;
SDL_mutex *lock;
voutEmbed param;
int running = 1;
int vlc;
// Supply input parametyers to the vout module, we need to pass primary
// output picture charasteristics only, vout module will update other fields with
// calculated values
static void setup(voutEmbed *param)
{
vlc_value_t val;
SDL_LockMutex(lock);
memset(param, 0, sizeof(voutEmbed));
param->changed = 1;
param->w = screen->w;
param->h = screen->h;
param->pitch = screen->pitch;
param->bpp = screen->format->BitsPerPixel;
param->rmask = screen->format->Rmask;
param->gmask = screen->format->Gmask;
param->bmask = screen->format->Bmask;
param->display = display;
val.i_int = (int)param;
VLC_VariableSet(vlc, "drawable", val);
SDL_UnlockMutex(lock);
}
// Display callback is called by vlc vout module with ready frame
static int display(void *arg, void *pixels)
{
voutEmbed *param = (voutEmbed*)arg;
unsigned char *pos;
// We are being called on playback stop, VLC calls one last time with empty pixel buffer
if (running == 0 || pixels == NULL) {
running = 0;
return 0;
}
SDL_LockMutex(lock);
// Environment changed, send new parameters to the vout module
if (param->changed) {
param->changed = 0;
SDL_UnlockMutex(lock);
return 1;
}
// Output provided frame, make sure frame properly aligned inside the window
if (param->h <= screen->h && param->w <= screen->w) {
if (param->h < screen->h && param->y == 0) {
param->y = (screen->h - param->h) / 2;
} 
if (param->w < screen->w && param->x == 0) {
param->x = (screen->w - param->w) / 2;
}
pos = ((unsigned char*)screen->pixels) + param->y * screen->pitch + param->x * screen->format->BytesPerPixel;
SDL_LockSurface(screen);
memcpy(pos, pixels, param->h * param->pitch);
SDL_UnlockSurface(screen);
SDL_Flip(screen);
}
SDL_UnlockMutex(lock);
return 0;
}
int main(int argc, char *argv[])
{
SDL_Event ev;
SDL_SysWMinfo wm;
static char const *args[] = { argv[0], "-I", "dummy", "-v1", "--vout", "embed", "--plugin-path", "./" };
if (argc < 2) {
fprintf(stderr, "%s filename\n", argv[0]);
exit(0);
}
if (SDL_Init(SDL_INIT_NOPARACHUTE|SDL_INIT_VIDEO|SDL_INIT_EVENTTHREAD) < 0) {
fprintf(stderr, "Failed to init SDL library");
return 0;
}
// Primary screen surface
screen = SDL_SetVideoMode(640, 480, 0, SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_HWACCEL | SDL_RESIZABLE);
if (screen == NULL) {
fprintf(stderr, "Unable to initialise SDL video mode");
SDL_Quit();
return 0;
}
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
SDL_GetWMInfo(&wm);
SDL_WM_SetCaption("vlc", "vlc");
lock = SDL_CreateMutex();
// VLC player
vlc = VLC_Create();
VLC_Init(vlc, 8, (char**)args);
// Initialize vout module
setup(&param);
VLC_AddTarget(vlc, argv[1], 0, 0, PLAYLIST_APPEND, PLAYLIST_END);
VLC_Play(vlc);
// Handle events from SDL
while (running) {
if (SDL_PollEvent(&ev)) {
switch (ev.type) {
case SDL_QUIT:
running = 0;
break;
case SDL_VIDEORESIZE: {
int w = screen->w, h = screen->h;
SDL_LockMutex(lock);
screen = SDL_SetVideoMode(ev.resize.w, ev.resize.h, 16, SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_HWACCEL | SDL_RESIZABLE);
if (screen == NULL) {
screen = SDL_SetVideoMode(w, h, 16, SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_HWACCEL | SDL_RESIZABLE);
}
if (screen == NULL) {
running = 0;
}
SDL_UnlockMutex(lock);
setup(&param);
break;
}
case SDL_KEYDOWN:
switch (ev.key.keysym.sym) {
case SDLK_SPACE:
VLC_Pause(vlc);
break;
case SDLK_RIGHT:
VLC_SpeedFaster(vlc);
break;
case SDLK_LEFT:
VLC_SpeedSlower(vlc);
break;
}
break;
}
} else {
SDL_Delay(50);
}
}
VLC_CleanUp(vlc);
VLC_Destroy(vlc);
SDL_ShowCursor(1);
SDL_Quit();
return 0;
}

The whole archive with makefile and additional headers from VLC-0.8.6d is available for download, make sure VLC, SDL and other required packages are installed. Unpack the tar and type make. It will tell you what is missing.

Full sources for the plugin: Download



With ODBC driver from postgresql.org installed, it is easy to reverse engineer
database and have nice visual represantation of the database.


ftp://ftp.crystalballinc.com/pub/vlad/PostgreSQL-Visio.ppt



The reason for this testing to see how different Web frameworks behave when
used as a whole. Before i tried to test language speed and that is not correct,
because the scripting language is only one part of the infrastructure.

I used:

Naviserver/Tcl/OSSWEB
Apache/PHP/Drupal
Mongrel/Ruby/Rails
Apache/Python/Django
Zope/Python

No DB access, simulate simple public page, templating enabled, request
routing/parsing enabled if supported. no special tuning was done, i tried
to tune whatever possible if i could get it from the docs.

-----------------------------------------------------------------
For OSSWEB i used simple script which produces list with 500 entries,
template shows it with current time on the page

URL: ab -c 50 -n 5000 http://localhost/test/test.oss
Result: 320 req/sec

Template:

Date: <%=[clock format [clock seconds]]%>


@data@


View:
for { set i 0 } { $i < 500 } { incr i } { lappend data $i }

-----------------------------------------------------------------
For Drupal i used maintenance page, in this mode it just loads minimal
number of php pages and produces simple page.

URL: ab -c 50 -n 5000 http://localhost:8080/drupal/test.php
Result: 90 req/sec

View:
require_once './includes/bootstrap.inc';
drupal_maintenance_theme();
$data = "";
for ($i = 0; $i < 500; ++$i) { $data = $data . $i; }
print theme_maintenance_page($data);

-----------------------------------------------------------------
For Rails, i generated empty application by rails Test, then started
mongrel by mongrel_rails start -e production

URL: ab -c 50 -n 5000 http://localhost:3000/rails/properties/info
Result: 30 req/sec

-----------------------------------------------------------------
For Django, i installed mod_python, turned off debug and autoloading,
produces empty app which calls index method which outputs 500 number
using small template

URL: ab -c 50 -n 5000 http://localhost:8080/django/
Result: 300 req/sec

View:
def index(request):
t = loader.get_template('index.html')
data = ""
for n in xrange(500): data = data + str(n)
c = Context({ 'test_list': data })
return HttpResponse(t.render(c))

Template:
{% now "M-d-Y T" %}

{{ test_list }}

-----------------------------------------------------------------
For Zope, i hit main page of just installed new Zope instance

URL: ab -c 50 -n 5000 http://localhost:8080/
Result: 35 req/sec

------------------------------------------------------------------
Still, this is simple example but it shows how fast/slow the framework
can be in doing the most simple things, once applications will get more
complex, performance will suffer as well. Of course DB will add more delay,
but assuming that same DB will work at the same speed with all frameworks,
layers that frameworks add makes a big difference.

Other Postings:

Emdedding VLC into SDL application
Reverse engineering PostgreSQL with Visio
Benchmarking Web Frameworks
Naviserver site
Streaming with NaviServer and Flash player
Future TV service
Naviserver project
LMBOX project is out
New demo of OSSWEB