diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/dxr3/Makefile.am | 2 | ||||
-rw-r--r-- | src/dxr3/dxr3_decode_spu.c | 56 | ||||
-rw-r--r-- | src/dxr3/dxr3_spu_encoder.c | 493 | ||||
-rw-r--r-- | src/dxr3/video_out_dxr3.c | 113 | ||||
-rw-r--r-- | src/dxr3/video_out_dxr3.h | 31 |
5 files changed, 677 insertions, 18 deletions
diff --git a/src/dxr3/Makefile.am b/src/dxr3/Makefile.am index f3db2f562..92fd08ab5 100644 --- a/src/dxr3/Makefile.am +++ b/src/dxr3/Makefile.am @@ -22,7 +22,7 @@ xineplug_decode_dxr3_video_la_LDFLAGS = -avoid-version -module xineplug_decode_dxr3_spu_la_SOURCES = dxr3_decode_spu.c nav_read.c xineplug_decode_dxr3_spu_la_LDFLAGS = -avoid-version -module -xineplug_vo_out_dxr3_la_SOURCES = video_out_dxr3.c dxr3_mpeg_encoders.c alphablend.c +xineplug_vo_out_dxr3_la_SOURCES = video_out_dxr3.c dxr3_mpeg_encoders.c alphablend.c dxr3_spu_encoder.c if HAVE_X11 xineplug_vo_out_dxr3_la_LIBADD = $(X_LIBS) -lXext xineplug_vo_out_dxr3_la_LDFLAGS = -avoid-version -module $(link_fame) $(link_rte) $(X_LIBS) diff --git a/src/dxr3/dxr3_decode_spu.c b/src/dxr3/dxr3_decode_spu.c index cc35ed734..b1d61c7e6 100644 --- a/src/dxr3/dxr3_decode_spu.c +++ b/src/dxr3/dxr3_decode_spu.c @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: dxr3_decode_spu.c,v 1.14 2002/07/21 10:02:35 mroi Exp $ + * $Id: dxr3_decode_spu.c,v 1.15 2002/08/17 14:30:09 mroi Exp $ */ /* dxr3 spu decoder plugin. @@ -42,6 +42,7 @@ #include "xine-engine/bswap.h" #include "nav_types.h" #include "nav_read.h" +#include "video_out_dxr3.h" #include "dxr3.h" #define LOG_PTS 0 @@ -76,14 +77,15 @@ typedef struct dxr3_spu_stream_state_s { typedef struct dxr3_spudec_s { spu_decoder_t spu_decoder; - vo_instance_t *vo_out; xine_t *xine; + dxr3_driver_t *dxr3_vo; /* we need to talk to the video out */ char devname[128]; char devnum[3]; int fd_spu; /* to access the dxr3 spu device */ dxr3_spu_stream_state_t spu_stream_state[MAX_SPU_STREAMS]; + uint32_t clut[16]; /* the current color lookup table */ int menu; /* are we in a menu? */ int button_filter; pci_t pci; @@ -145,6 +147,9 @@ spu_decoder_t *init_spu_decoder_plugin(int iface_version, xine_t *xine) this->spu_decoder.priority = 10; this->xine = xine; + /* We need to talk to dxr3 video out to coordinate spus and overlays */ + this->dxr3_vo = (dxr3_driver_t *)xine->video_driver; + this->fd_spu = 0; this->menu = 0; this->button_filter = 1; @@ -176,8 +181,7 @@ static void dxr3_spudec_init(spu_decoder_t *this_gen, vo_instance_t *vo_out) char tmpstr[128]; int i; - this->vo_out = vo_out; - + pthread_mutex_lock(&this->dxr3_vo->spu_device_lock); /* open dxr3 spu device */ snprintf(tmpstr, sizeof(tmpstr), "%s_sp%s", this->devname, this->devnum); if ((this->fd_spu = open(tmpstr, O_WRONLY)) < 0) { @@ -188,6 +192,11 @@ static void dxr3_spudec_init(spu_decoder_t *this_gen, vo_instance_t *vo_out) #if LOG_SPU printf ("dxr3_decode_spu: init: SPU_FD = %i\n",this->fd_spu); #endif + /* We are talking directly to the dxr3 video out to allow concurrent + * access to the same spu device */ + this->dxr3_vo->fd_spu = this->fd_spu; + pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); + for (i=0; i < MAX_SPU_STREAMS; i++) { this->spu_stream_state[i].stream_filter = 1; this->spu_stream_state[i].spu_length = 0; @@ -208,8 +217,13 @@ static void dxr3_spudec_decode_data(spu_decoder_t *this_gen, buf_element_t *buf) #endif if (buf->content[0] == 0) /* cheap endianess detection */ dxr3_swab_clut((int *)buf->content); + pthread_mutex_lock(&this->dxr3_vo->spu_device_lock); if (ioctl(this->fd_spu, EM8300_IOCTL_SPU_SETPALETTE, buf->content)) printf("dxr3_decode_spu: failed to set CLUT (%s)\n", strerror(errno)); + /* remember clut, when video out places some overlay we may need to restore it */ + memcpy(this->clut, buf->content, 16 * sizeof(uint32_t)); + this->dxr3_vo->clut_cluttered = 0; + pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); return; } if(buf->type == BUF_SPU_SUBP_CONTROL) { @@ -252,10 +266,13 @@ static void dxr3_spudec_decode_data(spu_decoder_t *this_gen, buf_element_t *buf) spu_button.buttonN = this->buttonN; xine_send_event(this->xine, &spu_event.event); } - if ((dxr3_spudec_copy_nav_to_btn(this, 0, &btn ) > 0)) + if ((dxr3_spudec_copy_nav_to_btn(this, 0, &btn ) > 0)) { + pthread_mutex_lock(&this->dxr3_vo->spu_device_lock); if (ioctl(this->fd_spu, EM8300_IOCTL_SPU_BUTTON, &btn)) printf("dxr3_decode_spu: failed to set spu button (%s)\n", strerror(errno)); + pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); + } } if ((pci.hli.hl_gi.hli_ss == 0) && (this->pci.hli.hl_gi.hli_ss == 1)) { @@ -271,8 +288,10 @@ static void dxr3_spudec_decode_data(spu_decoder_t *this_gen, buf_element_t *buf) this->pci.hli.hl_gi.hli_ss = 0; this->menu = 0; this->button_filter = 1; + pthread_mutex_lock(&this->dxr3_vo->spu_device_lock); ioctl(this->fd_spu, EM8300_IOCTL_SPU_BUTTON, NULL); write(this->fd_spu, empty_spu, sizeof(empty_spu)); + pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); } } return; @@ -350,6 +369,8 @@ static void dxr3_spudec_decode_data(spu_decoder_t *this_gen, buf_element_t *buf) return; } + pthread_mutex_lock(&this->dxr3_vo->spu_device_lock); + /* write sync timestamp to the card */ if (buf->pts) { int64_t vpts; @@ -367,6 +388,13 @@ static void dxr3_spudec_decode_data(spu_decoder_t *this_gen, buf_element_t *buf) printf("dxr3_decode_spu: spu setpts failed (%s)\n", strerror(errno)); } + /* has video out tampered with our palette */ + if (this->dxr3_vo->clut_cluttered) { + if (ioctl(this->fd_spu, EM8300_IOCTL_SPU_SETPALETTE, this->clut)) + printf("dxr3_decode_spu: failed to set CLUT (%s)\n", strerror(errno)); + this->dxr3_vo->clut_cluttered = 0; + } + /* write spu data to the card */ #if LOG_SPU printf ("dxr3_decode_spu: write: SPU_FD = %i\n",this->fd_spu); @@ -375,11 +403,14 @@ static void dxr3_spudec_decode_data(spu_decoder_t *this_gen, buf_element_t *buf) if (written < 0) { printf("dxr3_decode_spu: spu device write failed (%s)\n", strerror(errno)); + pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); return; } if (written != buf->size) printf("dxr3_decode_spu: Could only write %d of %d spu bytes.\n", written, buf->size); + + pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); } static void dxr3_spudec_reset(spu_decoder_t *this_gen) @@ -397,8 +428,11 @@ static void dxr3_spudec_close(spu_decoder_t *this_gen) #if LOG_SPU printf("dxr3_decode_spu: close: SPU_FD = %i\n",this->fd_spu); #endif + pthread_mutex_lock(&this->dxr3_vo->spu_device_lock); close(this->fd_spu); this->fd_spu = 0; + this->dxr3_vo->fd_spu = 0; + pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); } static void dxr3_spudec_dispose(spu_decoder_t *this_gen) @@ -444,10 +478,13 @@ static void dxr3_spudec_event_listener(void *this_gen, xine_event_t *event_gen) #endif this->buttonN = but->buttonN; if ((but->show > 0) && !this->button_filter && - (dxr3_spudec_copy_nav_to_btn(this, but->show - 1, &btn) > 0)) + (dxr3_spudec_copy_nav_to_btn(this, but->show - 1, &btn) > 0)) { + pthread_mutex_lock(&this->dxr3_vo->spu_device_lock); if (ioctl(this->fd_spu, EM8300_IOCTL_SPU_BUTTON, &btn)) printf("dxr3_decode_spu: failed to set spu button (%s)\n", strerror(errno)); + pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); + } if (but->show == 2) this->button_filter = 1; #if LOG_BTN printf("dxr3_decode_spu: buttonN = %u\n",but->buttonN); @@ -464,9 +501,14 @@ static void dxr3_spudec_event_listener(void *this_gen, xine_event_t *event_gen) #ifdef WORDS_BIGENDIAN dxr3_swab_clut(clut->clut); #endif + pthread_mutex_lock(&this->dxr3_vo->spu_device_lock); if (ioctl(this->fd_spu, EM8300_IOCTL_SPU_SETPALETTE, clut->clut)) printf("dxr3_decode_spu: failed to set CLUT (%s)\n", strerror(errno)); + /* remember clut, when video out places some overlay we may need to restore it */ + memcpy(this->clut, clut->clut, 16 * sizeof(uint32_t)); + this->dxr3_vo->clut_cluttered = 0; + pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); } break; case XINE_EVENT_FRAME_CHANGE: @@ -476,7 +518,7 @@ static void dxr3_spudec_event_listener(void *this_gen, xine_event_t *event_gen) printf("dxr3_decode_spu: aspect changed to %d\n", this->aspect); #endif break; - } + } } static int dxr3_spudec_copy_nav_to_btn(dxr3_spudec_t *this, int32_t mode, em8300_button_t *btn) diff --git a/src/dxr3/dxr3_spu_encoder.c b/src/dxr3/dxr3_spu_encoder.c new file mode 100644 index 000000000..c2cfe64a6 --- /dev/null +++ b/src/dxr3/dxr3_spu_encoder.c @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2000-2002 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: dxr3_spu_encoder.c,v 1.1 2002/08/17 14:30:10 mroi Exp $ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <float.h> + +#include "video_out_dxr3.h" + +/* We use the following algorithm to reduce the given overlay palette + * to a spu palette with only four distinct colours: + * - create a histogram on the overlay palette + * - the color with the maximum histogram value becomes one spu color + * - modify the histogram so that the counts for colors very near to the + * chosen one are lowered; this is done by multiplying with a penalty + * function 1-1/(dist/DIST_COEFF + 1) where dist is the squared spatial + * distance between current color and chosen spu color + * - continue with the next maximum + * The used histogram modification function from above looks like that: + * ^ + * 1 + ******** + * | ****** + * | **** + * | ** + * | * + * 0 **--------------------> dist + */ +#define DIST_COEFF 1024.0 + +#define LOG_ENC 1 + +/* spu encoder function */ +spu_encoder_t *dxr3_spu_encoder_init(void); +void dxr3_spu_encode(spu_encoder_t *this); + +/* helper functions */ +static void convert_palette(spu_encoder_t *this); +static void create_histogram(spu_encoder_t *this); +static void generate_clut(spu_encoder_t *this); +static void map_colors(spu_encoder_t *this); +static void convert_clut(spu_encoder_t *this); +static void convert_overlay(spu_encoder_t *this); +static void write_rle(spu_encoder_t *this, int *offset, int *higher_nibble, int length, int color); +static void write_byte(spu_encoder_t *this, int *offset, uint8_t byte); +static void write_nibble(spu_encoder_t *this, int *offset, int *higher_nibble, uint8_t nibble); + + +spu_encoder_t *dxr3_spu_encoder_init(void) +{ + spu_encoder_t *this; + + this = (spu_encoder_t *)malloc(sizeof(spu_encoder_t)); + this->target = NULL; + this->need_reencode = 0; + this->malloc_size = 0; +#if LOG_ENC + printf("dxr3_spu_encoder: initialized\n"); +#endif + return this; +} + +void dxr3_spu_encode(spu_encoder_t *this) +{ + if (!this->need_reencode || !this->overlay) return; +#if LOG_ENC + printf("dxr3_spu_encoder: overlay for encoding arrived.\n"); +#endif + convert_palette(this); + create_histogram(this); + generate_clut(this); + map_colors(this); + convert_clut(this); + convert_overlay(this); +#if LOG_ENC + printf("dxr3_spu_encoder: overlay encoding completed\n"); +#endif +} + + +static void convert_palette(spu_encoder_t *this) +{ + int i, y, cb, cr, r, g, b; + + if (!this->overlay->rgb_clut) { + for (i = 0; i < OVL_PALETTE_SIZE; i++) { + y = (this->overlay->color[i] >> 16) & 0xff; + cr = (this->overlay->color[i] >> 8) & 0xff; + cb = (this->overlay->color[i] ) & 0xff; + r = 1.164 * y + 1.596 * (cr - 128); + g = 1.164 * y - 0.813 * (cr - 128) - 0.392 * (cb - 128); + b = 1.164 * y + 2.017 * (cb - 128); + if (r < 0) r = 0; + if (g < 0) g = 0; + if (b < 0) b = 0; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + this->overlay->color[i] = (r << 16) | (g << 8) | b; + } + this->overlay->rgb_clut = 1; + } + if (!this->overlay->clip_rgb_clut) { + for (i = 0; i < OVL_PALETTE_SIZE; i++) { + y = (this->overlay->clip_color[i] >> 16) & 0xff; + cr = (this->overlay->clip_color[i] >> 8) & 0xff; + cb = (this->overlay->clip_color[i] ) & 0xff; + r = 1.164 * y + 1.596 * (cr - 128); + g = 1.164 * y - 0.813 * (cr - 128) - 0.392 * (cb - 128); + b = 1.164 * y + 2.017 * (cb - 128); + if (r < 0) r = 0; + if (g < 0) g = 0; + if (b < 0) b = 0; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + this->overlay->clip_color[i] = (r << 16) | (g << 8) | b; + } + this->overlay->clip_rgb_clut = 1; + } +} + +static void create_histogram(spu_encoder_t *this) +{ + rle_elem_t *rle; + int i, x, y, len, part; + + for (i = 0; i < OVL_PALETTE_SIZE; i++) + this->map[i] = this->clip_map[i] = 0; + x = y = 0; + for (i = 0, rle = this->overlay->rle; i < this->overlay->num_rle; i++, rle++) { + len = rle->len; + if (y >= this->overlay->clip_top && y < this->overlay->clip_bottom) { + if (x < this->overlay->clip_left) { + part = (this->overlay->clip_left - x < len) ? (this->overlay->clip_left - x) : len; + this->map[rle->color] += part; + len -= part; + x += part; + } + if (x >= this->overlay->clip_left && x < this->overlay->clip_right) { + part = (this->overlay->clip_right - x < len) ? (this->overlay->clip_right - x) : len; + this->clip_map[rle->color] += part; + len -= part; + x += part; + } + } + this->map[rle->color] += len; + x += len; + if (x >= this->overlay->width) { + x = 0; + y++; + } + } +#if LOG_ENC + for (i = 0; i < OVL_PALETTE_SIZE; i++) + if (this->map[i]) + printf("dxr3_spu_encoder: histogram: color #%d 0x%.8x appears %d times\n", + i, this->overlay->color[i], this->map[i]); + for (i = 0; i < OVL_PALETTE_SIZE; i++) + if (this->clip_map[i]) + printf("dxr3_spu_encoder: histogram: clip color #%d 0x%.8x appears %d times\n", + i, this->overlay->clip_color[i], this->clip_map[i]); +#endif +} + +static void generate_clut(spu_encoder_t *this) +{ + int i, max, spu_color; + double dist, diff; + + /* find first maximum -> first spu color */ + max = 0; + for (i = 1; i < OVL_PALETTE_SIZE; i++) + if (this->map[i] > this->map[max]) max = i; + this->color[0] = this->overlay->color[max]; + this->trans[0] = this->overlay->trans[max]; + + for (spu_color = 1; spu_color < 4; spu_color++) { + /* modify histogram and find next maximum -> next spu color */ + max = 0; + for (i = 0; i < OVL_PALETTE_SIZE; i++) { + /* subtract a correction based on the distance to the last spu color */ + diff = ((this->overlay->color[i] ) & 0xff) - ((this->color[spu_color - 1] ) & 0xff); + dist = diff * diff; + diff = ((this->overlay->color[i] >> 8) & 0xff) - ((this->color[spu_color - 1] >> 8) & 0xff); + dist += diff * diff; + diff = ((this->overlay->color[i] >> 16) & 0xff) - ((this->color[spu_color - 1] >> 16) & 0xff); + dist += diff * diff; + diff = ((this->overlay->trans[i] ) ) - ((this->trans[spu_color - 1] ) ); + dist += diff * diff; + this->map[i] *= 1 - 1.0 / (dist / DIST_COEFF + 1.0); + if (this->map[i] > this->map[max]) max = i; + } + this->color[spu_color] = this->overlay->color[max]; + this->trans[spu_color] = this->overlay->trans[max]; + } +#if LOG_ENC + for (spu_color = 0; spu_color < 4; spu_color++) + printf("dxr3_spu_encoder: spu color %d: 0x%.8x, trans: %d\n", spu_color, + this->color[spu_color], this->trans[spu_color]); +#endif + + /* now the same stuff again, this time for the palette of the clipping area */ + + /* find first maximum -> first spu color */ + max = 0; + for (i = 1; i < OVL_PALETTE_SIZE; i++) + if (this->clip_map[i] > this->clip_map[max]) max = i; + this->clip_color[0] = this->overlay->clip_color[max]; + this->clip_trans[0] = this->overlay->clip_trans[max]; + + for (spu_color = 1; spu_color < 4; spu_color++) { + /* modify histogram and find next maximum -> next spu color */ + max = 0; + for (i = 0; i < OVL_PALETTE_SIZE; i++) { + /* subtract a correction based on the distance to the last spu color */ + diff = ((this->overlay->clip_color[i] ) & 0xff) - ((this->clip_color[spu_color - 1] ) & 0xff); + dist = diff * diff; + diff = ((this->overlay->clip_color[i] >> 8) & 0xff) - ((this->clip_color[spu_color - 1] >> 8) & 0xff); + dist += diff * diff; + diff = ((this->overlay->clip_color[i] >> 16) & 0xff) - ((this->clip_color[spu_color - 1] >> 16) & 0xff); + dist += diff * diff; + diff = ((this->overlay->clip_trans[i] ) ) - ((this->clip_trans[spu_color - 1] ) ); + dist += diff * diff; + this->clip_map[i] *= 1 - 1.0 / (dist / DIST_COEFF + 1.0); + if (this->clip_map[i] > this->clip_map[max]) max = i; + } + this->clip_color[spu_color] = this->overlay->clip_color[max]; + this->clip_trans[spu_color] = this->overlay->clip_trans[max]; + } +#if LOG_ENC + for (spu_color = 0; spu_color < 4; spu_color++) + printf("dxr3_spu_encoder: spu clip color %d: 0x%.8x, trans: %d\n", spu_color, + this->clip_color[spu_color], this->clip_trans[spu_color]); +#endif +} + +static void map_colors(spu_encoder_t *this) +{ + int i, min, spu_color; + double dist, diff, min_dist; + + /* for all colors in overlay palette find closest spu color */ + for (i = 0; i < OVL_PALETTE_SIZE; i++) { + min = 0; + min_dist = DBL_MAX; + for (spu_color = 0; spu_color < 4; spu_color++) { + diff = ((this->overlay->color[i] ) & 0xff) - ((this->color[spu_color] ) & 0xff); + dist = diff * diff; + diff = ((this->overlay->color[i] >> 8) & 0xff) - ((this->color[spu_color] >> 8) & 0xff); + dist += diff * diff; + diff = ((this->overlay->color[i] >> 16) & 0xff) - ((this->color[spu_color] >> 16) & 0xff); + dist += diff * diff; + diff = ((this->overlay->trans[i] ) ) - ((this->trans[spu_color] ) ); + dist += diff * diff; + if (dist < min_dist) { + min_dist = dist; + min = spu_color; + } + } + this->map[i] = min; + } + + /* for all colors in overlay clip palette find closest spu color */ + for (i = 0; i < OVL_PALETTE_SIZE; i++) { + min = 0; + min_dist = DBL_MAX; + for (spu_color = 0; spu_color < 4; spu_color++) { + diff = ((this->overlay->clip_color[i] ) & 0xff) - ((this->clip_color[spu_color] ) & 0xff); + dist = diff * diff; + diff = ((this->overlay->clip_color[i] >> 8) & 0xff) - ((this->clip_color[spu_color] >> 8) & 0xff); + dist += diff * diff; + diff = ((this->overlay->clip_color[i] >> 16) & 0xff) - ((this->clip_color[spu_color] >> 16) & 0xff); + dist += diff * diff; + diff = ((this->overlay->clip_trans[i] ) ) - ((this->clip_trans[spu_color] ) ); + dist += diff * diff; + if (dist < min_dist) { + min_dist = dist; + min = spu_color; + } + } + this->clip_map[i] = min; + } +} + +static void convert_clut(spu_encoder_t *this) +{ + int i, r, g, b, y, cb, cr; + + for (i = 0; i < 4; i++) { + r = (this->color[i] >> 16) & 0xff; + g = (this->color[i] >> 8) & 0xff; + b = (this->color[i] ) & 0xff; + y = 0.257 * r + 0.504 * g + 0.098 * b; + cr = 0.439 * r - 0.368 * g - 0.071 * b + 128; + cb = -0.148 * r - 0.291 * g + 0.439 * b + 128; + this->color[i] = (y << 16) | (cr << 8) | cb; + } + for (i = 4; i < 16; i++) + this->color[i] = 0x00008080; + + for (i = 0; i < 4; i++) { + r = (this->clip_color[i] >> 16) & 0xff; + g = (this->clip_color[i] >> 8) & 0xff; + b = (this->clip_color[i] ) & 0xff; + y = 0.257 * r + 0.504 * g + 0.098 * b; + cr = 0.439 * r - 0.368 * g - 0.071 * b + 128; + cb = -0.148 * r - 0.291 * g + 0.439 * b + 128; + this->clip_color[i] = (y << 16) | (cr << 8) | cb; + } + for (i = 4; i < 16; i++) + this->clip_color[i] = 0x00008080; +} + +static void convert_overlay(spu_encoder_t *this) +{ + int offset = 0, field_start[2]; + rle_elem_t *rle; + int field, i, len, part, x, y, higher_nibble = 1; + + /* size will be determined later */ + write_byte(this, &offset, 0x00); + write_byte(this, &offset, 0x00); + + /* control sequence pointer will be determined later */ + write_byte(this, &offset, 0x00); + write_byte(this, &offset, 0x00); + + for (field = 0; field < 2; field++) { + write_byte(this, &offset, 0x00); + write_byte(this, &offset, 0x00); +#if LOG_ENC + printf("dxr3_spu_encoder: encoding field %d\n", field); +#endif + field_start[field] = offset; + x = y = 0; + for (i = 0, rle = this->overlay->rle; i < this->overlay->num_rle; i++, rle++) { + len = rle->len; + if ((y & 1) == field) { + if (y >= this->overlay->clip_top && y < this->overlay->clip_bottom) { + if (x < this->overlay->clip_left) { + part = (this->overlay->clip_left - x < len) ? (this->overlay->clip_left - x) : len; + write_rle(this, &offset, &higher_nibble, part, this->map[rle->color]); + len -= part; + x += part; + } + if (x >= this->overlay->clip_left && x < this->overlay->clip_right) { + part = (this->overlay->clip_right - x < len) ? (this->overlay->clip_right - x) : len; + write_rle(this, &offset, &higher_nibble, part, this->clip_map[rle->color]); + len -= part; + x += part; + } + } + write_rle(this, &offset, &higher_nibble, len, this->map[rle->color]); + } + x += len; + if (x >= this->overlay->width) { + if ((y & 1) == field && !higher_nibble) + write_nibble(this, &offset, &higher_nibble, 0); + x = 0; + y++; + } + } + } + + /* we should be byte aligned here */ + assert(higher_nibble); + + /* control sequence starts here */ + this->target[2] = offset >> 8; + this->target[3] = offset & 0xff; + write_byte(this, &offset, 0x00); + write_byte(this, &offset, 0x00); + /* write pointer to end sequence */ + write_byte(this, &offset, this->target[2]); + write_byte(this, &offset, this->target[3]); + /* write control sequence */ + write_byte(this, &offset, 0x00); + /* clut indices */ + write_byte(this, &offset, 0x03); + write_byte(this, &offset, 0x32); + write_byte(this, &offset, 0x10); + /* alpha information */ + write_byte(this, &offset, 0x04); + write_nibble(this, &offset, &higher_nibble, this->trans[3] & 0xf); + write_nibble(this, &offset, &higher_nibble, this->trans[2] & 0xf); + write_nibble(this, &offset, &higher_nibble, this->trans[1] & 0xf); + write_nibble(this, &offset, &higher_nibble, this->trans[0] & 0xf); + /* on screen position */ +#if LOG_ENC + printf("dxr3_spu_encoder: overlay position: x %d, y %d, width %d, height %d\n", + this->overlay->x, this->overlay->y, this->overlay->width, this->overlay->height); +#endif + write_byte(this, &offset, 0x05); + write_byte(this, &offset, this->overlay->x >> 4); + write_nibble(this, &offset, &higher_nibble, this->overlay->x & 0xf); + write_nibble(this, &offset, &higher_nibble, (this->overlay->x + this->overlay->width - 1) >> 8); + write_byte(this, &offset, (this->overlay->x + this->overlay->width - 1) & 0xff); + write_byte(this, &offset, this->overlay->y >> 4); + write_nibble(this, &offset, &higher_nibble, this->overlay->y & 0xf); + write_nibble(this, &offset, &higher_nibble, (this->overlay->y + this->overlay->height - 1) >> 8); + write_byte(this, &offset, (this->overlay->y + this->overlay->height - 1) & 0xff); + /* field pointers */ + write_byte(this, &offset, 0x06); + write_byte(this, &offset, field_start[0] >> 8); + write_byte(this, &offset, field_start[0] & 0xff); + write_byte(this, &offset, field_start[1] >> 8); + write_byte(this, &offset, field_start[1] & 0xff); + /* end marker */ + write_byte(this, &offset, 0xff); + if (offset & 1) + write_byte(this, &offset, 0xff); + /* write size information */ + this->size = offset; + this->target[0] = offset >> 8; + this->target[1] = offset & 0xff; +} + +static void write_rle(spu_encoder_t *this, int *offset, int *higher_nibble, int length, int color) +{ + if (!length) return; + length <<= 2; + while (length > 0x03fc) { + write_nibble(this, offset, higher_nibble, 0x0); + write_nibble(this, offset, higher_nibble, 0x3); + write_nibble(this, offset, higher_nibble, 0xf); + write_nibble(this, offset, higher_nibble, 0xc | color); + length -= 0x03fc; + } + if ((length & ~0xc) == 0) { + write_nibble(this, offset, higher_nibble, length | color); + return; + } + if ((length & ~0x3c) == 0) { + write_nibble(this, offset, higher_nibble, length >> 4); + write_nibble(this, offset, higher_nibble, (length & 0xc) | color); + return; + } + if ((length & ~0xfc) == 0) { + write_nibble(this, offset, higher_nibble, 0x0); + write_nibble(this, offset, higher_nibble, length >> 4); + write_nibble(this, offset, higher_nibble, (length & 0xc) | color); + return; + } + if ((length & ~0x3fc) == 0) { + write_nibble(this, offset, higher_nibble, 0x0); + write_nibble(this, offset, higher_nibble, length >> 8); + write_nibble(this, offset, higher_nibble, (length >> 4) & 0xf); + write_nibble(this, offset, higher_nibble, (length & 0xc) | color); + return; + } + assert(0); +} + +static void write_byte(spu_encoder_t *this, int *offset, uint8_t byte) +{ + if (*offset >= this->malloc_size) + this->target = realloc(this->target, this->malloc_size += 2048); + this->target[(*offset)++] = byte; +} + +static void write_nibble(spu_encoder_t *this, int *offset, int *higher_nibble, uint8_t nibble) +{ + if (*offset >= this->malloc_size) + this->target = realloc(this->target, this->malloc_size += 2048); + if (*higher_nibble) { + this->target[*offset] &= 0x0f; + this->target[*offset] |= nibble << 4; + *higher_nibble = 0; + } else { + this->target[*offset] &= 0xf0; + this->target[(*offset)++] |= nibble; + *higher_nibble = 1; + } +} diff --git a/src/dxr3/video_out_dxr3.c b/src/dxr3/video_out_dxr3.c index e5204f0c2..7650eaab4 100644 --- a/src/dxr3/video_out_dxr3.c +++ b/src/dxr3/video_out_dxr3.c @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: video_out_dxr3.c,v 1.50 2002/08/15 13:35:12 mroi Exp $ + * $Id: video_out_dxr3.c,v 1.51 2002/08/17 14:30:10 mroi Exp $ */ /* mpeg1 encoding video out plugin for the dxr3. @@ -79,8 +79,10 @@ static void dxr3_frame_dispose(vo_frame_t *frame_gen); static void dxr3_update_frame_format(vo_driver_t *this_gen, vo_frame_t *frame_gen, uint32_t width, uint32_t height, int ratio_code, int format, int flags); +static void dxr3_overlay_begin(vo_driver_t *this_gen, vo_frame_t *frame_gen, int changed); static void dxr3_overlay_blend(vo_driver_t *this_gen, vo_frame_t *frame_gen, vo_overlay_t *overlay); +static void dxr3_overlay_end(vo_driver_t *this_gen, vo_frame_t *frame_gen); static void dxr3_display_frame(vo_driver_t *this_gen, vo_frame_t *frame_gen); static int dxr3_redraw_needed(vo_driver_t *this_gen); static int dxr3_get_property(vo_driver_t *this_gen, int property); @@ -145,9 +147,9 @@ vo_driver_t *init_video_out_plugin(config_values_t *config, void *visual_gen) this->vo_driver.get_capabilities = dxr3_get_capabilities; this->vo_driver.alloc_frame = dxr3_alloc_frame; this->vo_driver.update_frame_format = dxr3_update_frame_format; - this->vo_driver.overlay_begin = NULL; /* not used */ + this->vo_driver.overlay_begin = dxr3_overlay_begin; this->vo_driver.overlay_blend = dxr3_overlay_blend; - this->vo_driver.overlay_end = NULL; /* not used */ + this->vo_driver.overlay_end = dxr3_overlay_end; this->vo_driver.display_frame = dxr3_display_frame; this->vo_driver.redraw_needed = dxr3_redraw_needed; this->vo_driver.get_property = dxr3_get_property; @@ -156,6 +158,8 @@ vo_driver_t *init_video_out_plugin(config_values_t *config, void *visual_gen) this->vo_driver.gui_data_exchange = dxr3_gui_data_exchange; this->vo_driver.exit = dxr3_exit; + pthread_mutex_init(&this->spu_device_lock, NULL); + this->config = config; this->swap_fields = config->register_bool(config, "dxr3.enc_swap_fields", 0, _("swap odd and even lines"), @@ -610,11 +614,27 @@ static void dxr3_update_frame_format(vo_driver_t *this_gen, vo_frame_t *frame_ge frame->swap_fields = this->swap_fields; } +static void dxr3_overlay_begin(vo_driver_t *this_gen, vo_frame_t *frame_gen, int changed) +{ + dxr3_driver_t *this = (dxr3_driver_t *)this_gen; + + /* special treatment is only necessary for mpeg frames */ + if (frame_gen->format != IMGFMT_MPEG) return; + + if (!this->spu_enc) this->spu_enc = dxr3_spu_encoder_init(); + + if (!changed) { + this->spu_enc->need_reencode = 0; + return; + } + + this->spu_enc->need_reencode = 1; + this->spu_enc->overlay = NULL; +} + static void dxr3_overlay_blend(vo_driver_t *this_gen, vo_frame_t *frame_gen, vo_overlay_t *overlay) { - /* FIXME: We only blend non-mpeg frames here. - Is there any way to provide overlays for mpeg content? Subpictures? */ if (frame_gen->format != IMGFMT_MPEG) { dxr3_frame_t *frame = (dxr3_frame_t *)frame_gen; @@ -624,7 +644,86 @@ static void dxr3_overlay_blend(vo_driver_t *this_gen, vo_frame_t *frame_gen, else blend_yuy2(frame->vo_frame.base[0], overlay, frame->vo_frame.width, frame->vo_frame.height); } + } else { /* IMGFMT_MPEG */ + dxr3_driver_t *this = (dxr3_driver_t *)this_gen; + if (!this->spu_enc->need_reencode) return; + /* FIXME: we only handle the last overlay because previous ones are simply overwritten */ + this->spu_enc->overlay = overlay; + } +} + +static void dxr3_overlay_end(vo_driver_t *this_gen, vo_frame_t *frame_gen) +{ + dxr3_driver_t *this = (dxr3_driver_t *)this_gen; + em8300_button_t btn; + char tmpstr[128]; + ssize_t written; + + if (frame_gen->format != IMGFMT_MPEG) return; + if (!this->spu_enc->need_reencode) return; + + dxr3_spu_encode(this->spu_enc); + + /* try to open the dxr3 spu device */ + if (!this->fd_spu) { + snprintf (tmpstr, sizeof(tmpstr), "%s_sp%s", this->devname, this->devnum); + if ((this->fd_spu = open (tmpstr, O_WRONLY)) < 0) { + printf("video_out_dxr3: Failed to open spu device %s (%s)\n", + tmpstr, strerror(errno)); + printf("video_out_dxr3: Overlays are not available\n"); + return; + } + } + + pthread_mutex_lock(&this->spu_device_lock); + + if (!this->spu_enc->overlay) { + uint8_t empty_spu[] = { + 0x00, 0x26, 0x00, 0x08, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x20, 0x01, 0x03, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x06, 0x00, 0x04, 0x00, 0x07, 0xFF, + 0x00, 0x01, 0x00, 0x20, 0x02, 0xFF }; + /* just clear any previous spu */ + ioctl(this->fd_spu, EM8300_IOCTL_SPU_BUTTON, NULL); + write(this->fd_spu, empty_spu, sizeof(empty_spu)); + pthread_mutex_unlock(&this->spu_device_lock); + return; } + + /* copy clip palette */ + this->spu_enc->color[4] = this->spu_enc->clip_color[0]; + this->spu_enc->color[5] = this->spu_enc->clip_color[1]; + this->spu_enc->color[6] = this->spu_enc->clip_color[2]; + this->spu_enc->color[7] = this->spu_enc->clip_color[3]; + /* set palette */ + if (ioctl(this->fd_spu, EM8300_IOCTL_SPU_SETPALETTE, this->spu_enc->color)) + printf("video_out_dxr3: failed to set CLUT (%s)\n", strerror(errno)); + this->clut_cluttered = 1; + /* write spu */ + written = write(this->fd_spu, this->spu_enc->target, this->spu_enc->size); + if (written < 0) + printf("video_out_dxr3: spu device write failed (%s)\n", + strerror(errno)); + else if (written != this->spu_enc->size) + printf("video_out_dxr3: Could only write %d of %d spu bytes.\n", + written, this->spu_enc->size); + /* set clipping */ + btn.color = 0x7654; + btn.contrast = + ((this->spu_enc->clip_trans[3] << 12) & 0xf000) | + ((this->spu_enc->clip_trans[2] << 8) & 0x0f00) | + ((this->spu_enc->clip_trans[1] << 4) & 0x00f0) | + ((this->spu_enc->clip_trans[0] ) & 0x000f); + btn.left = this->spu_enc->overlay->x + this->spu_enc->overlay->clip_left; + btn.right = this->spu_enc->overlay->x + this->spu_enc->overlay->clip_right - 1; + btn.top = this->spu_enc->overlay->y + this->spu_enc->overlay->clip_top; + btn.bottom = this->spu_enc->overlay->y + this->spu_enc->overlay->clip_bottom - 2; + if (ioctl(this->fd_spu, EM8300_IOCTL_SPU_BUTTON, &btn)) + printf("dxr3_decode_spu: failed to set spu button (%s)\n", + strerror(errno)); + + pthread_mutex_unlock(&this->spu_device_lock); } static void dxr3_display_frame(vo_driver_t *this_gen, vo_frame_t *frame_gen) @@ -902,7 +1001,9 @@ static void dxr3_exit(vo_driver_t *this_gen) this->enc->on_close(this); if(this->overlay_enabled) ioctl(this->fd_control, EM8300_IOCTL_OVERLAY_SETMODE, &val); - + close(this->fd_control); + if (this->fd_spu) close(this->fd_spu); + pthread_mutex_destroy(&this->spu_device_lock); free(this); } diff --git a/src/dxr3/video_out_dxr3.h b/src/dxr3/video_out_dxr3.h index d930363a5..ae6e7f6a1 100644 --- a/src/dxr3/video_out_dxr3.h +++ b/src/dxr3/video_out_dxr3.h @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: video_out_dxr3.h,v 1.8 2002/08/11 13:22:55 mroi Exp $ + * $Id: video_out_dxr3.h,v 1.9 2002/08/17 14:30:10 mroi Exp $ */ #ifdef HAVE_CONFIG_H @@ -41,6 +41,7 @@ /* plugin structures */ typedef struct encoder_data_s encoder_data_t; +typedef struct spu_encoder_s spu_encoder_t; typedef enum { ENC_FAME, ENC_RTE } encoder_type; @@ -74,7 +75,10 @@ typedef struct dxr3_driver_s { char devname[128]; char devnum[3]; int fd_control; - int fd_video; /* to access the relevant dxr3 devices */ + int fd_video; + pthread_mutex_t spu_device_lock; + int fd_spu; /* to access the relevant dxr3 devices */ + int clut_cluttered;/* to tell spu decoder that it has to restore the palette */ int enhanced_mode; int swap_fields; /* swap fields */ @@ -88,7 +92,8 @@ typedef struct dxr3_driver_s { int widescreen_enabled; em8300_bcs_t bcs; - encoder_data_t *enc; /* encoder data */ + encoder_data_t *enc; /* mpeg encoder data */ + spu_encoder_t *spu_enc; /* spu encoder */ int video_iheight; /* input height (before adding black bars) */ int video_oheight; /* output height (after adding bars) */ int video_width; @@ -133,10 +138,28 @@ struct encoder_data_s { int (*on_close)(dxr3_driver_t *); }; -/* encoder plugins initialization functions */ +struct spu_encoder_s { + vo_overlay_t *overlay; + int need_reencode; + uint8_t *target; + int size; + int malloc_size; + uint32_t color[16]; + uint8_t trans[4]; + int map[OVL_PALETTE_SIZE]; + uint32_t clip_color[16]; + uint8_t clip_trans[4]; + int clip_map[OVL_PALETTE_SIZE]; +}; + +/* mpeg encoder plugins initialization functions */ #ifdef HAVE_LIBRTE int dxr3_rte_init(dxr3_driver_t *); #endif #ifdef HAVE_LIBFAME int dxr3_fame_init(dxr3_driver_t *); #endif + +/* spu encoder functions */ +spu_encoder_t *dxr3_spu_encoder_init(void); +void dxr3_spu_encode(spu_encoder_t *); |