Mercurial > mplayer.hg
changeset 32456:728bd5c2aea7
Move spudec.[ch] to the sub directory.
author | cigaes |
---|---|
date | Wed, 27 Oct 2010 16:38:51 +0000 |
parents | 5c9189c15fe0 |
children | f4822d5572f5 |
files | Makefile TOOLS/subrip.c av_sub.c command.c libvo/sub.c mencoder.c mpcommon.c mplayer.c spudec.c spudec.h stream/stream_dvdnav.c sub/spudec.c sub/spudec.h vobsub.c |
diffstat | 14 files changed, 1449 insertions(+), 1449 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Wed Oct 27 16:32:03 2010 +0000 +++ b/Makefile Wed Oct 27 16:38:51 2010 +0000 @@ -346,7 +346,6 @@ path.c \ playtree.c \ playtreeparser.c \ - spudec.c \ sub_cc.c \ subopt-helper.c \ vobsub.c \ @@ -521,6 +520,7 @@ stream/stream_null.c \ stream/url.c \ sub/find_sub.c \ + sub/spudec.c \ sub/subreader.c \ $(SRCS_COMMON-yes) @@ -1059,7 +1059,7 @@ TOOLS/bmovl-test$(EXESUF): -lSDL_image -TOOLS/subrip$(EXESUF): vobsub.o spudec.o unrar_exec.o libvo/aclib.o \ +TOOLS/subrip$(EXESUF): vobsub.o sub/spudec.o unrar_exec.o libvo/aclib.o \ ffmpeg/libswscale/libswscale.a ffmpeg/libavutil/libavutil.a $(TEST_OBJS) TOOLS/vfw2menc$(EXESUF): -lwinmm -lole32
--- a/TOOLS/subrip.c Wed Oct 27 16:32:03 2010 +0000 +++ b/TOOLS/subrip.c Wed Oct 27 16:38:51 2010 +0000 @@ -32,7 +32,7 @@ #include <sys/wait.h> #include "libvo/video_out.h" #include "vobsub.h" -#include "spudec.h" +#include "sub/spudec.h" /* linking hacks */ char *mplayer_version;
--- a/av_sub.c Wed Oct 27 16:32:03 2010 +0000 +++ b/av_sub.c Wed Oct 27 16:38:51 2010 +0000 @@ -19,7 +19,7 @@ #include "libavcodec/avcodec.h" #include "libmpdemux/stheader.h" #include "libvo/sub.h" -#include "spudec.h" +#include "sub/spudec.h" #include "av_sub.h" void reset_avsub(struct sh_sub *sh)
--- a/command.c Wed Oct 27 16:32:03 2010 +0000 +++ b/command.c Wed Oct 27 16:38:51 2010 +0000 @@ -45,7 +45,7 @@ #include "libmpcodecs/dec_video.h" #include "libmpcodecs/dec_teletext.h" #include "vobsub.h" -#include "spudec.h" +#include "sub/spudec.h" #include "path.h" #include "stream/tv.h" #include "stream/stream_radio.h"
--- a/libvo/sub.c Wed Oct 27 16:32:03 2010 +0000 +++ b/libvo/sub.c Wed Oct 27 16:38:51 2010 +0000 @@ -38,7 +38,7 @@ #include "video_out.h" #include "font_load.h" #include "sub.h" -#include "spudec.h" +#include "sub/spudec.h" #include "libavutil/common.h" #define NEW_SPLITTING
--- a/mencoder.c Wed Oct 27 16:32:03 2010 +0000 +++ b/mencoder.c Wed Oct 27 16:38:51 2010 +0000 @@ -94,7 +94,7 @@ #include "parser-cfg.h" #include "parser-mecmd.h" #include "path.h" -#include "spudec.h" +#include "sub/spudec.h" #include "vobsub.h" #include "eosd.h" #include "mencoder.h"
--- a/mpcommon.c Wed Oct 27 16:32:03 2010 +0000 +++ b/mpcommon.c Wed Oct 27 16:38:51 2010 +0000 @@ -34,7 +34,7 @@ #include "cpudetect.h" #include "help_mp.h" #include "mp_msg.h" -#include "spudec.h" +#include "sub/spudec.h" #include "version.h" #include "vobsub.h" #include "av_sub.h"
--- a/mplayer.c Wed Oct 27 16:32:03 2010 +0000 +++ b/mplayer.c Wed Oct 27 16:38:51 2010 +0000 @@ -120,7 +120,7 @@ #include "path.h" #include "playtree.h" #include "playtreeparser.h" -#include "spudec.h" +#include "sub/spudec.h" #include "sub/subreader.h" #include "vobsub.h" #include "eosd.h"
--- a/spudec.c Wed Oct 27 16:32:03 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1393 +0,0 @@ -/* - * Skeleton of function spudec_process_controll() is from xine sources. - * Further works: - * LGB,... (yeah, try to improve it and insert your name here! ;-) - * - * Kim Minh Kaplan - * implement fragments reassembly, RLE decoding. - * read brightness from the IFO. - * - * For information on SPU format see <URL:http://sam.zoy.org/doc/dvd/subtitles/> - * and <URL:http://members.aol.com/mpucoder/DVD/spu.html> - * - * This file is part of MPlayer. - * - * MPlayer 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. - * - * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "mp_msg.h" - -#include <errno.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <math.h> -#include "libvo/sub.h" -#include "libvo/video_out.h" -#include "spudec.h" -#include "vobsub.h" -#include "libavutil/avutil.h" -#include "libavutil/intreadwrite.h" -#include "libswscale/swscale.h" - -/* Valid values for spu_aamode: - 0: none (fastest, most ugly) - 1: approximate - 2: full (slowest) - 3: bilinear (similiar to vobsub, fast and not too bad) - 4: uses swscaler gaussian (this is the only one that looks good) - */ - -int spu_aamode = 3; -int spu_alignment = -1; -float spu_gaussvar = 1.0; - -typedef struct packet_t packet_t; -struct packet_t { - int is_decoded; - unsigned char *packet; - int data_len; - unsigned int palette[4]; - unsigned int alpha[4]; - unsigned int control_start; /* index of start of control data */ - unsigned int current_nibble[2]; /* next data nibble (4 bits) to be - processed (for RLE decoding) for - even and odd lines */ - int deinterlace_oddness; /* 0 or 1, index into current_nibble */ - unsigned int start_col; - unsigned int start_row; - unsigned int width, height, stride; - unsigned int start_pts, end_pts; - packet_t *next; -}; - -struct palette_crop_cache { - int valid; - uint32_t palette; - int sx, sy, ex, ey; - int result; -}; - -typedef struct { - packet_t *queue_head; - packet_t *queue_tail; - unsigned int global_palette[16]; - unsigned int orig_frame_width, orig_frame_height; - unsigned char* packet; - size_t packet_reserve; /* size of the memory pointed to by packet */ - unsigned int packet_offset; /* end of the currently assembled fragment */ - unsigned int packet_size; /* size of the packet once all fragments are assembled */ - int packet_pts; /* PTS for this packet */ - unsigned int palette[4]; - unsigned int alpha[4]; - unsigned int cuspal[4]; - unsigned int custom; - unsigned int now_pts; - unsigned int start_pts, end_pts; - unsigned int start_col; - unsigned int start_row; - unsigned int width, height, stride; - size_t image_size; /* Size of the image buffer */ - unsigned char *image; /* Grayscale value */ - unsigned char *aimage; /* Alpha value */ - unsigned int pal_start_col, pal_start_row; - unsigned int pal_width, pal_height; - unsigned char *pal_image; /* palette entry value */ - unsigned int scaled_frame_width, scaled_frame_height; - unsigned int scaled_start_col, scaled_start_row; - unsigned int scaled_width, scaled_height, scaled_stride; - size_t scaled_image_size; - unsigned char *scaled_image; - unsigned char *scaled_aimage; - int auto_palette; /* 1 if we lack a palette and must use an heuristic. */ - int font_start_level; /* Darkest value used for the computed font */ - const vo_functions_t *hw_spu; - int spu_changed; - unsigned int forced_subs_only; /* flag: 0=display all subtitle, !0 display only forced subtitles */ - unsigned int is_forced_sub; /* true if current subtitle is a forced subtitle */ - - struct palette_crop_cache palette_crop_cache; -} spudec_handle_t; - -static void spudec_queue_packet(spudec_handle_t *this, packet_t *packet) -{ - if (this->queue_head == NULL) - this->queue_head = packet; - else - this->queue_tail->next = packet; - this->queue_tail = packet; -} - -static packet_t *spudec_dequeue_packet(spudec_handle_t *this) -{ - packet_t *retval = this->queue_head; - - this->queue_head = retval->next; - if (this->queue_head == NULL) - this->queue_tail = NULL; - - return retval; -} - -static void spudec_free_packet(packet_t *packet) -{ - if (packet->packet != NULL) - free(packet->packet); - free(packet); -} - -static inline unsigned int get_be16(const unsigned char *p) -{ - return (p[0] << 8) + p[1]; -} - -static inline unsigned int get_be24(const unsigned char *p) -{ - return (get_be16(p) << 8) + p[2]; -} - -static void next_line(packet_t *packet) -{ - if (packet->current_nibble[packet->deinterlace_oddness] % 2) - packet->current_nibble[packet->deinterlace_oddness]++; - packet->deinterlace_oddness = (packet->deinterlace_oddness + 1) % 2; -} - -static inline unsigned char get_nibble(packet_t *packet) -{ - unsigned char nib; - unsigned int *nibblep = packet->current_nibble + packet->deinterlace_oddness; - if (*nibblep / 2 >= packet->control_start) { - mp_msg(MSGT_SPUDEC,MSGL_WARN, "SPUdec: ERROR: get_nibble past end of packet\n"); - return 0; - } - nib = packet->packet[*nibblep / 2]; - if (*nibblep % 2) - nib &= 0xf; - else - nib >>= 4; - ++*nibblep; - return nib; -} - -/* Cut the sub to visible part */ -static inline void spudec_cut_image(spudec_handle_t *this) -{ - unsigned int fy, ly; - unsigned int first_y, last_y; - - if (this->stride == 0 || this->height == 0) { - return; - } - - for (fy = 0; fy < this->image_size && !this->aimage[fy]; fy++); - for (ly = this->stride * this->height-1; ly && !this->aimage[ly]; ly--); - first_y = fy / this->stride; - last_y = ly / this->stride; - //printf("first_y: %d, last_y: %d\n", first_y, last_y); - this->start_row += first_y; - - // Some subtitles trigger this condition - if (last_y + 1 > first_y ) { - this->height = last_y - first_y +1; - } else { - this->height = 0; - return; - } - -// printf("new h %d new start %d (sz %d st %d)---\n\n", this->height, this->start_row, this->image_size, this->stride); - - if (first_y > 0) { - memmove(this->image, this->image + this->stride * first_y, this->stride * this->height); - memmove(this->aimage, this->aimage + this->stride * first_y, this->stride * this->height); - } -} - - -static int spudec_alloc_image(spudec_handle_t *this, int stride, int height) -{ - if (this->width > stride) // just a safeguard - this->width = stride; - this->stride = stride; - this->height = height; - if (this->image_size < this->stride * this->height) { - if (this->image != NULL) { - free(this->image); - free(this->pal_image); - this->image_size = 0; - this->pal_width = this->pal_height = 0; - } - this->image = malloc(2 * this->stride * this->height); - if (this->image) { - this->image_size = this->stride * this->height; - this->aimage = this->image + this->image_size; - // use stride here as well to simplify reallocation checks - this->pal_image = malloc(this->stride * this->height); - } - } - return this->image != NULL; -} - -/** - * \param pal palette in MPlayer-style gray-alpha values, i.e. - * alpha == 0 means transparent, 1 fully opaque, - * gray value <= 256 - alpha. - */ -static void pal2gray_alpha(const uint16_t *pal, - const uint8_t *src, int src_stride, - uint8_t *dst, uint8_t *dsta, - int dst_stride, int w, int h) -{ - int x, y; - for (y = 0; y < h; y++) { - for (x = 0; x < w; x++) { - uint16_t pixel = pal[src[x]]; - *dst++ = pixel; - *dsta++ = pixel >> 8; - } - for (; x < dst_stride; x++) - *dsta++ = *dst++ = 0; - src += src_stride; - } -} - -static int apply_palette_crop(spudec_handle_t *this, - unsigned crop_x, unsigned crop_y, - unsigned crop_w, unsigned crop_h) -{ - int i; - uint8_t *src; - uint16_t pal[4]; - unsigned stride = (crop_w + 7) & ~7; - if (crop_x > this->pal_width || crop_y > this->pal_height || - crop_w > this->pal_width - crop_x || crop_h > this->pal_width - crop_y || - crop_w > 0x8000 || crop_h > 0x8000 || - stride * crop_h > this->image_size) { - return 0; - } - for (i = 0; i < 4; ++i) { - int color; - int alpha = this->alpha[i]; - // extend 4 -> 8 bit - alpha |= alpha << 4; - if (this->custom && (this->cuspal[i] >> 31) != 0) - alpha = 0; - color = this->custom ? this->cuspal[i] : - this->global_palette[this->palette[i]]; - color = (color >> 16) & 0xff; - // convert to MPlayer-style gray/alpha palette - color = FFMIN(color, alpha); - pal[i] = (-alpha << 8) | color; - } - src = this->pal_image + crop_y * this->pal_width + crop_x; - pal2gray_alpha(pal, src, this->pal_width, - this->image, this->aimage, stride, - crop_w, crop_h); - this->width = crop_w; - this->height = crop_h; - this->stride = stride; - this->start_col = this->pal_start_col + crop_x; - this->start_row = this->pal_start_row + crop_y; - spudec_cut_image(this); - - // reset scaled image - this->scaled_frame_width = 0; - this->scaled_frame_height = 0; - this->palette_crop_cache.valid = 0; - return 1; -} - -int spudec_apply_palette_crop(void *this, uint32_t palette, - int sx, int sy, int ex, int ey) -{ - spudec_handle_t *spu = this; - struct palette_crop_cache *c = &spu->palette_crop_cache; - if (c->valid && c->palette == palette && - c->sx == sx && c->sy == sy && c->ex == ex && c->ey == ey) - return c->result; - spu->palette[0] = (palette >> 28) & 0xf; - spu->palette[1] = (palette >> 24) & 0xf; - spu->palette[2] = (palette >> 20) & 0xf; - spu->palette[3] = (palette >> 16) & 0xf; - spu->alpha[0] = (palette >> 12) & 0xf; - spu->alpha[1] = (palette >> 8) & 0xf; - spu->alpha[2] = (palette >> 4) & 0xf; - spu->alpha[3] = palette & 0xf; - spu->spu_changed = 1; - c->result = apply_palette_crop(spu, - sx - spu->pal_start_col, sy - spu->pal_start_row, - ex - sx, ey - sy); - c->palette = palette; - c->sx = sx; c->sy = sy; - c->ex = ex; c->ey = ey; - c->valid = 1; - return c->result; -} - -static void spudec_process_data(spudec_handle_t *this, packet_t *packet) -{ - unsigned int i, x, y; - uint8_t *dst; - - if (!spudec_alloc_image(this, packet->stride, packet->height)) - return; - - this->pal_start_col = packet->start_col; - this->pal_start_row = packet->start_row; - this->pal_height = packet->height; - this->pal_width = packet->width; - this->stride = packet->stride; - memcpy(this->palette, packet->palette, sizeof(this->palette)); - memcpy(this->alpha, packet->alpha, sizeof(this->alpha)); - - i = packet->current_nibble[1]; - x = 0; - y = 0; - dst = this->pal_image; - while (packet->current_nibble[0] < i - && packet->current_nibble[1] / 2 < packet->control_start - && y < this->pal_height) { - unsigned int len, color; - unsigned int rle = 0; - rle = get_nibble(packet); - if (rle < 0x04) { - if (rle == 0) { - rle = (rle << 4) | get_nibble(packet); - if (rle < 0x04) - rle = (rle << 4) | get_nibble(packet); - } - rle = (rle << 4) | get_nibble(packet); - } - color = 3 - (rle & 0x3); - len = rle >> 2; - x += len; - if (len == 0 || x >= this->pal_width) { - len += this->pal_width - x; - next_line(packet); - x = 0; - ++y; - } - memset(dst, color, len); - dst += len; - } - apply_palette_crop(this, 0, 0, this->pal_width, this->pal_height); -} - - -/* - This function tries to create a usable palette. - It determines how many non-transparent colors are used, and assigns different -gray scale values to each color. - I tested it with four streams and even got something readable. Half of the -times I got black characters with white around and half the reverse. -*/ -static void compute_palette(spudec_handle_t *this, packet_t *packet) -{ - int used[16],i,cused,start,step,color; - - memset(used, 0, sizeof(used)); - for (i=0; i<4; i++) - if (packet->alpha[i]) /* !Transparent? */ - used[packet->palette[i]] = 1; - for (cused=0, i=0; i<16; i++) - if (used[i]) cused++; - if (!cused) return; - if (cused == 1) { - start = 0x80; - step = 0; - } else { - start = this->font_start_level; - step = (0xF0-this->font_start_level)/(cused-1); - } - memset(used, 0, sizeof(used)); - for (i=0; i<4; i++) { - color = packet->palette[i]; - if (packet->alpha[i] && !used[color]) { /* not assigned? */ - used[color] = 1; - this->global_palette[color] = start<<16; - start += step; - } - } -} - -static void spudec_process_control(spudec_handle_t *this, int pts100) -{ - int a,b,c,d; /* Temporary vars */ - unsigned int date, type; - unsigned int off; - unsigned int start_off = 0; - unsigned int next_off; - unsigned int start_pts = 0; - unsigned int end_pts = 0; - unsigned int current_nibble[2] = {0, 0}; - unsigned int control_start; - unsigned int display = 0; - unsigned int start_col = 0; - unsigned int end_col = 0; - unsigned int start_row = 0; - unsigned int end_row = 0; - unsigned int width = 0; - unsigned int height = 0; - unsigned int stride = 0; - - control_start = get_be16(this->packet + 2); - next_off = control_start; - while (start_off != next_off) { - start_off = next_off; - date = get_be16(this->packet + start_off) * 1024; - next_off = get_be16(this->packet + start_off + 2); - mp_msg(MSGT_SPUDEC,MSGL_DBG2, "date=%d\n", date); - off = start_off + 4; - for (type = this->packet[off++]; type != 0xff; type = this->packet[off++]) { - mp_msg(MSGT_SPUDEC,MSGL_DBG2, "cmd=%d ",type); - switch(type) { - case 0x00: - /* Menu ID, 1 byte */ - mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Menu ID\n"); - /* shouldn't a Menu ID type force display start? */ - start_pts = pts100 < 0 && -pts100 >= date ? 0 : pts100 + date; - end_pts = UINT_MAX; - display = 1; - this->is_forced_sub=~0; // current subtitle is forced - break; - case 0x01: - /* Start display */ - mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Start display!\n"); - start_pts = pts100 < 0 && -pts100 >= date ? 0 : pts100 + date; - end_pts = UINT_MAX; - display = 1; - this->is_forced_sub=0; - break; - case 0x02: - /* Stop display */ - mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Stop display!\n"); - end_pts = pts100 < 0 && -pts100 >= date ? 0 : pts100 + date; - break; - case 0x03: - /* Palette */ - this->palette[0] = this->packet[off] >> 4; - this->palette[1] = this->packet[off] & 0xf; - this->palette[2] = this->packet[off + 1] >> 4; - this->palette[3] = this->packet[off + 1] & 0xf; - mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Palette %d, %d, %d, %d\n", - this->palette[0], this->palette[1], this->palette[2], this->palette[3]); - off+=2; - break; - case 0x04: - /* Alpha */ - a = this->packet[off] >> 4; - b = this->packet[off] & 0xf; - c = this->packet[off + 1] >> 4; - d = this->packet[off + 1] & 0xf; - // Note: some DVDs change these values to create a fade-in/fade-out effect - // We can not handle this, so just keep the highest value during the display time. - if (display) { - a = FFMAX(a, this->alpha[0]); - b = FFMAX(b, this->alpha[1]); - c = FFMAX(c, this->alpha[2]); - d = FFMAX(d, this->alpha[3]); - } - this->alpha[0] = a; - this->alpha[1] = b; - this->alpha[2] = c; - this->alpha[3] = d; - mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Alpha %d, %d, %d, %d\n", - this->alpha[0], this->alpha[1], this->alpha[2], this->alpha[3]); - off+=2; - break; - case 0x05: - /* Co-ords */ - a = get_be24(this->packet + off); - b = get_be24(this->packet + off + 3); - start_col = a >> 12; - end_col = a & 0xfff; - width = (end_col < start_col) ? 0 : end_col - start_col + 1; - stride = (width + 7) & ~7; /* Kludge: draw_alpha needs width multiple of 8 */ - start_row = b >> 12; - end_row = b & 0xfff; - height = (end_row < start_row) ? 0 : end_row - start_row /* + 1 */; - mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Coords col: %d - %d row: %d - %d (%dx%d)\n", - start_col, end_col, start_row, end_row, - width, height); - off+=6; - break; - case 0x06: - /* Graphic lines */ - current_nibble[0] = 2 * get_be16(this->packet + off); - current_nibble[1] = 2 * get_be16(this->packet + off + 2); - mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Graphic offset 1: %d offset 2: %d\n", - current_nibble[0] / 2, current_nibble[1] / 2); - off+=4; - break; - case 0xff: - /* All done, bye-bye */ - mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Done!\n"); - return; -// break; - default: - mp_msg(MSGT_SPUDEC,MSGL_WARN,"spudec: Error determining control type 0x%02x. Skipping %d bytes.\n", - type, next_off - off); - goto next_control; - } - } - next_control: - if (!display) - continue; - if (end_pts == UINT_MAX && start_off != next_off) { - end_pts = get_be16(this->packet + next_off) * 1024; - end_pts = 1 - pts100 >= end_pts ? 0 : pts100 + end_pts - 1; - } - if (end_pts > 0) { - packet_t *packet = calloc(1, sizeof(packet_t)); - int i; - packet->start_pts = start_pts; - packet->end_pts = end_pts; - packet->current_nibble[0] = current_nibble[0]; - packet->current_nibble[1] = current_nibble[1]; - packet->start_row = start_row; - packet->start_col = start_col; - packet->width = width; - packet->height = height; - packet->stride = stride; - packet->control_start = control_start; - for (i=0; i<4; i++) { - packet->alpha[i] = this->alpha[i]; - packet->palette[i] = this->palette[i]; - } - packet->packet = malloc(this->packet_size); - memcpy(packet->packet, this->packet, this->packet_size); - spudec_queue_packet(this, packet); - } - } -} - -static void spudec_decode(spudec_handle_t *this, int pts100) -{ - if (!this->hw_spu) - spudec_process_control(this, pts100); - else if (pts100 >= 0) { - static vo_mpegpes_t packet = { NULL, 0, 0x20, 0 }; - static vo_mpegpes_t *pkg=&packet; - packet.data = this->packet; - packet.size = this->packet_size; - packet.timestamp = pts100; - this->hw_spu->draw_frame((uint8_t**)&pkg); - } -} - -int spudec_changed(void * this) -{ - spudec_handle_t * spu = this; - return spu->spu_changed || spu->now_pts > spu->end_pts; -} - -void spudec_assemble(void *this, unsigned char *packet, unsigned int len, int pts100) -{ - spudec_handle_t *spu = this; -// spudec_heartbeat(this, pts100); - if (len < 2) { - mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: packet too short\n"); - return; - } - spu->packet_pts = pts100; - if (spu->packet_offset == 0) { - unsigned int len2 = get_be16(packet); - // Start new fragment - if (spu->packet_reserve < len2) { - if (spu->packet != NULL) - free(spu->packet); - spu->packet = malloc(len2); - spu->packet_reserve = spu->packet != NULL ? len2 : 0; - } - if (spu->packet != NULL) { - spu->packet_size = len2; - if (len > len2) { - mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: invalid frag len / len2: %d / %d \n", len, len2); - return; - } - memcpy(spu->packet, packet, len); - spu->packet_offset = len; - spu->packet_pts = pts100; - } - } else { - // Continue current fragment - if (spu->packet_size < spu->packet_offset + len){ - mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: invalid fragment\n"); - spu->packet_size = spu->packet_offset = 0; - return; - } else { - memcpy(spu->packet + spu->packet_offset, packet, len); - spu->packet_offset += len; - } - } -#if 1 - // check if we have a complete packet (unfortunatelly packet_size is bad - // for some disks) - // [cb] packet_size is padded to be even -> may be one byte too long - if ((spu->packet_offset == spu->packet_size) || - ((spu->packet_offset + 1) == spu->packet_size)){ - unsigned int x=0,y; - while(x+4<=spu->packet_offset){ - y=get_be16(spu->packet+x+2); // next control pointer - mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUtest: x=%d y=%d off=%d size=%d\n",x,y,spu->packet_offset,spu->packet_size); - if(x>=4 && x==y){ // if it points to self - we're done! - // we got it! - mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUgot: off=%d size=%d \n",spu->packet_offset,spu->packet_size); - spudec_decode(spu, pts100); - spu->packet_offset = 0; - break; - } - if(y<=x || y>=spu->packet_size){ // invalid? - mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUtest: broken packet!!!!! y=%d < x=%d\n",y,x); - spu->packet_size = spu->packet_offset = 0; - break; - } - x=y; - } - // [cb] packet is done; start new packet - spu->packet_offset = 0; - } -#else - if (spu->packet_offset == spu->packet_size) { - spudec_decode(spu, pts100); - spu->packet_offset = 0; - } -#endif -} - -void spudec_reset(void *this) // called after seek -{ - spudec_handle_t *spu = this; - while (spu->queue_head) - spudec_free_packet(spudec_dequeue_packet(spu)); - spu->now_pts = 0; - spu->end_pts = 0; - spu->packet_size = spu->packet_offset = 0; -} - -void spudec_heartbeat(void *this, unsigned int pts100) -{ - spudec_handle_t *spu = this; - spu->now_pts = pts100; - - // TODO: detect and handle broken timestamps (e.g. due to wrapping) - while (spu->queue_head != NULL && pts100 >= spu->queue_head->start_pts) { - packet_t *packet = spudec_dequeue_packet(spu); - spu->start_pts = packet->start_pts; - spu->end_pts = packet->end_pts; - if (packet->is_decoded) { - free(spu->image); - spu->image_size = packet->data_len; - spu->image = packet->packet; - spu->aimage = packet->packet + packet->stride * packet->height; - packet->packet = NULL; - spu->width = packet->width; - spu->height = packet->height; - spu->stride = packet->stride; - spu->start_col = packet->start_col; - spu->start_row = packet->start_row; - - // reset scaled image - spu->scaled_frame_width = 0; - spu->scaled_frame_height = 0; - } else { - if (spu->auto_palette) - compute_palette(spu, packet); - spudec_process_data(spu, packet); - } - spudec_free_packet(packet); - spu->spu_changed = 1; - } -} - -int spudec_visible(void *this){ - spudec_handle_t *spu = this; - int ret=(spu->start_pts <= spu->now_pts && - spu->now_pts < spu->end_pts && - spu->height > 0); -// printf("spu visible: %d \n",ret); - return ret; -} - -void spudec_set_forced_subs_only(void * const this, const unsigned int flag) -{ - if(this){ - ((spudec_handle_t *)this)->forced_subs_only=flag; - mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPU: Display only forced subs now %s\n", flag ? "enabled": "disabled"); - } -} - -void spudec_draw(void *this, void (*draw_alpha)(int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride)) -{ - spudec_handle_t *spu = this; - if (spudec_visible(spu)) - { - draw_alpha(spu->start_col, spu->start_row, spu->width, spu->height, - spu->image, spu->aimage, spu->stride); - spu->spu_changed = 0; - } -} - -/* calc the bbox for spudec subs */ -void spudec_calc_bbox(void *me, unsigned int dxs, unsigned int dys, unsigned int* bbox) -{ - spudec_handle_t *spu = me; - if (spu->orig_frame_width == 0 || spu->orig_frame_height == 0 - || (spu->orig_frame_width == dxs && spu->orig_frame_height == dys)) { - // unscaled - bbox[0] = spu->start_col; - bbox[1] = spu->start_col + spu->width; - bbox[2] = spu->start_row; - bbox[3] = spu->start_row + spu->height; - } - else { - // scaled - unsigned int scalex = 0x100 * dxs / spu->orig_frame_width; - unsigned int scaley = 0x100 * dys / spu->orig_frame_height; - bbox[0] = spu->start_col * scalex / 0x100; - bbox[1] = spu->start_col * scalex / 0x100 + spu->width * scalex / 0x100; - switch (spu_alignment) { - case 0: - bbox[3] = dys*sub_pos/100 + spu->height * scaley / 0x100; - if (bbox[3] > dys) bbox[3] = dys; - bbox[2] = bbox[3] - spu->height * scaley / 0x100; - break; - case 1: - if (sub_pos < 50) { - bbox[2] = dys*sub_pos/100 - spu->height * scaley / 0x200; - bbox[3] = bbox[2] + spu->height; - } else { - bbox[3] = dys*sub_pos/100 + spu->height * scaley / 0x200; - if (bbox[3] > dys) bbox[3] = dys; - bbox[2] = bbox[3] - spu->height * scaley / 0x100; - } - break; - case 2: - bbox[2] = dys*sub_pos/100 - spu->height * scaley / 0x100; - bbox[3] = bbox[2] + spu->height; - break; - default: /* -1 */ - bbox[2] = spu->start_row * scaley / 0x100; - bbox[3] = spu->start_row * scaley / 0x100 + spu->height * scaley / 0x100; - break; - } - } -} -/* transform mplayer's alpha value into an opacity value that is linear */ -static inline int canon_alpha(int alpha) -{ - return (uint8_t)-alpha; -} - -typedef struct { - unsigned position; - unsigned left_up; - unsigned right_down; -}scale_pixel; - - -static void scale_table(unsigned int start_src, unsigned int start_tar, unsigned int end_src, unsigned int end_tar, scale_pixel * table) -{ - unsigned int t; - unsigned int delta_src = end_src - start_src; - unsigned int delta_tar = end_tar - start_tar; - int src = 0; - int src_step; - if (delta_src == 0 || delta_tar == 0) { - return; - } - src_step = (delta_src << 16) / delta_tar >>1; - for (t = 0; t<=delta_tar; src += (src_step << 1), t++){ - table[t].position= FFMIN(src >> 16, end_src - 1); - table[t].right_down = src & 0xffff; - table[t].left_up = 0x10000 - table[t].right_down; - } -} - -/* bilinear scale, similar to vobsub's code */ -static void scale_image(int x, int y, scale_pixel* table_x, scale_pixel* table_y, spudec_handle_t * spu) -{ - int alpha[4]; - int color[4]; - unsigned int scale[4]; - int base = table_y[y].position * spu->stride + table_x[x].position; - int scaled = y * spu->scaled_stride + x; - alpha[0] = canon_alpha(spu->aimage[base]); - alpha[1] = canon_alpha(spu->aimage[base + 1]); - alpha[2] = canon_alpha(spu->aimage[base + spu->stride]); - alpha[3] = canon_alpha(spu->aimage[base + spu->stride + 1]); - color[0] = spu->image[base]; - color[1] = spu->image[base + 1]; - color[2] = spu->image[base + spu->stride]; - color[3] = spu->image[base + spu->stride + 1]; - scale[0] = (table_x[x].left_up * table_y[y].left_up >> 16) * alpha[0]; - if (table_y[y].left_up == 0x10000) // necessary to avoid overflow-case - scale[0] = table_x[x].left_up * alpha[0]; - scale[1] = (table_x[x].right_down * table_y[y].left_up >>16) * alpha[1]; - scale[2] = (table_x[x].left_up * table_y[y].right_down >> 16) * alpha[2]; - scale[3] = (table_x[x].right_down * table_y[y].right_down >> 16) * alpha[3]; - spu->scaled_image[scaled] = (color[0] * scale[0] + color[1] * scale[1] + color[2] * scale[2] + color[3] * scale[3])>>24; - spu->scaled_aimage[scaled] = (scale[0] + scale[1] + scale[2] + scale[3]) >> 16; - if (spu->scaled_aimage[scaled]){ - // ensure that MPlayer's simplified alpha-blending can not overflow - spu->scaled_image[scaled] = FFMIN(spu->scaled_image[scaled], spu->scaled_aimage[scaled]); - // convert to MPlayer-style alpha - spu->scaled_aimage[scaled] = -spu->scaled_aimage[scaled]; - } -} - -static void sws_spu_image(unsigned char *d1, unsigned char *d2, int dw, int dh, - int ds, const unsigned char* s1, unsigned char* s2, - int sw, int sh, int ss) -{ - struct SwsContext *ctx; - static SwsFilter filter; - static int firsttime = 1; - static float oldvar; - int i; - - if (!firsttime && oldvar != spu_gaussvar) sws_freeVec(filter.lumH); - if (firsttime) { - filter.lumH = filter.lumV = - filter.chrH = filter.chrV = sws_getGaussianVec(spu_gaussvar, 3.0); - sws_normalizeVec(filter.lumH, 1.0); - firsttime = 0; - oldvar = spu_gaussvar; - } - - ctx=sws_getContext(sw, sh, PIX_FMT_GRAY8, dw, dh, PIX_FMT_GRAY8, SWS_GAUSS, &filter, NULL, NULL); - sws_scale(ctx,&s1,&ss,0,sh,&d1,&ds); - for (i=ss*sh-1; i>=0; i--) if (!s2[i]) s2[i] = 255; //else s2[i] = 1; - sws_scale(ctx,&s2,&ss,0,sh,&d2,&ds); - for (i=ds*dh-1; i>=0; i--) if (d2[i]==0) d2[i] = 1; else if (d2[i]==255) d2[i] = 0; - sws_freeContext(ctx); -} - -void spudec_draw_scaled(void *me, unsigned int dxs, unsigned int dys, void (*draw_alpha)(int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride)) -{ - spudec_handle_t *spu = me; - scale_pixel *table_x; - scale_pixel *table_y; - - if (spudec_visible(spu)) { - - // check if only forced subtitles are requested - if( (spu->forced_subs_only) && !(spu->is_forced_sub) ){ - return; - } - - if (!(spu_aamode&16) && (spu->orig_frame_width == 0 || spu->orig_frame_height == 0 - || (spu->orig_frame_width == dxs && spu->orig_frame_height == dys))) { - spudec_draw(spu, draw_alpha); - } - else { - if (spu->scaled_frame_width != dxs || spu->scaled_frame_height != dys) { /* Resizing is needed */ - /* scaled_x = scalex * x / 0x100 - scaled_y = scaley * y / 0x100 - order of operations is important because of rounding. */ - unsigned int scalex = 0x100 * dxs / spu->orig_frame_width; - unsigned int scaley = 0x100 * dys / spu->orig_frame_height; - spu->scaled_start_col = spu->start_col * scalex / 0x100; - spu->scaled_start_row = spu->start_row * scaley / 0x100; - spu->scaled_width = spu->width * scalex / 0x100; - spu->scaled_height = spu->height * scaley / 0x100; - /* Kludge: draw_alpha needs width multiple of 8 */ - spu->scaled_stride = (spu->scaled_width + 7) & ~7; - if (spu->scaled_image_size < spu->scaled_stride * spu->scaled_height) { - if (spu->scaled_image) { - free(spu->scaled_image); - spu->scaled_image_size = 0; - } - spu->scaled_image = malloc(2 * spu->scaled_stride * spu->scaled_height); - if (spu->scaled_image) { - spu->scaled_image_size = spu->scaled_stride * spu->scaled_height; - spu->scaled_aimage = spu->scaled_image + spu->scaled_image_size; - } - } - if (spu->scaled_image) { - unsigned int x, y; - if (spu->scaled_width <= 1 || spu->scaled_height <= 1) { - goto nothing_to_do; - } - switch(spu_aamode&15) { - case 4: - sws_spu_image(spu->scaled_image, spu->scaled_aimage, - spu->scaled_width, spu->scaled_height, spu->scaled_stride, - spu->image, spu->aimage, spu->width, spu->height, spu->stride); - break; - case 3: - table_x = calloc(spu->scaled_width, sizeof(scale_pixel)); - table_y = calloc(spu->scaled_height, sizeof(scale_pixel)); - if (!table_x || !table_y) { - mp_msg(MSGT_SPUDEC, MSGL_FATAL, "Fatal: spudec_draw_scaled: calloc failed\n"); - } - scale_table(0, 0, spu->width - 1, spu->scaled_width - 1, table_x); - scale_table(0, 0, spu->height - 1, spu->scaled_height - 1, table_y); - for (y = 0; y < spu->scaled_height; y++) - for (x = 0; x < spu->scaled_width; x++) - scale_image(x, y, table_x, table_y, spu); - free(table_x); - free(table_y); - break; - case 0: - /* no antialiasing */ - for (y = 0; y < spu->scaled_height; ++y) { - int unscaled_y = y * 0x100 / scaley; - int strides = spu->stride * unscaled_y; - int scaled_strides = spu->scaled_stride * y; - for (x = 0; x < spu->scaled_width; ++x) { - int unscaled_x = x * 0x100 / scalex; - spu->scaled_image[scaled_strides + x] = spu->image[strides + unscaled_x]; - spu->scaled_aimage[scaled_strides + x] = spu->aimage[strides + unscaled_x]; - } - } - break; - case 1: - { - /* Intermediate antialiasing. */ - for (y = 0; y < spu->scaled_height; ++y) { - const unsigned int unscaled_top = y * spu->orig_frame_height / dys; - unsigned int unscaled_bottom = (y + 1) * spu->orig_frame_height / dys; - if (unscaled_bottom >= spu->height) - unscaled_bottom = spu->height - 1; - for (x = 0; x < spu->scaled_width; ++x) { - const unsigned int unscaled_left = x * spu->orig_frame_width / dxs; - unsigned int unscaled_right = (x + 1) * spu->orig_frame_width / dxs; - unsigned int color = 0; - unsigned int alpha = 0; - unsigned int walkx, walky; - unsigned int base, tmp; - if (unscaled_right >= spu->width) - unscaled_right = spu->width - 1; - for (walky = unscaled_top; walky <= unscaled_bottom; ++walky) - for (walkx = unscaled_left; walkx <= unscaled_right; ++walkx) { - base = walky * spu->stride + walkx; - tmp = canon_alpha(spu->aimage[base]); - alpha += tmp; - color += tmp * spu->image[base]; - } - base = y * spu->scaled_stride + x; - spu->scaled_image[base] = alpha ? color / alpha : 0; - spu->scaled_aimage[base] = - alpha * (1 + unscaled_bottom - unscaled_top) * (1 + unscaled_right - unscaled_left); - /* spu->scaled_aimage[base] = - alpha * dxs * dys / spu->orig_frame_width / spu->orig_frame_height; */ - if (spu->scaled_aimage[base]) { - spu->scaled_aimage[base] = 256 - spu->scaled_aimage[base]; - if (spu->scaled_aimage[base] + spu->scaled_image[base] > 255) - spu->scaled_image[base] = 256 - spu->scaled_aimage[base]; - } - } - } - } - break; - case 2: - { - /* Best antialiasing. Very slow. */ - /* Any pixel (x, y) represents pixels from the original - rectangular region comprised between the columns - unscaled_y and unscaled_y + 0x100 / scaley and the rows - unscaled_x and unscaled_x + 0x100 / scalex - - The original rectangular region that the scaled pixel - represents is cut in 9 rectangular areas like this: - - +---+-----------------+---+ - | 1 | 2 | 3 | - +---+-----------------+---+ - | | | | - | 4 | 5 | 6 | - | | | | - +---+-----------------+---+ - | 7 | 8 | 9 | - +---+-----------------+---+ - - The width of the left column is at most one pixel and - it is never null and its right column is at a pixel - boundary. The height of the top row is at most one - pixel it is never null and its bottom row is at a - pixel boundary. The width and height of region 5 are - integral values. The width of the right column is - what remains and is less than one pixel. The height - of the bottom row is what remains and is less than - one pixel. - - The row above 1, 2, 3 is unscaled_y. The row between - 1, 2, 3 and 4, 5, 6 is top_low_row. The row between 4, - 5, 6 and 7, 8, 9 is (unsigned int)unscaled_y_bottom. - The row beneath 7, 8, 9 is unscaled_y_bottom. - - The column left of 1, 4, 7 is unscaled_x. The column - between 1, 4, 7 and 2, 5, 8 is left_right_column. The - column between 2, 5, 8 and 3, 6, 9 is (unsigned - int)unscaled_x_right. The column right of 3, 6, 9 is - unscaled_x_right. */ - const double inv_scalex = (double) 0x100 / scalex; - const double inv_scaley = (double) 0x100 / scaley; - for (y = 0; y < spu->scaled_height; ++y) { - const double unscaled_y = y * inv_scaley; - const double unscaled_y_bottom = unscaled_y + inv_scaley; - const unsigned int top_low_row = FFMIN(unscaled_y_bottom, unscaled_y + 1.0); - const double top = top_low_row - unscaled_y; - const unsigned int height = unscaled_y_bottom > top_low_row - ? (unsigned int) unscaled_y_bottom - top_low_row - : 0; - const double bottom = unscaled_y_bottom > top_low_row - ? unscaled_y_bottom - floor(unscaled_y_bottom) - : 0.0; - for (x = 0; x < spu->scaled_width; ++x) { - const double unscaled_x = x * inv_scalex; - const double unscaled_x_right = unscaled_x + inv_scalex; - const unsigned int left_right_column = FFMIN(unscaled_x_right, unscaled_x + 1.0); - const double left = left_right_column - unscaled_x; - const unsigned int width = unscaled_x_right > left_right_column - ? (unsigned int) unscaled_x_right - left_right_column - : 0; - const double right = unscaled_x_right > left_right_column - ? unscaled_x_right - floor(unscaled_x_right) - : 0.0; - double color = 0.0; - double alpha = 0.0; - double tmp; - unsigned int base; - /* Now use these informations to compute a good alpha, - and lightness. The sum is on each of the 9 - region's surface and alpha and lightness. - - transformed alpha = sum(surface * alpha) / sum(surface) - transformed color = sum(surface * alpha * color) / sum(surface * alpha) - */ - /* 1: top left part */ - base = spu->stride * (unsigned int) unscaled_y; - tmp = left * top * canon_alpha(spu->aimage[base + (unsigned int) unscaled_x]); - alpha += tmp; - color += tmp * spu->image[base + (unsigned int) unscaled_x]; - /* 2: top center part */ - if (width > 0) { - unsigned int walkx; - for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) { - base = spu->stride * (unsigned int) unscaled_y + walkx; - tmp = /* 1.0 * */ top * canon_alpha(spu->aimage[base]); - alpha += tmp; - color += tmp * spu->image[base]; - } - } - /* 3: top right part */ - if (right > 0.0) { - base = spu->stride * (unsigned int) unscaled_y + (unsigned int) unscaled_x_right; - tmp = right * top * canon_alpha(spu->aimage[base]); - alpha += tmp; - color += tmp * spu->image[base]; - } - /* 4: center left part */ - if (height > 0) { - unsigned int walky; - for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) { - base = spu->stride * walky + (unsigned int) unscaled_x; - tmp = left /* * 1.0 */ * canon_alpha(spu->aimage[base]); - alpha += tmp; - color += tmp * spu->image[base]; - } - } - /* 5: center part */ - if (width > 0 && height > 0) { - unsigned int walky; - for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) { - unsigned int walkx; - base = spu->stride * walky; - for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) { - tmp = /* 1.0 * 1.0 * */ canon_alpha(spu->aimage[base + walkx]); - alpha += tmp; - color += tmp * spu->image[base + walkx]; - } - } - } - /* 6: center right part */ - if (right > 0.0 && height > 0) { - unsigned int walky; - for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) { - base = spu->stride * walky + (unsigned int) unscaled_x_right; - tmp = right /* * 1.0 */ * canon_alpha(spu->aimage[base]); - alpha += tmp; - color += tmp * spu->image[base]; - } - } - /* 7: bottom left part */ - if (bottom > 0.0) { - base = spu->stride * (unsigned int) unscaled_y_bottom + (unsigned int) unscaled_x; - tmp = left * bottom * canon_alpha(spu->aimage[base]); - alpha += tmp; - color += tmp * spu->image[base]; - } - /* 8: bottom center part */ - if (width > 0 && bottom > 0.0) { - unsigned int walkx; - base = spu->stride * (unsigned int) unscaled_y_bottom; - for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) { - tmp = /* 1.0 * */ bottom * canon_alpha(spu->aimage[base + walkx]); - alpha += tmp; - color += tmp * spu->image[base + walkx]; - } - } - /* 9: bottom right part */ - if (right > 0.0 && bottom > 0.0) { - base = spu->stride * (unsigned int) unscaled_y_bottom + (unsigned int) unscaled_x_right; - tmp = right * bottom * canon_alpha(spu->aimage[base]); - alpha += tmp; - color += tmp * spu->image[base]; - } - /* Finally mix these transparency and brightness information suitably */ - base = spu->scaled_stride * y + x; - spu->scaled_image[base] = alpha > 0 ? color / alpha : 0; - spu->scaled_aimage[base] = alpha * scalex * scaley / 0x10000; - if (spu->scaled_aimage[base]) { - spu->scaled_aimage[base] = 256 - spu->scaled_aimage[base]; - if (spu->scaled_aimage[base] + spu->scaled_image[base] > 255) - spu->scaled_image[base] = 256 - spu->scaled_aimage[base]; - } - } - } - } - } -nothing_to_do: - /* Kludge: draw_alpha needs width multiple of 8. */ - if (spu->scaled_width < spu->scaled_stride) - for (y = 0; y < spu->scaled_height; ++y) { - memset(spu->scaled_aimage + y * spu->scaled_stride + spu->scaled_width, 0, - spu->scaled_stride - spu->scaled_width); - } - spu->scaled_frame_width = dxs; - spu->scaled_frame_height = dys; - } - } - if (spu->scaled_image){ - switch (spu_alignment) { - case 0: - spu->scaled_start_row = dys*sub_pos/100; - if (spu->scaled_start_row + spu->scaled_height > dys) - spu->scaled_start_row = dys - spu->scaled_height; - break; - case 1: - spu->scaled_start_row = dys*sub_pos/100 - spu->scaled_height/2; - if (sub_pos >= 50 && spu->scaled_start_row + spu->scaled_height > dys) - spu->scaled_start_row = dys - spu->scaled_height; - break; - case 2: - spu->scaled_start_row = dys*sub_pos/100 - spu->scaled_height; - break; - } - draw_alpha(spu->scaled_start_col, spu->scaled_start_row, spu->scaled_width, spu->scaled_height, - spu->scaled_image, spu->scaled_aimage, spu->scaled_stride); - spu->spu_changed = 0; - } - } - } - else - { - mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPU not displayed: start_pts=%d end_pts=%d now_pts=%d\n", - spu->start_pts, spu->end_pts, spu->now_pts); - } -} - -void spudec_update_palette(void * this, unsigned int *palette) -{ - spudec_handle_t *spu = this; - if (spu && palette) { - memcpy(spu->global_palette, palette, sizeof(spu->global_palette)); - if(spu->hw_spu) - spu->hw_spu->control(VOCTRL_SET_SPU_PALETTE,spu->global_palette); - } -} - -void spudec_set_font_factor(void * this, double factor) -{ - spudec_handle_t *spu = this; - spu->font_start_level = (int)(0xF0-(0xE0*factor)); -} - -static void spudec_parse_extradata(spudec_handle_t *this, - uint8_t *extradata, int extradata_len) -{ - uint8_t *buffer, *ptr; - unsigned int *pal = this->global_palette, *cuspal = this->cuspal; - unsigned int tridx; - int i; - - if (extradata_len == 16*4) { - for (i=0; i<16; i++) - pal[i] = AV_RB32(extradata + i*4); - this->auto_palette = 0; - return; - } - - if (!(ptr = buffer = malloc(extradata_len+1))) - return; - memcpy(buffer, extradata, extradata_len); - buffer[extradata_len] = 0; - - do { - if (*ptr == '#') - continue; - if (!strncmp(ptr, "size: ", 6)) - sscanf(ptr + 6, "%dx%d", &this->orig_frame_width, &this->orig_frame_height); - if (!strncmp(ptr, "palette: ", 9) && - sscanf(ptr + 9, "%x, %x, %x, %x, %x, %x, %x, %x, " - "%x, %x, %x, %x, %x, %x, %x, %x", - &pal[ 0], &pal[ 1], &pal[ 2], &pal[ 3], - &pal[ 4], &pal[ 5], &pal[ 6], &pal[ 7], - &pal[ 8], &pal[ 9], &pal[10], &pal[11], - &pal[12], &pal[13], &pal[14], &pal[15]) == 16) { - for (i=0; i<16; i++) - pal[i] = vobsub_palette_to_yuv(pal[i]); - this->auto_palette = 0; - } - if (!strncasecmp(ptr, "forced subs: on", 15)) - this->forced_subs_only = 1; - if (!strncmp(ptr, "custom colors: ON, tridx: ", 26) && - sscanf(ptr + 26, "%x, colors: %x, %x, %x, %x", - &tridx, cuspal+0, cuspal+1, cuspal+2, cuspal+3) == 5) { - for (i=0; i<4; i++) { - cuspal[i] = vobsub_rgb_to_yuv(cuspal[i]); - if (tridx & (1 << (12-4*i))) - cuspal[i] |= 1 << 31; - } - this->custom = 1; - } - } while ((ptr=strchr(ptr,'\n')) && *++ptr); - - free(buffer); -} - -void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len) -{ - spudec_handle_t *this = calloc(1, sizeof(spudec_handle_t)); - if (this){ - this->orig_frame_height = frame_height; - this->orig_frame_width = frame_width; - // set up palette: - if (palette) - memcpy(this->global_palette, palette, sizeof(this->global_palette)); - else - this->auto_palette = 1; - if (extradata) - spudec_parse_extradata(this, extradata, extradata_len); - /* XXX Although the video frame is some size, the SPU frame is - always maximum size i.e. 720 wide and 576 or 480 high */ - // For HD files in MKV the VobSub resolution can be higher though, - // see largeres_vobsub.mkv - if (this->orig_frame_width <= 720 && this->orig_frame_height <= 576) { - this->orig_frame_width = 720; - if (this->orig_frame_height == 480 || this->orig_frame_height == 240) - this->orig_frame_height = 480; - else - this->orig_frame_height = 576; - } - } - else - mp_msg(MSGT_SPUDEC,MSGL_FATAL, "FATAL: spudec_init: calloc"); - return this; -} - -void *spudec_new(unsigned int *palette) -{ - return spudec_new_scaled(palette, 0, 0, NULL, 0); -} - -void spudec_free(void *this) -{ - spudec_handle_t *spu = this; - if (spu) { - while (spu->queue_head) - spudec_free_packet(spudec_dequeue_packet(spu)); - free(spu->packet); - spu->packet = NULL; - free(spu->scaled_image); - spu->scaled_image = NULL; - free(spu->image); - spu->image = NULL; - spu->aimage = NULL; - free(spu->pal_image); - spu->pal_image = NULL; - spu->image_size = 0; - spu->pal_width = spu->pal_height = 0; - free(spu); - } -} - -void spudec_set_hw_spu(void *this, const vo_functions_t *hw_spu) -{ - spudec_handle_t *spu = this; - if (!spu) - return; - spu->hw_spu = hw_spu; - hw_spu->control(VOCTRL_SET_SPU_PALETTE,spu->global_palette); -} - -#define MP_NOPTS_VALUE (-1LL<<63) //both int64_t and double should be able to represent this exactly - -/** - * palette must contain at least 256 32-bit entries, otherwise crashes - * are possible - */ -void spudec_set_paletted(void *this, const uint8_t *pal_img, int pal_stride, - const void *palette, - int x, int y, int w, int h, - double pts, double endpts) -{ - int i; - uint16_t g8a8_pal[256]; - packet_t *packet; - const uint32_t *pal = palette; - spudec_handle_t *spu = this; - uint8_t *img; - uint8_t *aimg; - int stride = (w + 7) & ~7; - if ((unsigned)w >= 0x8000 || (unsigned)h > 0x4000) - return; - packet = calloc(1, sizeof(packet_t)); - packet->is_decoded = 1; - packet->width = w; - packet->height = h; - packet->stride = stride; - packet->start_col = x; - packet->start_row = y; - packet->data_len = 2 * stride * h; - if (packet->data_len) { // size 0 is a special "clear" packet - packet->packet = malloc(packet->data_len); - img = packet->packet; - aimg = packet->packet + stride * h; - for (i = 0; i < 256; i++) { - uint32_t pixel = pal[i]; - int alpha = pixel >> 24; - int gray = (((pixel & 0x000000ff) >> 0) + - ((pixel & 0x0000ff00) >> 7) + - ((pixel & 0x00ff0000) >> 16)) >> 2; - gray = FFMIN(gray, alpha); - g8a8_pal[i] = (-alpha << 8) | gray; - } - pal2gray_alpha(g8a8_pal, pal_img, pal_stride, - img, aimg, stride, w, h); - } - packet->start_pts = 0; - packet->end_pts = 0x7fffffff; - if (pts != MP_NOPTS_VALUE) - packet->start_pts = pts * 90000; - if (endpts != MP_NOPTS_VALUE) - packet->end_pts = endpts * 90000; - spudec_queue_packet(spu, packet); -}
--- a/spudec.h Wed Oct 27 16:32:03 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -/* - * This file is part of MPlayer. - * - * MPlayer 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. - * - * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPLAYER_SPUDEC_H -#define MPLAYER_SPUDEC_H - -#include "libvo/video_out.h" - -void spudec_heartbeat(void *this, unsigned int pts100); -void spudec_assemble(void *this, unsigned char *packet, unsigned int len, int pts100); -void spudec_draw(void *this, void (*draw_alpha)(int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride)); -void spudec_draw_scaled(void *this, unsigned int dxs, unsigned int dys, void (*draw_alpha)(int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride)); -int spudec_apply_palette_crop(void *this, uint32_t palette, int sx, int ex, int sy, int ey); -void spudec_update_palette(void *this, unsigned int *palette); -void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len); -void *spudec_new(unsigned int *palette); -void spudec_free(void *this); -void spudec_reset(void *this); // called after seek -int spudec_visible(void *this); // check if spu is visible -void spudec_set_font_factor(void * this, double factor); // sets the equivalent to ffactor -void spudec_set_hw_spu(void *this, const vo_functions_t *hw_spu); -int spudec_changed(void *this); -void spudec_calc_bbox(void *me, unsigned int dxs, unsigned int dys, unsigned int* bbox); -void spudec_set_forced_subs_only(void * const this, const unsigned int flag); -void spudec_set_paletted(void *this, const uint8_t *pal_img, int stride, - const void *palette, - int x, int y, int w, int h, - double pts, double endpts); - -#endif /* MPLAYER_SPUDEC_H */
--- a/stream/stream_dvdnav.c Wed Oct 27 16:32:03 2010 +0000 +++ b/stream/stream_dvdnav.c Wed Oct 27 16:38:51 2010 +0000 @@ -32,7 +32,7 @@ #include "stream_dvdnav.h" #include "libvo/video_out.h" #include "libavutil/common.h" -#include "spudec.h" +#include "sub/spudec.h" #include "m_option.h" #include "m_struct.h" #include "help_mp.h"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sub/spudec.c Wed Oct 27 16:38:51 2010 +0000 @@ -0,0 +1,1393 @@ +/* + * Skeleton of function spudec_process_controll() is from xine sources. + * Further works: + * LGB,... (yeah, try to improve it and insert your name here! ;-) + * + * Kim Minh Kaplan + * implement fragments reassembly, RLE decoding. + * read brightness from the IFO. + * + * For information on SPU format see <URL:http://sam.zoy.org/doc/dvd/subtitles/> + * and <URL:http://members.aol.com/mpucoder/DVD/spu.html> + * + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "mp_msg.h" + +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <math.h> +#include "libvo/sub.h" +#include "libvo/video_out.h" +#include "sub/spudec.h" +#include "vobsub.h" +#include "libavutil/avutil.h" +#include "libavutil/intreadwrite.h" +#include "libswscale/swscale.h" + +/* Valid values for spu_aamode: + 0: none (fastest, most ugly) + 1: approximate + 2: full (slowest) + 3: bilinear (similiar to vobsub, fast and not too bad) + 4: uses swscaler gaussian (this is the only one that looks good) + */ + +int spu_aamode = 3; +int spu_alignment = -1; +float spu_gaussvar = 1.0; + +typedef struct packet_t packet_t; +struct packet_t { + int is_decoded; + unsigned char *packet; + int data_len; + unsigned int palette[4]; + unsigned int alpha[4]; + unsigned int control_start; /* index of start of control data */ + unsigned int current_nibble[2]; /* next data nibble (4 bits) to be + processed (for RLE decoding) for + even and odd lines */ + int deinterlace_oddness; /* 0 or 1, index into current_nibble */ + unsigned int start_col; + unsigned int start_row; + unsigned int width, height, stride; + unsigned int start_pts, end_pts; + packet_t *next; +}; + +struct palette_crop_cache { + int valid; + uint32_t palette; + int sx, sy, ex, ey; + int result; +}; + +typedef struct { + packet_t *queue_head; + packet_t *queue_tail; + unsigned int global_palette[16]; + unsigned int orig_frame_width, orig_frame_height; + unsigned char* packet; + size_t packet_reserve; /* size of the memory pointed to by packet */ + unsigned int packet_offset; /* end of the currently assembled fragment */ + unsigned int packet_size; /* size of the packet once all fragments are assembled */ + int packet_pts; /* PTS for this packet */ + unsigned int palette[4]; + unsigned int alpha[4]; + unsigned int cuspal[4]; + unsigned int custom; + unsigned int now_pts; + unsigned int start_pts, end_pts; + unsigned int start_col; + unsigned int start_row; + unsigned int width, height, stride; + size_t image_size; /* Size of the image buffer */ + unsigned char *image; /* Grayscale value */ + unsigned char *aimage; /* Alpha value */ + unsigned int pal_start_col, pal_start_row; + unsigned int pal_width, pal_height; + unsigned char *pal_image; /* palette entry value */ + unsigned int scaled_frame_width, scaled_frame_height; + unsigned int scaled_start_col, scaled_start_row; + unsigned int scaled_width, scaled_height, scaled_stride; + size_t scaled_image_size; + unsigned char *scaled_image; + unsigned char *scaled_aimage; + int auto_palette; /* 1 if we lack a palette and must use an heuristic. */ + int font_start_level; /* Darkest value used for the computed font */ + const vo_functions_t *hw_spu; + int spu_changed; + unsigned int forced_subs_only; /* flag: 0=display all subtitle, !0 display only forced subtitles */ + unsigned int is_forced_sub; /* true if current subtitle is a forced subtitle */ + + struct palette_crop_cache palette_crop_cache; +} spudec_handle_t; + +static void spudec_queue_packet(spudec_handle_t *this, packet_t *packet) +{ + if (this->queue_head == NULL) + this->queue_head = packet; + else + this->queue_tail->next = packet; + this->queue_tail = packet; +} + +static packet_t *spudec_dequeue_packet(spudec_handle_t *this) +{ + packet_t *retval = this->queue_head; + + this->queue_head = retval->next; + if (this->queue_head == NULL) + this->queue_tail = NULL; + + return retval; +} + +static void spudec_free_packet(packet_t *packet) +{ + if (packet->packet != NULL) + free(packet->packet); + free(packet); +} + +static inline unsigned int get_be16(const unsigned char *p) +{ + return (p[0] << 8) + p[1]; +} + +static inline unsigned int get_be24(const unsigned char *p) +{ + return (get_be16(p) << 8) + p[2]; +} + +static void next_line(packet_t *packet) +{ + if (packet->current_nibble[packet->deinterlace_oddness] % 2) + packet->current_nibble[packet->deinterlace_oddness]++; + packet->deinterlace_oddness = (packet->deinterlace_oddness + 1) % 2; +} + +static inline unsigned char get_nibble(packet_t *packet) +{ + unsigned char nib; + unsigned int *nibblep = packet->current_nibble + packet->deinterlace_oddness; + if (*nibblep / 2 >= packet->control_start) { + mp_msg(MSGT_SPUDEC,MSGL_WARN, "SPUdec: ERROR: get_nibble past end of packet\n"); + return 0; + } + nib = packet->packet[*nibblep / 2]; + if (*nibblep % 2) + nib &= 0xf; + else + nib >>= 4; + ++*nibblep; + return nib; +} + +/* Cut the sub to visible part */ +static inline void spudec_cut_image(spudec_handle_t *this) +{ + unsigned int fy, ly; + unsigned int first_y, last_y; + + if (this->stride == 0 || this->height == 0) { + return; + } + + for (fy = 0; fy < this->image_size && !this->aimage[fy]; fy++); + for (ly = this->stride * this->height-1; ly && !this->aimage[ly]; ly--); + first_y = fy / this->stride; + last_y = ly / this->stride; + //printf("first_y: %d, last_y: %d\n", first_y, last_y); + this->start_row += first_y; + + // Some subtitles trigger this condition + if (last_y + 1 > first_y ) { + this->height = last_y - first_y +1; + } else { + this->height = 0; + return; + } + +// printf("new h %d new start %d (sz %d st %d)---\n\n", this->height, this->start_row, this->image_size, this->stride); + + if (first_y > 0) { + memmove(this->image, this->image + this->stride * first_y, this->stride * this->height); + memmove(this->aimage, this->aimage + this->stride * first_y, this->stride * this->height); + } +} + + +static int spudec_alloc_image(spudec_handle_t *this, int stride, int height) +{ + if (this->width > stride) // just a safeguard + this->width = stride; + this->stride = stride; + this->height = height; + if (this->image_size < this->stride * this->height) { + if (this->image != NULL) { + free(this->image); + free(this->pal_image); + this->image_size = 0; + this->pal_width = this->pal_height = 0; + } + this->image = malloc(2 * this->stride * this->height); + if (this->image) { + this->image_size = this->stride * this->height; + this->aimage = this->image + this->image_size; + // use stride here as well to simplify reallocation checks + this->pal_image = malloc(this->stride * this->height); + } + } + return this->image != NULL; +} + +/** + * \param pal palette in MPlayer-style gray-alpha values, i.e. + * alpha == 0 means transparent, 1 fully opaque, + * gray value <= 256 - alpha. + */ +static void pal2gray_alpha(const uint16_t *pal, + const uint8_t *src, int src_stride, + uint8_t *dst, uint8_t *dsta, + int dst_stride, int w, int h) +{ + int x, y; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + uint16_t pixel = pal[src[x]]; + *dst++ = pixel; + *dsta++ = pixel >> 8; + } + for (; x < dst_stride; x++) + *dsta++ = *dst++ = 0; + src += src_stride; + } +} + +static int apply_palette_crop(spudec_handle_t *this, + unsigned crop_x, unsigned crop_y, + unsigned crop_w, unsigned crop_h) +{ + int i; + uint8_t *src; + uint16_t pal[4]; + unsigned stride = (crop_w + 7) & ~7; + if (crop_x > this->pal_width || crop_y > this->pal_height || + crop_w > this->pal_width - crop_x || crop_h > this->pal_width - crop_y || + crop_w > 0x8000 || crop_h > 0x8000 || + stride * crop_h > this->image_size) { + return 0; + } + for (i = 0; i < 4; ++i) { + int color; + int alpha = this->alpha[i]; + // extend 4 -> 8 bit + alpha |= alpha << 4; + if (this->custom && (this->cuspal[i] >> 31) != 0) + alpha = 0; + color = this->custom ? this->cuspal[i] : + this->global_palette[this->palette[i]]; + color = (color >> 16) & 0xff; + // convert to MPlayer-style gray/alpha palette + color = FFMIN(color, alpha); + pal[i] = (-alpha << 8) | color; + } + src = this->pal_image + crop_y * this->pal_width + crop_x; + pal2gray_alpha(pal, src, this->pal_width, + this->image, this->aimage, stride, + crop_w, crop_h); + this->width = crop_w; + this->height = crop_h; + this->stride = stride; + this->start_col = this->pal_start_col + crop_x; + this->start_row = this->pal_start_row + crop_y; + spudec_cut_image(this); + + // reset scaled image + this->scaled_frame_width = 0; + this->scaled_frame_height = 0; + this->palette_crop_cache.valid = 0; + return 1; +} + +int spudec_apply_palette_crop(void *this, uint32_t palette, + int sx, int sy, int ex, int ey) +{ + spudec_handle_t *spu = this; + struct palette_crop_cache *c = &spu->palette_crop_cache; + if (c->valid && c->palette == palette && + c->sx == sx && c->sy == sy && c->ex == ex && c->ey == ey) + return c->result; + spu->palette[0] = (palette >> 28) & 0xf; + spu->palette[1] = (palette >> 24) & 0xf; + spu->palette[2] = (palette >> 20) & 0xf; + spu->palette[3] = (palette >> 16) & 0xf; + spu->alpha[0] = (palette >> 12) & 0xf; + spu->alpha[1] = (palette >> 8) & 0xf; + spu->alpha[2] = (palette >> 4) & 0xf; + spu->alpha[3] = palette & 0xf; + spu->spu_changed = 1; + c->result = apply_palette_crop(spu, + sx - spu->pal_start_col, sy - spu->pal_start_row, + ex - sx, ey - sy); + c->palette = palette; + c->sx = sx; c->sy = sy; + c->ex = ex; c->ey = ey; + c->valid = 1; + return c->result; +} + +static void spudec_process_data(spudec_handle_t *this, packet_t *packet) +{ + unsigned int i, x, y; + uint8_t *dst; + + if (!spudec_alloc_image(this, packet->stride, packet->height)) + return; + + this->pal_start_col = packet->start_col; + this->pal_start_row = packet->start_row; + this->pal_height = packet->height; + this->pal_width = packet->width; + this->stride = packet->stride; + memcpy(this->palette, packet->palette, sizeof(this->palette)); + memcpy(this->alpha, packet->alpha, sizeof(this->alpha)); + + i = packet->current_nibble[1]; + x = 0; + y = 0; + dst = this->pal_image; + while (packet->current_nibble[0] < i + && packet->current_nibble[1] / 2 < packet->control_start + && y < this->pal_height) { + unsigned int len, color; + unsigned int rle = 0; + rle = get_nibble(packet); + if (rle < 0x04) { + if (rle == 0) { + rle = (rle << 4) | get_nibble(packet); + if (rle < 0x04) + rle = (rle << 4) | get_nibble(packet); + } + rle = (rle << 4) | get_nibble(packet); + } + color = 3 - (rle & 0x3); + len = rle >> 2; + x += len; + if (len == 0 || x >= this->pal_width) { + len += this->pal_width - x; + next_line(packet); + x = 0; + ++y; + } + memset(dst, color, len); + dst += len; + } + apply_palette_crop(this, 0, 0, this->pal_width, this->pal_height); +} + + +/* + This function tries to create a usable palette. + It determines how many non-transparent colors are used, and assigns different +gray scale values to each color. + I tested it with four streams and even got something readable. Half of the +times I got black characters with white around and half the reverse. +*/ +static void compute_palette(spudec_handle_t *this, packet_t *packet) +{ + int used[16],i,cused,start,step,color; + + memset(used, 0, sizeof(used)); + for (i=0; i<4; i++) + if (packet->alpha[i]) /* !Transparent? */ + used[packet->palette[i]] = 1; + for (cused=0, i=0; i<16; i++) + if (used[i]) cused++; + if (!cused) return; + if (cused == 1) { + start = 0x80; + step = 0; + } else { + start = this->font_start_level; + step = (0xF0-this->font_start_level)/(cused-1); + } + memset(used, 0, sizeof(used)); + for (i=0; i<4; i++) { + color = packet->palette[i]; + if (packet->alpha[i] && !used[color]) { /* not assigned? */ + used[color] = 1; + this->global_palette[color] = start<<16; + start += step; + } + } +} + +static void spudec_process_control(spudec_handle_t *this, int pts100) +{ + int a,b,c,d; /* Temporary vars */ + unsigned int date, type; + unsigned int off; + unsigned int start_off = 0; + unsigned int next_off; + unsigned int start_pts = 0; + unsigned int end_pts = 0; + unsigned int current_nibble[2] = {0, 0}; + unsigned int control_start; + unsigned int display = 0; + unsigned int start_col = 0; + unsigned int end_col = 0; + unsigned int start_row = 0; + unsigned int end_row = 0; + unsigned int width = 0; + unsigned int height = 0; + unsigned int stride = 0; + + control_start = get_be16(this->packet + 2); + next_off = control_start; + while (start_off != next_off) { + start_off = next_off; + date = get_be16(this->packet + start_off) * 1024; + next_off = get_be16(this->packet + start_off + 2); + mp_msg(MSGT_SPUDEC,MSGL_DBG2, "date=%d\n", date); + off = start_off + 4; + for (type = this->packet[off++]; type != 0xff; type = this->packet[off++]) { + mp_msg(MSGT_SPUDEC,MSGL_DBG2, "cmd=%d ",type); + switch(type) { + case 0x00: + /* Menu ID, 1 byte */ + mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Menu ID\n"); + /* shouldn't a Menu ID type force display start? */ + start_pts = pts100 < 0 && -pts100 >= date ? 0 : pts100 + date; + end_pts = UINT_MAX; + display = 1; + this->is_forced_sub=~0; // current subtitle is forced + break; + case 0x01: + /* Start display */ + mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Start display!\n"); + start_pts = pts100 < 0 && -pts100 >= date ? 0 : pts100 + date; + end_pts = UINT_MAX; + display = 1; + this->is_forced_sub=0; + break; + case 0x02: + /* Stop display */ + mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Stop display!\n"); + end_pts = pts100 < 0 && -pts100 >= date ? 0 : pts100 + date; + break; + case 0x03: + /* Palette */ + this->palette[0] = this->packet[off] >> 4; + this->palette[1] = this->packet[off] & 0xf; + this->palette[2] = this->packet[off + 1] >> 4; + this->palette[3] = this->packet[off + 1] & 0xf; + mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Palette %d, %d, %d, %d\n", + this->palette[0], this->palette[1], this->palette[2], this->palette[3]); + off+=2; + break; + case 0x04: + /* Alpha */ + a = this->packet[off] >> 4; + b = this->packet[off] & 0xf; + c = this->packet[off + 1] >> 4; + d = this->packet[off + 1] & 0xf; + // Note: some DVDs change these values to create a fade-in/fade-out effect + // We can not handle this, so just keep the highest value during the display time. + if (display) { + a = FFMAX(a, this->alpha[0]); + b = FFMAX(b, this->alpha[1]); + c = FFMAX(c, this->alpha[2]); + d = FFMAX(d, this->alpha[3]); + } + this->alpha[0] = a; + this->alpha[1] = b; + this->alpha[2] = c; + this->alpha[3] = d; + mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Alpha %d, %d, %d, %d\n", + this->alpha[0], this->alpha[1], this->alpha[2], this->alpha[3]); + off+=2; + break; + case 0x05: + /* Co-ords */ + a = get_be24(this->packet + off); + b = get_be24(this->packet + off + 3); + start_col = a >> 12; + end_col = a & 0xfff; + width = (end_col < start_col) ? 0 : end_col - start_col + 1; + stride = (width + 7) & ~7; /* Kludge: draw_alpha needs width multiple of 8 */ + start_row = b >> 12; + end_row = b & 0xfff; + height = (end_row < start_row) ? 0 : end_row - start_row /* + 1 */; + mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Coords col: %d - %d row: %d - %d (%dx%d)\n", + start_col, end_col, start_row, end_row, + width, height); + off+=6; + break; + case 0x06: + /* Graphic lines */ + current_nibble[0] = 2 * get_be16(this->packet + off); + current_nibble[1] = 2 * get_be16(this->packet + off + 2); + mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Graphic offset 1: %d offset 2: %d\n", + current_nibble[0] / 2, current_nibble[1] / 2); + off+=4; + break; + case 0xff: + /* All done, bye-bye */ + mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Done!\n"); + return; +// break; + default: + mp_msg(MSGT_SPUDEC,MSGL_WARN,"spudec: Error determining control type 0x%02x. Skipping %d bytes.\n", + type, next_off - off); + goto next_control; + } + } + next_control: + if (!display) + continue; + if (end_pts == UINT_MAX && start_off != next_off) { + end_pts = get_be16(this->packet + next_off) * 1024; + end_pts = 1 - pts100 >= end_pts ? 0 : pts100 + end_pts - 1; + } + if (end_pts > 0) { + packet_t *packet = calloc(1, sizeof(packet_t)); + int i; + packet->start_pts = start_pts; + packet->end_pts = end_pts; + packet->current_nibble[0] = current_nibble[0]; + packet->current_nibble[1] = current_nibble[1]; + packet->start_row = start_row; + packet->start_col = start_col; + packet->width = width; + packet->height = height; + packet->stride = stride; + packet->control_start = control_start; + for (i=0; i<4; i++) { + packet->alpha[i] = this->alpha[i]; + packet->palette[i] = this->palette[i]; + } + packet->packet = malloc(this->packet_size); + memcpy(packet->packet, this->packet, this->packet_size); + spudec_queue_packet(this, packet); + } + } +} + +static void spudec_decode(spudec_handle_t *this, int pts100) +{ + if (!this->hw_spu) + spudec_process_control(this, pts100); + else if (pts100 >= 0) { + static vo_mpegpes_t packet = { NULL, 0, 0x20, 0 }; + static vo_mpegpes_t *pkg=&packet; + packet.data = this->packet; + packet.size = this->packet_size; + packet.timestamp = pts100; + this->hw_spu->draw_frame((uint8_t**)&pkg); + } +} + +int spudec_changed(void * this) +{ + spudec_handle_t * spu = this; + return spu->spu_changed || spu->now_pts > spu->end_pts; +} + +void spudec_assemble(void *this, unsigned char *packet, unsigned int len, int pts100) +{ + spudec_handle_t *spu = this; +// spudec_heartbeat(this, pts100); + if (len < 2) { + mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: packet too short\n"); + return; + } + spu->packet_pts = pts100; + if (spu->packet_offset == 0) { + unsigned int len2 = get_be16(packet); + // Start new fragment + if (spu->packet_reserve < len2) { + if (spu->packet != NULL) + free(spu->packet); + spu->packet = malloc(len2); + spu->packet_reserve = spu->packet != NULL ? len2 : 0; + } + if (spu->packet != NULL) { + spu->packet_size = len2; + if (len > len2) { + mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: invalid frag len / len2: %d / %d \n", len, len2); + return; + } + memcpy(spu->packet, packet, len); + spu->packet_offset = len; + spu->packet_pts = pts100; + } + } else { + // Continue current fragment + if (spu->packet_size < spu->packet_offset + len){ + mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: invalid fragment\n"); + spu->packet_size = spu->packet_offset = 0; + return; + } else { + memcpy(spu->packet + spu->packet_offset, packet, len); + spu->packet_offset += len; + } + } +#if 1 + // check if we have a complete packet (unfortunatelly packet_size is bad + // for some disks) + // [cb] packet_size is padded to be even -> may be one byte too long + if ((spu->packet_offset == spu->packet_size) || + ((spu->packet_offset + 1) == spu->packet_size)){ + unsigned int x=0,y; + while(x+4<=spu->packet_offset){ + y=get_be16(spu->packet+x+2); // next control pointer + mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUtest: x=%d y=%d off=%d size=%d\n",x,y,spu->packet_offset,spu->packet_size); + if(x>=4 && x==y){ // if it points to self - we're done! + // we got it! + mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUgot: off=%d size=%d \n",spu->packet_offset,spu->packet_size); + spudec_decode(spu, pts100); + spu->packet_offset = 0; + break; + } + if(y<=x || y>=spu->packet_size){ // invalid? + mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUtest: broken packet!!!!! y=%d < x=%d\n",y,x); + spu->packet_size = spu->packet_offset = 0; + break; + } + x=y; + } + // [cb] packet is done; start new packet + spu->packet_offset = 0; + } +#else + if (spu->packet_offset == spu->packet_size) { + spudec_decode(spu, pts100); + spu->packet_offset = 0; + } +#endif +} + +void spudec_reset(void *this) // called after seek +{ + spudec_handle_t *spu = this; + while (spu->queue_head) + spudec_free_packet(spudec_dequeue_packet(spu)); + spu->now_pts = 0; + spu->end_pts = 0; + spu->packet_size = spu->packet_offset = 0; +} + +void spudec_heartbeat(void *this, unsigned int pts100) +{ + spudec_handle_t *spu = this; + spu->now_pts = pts100; + + // TODO: detect and handle broken timestamps (e.g. due to wrapping) + while (spu->queue_head != NULL && pts100 >= spu->queue_head->start_pts) { + packet_t *packet = spudec_dequeue_packet(spu); + spu->start_pts = packet->start_pts; + spu->end_pts = packet->end_pts; + if (packet->is_decoded) { + free(spu->image); + spu->image_size = packet->data_len; + spu->image = packet->packet; + spu->aimage = packet->packet + packet->stride * packet->height; + packet->packet = NULL; + spu->width = packet->width; + spu->height = packet->height; + spu->stride = packet->stride; + spu->start_col = packet->start_col; + spu->start_row = packet->start_row; + + // reset scaled image + spu->scaled_frame_width = 0; + spu->scaled_frame_height = 0; + } else { + if (spu->auto_palette) + compute_palette(spu, packet); + spudec_process_data(spu, packet); + } + spudec_free_packet(packet); + spu->spu_changed = 1; + } +} + +int spudec_visible(void *this){ + spudec_handle_t *spu = this; + int ret=(spu->start_pts <= spu->now_pts && + spu->now_pts < spu->end_pts && + spu->height > 0); +// printf("spu visible: %d \n",ret); + return ret; +} + +void spudec_set_forced_subs_only(void * const this, const unsigned int flag) +{ + if(this){ + ((spudec_handle_t *)this)->forced_subs_only=flag; + mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPU: Display only forced subs now %s\n", flag ? "enabled": "disabled"); + } +} + +void spudec_draw(void *this, void (*draw_alpha)(int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride)) +{ + spudec_handle_t *spu = this; + if (spudec_visible(spu)) + { + draw_alpha(spu->start_col, spu->start_row, spu->width, spu->height, + spu->image, spu->aimage, spu->stride); + spu->spu_changed = 0; + } +} + +/* calc the bbox for spudec subs */ +void spudec_calc_bbox(void *me, unsigned int dxs, unsigned int dys, unsigned int* bbox) +{ + spudec_handle_t *spu = me; + if (spu->orig_frame_width == 0 || spu->orig_frame_height == 0 + || (spu->orig_frame_width == dxs && spu->orig_frame_height == dys)) { + // unscaled + bbox[0] = spu->start_col; + bbox[1] = spu->start_col + spu->width; + bbox[2] = spu->start_row; + bbox[3] = spu->start_row + spu->height; + } + else { + // scaled + unsigned int scalex = 0x100 * dxs / spu->orig_frame_width; + unsigned int scaley = 0x100 * dys / spu->orig_frame_height; + bbox[0] = spu->start_col * scalex / 0x100; + bbox[1] = spu->start_col * scalex / 0x100 + spu->width * scalex / 0x100; + switch (spu_alignment) { + case 0: + bbox[3] = dys*sub_pos/100 + spu->height * scaley / 0x100; + if (bbox[3] > dys) bbox[3] = dys; + bbox[2] = bbox[3] - spu->height * scaley / 0x100; + break; + case 1: + if (sub_pos < 50) { + bbox[2] = dys*sub_pos/100 - spu->height * scaley / 0x200; + bbox[3] = bbox[2] + spu->height; + } else { + bbox[3] = dys*sub_pos/100 + spu->height * scaley / 0x200; + if (bbox[3] > dys) bbox[3] = dys; + bbox[2] = bbox[3] - spu->height * scaley / 0x100; + } + break; + case 2: + bbox[2] = dys*sub_pos/100 - spu->height * scaley / 0x100; + bbox[3] = bbox[2] + spu->height; + break; + default: /* -1 */ + bbox[2] = spu->start_row * scaley / 0x100; + bbox[3] = spu->start_row * scaley / 0x100 + spu->height * scaley / 0x100; + break; + } + } +} +/* transform mplayer's alpha value into an opacity value that is linear */ +static inline int canon_alpha(int alpha) +{ + return (uint8_t)-alpha; +} + +typedef struct { + unsigned position; + unsigned left_up; + unsigned right_down; +}scale_pixel; + + +static void scale_table(unsigned int start_src, unsigned int start_tar, unsigned int end_src, unsigned int end_tar, scale_pixel * table) +{ + unsigned int t; + unsigned int delta_src = end_src - start_src; + unsigned int delta_tar = end_tar - start_tar; + int src = 0; + int src_step; + if (delta_src == 0 || delta_tar == 0) { + return; + } + src_step = (delta_src << 16) / delta_tar >>1; + for (t = 0; t<=delta_tar; src += (src_step << 1), t++){ + table[t].position= FFMIN(src >> 16, end_src - 1); + table[t].right_down = src & 0xffff; + table[t].left_up = 0x10000 - table[t].right_down; + } +} + +/* bilinear scale, similar to vobsub's code */ +static void scale_image(int x, int y, scale_pixel* table_x, scale_pixel* table_y, spudec_handle_t * spu) +{ + int alpha[4]; + int color[4]; + unsigned int scale[4]; + int base = table_y[y].position * spu->stride + table_x[x].position; + int scaled = y * spu->scaled_stride + x; + alpha[0] = canon_alpha(spu->aimage[base]); + alpha[1] = canon_alpha(spu->aimage[base + 1]); + alpha[2] = canon_alpha(spu->aimage[base + spu->stride]); + alpha[3] = canon_alpha(spu->aimage[base + spu->stride + 1]); + color[0] = spu->image[base]; + color[1] = spu->image[base + 1]; + color[2] = spu->image[base + spu->stride]; + color[3] = spu->image[base + spu->stride + 1]; + scale[0] = (table_x[x].left_up * table_y[y].left_up >> 16) * alpha[0]; + if (table_y[y].left_up == 0x10000) // necessary to avoid overflow-case + scale[0] = table_x[x].left_up * alpha[0]; + scale[1] = (table_x[x].right_down * table_y[y].left_up >>16) * alpha[1]; + scale[2] = (table_x[x].left_up * table_y[y].right_down >> 16) * alpha[2]; + scale[3] = (table_x[x].right_down * table_y[y].right_down >> 16) * alpha[3]; + spu->scaled_image[scaled] = (color[0] * scale[0] + color[1] * scale[1] + color[2] * scale[2] + color[3] * scale[3])>>24; + spu->scaled_aimage[scaled] = (scale[0] + scale[1] + scale[2] + scale[3]) >> 16; + if (spu->scaled_aimage[scaled]){ + // ensure that MPlayer's simplified alpha-blending can not overflow + spu->scaled_image[scaled] = FFMIN(spu->scaled_image[scaled], spu->scaled_aimage[scaled]); + // convert to MPlayer-style alpha + spu->scaled_aimage[scaled] = -spu->scaled_aimage[scaled]; + } +} + +static void sws_spu_image(unsigned char *d1, unsigned char *d2, int dw, int dh, + int ds, const unsigned char* s1, unsigned char* s2, + int sw, int sh, int ss) +{ + struct SwsContext *ctx; + static SwsFilter filter; + static int firsttime = 1; + static float oldvar; + int i; + + if (!firsttime && oldvar != spu_gaussvar) sws_freeVec(filter.lumH); + if (firsttime) { + filter.lumH = filter.lumV = + filter.chrH = filter.chrV = sws_getGaussianVec(spu_gaussvar, 3.0); + sws_normalizeVec(filter.lumH, 1.0); + firsttime = 0; + oldvar = spu_gaussvar; + } + + ctx=sws_getContext(sw, sh, PIX_FMT_GRAY8, dw, dh, PIX_FMT_GRAY8, SWS_GAUSS, &filter, NULL, NULL); + sws_scale(ctx,&s1,&ss,0,sh,&d1,&ds); + for (i=ss*sh-1; i>=0; i--) if (!s2[i]) s2[i] = 255; //else s2[i] = 1; + sws_scale(ctx,&s2,&ss,0,sh,&d2,&ds); + for (i=ds*dh-1; i>=0; i--) if (d2[i]==0) d2[i] = 1; else if (d2[i]==255) d2[i] = 0; + sws_freeContext(ctx); +} + +void spudec_draw_scaled(void *me, unsigned int dxs, unsigned int dys, void (*draw_alpha)(int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride)) +{ + spudec_handle_t *spu = me; + scale_pixel *table_x; + scale_pixel *table_y; + + if (spudec_visible(spu)) { + + // check if only forced subtitles are requested + if( (spu->forced_subs_only) && !(spu->is_forced_sub) ){ + return; + } + + if (!(spu_aamode&16) && (spu->orig_frame_width == 0 || spu->orig_frame_height == 0 + || (spu->orig_frame_width == dxs && spu->orig_frame_height == dys))) { + spudec_draw(spu, draw_alpha); + } + else { + if (spu->scaled_frame_width != dxs || spu->scaled_frame_height != dys) { /* Resizing is needed */ + /* scaled_x = scalex * x / 0x100 + scaled_y = scaley * y / 0x100 + order of operations is important because of rounding. */ + unsigned int scalex = 0x100 * dxs / spu->orig_frame_width; + unsigned int scaley = 0x100 * dys / spu->orig_frame_height; + spu->scaled_start_col = spu->start_col * scalex / 0x100; + spu->scaled_start_row = spu->start_row * scaley / 0x100; + spu->scaled_width = spu->width * scalex / 0x100; + spu->scaled_height = spu->height * scaley / 0x100; + /* Kludge: draw_alpha needs width multiple of 8 */ + spu->scaled_stride = (spu->scaled_width + 7) & ~7; + if (spu->scaled_image_size < spu->scaled_stride * spu->scaled_height) { + if (spu->scaled_image) { + free(spu->scaled_image); + spu->scaled_image_size = 0; + } + spu->scaled_image = malloc(2 * spu->scaled_stride * spu->scaled_height); + if (spu->scaled_image) { + spu->scaled_image_size = spu->scaled_stride * spu->scaled_height; + spu->scaled_aimage = spu->scaled_image + spu->scaled_image_size; + } + } + if (spu->scaled_image) { + unsigned int x, y; + if (spu->scaled_width <= 1 || spu->scaled_height <= 1) { + goto nothing_to_do; + } + switch(spu_aamode&15) { + case 4: + sws_spu_image(spu->scaled_image, spu->scaled_aimage, + spu->scaled_width, spu->scaled_height, spu->scaled_stride, + spu->image, spu->aimage, spu->width, spu->height, spu->stride); + break; + case 3: + table_x = calloc(spu->scaled_width, sizeof(scale_pixel)); + table_y = calloc(spu->scaled_height, sizeof(scale_pixel)); + if (!table_x || !table_y) { + mp_msg(MSGT_SPUDEC, MSGL_FATAL, "Fatal: spudec_draw_scaled: calloc failed\n"); + } + scale_table(0, 0, spu->width - 1, spu->scaled_width - 1, table_x); + scale_table(0, 0, spu->height - 1, spu->scaled_height - 1, table_y); + for (y = 0; y < spu->scaled_height; y++) + for (x = 0; x < spu->scaled_width; x++) + scale_image(x, y, table_x, table_y, spu); + free(table_x); + free(table_y); + break; + case 0: + /* no antialiasing */ + for (y = 0; y < spu->scaled_height; ++y) { + int unscaled_y = y * 0x100 / scaley; + int strides = spu->stride * unscaled_y; + int scaled_strides = spu->scaled_stride * y; + for (x = 0; x < spu->scaled_width; ++x) { + int unscaled_x = x * 0x100 / scalex; + spu->scaled_image[scaled_strides + x] = spu->image[strides + unscaled_x]; + spu->scaled_aimage[scaled_strides + x] = spu->aimage[strides + unscaled_x]; + } + } + break; + case 1: + { + /* Intermediate antialiasing. */ + for (y = 0; y < spu->scaled_height; ++y) { + const unsigned int unscaled_top = y * spu->orig_frame_height / dys; + unsigned int unscaled_bottom = (y + 1) * spu->orig_frame_height / dys; + if (unscaled_bottom >= spu->height) + unscaled_bottom = spu->height - 1; + for (x = 0; x < spu->scaled_width; ++x) { + const unsigned int unscaled_left = x * spu->orig_frame_width / dxs; + unsigned int unscaled_right = (x + 1) * spu->orig_frame_width / dxs; + unsigned int color = 0; + unsigned int alpha = 0; + unsigned int walkx, walky; + unsigned int base, tmp; + if (unscaled_right >= spu->width) + unscaled_right = spu->width - 1; + for (walky = unscaled_top; walky <= unscaled_bottom; ++walky) + for (walkx = unscaled_left; walkx <= unscaled_right; ++walkx) { + base = walky * spu->stride + walkx; + tmp = canon_alpha(spu->aimage[base]); + alpha += tmp; + color += tmp * spu->image[base]; + } + base = y * spu->scaled_stride + x; + spu->scaled_image[base] = alpha ? color / alpha : 0; + spu->scaled_aimage[base] = + alpha * (1 + unscaled_bottom - unscaled_top) * (1 + unscaled_right - unscaled_left); + /* spu->scaled_aimage[base] = + alpha * dxs * dys / spu->orig_frame_width / spu->orig_frame_height; */ + if (spu->scaled_aimage[base]) { + spu->scaled_aimage[base] = 256 - spu->scaled_aimage[base]; + if (spu->scaled_aimage[base] + spu->scaled_image[base] > 255) + spu->scaled_image[base] = 256 - spu->scaled_aimage[base]; + } + } + } + } + break; + case 2: + { + /* Best antialiasing. Very slow. */ + /* Any pixel (x, y) represents pixels from the original + rectangular region comprised between the columns + unscaled_y and unscaled_y + 0x100 / scaley and the rows + unscaled_x and unscaled_x + 0x100 / scalex + + The original rectangular region that the scaled pixel + represents is cut in 9 rectangular areas like this: + + +---+-----------------+---+ + | 1 | 2 | 3 | + +---+-----------------+---+ + | | | | + | 4 | 5 | 6 | + | | | | + +---+-----------------+---+ + | 7 | 8 | 9 | + +---+-----------------+---+ + + The width of the left column is at most one pixel and + it is never null and its right column is at a pixel + boundary. The height of the top row is at most one + pixel it is never null and its bottom row is at a + pixel boundary. The width and height of region 5 are + integral values. The width of the right column is + what remains and is less than one pixel. The height + of the bottom row is what remains and is less than + one pixel. + + The row above 1, 2, 3 is unscaled_y. The row between + 1, 2, 3 and 4, 5, 6 is top_low_row. The row between 4, + 5, 6 and 7, 8, 9 is (unsigned int)unscaled_y_bottom. + The row beneath 7, 8, 9 is unscaled_y_bottom. + + The column left of 1, 4, 7 is unscaled_x. The column + between 1, 4, 7 and 2, 5, 8 is left_right_column. The + column between 2, 5, 8 and 3, 6, 9 is (unsigned + int)unscaled_x_right. The column right of 3, 6, 9 is + unscaled_x_right. */ + const double inv_scalex = (double) 0x100 / scalex; + const double inv_scaley = (double) 0x100 / scaley; + for (y = 0; y < spu->scaled_height; ++y) { + const double unscaled_y = y * inv_scaley; + const double unscaled_y_bottom = unscaled_y + inv_scaley; + const unsigned int top_low_row = FFMIN(unscaled_y_bottom, unscaled_y + 1.0); + const double top = top_low_row - unscaled_y; + const unsigned int height = unscaled_y_bottom > top_low_row + ? (unsigned int) unscaled_y_bottom - top_low_row + : 0; + const double bottom = unscaled_y_bottom > top_low_row + ? unscaled_y_bottom - floor(unscaled_y_bottom) + : 0.0; + for (x = 0; x < spu->scaled_width; ++x) { + const double unscaled_x = x * inv_scalex; + const double unscaled_x_right = unscaled_x + inv_scalex; + const unsigned int left_right_column = FFMIN(unscaled_x_right, unscaled_x + 1.0); + const double left = left_right_column - unscaled_x; + const unsigned int width = unscaled_x_right > left_right_column + ? (unsigned int) unscaled_x_right - left_right_column + : 0; + const double right = unscaled_x_right > left_right_column + ? unscaled_x_right - floor(unscaled_x_right) + : 0.0; + double color = 0.0; + double alpha = 0.0; + double tmp; + unsigned int base; + /* Now use these informations to compute a good alpha, + and lightness. The sum is on each of the 9 + region's surface and alpha and lightness. + + transformed alpha = sum(surface * alpha) / sum(surface) + transformed color = sum(surface * alpha * color) / sum(surface * alpha) + */ + /* 1: top left part */ + base = spu->stride * (unsigned int) unscaled_y; + tmp = left * top * canon_alpha(spu->aimage[base + (unsigned int) unscaled_x]); + alpha += tmp; + color += tmp * spu->image[base + (unsigned int) unscaled_x]; + /* 2: top center part */ + if (width > 0) { + unsigned int walkx; + for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) { + base = spu->stride * (unsigned int) unscaled_y + walkx; + tmp = /* 1.0 * */ top * canon_alpha(spu->aimage[base]); + alpha += tmp; + color += tmp * spu->image[base]; + } + } + /* 3: top right part */ + if (right > 0.0) { + base = spu->stride * (unsigned int) unscaled_y + (unsigned int) unscaled_x_right; + tmp = right * top * canon_alpha(spu->aimage[base]); + alpha += tmp; + color += tmp * spu->image[base]; + } + /* 4: center left part */ + if (height > 0) { + unsigned int walky; + for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) { + base = spu->stride * walky + (unsigned int) unscaled_x; + tmp = left /* * 1.0 */ * canon_alpha(spu->aimage[base]); + alpha += tmp; + color += tmp * spu->image[base]; + } + } + /* 5: center part */ + if (width > 0 && height > 0) { + unsigned int walky; + for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) { + unsigned int walkx; + base = spu->stride * walky; + for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) { + tmp = /* 1.0 * 1.0 * */ canon_alpha(spu->aimage[base + walkx]); + alpha += tmp; + color += tmp * spu->image[base + walkx]; + } + } + } + /* 6: center right part */ + if (right > 0.0 && height > 0) { + unsigned int walky; + for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) { + base = spu->stride * walky + (unsigned int) unscaled_x_right; + tmp = right /* * 1.0 */ * canon_alpha(spu->aimage[base]); + alpha += tmp; + color += tmp * spu->image[base]; + } + } + /* 7: bottom left part */ + if (bottom > 0.0) { + base = spu->stride * (unsigned int) unscaled_y_bottom + (unsigned int) unscaled_x; + tmp = left * bottom * canon_alpha(spu->aimage[base]); + alpha += tmp; + color += tmp * spu->image[base]; + } + /* 8: bottom center part */ + if (width > 0 && bottom > 0.0) { + unsigned int walkx; + base = spu->stride * (unsigned int) unscaled_y_bottom; + for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) { + tmp = /* 1.0 * */ bottom * canon_alpha(spu->aimage[base + walkx]); + alpha += tmp; + color += tmp * spu->image[base + walkx]; + } + } + /* 9: bottom right part */ + if (right > 0.0 && bottom > 0.0) { + base = spu->stride * (unsigned int) unscaled_y_bottom + (unsigned int) unscaled_x_right; + tmp = right * bottom * canon_alpha(spu->aimage[base]); + alpha += tmp; + color += tmp * spu->image[base]; + } + /* Finally mix these transparency and brightness information suitably */ + base = spu->scaled_stride * y + x; + spu->scaled_image[base] = alpha > 0 ? color / alpha : 0; + spu->scaled_aimage[base] = alpha * scalex * scaley / 0x10000; + if (spu->scaled_aimage[base]) { + spu->scaled_aimage[base] = 256 - spu->scaled_aimage[base]; + if (spu->scaled_aimage[base] + spu->scaled_image[base] > 255) + spu->scaled_image[base] = 256 - spu->scaled_aimage[base]; + } + } + } + } + } +nothing_to_do: + /* Kludge: draw_alpha needs width multiple of 8. */ + if (spu->scaled_width < spu->scaled_stride) + for (y = 0; y < spu->scaled_height; ++y) { + memset(spu->scaled_aimage + y * spu->scaled_stride + spu->scaled_width, 0, + spu->scaled_stride - spu->scaled_width); + } + spu->scaled_frame_width = dxs; + spu->scaled_frame_height = dys; + } + } + if (spu->scaled_image){ + switch (spu_alignment) { + case 0: + spu->scaled_start_row = dys*sub_pos/100; + if (spu->scaled_start_row + spu->scaled_height > dys) + spu->scaled_start_row = dys - spu->scaled_height; + break; + case 1: + spu->scaled_start_row = dys*sub_pos/100 - spu->scaled_height/2; + if (sub_pos >= 50 && spu->scaled_start_row + spu->scaled_height > dys) + spu->scaled_start_row = dys - spu->scaled_height; + break; + case 2: + spu->scaled_start_row = dys*sub_pos/100 - spu->scaled_height; + break; + } + draw_alpha(spu->scaled_start_col, spu->scaled_start_row, spu->scaled_width, spu->scaled_height, + spu->scaled_image, spu->scaled_aimage, spu->scaled_stride); + spu->spu_changed = 0; + } + } + } + else + { + mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPU not displayed: start_pts=%d end_pts=%d now_pts=%d\n", + spu->start_pts, spu->end_pts, spu->now_pts); + } +} + +void spudec_update_palette(void * this, unsigned int *palette) +{ + spudec_handle_t *spu = this; + if (spu && palette) { + memcpy(spu->global_palette, palette, sizeof(spu->global_palette)); + if(spu->hw_spu) + spu->hw_spu->control(VOCTRL_SET_SPU_PALETTE,spu->global_palette); + } +} + +void spudec_set_font_factor(void * this, double factor) +{ + spudec_handle_t *spu = this; + spu->font_start_level = (int)(0xF0-(0xE0*factor)); +} + +static void spudec_parse_extradata(spudec_handle_t *this, + uint8_t *extradata, int extradata_len) +{ + uint8_t *buffer, *ptr; + unsigned int *pal = this->global_palette, *cuspal = this->cuspal; + unsigned int tridx; + int i; + + if (extradata_len == 16*4) { + for (i=0; i<16; i++) + pal[i] = AV_RB32(extradata + i*4); + this->auto_palette = 0; + return; + } + + if (!(ptr = buffer = malloc(extradata_len+1))) + return; + memcpy(buffer, extradata, extradata_len); + buffer[extradata_len] = 0; + + do { + if (*ptr == '#') + continue; + if (!strncmp(ptr, "size: ", 6)) + sscanf(ptr + 6, "%dx%d", &this->orig_frame_width, &this->orig_frame_height); + if (!strncmp(ptr, "palette: ", 9) && + sscanf(ptr + 9, "%x, %x, %x, %x, %x, %x, %x, %x, " + "%x, %x, %x, %x, %x, %x, %x, %x", + &pal[ 0], &pal[ 1], &pal[ 2], &pal[ 3], + &pal[ 4], &pal[ 5], &pal[ 6], &pal[ 7], + &pal[ 8], &pal[ 9], &pal[10], &pal[11], + &pal[12], &pal[13], &pal[14], &pal[15]) == 16) { + for (i=0; i<16; i++) + pal[i] = vobsub_palette_to_yuv(pal[i]); + this->auto_palette = 0; + } + if (!strncasecmp(ptr, "forced subs: on", 15)) + this->forced_subs_only = 1; + if (!strncmp(ptr, "custom colors: ON, tridx: ", 26) && + sscanf(ptr + 26, "%x, colors: %x, %x, %x, %x", + &tridx, cuspal+0, cuspal+1, cuspal+2, cuspal+3) == 5) { + for (i=0; i<4; i++) { + cuspal[i] = vobsub_rgb_to_yuv(cuspal[i]); + if (tridx & (1 << (12-4*i))) + cuspal[i] |= 1 << 31; + } + this->custom = 1; + } + } while ((ptr=strchr(ptr,'\n')) && *++ptr); + + free(buffer); +} + +void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len) +{ + spudec_handle_t *this = calloc(1, sizeof(spudec_handle_t)); + if (this){ + this->orig_frame_height = frame_height; + this->orig_frame_width = frame_width; + // set up palette: + if (palette) + memcpy(this->global_palette, palette, sizeof(this->global_palette)); + else + this->auto_palette = 1; + if (extradata) + spudec_parse_extradata(this, extradata, extradata_len); + /* XXX Although the video frame is some size, the SPU frame is + always maximum size i.e. 720 wide and 576 or 480 high */ + // For HD files in MKV the VobSub resolution can be higher though, + // see largeres_vobsub.mkv + if (this->orig_frame_width <= 720 && this->orig_frame_height <= 576) { + this->orig_frame_width = 720; + if (this->orig_frame_height == 480 || this->orig_frame_height == 240) + this->orig_frame_height = 480; + else + this->orig_frame_height = 576; + } + } + else + mp_msg(MSGT_SPUDEC,MSGL_FATAL, "FATAL: spudec_init: calloc"); + return this; +} + +void *spudec_new(unsigned int *palette) +{ + return spudec_new_scaled(palette, 0, 0, NULL, 0); +} + +void spudec_free(void *this) +{ + spudec_handle_t *spu = this; + if (spu) { + while (spu->queue_head) + spudec_free_packet(spudec_dequeue_packet(spu)); + free(spu->packet); + spu->packet = NULL; + free(spu->scaled_image); + spu->scaled_image = NULL; + free(spu->image); + spu->image = NULL; + spu->aimage = NULL; + free(spu->pal_image); + spu->pal_image = NULL; + spu->image_size = 0; + spu->pal_width = spu->pal_height = 0; + free(spu); + } +} + +void spudec_set_hw_spu(void *this, const vo_functions_t *hw_spu) +{ + spudec_handle_t *spu = this; + if (!spu) + return; + spu->hw_spu = hw_spu; + hw_spu->control(VOCTRL_SET_SPU_PALETTE,spu->global_palette); +} + +#define MP_NOPTS_VALUE (-1LL<<63) //both int64_t and double should be able to represent this exactly + +/** + * palette must contain at least 256 32-bit entries, otherwise crashes + * are possible + */ +void spudec_set_paletted(void *this, const uint8_t *pal_img, int pal_stride, + const void *palette, + int x, int y, int w, int h, + double pts, double endpts) +{ + int i; + uint16_t g8a8_pal[256]; + packet_t *packet; + const uint32_t *pal = palette; + spudec_handle_t *spu = this; + uint8_t *img; + uint8_t *aimg; + int stride = (w + 7) & ~7; + if ((unsigned)w >= 0x8000 || (unsigned)h > 0x4000) + return; + packet = calloc(1, sizeof(packet_t)); + packet->is_decoded = 1; + packet->width = w; + packet->height = h; + packet->stride = stride; + packet->start_col = x; + packet->start_row = y; + packet->data_len = 2 * stride * h; + if (packet->data_len) { // size 0 is a special "clear" packet + packet->packet = malloc(packet->data_len); + img = packet->packet; + aimg = packet->packet + stride * h; + for (i = 0; i < 256; i++) { + uint32_t pixel = pal[i]; + int alpha = pixel >> 24; + int gray = (((pixel & 0x000000ff) >> 0) + + ((pixel & 0x0000ff00) >> 7) + + ((pixel & 0x00ff0000) >> 16)) >> 2; + gray = FFMIN(gray, alpha); + g8a8_pal[i] = (-alpha << 8) | gray; + } + pal2gray_alpha(g8a8_pal, pal_img, pal_stride, + img, aimg, stride, w, h); + } + packet->start_pts = 0; + packet->end_pts = 0x7fffffff; + if (pts != MP_NOPTS_VALUE) + packet->start_pts = pts * 90000; + if (endpts != MP_NOPTS_VALUE) + packet->end_pts = endpts * 90000; + spudec_queue_packet(spu, packet); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sub/spudec.h Wed Oct 27 16:38:51 2010 +0000 @@ -0,0 +1,45 @@ +/* + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_SPUDEC_H +#define MPLAYER_SPUDEC_H + +#include "libvo/video_out.h" + +void spudec_heartbeat(void *this, unsigned int pts100); +void spudec_assemble(void *this, unsigned char *packet, unsigned int len, int pts100); +void spudec_draw(void *this, void (*draw_alpha)(int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride)); +void spudec_draw_scaled(void *this, unsigned int dxs, unsigned int dys, void (*draw_alpha)(int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride)); +int spudec_apply_palette_crop(void *this, uint32_t palette, int sx, int ex, int sy, int ey); +void spudec_update_palette(void *this, unsigned int *palette); +void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len); +void *spudec_new(unsigned int *palette); +void spudec_free(void *this); +void spudec_reset(void *this); // called after seek +int spudec_visible(void *this); // check if spu is visible +void spudec_set_font_factor(void * this, double factor); // sets the equivalent to ffactor +void spudec_set_hw_spu(void *this, const vo_functions_t *hw_spu); +int spudec_changed(void *this); +void spudec_calc_bbox(void *me, unsigned int dxs, unsigned int dys, unsigned int* bbox); +void spudec_set_forced_subs_only(void * const this, const unsigned int flag); +void spudec_set_paletted(void *this, const uint8_t *pal_img, int stride, + const void *palette, + int x, int y, int w, int h, + double pts, double endpts); + +#endif /* MPLAYER_SPUDEC_H */