[Mlt-devel] SF.net SVN: mlt: [987] trunk/mlt/src/modules
Brought to you by:
ddennedy,
lilo_booter
From: <dde...@us...> - 2007-06-10 21:34:59
|
Revision: 987 http://mlt.svn.sourceforge.net/mlt/?rev=987&view=rev Author: ddennedy Date: 2007-06-10 14:34:57 -0700 (Sun, 10 Jun 2007) Log Message: ----------- added effectv module with BurningTV filter provided by Stephane Fillod Added Paths: ----------- trunk/mlt/src/modules/effectv/ trunk/mlt/src/modules/effectv/Makefile trunk/mlt/src/modules/effectv/configure trunk/mlt/src/modules/effectv/factory.c trunk/mlt/src/modules/effectv/filter_burn.c trunk/mlt/src/modules/effectv/filter_burn.h trunk/mlt/src/modules/effectv/gpl trunk/mlt/src/modules/effectv/image.c trunk/mlt/src/modules/effectv/utils.c trunk/mlt/src/modules/effectv/utils.h Added: trunk/mlt/src/modules/effectv/Makefile =================================================================== --- trunk/mlt/src/modules/effectv/Makefile (rev 0) +++ trunk/mlt/src/modules/effectv/Makefile 2007-06-10 21:34:57 UTC (rev 987) @@ -0,0 +1,35 @@ +include ../../../config.mak + +TARGET = ../libmlteffectv$(LIBSUF) + +OBJS = factory.o \ + filter_burn.o \ + image.o \ + utils.o + +CFLAGS += -I../.. + +LDFLAGS+=-L../../framework -lmlt + +SRCS := $(OBJS:.o=.c) + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS) + +depend: $(SRCS) + $(CC) -MM $(CFLAGS) $^ 1>.depend + +distclean: clean + rm -f .depend + +clean: + rm -f $(OBJS) $(TARGET) + +install: all + install -m 755 $(TARGET) "$(DESTDIR)$(prefix)/share/mlt/modules" + +ifneq ($(wildcard .depend),) +include .depend +endif Added: trunk/mlt/src/modules/effectv/configure =================================================================== --- trunk/mlt/src/modules/effectv/configure (rev 0) +++ trunk/mlt/src/modules/effectv/configure 2007-06-10 21:34:57 UTC (rev 987) @@ -0,0 +1,19 @@ +#!/bin/sh + +if [ "$help" != "1" ] +then + +cat << EOF >> ../producers.dat +EOF + +cat << EOF >> ../filters.dat +BurningTV libmlteffectv$LIBSUF +EOF + +cat << EOF >> ../transitions.dat +EOF + +cat << EOF >> ../consumers.dat +EOF + +fi Property changes on: trunk/mlt/src/modules/effectv/configure ___________________________________________________________________ Name: svn:executable + * Added: trunk/mlt/src/modules/effectv/factory.c =================================================================== --- trunk/mlt/src/modules/effectv/factory.c (rev 0) +++ trunk/mlt/src/modules/effectv/factory.c 2007-06-10 21:34:57 UTC (rev 987) @@ -0,0 +1,44 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2007 Stephane Fillod + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <string.h> + +#include "filter_burn.h" + +void *mlt_create_producer( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_filter( char *id, void *arg ) +{ + if ( !strcmp( id, "BurningTV" ) ) + return filter_burn_init( arg ); + return NULL; +} + +void *mlt_create_transition( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_consumer( char *id, void *arg ) +{ + return NULL; +} Added: trunk/mlt/src/modules/effectv/filter_burn.c =================================================================== --- trunk/mlt/src/modules/effectv/filter_burn.c (rev 0) +++ trunk/mlt/src/modules/effectv/filter_burn.c 2007-06-10 21:34:57 UTC (rev 987) @@ -0,0 +1,214 @@ +/* + * filter_burn.c -- burning filter + * Copyright (C) 2007 Stephane Fillod + * + * Filter taken from EffecTV - Realtime Digital Video Effector + * Copyright (C) 2001-2006 FUKUCHI Kentaro + * + * BurningTV - burns incoming objects. + * Copyright (C) 2001-2002 FUKUCHI Kentaro + * + * Fire routine is taken from Frank Jan Sorensen's demo program. + * + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "filter_burn.h" + +#include <framework/mlt_frame.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include "utils.h" + + +#define MaxColor 120 +#define Decay 15 +#define MAGIC_THRESHOLD "50" + +static RGB32 palette[256]; + +/* FIXME: endianess? */ +static void makePalette(void) +{ + int i, r, g, b; + + for(i=0; i<MaxColor; i++) { + HSItoRGB(4.6-1.5*i/MaxColor, (double)i/MaxColor, (double)i/MaxColor, &r, &g, &b); + palette[i] = ((r<<16)|(g<<8)|b) & 0xfefeff; + } + for(i=MaxColor; i<256; i++) { + if(r<255)r++;if(r<255)r++;if(r<255)r++; + if(g<255)g++; + if(g<255)g++; + if(b<255)b++; + if(b<255)b++; + palette[i] = ((r<<16)|(g<<8)|b) & 0xfefeff; + } +} + +static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + RGB32 *background; + unsigned char *diff; + unsigned char *buffer; + + // Get the filter + mlt_filter filter = mlt_frame_pop_service( this ); + + // Get the image + int error = mlt_frame_get_image( this, image, format, width, height, 1 ); + + // Only process if we have no error and a valid colour space + if ( error == 0 && *format == mlt_image_yuv422 ) + { + // Get the "Burn the foreground" value + int burn_foreground = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "foreground" ); + int y_threshold = image_set_threshold_y( + mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "threshold" )); + + // We'll process pixel by pixel + int x = 0; + int y = 0; + int i; + + int video_width = *width; + int video_height = *height; + int video_area = video_width * video_height; + // We need to create a new frame as this effect modifies the input + RGB32 *dest = mlt_pool_alloc( video_area * sizeof(RGB32) ); + RGB32 *src = (RGB32*)dest; + + unsigned char v, w; + RGB32 a, b; + + mlt_convert_yuv422_to_rgb24a(*image, (uint8_t *)dest, video_area); + + + diff = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), + "_diff", NULL ); + if (diff == NULL) + { + diff = mlt_pool_alloc(video_area*sizeof(unsigned char)); + mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "_diff", + diff, video_area*sizeof(unsigned char), mlt_pool_release, NULL ); + } + + buffer = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), + "_buffer", NULL ); + if (buffer == NULL) + { + buffer = mlt_pool_alloc(video_area*sizeof(unsigned char)); + memset(buffer, 0, video_area*sizeof(unsigned char)); + mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "_buffer", + buffer, video_area*sizeof(unsigned char), mlt_pool_release, NULL ); + } + + + if (burn_foreground == 1) { + /* to burn the foreground, we need a background */ + background = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), + "_background", NULL ); + if (background == NULL) + { + background = mlt_pool_alloc(video_area*sizeof(RGB32)); + image_bgset_y(background, src, video_area, y_threshold); + mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "_background", + background, video_area*sizeof(RGB32), mlt_pool_release, NULL ); + } + } + + if (burn_foreground == 1) { + image_bgsubtract_y(diff, background, src, video_area, y_threshold); + } else { + /* default */ + image_y_over(diff, src, video_area, y_threshold); + } + + for(x=1; x<video_width-1; x++) { + v = 0; + for(y=0; y<video_height-1; y++) { + w = diff[y*video_width+x]; + buffer[y*video_width+x] |= v ^ w; + v = w; + } + } + for(x=1; x<video_width-1; x++) { + i = video_width + x; + for(y=1; y<video_height; y++) { + v = buffer[i]; + if(v<Decay) + buffer[i-video_width] = 0; + else + buffer[i-video_width+fastrand()%3-1] = v - (fastrand()&Decay); + i += video_width; + } + } + + i = 1; + for(y=0; y<video_height; y++) { + for(x=1; x<video_width-1; x++) { + /* FIXME: endianess? */ + a = (src[i] & 0xfefeff) + palette[buffer[i]]; + b = a & 0x1010100; + dest[i] = a | (b - (b >> 8)); + i++; + } + i += 2; + } + + mlt_convert_rgb24a_to_yuv422((uint8_t *)dest, *width, *height, *width * sizeof(RGB32), + *image, NULL ); + + mlt_pool_release(dest); + } + + return error; +} + +/** Filter processing. +*/ + +static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +{ + // Push the frame filter + mlt_frame_push_service( frame, this ); + mlt_frame_push_get_image( frame, filter_get_image ); + + return frame; +} + +/** Constructor for the filter. +*/ + +mlt_filter filter_burn_init( char *arg ) +{ + mlt_filter this = mlt_filter_new( ); + if ( this != NULL ) + { + this->process = filter_process; + mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "foreground", "0" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "threshold", MAGIC_THRESHOLD ); + } + if (!palette[128]) + { + makePalette(); + } + return this; +} + Added: trunk/mlt/src/modules/effectv/filter_burn.h =================================================================== --- trunk/mlt/src/modules/effectv/filter_burn.h (rev 0) +++ trunk/mlt/src/modules/effectv/filter_burn.h 2007-06-10 21:34:57 UTC (rev 987) @@ -0,0 +1,27 @@ +/* + * filter_brun.h -- burning filter + * Copyright (C) 2007 Stephane Fillod + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _FILTER_BURN_H_ +#define _FILTER_BURN_H_ + +#include <framework/mlt_filter.h> + +extern mlt_filter filter_burn_init( char *arg ); + +#endif Added: trunk/mlt/src/modules/effectv/gpl =================================================================== Added: trunk/mlt/src/modules/effectv/image.c =================================================================== --- trunk/mlt/src/modules/effectv/image.c (rev 0) +++ trunk/mlt/src/modules/effectv/image.c 2007-06-10 21:34:57 UTC (rev 987) @@ -0,0 +1,307 @@ +/* + * EffecTV - Realtime Digital Video Effector + * Copyright (C) 2001-2006 FUKUCHI Kentaro + * + * image.c: utilities for image processing. + * + */ + +#include <string.h> +#include <stdlib.h> +#include "utils.h" + + +/* + * Collection of background subtraction functions + */ + +/* checks only fake-Y value */ +/* In these function Y value is treated as R*2+G*4+B. */ + +int image_set_threshold_y(int threshold) +{ + int y_threshold = threshold * 7; /* fake-Y value is timed by 7 */ + + return y_threshold; +} + +void image_bgset_y(RGB32 *background, const RGB32 *src, int video_area, int y_threshold) +{ + int i; + int R, G, B; + const RGB32 *p; + short *q; + + p = src; + q = (short *)background; + for(i=0; i<video_area; i++) { + /* FIXME: endianess */ + + R = ((*p)&0xff0000)>>(16-1); + G = ((*p)&0xff00)>>(8-2); + B = (*p)&0xff; + *q = (short)(R + G + B); + p++; + q++; + } +} + +void image_bgsubtract_y(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, int y_threshold) +{ + int i; + int R, G, B; + const RGB32 *p; + short *q; + unsigned char *r; + int v; + + p = src; + q = (short *)background; + r = diff; + for(i=0; i<video_area; i++) { + /* FIXME: endianess */ + + R = ((*p)&0xff0000)>>(16-1); + G = ((*p)&0xff00)>>(8-2); + B = (*p)&0xff; + v = (R + G + B) - (int)(*q); + *r = ((v + y_threshold)>>24) | ((y_threshold - v)>>24); + + p++; + q++; + r++; + } + +/* The origin of subtraction function is; + * diff(src, dest) = (abs(src - dest) > threshold) ? 0xff : 0; + * + * This functions is transformed to; + * (threshold > (src - dest) > -threshold) ? 0 : 0xff; + * + * (v + threshold)>>24 is 0xff when v is less than -threshold. + * (v - threshold)>>24 is 0xff when v is less than threshold. + * So, ((v + threshold)>>24) | ((threshold - v)>>24) will become 0xff when + * abs(src - dest) > threshold. + */ +} + +/* Background image is refreshed every frame */ +void image_bgsubtract_update_y(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, int y_threshold) +{ + int i; + int R, G, B; + const RGB32 *p; + short *q; + unsigned char *r; + int v; + + p = src; + q = (short *)background; + r = diff; + for(i=0; i<video_area; i++) { + /* FIXME: endianess */ + + R = ((*p)&0xff0000)>>(16-1); + G = ((*p)&0xff00)>>(8-2); + B = (*p)&0xff; + v = (R + G + B) - (int)(*q); + *q = (short)(R + G + B); + *r = ((v + y_threshold)>>24) | ((y_threshold - v)>>24); + + p++; + q++; + r++; + } +} + +/* checks each RGB value */ + +/* The range of r, g, b are [0..7] */ +RGB32 image_set_threshold_RGB(int r, int g, int b) +{ + unsigned char R, G, B; + RGB32 rgb_threshold; + + R = G = B = 0xff; + R = R<<r; + G = G<<g; + B = B<<b; + rgb_threshold = (RGB32)(R<<16 | G<<8 | B); + + return rgb_threshold; +} + +void image_bgset_RGB(RGB32 *background, const RGB32 *src, int video_area) +{ + int i; + RGB32 *p; + + p = background; + for(i=0; i<video_area; i++) { + *p++ = (*src++) & 0xfefefe; + } +} + +void image_bgsubtract_RGB(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold) +{ + int i; + const RGB32 *p, *q; + unsigned a, b; + unsigned char *r; + + p = src; + q = background; + r = diff; + for(i=0; i<video_area; i++) { + a = (*p++)|0x1010100; + b = *q++; + a = a - b; + b = a & 0x1010100; + b = b - (b>>8); + b = b ^ 0xffffff; + a = a ^ b; + a = a & rgb_threshold; + *r++ = (0 - a)>>24; + } +} + +void image_bgsubtract_update_RGB(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold) +{ + int i; + const RGB32 *p; + RGB32 *q; + unsigned a, b; + unsigned char *r; + + p = src; + q = background; + r = diff; + for(i=0; i<video_area; i++) { + a = *p|0x1010100; + b = *q&0xfefefe; + *q++ = *p++; + a = a - b; + b = a & 0x1010100; + b = b - (b>>8); + b = b ^ 0xffffff; + a = a ^ b; + a = a & rgb_threshold; + *r++ = (0 - a)>>24; + } +} + +/* noise filter for subtracted image. */ +void image_diff_filter(unsigned char *diff2, const unsigned char *diff, int width, int height) +{ + int x, y; + const unsigned char *src; + unsigned char *dest; + unsigned int count; + unsigned int sum1, sum2, sum3; + + src = diff; + dest = diff2 + width +1; + for(y=1; y<height-1; y++) { + sum1 = src[0] + src[width] + src[width*2]; + sum2 = src[1] + src[width+1] + src[width*2+1]; + src += 2; + for(x=1; x<width-1; x++) { + sum3 = src[0] + src[width] + src[width*2]; + count = sum1 + sum2 + sum3; + sum1 = sum2; + sum2 = sum3; + *dest++ = (0xff*3 - count)>>24; + src++; + } + dest += 2; + } +} + +/* Y value filters */ +void image_y_over(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold) +{ + int i; + int R, G, B, v; + unsigned char *p = diff; + + for(i = video_area; i>0; i--) { + R = ((*src)&0xff0000)>>(16-1); + G = ((*src)&0xff00)>>(8-2); + B = (*src)&0xff; + v = y_threshold - (R + G + B); + *p = (unsigned char)(v>>24); + src++; + p++; + } +} + +void image_y_under(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold) +{ + int i; + int R, G, B, v; + unsigned char *p = diff; + + for(i = video_area; i>0; i--) { + R = ((*src)&0xff0000)>>(16-1); + G = ((*src)&0xff00)>>(8-2); + B = (*src)&0xff; + v = (R + G + B) - y_threshold; + *p = (unsigned char)(v>>24); + src++; + p++; + } +} + +/* tiny edge detection */ +void image_edge(unsigned char *diff2, const RGB32 *src, int width, int height, int y_threshold) +{ + int x, y; + unsigned char *p, *q; + int r, g, b; + int ar, ag, ab; + int w; + + p = (unsigned char *)src; + q = diff2; + w = width * sizeof(RGB32); + + for(y=0; y<height - 1; y++) { + for(x=0; x<width - 1; x++) { + b = p[0]; + g = p[1]; + r = p[2]; + ab = abs(b - p[4]); + ag = abs(g - p[5]); + ar = abs(r - p[6]); + ab += abs(b - p[w]); + ag += abs(g - p[w+1]); + ar += abs(r - p[w+2]); + b = ab+ag+ar; + if(b > y_threshold) { + *q = 255; + } else { + *q = 0; + } + q++; + p += 4; + } + p += 4; + *q++ = 0; + } + memset(q, 0, width); +} + +/* horizontal flipping */ +void image_hflip(const RGB32 *src, RGB32 *dest, int width, int height) +{ + int x, y; + + src += width - 1; + for(y=0; y<height; y++) { + for(x=0; x<width; x++) { + *dest++ = *src--; + } + src += width * 2; + } +} + Added: trunk/mlt/src/modules/effectv/utils.c =================================================================== --- trunk/mlt/src/modules/effectv/utils.c (rev 0) +++ trunk/mlt/src/modules/effectv/utils.c 2007-06-10 21:34:57 UTC (rev 987) @@ -0,0 +1,55 @@ +/* + * EffecTV - Realtime Digital Video Effector + * Copyright (C) 2001-2006 FUKUCHI Kentaro + * + * utils.c: utilities + * + */ + +#include <math.h> +#include "utils.h" + +/* + * HSI color system utilities + */ +static int itrunc(double f) +{ + int i; + + i=(int)f; + if(i<0)i=0; + if(i>255)i=255; + return i; +} + +void HSItoRGB(double H, double S, double I, int *r, int *g, int *b) +{ + double T,Rv,Gv,Bv; + + Rv=1+S*sin(H-2*M_PI/3); + Gv=1+S*sin(H); + Bv=1+S*sin(H+2*M_PI/3); + T=255.999*I/2; + *r=itrunc(Rv*T); + *g=itrunc(Gv*T); + *b=itrunc(Bv*T); +} + +/* + * fastrand - fast fake random number generator + * Warning: The low-order bits of numbers generated by fastrand() + * are bad as random numbers. For example, fastrand()%4 + * generates 1,2,3,0,1,2,3,0... + * You should use high-order bits. + */ +unsigned int fastrand_val; + +unsigned int fastrand(void) +{ + return (fastrand_val=fastrand_val*1103515245+12345); +} + +void fastsrand(unsigned int seed) +{ + fastrand_val = seed; +} Added: trunk/mlt/src/modules/effectv/utils.h =================================================================== --- trunk/mlt/src/modules/effectv/utils.h (rev 0) +++ trunk/mlt/src/modules/effectv/utils.h 2007-06-10 21:34:57 UTC (rev 987) @@ -0,0 +1,46 @@ +/* + * EffecTV - Realtime Digital Video Effector + * Copyright (C) 2001-2006 FUKUCHI Kentaro + * + * utils.h: header file for utils + * + */ + +#ifndef __UTILS_H__ +#define __UTILS_H__ + +#include <inttypes.h> + +typedef uint32_t RGB32; + +/* DEFINE's by nu...@do... */ +#define RED(n) ((n>>16) & 0x000000FF) +#define GREEN(n) ((n>>8) & 0x000000FF) +#define BLUE(n) ((n>>0) & 0x000000FF) +#define RGB(r,g,b) ((0<<24) + (r<<16) + (g <<8) + (b)) +#define INTENSITY(n) ( ( (RED(n)+GREEN(n)+BLUE(n))/3)) + +/* utils.c */ +void HSItoRGB(double H, double S, double I, int *r, int *g, int *b); + +extern unsigned int fastrand_val; +unsigned int fastrand(void); +void fastsrand(unsigned int); +#define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345) + +/* image.c */ +int image_set_threshold_y(int threshold); +void image_bgset_y(RGB32 *background, const RGB32 *src, int video_area, int y_threshold); +void image_bgsubtract_y(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, int y_threshold); +void image_bgsubtract_update_y(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, int y_threshold); +RGB32 image_set_threshold_RGB(int r, int g, int b); +void image_bgset_RGB(RGB32 *background, const RGB32 *src, int video_area); +void image_bgsubtract_RGB(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold); +void image_bgsubtract_update_RGB(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold); +void image_diff_filter(unsigned char *diff2, const unsigned char *diff, int width, int height); +void image_y_over(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold); +void image_y_under(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold); +void image_edge(unsigned char *diff2, const RGB32 *src, int width, int height, int y_threshold); +void image_hflip(const RGB32 *src, RGB32 *dest, int width, int height); + +#endif /* __UTILS_H__ */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |