summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog1
-rw-r--r--m4/decoders.m415
-rw-r--r--m4/summary.m41
-rw-r--r--src/video_dec/Makefile.am9
-rw-r--r--src/video_dec/libjpeg.c375
5 files changed, 401 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index 4c9c97d9e..870752b85 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,6 @@
xine-lib (1.2.3) ????-??-??
* Add test image generator input plugin
+ * Add fast libjpeg based JPEG decoder
xine-lib (1.2.2) 2012-06-09
* Fix musepack decoder compilation if libmpcdec headers aren't available.
diff --git a/m4/decoders.m4 b/m4/decoders.m4
index 09d8dd900..659f8a766 100644
--- a/m4/decoders.m4
+++ b/m4/decoders.m4
@@ -112,6 +112,21 @@ AC_DEFUN([XINE_DECODER_PLUGINS], [
fi
AM_CONDITIONAL([ENABLE_GDK_PIXBUF], [test x"$have_gdkpixbuf" = x"yes"])
+ dnl libjpeg (optional; enabled by default)
+ AC_ARG_ENABLE([libjpeg],
+ [AS_HELP_STRING([--enable-libjpeg], [Enable libjpeg support (default: enabled)])],
+ [test x"$enableval" != x"no" && enable_libjpeg="yes"])
+ if test x"$enable_libjpeg" != x"no"; then
+ AC_CHECK_LIB([jpeg], [jpeg_start_decompress],
+ [AC_CHECK_HEADERS([jpeglib.h], [have_libjpeg=yes], [have_libjpeg=no])], [have_libjpeg=no])
+ if test x"$enable_libjpeg" = x"yes" && test x"$have_libjpeg" != x"yes"; then
+ AC_MSG_ERROR([libjpeg support requested, but libjpeg not found])
+ elif test x"$have_libjpeg" = x"yes"; then
+ JPEG_LIBS="-ljpeg"
+ AC_SUBST(JPEG_LIBS)
+ fi
+ fi
+ AM_CONDITIONAL([ENABLE_LIBJPEG], [test x"$have_libjpeg" = x"yes"])
dnl ImageMagick (optional; enabled by default)
AC_ARG_WITH([imagemagick],
diff --git a/m4/summary.m4 b/m4/summary.m4
index 0fe0d4fd0..49783252c 100644
--- a/m4/summary.m4
+++ b/m4/summary.m4
@@ -79,6 +79,7 @@ AC_DEFUN([XINE_LIB_SUMMARY], [
test x"$have_dxr3" = x"yes" && echo " - dxr3_video"
test x"$have_gdkpixbuf" = x"yes" && echo " - gdk-pixbuf"
test x"$have_imagemagick" = x"yes" && echo " - image"
+ test x"$have_libjpeg" = x"yes" && echo " - libjpeg"
test x"$have_theora" = x"yes" && echo " - theora"
test x"$have_w32dll" = x"yes" && echo " - w32dll"
test x"$have_vdpau" = x"yes" && echo " - vdpau"
diff --git a/src/video_dec/Makefile.am b/src/video_dec/Makefile.am
index f20a88bad..10c6b18eb 100644
--- a/src/video_dec/Makefile.am
+++ b/src/video_dec/Makefile.am
@@ -20,8 +20,13 @@ if ENABLE_GDK_PIXBUF
gdkpixbuf_module = xineplug_decode_gdk_pixbuf.la
endif
+if ENABLE_LIBJPEG
+libjpeg_module = xineplug_decode_libjpeg.la
+endif
+
xineplug_LTLIBRARIES = $(image_module) \
$(gdkpixbuf_module) \
+ $(libjpeg_module) \
$(theora_module) \
xineplug_decode_bitplane.la \
xineplug_decode_rgb.la \
@@ -43,3 +48,7 @@ xineplug_decode_image_la_CFLAGS = $(AM_CFLAGS) $(WAND_CFLAGS)
xineplug_decode_gdk_pixbuf_la_SOURCES = gdkpixbuf.c
xineplug_decode_gdk_pixbuf_la_LIBADD = $(XINE_LIB) $(DYNAMIC_LD_LIBS) $(GDK_PIXBUF_LIBS)
xineplug_decode_gdk_pixbuf_la_CFLAGS = $(AM_CFLAGS) $(GDK_PIXBUF_CFLAGS)
+
+xineplug_decode_libjpeg_la_SOURCES = libjpeg.c
+xineplug_decode_libjpeg_la_LIBADD = $(XINE_LIB) $(JPEG_LIBS)
+xineplug_decode_libjpeg_la_CFLAGS = $(AM_CFLAGS) $(JPEG_CFLAGS)
diff --git a/src/video_dec/libjpeg.c b/src/video_dec/libjpeg.c
new file mode 100644
index 000000000..6e9b22e4b
--- /dev/null
+++ b/src/video_dec/libjpeg.c
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2003-2012 the xine project
+ * Copyright (C) 2012 Petri Hintukainen <phintuka@users.sourceforge.net>
+ *
+ * This file is part of xine, a free video player.
+ *
+ * xine 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.
+ *
+ * xine is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Fast JPEG image decoder using libjpeg
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#define LOG_MODULE "jpeg_video_decoder"
+#define LOG_VERBOSE
+
+/*
+#define LOG
+*/
+
+#include <jpeglib.h>
+
+#include <xine/xine_internal.h>
+#include <xine/video_out.h>
+#include <xine/buffer.h>
+
+
+typedef struct {
+ video_decoder_class_t decoder_class;
+
+ int enable_downscaling;
+
+} jpeg_class_t;
+
+
+typedef struct jpeg_decoder_s {
+ video_decoder_t video_decoder;
+
+ jpeg_class_t *cls;
+
+ xine_stream_t *stream;
+ int video_open;
+
+ unsigned char *image;
+ int index;
+
+} jpeg_decoder_t;
+
+/*
+ * memory source
+ */
+
+METHODDEF(void)
+mem_init_source (j_decompress_ptr cinfo)
+{
+}
+
+METHODDEF(boolean)
+mem_fill_input_buffer (j_decompress_ptr cinfo)
+{
+ static const JOCTET EOI[] = { 0xFF, JPEG_EOI };
+
+ cinfo->src->next_input_byte = EOI;
+ cinfo->src->bytes_in_buffer = 2;
+
+ return TRUE;
+}
+
+METHODDEF(void)
+mem_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
+{
+ if (num_bytes <= cinfo->src->bytes_in_buffer) {
+ cinfo->src->bytes_in_buffer -= num_bytes;
+ cinfo->src->next_input_byte += num_bytes;
+ } else {
+ mem_fill_input_buffer(cinfo);
+ }
+}
+
+METHODDEF(void)
+mem_term_source (j_decompress_ptr cinfo)
+{
+}
+
+static void jpeg_memory_src (j_decompress_ptr cinfo, const JOCTET *data, size_t size)
+{
+ if (!cinfo->src) {
+ cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(struct jpeg_source_mgr));
+ }
+
+ cinfo->src->init_source = mem_init_source;
+ cinfo->src->fill_input_buffer = mem_fill_input_buffer;
+ cinfo->src->skip_input_data = mem_skip_input_data;
+ cinfo->src->resync_to_restart = jpeg_resync_to_restart;
+ cinfo->src->term_source = mem_term_source;
+
+ cinfo->src->bytes_in_buffer = size;
+ cinfo->src->next_input_byte = data;
+}
+
+/*
+ * xine-lib decoder interface
+ */
+
+static void jpeg_decode_data (video_decoder_t *this_gen, buf_element_t *buf) {
+ jpeg_decoder_t *this = (jpeg_decoder_t *) this_gen;
+
+ if (!this->video_open) {
+ lprintf("opening video\n");
+ (this->stream->video_out->open) (this->stream->video_out, this->stream);
+ this->video_open = 1;
+ }
+
+ xine_buffer_copyin(this->image, this->index, buf->mem, buf->size);
+ this->index += buf->size;
+
+ if (buf->decoder_flags & BUF_FLAG_FRAME_END && this->index > 0) {
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ JSAMPARRAY buffer;
+
+ int i, linesize;
+ int width, height;
+ vo_frame_t *img;
+ int max_width, max_height;
+
+ /* query max. image size vo can handle */
+ max_width = this->stream->video_out->get_property( this->stream->video_out,
+ VO_PROP_MAX_VIDEO_WIDTH);
+ max_height = this->stream->video_out->get_property( this->stream->video_out,
+ VO_PROP_MAX_VIDEO_HEIGHT);
+
+ /* init and parse header */
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_decompress(&cinfo);
+ jpeg_memory_src(&cinfo, this->image, this->index);
+ jpeg_read_header(&cinfo, TRUE);
+
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_WIDTH, cinfo.image_width);
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HEIGHT, cinfo.image_height);
+
+ lprintf("header parsed\n");
+
+ /* set decoding parameters */
+
+ cinfo.out_color_space = JCS_YCbCr;
+
+ /* request scaling when image is too large for vo */
+ if (this->cls->enable_downscaling) {
+ cinfo.output_width = cinfo.image_width;
+ cinfo.output_height = cinfo.image_height;
+ cinfo.scale_num = 1;
+ cinfo.scale_denom = 1;
+ while ((max_width > 0 && cinfo.output_width > max_width) ||
+ (max_height > 0 && cinfo.output_height > max_height)) {
+ cinfo.scale_denom <<= 1;
+ cinfo.output_width >>= 1;
+ cinfo.output_height >>= 1;
+ }
+ if (cinfo.scale_denom > 1) {
+ xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
+ LOG_MODULE ": downscaling image by 1:%d to %dx%d\n",
+ cinfo.scale_denom, cinfo.output_width, cinfo.output_height);
+ }
+ }
+
+ /* start decompress */
+
+ jpeg_start_decompress(&cinfo);
+
+ width = cinfo.output_width;
+ height = cinfo.output_height;
+
+ /* crop when image is too large for vo */
+ if (max_width > 0 && cinfo.output_width > max_width)
+ width = max_width;
+ if (max_height > 0 && cinfo.output_height > max_height)
+ height = max_height;
+
+ img = this->stream->video_out->get_frame (this->stream->video_out,
+ width, height,
+ (double)width/(double)height,
+ XINE_IMGFMT_YUY2,
+ VO_BOTH_FIELDS);
+
+ linesize = cinfo.output_width * cinfo.output_components;
+ buffer = (cinfo.mem->alloc_sarray)((void*)&cinfo, JPOOL_IMAGE, linesize, 1);
+
+ /* cut to frame width */
+ if (cinfo.output_width > img->width) {
+ lprintf("cut right border %d pixels\n", cinfo.output_width - img->width);
+ linesize = img->width * 3;
+ }
+
+ /* YUV444->YUV422 simple */
+ while (cinfo.output_scanline < cinfo.output_height) {
+ uint8_t *dst = img->base[0] + img->pitches[0] * cinfo.output_scanline;
+
+ jpeg_read_scanlines(&cinfo, buffer, 1);
+
+ /* cut to frame height */
+ if (cinfo.output_scanline > img->height) {
+ lprintf("cut bottom scanline %d\n", cinfo.output_scanline - 1);
+ continue;
+ }
+
+ for (i = 0; i < linesize; i += 3) {
+ *dst++ = buffer[0][i];
+ if (i & 1) {
+ *dst++ = buffer[0][i + 2];
+ } else {
+ *dst++ = buffer[0][i + 1];
+ }
+ }
+ }
+
+ jpeg_finish_decompress(&cinfo);
+ jpeg_destroy_decompress(&cinfo);
+
+ img->pts = buf->pts;
+ img->duration = 3600;
+ img->bad_frame = 0;
+
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_FRAME_DURATION, img->duration);
+
+ img->draw(img, this->stream);
+ img->free(img);
+
+ this->index = 0;
+ }
+}
+
+
+static void jpeg_flush (video_decoder_t *this_gen) {
+ /*
+ * flush out any frames that are still stored in the decoder
+ */
+}
+
+
+static void jpeg_reset (video_decoder_t *this_gen) {
+ jpeg_decoder_t *this = (jpeg_decoder_t *) this_gen;
+
+ /*
+ * reset decoder after engine flush (prepare for new
+ * video data not related to recently decoded data)
+ */
+
+ this->index = 0;
+}
+
+
+static void jpeg_discontinuity (video_decoder_t *this_gen) {
+ /*
+ * a time reference discontinuity has happened.
+ * that is, it must forget any currently held pts value
+ */
+}
+
+static void jpeg_dispose (video_decoder_t *this_gen) {
+ jpeg_decoder_t *this = (jpeg_decoder_t *) this_gen;
+
+ if (this->video_open) {
+ lprintf("closing video\n");
+
+ this->stream->video_out->close(this->stream->video_out, this->stream);
+ this->video_open = 0;
+ }
+
+ xine_buffer_free(this->image);
+
+ lprintf("closed\n");
+ free (this);
+}
+
+
+static video_decoder_t *open_plugin (video_decoder_class_t *class_gen,
+ xine_stream_t *stream) {
+
+ jpeg_class_t *cls = (jpeg_class_t *) class_gen;
+ jpeg_decoder_t *this;
+
+ lprintf("opened\n");
+
+ this = (jpeg_decoder_t *) calloc(1, sizeof(jpeg_decoder_t));
+
+ this->video_decoder.decode_data = jpeg_decode_data;
+ this->video_decoder.flush = jpeg_flush;
+ this->video_decoder.reset = jpeg_reset;
+ this->video_decoder.discontinuity = jpeg_discontinuity;
+ this->video_decoder.dispose = jpeg_dispose;
+ this->cls = cls;
+ this->stream = stream;
+
+ /*
+ * initialisation of privates
+ */
+
+ this->image = xine_buffer_init(10240);
+
+ return &this->video_decoder;
+}
+
+/*
+ * jpeg plugin class
+ */
+static void *init_class (xine_t *xine, void *data) {
+
+ jpeg_class_t *this;
+
+ this = (jpeg_class_t *) calloc(1, sizeof(jpeg_class_t));
+
+ this->decoder_class.open_plugin = open_plugin;
+ this->decoder_class.identifier = "jpegvdec";
+ this->decoder_class.description = N_("JPEG image video decoder plugin");
+ this->decoder_class.dispose = default_video_decoder_class_dispose;
+
+ /*
+ * initialisation of privates
+ */
+
+ this->enable_downscaling =
+ xine->config->register_bool(xine->config,
+ "video.processing.libjpeg_downscaling", 1,
+ _("allow downscaling of JPEG images (an alternative is to crop)"),
+ _("If enabled, you allow xine to downscale JPEG images "
+ "so that those can be viewed with your graphics hardware. "
+ "If scaling is disabled, images will be cropped."),
+ 10, NULL, this);
+
+ lprintf("class opened\n");
+
+ return this;
+}
+
+/*
+ * exported plugin catalog entry
+ */
+
+static const uint32_t supported_types[] = { BUF_VIDEO_JPEG, 0 };
+
+static const decoder_info_t dec_info_jpeg = {
+ supported_types, /* supported types */
+ 10, /* priority */
+};
+
+const plugin_info_t xine_plugin_info[] EXPORTED = {
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_VIDEO_DECODER, 19, "jpeg", XINE_VERSION_CODE, &dec_info_jpeg, init_class },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};