/* 2004.02.01 first released source code for IOMP */ /* * Copyright (C) 2003 the xine project * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * * $Id: image.c,v 1.2 2003/11/25 04:26:07 georgedon Exp $ * * a image video decoder */ #include #include #include #include #include #include #include #include "xine_internal.h" #include "bswap.h" #include "video_out.h" #include "buffer.h" /* #define LOG */ typedef struct { video_decoder_class_t decoder_class; /* * private variables */ } image_class_t; typedef struct image_decoder_s { video_decoder_t video_decoder; image_class_t *cls; xine_stream_t *stream; int video_open; int pts; /* png */ png_structp png_ptr; png_infop info_ptr; char *user_error_ptr; png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type, filter_type; png_bytep *rows; jmp_buf jmpbuf; int passes, rowbytes; int rows_valid; } image_decoder_t; /* * png stuff */ static void info_callback(png_structp png_ptr, png_infop info); static void row_callback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass); static void end_callback(png_structp png_ptr, png_infop info); static int initialize_png_reader(image_decoder_t *this) { this->png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, (png_voidp)this, NULL, NULL); if (!this->png_ptr) return -1; this->info_ptr = png_create_info_struct(this->png_ptr); if (!this->info_ptr) { png_destroy_read_struct(&this->png_ptr, NULL, NULL); return -1; } if (setjmp(this->jmpbuf)) { png_destroy_read_struct(&this->png_ptr, &this->info_ptr, (png_infopp)NULL); return -1; } png_set_progressive_read_fn(this->png_ptr, (void *)this, info_callback, row_callback, end_callback); return 0; } static void finalize_png_reader(image_decoder_t *this) { png_destroy_read_struct(&this->png_ptr, &this->info_ptr, (png_infopp)NULL); this->png_ptr = NULL; this->info_ptr = NULL; } static int process_data(image_decoder_t *this, png_bytep buffer, png_uint_32 length) { if (setjmp(this->jmpbuf)) { png_destroy_read_struct(&this->png_ptr, &this->info_ptr, (png_infopp)NULL); return -1; } png_process_data(this->png_ptr, this->info_ptr, buffer, length); return 0; } /* * process png header (do some conversions if necessary) */ static void info_callback(png_structp png_ptr, png_infop info_ptr) { int i; image_decoder_t *this = png_get_progressive_ptr(png_ptr); #ifdef LOG printf("image: png info cb\n"); #endif png_get_IHDR(png_ptr, info_ptr, &this->width, &this->height, &this->bit_depth, &this->color_type, &this->interlace_type, &this->compression_type, &this->filter_type); /* expand palette images to RGB, low-bit-depth * grayscale images to 8 bits, transparency chunks to full alpha channel; * strip 16-bit-per-sample images to 8 bits per sample; and convert * grayscale to RGB[A] */ if (this->color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); if (this->color_type == PNG_COLOR_TYPE_GRAY && this->bit_depth < 8) png_set_expand(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr); if (this->bit_depth == 16) png_set_strip_16(png_ptr); if (this->color_type == PNG_COLOR_TYPE_GRAY || this->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); if (this->color_type & PNG_COLOR_MASK_ALPHA) png_set_strip_alpha(png_ptr); /* we'll let libpng expand interlaced images, too */ this->passes = png_set_interlace_handling(png_ptr); /* all transformations have been registered; now update info_ptr data and * then get rowbytes */ png_read_update_info(png_ptr, info_ptr); this->rowbytes = (int)png_get_rowbytes(png_ptr, info_ptr); this->rows = xine_xmalloc(sizeof(png_bytep)*this->height); for (i=0; iheight; i++) { this->rows[i] = xine_xmalloc(this->rowbytes); } this->rows_valid = 1; } static void row_callback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) { image_decoder_t *this = png_get_progressive_ptr(png_ptr); if (!new_row) return; /* * copy new row to this->rows */ png_progressive_combine_row(png_ptr, this->rows[row_num], new_row); } /* for rgb2yuv */ #define CENTERSAMPLE 128 #define SCALEBITS 16 #define FIX(x) ( (int32_t) ( (x) * (1<rows_valid) { img = this->stream->video_out->get_frame (this->stream->video_out, this->width, this->height, (double)this->width/(double)this->height, XINE_IMGFMT_YUY2, VO_BOTH_FIELDS); img->pts = this->pts; img->duration = 3600; img->bad_frame = 0; for (row=0; rowheight; row++) { uint16_t *out; out = (uint16_t *) (img->base[0] + row * img->pitches[0] ); for (col=0; colwidth; col++, out++) { uint8_t r,g,b; uint8_t y,u,v; r = *(this->rows[row]+col*3); g = *(this->rows[row]+col*3+1); b = *(this->rows[row]+col*3+2); y = (FIX(0.299) * r + FIX(0.587) * g + FIX(0.114) * b + ONE_HALF) >> SCALEBITS; if (!(col & 0x0001)) { /* even pixel, do u */ u = (- FIX(0.16874) * r - FIX(0.33126) * g + FIX(0.5) * b + CBCR_OFFSET + ONE_HALF-1) >> SCALEBITS; *out = ( (uint16_t) u << 8) | (uint16_t) y; } else { /* odd pixel, do v */ v = (FIX(0.5) * r - FIX(0.41869) * g - FIX(0.08131) * b + CBCR_OFFSET + ONE_HALF-1) >> SCALEBITS; *out = ( (uint16_t) v << 8) | (uint16_t) y; } *out = le2me_16(*out); } } this->stream->stream_info[XINE_STREAM_INFO_FRAME_DURATION] = img->duration; img->draw(img, this->stream); img->free(img); } } /* * png stuff end */ static void image_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { image_decoder_t *this = (image_decoder_t *) this_gen; if (!this->png_ptr) { if (initialize_png_reader(this) < 0) { printf("image: failed to init png reader\n"); } } if (!this->video_open) { #ifdef LOG printf("image: opening video\n"); #endif this->stream->video_out->open(this->stream->video_out, this->stream); this->video_open = 1; } #ifdef LOG printf("image: have to decode data\n"); #endif this->pts = buf->pts; if (process_data(this, buf->content, buf->size) < 0) { printf("image: error processing data\n"); } } static void image_flush (video_decoder_t *this_gen) { /* image_decoder_t *this = (image_decoder_t *) this_gen; */ /* * flush out any frames that are still stored in the decoder */ } static void image_reset (video_decoder_t *this_gen) { /* image_decoder_t *this = (image_decoder_t *) this_gen; */ /* * reset decoder after engine flush (prepare for new * video data not related to recently decoded data) */ } static void image_discontinuity (video_decoder_t *this_gen) { /* image_decoder_t *this = (image_decoder_t *) this_gen; */ /* * a time reference discontinuity has happened. * that is, it must forget any currently held pts value */ } static void image_dispose (video_decoder_t *this_gen) { image_decoder_t *this = (image_decoder_t *) this_gen; if (this->video_open) { #ifdef LOG printf("image: closing video\n"); #endif this->stream->video_out->close(this->stream->video_out, this->stream); this->video_open = 0; } #ifdef LOG printf("image: closed\n"); #endif free (this); } static video_decoder_t *open_plugin (video_decoder_class_t *class_gen, xine_stream_t *stream) { image_class_t *cls = (image_class_t *) class_gen; image_decoder_t *this; #ifdef LOG printf("image: opened\n"); #endif this = (image_decoder_t *) xine_xmalloc (sizeof (image_decoder_t)); this->video_decoder.decode_data = image_decode_data; this->video_decoder.flush = image_flush; this->video_decoder.reset = image_reset; this->video_decoder.discontinuity = image_discontinuity; this->video_decoder.dispose = image_dispose; this->cls = cls; this->stream = stream; /* * initialisation of privates */ if (initialize_png_reader(this) < 0) { printf("image: failed to init png reader\n"); } return &this->video_decoder; } /* * image plugin class */ static char *get_identifier (video_decoder_class_t *this) { return "imagevdec"; } static char *get_description (video_decoder_class_t *this) { return "image video decoder plugin"; } static void dispose_class (video_decoder_class_t *this_gen) { image_class_t *this = (image_class_t *) this_gen; #ifdef LOG printf("image: class closed\n"); #endif free (this); } static void *init_class (xine_t *xine, void *data) { image_class_t *this; /* config_values_t *config = xine->config; */ this = (image_class_t *) xine_xmalloc (sizeof (image_class_t)); this->decoder_class.open_plugin = open_plugin; this->decoder_class.get_identifier = get_identifier; this->decoder_class.get_description = get_description; this->decoder_class.dispose = dispose_class; /* * initialisation of privates */ #ifdef LOG printf("image: class opened\n"); #endif return this; } /* * exported plugin catalog entry */ static uint32_t supported_types[] = { BUF_VIDEO_IMAGE, 0 }; static decoder_info_t dec_info_image = { supported_types, /* supported types */ 6 /* priority */ }; plugin_info_t xine_plugin_info[] = { /* type, API, "name", version, special_info, init_function */ { PLUGIN_VIDEO_DECODER, 15, "image", XINE_VERSION_CODE, &dec_info_image, init_class }, { PLUGIN_NONE, 0, "", 0, NULL, NULL } };