[MPlayer-dev-eng] DC10(+)/buz/lml33/Matrox Rainbow Runner TV-out support
Rik Snel
rsnel at cube.dyndns.org
Wed Jan 2 01:16:57 CET 2002
Hello all,
This patch adds a display-driver (-vo zr) for the MJPEG capture/playback
cards mentioned in the subject (tested for DC10+ and buz). The driver
works by encoding the frame to jpeg and then sending it to the card. For
the jpeg encoding I use the standard libjpeg-6b (I tried to use the mjpeg
codec from ffmpeg, but it couldn't get it to generate a JPEG in the YUV422
format (internal), the hardware only takes YUV422 jpegs, the code is still
there and it can be made to work if/when libavcodec generates YV12 jpegs)
Remarks:
-users must only complain about this driver if they got the mjpegtools
working. They include the most recent kernel driver and playback/record
utilities. (if that doesn't work than this driver is not the problem,
becouse this driver talks to the kernel driver from the
mjpeg.sourceforge.net)
-This driver adds -zr* commandline options, the explanation of the options
can be viewed with -zrhelp. It is possible to crop the input frame (cut
borders to make it fit or to enhance performace) and to do other things.
-The driver only gets compiled if --enable-zr is specified on the
configure commandline. (and if libjpeg is present)
-The was a conflict between the jpeg_fdct_ifast from libavcodec and
the one from libjpeg-6b. I added -Djpeg_fdct_ifast=jpeg_fdct_ifast2
to the OPTFLAGS. (without a fix the jpeg compression aborts with 'DCT
coeffcient out of range' in Integer Fast mode)
-configure checks for libjpeg
-A/V sync is decent
-performance: my AMD K6-2 can play a PAL VCD at vertical decimation 2
(throw half of the lines away) in Integer Fast mode at quality 70 without
frame drop.
-some (factor 2, 4) hardware scaling, it will automatically do the right
thing. (helped by -zrfi 'force interlace')
-The driver only takes data in the YV12 format.
Greetings,
Rik.
--------
Nothing is ever a total loss; it can always serve as a bad example.
-------------- next part --------------
diff -Naur main/Makefile main.dev/Makefile
--- main/Makefile Tue Jan 1 01:39:50 2002
+++ main.dev/Makefile Tue Jan 1 23:19:28 2002
@@ -36,7 +36,7 @@
VO_LIBS = -Llibvo -lvo
VO_INC = -Ilibvo
endif
-V_LIBS = $(X_LIB) $(MP1E_LIB) $(GGI_LIB) $(MLIB_LIB) $(PNG_LIB) $(SDL_LIB) $(SVGA_LIB) $(AA_LIB) $(DIRECTFB_LIB)
+V_LIBS = $(X_LIB) $(MP1E_LIB) $(GGI_LIB) $(MLIB_LIB) $(JPEG_LIB) $(PNG_LIB) $(SDL_LIB) $(SVGA_LIB) $(AA_LIB) $(DIRECTFB_LIB)
AO_LIBS = -Llibao2 -lao2
A_LIBS = $(ALSA_LIB) $(NAS_LIB) $(MAD_LIB) $(VORBIS_LIB) $(SGIAUDIO_LIB)
diff -Naur main/cfg-mplayer.h main.dev/cfg-mplayer.h
--- main/cfg-mplayer.h Fri Dec 28 14:28:06 2001
+++ main.dev/cfg-mplayer.h Tue Jan 1 22:53:03 2002
@@ -65,6 +65,10 @@
extern int vo_aa_parseoption(struct config * conf, char *opt, char * param);
#endif
+#ifdef HAVE_ZR
+extern int vo_zr_parseoption(struct config * conf, char *opt, char * param);
+#endif
+
#ifdef HAVE_NEW_GUI
extern char * skinName;
#endif
@@ -231,6 +235,10 @@
#ifdef HAVE_AA
{"aa*", vo_aa_parseoption, CONF_TYPE_FUNC_FULL, 0, 0, 0 },
+#endif
+
+#ifdef HAVE_ZR
+ {"zr*", vo_zr_parseoption, CONF_TYPE_FUNC_FULL, 0, 0, 0 },
#endif
#ifdef HAVE_LIRC
diff -Naur main/configure main.dev/configure
--- main/configure Tue Jan 1 01:39:51 2002
+++ main.dev/configure Tue Jan 1 23:44:15 2002
@@ -153,6 +153,7 @@
--enable-3dfx build with 3dfx support [disable]
--enable-tdfxfb build with tdfxfb support [disable]
--enable-directfb build with DirectFB support [autodetect]
+ --enable-zr build with ZR36067/ZR36060 support [disable]
Audio:
--disable-ossaudio disable OSS sound support [autodetect]
@@ -674,6 +675,7 @@
_xv=auto
_sdl=auto
_nas=auto
+_jpeg=auto
_png=auto
_gl=auto
_ggi=auto
@@ -713,6 +715,7 @@
_3dfx=no
_tdfxfb=no
_directfb=auto
+_zr=no
_largefiles=no
_vo2=no
_language=en
@@ -743,6 +746,8 @@
--disable-sdl) _sdl=no ;;
--enable-nas) _nas=yes ;;
--disable-nas) _nas=no ;;
+ --enable-jpeg) _jpeg=yes ;;
+ --disable-jpeg) _jpeg=no ;;
--enable-png) _png=yes ;;
--disable-png) _png=no ;;
--enable-gl) _gl=yes ;;
@@ -817,6 +822,8 @@
--disable-tdfxfb) _tdfxfb=no ;;
--enable-directfb) _directfb=yes ;;
--disable-directfb) _directfb=no ;;
+ --enable-zr) _zr=yes ;;
+ --disable-zr) _zr=no ;;
--enable-mtrr) _mtrr=yes ;;
--disable-mtrr) _mtrr=no ;;
--enable-largefiles) _largefiles=yes ;;
@@ -1783,6 +1790,34 @@
fi
echores "$_dvb"
+echocheck "JPEG support"
+if test "$_jpeg" = auto; then
+ _jpeg=no
+cat > $TMPC << EOF
+#include <stdio.h>
+#include <jpeglib.h>
+int main(void) { return 0; }
+EOF
+ cc_check -ljpeg && _jpeg=yes
+fi
+echores "$_jpeg"
+
+echocheck "zr"
+if test "$_zr" = yes ; then
+ if test "$_jpeg" = yes ; then
+ _ld_jpeg='-ljpeg'
+ _def_zr='#define HAVE_ZR 1'
+ _vosrc="$_vosrc vo_zr.c"
+ _vomodules="zr $_vomodules"
+ echores "$_zr"
+ else
+ echores "jpeglib is required by zr, sorry"
+ _def_zr='#undef HAVE_ZR'
+ fi
+else
+ _def_zr='#undef HAVE_ZR'
+ echores "$_zr"
+fi
echocheck "PNG support"
if test "$_png" = auto ; then
@@ -2741,7 +2776,7 @@
AR = ar
CC = $_cc
# OPTFLAGS = -O4 $_profile $_debug $_march $_mcpu -pipe -fomit-frame-pointer -ffast-math
-OPTFLAGS = $CFLAGS
+OPTFLAGS = -Djpeg_fdct_ifast=jpeg_fdct_ifast2 $CFLAGS
EXTRA_INC = $_inc_extra $_inc_gtk
WIN32_PATH = -DWIN32_PATH=\\"$_win32libdir\\"
@@ -2763,6 +2798,7 @@
GGI_LIB = $_ld_ggi
MLIB_LIB = $_ld_mlib
MLIB_INC = $_inc_mlib
+JPEG_LIB = $_ld_jpeg
PNG_LIB = $_ld_png
SDL_LIB = $_ld_sdl
SVGA_LIB = $_ld_svga
@@ -3072,6 +3108,7 @@
$_def_3dfx
$_def_tdfxfb
$_def_directfb
+$_def_zr
$_def_mga
$_def_xmga
$_def_syncfb
diff -Naur main/libvo/video_out.c main.dev/libvo/video_out.c
--- main/libvo/video_out.c Mon Dec 3 02:09:36 2001
+++ main.dev/libvo/video_out.c Tue Jan 1 22:53:03 2002
@@ -64,6 +64,7 @@
extern vo_functions_t video_out_tdfxfb;
extern vo_functions_t video_out_null;
//extern vo_functions_t video_out_odivx;
+extern vo_functions_t video_out_zr;
extern vo_functions_t video_out_pgm;
extern vo_functions_t video_out_md5;
extern vo_functions_t video_out_syncfb;
@@ -126,6 +127,9 @@
#endif
#ifdef HAVE_DXR3
&video_out_dxr3,
+#endif
+#ifdef HAVE_ZR
+ &video_out_zr,
#endif
#ifdef HAVE_PNG
diff -Naur main/libvo/vo_zr.c main.dev/libvo/vo_zr.c
--- main/libvo/vo_zr.c Thu Jan 1 01:00:00 1970
+++ main.dev/libvo/vo_zr.c Tue Jan 1 23:51:44 2002
@@ -0,0 +1,639 @@
+/*
+ * vo_zr.c - playback on zoran cards
+ * Copyright (C) Rik Snel 2001,2002, License GNU GPL v2
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#include <linux/videodev.h>
+#include "zoran.h"
+
+#include "config.h"
+#define ZR_USES_LIBJPEG
+
+#include "video_out.h"
+#include "video_out_internal.h"
+#include "../mp_msg.h"
+#include "../cfgparser.h"
+
+LIBVO_EXTERN (zr)
+
+static vo_info_t vo_info =
+{
+ "Zoran ZR360[56]7/ZR36060 Driver (DC10(+)/buz/lml33/MatroxRR)",
+ "zr",
+ "Rik Snel <snel at phys.uu.nl>",
+ ""
+};
+
+/* General variables */
+
+static int image_width;
+static int image_height;
+static int off_y, off_c, stride; /* for use by 'draw slice' */
+static int framenum;
+static int fields = 1; /* currently no interlacing */
+static int forceinter = 0;
+static int vdec = 1;
+static int size;
+static int quality = 70;
+
+typedef struct {
+ int width;
+ int height;
+ int xoff;
+ int yoff;
+ int set;
+} geo;
+geo g = {0, 0, 0, 0, 0};
+
+static uint8_t *image=NULL;
+static uint8_t *buf=NULL;
+
+
+/* Variables needed for Zoran */
+
+int vdes; /* the file descriptor of the video device */
+int frame = 0, synco = 0, queue = 0; /* buffer management */
+struct zoran_params zp;
+struct zoran_requestbuffers zrq;
+struct zoran_sync zs;
+struct video_capability vc;
+#define MJPEG_NBUFFERS 2
+#define MJPEG_SIZE 1024*256
+
+//should be command line options
+int norm = VIDEO_MODE_AUTO;
+char *device = "/dev/video";
+
+
+#ifdef ZR_USES_LIBJPEG
+#include<jpeglib.h>
+int ccount;
+unsigned char *ccbuf;
+struct jpeg_compress_struct cinfo;
+struct jpeg_destination_mgr jdest;
+struct jpeg_error_mgr jerr;
+
+/* minimal destination handler to output to buffer */
+METHODDEF(void) init_destination(struct jpeg_compress_struct *cinfo) {
+// printf("init_destination called %p %d\n", ccbuf, ccount);
+ cinfo->dest->next_output_byte = (JOCTET*)(ccbuf+ccount);
+ cinfo->dest->free_in_buffer = MJPEG_SIZE - ccount;
+}
+
+METHODDEF(boolean) empty_output_buffer(struct jpeg_compress_struct *cinfo) {
+// printf("empty_output_buffer called\n");
+ mp_msg(MSGT_VO, MSGL_ERR, "empty_output_buffer called, may not happen because buffer must me large enough\n");
+ return(FALSE);
+}
+
+METHODDEF(void) term_destination(struct jpeg_compress_struct *cinfo) {
+// printf("term_destination called %p %d\n", ccbuf, ccount);
+ ccount = MJPEG_SIZE - cinfo->dest->free_in_buffer;
+}
+/* end of minimal destination handler */
+
+JSAMPARRAY ***jsi;
+
+#else
+#include "../libavcodec/avcodec.h"
+AVCodec *codec;
+AVCodecContext codec_context;
+AVPicture picture;
+#endif
+
+static int jpegdct = JDCT_IFAST;
+
+int init_codec() {
+#ifdef ZR_USES_LIBJPEG
+ int i, j, k;
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cinfo);
+
+ cinfo.dest = &jdest;
+ cinfo.dest->init_destination = init_destination;
+ cinfo.dest->empty_output_buffer = empty_output_buffer;
+ cinfo.dest->term_destination = term_destination;
+
+ cinfo.input_components = 3;
+
+ jpeg_set_defaults(&cinfo);
+
+ cinfo.image_width = image_width;
+ cinfo.image_height = image_height/fields;
+ cinfo.input_gamma = 1.0;
+ cinfo.in_color_space = JCS_YCbCr;
+ cinfo.raw_data_in = TRUE;
+ cinfo.comp_info[0].h_samp_factor = 2;
+ cinfo.comp_info[0].v_samp_factor = 1;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+ cinfo.dct_method = jpegdct;
+ jpeg_set_quality(&cinfo, quality, FALSE);
+ jsi = malloc(sizeof(JSAMPARRAY**)*fields);
+
+ /* Just some clutter to give libjpeg the pointers,
+ * and I don't want to recalculate everything everytime
+ * it is needed */
+ for (k = 0; k < fields; k++) {
+ jsi[k] = malloc(sizeof(JSAMPARRAY*)*image_height/(8*fields));
+
+ for (i = 0; i < image_height/(8*fields); i++) {
+ jsi[k][i] = malloc(3*sizeof(JSAMPARRAY));
+ jsi[k][i][0] = malloc(8*sizeof(JSAMPROW));
+ jsi[k][i][1] = malloc(8*sizeof(JSAMPROW));
+ jsi[k][i][2] = malloc(8*sizeof(JSAMPROW));
+ for (j = 0; j < 8; j++) {
+ jsi[k][i][0][j] = (JSAMPROW)(image +
+ (fields*(8*i + j) + k)*image_width);
+ jsi[k][i][1][j] = (JSAMPROW)(image + size +
+ (fields*(8*i + j)/2)*image_width/2);
+ jsi[k][i][2][j] = (JSAMPROW)(image + 3*size/2 +
+ (fields*(8*i + j)/2)*image_width/2);
+ }
+ }
+
+ }
+#else
+ AVCodecContext *c = &codec_context;
+ codec = avcodec_find_encoder(CODEC_ID_MJPEG);
+ if (!codec) {
+ /* maybe libavcodec was not initialized */
+ avcodec_init();
+ avcodec_register_all();
+ codec = avcodec_find_encoder(CODEC_ID_MJPEG);
+ if (!codec) {
+ mp_msg(MSGT_VO, MSGL_ERR, "MJPG codec not found in libavcodec\n");
+ return 1;
+ }
+ }
+ /* put default values */
+ memset(c, 0, sizeof(*c));
+
+ c->width = image_width;
+ c->height = image_height;
+ c->bit_rate = 4000000;
+ c->frame_rate = 25*FRAME_RATE_BASE;
+ //c->gop_size = 1;
+ c->pix_fmt = PIX_FMT_YUV422P;
+
+ if (avcodec_open(c, codec) < 0) {
+ mp_msg(MSGT_VO, MSGL_ERR, "MJPG codec could not be opened\n");
+ return 1;
+ }
+
+ picture.data[0] = image;
+ picture.data[1] = image + size;
+ picture.data[2] = image + 3*size/2;
+ picture.linesize[0] = image_width;
+ picture.linesize[1] = image_width/2;
+ picture.linesize[2] = image_width/2;
+#endif
+ return 0;
+}
+
+
+int zoran_getcap() {
+ vdes = open(device, O_RDWR);
+ if (vdes < 0) {
+ mp_msg(MSGT_VO, MSGL_ERR, "error opening %s\n",
+ device);
+ return 1;
+ }
+
+
+ if (ioctl(vdes, VIDIOCGCAP, &vc) < 0) {
+ mp_msg(MSGT_VO, MSGL_ERR, "error getting video capabilities from %s\n");
+ return 1;
+ }
+ return 0;
+}
+
+int init_zoran() {
+ if (ioctl(vdes, BUZIOC_G_PARAMS, &zp) < 0) {
+ mp_msg(MSGT_VO, MSGL_ERR, "device at %s is probably not a DC10(+)/buz/lml33\n", device);
+ return 1;
+ }
+
+ if (zp.norm != norm && norm != VIDEO_MODE_AUTO) {
+ /* attempt to set requested norm */
+ zp.norm = norm;
+ if (ioctl(vdes, BUZIOC_S_PARAMS, &zp) < 0) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "unable to change video norm, use another program to change it (XawTV)\n");
+ return 1;
+ }
+ ioctl(vdes, BUZIOC_G_PARAMS, &zp);
+ if (norm != zp.norm) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "unable to change video norm, use another program to change it (XawTV)\n");
+ return 1;
+ }
+ }
+
+ /* center the image, and stretch it as far as possible (try to keep
+ * aspect) and check if it fits */
+ mp_msg(MSGT_VO, MSGL_V, "zr36067 reports: maxwidth=%d, maxheight=%d\n", vc.maxwidth, vc.maxheight);
+
+ if (image_width > vc.maxwidth) {
+ mp_msg(MSGT_VO, MSGL_ERR, "movie to be played is too wide, max width currenty %d\n", vc.maxwidth);
+ return 1;
+ }
+
+ if (image_height > vc.maxheight) {
+ mp_msg(MSGT_VO, MSGL_ERR, "movie to be played is too high, max height currenty %d\n", vc.maxheight);
+ return 1;
+ }
+
+ zp.decimation = 0;
+ zp.HorDcm = (vc.maxwidth >= 2*(int)image_width) ? 2 : 1;
+ zp.VerDcm = 1;
+ if (zp.HorDcm == 2 && 4*image_width <= vc.maxwidth &&
+ 4*image_height/fields <= vc.maxheight) {
+ zp.HorDcm = 4;
+ zp.VerDcm = 2;
+ }
+ if (((forceinter == 0 && vdec >= 2) || (forceinter == 1 && vdec == 4)) && 4*image_height/fields <= vc.maxheight) {
+ zp.VerDcm = 2;
+ }
+ zp.TmpDcm = 1;
+ zp.field_per_buff = fields;
+ zp.img_x = (vc.maxwidth - zp.HorDcm*(int)image_width)/2;
+ zp.img_y = (vc.maxheight - zp.VerDcm*(3-fields)*(int)image_height)/4;
+ zp.img_width = zp.HorDcm*image_width;
+ zp.img_height = zp.VerDcm*image_height/fields;
+ mp_msg(MSGT_VO, MSGL_V, "zr: geometry (after 'scaling'): %dx%d+%d+%d fields=%d, w=%d, h=%d\n", zp.img_width, zp.img_height, zp.img_x, zp.img_y, fields, image_width, image_height);
+
+ if (ioctl(vdes, BUZIOC_S_PARAMS, &zp) < 0) {
+ mp_msg(MSGT_VO, MSGL_ERR, "error setting display parameters\n");
+ return 1;
+ }
+
+ zrq.count = MJPEG_NBUFFERS;
+ zrq.size = MJPEG_SIZE;
+
+ if (ioctl(vdes, BUZIOC_REQBUFS, &zrq)) {
+ mp_msg(MSGT_VO, MSGL_ERR, "error requesting %d buffers of size %d\n", zrq.count, zrq.size);
+ return 1;
+ }
+
+ buf = (char*)mmap(0, zrq.count*zrq.size, PROT_READ|PROT_WRITE,
+ MAP_SHARED, vdes, 0);
+
+ if (buf == MAP_FAILED) {
+ mp_msg(MSGT_VO, MSGL_ERR, "error requesting %d buffers of size %d\n", zrq.count, zrq.size);
+ return 1;
+ }
+ return 0;
+}
+
+void uninit_zoran(void) {
+ if (image) {
+ free(image);
+ image=NULL;
+ }
+ while (queue > synco + 1) {
+ if (ioctl(vdes, BUZIOC_SYNC, &zs) < 0)
+ mp_msg(MSGT_VO, MSGL_ERR, "error waiting for buffers to become free");
+ synco++;
+ }
+ /* stop streaming */
+ frame = -1;
+ if (ioctl(vdes, BUZIOC_QBUF_PLAY, &frame) < 0)
+ mp_msg(MSGT_VO, MSGL_ERR, "error stopping playback of last frame");
+ close(vdes);
+}
+
+static uint32_t init(uint32_t width, uint32_t height, uint32_t d_width,
+ uint32_t d_height, uint32_t fullscreen, char *title, uint32_t format)
+{
+ int j;
+ /* this allows to crop parts from incoming picture,
+ * for easy 512x240 -> 352x240 */
+ /* These values must be multples of 2 */
+
+ if (g.set) {
+ if (g.width%2 != 0 || g.height%2 != 0 ||
+ g.xoff%2 != 0 || g.yoff%2 != 0) {
+ mp_msg(MSGT_VO, MSGL_ERR, "arguments in -zrcrop must be multiples of 2\n");
+ return 1;
+ }
+ if (g.width <= 0 || g.height <= 0 ||
+ g.xoff < 0 || g.yoff < 0) {
+ mp_msg(MSGT_VO, MSGL_ERR, "width and height must be positive and offset nonnegative\n");
+ return 1;
+ }
+ if (g.width + g.xoff > width) {
+ mp_msg(MSGT_VO, MSGL_ERR, "width+xoffset (%d+%d>%d) is too big\n", g.width, g.xoff, width);
+ return 1;
+ }
+ if (g.height + g.yoff > height) {
+ mp_msg(MSGT_VO, MSGL_ERR, "height+yoffset (%d+%d>%d) is too big\n", g.height, g.yoff, height);
+ return 1;
+ }
+ } else {
+ g.width = width;
+ g.height = height;
+ g.xoff = 0;
+ g.yoff = 0;
+ }
+ /* we must know the maximum resolution of the device
+ * it differs for DC10+ and buz for example */
+ zoran_getcap(); /*must be called before init_zoran */
+ if (g.height/vdec > vc.maxheight/2 || (forceinter == 1 && vdec == 1))
+ fields = 2;
+ printf("fields=%d\n", fields);
+ /* the height must be a multiple of fields*8 and the width
+ * must be a multiple of 16 */
+ /* add some black borders to make it so, and center the image*/
+ image_height = fields*8*((g.height/vdec - 1)/(fields*8) + 1);
+ image_width = 16*((g.width - 1)/16 + 1);
+ off_y = (image_height - g.height/vdec)/2;
+ if (off_y%2 != 0) off_y++;
+ off_y *= image_width;
+ off_c = off_y/4;
+ off_y += (image_width - g.width)/2;
+ if (off_y%2 != 0) off_y--;
+ off_c += (image_width - g.width)/4;
+ framenum = 0;
+ size = image_width*image_height;
+ mp_msg(MSGT_VO, MSGL_V, "input: %dx%d, cropped: %dx%d, output: %dx%d, off_y=%d, off_c=%d\n", width, height, g.width, g.height, image_width, image_height, off_y, off_c);
+
+ image = malloc(2*size); /* this buffer allows for YUV422 data,
+ * so it is a bit too big for YUV420 */
+ if (!image) {
+ mp_msg(MSGT_VO, MSGL_ERR, "Memory exhausted\n");
+ return 1;
+ }
+ /* and make sure that the borders are _really_ black */
+ memset(image, 0, image_width*image_height);
+ memset(image + size, 0x80, image_width*image_height/4);
+ memset(image + 3*size/2, 0x80, image_width*image_height/4);
+
+ if (init_codec()) {
+ return 1;
+ }
+
+ if (init_zoran()) {
+#ifdef ZR_USES_LIBJPEG
+ jpeg_destroy_compress(&cinfo);
+#else
+ avcodec_close(&codec_context);
+#endif
+ return 1;
+ }
+
+ return 0;
+}
+
+static const vo_info_t* get_info(void) {
+ return &vo_info;
+}
+
+static void draw_osd(void) {
+}
+
+static void flip_page (void) {
+#ifdef ZR_USES_LIBJPEG
+ int i, j, k;
+#else
+ AVCodecContext *c = &codec_context;
+#endif
+
+ /* do we have a free buffer? */
+ if (queue-synco < zrq.count) {
+ frame = queue;
+ } else {
+ if (ioctl(vdes, BUZIOC_SYNC, &zs) < 0)
+ mp_msg(MSGT_VO, MSGL_ERR, "error waiting for buffers to become free");
+ frame = zs.frame;
+ synco++;
+ }
+
+#ifdef ZR_USES_LIBJPEG
+ ccbuf = buf + frame*zrq.size;
+ ccount = 0;
+ k = fields;
+ for (j=0; j < k; j++) {
+
+ jpeg_start_compress(&cinfo, TRUE);
+ i=0;
+ while (cinfo.next_scanline < cinfo.image_height) {
+ jpeg_write_raw_data(&cinfo, jsi[j][i], 8);
+ i++;
+ }
+ jpeg_finish_compress(&cinfo);
+
+ }
+#else
+ avcodec_encode_video(c, buf + frame*zrq.size, MJPEG_SIZE, &picture);
+#endif
+
+ if (ioctl(vdes, BUZIOC_QBUF_PLAY, &frame) < 0)
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "error queueing buffer for playback");
+ queue++;
+
+ framenum++;
+ return;
+}
+
+static uint32_t draw_frame(uint8_t * src[]) {
+ return 0;
+}
+
+static uint32_t query_format(uint32_t format) {
+ if(format==IMGFMT_YV12) return 1;
+ return 0;
+}
+
+static void uninit(void) {
+ uninit_zoran();
+
+#ifdef ZR_USES_LIBJPEG
+ jpeg_destroy_compress(&cinfo);
+#else
+ avcodec_close(&codec_context);
+#endif
+}
+
+static void check_events(void) {
+}
+
+
+static uint32_t draw_slice(uint8_t *srcimg[], int stride[],
+ int w, int h, int x, int y) {
+ int i;
+ /* Apply 'geometry', crop unwanted parts */
+ uint8_t *dst;
+ uint8_t *src;
+ //printf("before: w=%d, h=%d, x=%d, y=%d, src0=%p, src1=%p, src2=%p\n", w, h, x, y, srcimg[0], srcimg[1], srcimg[2]);
+ if (x < g.xoff) {
+ srcimg[0] += g.xoff - x;
+ srcimg[1] += (g.xoff - x)/2;
+ srcimg[2] += (g.xoff - x)/2;
+ w -= g.xoff - x;
+ if (w < 0) return 0;
+ x = 0 /*g.xoff*/;
+ } else {
+ x -= g.xoff;
+ }
+ if (x + w > g.width) {
+ w = g.width - x;
+ if (w < 0) return 0;
+ }
+ if (y < g.yoff) {
+ srcimg[0] += (g.yoff - y)*stride[0];
+ srcimg[1] += ((g.yoff - y)/2)*stride[1];
+ srcimg[2] += ((g.yoff - y)/2)*stride[2];
+ h -= g.yoff - y;
+ if (h < 0) return 0;
+ y = 0;
+ } else {
+ y -= g.yoff;
+ }
+ if (y + h > g.height) {
+ h = g.height - y;
+ if (h < 0) return 0;
+ }
+ //printf("after: w=%d, h=%d, x=%d, y=%d, src0=%p, src1=%p, src2=%p\n", w, h, x, y, srcimg[0], srcimg[1], srcimg[2]);
+ dst=image + off_y + image_width*(y/vdec)+x;
+ src=srcimg[0];
+ // copy Y:
+ for (i = 0; i < h; i++) {
+ if ((i + x)%vdec == 0) {
+ memcpy(dst,src,w);
+ dst+=image_width;
+ }
+ src+=stride[0];
+
+ }
+ {
+ // copy U+V:
+ uint8_t *src1=srcimg[1];
+ uint8_t *src2=srcimg[2];
+ uint8_t *dst1=image + size + off_c+ (y/(vdec*2))*image_width/2+(x/2);
+ uint8_t *dst2=image + 3*size/2 + off_c +
+ (y/(vdec*2))*image_width/2+(x/2);
+ for (i = 0; i< h/2; i++) {
+ if ((i+x/2)%vdec == 0) {
+ memcpy(dst1,src1,w/2);
+ memcpy(dst2,src2,w/2);
+ dst1+=image_width/2;
+ dst2+=image_width/2;
+ }
+ src1+=stride[1];
+ src2+=stride[2];
+ }
+ }
+ return 0;
+}
+
+
+/* copied and adapted from vo_aa_parseoption */
+int
+vo_zr_parseoption(struct config * conf, char *opt, char *param){
+ /* got an option starting with zr */
+ char *x, *help;
+ int i;
+ /* do WE need it ?, always */
+ if (!strcasecmp(opt, "zrdev")) {
+ if (param == NULL) return ERR_MISSING_PARAM;
+ //if ((i=getcolor(param))==-1) return ERR_OUT_OF_RANGE;
+ //aaopt_osdcolor=i;
+ device = malloc(strlen(param)+1);
+ strcpy(device, param);
+ mp_msg(MSGT_VO, MSGL_V, "zr: using device %s\n", device);
+ return 1;
+ } else if (!strcasecmp(opt, "zrfi")) {
+ if (param != NULL) {
+ return ERR_OUT_OF_RANGE;
+ }
+ forceinter = 1;
+ return 1;
+ } else if (!strcasecmp(opt, "zrcrop")){
+ if (param == NULL) return ERR_MISSING_PARAM;
+ if (sscanf(param, "%dx%d+%d+%d", &g.width, &g.height,
+ &g.xoff, &g.yoff) != 4) {
+ g.xoff = 0; g.yoff = 0;
+ if (sscanf(param, "%dx%d", &g.width, &g.height) != 2) {
+ mp_msg(MSGT_VO, MSGL_ERR, "argument to -zrcrop must be of the form 352x288+16+0\n");
+ return ERR_OUT_OF_RANGE;
+ }
+ }
+ g.set = 1;
+ mp_msg(MSGT_VO, MSGL_V, "zr: cropping %s\n", param);
+ return 1;
+ }else if (!strcasecmp(opt, "zrvdec")) {
+ i = atoi(param);
+ if (i != 1 && i != 2 && i != 4) return ERR_OUT_OF_RANGE;
+ vdec = i;
+ return 1;
+ }else if (!strcasecmp(opt, "zrquality")) {
+ i = atoi(param);
+ if (i < 30 || i > 100) return ERR_OUT_OF_RANGE;
+ quality = i;
+ return 1;
+ }else if (!strcasecmp(opt, "zrdct")) {
+ if (param == NULL) return ERR_MISSING_PARAM;
+ if (!strcasecmp(param, "IFAST")) {
+ jpegdct = JDCT_IFAST;
+ return 1;
+ } else if (!strcasecmp(param, "ISLOW")) {
+ jpegdct = JDCT_ISLOW;
+ return 1;
+ } else if (!strcasecmp(param, "FLOAT")) {
+ jpegdct = JDCT_FLOAT;
+ return 1;
+ } else {
+ return ERR_OUT_OF_RANGE;
+ }
+ }else if (!strcasecmp(opt, "zrnorm")) {
+ if (param == NULL) return ERR_MISSING_PARAM;
+ if (!strcasecmp(param, "NTSC")) {
+ mp_msg(MSGT_VO, MSGL_V, "zr: Norm set to NTSC\n");
+ norm = VIDEO_MODE_NTSC;
+ return 1;
+ } else if (!strcasecmp(param, "PAL")) {
+ mp_msg(MSGT_VO, MSGL_V, "zr: Norm set to PAL\n");
+ norm = VIDEO_MODE_PAL;
+ return 1;
+ } else {
+ return ERR_OUT_OF_RANGE;
+ }
+ }else if (!strcasecmp(opt, "zrhelp")){
+ printf("Help for -vo zr: Zoran ZR360[56]7/ZR36060 based MJPEG capture/playback cards\n");
+ printf("\n");
+ printf("Here are the zr options:\n");
+ printf(
+ "\n"
+ " -zrcrop specify part of the input image that\n"
+ " you want to see as an x-style geometry string\n"
+ " example: -zrcrop 352x288+16+0\n"
+ " -zrvdec vertical decimation 1, 2 or 4\n"
+ " -zrfi force interlacing ('wide screen')\n"
+ " (by default we only interlace if the movie\n"
+ " is higher than half of the screen height)\n"
+ " -zrquality jpeg compression quality 30-100\n"
+ " -zrdct specify DCT method: IFAST, ISLOW or FLOAT\n"
+ " -zrdev playback device (example -zrdev /dev/video1\n"
+ " -zrnorm specify norm PAL/NTSC [dev: leave at current setting]\n"
+ "\n"
+ );
+ exit(0);
+
+ }
+ return ERR_NOT_AN_OPTION;
+}
diff -Naur main/libvo/zoran.h main.dev/libvo/zoran.h
--- main/libvo/zoran.h Thu Jan 1 01:00:00 1970
+++ main.dev/libvo/zoran.h Tue Jan 1 22:53:03 2002
@@ -0,0 +1,372 @@
+/*
+ zoran - Iomega Buz driver
+
+ Copyright (C) 1999 Rainer Johanni <Rainer at Johanni.de>
+
+ based on
+
+ zoran.0.0.3 Copyright (C) 1998 Dave Perks <dperks at ibm.net>
+
+ and
+
+ bttv - Bt848 frame grabber driver
+ Copyright (C) 1996,97 Ralph Metzler (rjkm at thp.uni-koeln.de)
+
+ 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 _BUZ_H_
+#define _BUZ_H_
+
+#include <linux/config.h>
+
+#if LINUX_VERSION_CODE < 0x20212
+typedef struct wait_queue *wait_queue_head_t;
+#endif
+
+/* The Buz only supports a maximum width of 720, but some V4L
+ applications (e.g. xawtv are more happy with 768).
+ If XAWTV_HACK is defined, we try to fake a device with bigger width */
+
+//#define XAWTV_HACK
+
+//#ifdef XAWTV_HACK
+//#define BUZ_MAX_WIDTH 768 /* never display more than 768 pixels */
+#define BUZ_MAX_WIDTH (zr->timing->Wa)
+//#else
+//#define BUZ_MAX_WIDTH 720 /* never display more than 720 pixels */
+//#endif
+//#define BUZ_MAX_HEIGHT 576 /* never display more than 576 rows */
+#define BUZ_MAX_HEIGHT (zr->timing->Ha)
+#define BUZ_MIN_WIDTH 32 /* never display less than 32 pixels */
+#define BUZ_MIN_HEIGHT 24 /* never display less than 24 rows */
+
+struct zoran_requestbuffers {
+ unsigned long count; /* Number of buffers for MJPEG grabbing */
+ unsigned long size; /* Size PER BUFFER in bytes */
+};
+
+struct zoran_sync {
+ unsigned long frame; /* number of buffer that has been free'd */
+ unsigned long length; /* number of code bytes in buffer (capture only) */
+ unsigned long seq; /* frame sequence number */
+ struct timeval timestamp; /* timestamp */
+};
+
+struct zoran_status {
+ int input; /* Input channel, has to be set prior to BUZIOC_G_STATUS */
+ int signal; /* Returned: 1 if valid video signal detected */
+ int norm; /* Returned: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */
+ int color; /* Returned: 1 if color signal detected */
+};
+
+struct zoran_params {
+
+ /* The following parameters can only be queried */
+
+ int major_version; /* Major version number of driver */
+ int minor_version; /* Minor version number of driver */
+
+ /* Main control parameters */
+
+ int input; /* Input channel: 0 = Composite, 1 = S-VHS */
+ int norm; /* Norm: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */
+ int decimation; /* decimation of captured video,
+ enlargement of video played back.
+ Valid values are 1, 2, 4 or 0.
+ 0 is a special value where the user
+ has full control over video scaling */
+
+ /* The following parameters only have to be set if decimation==0,
+ for other values of decimation they provide the data how the image is captured */
+
+ int HorDcm; /* Horizontal decimation: 1, 2 or 4 */
+ int VerDcm; /* Vertical decimation: 1 or 2 */
+ int TmpDcm; /* Temporal decimation: 1 or 2,
+ if TmpDcm==2 in capture every second frame is dropped,
+ in playback every frame is played twice */
+ int field_per_buff; /* Number of fields per buffer: 1 or 2 */
+ int img_x; /* start of image in x direction */
+ int img_y; /* start of image in y direction */
+ int img_width; /* image width BEFORE decimation,
+ must be a multiple of HorDcm*16 */
+ int img_height; /* image height BEFORE decimation,
+ must be a multiple of VerDcm*8 */
+
+ /* --- End of parameters for decimation==0 only --- */
+
+ /* JPEG control parameters */
+
+ int quality; /* Measure for quality of compressed images.
+ Scales linearly with the size of the compressed images.
+ Must be beetween 0 and 100, 100 is a compression
+ ratio of 1:4 */
+
+ int odd_even; /* Which field should come first ??? */
+
+ int APPn; /* Number of APP segment to be written, must be 0..15 */
+ int APP_len; /* Length of data in JPEG APPn segment */
+ char APP_data[60]; /* Data in the JPEG APPn segment. */
+
+ int COM_len; /* Length of data in JPEG COM segment */
+ char COM_data[60]; /* Data in JPEG COM segment */
+
+ unsigned long jpeg_markers; /* Which markers should go into the JPEG output.
+ Unless you exactly know what you do, leave them untouched.
+ Inluding less markers will make the resulting code
+ smaller, but there will be fewer aplications
+ which can read it.
+ The presence of the APP and COM marker is
+ influenced by APP0_len and COM_len ONLY! */
+#define JPEG_MARKER_DHT (1<<3) /* Define Huffman Tables */
+#define JPEG_MARKER_DQT (1<<4) /* Define Quantization Tables */
+#define JPEG_MARKER_DRI (1<<5) /* Define Restart Interval */
+#define JPEG_MARKER_COM (1<<6) /* Comment segment */
+#define JPEG_MARKER_APP (1<<7) /* App segment, driver will allways use APP0 */
+
+ int VFIFO_FB; /* Flag for enabling Video Fifo Feedback.
+ If this flag is turned on and JPEG decompressing
+ is going to the screen, the decompress process
+ is stopped every time the Video Fifo is full.
+ This enables a smooth decompress to the screen
+ but the video output signal will get scrambled */
+
+ /* Misc */
+
+ char reserved[312]; /* Makes 512 bytes for this structure */
+};
+
+/*
+Private IOCTL to set up for displaying MJPEG
+*/
+#define BUZIOC_G_PARAMS _IOR ('v', BASE_VIDIOCPRIVATE+0, struct zoran_params)
+#define BUZIOC_S_PARAMS _IOWR('v', BASE_VIDIOCPRIVATE+1, struct zoran_params)
+#define BUZIOC_REQBUFS _IOWR('v', BASE_VIDIOCPRIVATE+2, struct zoran_requestbuffers)
+#define BUZIOC_QBUF_CAPT _IOW ('v', BASE_VIDIOCPRIVATE+3, int)
+#define BUZIOC_QBUF_PLAY _IOW ('v', BASE_VIDIOCPRIVATE+4, int)
+#define BUZIOC_SYNC _IOR ('v', BASE_VIDIOCPRIVATE+5, struct zoran_sync)
+#define BUZIOC_G_STATUS _IOWR('v', BASE_VIDIOCPRIVATE+6, struct zoran_status)
+
+
+#ifdef __KERNEL__
+
+#define BUZ_NUM_STAT_COM 4
+#define BUZ_MASK_STAT_COM 3
+
+#define BUZ_MAX_FRAME 256 /* Must be a power of 2 */
+#define BUZ_MASK_FRAME 255 /* Must be BUZ_MAX_FRAME-1 */
+
+#if VIDEO_MAX_FRAME <= 32
+# define V4L_MAX_FRAME 32
+#elif VIDEO_MAX_FRAME <= 64
+# define V4L_MAX_FRAME 64
+#else
+# error "Too many video frame buffers to handle"
+#endif
+#define V4L_MASK_FRAME (V4L_MAX_FRAME - 1)
+
+
+#include "zr36057.h"
+
+enum card_type {
+ UNKNOWN = 0,
+ DC10,
+ DC10plus,
+ LML33,
+ BUZ
+};
+
+enum zoran_codec_mode {
+ BUZ_MODE_IDLE, /* nothing going on */
+ BUZ_MODE_MOTION_COMPRESS, /* grabbing frames */
+ BUZ_MODE_MOTION_DECOMPRESS, /* playing frames */
+ BUZ_MODE_STILL_COMPRESS, /* still frame conversion */
+ BUZ_MODE_STILL_DECOMPRESS /* still frame conversion */
+};
+
+enum zoran_buffer_state {
+ BUZ_STATE_USER, /* buffer is owned by application */
+ BUZ_STATE_PEND, /* buffer is queued in pend[] ready to feed to I/O */
+ BUZ_STATE_DMA, /* buffer is queued in dma[] for I/O */
+ BUZ_STATE_DONE /* buffer is ready to return to application */
+};
+
+struct zoran_gbuffer {
+ u32 *frag_tab; /* addresses of frag table */
+ u32 frag_tab_bus; /* same value cached to save time in ISR */
+ enum zoran_buffer_state state; /* non-zero if corresponding buffer is in use in grab queue */
+ struct zoran_sync bs; /* DONE: info to return to application */
+};
+
+struct v4l_gbuffer {
+ char *fbuffer; /* virtual address of frame buffer */
+ unsigned long fbuffer_phys; /* physical address of frame buffer */
+ unsigned long fbuffer_bus; /* bus address of frame buffer */
+ enum zoran_buffer_state state; /* state: unused/pending/done */
+};
+
+struct tvnorm {
+ u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart;
+};
+
+struct zoran {
+ struct video_device video_dev;
+ struct i2c_bus i2c;
+
+ int initialized; /* flag if zoran has been correctly initalized */
+ int user; /* number of current users (0 or 1) */
+ enum card_type card;
+ struct tvnorm *timing;
+
+ unsigned short id; /* number of this device */
+ char name[32]; /* name of this device */
+ struct pci_dev *pci_dev; /* PCI device */
+ unsigned char revision; /* revision of zr36057 */
+ unsigned int zr36057_adr; /* bus address of IO mem returned by PCI BIOS */
+ unsigned char *zr36057_mem; /* pointer to mapped IO memory */
+
+ int map_mjpeg_buffers; /* Flag which bufferset will map by next mmap() */
+
+ spinlock_t lock; /* Spinlock irq and hardware */
+ struct semaphore sem; /* Guard parallel ioctls and mmap */
+
+ /* Video for Linux parameters */
+
+ struct video_picture picture; /* Current picture params */
+ struct video_buffer buffer; /* Current buffer params */
+ struct video_window window; /* Current window params */
+ int buffer_set, window_set; /* Flags if the above structures are set */
+ int video_interlace; /* Image on screen is interlaced */
+
+ u32 *overlay_mask;
+ wait_queue_head_t v4l_capq;
+
+ int v4l_overlay_active; /* Overlay grab is activated */
+ int v4l_memgrab_active; /* Memory grab is activated */
+
+ int v4l_grab_frame; /* Frame number being currently grabbed */
+#define NO_GRAB_ACTIVE (-1)
+ int v4l_grab_seq; /* Number of frames grabbed */
+ int gwidth; /* Width of current memory capture */
+ int gheight; /* Height of current memory capture */
+ int gformat; /* Format of ... */
+ int gbpl; /* byte per line of ... */
+
+ /* V4L grab queue of frames pending */
+
+ unsigned v4l_pend_head;
+ unsigned v4l_pend_tail;
+ int v4l_pend[V4L_MAX_FRAME];
+
+ struct v4l_gbuffer v4l_gbuf[VIDEO_MAX_FRAME]; /* V4L buffers' info */
+
+ /* Buz MJPEG parameters */
+
+ unsigned long jpg_nbufs; /* Number of buffers */
+ unsigned long jpg_bufsize; /* Size of mjpeg buffers in bytes */
+ int jpg_buffers_allocated; /* Flag if buffers are allocated */
+ int need_contiguous; /* Flag if contiguous buffers are needed */
+
+ enum zoran_codec_mode codec_mode; /* status of codec */
+ struct zoran_params params; /* structure with a lot of things to play with */
+
+ wait_queue_head_t jpg_capq; /* wait here for grab to finish */
+
+ /* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */
+ /* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */
+ /* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */
+ unsigned long jpg_que_head; /* Index where to put next buffer which is queued */
+ unsigned long jpg_dma_head; /* Index of next buffer which goes into stat_com */
+ unsigned long jpg_dma_tail; /* Index of last buffer in stat_com */
+ unsigned long jpg_que_tail; /* Index of last buffer in queue */
+ unsigned long jpg_seq_num; /* count of frames since grab/play started */
+ unsigned long jpg_err_seq; /* last seq_num before error */
+ unsigned long jpg_err_shift;
+ unsigned long jpg_queued_num; /* count of frames queued since grab/play started */
+
+ /* zr36057's code buffer table */
+ u32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */
+
+ /* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */
+ int jpg_pend[BUZ_MAX_FRAME];
+
+ /* array indexed by frame number */
+ struct zoran_gbuffer jpg_gbuf[BUZ_MAX_FRAME]; /* MJPEG buffers' info */
+
+ /* Additional stuff for testing */
+ struct proc_dir_entry *zoran_proc;
+
+ int testing;
+ int jpeg_error;
+ int intr_counter_GIRQ1;
+ int intr_counter_GIRQ0;
+ int intr_counter_CodRepIRQ;
+ int intr_counter_JPEGRepIRQ;
+ int field_counter;
+ int IRQ1_in;
+ int IRQ1_out;
+ int JPEG_in;
+ int JPEG_out;
+ int JPEG_0;
+ int JPEG_1;
+ int END_event_missed;
+ int JPEG_missed;
+ int JPEG_error;
+ int num_errors;
+ int JPEG_max_missed;
+ int JPEG_min_missed;
+
+ u32 last_isr;
+ unsigned long frame_num;
+
+ wait_queue_head_t test_q;
+};
+
+#endif
+
+/*The following should be done in more portable way. It depends on define
+ of _ALPHA_BUZ in the Makefile.*/
+
+#ifdef _ALPHA_BUZ
+#define btwrite(dat,adr) writel((dat),(char *) (zr->zr36057_adr+(adr)))
+#define btread(adr) readl(zr->zr36057_adr+(adr))
+#else
+#define btwrite(dat,adr) writel((dat), (char *) (zr->zr36057_mem+(adr)))
+#define btread(adr) readl(zr->zr36057_mem+(adr))
+#endif
+
+#define btand(dat,adr) btwrite((dat) & btread(adr), adr)
+#define btor(dat,adr) btwrite((dat) | btread(adr), adr)
+#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
+
+#define I2C_TSA5522 0xc2
+#define I2C_TDA9850 0xb6
+#define I2C_HAUPEE 0xa0
+#define I2C_STBEE 0xae
+#define I2C_SAA7111 0x48
+#define I2C_SAA7110 0x9c
+#define I2C_SAA7185 0x88
+//#define I2C_ADV7175 0xd4
+#define I2C_ADV7175 0x54
+
+#define TDA9850_CON1 0x04
+#define TDA9850_CON2 0x05
+#define TDA9850_CON3 0x06
+#define TDA9850_CON4 0x07
+#define TDA9850_ALI1 0x08
+#define TDA9850_ALI2 0x09
+#define TDA9850_ALI3 0x0a
+
+#endif
More information about the MPlayer-dev-eng
mailing list