rendered paste body/* * Copyright (C) 2007 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 * * * video_out_raw.c, a video output plugin to pass raw data to frontend * * Written by Christophe Thommeret <hftom@free.fr>, * based on others video output plugins. * *//* #define LOG */#define LOG_MODULE "video_out_raw"#define NUM_FRAMES_BACKLOG 4 /* Allow thread some time to render frames */#define BYTES_PER_PIXEL 3#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <math.h>#include <errno.h>#include <ctype.h>#include <pthread.h>#include "xine.h"#include "video_out.h"#include "xine_internal.h"#include "yuv2rgb.h"#include "xineutils.h"typedef struct { vo_frame_t vo_frame; int width, height, format, flags; double ratio; uint8_t *chunk[4]; /* mem alloc by xmalloc_aligned */ uint8_t *rgb, *rgb_dst; yuv2rgb_t *yuv2rgb; /* yuv2rgb converter set up for this frame */} raw_frame_t;#define MAX_OVL_NUM 16typedef struct { vo_driver_t vo_driver; void *user_data; void (*raw_output_cb) (void *user_data, void **frame_keeper, int format, int frame_width, int frame_height, double frame_aspect, void *data0, void *data1, void *data2); void (*raw_overlay_cb) (void *user_data, int num_ovl, raw_overlay_t *overlays_p); void *current_frame; int ovl_changed; raw_overlay_t overlays[MAX_OVL_NUM]; int doYV12; int doYUY2; yuv2rgb_factory_t *yuv2rgb_factory; /* Frame state */ raw_frame_t *frame[NUM_FRAMES_BACKLOG]; xine_t *xine;} raw_driver_t;typedef struct { video_driver_class_t driver_class; xine_t *xine;} raw_class_t;static void raw_overlay_clut_yuv2rgb(raw_driver_t *this, vo_overlay_t *overlay, raw_frame_t *frame){ int i; clut_t* clut = (clut_t*) overlay->color; if (!overlay->rgb_clut) { for ( i=0; i<sizeof(overlay->color)/sizeof(overlay->color[0]); i++ ) { *((uint32_t *)&clut[i]) = frame->yuv2rgb->yuv2rgb_single_pixel_fun(frame->yuv2rgb, clut[i].y, clut[i].cb, clut[i].cr); } overlay->rgb_clut++; } if (!overlay->hili_rgb_clut) { clut = (clut_t*) overlay->hili_color; for ( i=0; i<sizeof(overlay->color)/sizeof(overlay->color[0]); i++) { *((uint32_t *)&clut[i]) = frame->yuv2rgb->yuv2rgb_single_pixel_fun(frame->yuv2rgb, clut[i].y, clut[i].cb, clut[i].cr); } overlay->hili_rgb_clut++; }}static int raw_process_ovl( raw_driver_t *this_gen, vo_overlay_t *overlay ){ raw_overlay_t *ovl = &this_gen->overlays[this_gen->ovl_changed-1]; if ( overlay->width<=0 || overlay->height<=0 ) return 0; if ( ovl->in_use ) /* don't modify it while it's used by frontend */ return 1; if ( (overlay->width*overlay->height)!=(ovl->ovl_w*ovl->ovl_h) ) ovl->ovl_rgba = (uint8_t*)realloc( ovl->ovl_rgba, overlay->width*overlay->height*4 ); ovl->ovl_w = overlay->width; ovl->ovl_h = overlay->height; ovl->ovl_x = overlay->x; ovl->ovl_y = overlay->y; int num_rle = overlay->num_rle; rle_elem_t *rle = overlay->rle; uint8_t *rgba = ovl->ovl_rgba; clut_t *low_colors = (clut_t*)overlay->color; clut_t *hili_colors = (clut_t*)overlay->hili_color; uint8_t *low_trans = overlay->trans; uint8_t *hili_trans = overlay->hili_trans; clut_t *colors; uint8_t *trans; uint8_t alpha; int rlelen = 0; uint8_t clr = 0; int i, pos=0, x, y; while ( num_rle>0 ) { x = pos%ovl->ovl_w; y = pos/ovl->ovl_w; if ( (x>=overlay->hili_left && x<=overlay->hili_right) && (y>=overlay->hili_top && y<=overlay->hili_bottom) ) { colors = hili_colors; trans = hili_trans; } else { colors = low_colors; trans = low_trans; } rlelen = rle->len; clr = rle->color; alpha = trans[clr]; for ( i=0; i<rlelen; ++i ) { rgba[0] = colors[clr].y; rgba[1] = colors[clr].cr; rgba[2] = colors[clr].cb; rgba[3] = alpha*255/15; rgba+= 4; ++pos; } ++rle; --num_rle; } ovl->in_use = 1; return 1;}static void raw_overlay_begin (vo_driver_t *this_gen, vo_frame_t *frame_gen, int changed){ raw_driver_t *this = (raw_driver_t *) this_gen; if ( !changed ) return; ++this->ovl_changed;}static void raw_overlay_blend (vo_driver_t *this_gen, vo_frame_t *frame_gen, vo_overlay_t *overlay){ raw_driver_t *this = (raw_driver_t *) this_gen; raw_frame_t *frame = (raw_frame_t *) frame_gen; if ( !this->ovl_changed || this->ovl_changed>MAX_OVL_NUM ) return; if (overlay->rle) { if (!overlay->rgb_clut || !overlay->hili_rgb_clut) raw_overlay_clut_yuv2rgb (this, overlay, frame); if ( raw_process_ovl( this, overlay ) ) ++this->ovl_changed; }}static void raw_overlay_end (vo_driver_t *this_gen, vo_frame_t *vo_img){ raw_driver_t *this = (raw_driver_t *) this_gen; int i; if ( !this->ovl_changed ) return; this->raw_overlay_cb( this->user_data, this->ovl_changed-1, &this->overlays ); this->ovl_changed = 0;}static void raw_frame_proc_slice (vo_frame_t *vo_img, uint8_t **src){ raw_frame_t *frame = (raw_frame_t *) vo_img ; vo_img->proc_called = 1; if (! frame->rgb_dst) return; if( frame->vo_frame.crop_left || frame->vo_frame.crop_top || frame->vo_frame.crop_right || frame->vo_frame.crop_bottom ) { /* TODO: opengl *could* support this?!? */ /* cropping will be performed by video_out.c */ return; } if (frame->format == XINE_IMGFMT_YV12) frame->yuv2rgb->yuv2rgb_fun (frame->yuv2rgb, frame->rgb_dst, src[0], src[1], src[2]); else frame->yuv2rgb->yuy22rgb_fun (frame->yuv2rgb, frame->rgb_dst, src[0]);}static void raw_frame_field (vo_frame_t *vo_img, int which_field){ raw_frame_t *frame = (raw_frame_t *) vo_img ; raw_driver_t *this = (raw_driver_t *) vo_img->driver; if ( frame->format==XINE_IMGFMT_YV12 && this->doYV12 ) { frame->rgb_dst = 0; return; } else if ( frame->format==XINE_IMGFMT_YUY2 && this->doYUY2 ) { frame->rgb_dst = 0; return; } switch (which_field) { case VO_TOP_FIELD: frame->rgb_dst = (uint8_t *)frame->rgb; break; case VO_BOTTOM_FIELD: frame->rgb_dst = (uint8_t *)frame->rgb + frame->width * BYTES_PER_PIXEL; break; case VO_BOTH_FIELDS: frame->rgb_dst = (uint8_t *)frame->rgb; break; } frame->yuv2rgb->next_slice (frame->yuv2rgb, NULL);}static void raw_frame_dispose (vo_frame_t *vo_img){ raw_frame_t *frame = (raw_frame_t *) vo_img ; frame->yuv2rgb->dispose (frame->yuv2rgb); free (frame->chunk[0]); free (frame->chunk[1]); free (frame->chunk[2]); free (frame->chunk[3]); free (frame);}static vo_frame_t *raw_alloc_frame (vo_driver_t *this_gen){ raw_frame_t *frame; raw_driver_t *this = (raw_driver_t *) this_gen; frame = (raw_frame_t *) xine_xmalloc (sizeof (raw_frame_t)); if (!frame) return NULL; pthread_mutex_init (&frame->vo_frame.mutex, NULL); /* * supply required functions/fields */ frame->vo_frame.proc_slice = raw_frame_proc_slice; frame->vo_frame.proc_frame = NULL; frame->vo_frame.field = raw_frame_field; frame->vo_frame.dispose = raw_frame_dispose; frame->vo_frame.driver = this_gen; /* * colorspace converter for this frame */ frame->yuv2rgb = this->yuv2rgb_factory->create_converter (this->yuv2rgb_factory); return (vo_frame_t *) frame;}static void raw_update_frame_format (vo_driver_t *this_gen, vo_frame_t *frame_gen, uint32_t width, uint32_t height, double ratio, int format, int flags){ raw_driver_t *this = (raw_driver_t *) this_gen; raw_frame_t *frame = (raw_frame_t *) frame_gen; /* Check frame size and format and reallocate if necessary */ if ((frame->width != width) || (frame->height != height) || (frame->format != format) || (frame->flags != flags)) {/* lprintf ("updating frame to %d x %d (ratio=%g, format=%08x)\n", width, height, ratio, format); */ flags &= VO_BOTH_FIELDS; /* (re-) allocate render space */ free (frame->chunk[0]); free (frame->chunk[1]); free (frame->chunk[2]); free (frame->chunk[3]); if (format == XINE_IMGFMT_YV12) { frame->vo_frame.pitches[0] = 8*((width + 7) / 8); frame->vo_frame.pitches[1] = 8*((width + 15) / 16); frame->vo_frame.pitches[2] = 8*((width + 15) / 16); frame->vo_frame.base[0] = xine_xmalloc_aligned (16, frame->vo_frame.pitches[0] * height, (void **) &frame->chunk[0]); frame->vo_frame.base[1] = xine_xmalloc_aligned (16, frame->vo_frame.pitches[1] * ((height+1)/2), (void **) &frame->chunk[1]); frame->vo_frame.base[2] = xine_xmalloc_aligned (16, frame->vo_frame.pitches[2] * ((height+1)/2), (void **) &frame->chunk[2]); } else { frame->vo_frame.pitches[0] = 8*((width + 3) / 4); frame->vo_frame.base[0] = xine_xmalloc_aligned (16, frame->vo_frame.pitches[0] * height, (void **) &frame->chunk[0]); frame->chunk[1] = NULL; frame->chunk[2] = NULL; } frame->rgb = xine_xmalloc_aligned (16, BYTES_PER_PIXEL*width*height, (void **) &frame->chunk[3]); /* set up colorspace converter */ switch (flags) { case VO_TOP_FIELD: case VO_BOTTOM_FIELD: frame->yuv2rgb->configure (frame->yuv2rgb, width, height, 2*frame->vo_frame.pitches[0], 2*frame->vo_frame.pitches[1], width, height, BYTES_PER_PIXEL*width * 2); break; case VO_BOTH_FIELDS: frame->yuv2rgb->configure (frame->yuv2rgb, width, height, frame->vo_frame.pitches[0], frame->vo_frame.pitches[1], width, height, BYTES_PER_PIXEL*width); break; } frame->width = width; frame->height = height; frame->format = format; raw_frame_field ((vo_frame_t *)frame, flags); } frame->ratio = ratio;}static int raw_redraw_needed (vo_driver_t *this_gen){ return 0;}static void raw_display_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen){ raw_driver_t *this = (raw_driver_t *) this_gen; raw_frame_t *frame = (raw_frame_t *) frame_gen; int i; if (this->frame[NUM_FRAMES_BACKLOG-1]) { /*while ( this->current_frame ) { printf("SLEEP\n"); this->raw_output_cb( this->user_data, &this->current_frame, 2, frame->width, frame->height, frame->ratio, 0, 0, 0 ); usleep(10000); }*/ this->frame[NUM_FRAMES_BACKLOG-1]->vo_frame.free (&this->frame[NUM_FRAMES_BACKLOG-1]->vo_frame); } for (i = NUM_FRAMES_BACKLOG-1; i > 0; i--) this->frame[i] = this->frame[i-1]; this->frame[0] = frame; this->current_frame = frame; if ( frame->rgb_dst ) { this->raw_output_cb( this->user_data, &this->current_frame, 4, frame->width, frame->height, frame->ratio, frame->rgb, 0, 0 ); } else if ( frame->format==XINE_IMGFMT_YV12 ) { this->raw_output_cb( this->user_data, &this->current_frame, 1, frame->width, frame->height, frame->ratio, frame->vo_frame.base[0], frame->vo_frame.base[1], frame->vo_frame.base[2] ); } else { this->raw_output_cb( this->user_data, &this->current_frame, 2, frame->width, frame->height, frame->ratio, frame->vo_frame.base[0], 0, 0 ); }}static int raw_get_property (vo_driver_t *this_gen, int property){ raw_driver_t *this = (raw_driver_t *) this_gen; switch (property) { case VO_PROP_ASPECT_RATIO: return XINE_VO_ASPECT_AUTO; case VO_PROP_MAX_NUM_FRAMES: return 15; case VO_PROP_BRIGHTNESS: return 0; case VO_PROP_CONTRAST: return 128; case VO_PROP_SATURATION: return 128; case VO_PROP_WINDOW_WIDTH: return 0; case VO_PROP_WINDOW_HEIGHT: return 0; default: return 0; }}static int raw_set_property (vo_driver_t *this_gen, int property, int value){ return value;}static void raw_get_property_min_max (vo_driver_t *this_gen, int property, int *min, int *max){ *min = 0; *max = 0;}static int raw_gui_data_exchange (vo_driver_t *this_gen, int data_type, void *data){ return 0;}static uint32_t raw_get_capabilities (vo_driver_t *this_gen){ uint32_t capabilities = VO_CAP_YV12 | VO_CAP_YUY2; return capabilities;}static void raw_dispose (vo_driver_t *this_gen){ raw_driver_t *this = (raw_driver_t *) this_gen; int i; for (i = 0; i < NUM_FRAMES_BACKLOG; i++) if (this->frame[i]) this->frame[i]->vo_frame.dispose (&this->frame[i]->vo_frame); this->yuv2rgb_factory->dispose (this->yuv2rgb_factory); for ( i=0; i<MAX_OVL_NUM; ++i ) free( this->overlays[i].ovl_rgba ); free (this);}static vo_driver_t *raw_open_plugin (video_driver_class_t *class_gen, const void *visual_gen){ raw_class_t *class = (raw_class_t *) class_gen; raw_visual_t *visual = (raw_visual_t *) visual_gen; raw_driver_t *this; int i; this = (raw_driver_t *) xine_xmalloc (sizeof (raw_driver_t)); if (!this) return NULL; this->raw_output_cb = visual->raw_output_cb; this->user_data = visual->user_data; this->xine = class->xine; this->raw_overlay_cb = visual->raw_overlay_cb; this->doYV12 = visual->supported_formats&1; this->doYUY2 = visual->supported_formats&2; this->vo_driver.get_capabilities = raw_get_capabilities; this->vo_driver.alloc_frame = raw_alloc_frame; this->vo_driver.update_frame_format = raw_update_frame_format; this->vo_driver.overlay_begin = raw_overlay_begin; this->vo_driver.overlay_blend = raw_overlay_blend; this->vo_driver.overlay_end = raw_overlay_end; this->vo_driver.display_frame = raw_display_frame; this->vo_driver.get_property = raw_get_property; this->vo_driver.set_property = raw_set_property; this->vo_driver.get_property_min_max = raw_get_property_min_max; this->vo_driver.gui_data_exchange = raw_gui_data_exchange; this->vo_driver.dispose = raw_dispose; this->vo_driver.redraw_needed = raw_redraw_needed; this->yuv2rgb_factory = yuv2rgb_factory_init (MODE_24_BGR, 0, NULL); for (i = 0; i < NUM_FRAMES_BACKLOG; i++) this->frame[i] = 0; for ( i=0; i<MAX_OVL_NUM; ++i ) { this->overlays[i].ovl_w = this->overlays[i].ovl_h = 2; this->overlays[i].ovl_rgba = (uint8_t*)malloc(2*2*4); this->overlays[i].ovl_x = this->overlays[i].ovl_y = 0; this->overlays[i].in_use = 0; } this->ovl_changed = 0; this->current_frame = 0; return &this->vo_driver;}/* * class functions */static char* raw_get_identifier (video_driver_class_t *this_gen){ return "raw";}static char* raw_get_description (video_driver_class_t *this_gen){ return _("xine video output plugin passing raw data to supplied callback");}static void raw_dispose_class (video_driver_class_t *this_gen){ raw_class_t *this = (raw_class_t *) this_gen; free (this);}static void *raw_init_class (xine_t *xine, void *visual_gen){ raw_class_t *this = (raw_class_t *) xine_xmalloc (sizeof (raw_class_t)); this->driver_class.open_plugin = raw_open_plugin; this->driver_class.get_identifier = raw_get_identifier; this->driver_class.get_description = raw_get_description; this->driver_class.dispose = raw_dispose_class; this->xine = xine; return this;}static const vo_info_t vo_info_raw = { 7, /* priority */ XINE_VISUAL_TYPE_RAW /* visual type */};/* * exported plugin catalog entry */const plugin_info_t xine_plugin_info[] EXPORTED = { /* type, API, "name", version, special_info, init_function */ { PLUGIN_VIDEO_OUT, 21, "raw", XINE_VERSION_CODE, &vo_info_raw, raw_init_class }, { PLUGIN_NONE, 0, "", 0, NULL, NULL }};