diff --git a/veejay-current/ChangeLog b/veejay-current/ChangeLog index 88ab1f79..cba8ccf1 100644 --- a/veejay-current/ChangeLog +++ b/veejay-current/ChangeLog @@ -17,6 +17,7 @@ + revisited OSC implementation ( use veejay -u -n |less ) + updated documentation (Howto, MAN pages, etc) + removed obsolete VIMS messages + + librified editlist,stream and yuv functions + removed any references on dropped vloopback support 0.7.1 diff --git a/veejay-current/Makefile.am b/veejay-current/Makefile.am index 00d5e21f..8e535ca8 100644 --- a/veejay-current/Makefile.am +++ b/veejay-current/Makefile.am @@ -1,6 +1,6 @@ # Process with automake to produce Makefile.in -SUBDIRS = ffmpeg utils bio2jack libOSC libvjmsg libvjmem libvje libsample libvjnet plugins veejay tools man +SUBDIRS = ffmpeg utils bio2jack libOSC libvjmsg libvjmem libvje libsample libvjnet libyuv libel libstream plugins veejay tools man #bin_SCRIPTS = veejay-config diff --git a/veejay-current/configure b/veejay-current/configure index 3def70bb..0f3dd72c 100755 --- a/veejay-current/configure +++ b/veejay-current/configure @@ -21739,7 +21739,7 @@ subdirs="$subdirs ffmpeg/ffmpeg" - ac_config_files="$ac_config_files Makefile ffmpeg/Makefile libOSC/Makefile libvjmsg/Makefile libvjmem/Makefile libvje/Makefile libsample/Makefile libvjnet/Makefile utils/Makefile bio2jack/Makefile plugins/Makefile veejay/Makefile man/Makefile tools/Makefile veejay-config veejay.pc" + ac_config_files="$ac_config_files Makefile ffmpeg/Makefile libOSC/Makefile libvjmsg/Makefile libvjmem/Makefile libvje/Makefile libsample/Makefile libvjnet/Makefile libyuv/Makefile libel/Makefile libstream/Makefile utils/Makefile bio2jack/Makefile plugins/Makefile veejay/Makefile man/Makefile tools/Makefile veejay-config veejay.pc" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -22479,6 +22479,9 @@ do "libvje/Makefile" ) CONFIG_FILES="$CONFIG_FILES libvje/Makefile" ;; "libsample/Makefile" ) CONFIG_FILES="$CONFIG_FILES libsample/Makefile" ;; "libvjnet/Makefile" ) CONFIG_FILES="$CONFIG_FILES libvjnet/Makefile" ;; + "libyuv/Makefile" ) CONFIG_FILES="$CONFIG_FILES libyuv/Makefile" ;; + "libel/Makefile" ) CONFIG_FILES="$CONFIG_FILES libel/Makefile" ;; + "libstream/Makefile" ) CONFIG_FILES="$CONFIG_FILES libstream/Makefile" ;; "utils/Makefile" ) CONFIG_FILES="$CONFIG_FILES utils/Makefile" ;; "bio2jack/Makefile" ) CONFIG_FILES="$CONFIG_FILES bio2jack/Makefile" ;; "plugins/Makefile" ) CONFIG_FILES="$CONFIG_FILES plugins/Makefile" ;; diff --git a/veejay-current/configure.in b/veejay-current/configure.in index 88286894..e90fc3f8 100644 --- a/veejay-current/configure.in +++ b/veejay-current/configure.in @@ -946,6 +946,9 @@ libvjmem/Makefile libvje/Makefile libsample/Makefile libvjnet/Makefile +libyuv/Makefile +libel/Makefile +libstream/Makefile utils/Makefile bio2jack/Makefile plugins/Makefile diff --git a/veejay-current/libel/Makefile.am b/veejay-current/libel/Makefile.am new file mode 100644 index 00000000..8682efc2 --- /dev/null +++ b/veejay-current/libel/Makefile.am @@ -0,0 +1,25 @@ +# Makefile for veejay + +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir) -I$(includedir) -I$(top_srcdir)/vjmem \ + -I$(top_srcdir)/vjmsg \ + -I$(top_srcdir)/ffmpeg/ffmpeg/libavcodec \ + -I$(top_srcdir)/ffmpeg/ffmpeg/libavformat \ + -I$(top_srcdir)/utils + +VJEL_LIB_OPTS = -export-dynamic + +VJEL_LIB_FILE = libel.la + +noinst_LTLIBRARIES = $(VJEL_LIB_FILE) + +libel_la_SOURCES = avilib.c lav_io.c vj-dv.c vj-avcodec.c vj-avformat.c vj-el.c + + +libel_la_LDFLAGS = $(VJEL_LIB_OPTS) \ + -L$(top_builddir)/ffmpeg/ffmpeg/libavcodec -lavcodec \ + -L$(top_builddir)/ffmpeg/ffmpeg/libavformat -lavformat \ + -L$(top_builddir)/libvjmem -lvjmem \ + -L$(top_builddir)/libvjmsg -lvjmsg \ + -DDYNAMIC -O3 -Wall diff --git a/veejay-current/veejay/avilib.c b/veejay-current/libel/avilib.c similarity index 100% rename from veejay-current/veejay/avilib.c rename to veejay-current/libel/avilib.c diff --git a/veejay-current/veejay/avilib.h b/veejay-current/libel/avilib.h similarity index 99% rename from veejay-current/veejay/avilib.h rename to veejay-current/libel/avilib.h index 4d83d217..47d6efff 100644 --- a/veejay-current/veejay/avilib.h +++ b/veejay-current/libel/avilib.h @@ -17,7 +17,7 @@ #ifndef AVILIB_H #define AVILIB_H -#include +#include typedef struct { diff --git a/veejay-current/veejay/lav_io.c b/veejay-current/libel/lav_io.c similarity index 99% rename from veejay-current/veejay/lav_io.c rename to veejay-current/libel/lav_io.c index 81701245..4944068b 100644 --- a/veejay-current/veejay/lav_io.c +++ b/veejay-current/libel/lav_io.c @@ -28,8 +28,8 @@ #include #include #include -#include -#include +#include +//#include #include extern int AVI_errno; diff --git a/veejay-current/veejay/lav_io.h b/veejay-current/libel/lav_io.h similarity index 98% rename from veejay-current/veejay/lav_io.h rename to veejay-current/libel/lav_io.h index 592dab0b..ba01aa36 100644 --- a/veejay-current/veejay/lav_io.h +++ b/veejay-current/libel/lav_io.h @@ -23,12 +23,12 @@ #include #endif -#include +#include // play with mlt here, avformat producer/several consumers (we can write to) // for normalized output (720x576/480) , dv1394 capture and playback -#include "yuv4mpeg.h" +#include #define LAV_INTER_UNKNOWN Y4M_UNKNOWN #define LAV_NOT_INTERLACED Y4M_ILACE_NONE diff --git a/veejay-current/veejay/vj-avcodec.c b/veejay-current/libel/vj-avcodec.c similarity index 74% rename from veejay-current/veejay/vj-avcodec.c rename to veejay-current/libel/vj-avcodec.c index a9129f0d..274c5dcc 100644 --- a/veejay-current/veejay/vj-avcodec.c +++ b/veejay-current/libel/vj-avcodec.c @@ -17,18 +17,16 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include -#include -#include -#include -#include +#include +#include #include #include #include - +#include #define __FALLBACK_LIBDV 1 #ifdef __FALLBACK_LIBDV -#include +#include #endif static int out_pixel_format = FMT_420; @@ -198,153 +196,6 @@ int vj_avcodec_free() return 1; } -/* this routine is the same as frame_YUV422_to_YUV420P , unpack - * libdv's 4:2:2-packed into 4:2:0 planar - * See http://mjpeg.sourceforge.net/ (MJPEG Tools) (lav-common.c) - */ -void yuy2toyv12(uint8_t * _y, uint8_t * _u, uint8_t * _v, uint8_t * input, - int width, int height) -{ - - int i, j, w2; - uint8_t *y, *u, *v; - - w2 = width / 2; - - //I420 - y = _y; - v = _v; - u = _u; - - for (i = 0; i < height; i += 4) { - /* top field scanline */ - for (j = 0; j < w2; j++) { - /* packed YUV 422 is: Y[i] U[i] Y[i+1] V[i] */ - *(y++) = *(input++); - *(u++) = *(input++); - *(y++) = *(input++); - *(v++) = *(input++); - } - for (j = 0; j < w2; j++) - { - *(y++) = *(input++); - *(u++) = *(input++); - *(y++) = *(input++); - *(v++) = *(input++); - - } - - /* next two scanlines, one frome each field , interleaved */ - for (j = 0; j < w2; j++) { - /* skip every second line for U and V */ - *(y++) = *(input++); - input++; - *(y++) = *(input++); - input++; - } - /* bottom field scanline*/ - for (j = 0; j < w2; j++) { - /* skip every second line for U and V */ - *(y++) = *(input++); - input++; - *(y++) = *(input++); - input++; - } - - } -} -/* convert 4:2:0 to yuv 4:2:2 packed */ -void yuv422p_to_yuv422(uint8_t * yuv420[3], uint8_t * dest, int width, - int height) -{ - unsigned int x, y; - - - for (y = 0; y < height; ++y) { - uint8_t *Y = yuv420[0] + y * width; - uint8_t *Cb = yuv420[1] + (y / 2) * (width); - uint8_t *Cr = yuv420[2] + (y / 2) * (width); - for (x = 0; x < width; x +=2) { - *(dest + 0) = Y[0]; - *(dest + 1) = Cb[0]; - *(dest + 2) = Y[1]; - *(dest + 3) = Cr[0]; - dest += 4; - Y += 2; - ++Cb; - ++Cr; - } - } -} - - - -/* convert 4:2:0 to yuv 4:2:2 */ -void yuv420p_to_yuv422(uint8_t * yuv420[3], uint8_t * dest, int width, - int height) -{ - unsigned int x, y; - - - for (y = 0; y < height; ++y) { - uint8_t *Y = yuv420[0] + y * width; - uint8_t *Cb = yuv420[1] + (y >> 1) * (width >> 1); - uint8_t *Cr = yuv420[2] + (y >> 1) * (width >> 1); - for (x = 0; x < width; x += 2) { - *(dest + 0) = Y[0]; - *(dest + 1) = Cb[0]; - *(dest + 2) = Y[1]; - *(dest + 3) = Cr[0]; - dest += 4; - Y += 2; - ++Cb; - ++Cr; - } - } -} - -/* convert yuv422 planar to YUYV */ -void yuv422_to_yuyv(uint8_t *yuv422[3], uint8_t *pixels, int w, int h) -{ - int x,y; - uint8_t *Y = yuv422[0]; - uint8_t *U = yuv422[1]; - uint8_t *V = yuv422[2]; // U Y V Y - for(y = 0; y < h; y ++ ) - { - Y = yuv422[0] + y * w; - U = yuv422[1] + (y>>1) * w; - V = yuv422[2] + (y>>1) * w; - /* for(x=0; x < w; x+= 2) - { - *(pixels + 0) = Y[0]; - *(pixels + 1) = U[0]; - *(pixels + 2) = Y[1]; - *(pixels + 3) = V[0]; - pixels += 4; - Y+=2; - ++U; - ++V; - } - */ - for( x = 0 ; x < w ; x += 4 ) - { - *(pixels + 0) = Y[0]; - *(pixels + 1) = U[0]; - *(pixels + 2) = Y[1]; - *(pixels + 3) = V[0]; - *(pixels + 4) = Y[2]; - *(pixels + 5) = U[1]; - *(pixels + 6) = Y[3]; - *(pixels + 7) = V[1]; - pixels += 8; - Y+=4; - U+=2; - V+=2; - } - } -} - void yuv422p_to_yuv420p2( uint8_t *src[3], uint8_t *dst[3], int w, int h) { int len = w* h ; diff --git a/veejay-current/veejay/vj-avcodec.h b/veejay-current/libel/vj-avcodec.h similarity index 75% rename from veejay-current/veejay/vj-avcodec.h rename to veejay-current/libel/vj-avcodec.h index 0f9a5a8f..49cd548b 100644 --- a/veejay-current/veejay/vj-avcodec.h +++ b/veejay-current/libel/vj-avcodec.h @@ -55,26 +55,12 @@ int vj_avcodec_free(); */ -// from yuv 4:2:2 packed to yuv 4:2:0 planar -void yuy2toyv12(uint8_t * _y, uint8_t * _u, uint8_t * _v, uint8_t * input, int w, int h); - -// from yuv 4:2:2 planar to yuv 4:2:2 packed -void yuv422p_to_yuv422(uint8_t * yuv420[3], uint8_t * dest, int w, int h); - -// from yuv 4:2:0 planar to yuv 4:2:2 packed -void yuv420p_to_yuv422(uint8_t * yuv420[3], uint8_t * dest, int w, int h ); - -// from yuv 4:2:2 planar to yuv 4:2:0 planar -int yuv422p_to_yuv420p( uint8_t *src[3], uint8_t *dst, int w, int h); // from yuv 4:2:0 planar to yuv 4:2:2 planar int yuv420p_to_yuv422p( uint8_t *Y, uint8_t *Cb, uint8_t *Cr, uint8_t *dst[3], int w, int h ); void yuv422p_to_yuv420p2( uint8_t *src[3], uint8_t *dst[3], int w, int h ); -// from yuv 4:2:0 planar to YUYV -void yuv422_to_yuyv( uint8_t *yuv422[3], uint8_t *pixels, int w, int h ); - int yuv420p_to_yuv422p2( uint8_t *sY,uint8_t *sCb, uint8_t *sCr, uint8_t *dst[3], int w, int h ); diff --git a/veejay-current/veejay/vj-avformat.c b/veejay-current/libel/vj-avformat.c similarity index 99% rename from veejay-current/veejay/vj-avformat.c rename to veejay-current/libel/vj-avformat.c index 34ad0c37..a4320a0b 100644 --- a/veejay-current/veejay/vj-avformat.c +++ b/veejay-current/libel/vj-avformat.c @@ -17,10 +17,11 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include -#include -#include +#include +#include #include #include +#include #if LIBAVFORMAT_BUILD >= 4620 #define m_av_seek_frame( a,b,c,d ) \ ( av_seek_frame( a,b,c,d ) ) diff --git a/veejay-current/veejay/vj-avformat.h b/veejay-current/libel/vj-avformat.h similarity index 95% rename from veejay-current/veejay/vj-avformat.h rename to veejay-current/libel/vj-avformat.h index 0e7557d3..1c6b9a56 100644 --- a/veejay-current/veejay/vj-avformat.h +++ b/veejay-current/libel/vj-avformat.h @@ -18,8 +18,10 @@ */ #ifndef VJ_AV_FORMAT_H #define VJ_AV_FORMAT_H -#include "avcodec.h" -#include "avformat.h" +#include +#include +#include + typedef struct vj_avformat_t { diff --git a/veejay-current/veejay/vj-dv.c b/veejay-current/libel/vj-dv.c similarity index 87% rename from veejay-current/veejay/vj-dv.c rename to veejay-current/libel/vj-dv.c index 73e808e9..c059995d 100644 --- a/veejay-current/veejay/vj-dv.c +++ b/veejay-current/libel/vj-dv.c @@ -21,8 +21,8 @@ #ifdef SUPPORT_READ_DV2 #include #include -#include -#include +#include +#include #include #define NTSC_W 720 @@ -72,33 +72,6 @@ void vj_dv_init_encoder(editlist * el, int pixel_format) isPAL ? DV_PAL_SIZE : DV_NTSC_SIZE))); } - -/* convert 4:2:0 to yuv 4:2:2 -static void convert_yuv420p_to_yuv422(uint8_t * yuv_in[3], - uint8_t * yuv422, int width, - int height) -{ - unsigned int x, y; - unsigned int i = 0; - - for (y = 0; y < height; ++y) { - uint8_t *Y = yuv_in[0] + y * width; - uint8_t *Cb = yuv_in[1] + (y / 2) * (width / 2); - uint8_t *Cr = yuv_in[2] + (y / 2) * (width / 2); - for (x = 0; x < width; x += 2) { - *(yuv422 + i) = Y[0]; - *(yuv422 + i + 1) = Cb[0]; - *(yuv422 + i + 2) = Y[1]; - *(yuv422 + i + 3) = Cr[0]; - i += 4; - Y += 2; - ++Cb; - ++Cr; - } - } -} -*/ - /* encode frame to dv format, dv frame will be in output_buf */ int vj_dv_encode_frame(uint8_t * input_buf[3], uint8_t * output_buf) { diff --git a/veejay-current/veejay/vj-dv.h b/veejay-current/libel/vj-dv.h similarity index 100% rename from veejay-current/veejay/vj-dv.h rename to veejay-current/libel/vj-dv.h diff --git a/veejay-current/libel/vj-el.c b/veejay-current/libel/vj-el.c new file mode 100644 index 00000000..7dd6c1d6 --- /dev/null +++ b/veejay-current/libel/vj-el.c @@ -0,0 +1,1302 @@ +/* veejay - Linux VeeJay + * (C) 2002-2004 Niels Elburg + * + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + + + This file contains code-snippets from the mjpegtools' EditList + (C) The Mjpegtools project + + http://mjpeg.sourceforge.net +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "avcodec.h" +#include + +static struct +{ + const char *name; +} _chroma_str[] = +{ + { "Unknown" }, + { "4:2:0" }, + { "4:2:2" }, + { "4:4:4" }, +}; + +#define MAX_CODECS 7 +#define CODEC_ID_YUV420 999 +#define CODEC_ID_YUV422 998 + +static struct +{ + const char *name; + int id; +} _supported_codecs[] = +{ + { "mjpeg" , CODEC_ID_MJPEG }, + { "dv" , CODEC_ID_DVVIDEO }, + { "msmpeg4",CODEC_ID_MPEG4 }, + { "divx" ,CODEC_ID_MSMPEG4V3 }, + { "i420", CODEC_ID_YUV420 }, + { "i422", CODEC_ID_YUV422 }, + { NULL , 0 }, +}; + +typedef struct +{ + AVCodec *codec; + AVFrame *frame; + AVCodecContext *context; + uint8_t *tmp_buffer; + uint8_t *deinterlace_buffer[3]; + int fmt; +} vj_decoder; + + + +static vj_decoder *el_codecs[MAX_CODECS]; + + +int open_video_file(char *filename, editlist * el, int preserve_pathname, int deinter, int force); + +static int _el_get_codec(int id) +{ + int i; + for( i=0; _supported_codecs[i].name != NULL; i++) + { + if( _supported_codecs[i].id == id ) return i; + } + return -1; +} + +static vj_decoder *_el_new_decoder( int id , int width, int height, float fps, int pixel_format) +{ + vj_decoder *d = (vj_decoder*) vj_malloc(sizeof(vj_decoder)); + + if(!d) return NULL; + + d->codec = NULL; + if( id != CODEC_ID_YUV422 && id != CODEC_ID_YUV420) + { + d->codec = avcodec_find_decoder( id ); + if(d->codec) + { + d->context = avcodec_alloc_context(); + //d->frame = avcodec_alloc_frame(); + d->context->width = width; + d->context->height= height; + d->context->frame_rate = fps; + d->context->pix_fmt = (pixel_format==FMT_420 ? PIX_FMT_YUV420P : PIX_FMT_YUV422P); + d->frame = avcodec_alloc_frame(); + } + else + { + veejay_msg(VEEJAY_MSG_ERROR, "AVFormat: Cannot find decoder for %d", id); + } + } + d->tmp_buffer = (uint8_t*) vj_malloc(sizeof(uint8_t) * width * height * 4 ); + d->fmt = id; + + if(!d->tmp_buffer) + { + if(d) free(d); + return NULL; + } + memset( d->tmp_buffer, 0, width * height * 4 ); + + d->deinterlace_buffer[0] = (uint8_t*) vj_malloc(sizeof(uint8_t) * width * height); + if(!d->deinterlace_buffer[0]) { if(d) free(d); return NULL; } + d->deinterlace_buffer[1] = (uint8_t*) vj_malloc(sizeof(uint8_t) * width * height); + if(!d->deinterlace_buffer[1]) { if(d) free(d); return NULL; } + d->deinterlace_buffer[2] = (uint8_t*) vj_malloc(sizeof(uint8_t) * width * height); + if(!d->deinterlace_buffer[2]) { if(d) free(d); return NULL; } + + memset( d->deinterlace_buffer[0], 0, width * height ); + memset( d->deinterlace_buffer[1], 0, width * height ); + memset( d->deinterlace_buffer[2], 0, width * height ); + + if(d->codec != NULL) + { + if ( avcodec_open( d->context, d->codec ) < 0 ) + { + veejay_msg(VEEJAY_MSG_ERROR, "Error initializing decoder %d",id); + if(d) free(d); + return NULL; + } + else + { + veejay_msg(VEEJAY_MSG_DEBUG,"Initialized decoder %d", id); + } + } + return d; +} + + + +static void _el_free_decoder(vj_decoder *d) +{ + if(d) + { + if(d->tmp_buffer) free(d->tmp_buffer); + avcodec_close( d->context ); + free(d); + d = NULL; + } +} +/* +static int _el_lav_fallback( char *filename ) +{ + lav_file_t *fd = lav_open_input_file( filename ); + if(fd) + { + // veejay_msg(VEEJAY_MSG_DEBUG,"Probe finds %s", lav_video_compressor(fd)); + lav_close(fd); + return 1; + } + return 0; +} +*/ + +static int _el_probe_for_pixel_fmt( lav_file_t *fd ) +{ + if(!fd) return -1; + if( fd->MJPG_chroma == CHROMA420 ) + return FMT_420; + if( fd->MJPG_chroma == CHROMA422 ) + return FMT_422; + if( fd->MJPG_chroma == CHROMA444 ) + { + veejay_msg(VEEJAY_MSG_WARNING, "YUV 4:4:4 is not supported yet. "); + return FMT_422; + } + if( fd->MJPG_chroma == CHROMAUNKNOWN ) + return FMT_420; + + return -1; +} + + +// this method should add a new producer to the playlist +int open_video_file(char *filename, editlist * el, int preserve_pathname, int deinter, int force) +{ + int i, n, nerr; + int chroma=0; + int _fc; + char realname[PATH_MAX]; + int decoder_id = 0; + const char *compr_type; + int pix_fmt = -1; + + /* Get full pathname of file if the user hasn't specified preservation + of pathnames... + */ + bzero(realname, PATH_MAX); + + if (preserve_pathname) + { + strcpy(realname, filename); + } + else if (realpath(filename, realname) == 0) + { + veejay_msg(VEEJAY_MSG_ERROR ,"Cannot deduce real filename"); + return -1; + } + for (i = 0; i < el->num_video_files; i++) + if (strcmp(realname, el->video_file_list[i]) == 0) + { + veejay_msg(VEEJAY_MSG_ERROR, "File %s already open", realname); + return i; + } + + /* Check if MAX_EDIT_LIST_FILES will be exceeded */ + + if (el->num_video_files >= MAX_EDIT_LIST_FILES) + { + // mjpeg_error_exit1("Maximum number of video files exceeded"); + veejay_msg(VEEJAY_MSG_ERROR,"Maximum number of video files exceeded\n"); + return -1; + } + + if (el->num_video_files >= 1) + chroma = el->MJPG_chroma; + + n = el->num_video_files; + if(el->has_video == 0) + { // in case we have initialized from probing + el->video_frames = 0; + if(el->frame_list) free(el->frame_list); + el->frame_list = NULL; + } + el->num_video_files++; + + el->lav_fd[n] = lav_open_input_file(filename); + + if (!el->lav_fd[n]) + { + veejay_msg(VEEJAY_MSG_ERROR,"Error opening file [%s]: %s", filename, + lav_strerror()); + el->num_video_files--; + return -1; + } + + _fc = lav_video_MJPG_chroma(el->lav_fd[n]); + + if( !(_fc == CHROMA422 || _fc == CHROMA420 || _fc == CHROMA444 || _fc == CHROMAUNKNOWN )) + { + veejay_msg(VEEJAY_MSG_ERROR,"Input file %s is not in a valid format (%d)",filename,_fc); + el->num_video_files --; + return -1; + + } + + if(chroma == CHROMAUNKNOWN) + { /* set chroma */ + el->MJPG_chroma = _fc; + chroma = _fc; + } + pix_fmt = _el_probe_for_pixel_fmt( el->lav_fd[n] ); + if(pix_fmt < 0) + { + veejay_msg(VEEJAY_MSG_ERROR, "Unable to determine pixel format"); + return -1; + } + + if(el->pixel_format == -1) + { + el->pixel_format = pix_fmt; + } + else + { + // check on sanity + if( pix_fmt > el->pixel_format) + { + veejay_msg(VEEJAY_MSG_ERROR, "Cannot handle mixed 4:2:2 and 4:2:0 editlists"); + return -1; + } + } + + el->num_frames[n] = lav_video_frames(el->lav_fd[n]); + + el->video_file_list[n] = strndup(realname, strlen(realname)); + if (el->video_file_list[n] == 0) + { + veejay_msg(VEEJAY_MSG_ERROR, "Insufficient memory to allocate a few bytes"); + return -1; + } + + /* Debug Output */ + + veejay_msg(VEEJAY_MSG_DEBUG,"File: %s, absolute name: %s", filename, realname); + veejay_msg(VEEJAY_MSG_DEBUG,"\tFrames: %ld", lav_video_frames(el->lav_fd[n])); + veejay_msg(VEEJAY_MSG_DEBUG,"\tWidth: %d", lav_video_width(el->lav_fd[n])); + veejay_msg(VEEJAY_MSG_DEBUG,"\tHeight: %d", lav_video_height(el->lav_fd[n])); + + { + const char *int_msg; + switch (lav_video_interlacing(el->lav_fd[n])) + { + case LAV_NOT_INTERLACED: + int_msg = "Not interlaced"; + break; + case LAV_INTER_TOP_FIRST: + int_msg = "Top field first"; + break; + case LAV_INTER_BOTTOM_FIRST: + int_msg = "Bottom field first"; + break; + default: + int_msg = "Unknown!"; + break; + } + + if( deinter == 1 && ( (lav_video_interlacing(el->lav_fd[n]) == LAV_INTER_TOP_FIRST ) || (lav_video_interlacing(el->lav_fd[n])==LAV_INTER_BOTTOM_FIRST))) + el->auto_deinter = 1; + + veejay_msg(VEEJAY_MSG_DEBUG,"\tInterlacing: %s", int_msg); + } + + veejay_msg(VEEJAY_MSG_DEBUG,"\tFrames/sec: %f", lav_frame_rate(el->lav_fd[n])); + veejay_msg(VEEJAY_MSG_DEBUG,"\tSampling format: %s", _chroma_str[ lav_video_MJPG_chroma(el->lav_fd[n])].name); + veejay_msg(VEEJAY_MSG_DEBUG,"\tFOURCC: %s",lav_video_compressor(el->lav_fd[n])); + veejay_msg(VEEJAY_MSG_DEBUG,"\tAudio samps: %ld", lav_audio_clips(el->lav_fd[n])); + veejay_msg(VEEJAY_MSG_DEBUG,"\tAudio chans: %d", lav_audio_channels(el->lav_fd[n])); + veejay_msg(VEEJAY_MSG_DEBUG,"\tAudio bits: %d", lav_audio_bits(el->lav_fd[n])); + veejay_msg(VEEJAY_MSG_DEBUG,"\tAudio rate: %ld", lav_audio_rate(el->lav_fd[n])); + + + nerr = 0; + if (n == 0) { + /* First file determines parameters */ + + el->video_height = lav_video_height(el->lav_fd[n]); + el->video_width = lav_video_width(el->lav_fd[n]); + el->video_inter = lav_video_interlacing(el->lav_fd[n]); + el->video_fps = lav_frame_rate(el->lav_fd[n]); + lav_video_clipaspect(el->lav_fd[n], + &el->video_sar_width, + &el->video_sar_height); + + if (!el->video_norm) + { + /* TODO: This guessing here is a bit dubious but it can be over-ridden */ + if (el->video_fps > 24.95 && el->video_fps < 25.05) + el->video_norm = 'p'; + else if (el->video_fps > 29.92 && el->video_fps <= 30.02) + el->video_norm = 'n'; + } + + el->audio_chans = lav_audio_channels(el->lav_fd[n]); + if (el->audio_chans > 2) { + el->num_video_files --; + veejay_msg(VEEJAY_MSG_ERROR, "File %s has %d audio channels - cant play that!", + filename,el->audio_chans); + nerr++; + } + + el->has_audio = (el->audio_chans == 0 ? 0: 1); + el->audio_bits = lav_audio_bits(el->lav_fd[n]); + el->play_rate = el->audio_rate = lav_audio_rate(el->lav_fd[n]); + el->audio_bps = (el->audio_bits * el->audio_chans + 7) / 8; + } else { + /* All files after first have to match the paramters of the first */ + + if (el->video_height != lav_video_height(el->lav_fd[n]) || + el->video_width != lav_video_width(el->lav_fd[n])) { + veejay_msg(VEEJAY_MSG_ERROR,"File %s: Geometry %dx%d does not match %dx%d.", + filename, lav_video_width(el->lav_fd[n]), + lav_video_height(el->lav_fd[n]), el->video_width, + el->video_height); + nerr++; + } + if (el->video_inter != lav_video_interlacing(el->lav_fd[n])) { + if(force) + veejay_msg(VEEJAY_MSG_WARNING,"File %s: Interlacing is %d should be %d", + filename, lav_video_interlacing(el->lav_fd[n]), + el->video_inter); + else + veejay_msg(VEEJAY_MSG_ERROR, "File %s: Interlacing is %d should be %d", + filename, lav_video_interlacing(el->lav_fd[n]), + el->video_inter); + + + if(!el->auto_deinter) + { + if(force) + { + veejay_msg(VEEJAY_MSG_WARNING, "(Force loading video) Auto deinterlacing enabled"); + el->auto_deinter = 1; + } + else + { + nerr++; + } + } + } + /* give a warning on different fps instead of error , this is better + for live performances */ + if (fabs(el->video_fps - lav_frame_rate(el->lav_fd[n])) > + 0.0000001) { + veejay_msg(VEEJAY_MSG_WARNING,"(Ignoring) File %s: fps is %3.2f should be %3.2f", filename, + lav_frame_rate(el->lav_fd[n]), el->video_fps); + } + /* If first file has no audio, we don't care about audio */ + + if (el->has_audio) { + if (el->audio_chans != lav_audio_channels(el->lav_fd[n]) || + el->audio_bits != lav_audio_bits(el->lav_fd[n]) || + el->audio_rate != lav_audio_rate(el->lav_fd[n])) { + veejay_msg(VEEJAY_MSG_WARNING,"File %s: Audio is %d chans %d bit %ld Hz," + " should be %d chans %d bit %ld Hz", + filename, lav_audio_channels(el->lav_fd[n]), + lav_audio_bits(el->lav_fd[n]), + lav_audio_rate(el->lav_fd[n]), el->audio_chans, + el->audio_bits, el->audio_rate); + // nerr++; + } + } + + if (nerr) { + el->num_video_files --; + return -1; + } + } + compr_type = (const char*) lav_video_compressor(el->lav_fd[n]); + if(!compr_type) + { + veejay_msg(VEEJAY_MSG_ERROR, "Cannot get codec information from lav file"); + return -1; + } + + // initialze a decoder if needed + if( strncasecmp("mjpg", compr_type, 4) == 0) + decoder_id = CODEC_ID_MJPEG; + if( strncasecmp("jpeg", compr_type, 4) == 0) + decoder_id = CODEC_ID_MJPEG; + if( strncasecmp("mjpa", compr_type, 4) == 0) + decoder_id = CODEC_ID_MJPEG; + if( strncasecmp("dvsd", compr_type, 4) == 0) + decoder_id = CODEC_ID_DVVIDEO; + if( strncasecmp("dv", compr_type, 2) == 0) + decoder_id = CODEC_ID_DVVIDEO; + if( strncasecmp("mp4v",compr_type,4) == 0) + decoder_id = CODEC_ID_MPEG4; + if( strncasecmp("div3", compr_type,4) == 0) + decoder_id = CODEC_ID_MSMPEG4V3; + if( strncasecmp("yuv", compr_type, 3) == 0) + decoder_id = CODEC_ID_YUV420; + if( strncasecmp("iyuv", compr_type,4) == 0) + decoder_id = CODEC_ID_YUV420; + if( strncasecmp("i420", compr_type,4) == 0) + decoder_id = CODEC_ID_YUV420; + if( strncasecmp("yv16", compr_type,4) == 0) + decoder_id = CODEC_ID_YUV422; + +// if( decoder_id == CODEC_ID_MPEG4 || decoder_id == CODEC_ID_MSMPEG4V3) +// { +// +// } + + + if(decoder_id > 0) + { + int c_i = _el_get_codec(decoder_id); + if(c_i == -1) + { + veejay_msg(VEEJAY_MSG_ERROR, "Unsupported codec %s",compr_type); + return -1; + } + if( el_codecs[c_i] == NULL ) + { + el_codecs[c_i] = _el_new_decoder( decoder_id, el->video_width, el->video_height, el->video_fps, pix_fmt ); + if(!el_codecs[c_i]) + { + veejay_msg(VEEJAY_MSG_ERROR,"Cannot initialize %s codec", compr_type); + return -1; + } + } + } + + if(decoder_id == 0) + { + veejay_msg(VEEJAY_MSG_ERROR, "Dont know how to handle %s (fmt %d)", compr_type, pix_fmt); + return -1; + } + + return n; +} + +static int vj_el_dummy_frame( uint8_t *dst[3], editlist *el ,int pix_fmt) +{ + const int uv_len = (el->video_width * el->video_height) / ( (pix_fmt==FMT_422 ? 2 : 4)); + const int len = el->video_width * el->video_height; + memset( dst[0], 16, len ); + memset( dst[1],128, uv_len ); + memset( dst[2],128, uv_len ); + return 1; +} + +int vj_el_get_video_frame(editlist *el, long nframe, uint8_t *dst[3], int pix_fmt) +{ + int res; + uint64_t n; + int decoder_id =0; + int c_i = 0; + vj_decoder *d = NULL; + + if( el->has_video == 0 ) + { vj_el_dummy_frame( dst, el, pix_fmt ); return 2; } + + if (nframe < 0) + nframe = 0; + + if (nframe > el->video_frames) + nframe = el->video_frames; + + n = el->frame_list[nframe]; + + res = lav_set_video_position(el->lav_fd[N_EL_FILE(n)], N_EL_FRAME(n)); + + decoder_id = lav_video_compressor_type( el->lav_fd[N_EL_FILE(n)] ); + + c_i = _el_get_codec( decoder_id ); + + if(c_i >= 0 && c_i < MAX_CODECS) + { + d = el_codecs[c_i]; + } + if(!d) + { + veejay_msg(VEEJAY_MSG_DEBUG, "Cannot find codec for id %d (%d)", decoder_id, + c_i); + return -1; + } + + if (res < 0) + { + veejay_msg(VEEJAY_MSG_ERROR,"Error setting video position: %s", + lav_strerror()); + } + res = lav_read_frame(el->lav_fd[N_EL_FILE(n)], d->tmp_buffer); + + if (res < 0) + { + veejay_msg(VEEJAY_MSG_ERROR,"Error reading video frame: %s", lav_strerror()); + return -1; + } + + if( decoder_id == CODEC_ID_YUV420 ) + { /* yuv420 raw */ + int len = el->video_width * el->video_height; + int uv_len = len / 4; + //memset( dst[0], 0, len); + //memset( dst[1], 0, uv_len*2); + //memset( dst[2], 0, uv_len*2); + if(pix_fmt == FMT_420) + { + veejay_memcpy( dst[0], d->tmp_buffer, len); + veejay_memcpy( dst[1], d->tmp_buffer+len,uv_len); + veejay_memcpy( dst[2], d->tmp_buffer+(len+uv_len), uv_len); + } + else + { + return (yuv420p_to_yuv422p( d->tmp_buffer, + d->tmp_buffer+len, + d->tmp_buffer+len+uv_len, + dst, el->video_width, el->video_height)); + } + + /* this should be faster (img_convert copies plane [0] but ss_420_to_422 is not yet implemented) + + int len = el->video_width * el->video_height; + int uv_len = len / 4; + veejay_memcpy( dst[0], d->tmp_buffer, len); + veejay_memcpy( dst[1], d->tmp_buffer+len,uv_len); + veejay_memcpy( dst[2], d->tmp_buffer+(len+uv_len), uv_len); + ss_420_to_422( dst[1], el->video_width, el->video_height ); + ss_420_to_422( dst[2], el->video_width, el->video_height );*/ + + } + else + { + if( decoder_id == CODEC_ID_YUV422) + { + int len = el->video_width * el->video_height; + int uv_len = len /2; + veejay_memcpy( dst[0], d->tmp_buffer, len); + veejay_memcpy( dst[1], d->tmp_buffer+len,uv_len); + veejay_memcpy( dst[2], d->tmp_buffer+len+uv_len, uv_len); + + } + else + { + int len; + int got_picture = 0; + int inter = lav_video_interlacing(el->lav_fd[N_EL_FILE(n)]); + len = avcodec_decode_video(d->context, d->frame, &got_picture, d->tmp_buffer, res); + if(len>0) + { + AVPicture pict,pict2; + int dst_pix_fmt = (pix_fmt == FMT_422 ? PIX_FMT_YUV422P : PIX_FMT_YUV420P); + int res = 0; + pict.data[0] = dst[0]; + pict.data[1] = dst[1]; + pict.data[2] = dst[2]; + + pict.linesize[0] = el->video_width; + pict.linesize[1] = el->video_width >> 1; + pict.linesize[2] = el->video_width >> 1; +/* +int avpicture_deinterlace(AVPicture *dst, const AVPicture *src, + int pix_fmt, int width, int height); +*/ + + if( el->auto_deinter && inter != LAV_NOT_INTERLACED) + { + pict2.data[0] = d->deinterlace_buffer[0]; + pict2.data[1] = d->deinterlace_buffer[1]; + pict2.data[2] = d->deinterlace_buffer[2]; + pict2.linesize[1] = el->video_width >> 1; + pict2.linesize[2] = el->video_width >> 1; + + pict2.linesize[0] = el->video_width; + res = avpicture_deinterlace( &pict2, (const AVPicture*) d->frame, d->context->pix_fmt, + el->video_width, el->video_height); + + img_convert( &pict, dst_pix_fmt, (const AVPicture*) &pict2, d->context->pix_fmt, + el->video_width,el->video_height); + + } + else + { + img_convert( &pict, dst_pix_fmt, (const AVPicture*) d->frame, d->context->pix_fmt, + el->video_width, el->video_height ); + } + return 1; + } + veejay_msg(VEEJAY_MSG_WARNING, "Error decoding frame %ld - %d ", nframe,len); + return 0; + } + } + + return 1; +} + +int vj_el_get_audio_frame(editlist *el, uint32_t nframe, uint8_t *dst) +{ + long pos, asize; + int ret = 0; + uint64_t n; + int ns0, ns1; + + if (!el->has_audio) + return 0; + + if (nframe < 0) + nframe = 0; + + if (nframe > el->video_frames) + nframe = el->video_frames; + + n = el->frame_list[nframe]; + + ns1 = (double) (N_EL_FRAME(n) + 1) * el->audio_rate / el->video_fps; + ns0 = (double) N_EL_FRAME(n) * el->audio_rate / el->video_fps; + + //asize = el->audio_rate / el->video_fps; + pos = nframe * asize; + ret = lav_set_audio_position(el->lav_fd[N_EL_FILE(n)], ns0); + + if (ret < 0) + return -1; + + //mlt need int16_t + ret = lav_read_audio(el->lav_fd[N_EL_FILE(n)], dst, (ns1 - ns0)); + if (ret < 0) + return -1; + + return (ns1 - ns0); + +} + +int vj_el_init_420_frame(editlist *el, VJFrame *frame) +{ + if(!el) return 0; + frame->data[0] = NULL; + frame->data[1] = NULL; + frame->data[2] = NULL; + frame->uv_len = (el->video_width>>1) * (el->video_height>>1); + frame->uv_width = el->video_width >> 1; + frame->uv_height = el->video_height >> 1; + frame->len = el->video_width * el->video_height; + frame->shift_v = 1; + frame->shift_h = 1; + return 1; +} + + +int vj_el_init_422_frame(editlist *el, VJFrame *frame) +{ + if(!el) return 0; + frame->data[0] = NULL; + frame->data[1] = NULL; + frame->data[2] = NULL; + frame->uv_len = (el->video_width>>1) * (el->video_height); + frame->uv_width = el->video_width >> 1; + frame->uv_height = el->video_height; + frame->len = el->video_width * el->video_height; + frame->shift_v = 0; + frame->shift_h = 1; + return 1; +} + +int vj_el_get_audio_frame_at(editlist *el, uint32_t nframe, uint8_t *dst, int num ) +{ + // get audio from current frame + n frames + long pos, asize; + int ret = 0; + uint64_t n; + int ns0, ns1; + + if (!el->has_audio) + return 0; + + if (nframe < 0) + nframe = 0; + + if (nframe > el->video_frames) + nframe = el->video_frames - num; + + n = el->frame_list[nframe]; + + ns1 = (double) (N_EL_FRAME(n) + num) * el->audio_rate / el->video_fps; + ns0 = (double) N_EL_FRAME(n) * el->audio_rate / el->video_fps; + + //asize = el->audio_rate / el->video_fps; + pos = nframe * asize; + ret = lav_set_audio_position(el->lav_fd[N_EL_FILE(n)], ns0); + + if (ret < 0) + return -1; + + //mlt need int16_t + ret = lav_read_audio(el->lav_fd[N_EL_FILE(n)], dst, (ns1 - ns0)); + if (ret < 0) + return -1; + + return (ns1 - ns0); + +} + +editlist *vj_el_dummy(int flags, int deinterlace, int chroma, char norm, int width, int height, float fps) +{ + editlist *el = vj_malloc(sizeof(editlist)); + if(!el) return NULL; + el->MJPG_chroma = chroma; + el->video_norm = norm; + el->has_audio = 0; + el->audio_rate = 0; + el->audio_bits = 0; + el->audio_bps = 0; + el->audio_chans = 0; + el->play_rate = 0; + el->num_video_files = 0; + el->video_width = width; + el->video_height = height; + el->video_frames = 2; + el->video_fps = fps; + el->video_inter = LAV_NOT_INTERLACED; + el->pixel_format = (chroma == CHROMA420 ? FMT_420 : FMT_422); + el->auto_deinter = deinterlace; + el->max_frame_size = width*height*3; + el->last_afile = -1; + el->last_apos = 0; + el->frame_list = NULL; + el->has_video = 0; + return el; +} + +void vj_el_close( editlist *el ) +{ + int i; + for ( i = 0; i < el->num_video_files; i ++ ) + { + if( el->lav_fd[i] ) lav_close( el->lav_fd[i] ); + if( el->video_file_list[i]) free(el->video_file_list[i]); + } + if( el->frame_list ) + free(el->frame_list ); + free(el); +} + + +editlist *vj_el_init_with_args(char **filename, int num_files, int flags, int deinterlace, int force) +{ + editlist *el = vj_malloc(sizeof(editlist)); + + FILE *fd; + char line[1024]; + uint64_t index_list[MAX_EDIT_LIST_FILES]; + int num_list_files; + long i,nf=0; + int n1=0; + int n2=0; + long nl=0; + uint64_t n =0; + + if(!el) return NULL; + + memset( el, 0, sizeof(editlist) ); + el->pixel_format = -1; + el->has_video = 1; //assume we get it + el->MJPG_chroma = CHROMA420; + /* Check if a norm parameter is present */ + if(!filename[0] || filename == NULL) + { + vj_el_free(el); + return NULL; + } + + if (strcmp(filename[0], "+p") == 0 || strcmp(filename[0], "+n") == 0) + { + el->video_norm = filename[0][1]; + nf = 1; + veejay_msg(VEEJAY_MSG_DEBUG,"Norm set to %s", el->video_norm == 'n' ? "NTSC" : "PAL"); + } + + if(force) + { + veejay_msg(VEEJAY_MSG_WARNING, "Forcing load on interlacing and gop_size"); + } + + for (; nf < num_files; nf++) + { + /* Check if filename[nf] is a edit list */ + fd = fopen(filename[nf], "r"); + if (fd == 0) + { + veejay_msg(VEEJAY_MSG_ERROR,"Error opening %s:", filename[nf]); + vj_el_free(el); + return NULL; + } + fgets(line, 1024, fd); + if (strcmp(line, "LAV Edit List\n") == 0) + { + /* Ok, it is a edit list */ + veejay_msg(VEEJAY_MSG_DEBUG, "Edit list %s opened", filename[nf]); + /* Read second line: Video norm */ + fgets(line, 1024, fd); + if (line[0] != 'N' && line[0] != 'n' && line[0] != 'P' && line[0] != 'p') + { + veejay_msg(VEEJAY_MSG_ERROR,"Edit list second line is not NTSC/PAL"); + vj_el_free(el); + return NULL; + } + veejay_msg(VEEJAY_MSG_DEBUG,"Edit list norm is %s", line[0] =='N' || line[0] == 'n' ? "NTSC" : "PAL" ); + if (line[0] == 'N' || line[0] == 'n') + { + if (el->video_norm == 'p') + { + veejay_msg(VEEJAY_MSG_ERROR, "Norm already set to PAL"); + vj_el_free(el); + return NULL; + } + el->video_norm = 'n'; + } + else + { + if (el->video_norm == 'n') + { + veejay_msg(VEEJAY_MSG_ERROR,"Norm allready set to NTSC"); + vj_el_free(el); + return NULL; + } + el->video_norm = 'p'; + } + /* read third line: Number of files */ + fgets(line, 1024, fd); + sscanf(line, "%d", &num_list_files); + + veejay_msg(VEEJAY_MSG_DEBUG, "Edit list contains %d files", num_list_files); + /* read files */ + + for (i = 0; i < num_list_files; i++) + { + fgets(line, 1024, fd); + n = strlen(line); + + if (line[n - 1] != '\n') + { + veejay_msg(VEEJAY_MSG_ERROR, "Filename in edit list too long"); + vj_el_free(el); + return NULL; + } + + line[n - 1] = 0; /* Get rid of \n at end */ + + index_list[i] = + open_video_file(line, el, flags, deinterlace,force); + + if(index_list[i]<0) + { + vj_el_free(el); + return NULL; + } + } + + /* Read edit list entries */ + + while (fgets(line, 1024, fd)) + { + if (line[0] != ':') + { /* ignore lines starting with a : */ + sscanf(line, "%ld %d %d", &nl, &n1, &n2); + + if (nl < 0 || nl >= num_list_files) + { + veejay_msg(VEEJAY_MSG_ERROR,"Wrong file number in edit list entry"); + //vj_el_free(el); + return NULL; + } + if (n1 < 0) + n1 = 0; + if (n2 >= el->num_frames[index_list[nl]]) + n2 = el->num_frames[index_list[nl]]; + if (n2 < n1) + continue; + + el->frame_list = (uint64_t *) realloc(el->frame_list, + (el->video_frames + + n2 - n1 + + 1) * sizeof(uint64_t)); + + if (el->frame_list==NULL) + { + veejay_msg(VEEJAY_MSG_ERROR, "Insufficient memory to allocate frame_list"); + vj_el_free(el); + return NULL; + } + + + for (i = n1; i <= n2; i++) + { + el->frame_list[el->video_frames] = EL_ENTRY( index_list[ nl], i); + + el->video_frames++; + } + } + } /* done reading editlist entries */ + fclose(fd); + } + else + { + /* Not an edit list - should be a ordinary video file */ + fclose(fd); + + n = open_video_file(filename[nf], el, flags, deinterlace,force); + if(n==-1 || n==-2) + { + veejay_msg(VEEJAY_MSG_DEBUG, "Cannot put file %s in editlist", filename[nf]); + vj_el_free(el); + return NULL; + } + el->frame_list = (uint64_t *) realloc(el->frame_list, + (el->video_frames + + el->num_frames[n]) * + sizeof(uint64_t)); + if (el->frame_list==NULL) + { + veejay_msg(VEEJAY_MSG_ERROR, "Insufficient memory to allocate frame_list"); + vj_el_free(el); + return NULL; + } + + for (i = 0; i < el->num_frames[n]; i++) + { + el->frame_list[el->video_frames] = EL_ENTRY(n, i); + el->video_frames++; + } + } + } + + /* Calculate maximum frame size */ + + for (i = 0; i < el->video_frames; i++) + { + n = el->frame_list[i]; + if (lav_frame_size(el->lav_fd[N_EL_FILE(n)], N_EL_FRAME(n)) > + el->max_frame_size) + el->max_frame_size = + lav_frame_size(el->lav_fd[N_EL_FILE(n)], N_EL_FRAME(n)); + } + + /* Help for audio positioning */ + + el->last_afile = -1; + + + //el->auto_deinter = auto_deinter; + //if(el->video_inter != 0 ) el->auto_deinter = 0; + el->auto_deinter = 0; + + // FIXME + veejay_msg(VEEJAY_MSG_WARNING, "Editlist is using %s", (el->pixel_format == FMT_420 ? "yuv420p" : "yuv422p")); + + return el; +} + + +void vj_el_free(editlist *el) +{ + int i=0; + if(el) + { + int n = el->num_video_files; + int i; + for( i = 0; i < n ; i++ ) + if( el->video_file_list[n]) free(el->video_file_list[n]); + if(el->frame_list) free(el->frame_list); + free(el); + el = NULL; + } +// for(i=0; i < MAX_CODECS; i++) +// { +// if(el_codecs[i]) _el_free_decoder(el_codecs[i]); +// } +} + +void vj_el_print(editlist *el) +{ + int i; + char timecode[64]; + char interlacing[64]; + MPEG_timecode_t ttc; + veejay_msg(VEEJAY_MSG_INFO,"EditList settings: Video:%dx%d@%2.2f %s\tAudio:%d Hz/%d channels/%d bits", + el->video_width,el->video_height,el->video_fps,(el->video_norm=='p' ? "PAL" :"NTSC"), + el->audio_rate, el->audio_chans, el->audio_bits); + for(i=0; i < el->num_video_files ; i++) + { + long num_frames = lav_video_frames(el->lav_fd[i]); + MPEG_timecode_t tc; + switch( lav_video_interlacing(el->lav_fd[i])) + { + case LAV_NOT_INTERLACED: + sprintf(interlacing, "Not interlaced"); break; + case LAV_INTER_TOP_FIRST: + sprintf(interlacing,"Top field first"); break; + case LAV_INTER_BOTTOM_FIRST: + sprintf(interlacing, "Bottom field first"); break; + default: + sprintf(interlacing, "Unknown !"); break; + } + + mpeg_timecode(&tc, num_frames, + mpeg_framerate_code( mpeg_conform_framerate( el->video_fps )), + el->video_fps ); + + sprintf( timecode, "%2d:%2.2d:%2.2d:%2.2d", tc.h, tc.m, tc.s, tc.f ); + + veejay_msg(VEEJAY_MSG_INFO, "\tFile %s (%s) with %ld frames (total duration %s)", + el->video_file_list[i], + interlacing, + num_frames, + timecode ); + + } + + mpeg_timecode(&ttc, el->video_frames, + mpeg_framerate_code( mpeg_conform_framerate( el->video_fps )), + el->video_fps ); + + sprintf( timecode, "%2d:%2.2d:%2.2d:%2.2d", ttc.h, ttc.m, ttc.s, ttc.f ); + + + veejay_msg(VEEJAY_MSG_INFO, "\tDuration: %s (%2d hours, %2d minutes)(%ld frames)", timecode,ttc.h,ttc.m,el->video_frames); + + + + +} + +editlist *vj_el_probe_from_file( char *filename ) +{ + editlist *el = vj_malloc(sizeof(editlist)); + vj_avformat *tmp; + if(!el) return NULL; + memset(el, 0, sizeof(editlist)); + el->pixel_format = -1; + + + tmp = vj_avformat_open_input(filename); + if(!tmp) + { + veejay_msg(VEEJAY_MSG_ERROR, "Unable to probe %s, is it a video file?", filename); + if(el) free(el); + return NULL; + } + + el->video_width = vj_avformat_get_video_width( tmp ); + el->video_height = vj_avformat_get_video_height(tmp); + el->video_fps = vj_avformat_get_video_fps(tmp); + el->video_inter = vj_avformat_get_video_inter(tmp); +// el->audio_rate = vj_avformat_get_audio_rate(tmp); +// el->audio_chans = vj_avformat_get_audio_channels(tmp); +// el->audio_bits = 16; +// el->audio_bps = 4; +// el->has_audio = (el->audio_rate > 0 ? 1: 0 ); + + if(el->video_inter == 25 ) + el->video_norm = 'p'; + else + el->video_norm = 'n'; + el->MJPG_chroma = CHROMA420; + + veejay_msg(VEEJAY_MSG_DEBUG,"Probed file: %s %dx%d@%2.2f , %s , %d Hz/%d channels/%d bits", filename, + el->video_width,el->video_height,el->video_fps,(el->video_norm=='p' ? "PAL" :"NTSC"), + el->audio_rate, el->audio_chans, el->audio_bits); + vj_avformat_close_input(tmp); + + el->has_video = 0; /* this is a stream */ + el->video_frames = 2; + el->num_video_files = 0; + el->frame_list = NULL; + + return el; +} + + + +int vj_el_write_editlist( char *name, long _n1, long _n2, editlist *el ) +{ + FILE *fd; + int num_files; + uint64_t oldfile, oldframe; + uint64_t index[MAX_EDIT_LIST_FILES]; + uint64_t n; + uint64_t n1 = (uint64_t) _n1; + uint64_t n2 = (uint64_t) _n2; + int i; + /* check n1 and n2 for correctness */ + + if (n1 < 0) + n1 = 0; + + if (n2 >= el->video_frames || n2 < n1) + n2 = el->video_frames - 1; + + fd = fopen(name, "w"); + + if (fd == 0) + { + veejay_msg(VEEJAY_MSG_ERROR,"Can not open %s - no edit list written!", name); + return 0; + } + + fprintf(fd, "LAV Edit List\n"); + fprintf(fd, "%s\n", el->video_norm == 'n' ? "NTSC" : "PAL"); + + /* get which files are actually referenced in the edit list entries */ + + for (i = 0; i < MAX_EDIT_LIST_FILES; i++) + index[i] = -1; + + for (i = n1; i <= n2; i++) + index[N_EL_FILE(el->frame_list[i])] = 1; + + num_files = 0; + for (i = 0; i < MAX_EDIT_LIST_FILES; i++) + if (index[i] == 1) index[i] = num_files++; + + fprintf(fd, "%d\n", num_files); + for (i = 0; i < MAX_EDIT_LIST_FILES; i++) + if (index[i] >= 0 && el->video_file_list[i] != NULL) + { + fprintf(fd, "%s\n", el->video_file_list[i]); + } + + n = el->frame_list[ n1 ]; + oldfile = index[N_EL_FILE(n)]; + oldframe = N_EL_FRAME(n); + + veejay_msg(VEEJAY_MSG_DEBUG, "%lld , %lld ,%lld, %ld, %ld ", + n,oldfile,oldframe,_n1,_n2); + + + fprintf(fd, "%lld %lld ", oldfile, oldframe); + for (i = n1 + 1; i <= n2; i++) + { + n = el->frame_list[i]; + if (index[N_EL_FILE(n)] != oldfile + || N_EL_FRAME(n) != oldframe + 1) + { + fprintf(fd, "%lld\n", oldframe); + fprintf(fd, "%lld %lld ",index[N_EL_FILE(n)], N_EL_FRAME(n)); + } + oldfile = index[N_EL_FILE(n)]; + oldframe = N_EL_FRAME(n); + } + n = fprintf(fd, "%lld\n", oldframe); + + /* We did not check if all our prints succeeded, so check at least the last one */ + + if (n <= 0) + { + veejay_msg(VEEJAY_MSG_ERROR,"Error writing edit list: "); + return 0; + } + + fclose(fd); + + return 1; +} + +editlist *vj_el_new(char *filename, char *norm, int deinterlace) +{ + editlist *el = vj_malloc(sizeof(editlist)); + uint64_t index_list[MAX_EDIT_LIST_FILES]; + uint64_t i = 0; + uint64_t n = 0; + + if(!el) return NULL; + + memset( el, 0, sizeof(editlist) ); + + el->pixel_format = -1; + el->has_video = 1; //assume we get it + el->MJPG_chroma = CHROMA420; + el->video_norm = norm; + + n = open_video_file(filename, el, 0, deinterlace,0); + + if(n<0) + { + vj_el_free(el); + return NULL; + } + + el->frame_list = (uint64_t *) realloc(el->frame_list, + (el->video_frames + + el->num_frames[n]) * + sizeof(uint64_t)); + + if (el->frame_list==NULL) + { + veejay_msg(VEEJAY_MSG_ERROR, "Insufficient memory to allocate frame_list"); + vj_el_free(el); + return NULL; + } + + for (i = 0; i < el->num_frames[n]; i++) + { + el->frame_list[el->video_frames] = EL_ENTRY(n, i); + el->video_frames++; + } + + /* Calculate maximum frame size */ + + for (i = 0; i < el->video_frames; i++) + { + n = el->frame_list[i]; + if (lav_frame_size(el->lav_fd[N_EL_FILE(n)], N_EL_FRAME(n)) > + el->max_frame_size) + el->max_frame_size = + lav_frame_size(el->lav_fd[N_EL_FILE(n)], N_EL_FRAME(n)); + } + + /* Help for audio positioning */ + el->last_afile = -1; + el->auto_deinter = 0; + + return el; +} + + + diff --git a/veejay-current/libel/vj-el.h b/veejay-current/libel/vj-el.h new file mode 100644 index 00000000..e9258361 --- /dev/null +++ b/veejay-current/libel/vj-el.h @@ -0,0 +1,100 @@ +/* veejay - Linux VeeJay + * (C) 2002-2004 Niels Elburg + * + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef VJ_MLT_EL_H +#define VJ_MLT_EL_H +#include +#include +#include +#include +#include + +#define N_EL_FRAME(x) ( (x)&0xfffffffffffffLLU ) +#define N_EL_FILE(x) (int) ( ((x)>>52)&0xfff ) +/* ((file)&0xfff<<52) */ +#define EL_ENTRY(file,frame) ( ((file)<<52) | ((frame)& 0xfffffffffffffLLU) ) + +//#define FMT_420 0 +//#define FMT_422 1 +//#define MAX_EDITLIST_FILES 4096 + + +typedef struct +{ + int has_video; + int video_width; + int video_height; + int video_inter; + float video_fps; + int video_sar_width; + int video_sar_height; + char video_norm; + + int has_audio; + long audio_rate; + int audio_chans; + int audio_bits; + int audio_bps; + int play_rate; + + long video_frames; + + long num_video_files; + + long max_frame_size; + int MJPG_chroma; + + char *(video_file_list[MAX_EDIT_LIST_FILES]); + lav_file_t *(lav_fd[MAX_EDIT_LIST_FILES]); + long num_frames[MAX_EDIT_LIST_FILES]; + uint64_t *frame_list; + + int last_afile; + long last_apos; + int auto_deinter; + + int pixel_format; + +} editlist; + + +editlist *vj_el_init_with_args(char **filenames, int n, int flags, int deinter, int force); + +editlist *vj_el_probe_from_file( char *filename ); + +void vj_el_free(editlist *el); + +int vj_el_get_audio_frame_at(editlist *el, uint32_t nframe, uint8_t *dst, int speed ); + +int vj_el_append_video_file(editlist *el, char *filename); + +int vj_el_write_editlist( char *filename, long start, long end, editlist *el ); + +int vj_el_get_video_frame(editlist *el, long nframe, uint8_t *dst[3], int pix_fmt); + +int vj_el_get_audio_frame(editlist *el, uint32_t nframe, uint8_t *dst); + +void vj_el_print(editlist *el); + +int vj_el_init_420_frame(editlist *el, VJFrame *frame); +int vj_el_init_422_frame(editlist *el, VJFrame *frame); + + +editlist *vj_el_dummy(int flags, int deinterlace, int chroma, char norm, int width, int height, float fps); + +#endif diff --git a/veejay-current/libstream/Makefile.am b/veejay-current/libstream/Makefile.am new file mode 100644 index 00000000..bf339ad2 --- /dev/null +++ b/veejay-current/libstream/Makefile.am @@ -0,0 +1,28 @@ +# Makefile for veejay + +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir) -I$(includedir) -I$(top_srcdir)/vjmem \ + -I$(top_srcdir)/vjmsg \ + -I$(top_srcdir)/ffmpeg/ffmpeg/libavcodec \ + -I$(top_srcdir)/ffmpeg/ffmpeg/libavformat \ + -I$(top_srcdir)/libvjnet/ \ + -I$(top_srcdir)/utils \ + ${XML_CFLAGS} ${XML_CPPFLAGS} + +STREAM_LIB_OPTS = -export-dynamic + +STREAM_LIB_FILE = libstream.la + +noinst_LTLIBRARIES = $(STREAM_LIB_FILE) + +libstream_la_SOURCES = hash.c v4lutils.c vj-v4lvideo.c vj-yuv4mpeg.c vj-shm.c vj-tag.c + + +libstream_la_LDFLAGS = $(STREAM_LIB_OPTS) \ + -L$(top_builddir)/ffmpeg/ffmpeg/libavcodec -lavcodec \ + -L$(top_builddir)/ffmpeg/ffmpeg/libavformat -lavformat \ + -L$(top_builddir)/libvjmem -lvjmem \ + -L$(top_builddir)/libvjmsg -lvjmsg \ + -L$(top_builddir)/libvjnet -lvjnet \ + -DDYNAMIC -O3 -Wall diff --git a/veejay-current/libstream/hash.c b/veejay-current/libstream/hash.c new file mode 100644 index 00000000..51635df1 --- /dev/null +++ b/veejay-current/libstream/hash.c @@ -0,0 +1,1035 @@ +/* + * Hash Table Data Type + * Copyright (C) 1997 Kaz Kylheku + * + * Free Software License: + * + * All rights are reserved by the author, with the following exceptions: + * Permission is granted to freely reproduce and distribute this software, + * possibly in exchange for a fee, provided that this copyright notice appears + * intact. Permission is also granted to adapt this software to produce + * derivative works, as long as the modified versions carry this copyright + * notice and additional notices stating that the work has been modified. + * This source code may be translated into executable form and incorporated + * into proprietary software; there is no requirement for such software to + * contain a copyright notice related to this source. + * + * $Id: hash.c,v 1.1.1.1 2004/10/27 23:48:59 niels Exp $ + * $Name: $ + */ + +#include +#include +#include +#include +#define HASH_IMPLEMENTATION +#include "hash.h" + +#ifdef KAZLIB_RCSID +static const char rcsid[] = "$Id: hash.c,v 1.1.1.1 2004/10/27 23:48:59 niels Exp $"; +#endif + +#define INIT_BITS 6 +#define INIT_SIZE (1UL << (INIT_BITS)) /* must be power of two */ +#define INIT_MASK ((INIT_SIZE) - 1) + +#define next hash_next +#define key hash_key +#define data hash_data +#define hkey hash_hkey + +#define table hash_table +#define nchains hash_nchains +#define nodecount hash_nodecount +#define maxcount hash_maxcount +#define highmark hash_highmark +#define lowmark hash_lowmark +#define compare hash_compare +#define function hash_function +#define allocnode hash_allocnode +#define freenode hash_freenode +#define context hash_context +#define mask hash_mask +#define dynamic hash_dynamic + +#define table hash_table +#define chain hash_chain + +static hnode_t *hnode_alloc(void *context); +static void hnode_free(hnode_t *node, void *context); +static hash_val_t hash_fun_default(const void *key); +static int hash_comp_default(const void *key1, const void *key2); + +int hash_val_t_bit; + +/* + * Compute the number of bits in the hash_val_t type. We know that hash_val_t + * is an unsigned integral type. Thus the highest value it can hold is a + * Mersenne number (power of two, less one). We initialize a hash_val_t + * object with this value and then shift bits out one by one while counting. + * Notes: + * 1. HASH_VAL_T_MAX is a Mersenne number---one that is one less than a power + * of two. This means that its binary representation consists of all one + * bits, and hence ``val'' is initialized to all one bits. + * 2. While bits remain in val, we increment the bit count and shift it to the + * right, replacing the topmost bit by zero. + */ + +static void compute_bits(void) +{ + hash_val_t val = HASH_VAL_T_MAX; /* 1 */ + int bits = 0; + + while (val) { /* 2 */ + bits++; + val >>= 1; + } + + hash_val_t_bit = bits; +} + +/* + * Verify whether the given argument is a power of two. + */ + +static int is_power_of_two(hash_val_t arg) +{ + if (arg == 0) + return 0; + while ((arg & 1) == 0) + arg >>= 1; + return (arg == 1); +} + +/* + * Compute a shift amount from a given table size + */ + +static hash_val_t compute_mask(hashcount_t size) +{ + assert (is_power_of_two(size)); + assert (size >= 2); + + return size - 1; +} + +/* + * Initialize the table of pointers to null. + */ + +static void clear_table(hash_t *hash) +{ + hash_val_t i; + + for (i = 0; i < hash->nchains; i++) + hash->table[i] = NULL; +} + +/* + * Double the size of a dynamic table. This works as follows. Each chain splits + * into two adjacent chains. The shift amount increases by one, exposing an + * additional bit of each hashed key. For each node in the original chain, the + * value of this newly exposed bit will decide which of the two new chains will + * receive the node: if the bit is 1, the chain with the higher index will have + * the node, otherwise the lower chain will receive the node. In this manner, + * the hash table will continue to function exactly as before without having to + * rehash any of the keys. + * Notes: + * 1. Overflow check. + * 2. The new number of chains is twice the old number of chains. + * 3. The new mask is one bit wider than the previous, revealing a + * new bit in all hashed keys. + * 4. Allocate a new table of chain pointers that is twice as large as the + * previous one. + * 5. If the reallocation was successful, we perform the rest of the growth + * algorithm, otherwise we do nothing. + * 6. The exposed_bit variable holds a mask with which each hashed key can be + * AND-ed to test the value of its newly exposed bit. + * 7. Now loop over each chain in the table and sort its nodes into two + * chains based on the value of each node's newly exposed hash bit. + * 8. The low chain replaces the current chain. The high chain goes + * into the corresponding sister chain in the upper half of the table. + * 9. We have finished dealing with the chains and nodes. We now update + * the various bookeeping fields of the hash structure. + */ + +static void grow_table(hash_t *hash) +{ + hnode_t **newtable; + + assert (2 * hash->nchains > hash->nchains); /* 1 */ + + newtable = realloc(hash->table, + sizeof *newtable * hash->nchains * 2); /* 4 */ + + if (newtable) { /* 5 */ + hash_val_t mask = (hash->mask << 1) | 1; /* 3 */ + hash_val_t exposed_bit = mask ^ hash->mask; /* 6 */ + hash_val_t chain; + + assert (mask != hash->mask); + + for (chain = 0; chain < hash->nchains; chain++) { /* 7 */ + hnode_t *low_chain = 0, *high_chain = 0, *hptr, *next; + + for (hptr = newtable[chain]; hptr != 0; hptr = next) { + next = hptr->next; + + if (hptr->hkey & exposed_bit) { + hptr->next = high_chain; + high_chain = hptr; + } else { + hptr->next = low_chain; + low_chain = hptr; + } + } + + newtable[chain] = low_chain; /* 8 */ + newtable[chain + hash->nchains] = high_chain; + } + + hash->table = newtable; /* 9 */ + hash->mask = mask; + hash->nchains *= 2; + hash->lowmark *= 2; + hash->highmark *= 2; + } + assert (hash_verify(hash)); +} + +/* + * Cut a table size in half. This is done by folding together adjacent chains + * and populating the lower half of the table with these chains. The chains are + * simply spliced together. Once this is done, the whole table is reallocated + * to a smaller object. + * Notes: + * 1. It is illegal to have a hash table with one slot. This would mean that + * hash->shift is equal to hash_val_t_bit, an illegal shift value. + * Also, other things could go wrong, such as hash->lowmark becoming zero. + * 2. Looping over each pair of sister chains, the low_chain is set to + * point to the head node of the chain in the lower half of the table, + * and high_chain points to the head node of the sister in the upper half. + * 3. The intent here is to compute a pointer to the last node of the + * lower chain into the low_tail variable. If this chain is empty, + * low_tail ends up with a null value. + * 4. If the lower chain is not empty, we simply tack the upper chain onto it. + * If the upper chain is a null pointer, nothing happens. + * 5. Otherwise if the lower chain is empty but the upper one is not, + * If the low chain is empty, but the high chain is not, then the + * high chain is simply transferred to the lower half of the table. + * 6. Otherwise if both chains are empty, there is nothing to do. + * 7. All the chain pointers are in the lower half of the table now, so + * we reallocate it to a smaller object. This, of course, invalidates + * all pointer-to-pointers which reference into the table from the + * first node of each chain. + * 8. Though it's unlikely, the reallocation may fail. In this case we + * pretend that the table _was_ reallocated to a smaller object. + * 9. Finally, update the various table parameters to reflect the new size. + */ + +static void shrink_table(hash_t *hash) +{ + hash_val_t chain, nchains; + hnode_t **newtable, *low_tail, *low_chain, *high_chain; + + assert (hash->nchains >= 2); /* 1 */ + nchains = hash->nchains / 2; + + for (chain = 0; chain < nchains; chain++) { + low_chain = hash->table[chain]; /* 2 */ + high_chain = hash->table[chain + nchains]; + for (low_tail = low_chain; low_tail && low_tail->next; low_tail = low_tail->next) + ; /* 3 */ + if (low_chain != 0) /* 4 */ + low_tail->next = high_chain; + else if (high_chain != 0) /* 5 */ + hash->table[chain] = high_chain; + else + assert (hash->table[chain] == NULL); /* 6 */ + } + newtable = realloc(hash->table, + sizeof *newtable * nchains); /* 7 */ + if (newtable) /* 8 */ + hash->table = newtable; + hash->mask >>= 1; /* 9 */ + hash->nchains = nchains; + hash->lowmark /= 2; + hash->highmark /= 2; + assert (hash_verify(hash)); +} + + +/* + * Create a dynamic hash table. Both the hash table structure and the table + * itself are dynamically allocated. Furthermore, the table is extendible in + * that it will automatically grow as its load factor increases beyond a + * certain threshold. + * Notes: + * 1. If the number of bits in the hash_val_t type has not been computed yet, + * we do so here, because this is likely to be the first function that the + * user calls. + * 2. Allocate a hash table control structure. + * 3. If a hash table control structure is successfully allocated, we + * proceed to initialize it. Otherwise we return a null pointer. + * 4. We try to allocate the table of hash chains. + * 5. If we were able to allocate the hash chain table, we can finish + * initializing the hash structure and the table. Otherwise, we must + * backtrack by freeing the hash structure. + * 6. INIT_SIZE should be a power of two. The high and low marks are always set + * to be twice the table size and half the table size respectively. When the + * number of nodes in the table grows beyond the high size (beyond load + * factor 2), it will double in size to cut the load factor down to about + * about 1. If the table shrinks down to or beneath load factor 0.5, + * it will shrink, bringing the load up to about 1. However, the table + * will never shrink beneath INIT_SIZE even if it's emptied. + * 7. This indicates that the table is dynamically allocated and dynamically + * resized on the fly. A table that has this value set to zero is + * assumed to be statically allocated and will not be resized. + * 8. The table of chains must be properly reset to all null pointers. + */ + +hash_t *hash_create(hashcount_t maxcount, hash_comp_t compfun, + hash_fun_t hashfun) +{ + hash_t *hash; + + if (hash_val_t_bit == 0) /* 1 */ + compute_bits(); + + hash = malloc(sizeof *hash); /* 2 */ + + if (hash) { /* 3 */ + hash->table = malloc(sizeof *hash->table * INIT_SIZE); /* 4 */ + if (hash->table) { /* 5 */ + hash->nchains = INIT_SIZE; /* 6 */ + hash->highmark = INIT_SIZE * 2; + hash->lowmark = INIT_SIZE / 2; + hash->nodecount = 0; + hash->maxcount = maxcount; + hash->compare = compfun ? compfun : hash_comp_default; + hash->function = hashfun ? hashfun : hash_fun_default; + hash->allocnode = hnode_alloc; + hash->freenode = hnode_free; + hash->context = NULL; + hash->mask = INIT_MASK; + hash->dynamic = 1; /* 7 */ + clear_table(hash); /* 8 */ + assert (hash_verify(hash)); + return hash; + } + free(hash); + } + + return NULL; +} + +/* + * Select a different set of node allocator routines. + */ + +void hash_set_allocator(hash_t *hash, hnode_alloc_t al, + hnode_free_t fr, void *context) +{ + assert (hash_count(hash) == 0); + assert ((al == 0 && fr == 0) || (al != 0 && fr != 0)); + + hash->allocnode = al ? al : hnode_alloc; + hash->freenode = fr ? fr : hnode_free; + hash->context = context; +} + +/* + * Free every node in the hash using the hash->freenode() function pointer, and + * cause the hash to become empty. + */ + +void hash_free_nodes(hash_t *hash) +{ + hscan_t hs; + hnode_t *node; + hash_scan_begin(&hs, hash); + while ((node = hash_scan_next(&hs))) { + hash_scan_delete(hash, node); + hash->freenode(node, hash->context); + } + hash->nodecount = 0; + clear_table(hash); +} + +/* + * Obsolescent function for removing all nodes from a table, + * freeing them and then freeing the table all in one step. + */ + +void hash_free(hash_t *hash) +{ +#ifdef KAZLIB_OBSOLESCENT_DEBUG + assert ("call to obsolescent function hash_free()" && 0); +#endif + hash_free_nodes(hash); + hash_destroy(hash); +} + +/* + * Free a dynamic hash table structure. + */ + +void hash_destroy(hash_t *hash) +{ + assert (hash_val_t_bit != 0); + assert (hash_isempty(hash)); + free(hash->table); + free(hash); +} + +/* + * Initialize a user supplied hash structure. The user also supplies a table of + * chains which is assigned to the hash structure. The table is static---it + * will not grow or shrink. + * 1. See note 1. in hash_create(). + * 2. The user supplied array of pointers hopefully contains nchains nodes. + * 3. See note 7. in hash_create(). + * 4. We must dynamically compute the mask from the given power of two table + * size. + * 5. The user supplied table can't be assumed to contain null pointers, + * so we reset it here. + */ + +hash_t *hash_init(hash_t *hash, hashcount_t maxcount, + hash_comp_t compfun, hash_fun_t hashfun, hnode_t **table, + hashcount_t nchains) +{ + if (hash_val_t_bit == 0) /* 1 */ + compute_bits(); + + assert (is_power_of_two(nchains)); + + hash->table = table; /* 2 */ + hash->nchains = nchains; + hash->nodecount = 0; + hash->maxcount = maxcount; + hash->compare = compfun ? compfun : hash_comp_default; + hash->function = hashfun ? hashfun : hash_fun_default; + hash->dynamic = 0; /* 3 */ + hash->mask = compute_mask(nchains); /* 4 */ + clear_table(hash); /* 5 */ + + assert (hash_verify(hash)); + + return hash; +} + +/* + * Reset the hash scanner so that the next element retrieved by + * hash_scan_next() shall be the first element on the first non-empty chain. + * Notes: + * 1. Locate the first non empty chain. + * 2. If an empty chain is found, remember which one it is and set the next + * pointer to refer to its first element. + * 3. Otherwise if a chain is not found, set the next pointer to NULL + * so that hash_scan_next() shall indicate failure. + */ + +void hash_scan_begin(hscan_t *scan, hash_t *hash) +{ + hash_val_t nchains = hash->nchains; + hash_val_t chain; + + scan->table = hash; + + /* 1 */ + + for (chain = 0; chain < nchains && hash->table[chain] == 0; chain++) + ; + + if (chain < nchains) { /* 2 */ + scan->chain = chain; + scan->next = hash->table[chain]; + } else { /* 3 */ + scan->next = NULL; + } +} + +/* + * Retrieve the next node from the hash table, and update the pointer + * for the next invocation of hash_scan_next(). + * Notes: + * 1. Remember the next pointer in a temporary value so that it can be + * returned. + * 2. This assertion essentially checks whether the module has been properly + * initialized. The first point of interaction with the module should be + * either hash_create() or hash_init(), both of which set hash_val_t_bit to + * a non zero value. + * 3. If the next pointer we are returning is not NULL, then the user is + * allowed to call hash_scan_next() again. We prepare the new next pointer + * for that call right now. That way the user is allowed to delete the node + * we are about to return, since we will no longer be needing it to locate + * the next node. + * 4. If there is a next node in the chain (next->next), then that becomes the + * new next node, otherwise ... + * 5. We have exhausted the current chain, and must locate the next subsequent + * non-empty chain in the table. + * 6. If a non-empty chain is found, the first element of that chain becomes + * the new next node. Otherwise there is no new next node and we set the + * pointer to NULL so that the next time hash_scan_next() is called, a null + * pointer shall be immediately returned. + */ + + +hnode_t *hash_scan_next(hscan_t *scan) +{ + hnode_t *next = scan->next; /* 1 */ + hash_t *hash = scan->table; + hash_val_t chain = scan->chain + 1; + hash_val_t nchains = hash->nchains; + + assert (hash_val_t_bit != 0); /* 2 */ + + if (next) { /* 3 */ + if (next->next) { /* 4 */ + scan->next = next->next; + } else { + while (chain < nchains && hash->table[chain] == 0) /* 5 */ + chain++; + if (chain < nchains) { /* 6 */ + scan->chain = chain; + scan->next = hash->table[chain]; + } else { + scan->next = NULL; + } + } + } + return next; +} + +/* + * Insert a node into the hash table. + * Notes: + * 1. It's illegal to insert more than the maximum number of nodes. The client + * should verify that the hash table is not full before attempting an + * insertion. + * 2. The same key may not be inserted into a table twice. + * 3. If the table is dynamic and the load factor is already at >= 2, + * grow the table. + * 4. We take the bottom N bits of the hash value to derive the chain index, + * where N is the base 2 logarithm of the size of the hash table. + */ + +void hash_insert(hash_t *hash, hnode_t *node, const void *key) +{ + hash_val_t hkey, chain; + + assert (hash_val_t_bit != 0); + assert (node->next == NULL); + assert (hash->nodecount < hash->maxcount); /* 1 */ + assert (hash_lookup(hash, key) == NULL); /* 2 */ + + if (hash->dynamic && hash->nodecount >= hash->highmark) /* 3 */ + grow_table(hash); + + hkey = hash->function(key); + chain = hkey & hash->mask; /* 4 */ + + node->key = key; + node->hkey = hkey; + node->next = hash->table[chain]; + hash->table[chain] = node; + hash->nodecount++; + + assert (hash_verify(hash)); +} + +/* + * Find a node in the hash table and return a pointer to it. + * Notes: + * 1. We hash the key and keep the entire hash value. As an optimization, when + * we descend down the chain, we can compare hash values first and only if + * hash values match do we perform a full key comparison. + * 2. To locate the chain from among 2^N chains, we look at the lower N bits of + * the hash value by anding them with the current mask. + * 3. Looping through the chain, we compare the stored hash value inside each + * node against our computed hash. If they match, then we do a full + * comparison between the unhashed keys. If these match, we have located the + * entry. + */ + +hnode_t *hash_lookup(hash_t *hash, const void *key) +{ + hash_val_t hkey, chain; + hnode_t *nptr; + + hkey = hash->function(key); /* 1 */ + chain = hkey & hash->mask; /* 2 */ + + for (nptr = hash->table[chain]; nptr; nptr = nptr->next) { /* 3 */ + if (nptr->hkey == hkey && hash->compare(nptr->key, key) == 0) + return nptr; + } + + return NULL; +} + +/* + * Delete the given node from the hash table. Since the chains + * are singly linked, we must locate the start of the node's chain + * and traverse. + * Notes: + * 1. The node must belong to this hash table, and its key must not have + * been tampered with. + * 2. If this deletion will take the node count below the low mark, we + * shrink the table now. + * 3. Determine which chain the node belongs to, and fetch the pointer + * to the first node in this chain. + * 4. If the node being deleted is the first node in the chain, then + * simply update the chain head pointer. + * 5. Otherwise advance to the node's predecessor, and splice out + * by updating the predecessor's next pointer. + * 6. Indicate that the node is no longer in a hash table. + */ + +hnode_t *hash_delete(hash_t *hash, hnode_t *node) +{ + hash_val_t chain; + hnode_t *hptr; + + assert (hash_lookup(hash, node->key) == node); /* 1 */ + assert (hash_val_t_bit != 0); + + if (hash->dynamic && hash->nodecount <= hash->lowmark + && hash->nodecount > INIT_SIZE) + shrink_table(hash); /* 2 */ + + chain = node->hkey & hash->mask; /* 3 */ + hptr = hash->table[chain]; + + if (hptr == node) { /* 4 */ + hash->table[chain] = node->next; + } else { + while (hptr->next != node) { /* 5 */ + assert (hptr != 0); + hptr = hptr->next; + } + assert (hptr->next == node); + hptr->next = node->next; + } + + hash->nodecount--; + assert (hash_verify(hash)); + + node->next = NULL; /* 6 */ + return node; +} + +int hash_alloc_insert(hash_t *hash, const void *key, void *data) +{ + hnode_t *node = hash->allocnode(hash->context); + + if (node) { + hnode_init(node, data); + hash_insert(hash, node, key); + return 1; + } + return 0; +} + +void hash_delete_free(hash_t *hash, hnode_t *node) +{ + hash_delete(hash, node); + hash->freenode(node, hash->context); +} + +/* + * Exactly like hash_delete, except does not trigger table shrinkage. This is to be + * used from within a hash table scan operation. See notes for hash_delete. + */ + +hnode_t *hash_scan_delete(hash_t *hash, hnode_t *node) +{ + hash_val_t chain; + hnode_t *hptr; + + assert (hash_lookup(hash, node->key) == node); + assert (hash_val_t_bit != 0); + + chain = node->hkey & hash->mask; + hptr = hash->table[chain]; + + if (hptr == node) { + hash->table[chain] = node->next; + } else { + while (hptr->next != node) + hptr = hptr->next; + hptr->next = node->next; + } + + hash->nodecount--; + assert (hash_verify(hash)); + node->next = NULL; + + return node; +} + +/* + * Like hash_delete_free but based on hash_scan_delete. + */ + +void hash_scan_delfree(hash_t *hash, hnode_t *node) +{ + hash_scan_delete(hash, node); + hash->freenode(node, hash->context); +} + +/* + * Verify whether the given object is a valid hash table. This means + * Notes: + * 1. If the hash table is dynamic, verify whether the high and + * low expansion/shrinkage thresholds are powers of two. + * 2. Count all nodes in the table, and test each hash value + * to see whether it is correct for the node's chain. + */ + +int hash_verify(hash_t *hash) +{ + hashcount_t count = 0; + hash_val_t chain; + hnode_t *hptr; + + if (hash->dynamic) { /* 1 */ + if (hash->lowmark >= hash->highmark) + return 0; + if (!is_power_of_two(hash->highmark)) + return 0; + if (!is_power_of_two(hash->lowmark)) + return 0; + } + + for (chain = 0; chain < hash->nchains; chain++) { /* 2 */ + for (hptr = hash->table[chain]; hptr != 0; hptr = hptr->next) { + if ((hptr->hkey & hash->mask) != chain) + return 0; + count++; + } + } + + if (count != hash->nodecount) + return 0; + + return 1; +} + +/* + * Test whether the hash table is full and return 1 if this is true, + * 0 if it is false. + */ + +#undef hash_isfull +int hash_isfull(hash_t *hash) +{ + return hash->nodecount == hash->maxcount; +} + +/* + * Test whether the hash table is empty and return 1 if this is true, + * 0 if it is false. + */ + +#undef hash_isempty +int hash_isempty(hash_t *hash) +{ + return hash->nodecount == 0; +} + +static hnode_t *hnode_alloc(void *context) +{ + return malloc(sizeof *hnode_alloc(NULL)); +} + +static void hnode_free(hnode_t *node, void *context) +{ + if(node) free(node); +} + + +/* + * Create a hash table node dynamically and assign it the given data. + */ + +hnode_t *hnode_create(void *data) +{ + hnode_t *node = malloc(sizeof *node); + if (node) { + node->data = data; + node->next = NULL; + } + return node; +} + +/* + * Initialize a client-supplied node + */ + +hnode_t *hnode_init(hnode_t *hnode, void *data) +{ + hnode->data = data; + hnode->next = NULL; + return hnode; +} + +/* + * Destroy a dynamically allocated node. + */ + +void hnode_destroy(hnode_t *hnode) +{ + free(hnode); +} + +#undef hnode_put +void hnode_put(hnode_t *node, void *data) +{ + node->data = data; +} + +#undef hnode_get +void *hnode_get(hnode_t *node) +{ + return node->data; +} + +#undef hnode_getkey +const void *hnode_getkey(hnode_t *node) +{ + return node->key; +} + +#undef hash_count +hashcount_t hash_count(hash_t *hash) +{ + return hash->nodecount; +} + +#undef hash_size +hashcount_t hash_size(hash_t *hash) +{ + return hash->nchains; +} + +static hash_val_t hash_fun_default(const void *key) +{ + static unsigned long randbox[] = { + 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U, + 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU, + 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU, + 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU, + }; + + const unsigned char *str = key; + hash_val_t acc = 0; + + while (*str) { + acc ^= randbox[(*str + acc) & 0xf]; + acc = (acc << 1) | (acc >> 31); + acc &= 0xffffffffU; + acc ^= randbox[((*str++ >> 4) + acc) & 0xf]; + acc = (acc << 2) | (acc >> 30); + acc &= 0xffffffffU; + } + return acc; +} + +static int hash_comp_default(const void *key1, const void *key2) +{ + return strcmp(key1, key2); +} + +#ifdef KAZLIB_TEST_MAIN + +#include +#include +#include + +typedef char input_t[256]; + +static int tokenize(char *string, ...) +{ + char **tokptr; + va_list arglist; + int tokcount = 0; + + va_start(arglist, string); + tokptr = va_arg(arglist, char **); + while (tokptr) { + while (*string && isspace((unsigned char) *string)) + string++; + if (!*string) + break; + *tokptr = string; + while (*string && !isspace((unsigned char) *string)) + string++; + tokptr = va_arg(arglist, char **); + tokcount++; + if (!*string) + break; + *string++ = 0; + } + va_end(arglist); + + return tokcount; +} + +static char *dupstring(char *str) +{ + int sz = strlen(str) + 1; + char *new = malloc(sz); + if (new) + memcpy(new, str, sz); + return new; +} + +static hnode_t *new_node(void *c) +{ + static hnode_t few[5]; + static int count; + + if (count < 5) + return few + count++; + + return NULL; +} + +static void del_node(hnode_t *n, void *c) +{ +} + +int main(void) +{ + input_t in; + hash_t *h = hash_create(HASHCOUNT_T_MAX, 0, 0); + hnode_t *hn; + hscan_t hs; + char *tok1, *tok2, *val; + const char *key; + int prompt = 0; + + char *help = + "a add value to hash table\n" + "d delete value from hash table\n" + "l lookup value in hash table\n" + "n show size of hash table\n" + "c show number of entries\n" + "t dump whole hash table\n" + "+ increase hash table (private func)\n" + "- decrease hash table (private func)\n" + "b print hash_t_bit value\n" + "p turn prompt on\n" + "s switch to non-functioning allocator\n" + "q quit"; + + if (!h) + puts("hash_create failed"); + + for (;;) { + if (prompt) + putchar('>'); + fflush(stdout); + + if (!fgets(in, sizeof(input_t), stdin)) + break; + + switch(in[0]) { + case '?': + puts(help); + break; + case 'b': + printf("%d\n", hash_val_t_bit); + break; + case 'a': + if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { + puts("what?"); + break; + } + key = dupstring(tok1); + val = dupstring(tok2); + + if (!key || !val) { + puts("out of memory"); + free((void *) key); + free(val); + } + + if (!hash_alloc_insert(h, key, val)) { + puts("hash_alloc_insert failed"); + free((void *) key); + free(val); + break; + } + break; + case 'd': + if (tokenize(in+1, &tok1, (char **) 0) != 1) { + puts("what?"); + break; + } + hn = hash_lookup(h, tok1); + if (!hn) { + puts("hash_lookup failed"); + break; + } + val = hnode_get(hn); + key = hnode_getkey(hn); + hash_scan_delfree(h, hn); + free((void *) key); + free(val); + break; + case 'l': + if (tokenize(in+1, &tok1, (char **) 0) != 1) { + puts("what?"); + break; + } + hn = hash_lookup(h, tok1); + if (!hn) { + puts("hash_lookup failed"); + break; + } + val = hnode_get(hn); + puts(val); + break; + case 'n': + printf("%lu\n", (unsigned long) hash_size(h)); + break; + case 'c': + printf("%lu\n", (unsigned long) hash_count(h)); + break; + case 't': + hash_scan_begin(&hs, h); + while ((hn = hash_scan_next(&hs))) + printf("%s\t%s\n", (char*) hnode_getkey(hn), + (char*) hnode_get(hn)); + break; + case '+': + grow_table(h); /* private function */ + break; + case '-': + shrink_table(h); /* private function */ + break; + case 'q': + exit(0); + break; + case '\0': + break; + case 'p': + prompt = 1; + break; + case 's': + hash_set_allocator(h, new_node, del_node, NULL); + break; + default: + putchar('?'); + putchar('\n'); + break; + } + } + + return 0; +} + +#endif diff --git a/veejay-current/libstream/hash.h b/veejay-current/libstream/hash.h new file mode 100644 index 00000000..9677632b --- /dev/null +++ b/veejay-current/libstream/hash.h @@ -0,0 +1,240 @@ +/* + * Hash Table Data Type + * Copyright (C) 1997 Kaz Kylheku + * + * Free Software License: + * + * All rights are reserved by the author, with the following exceptions: + * Permission is granted to freely reproduce and distribute this software, + * possibly in exchange for a fee, provided that this copyright notice appears + * intact. Permission is also granted to adapt this software to produce + * derivative works, as long as the modified versions carry this copyright + * notice and additional notices stating that the work has been modified. + * This source code may be translated into executable form and incorporated + * into proprietary software; there is no requirement for such software to + * contain a copyright notice related to this source. + * + * $Id: hash.h,v 1.1.1.1 2004/10/27 23:48:59 niels Exp $ + * $Name: $ + */ + +#ifndef HASH_H +#define HASH_H + +#include +#ifdef KAZLIB_SIDEEFFECT_DEBUG +#include "sfx.h" +#endif + +/* + * Blurb for inclusion into C++ translation units + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned long hashcount_t; +#define HASHCOUNT_T_MAX ULONG_MAX + +typedef unsigned long hash_val_t; +#define HASH_VAL_T_MAX ULONG_MAX + +extern int hash_val_t_bit; + +#ifndef HASH_VAL_T_BIT +#define HASH_VAL_T_BIT ((int) hash_val_t_bit) +#endif + +/* + * Hash chain node structure. + * Notes: + * 1. This preprocessing directive is for debugging purposes. The effect is + * that if the preprocessor symbol KAZLIB_OPAQUE_DEBUG is defined prior to the + * inclusion of this header, then the structure shall be declared as having + * the single member int __OPAQUE__. This way, any attempts by the + * client code to violate the principles of information hiding (by accessing + * the structure directly) can be diagnosed at translation time. However, + * note the resulting compiled unit is not suitable for linking. + * 2. This is a pointer to the next node in the chain. In the last node of a + * chain, this pointer is null. + * 3. The key is a pointer to some user supplied data that contains a unique + * identifier for each hash node in a given table. The interpretation of + * the data is up to the user. When creating or initializing a hash table, + * the user must supply a pointer to a function for comparing two keys, + * and a pointer to a function for hashing a key into a numeric value. + * 4. The value is a user-supplied pointer to void which may refer to + * any data object. It is not interpreted in any way by the hashing + * module. + * 5. The hashed key is stored in each node so that we don't have to rehash + * each key when the table must grow or shrink. + */ + +typedef struct hnode_t { + #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) /* 1 */ + struct hnode_t *hash_next; /* 2 */ + const void *hash_key; /* 3 */ + void *hash_data; /* 4 */ + hash_val_t hash_hkey; /* 5 */ + #else + int hash_dummy; + #endif +} hnode_t; + +/* + * The comparison function pointer type. A comparison function takes two keys + * and produces a value of -1 if the left key is less than the right key, a + * value of 0 if the keys are equal, and a value of 1 if the left key is + * greater than the right key. + */ + +typedef int (*hash_comp_t)(const void *, const void *); + +/* + * The hashing function performs some computation on a key and produces an + * integral value of type hash_val_t based on that key. For best results, the + * function should have a good randomness properties in *all* significant bits + * over the set of keys that are being inserted into a given hash table. In + * particular, the most significant bits of hash_val_t are most significant to + * the hash module. Only as the hash table expands are less significant bits + * examined. Thus a function that has good distribution in its upper bits but + * not lower is preferrable to one that has poor distribution in the upper bits + * but not the lower ones. + */ + +typedef hash_val_t (*hash_fun_t)(const void *); + +/* + * allocator functions + */ + +typedef hnode_t *(*hnode_alloc_t)(void *); +typedef void (*hnode_free_t)(hnode_t *, void *); + +/* + * This is the hash table control structure. It keeps track of information + * about a hash table, as well as the hash table itself. + * Notes: + * 1. Pointer to the hash table proper. The table is an array of pointers to + * hash nodes (of type hnode_t). If the table is empty, every element of + * this table is a null pointer. A non-null entry points to the first + * element of a chain of nodes. + * 2. This member keeps track of the size of the hash table---that is, the + * number of chain pointers. + * 3. The count member maintains the number of elements that are presently + * in the hash table. + * 4. The maximum count is the greatest number of nodes that can populate this + * table. If the table contains this many nodes, no more can be inserted, + * and the hash_isfull() function returns true. + * 5. The high mark is a population threshold, measured as a number of nodes, + * which, if exceeded, will trigger a table expansion. Only dynamic hash + * tables are subject to this expansion. + * 6. The low mark is a minimum population threshold, measured as a number of + * nodes. If the table population drops below this value, a table shrinkage + * will occur. Only dynamic tables are subject to this reduction. No table + * will shrink beneath a certain absolute minimum number of nodes. + * 7. This is the a pointer to the hash table's comparison function. The + * function is set once at initialization or creation time. + * 8. Pointer to the table's hashing function, set once at creation or + * initialization time. + * 9. The current hash table mask. If the size of the hash table is 2^N, + * this value has its low N bits set to 1, and the others clear. It is used + * to select bits from the result of the hashing function to compute an + * index into the table. + * 10. A flag which indicates whether the table is to be dynamically resized. It + * is set to 1 in dynamically allocated tables, 0 in tables that are + * statically allocated. + */ + +typedef struct hash_t { + #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) + struct hnode_t **hash_table; /* 1 */ + hashcount_t hash_nchains; /* 2 */ + hashcount_t hash_nodecount; /* 3 */ + hashcount_t hash_maxcount; /* 4 */ + hashcount_t hash_highmark; /* 5 */ + hashcount_t hash_lowmark; /* 6 */ + hash_comp_t hash_compare; /* 7 */ + hash_fun_t hash_function; /* 8 */ + hnode_alloc_t hash_allocnode; + hnode_free_t hash_freenode; + void *hash_context; + hash_val_t hash_mask; /* 9 */ + int hash_dynamic; /* 10 */ + #else + int hash_dummy; + #endif +} hash_t; + +/* + * Hash scanner structure, used for traversals of the data structure. + * Notes: + * 1. Pointer to the hash table that is being traversed. + * 2. Reference to the current chain in the table being traversed (the chain + * that contains the next node that shall be retrieved). + * 3. Pointer to the node that will be retrieved by the subsequent call to + * hash_scan_next(). + */ + +typedef struct hscan_t { + #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) + hash_t *hash_table; /* 1 */ + hash_val_t hash_chain; /* 2 */ + hnode_t *hash_next; /* 3 */ + #else + int hash_dummy; + #endif +} hscan_t; + +extern hash_t *hash_create(hashcount_t, hash_comp_t, hash_fun_t); +extern void hash_set_allocator(hash_t *, hnode_alloc_t, hnode_free_t, void *); +extern void hash_destroy(hash_t *); +extern void hash_free_nodes(hash_t *); +extern void hash_free(hash_t *); +extern hash_t *hash_init(hash_t *, hashcount_t, hash_comp_t, + hash_fun_t, hnode_t **, hashcount_t); +extern void hash_insert(hash_t *, hnode_t *, const void *); +extern hnode_t *hash_lookup(hash_t *, const void *); +extern hnode_t *hash_delete(hash_t *, hnode_t *); +extern int hash_alloc_insert(hash_t *, const void *, void *); +extern void hash_delete_free(hash_t *, hnode_t *); + +extern void hnode_put(hnode_t *, void *); +extern void *hnode_get(hnode_t *); +extern const void *hnode_getkey(hnode_t *); +extern hashcount_t hash_count(hash_t *); +extern hashcount_t hash_size(hash_t *); + +extern int hash_isfull(hash_t *); +extern int hash_isempty(hash_t *); + +extern void hash_scan_begin(hscan_t *, hash_t *); +extern hnode_t *hash_scan_next(hscan_t *); +extern hnode_t *hash_scan_delete(hash_t *, hnode_t *); +extern void hash_scan_delfree(hash_t *, hnode_t *); + +extern int hash_verify(hash_t *); + +extern hnode_t *hnode_create(void *); +extern hnode_t *hnode_init(hnode_t *, void *); +extern void hnode_destroy(hnode_t *); + +#if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) +#ifdef KAZLIB_SIDEEFFECT_DEBUG +#define hash_isfull(H) (SFX_CHECK(H)->hash_nodecount == (H)->hash_maxcount) +#else +#define hash_isfull(H) ((H)->hash_nodecount == (H)->hash_maxcount) +#endif +#define hash_isempty(H) ((H)->hash_nodecount == 0) +#define hash_count(H) ((H)->hash_nodecount) +#define hash_size(H) ((H)->hash_nchains) +#define hnode_get(N) ((N)->hash_data) +#define hnode_getkey(N) ((N)->hash_key) +#define hnode_put(N, V) ((N)->hash_data = (V)) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/veejay-current/veejay/v4lutils.c b/veejay-current/libstream/v4lutils.c similarity index 99% rename from veejay-current/veejay/v4lutils.c rename to veejay-current/libstream/v4lutils.c index d9150871..fbfb6f2b 100644 --- a/veejay-current/veejay/v4lutils.c +++ b/veejay-current/libstream/v4lutils.c @@ -36,8 +36,8 @@ #include #include #include -#include -#include +//#include +#include #ifdef V4L_DEBUG #undef V4L_DEBUG #endif diff --git a/veejay-current/veejay/v4lutils.h b/veejay-current/libstream/v4lutils.h similarity index 100% rename from veejay-current/veejay/v4lutils.h rename to veejay-current/libstream/v4lutils.h diff --git a/veejay-current/veejay/vj-shm.c b/veejay-current/libstream/vj-shm.c similarity index 99% rename from veejay-current/veejay/vj-shm.c rename to veejay-current/libstream/vj-shm.c index 100fef3b..0816a653 100644 --- a/veejay-current/veejay/vj-shm.c +++ b/veejay-current/libstream/vj-shm.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include diff --git a/veejay-current/veejay/vj-shm.h b/veejay-current/libstream/vj-shm.h similarity index 100% rename from veejay-current/veejay/vj-shm.h rename to veejay-current/libstream/vj-shm.h diff --git a/veejay-current/veejay/vj-tag.c b/veejay-current/libstream/vj-tag.c similarity index 99% rename from veejay-current/veejay/vj-tag.c rename to veejay-current/libstream/vj-tag.c index 230366c4..d73002f9 100644 --- a/veejay-current/veejay/vj-tag.c +++ b/veejay-current/libstream/vj-tag.c @@ -25,19 +25,17 @@ */ #include #include -#include -#include +#include +#include #include #ifdef HAVE_V4L -#include +#include #include #endif -#include #include -#include -#include -#include -#include +#include +#include +#include #include #include #include @@ -87,8 +85,8 @@ int vj_tag_size() return this_tag_id; } -void vj_tag_set_veejay_t(veejay_t *info) { - _tag_info = info; +void vj_tag_set_veejay_t(void *info) { + _tag_info = (veejay_t*)info; } static inline hash_val_t int_tag_hash(const void *key) diff --git a/veejay-current/veejay/vj-tag.h b/veejay-current/libstream/vj-tag.h similarity index 97% rename from veejay-current/veejay/vj-tag.h rename to veejay-current/libstream/vj-tag.h index 98f082a4..be66f429 100644 --- a/veejay-current/veejay/vj-tag.h +++ b/veejay-current/libstream/vj-tag.h @@ -39,9 +39,10 @@ #include #include -#include -#include -#include +#include +#include +#include +#include typedef struct { v4l_video *v4l[VJ_TAG_MAX_V4L]; @@ -222,7 +223,7 @@ int vj_tag_set_contrast(int t1, int value); int vj_tag_set_color(int t1, int value); int vj_tag_set_hue(int t1, int value); -void vj_tag_set_veejay_t(veejay_t *info); +void vj_tag_set_veejay_t(void *info); int vj_tag_set_manual_fader(int t1, int value ); diff --git a/veejay-current/veejay/vj-v4lvideo.c b/veejay-current/libstream/vj-v4lvideo.c similarity index 98% rename from veejay-current/veejay/vj-v4lvideo.c rename to veejay-current/libstream/vj-v4lvideo.c index fc9b388c..e6ca12ee 100644 --- a/veejay-current/veejay/vj-v4lvideo.c +++ b/veejay-current/libstream/vj-v4lvideo.c @@ -20,15 +20,14 @@ #ifdef HAVE_V4L #include #include -#include -#include +#include +#include #include #include #include #include -#include +#include #include -#include #define VJ_V4L_DEBUG diff --git a/veejay-current/veejay/vj-v4lvideo.h b/veejay-current/libstream/vj-v4lvideo.h similarity index 100% rename from veejay-current/veejay/vj-v4lvideo.h rename to veejay-current/libstream/vj-v4lvideo.h diff --git a/veejay-current/veejay/vj-yuv4mpeg.c b/veejay-current/libstream/vj-yuv4mpeg.c similarity index 99% rename from veejay-current/veejay/vj-yuv4mpeg.c rename to veejay-current/libstream/vj-yuv4mpeg.c index 6b95b303..03ac1a37 100644 --- a/veejay-current/veejay/vj-yuv4mpeg.c +++ b/veejay-current/libstream/vj-yuv4mpeg.c @@ -23,8 +23,7 @@ #include #include #include -#include -#include "vj-yuv4mpeg.h" +#include #include /* see vj-v4lvideo for details about structure */ diff --git a/veejay-current/veejay/vj-yuv4mpeg.h b/veejay-current/libstream/vj-yuv4mpeg.h similarity index 97% rename from veejay-current/veejay/vj-yuv4mpeg.h rename to veejay-current/libstream/vj-yuv4mpeg.h index 07280e10..8e09a62a 100644 --- a/veejay-current/veejay/vj-yuv4mpeg.h +++ b/veejay-current/libstream/vj-yuv4mpeg.h @@ -18,8 +18,9 @@ */ #ifndef VJ_YUV4MPEG_H #define VJ_YUV4MPEG_H -#include -#include "mpegconsts.h" +#include +#include + typedef struct { y4m_stream_info_t streaminfo; y4m_frame_info_t frameinfo; diff --git a/veejay-current/libvjnet/Makefile.am b/veejay-current/libvjnet/Makefile.am index 9f13de20..8d16e655 100644 --- a/veejay-current/libvjnet/Makefile.am +++ b/veejay-current/libvjnet/Makefile.am @@ -17,12 +17,3 @@ libvjnet_la_LDFLAGS = $(VJNET_LIB_OPTS) \ -L$(top_builddir)/libvjmem -lvjmem \ -L$(top_builddir)/libvjmsg -lvjmsg \ -DDYNAMIC -O3 -Wall - -SERVER_BIN = dummy - -check_PROGRAMS = $(SERVER_BIN) - -dummy_SOURCES = dummy.c -dummy_LDADD = libvjnet.la \ - $(top_builddir)/libvjmsg/libvjmsg.la \ - $(top_builddir)/libvjmem/libvjmem.la diff --git a/veejay-current/libvjnet/Makefile.in b/veejay-current/libvjnet/Makefile.in index c5415be0..a8eb895d 100644 --- a/veejay-current/libvjnet/Makefile.in +++ b/veejay-current/libvjnet/Makefile.in @@ -247,16 +247,6 @@ libvjnet_la_LDFLAGS = $(VJNET_LIB_OPTS) \ -L$(top_builddir)/libvjmsg -lvjmsg \ -DDYNAMIC -O3 -Wall - -SERVER_BIN = dummy - -check_PROGRAMS = $(SERVER_BIN) - -dummy_SOURCES = dummy.c -dummy_LDADD = libvjnet.la \ - $(top_builddir)/libvjmsg/libvjmsg.la \ - $(top_builddir)/libvjmem/libvjmem.la - subdir = libvjnet ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs @@ -268,17 +258,11 @@ libvjnet_la_LIBADD = am_libvjnet_la_OBJECTS = packet.lo mcastsender.lo mcastreceiver.lo \ cmd.lo vj-server.lo vj-client.lo libvjnet_la_OBJECTS = $(am_libvjnet_la_OBJECTS) -check_PROGRAMS = dummy$(EXEEXT) -am_dummy_OBJECTS = dummy.$(OBJEXT) -dummy_OBJECTS = $(am_dummy_OBJECTS) -dummy_DEPENDENCIES = libvjnet.la $(top_builddir)/libvjmsg/libvjmsg.la \ - $(top_builddir)/libvjmem/libvjmem.la -dummy_LDFLAGS = DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles -@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/cmd.Plo ./$(DEPDIR)/dummy.Po \ +@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/cmd.Plo \ @AMDEP_TRUE@ ./$(DEPDIR)/mcastreceiver.Plo \ @AMDEP_TRUE@ ./$(DEPDIR)/mcastsender.Plo ./$(DEPDIR)/packet.Plo \ @AMDEP_TRUE@ ./$(DEPDIR)/vj-client.Plo ./$(DEPDIR)/vj-server.Plo @@ -289,9 +273,9 @@ LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) \ CCLD = $(CC) LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ -DIST_SOURCES = $(libvjnet_la_SOURCES) $(dummy_SOURCES) +DIST_SOURCES = $(libvjnet_la_SOURCES) DIST_COMMON = $(srcdir)/Makefile.in Makefile.am -SOURCES = $(libvjnet_la_SOURCES) $(dummy_SOURCES) +SOURCES = $(libvjnet_la_SOURCES) all: all-am @@ -314,16 +298,6 @@ clean-noinstLTLIBRARIES: libvjnet.la: $(libvjnet_la_OBJECTS) $(libvjnet_la_DEPENDENCIES) $(LINK) $(libvjnet_la_LDFLAGS) $(libvjnet_la_OBJECTS) $(libvjnet_la_LIBADD) $(LIBS) -clean-checkPROGRAMS: - @list='$(check_PROGRAMS)'; for p in $$list; do \ - f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ - echo " rm -f $$p $$f"; \ - rm -f $$p $$f ; \ - done -dummy$(EXEEXT): $(dummy_OBJECTS) $(dummy_DEPENDENCIES) - @rm -f dummy$(EXEEXT) - $(LINK) $(dummy_LDFLAGS) $(dummy_OBJECTS) $(dummy_LDADD) $(LIBS) - mostlyclean-compile: -rm -f *.$(OBJEXT) core *.core @@ -331,7 +305,6 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dummy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mcastreceiver.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mcastsender.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet.Plo@am__quote@ @@ -467,7 +440,6 @@ distdir: $(DISTFILES) fi; \ done check-am: all-am - $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) check: check-am all-am: Makefile $(LTLIBRARIES) @@ -499,8 +471,8 @@ maintainer-clean-generic: -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am -clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ - clean-noinstLTLIBRARIES mostlyclean-am +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) @@ -548,9 +520,9 @@ ps-am: uninstall-am: uninstall-info-am -.PHONY: CTAGS GTAGS all all-am check check-am clean clean-checkPROGRAMS \ - clean-generic clean-libtool clean-noinstLTLIBRARIES ctags \ - distclean distclean-compile distclean-generic distclean-libtool \ +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am info info-am install \ install-am install-data install-data-am install-exec \ install-exec-am install-info install-info-am install-man \ diff --git a/veejay-current/libvjnet/packet.c b/veejay-current/libvjnet/packet.c index bc4a0215..3909db03 100644 --- a/veejay-current/libvjnet/packet.c +++ b/veejay-current/libvjnet/packet.c @@ -33,17 +33,22 @@ packet_header_t packet_construct_header(uint8_t flag) gettimeofday(&tv, NULL); packet_header_t header; header.flag = flag; - header.seq_num = 0; // not set - header.sec = tv.tv_sec; - header.usec = tv.tv_usec; - header.timeout = 50000; + header.seq_num = htons(0); // not set + header.sec = htonl(tv.tv_sec); + header.usec = htonl(tv.tv_usec); + header.timeout = htonl(40000); return header; } packet_header_t packet_get_header(const void *data) { - packet_header_t h; - veejay_memcpy( &h, data, sizeof(packet_header_t) ); + packet_header_t h,tmp; + veejay_memcpy( &tmp, data, sizeof(packet_header_t) ); + h.flag = tmp.flag; + h.seq_num = ntohs( tmp.seq_num ); + h.sec = ntohl( tmp.sec ); + h.usec = ntohl( tmp.usec ); + h.timeout = ntohl( tmp.timeout ); return h; } @@ -66,6 +71,7 @@ int packet_put_data(packet_header_t *h, frame_info_t *i , void *payload, const size_t len = sizeof( packet_header_t ); veejay_memcpy( payload, h , len ); + if(h->flag) { veejay_memcpy( payload + len, i , sizeof( frame_info_t )); diff --git a/veejay-current/libyuv/Makefile.am b/veejay-current/libyuv/Makefile.am new file mode 100644 index 00000000..54560fac --- /dev/null +++ b/veejay-current/libyuv/Makefile.am @@ -0,0 +1,19 @@ +# Makefile for veejay + +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir) -I$(includedir) -I$(top_srcdir)/vjmem -I$(top_srcdir)/vjmsg + +YUV_LIB_OPTS = -export-dynamic + +YUV_LIB_FILE = libyuv.la + +noinst_LTLIBRARIES = $(YUV_LIB_FILE) + +libyuv_la_SOURCES = subsample.c yuvconv.c + + +libyuv_la_LDFLAGS = $(YUV_LIB_OPTS) \ + -L$(top_builddir)/libvjmem -lvjmem \ + -L$(top_builddir)/libvjmsg -lvjmsg \ + -DDYNAMIC -O3 -Wall diff --git a/veejay-current/veejay/subsample.c b/veejay-current/libyuv/subsample.c similarity index 99% rename from veejay-current/veejay/subsample.c rename to veejay-current/libyuv/subsample.c index 3804dde3..8206d7e2 100644 --- a/veejay-current/veejay/subsample.c +++ b/veejay-current/libyuv/subsample.c @@ -28,11 +28,11 @@ #include #include #include -#include +#include #include #include -#include +#include const char *ssm_id[SSM_COUNT] = { diff --git a/veejay-current/libyuv/yuvconv.c b/veejay-current/libyuv/yuvconv.c new file mode 100644 index 00000000..a2fabcf9 --- /dev/null +++ b/veejay-current/libyuv/yuvconv.c @@ -0,0 +1,224 @@ +/* veejay - Linux VeeJay + * (C) 2002-2004 Niels Elburg + * + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +/* this routine is the same as frame_YUV422_to_YUV420P , unpack + * libdv's 4:2:2-packed into 4:2:0 planar + * See http://mjpeg.sourceforge.net/ (MJPEG Tools) (lav-common.c) + */ +void yuy2toyv12(uint8_t * _y, uint8_t * _u, uint8_t * _v, uint8_t * input, + int width, int height) +{ + + int i, j, w2; + uint8_t *y, *u, *v; + + w2 = width / 2; + + //I420 + y = _y; + v = _v; + u = _u; + + for (i = 0; i < height; i += 4) { + /* top field scanline */ + for (j = 0; j < w2; j++) { + /* packed YUV 422 is: Y[i] U[i] Y[i+1] V[i] */ + *(y++) = *(input++); + *(u++) = *(input++); + *(y++) = *(input++); + *(v++) = *(input++); + } + for (j = 0; j < w2; j++) + { + *(y++) = *(input++); + *(u++) = *(input++); + *(y++) = *(input++); + *(v++) = *(input++); + + } + + /* next two scanlines, one frome each field , interleaved */ + for (j = 0; j < w2; j++) { + /* skip every second line for U and V */ + *(y++) = *(input++); + input++; + *(y++) = *(input++); + input++; + } + /* bottom field scanline*/ + for (j = 0; j < w2; j++) { + /* skip every second line for U and V */ + *(y++) = *(input++); + input++; + *(y++) = *(input++); + input++; + } + + } +} +/* convert 4:2:0 to yuv 4:2:2 packed */ +void yuv422p_to_yuv422(uint8_t * yuv420[3], uint8_t * dest, int width, + int height) +{ + unsigned int x, y; + + + for (y = 0; y < height; ++y) { + uint8_t *Y = yuv420[0] + y * width; + uint8_t *Cb = yuv420[1] + (y / 2) * (width); + uint8_t *Cr = yuv420[2] + (y / 2) * (width); + for (x = 0; x < width; x +=2) { + *(dest + 0) = Y[0]; + *(dest + 1) = Cb[0]; + *(dest + 2) = Y[1]; + *(dest + 3) = Cr[0]; + dest += 4; + Y += 2; + ++Cb; + ++Cr; + } + } +} + + + +/* convert 4:2:0 to yuv 4:2:2 */ +void yuv420p_to_yuv422(uint8_t * yuv420[3], uint8_t * dest, int width, + int height) +{ + unsigned int x, y; + + + for (y = 0; y < height; ++y) { + uint8_t *Y = yuv420[0] + y * width; + uint8_t *Cb = yuv420[1] + (y >> 1) * (width >> 1); + uint8_t *Cr = yuv420[2] + (y >> 1) * (width >> 1); + for (x = 0; x < width; x += 2) { + *(dest + 0) = Y[0]; + *(dest + 1) = Cb[0]; + *(dest + 2) = Y[1]; + *(dest + 3) = Cr[0]; + dest += 4; + Y += 2; + ++Cb; + ++Cr; + } + } +} + +/* convert yuv422 planar to YUYV */ +void yuv422_to_yuyv(uint8_t *yuv422[3], uint8_t *pixels, int w, int h) +{ + int x,y; + uint8_t *Y = yuv422[0]; + uint8_t *U = yuv422[1]; + uint8_t *V = yuv422[2]; // U Y V Y + for(y = 0; y < h; y ++ ) + { + Y = yuv422[0] + y * w; + U = yuv422[1] + (y>>1) * w; + V = yuv422[2] + (y>>1) * w; + /* for(x=0; x < w; x+= 2) + { + *(pixels + 0) = Y[0]; + *(pixels + 1) = U[0]; + *(pixels + 2) = Y[1]; + *(pixels + 3) = V[0]; + pixels += 4; + Y+=2; + ++U; + ++V; + } + */ + for( x = 0 ; x < w ; x += 4 ) + { + *(pixels + 0) = Y[0]; + *(pixels + 1) = U[0]; + *(pixels + 2) = Y[1]; + *(pixels + 3) = V[0]; + *(pixels + 4) = Y[2]; + *(pixels + 5) = U[1]; + *(pixels + 6) = Y[3]; + *(pixels + 7) = V[1]; + pixels += 8; + Y+=4; + U+=2; + V+=2; + } + } +} + +/* lav_common - some general utility functionality used by multiple + lavtool utilities. */ + +/* Copyright (C) 2000, Rainer Johanni, Andrew Stevens */ +/* - added scene change detection code 2001, pHilipp Zabel */ +/* - broke some code out to lav_common.h and lav_common.c + * July 2001, Shawn Sulma . In doing this, + * I replaced the large number of globals with a handful of structs + * that are passed into the appropriate methods. Check lav_common.h + * for the structs. I'm sure some of what I've done is inefficient, + * subtly incorrect or just plain Wrong. Feedback is welcome. + */ + + +/*********************** + * + * Take a random(ish) clipd mean of a frame luma and chroma + * Its intended as a rough and ready hash of frame content. + * The idea is that changes above a certain threshold are treated as + * scene changes. + * + **********************/ + +int luminance_mean(uint8_t * frame[], int w, int h) +{ + uint8_t *p; + uint8_t *lim; + int sum = 0; + int count = 0; + p = frame[0]; + lim = frame[0] + w * (h - 1); + while (p < lim) { + sum += (p[0] + p[1]) + (p[w - 3] + p[w - 2]); + p += 31; + count += 4; + } + + w = w / 2; + h = h / 2; + + p = frame[1]; + lim = frame[1] + w * (h - 1); + while (p < lim) { + sum += (p[0] + p[1]) + (p[w - 3] + p[w - 2]); + p += 31; + count += 4; + } + p = frame[2]; + lim = frame[2] + w * (h - 1); + while (p < lim) { + sum += (p[0] + p[1]) + (p[w - 3] + p[w - 2]); + p += 31; + count += 4; + } + return sum / count; +} diff --git a/veejay-current/libyuv/yuvconv.h b/veejay-current/libyuv/yuvconv.h new file mode 100644 index 00000000..6924306d --- /dev/null +++ b/veejay-current/libyuv/yuvconv.h @@ -0,0 +1,64 @@ +#ifndef YUVCONF_H +#define YUVCONF_H + +/* + * subsample.h: Routines to do chroma subsampling. ("Work In Progress") + * + * + * Copyright (C) 2001 Matthew J. Marjanovic + * 2004 Niels Elburg + * + * + * 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. + * + */ + +typedef enum subsample_mode { + SSM_UNKNOWN = 0, + SSM_420_JPEG_TR = 1, + SSM_420_JPEG_BOX = 2, + SSM_420_MPEG2 = 3, + SSM_422_444 = 4, + SSM_420_422 = 5, + SSM_COUNT = 6, +} subsample_mode_t; + +extern const char *ssm_id[SSM_COUNT]; +extern const char *ssm_description[SSM_COUNT]; + +void subsample_init(void); + +void chroma_subsample(subsample_mode_t mode, uint8_t * ycbcr[], + int width, int height); + +void chroma_supersample(subsample_mode_t mode, uint8_t * ycbcr[], + int width, int height); + +// yuv 4:2:2 packed to yuv 4:2:0 planar +void yuy2toyv12( uint8_t *y, uint8_t *u, uint8_t *v, uint8_t *in, int w, int h); +// yuv 4:2:2 planar to yuv 4:2:2 packed +void yuv422p_to_yuv422( uint8_t *yuv422[3], uint8_t *dst, int w, int h ); + +// yuv 4:2:2 planar to yuv 4:2:0 planar +void yuv420p_to_yuv422( uint8_t *yuv420[3], uint8_t *dst, int w, int h ); + +// yuv 4:2:2 planar to YUYV +void yuv422_to_yuyv( uint8_t *yuv422[3], uint8_t *dst, int w, int h ); + +// scene detection +int luminance_mean(uint8_t * frame[], int w, int h); + + +#endif diff --git a/veejay-current/veejay/Makefile.am b/veejay-current/veejay/Makefile.am index 5f5c1e35..943b33ef 100644 --- a/veejay-current/veejay/Makefile.am +++ b/veejay-current/veejay/Makefile.am @@ -2,14 +2,18 @@ MAINTAINERCLEANFILES = Makefile.in -INCLUDES = -I$(top_srcdir)/ffmpeg/ffmpeg/libavcodec -I$(top_srcdir)/ffmpeg/ffmpeg/libavformat \ - -I$(top_srcdir) -I$(includedir) \ +INCLUDES = -I$(top_srcdir) -I$(includedir) \ -DG_LOG_DOMAIN=\"veejay\" -DVEEJAY_VERSION=\"$(VERSION)\" \ -I /usr/X11R6/include -I$(top_srcdir)/libOSC -I$(top_srcdir)/utils \ -I$(top_srcdir)/libvje \ -I$(top_srcdir)/libvjmem \ -I$(top_srcdir)/libvjmsg \ -I$(top_srcdir)/libvjnet \ + -I$(top_srcdir)/libel \ + -I$(top_srcdir)/libstream \ + -I$(top_scrdir)/libyuv \ + -I$(top_srcdir)/ffmpeg/ffmpeg/libavcodec \ + -I$(top_srcdir)/ffmpeg/ffmpeg/libavformat \ $(AVIPLAY_CFLAGS) ${XML_CFLAGS} ${GTK_CFLAGS} ${DV_FLAGS} ${X_CFLAGS} ${XML_CPPFLAGS} \ $(DIRECTFB_CFLAGS) ${SDL_CFLAGS} ${JPEG_CFLAGS} ${PTHREAD_CFLAGS} ${SAMPLERATE_CFLAGS} @@ -29,16 +33,15 @@ VEEJAY_ALL_LIB_OPTS = \ liblavjpeg_la_SOURCES = jpegutils.c liblavjpeg_la_LDFLAGS = $(VEEJAY_ALL_LIB_OPTS) $(JPEG_LIBS) -libveejay_la_SOURCES = vj-misc.c subsample.c hash.c avilib.c vj-shm.c lav_io.c \ - v4lutils.c vj-plugin.c vj-osc.c vj-event.c vj-pjack.c vj-perform.c \ +libveejay_la_SOURCES = vj-misc.c hash.c \ + vj-plugin.c vj-osc.c vj-event.c vj-pjack.c vj-perform.c \ vj-sdl.c vj-dfb.c samplerecord.c \ - vj-yuv4mpeg.c vj-v4lvideo.c vj-dv.c vj-avcodec.c vj-tag.c vj-avformat.c vj-el.c \ liblavplayvj.c libveejay_la_LDFLAGS = $(VEEJAY_ALL_LIB_OPTS) \ - -L$(top_builddir)/ffmpeg/ffmpeg/libavcodec/ -lavcodec \ - -L$(top_builddir)/ffmpeg/ffmpeg/libavformat/ -lavformat \ + -L$(top_builddir)/ffmpeg/ffmpeg/libavcodec -lavcodec \ + -L$(top_builddir)/ffmpeg/ffmpeg/libavformat -lavformat \ -L$(top_builddir)/libOSC / -lOSC \ -L$(top_builddir)/utils/ -lmjpegutils \ -L$(top_builddir)/bio2jack/ -lbio2jack \ @@ -47,8 +50,12 @@ libveejay_la_LDFLAGS = $(VEEJAY_ALL_LIB_OPTS) \ -L$(top_builddir)/libvje -lvje \ -L$(top_builddir)/libsample -lsample \ -L$(top_builddir)/libvjnet -lvjnet \ + -L$(top_builddir)/libel -lel \ + -L$(top_builddir)/libstream -lstream \ + -L$(top_builddir)/libyuv -lyuv \ + -L$(top_builddir)/veejay/ -llavjpeg \ ${SDL_LIBS} ${DIRECTFB_LIBS} ${PTHREAD_LIBS} ${X_LIBS} \ - ${XML_LIBS} ${JPEG_LIBS} \ + ${XML_LIBS} ${JPEG_LIBS} ${JACK_LIBS} \ ${DV_LIBS} ${SAMPLERATE_LIBS} \ -DDYNAMIC -O3 -Wall -rdynamic @@ -58,7 +65,7 @@ VEEJAY_BIN = veejay bin_PROGRAMS = $(VEEJAY_BIN) -veejay_headers = vj-lib.h vj-event.h libveejay.h vj-misc.h +veejay_headers = vims.h -veejay_SOURCES = veejay.c ${veejay_headers} -veejay_LDADD = libveejay.la liblavjpeg.la @LIBGETOPT_LIB@ @JACK_LIBS@ +veejay_SOURCES = veejay.c ${veejay_headers} +veejay_LDADD = libveejay.la @LIBGETOPT_LIB@ -L$(top_builddir)/libvjnet -lvjnet -L$(top_builddir)/ffmpeg/ffmpeg/libavcodec -lavcodec diff --git a/veejay-current/veejay/liblavplayvj.c b/veejay-current/veejay/liblavplayvj.c index c29902ad..4220f22e 100644 --- a/veejay-current/veejay/liblavplayvj.c +++ b/veejay-current/veejay/liblavplayvj.c @@ -62,7 +62,7 @@ #include #include "jpegutils.h" #include "vj-event.h" -#include "vj-shm.h" +#include #ifndef X_DISPLAY_MISSING #include #include @@ -85,8 +85,8 @@ #include #include #include -#include - +#include +#include // following struct copied from ../utils/videodev.h /* This is identical with the mgavideo internal params struct, @@ -178,9 +178,9 @@ struct mjpeg_params #include #include //#include "vj-common.h" -#include "vj-tag.h" +#include #include "libveejay.h" -#include "mjpeg_types.h" +#include #include "vj-perform.h" #include #include "mjpeg_types.h" @@ -188,7 +188,7 @@ struct mjpeg_params #ifdef HAVE_DIRECTFB #include "vj-dfb.h" #endif -#include "subsample.h" + /* TODO: set_clip and clip_action clean up; important items need more updates */ /* On some systems MAP_FAILED seems to be missing */ @@ -199,7 +199,7 @@ struct mjpeg_params #define HZ 100 -#include +#include #define VALUE_NOT_FILLED -10000 static float time_frame = 0; diff --git a/veejay-current/veejay/samplerecord.c b/veejay-current/veejay/samplerecord.c index 3b8e74ac..ebb91bd7 100644 --- a/veejay-current/veejay/samplerecord.c +++ b/veejay-current/veejay/samplerecord.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/veejay-current/veejay/vj-event.c b/veejay-current/veejay/vj-event.c index bbb340bc..dbe2e022 100644 --- a/veejay-current/veejay/vj-event.c +++ b/veejay-current/veejay/vj-event.c @@ -33,13 +33,13 @@ #include #include #include -#include +#include #include -#include -#include +#include +#include #include #include -#include +#include #include /* Highest possible SDL Key identifier */ #define MAX_SDL_KEY 350 diff --git a/veejay-current/veejay/vj-lib.h b/veejay-current/veejay/vj-lib.h index c45aae62..569e3c0a 100644 --- a/veejay-current/veejay/vj-lib.h +++ b/veejay-current/veejay/vj-lib.h @@ -24,17 +24,17 @@ #include #include #include +#include #ifdef HAVE_V4L -#include "vj-v4lvideo.h" +#include #endif -#include "vj-yuv4mpeg.h" +#include #include "vj-dfb.h" #include "vj-sdl.h" #include "vj-OSC.h" -#include "lav_io.h" -#include "vj-shm.h" -#include "subsample.h" -#include "vj-el.h" +#include +#include +#include enum { NO_AUDIO = 0, diff --git a/veejay-current/veejay/vj-perform.c b/veejay-current/veejay/vj-perform.c index 83e4ed17..3aa76a18 100644 --- a/veejay-current/veejay/vj-perform.c +++ b/veejay-current/veejay/vj-perform.c @@ -24,25 +24,24 @@ #include #include #include -#include +#include #include #include #include -#include +#include #include -#include +#include #include //#ifdef SUPPORT_READ_DV2 //#include "vj-dv.h" //#endif #include -#include +#include #include #include #include #include #include -//#include #ifdef HAVE_SAMPLERATE #include #endif diff --git a/veejay-current/veejay/vj-sdl.c b/veejay-current/veejay/vj-sdl.c index 66a8e934..b9c43fc9 100644 --- a/veejay-current/veejay/vj-sdl.c +++ b/veejay-current/veejay/vj-sdl.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include //extern void *(* veejay_memcpy)(void *to, const void *from, size_t len) ;