commit 9bd7882fffb6f410c4cb5f7bc81238a705fa7eda Author: Andreas Auras Date: Tue Jan 31 10:23:49 2012 +0100 Support for new continuous video frame grabbing feature This patch added support for new continuous video frame grabbing feature for the vdr input plugin. The old grab feature is still also supported retaining compatibility to older versions of vdr xine plugin. The patch changes the vdr input plugin protocol version to 905. The new grabbing feature is able to return a jpeg compressed image if xine-lib is compiled against the libjpeg library. Auto detection of jpeg library is added to configure. Library location could be specified with new configure parameter '--with-jpeg-prefix'. The libjpeg dependency is also added to debian control file. diff --git a/configure.ac b/configure.ac index c55f23a..881d749 100644 --- a/configure.ac +++ b/configure.ac @@ -304,6 +304,30 @@ test x"$have_zlib" != x"yes" && AC_MSG_ERROR(zlib needed) AC_SUBST(ZLIB_CPPFLAGS) AC_SUBST(ZLIB_LIBS) +dnl libjpeg (optional; enabled by default) +AC_ARG_WITH([jpeglib-prefix], + [AS_HELP_STRING([--with-jpeglib-prefix=PREFIX], [path to jpeg compression library])], + [if test x"$withval" != x"no"; then + JPEGLIB_CPPFLAGS="-I$withval/include" + JPEGLIB_LIBS="-L$withval/lib" + else + have_jpeglib=no + fi]) +if test x"$have_jpeglib" != x"no" ; then + AC_CHECK_LIB([jpeg], [jpeg_start_compress], + [JPEGLIB_LIBS="$JPEGLIB_LIBS -ljpeg" + ac_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $JPEGLIB_CPPFLAGS" + AC_CHECK_HEADER([jpeglib.h], [have_jpeglib=yes], [have_jpeglib=no]) + CPPFLAGS="$ac_save_CPPFLAGS"], + [have_jpeglib=no], + ["$JPEGLIB_LIBS"]) + if test x"$have_jpeglib" = x"yes"; then + AC_DEFINE([HAVE_JPEGLIB], 1, [Define this if you have jpeg library]) + AC_SUBST(JPEGLIB_CPPFLAGS) + AC_SUBST(JPEGLIB_LIBS) + fi +fi + dnl FreeType2 (optional; disabled by default) AC_ARG_WITH([freetype], [AS_HELP_STRING([--with-freetype], [Build with FreeType2 library])], diff --git a/debian/control b/debian/control index 60e11e0..6e77bc5 100644 --- a/debian/control +++ b/debian/control @@ -25,7 +25,7 @@ Build-Depends: debhelper (>= 5.0.1), binutils (>= 2.12.90.0.9), pkg-config, libsmbclient-dev, libspeex-dev, libmng-dev, libmad0-dev, libmpcdec-dev, libcdio-dev, libvcdinfo-dev, libdvdnav-dev, libdvdread-dev, libbluray-dev, - zlib1g-dev, w3m, xmlto, librsvg2-bin + zlib1g-dev, w3m, xmlto, librsvg2-bin, libjpeg-dev | libjpeg62-dev Standards-Version: 3.7.2 Package: libxine2-dev diff --git a/include/xine/vdr.h b/include/xine/vdr.h index 4093472..42f355a 100644 --- a/include/xine/vdr.h +++ b/include/xine/vdr.h @@ -22,7 +22,7 @@ #define __VDR_H -#define XINE_VDR_VERSION 901 +#define XINE_VDR_VERSION 905 enum funcs @@ -62,6 +62,7 @@ enum funcs , func_get_version , func_discontinuity , func_query_capabilities + , func_grab_image_v2 }; enum keys @@ -421,6 +422,19 @@ data_grab_image_t; +typedef struct __attribute__((packed)) data_grab_image_v2_s +{ + data_header_t header; + + uint16_t width; + uint16_t height; + uint16_t jpeg; + uint16_t quality; +} +data_grab_image_v2_t; + + + typedef struct __attribute__((packed)) result_grab_image_s { result_header_t header; @@ -618,6 +632,7 @@ typedef union __attribute__((packed)) data_union_u data_wait_t wait; data_setup_t setup; data_grab_image_t grab_image; + data_grab_image_v2_t grab_image_v2; data_get_pts_t get_pts; data_first_frame_t first_frame; data_still_frame_t still_frame; diff --git a/src/vdr/Makefile.am b/src/vdr/Makefile.am index 68e6cd8..9a31301 100644 --- a/src/vdr/Makefile.am +++ b/src/vdr/Makefile.am @@ -2,7 +2,7 @@ include $(top_srcdir)/misc/Makefile.quiet include $(top_builddir)/misc/Makefile.plugins include $(top_srcdir)/misc/Makefile.common -AM_CFLAGS = $(DEFAULT_OCFLAGS) $(VISIBILITY_FLAG) +AM_CFLAGS = $(DEFAULT_OCFLAGS) $(VISIBILITY_FLAG) $(JPEGLIB_CPPFLAGS) AM_LDFLAGS = $(xineplug_ldflags) if ENABLE_VDR @@ -11,4 +11,4 @@ endif xineplug_vdr_la_SOURCES = combined_vdr.c combined_vdr.h input_vdr.c post_vdr_video.c post_vdr_audio.c xineplug_vdr_la_CFLAGS = $(AM_CFLAGS) -fno-strict-aliasing -xineplug_vdr_la_LIBADD = $(XINE_LIB) $(PTHREAD_LIBS) +xineplug_vdr_la_LIBADD = $(XINE_LIB) $(PTHREAD_LIBS) $(JPEGLIB_LIBS) diff --git a/src/vdr/input_vdr.c b/src/vdr/input_vdr.c index 53da46c..7d83939 100644 --- a/src/vdr/input_vdr.c +++ b/src/vdr/input_vdr.c @@ -50,6 +50,14 @@ #include "combined_vdr.h" +#ifdef HAVE_JPEGLIB +#ifdef boolean +# define HAVE_BOOLEAN +#endif +#include +#undef boolean +#endif /*HAVE_JPEGLIB*/ + #define VDR_MAX_NUM_WINDOWS 16 #define VDR_ABS_FIFO_DIR "/tmp/vdr-xine" @@ -183,6 +191,57 @@ typedef struct vdr_input_class_t; +#ifdef HAVE_JPEGLIB +#define JPEGCOMPRESSMEM 500000 + +typedef struct tJpegCompressData_s { + int size; + unsigned char *mem; +} tJpegCompressData; + +static void JpegCompressInitDestination(const j_compress_ptr cinfo) +{ + tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data; + if (jcd) + { + cinfo->dest->free_in_buffer = jcd->size = JPEGCOMPRESSMEM; + cinfo->dest->next_output_byte = jcd->mem = (unsigned char *)malloc(jcd->size); + } +} + +static boolean JpegCompressEmptyOutputBuffer(const j_compress_ptr cinfo) +{ + tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data; + if (jcd) + { + int Used = jcd->size; + jcd->size += JPEGCOMPRESSMEM; + jcd->mem = (unsigned char *)realloc(jcd->mem, jcd->size); + if (jcd->mem) + { + cinfo->dest->next_output_byte = jcd->mem + Used; + cinfo->dest->free_in_buffer = jcd->size - Used; + return TRUE; + } + } + return FALSE; +} + +static void JpegCompressTermDestination(const j_compress_ptr cinfo) +{ + tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data; + if (jcd) + { + int Used = cinfo->dest->next_output_byte - jcd->mem; + if (Used < jcd->size) + { + jcd->size = Used; + jcd->mem = (unsigned char *)realloc(jcd->mem, jcd->size); + } + } +} +#endif /* HAVE_JPEGLIB */ + static int vdr_write(int f, void *b, int n) { @@ -1164,6 +1223,112 @@ t3 = _now(); } break; + case func_grab_image_v2: + { + READ_DATA_OR_FAIL(grab_image_v2, lprintf("Got GRABIMAGEV2\n")); + { + int size = 0; + char *img = NULL; + result_grab_image_t result_grab_image; + memset(&result_grab_image, 0, sizeof(result_grab_image)); + result_grab_image.header.func = data->header.func; + result_grab_image.header.len = sizeof(result_grab_image); + + xine_grab_video_frame_t *grab_frame = xine_new_grab_video_frame(this->stream); + if (grab_frame) + { + grab_frame->width = data->width; + grab_frame->height = data->height; + if (!grab_frame->grab(grab_frame)) + { + if (!data->jpeg) /* convert to PNM */ + { + /* allocate memory for result */ + size_t bytes = grab_frame->width * grab_frame->height * 3; + img = malloc(bytes + 64); + if (img) + { + /* PNM header */ + sprintf(img, "P6\n%d\n%d\n255\n", grab_frame->width, grab_frame->height); + int hdrlen = strlen(img); + + /* copy image */ + xine_fast_memcpy(img + hdrlen, grab_frame->img, bytes); + + size = bytes + hdrlen; + } + } + else /* JPEG */ + { +#ifdef HAVE_JPEGLIB + /* Compress JPEG */ + struct jpeg_destination_mgr jdm; + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + tJpegCompressData jcd; + + jdm.init_destination = JpegCompressInitDestination; + jdm.empty_output_buffer = JpegCompressEmptyOutputBuffer; + jdm.term_destination = JpegCompressTermDestination; + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + cinfo.dest = &jdm; + cinfo.client_data = &jcd; + cinfo.image_width = grab_frame->width; + cinfo.image_height = grab_frame->height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, data->quality, TRUE); + jpeg_start_compress(&cinfo, TRUE); + + JSAMPROW rp[grab_frame->height]; + int rs = grab_frame->width * 3; + int k; + for (k = 0; k < grab_frame->height; k++) + rp[k] = grab_frame->img + k * rs; + jpeg_write_scanlines(&cinfo, rp, grab_frame->height); + + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + + size = jcd.size; + img = (char*)jcd.mem; +#else + lprintf("No JPEG support present!\n"); +#endif /* HAVE_JPEG_LIB */ + } + } + + if (img && size > 0) + { + result_grab_image.width = grab_frame->width; + result_grab_image.height = grab_frame->height; + } + else + size = 0; + + grab_frame->dispose(grab_frame); + } + + result_grab_image.header.len += size; + + int ok = 0; + if (sizeof(result_grab_image) == vdr_write(this->fh_result, &result_grab_image, sizeof(result_grab_image))) + { + if (!size || (size == vdr_write(this->fh_result, img, size))) + ok = 1; + } + + free(img); + + if (!ok) + return -1; + } + } + break; + case func_get_pts: { READ_DATA_OR_FAIL(get_pts, lprintf("got GETPTS\n"));