summaryrefslogtreecommitdiffstats
path: root/spudec.c
diff options
context:
space:
mode:
authorarpi <arpi@b3059339-0415-0410-9bf9-f77b7e298cf2>2001-11-20 18:36:50 +0000
committerarpi <arpi@b3059339-0415-0410-9bf9-f77b7e298cf2>2001-11-20 18:36:50 +0000
commit7cb47d1cbc5b63c0c17b67a408a3eaabee2b38d9 (patch)
tree14bd177d8fa1f2134ecd518bbf4a6fa6f51fa5ba /spudec.c
parent8d04f52cd7753af44c05c87759b12aff672f19aa (diff)
downloadmpv-7cb47d1cbc5b63c0c17b67a408a3eaabee2b38d9.tar.bz2
mpv-7cb47d1cbc5b63c0c17b67a408a3eaabee2b38d9.tar.xz
DVD sub patch by Kim Minh Kaplan <kmkaplan@selfoffice.com>
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@3035 b3059339-0415-0410-9bf9-f77b7e298cf2
Diffstat (limited to 'spudec.c')
-rw-r--r--spudec.c408
1 files changed, 312 insertions, 96 deletions
diff --git a/spudec.c b/spudec.c
index f8f77ef86a..f455e9ea01 100644
--- a/spudec.c
+++ b/spudec.c
@@ -1,117 +1,333 @@
/* SPUdec.c
Skeleton of function spudec_process_controll() is from xine sources.
Further works:
- LGB,... (yeah, try to improve it and insert your name here! ;-) */
+ LGB,... (yeah, try to improve it and insert your name here! ;-)
+ Kim Minh Kaplan
+ implement fragments reassembly, RLE decoding.
+ image rendering needs to be corrected (see mkcolor & mkalpha).
+ For information on SPU format see <URL:http://sam.zoy.org/doc/dvd/subtitles/>
+
+ */
+
+#include <errno.h>
#include <stdio.h>
+#include <stdlib.h>
#include "spudec.h"
+typedef struct {
+ unsigned char* packet;
+ size_t packet_reserve; /* size of the memory pointed to by packet */
+ int packet_offset; /* end of the currently assembled fragment */
+ int packet_size; /* size of the packet once all fragments are assembled */
+ int control_start; /* index of start of control data */
+ int palette[4];
+ int alpha[4];
+ int now_pts;
+ int start_pts, end_pts;
+ int start_col, end_col;
+ int start_row, end_row;
+ int width, height;
+ 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 */
+ size_t image_size; /* Size of the image buffer */
+ unsigned char *image; /* Grayscale value */
+ unsigned char *aimage; /* Alpha value */
+} spudec_handle_t;
+
+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(spudec_handle_t *this)
+{
+ if (this->current_nibble[this->deinterlace_oddness] % 2)
+ this->current_nibble[this->deinterlace_oddness]++;
+ this->deinterlace_oddness = (this->deinterlace_oddness + 1) % 2;
+}
+
+static inline unsigned char get_nibble(spudec_handle_t *this)
+{
+ unsigned char nib;
+ int *nibblep = this->current_nibble + this->deinterlace_oddness;
+ if (*nibblep / 2 >= this->control_start) {
+ fprintf(stderr, "ERROR: get_nibble past end of packet\n");
+ return 0;
+ }
+ nib = this->packet[*nibblep / 2];
+ if (*nibblep % 2)
+ nib &= 0xf;
+ else
+ nib >>= 4;
+ ++*nibblep;
+ return nib;
+}
+
+static inline int mkalpha(int i)
+{
+ /* for VO 0 is transparent
+ 127 is quite dark, but still...
+ 255 is transparent with color 0, and hum... funny with other colors...
+
+ FIXME, I can't seem to get a good alpha value!
+
+ i is the value read from SPU, from 0 to 15. The function should
+ return the corresponding alpha value suitable for libvo's
+ draw_alpha. */
+#if 0
+ return (0xf - (i & 0xf)) << 4;
+#else
+ return (i < 8) ? 127 : 0;
+#endif
+}
+
+static inline int mkcolor(int i)
+{
+ /* FIXME, have to get the colormap's RGB values from the IFO */
+#if 0
+ switch (i) {
+ case 15: return 0;
+ default: return i << 4;
+ }
+#else
+ return i << 4;
+#endif
+}
+static void spudec_process_data(spudec_handle_t *this)
+{
+ int alpha[4] = {
+ mkalpha(this->alpha[0]),
+ mkalpha(this->alpha[1]),
+ mkalpha(this->alpha[2]),
+ mkalpha(this->alpha[3])
+ };
+ int cmap[4] = {
+ mkcolor(this->palette[0]),
+ mkcolor(this->palette[1]),
+ mkcolor(this->palette[2]),
+ mkcolor(this->palette[3])
+ };
+ int y = 0, x = 0;
+ if (this->image_size < this->width * this->height) {
+ if (this->image != NULL)
+ free(this->image);
+ this->image = malloc(2 * this->width * this->height);
+ if (this->image) {
+ this->image_size = this->width * this->height;
+ this->aimage = this->image + this->image_size;
+ }
+ }
+ if (this->image == NULL)
+ return;
+ while (this->current_nibble[0] / 2 < this->control_start
+ && this->current_nibble[1] / 2 < this->control_start
+ && y < this->height) {
+ int len, color;
+ unsigned int rle = 0;
+ rle = get_nibble(this);
+ if (rle < 0x04) {
+ rle = (rle << 4) | get_nibble(this);
+ if (rle < 0x10) {
+ rle = (rle << 4) | get_nibble(this);
+ if (rle < 0x040) {
+ rle = (rle << 4) | get_nibble(this);
+ if (rle < 0x0004)
+ rle |= ((this->width - x) << 2);
+ }
+ }
+ }
+ color = rle & 0x3;
+ len = rle >> 2;
+ if (len > this->width - x)
+ len = this->width - x;
+ /* FIXME have to use palette and alpha map*/
+ memset(this->image + y * this->width + x, cmap[color], len);
+ memset(this->aimage + y * this->width + x, alpha[color], len);
+ x += len;
+ if (x >= this->width) {
+ next_line(this);
+ x = 0;
+ ++y;
+ }
+ }
+}
-void spudec_process_control(unsigned char *control, int size, int* d1, int* d2)
+static void spudec_process_control(spudec_handle_t *this)
{
- int off = 2;
int a,b; /* Temporary vars */
+ int date, type;
+ int off;
+ int start_off = 0;
+ int next_off;
- do {
- int type = control[off];
- off++;
- printf("cmd=%d ",type);
-
- switch(type) {
- case 0x00:
- /* Menu ID, 1 byte */
- printf("Menu ID\n");
- break;
- case 0x01:
- /* Start display */
- printf("Start display!\n");
-// gSpudec.geom.bIsVisible = 1;
- break;
- case 0x03:
- /* Palette */
- printf("Palette\n");
-// palette[3] = &(gSpudec.clut[(control[off] >> 4)]);
-// palette[2] = &(gSpudec.clut[control[off] & 0xf]);
-// palette[1] = &(gSpudec.clut[(control[off+1] >> 4)]);
-// palette[0] = &(gSpudec.clut[control[off+1] & 0xf]);
- off+=2;
- break;
- case 0x04:
- /* Alpha */
- printf("Alpha\n");
-// alpha[3] = control[off] & 0xf0;
-// alpha[2] = (control[off] & 0xf) << 4;
-// alpha[1] = control[off+1] & 0xf0;
-// alpha[0] = (control[off+1] & 0xf) << 4;
- off+=2;
- break;
- case 0x05:
- /* Co-ords */
- a = (control[off] << 16) + (control[off+1] << 8) + control[off+2];
- b = (control[off+3] << 16) + (control[off+4] << 8) + control[off+5];
-
- printf("Coords col: %d - %d row: %d - %d\n",a >> 12,a & 0xfff,b >> 12,b & 0xfff);
-
-// gSpudec.geom.start_col = a >> 12;
-// gSpudec.geom.end_col = a & 0xfff;
-// gSpudec.geom.start_row = b >> 12;
-// gSpudec.geom.end_row = b & 0xfff;
-
- off+=6;
- break;
- case 0x06:
- /* Graphic lines */
- *(d1) = (control[off] << 8) + control[off+1];
- *(d2) = (control[off+2] << 8) + control[off+3];
- printf("Graphic pos color: %d b/w: %d\n",*d1,*d2);
- off+=4;
- break;
- case 0xff:
- /* All done, bye-bye */
- printf("Done!\n");
- return;
- break;
- default:
- printf("spudec: Error determining control type 0x%02x.\n",type);
- return;
- break;
+ this->control_start = get_be16(this->packet + 2);
+ next_off = this->control_start;
+ while (start_off != next_off) {
+ start_off = next_off;
+ date = get_be16(this->packet + start_off);
+ next_off = get_be16(this->packet + start_off + 2);
+ printf("date=%d\n", date);
+ off = start_off + 4;
+ for (type = this->packet[off++]; type != 0xff; type = this->packet[off++]) {
+ printf("cmd=%d ",type);
+ switch(type) {
+ case 0x00:
+ /* Menu ID, 1 byte */
+ printf("Menu ID\n");
+ break;
+ case 0x01:
+ /* Start display */
+ printf("Start display!\n");
+ this->start_pts = this->now_pts + date;
+ break;
+ case 0x02:
+ /* Stop display */
+ printf("Stop display!\n");
+ this->end_pts = this->now_pts + 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;
+ printf("Palette %d, %d, %d, %d\n",
+ this->palette[0], this->palette[1], this->palette[2], this->palette[3]);
+ off+=2;
+ break;
+ case 0x04:
+ /* Alpha */
+ this->alpha[0] = this->packet[off] >> 4;
+ this->alpha[1] = this->packet[off] & 0xf;
+ this->alpha[2] = this->packet[off + 1] >> 4;
+ this->alpha[3] = this->packet[off + 1] & 0xf;
+ printf("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);
+ this->start_col = a >> 12;
+ this->end_col = a & 0xfff;
+ this->width = this->end_col - this->start_col + 1;
+ this->start_row = b >> 12;
+ this->end_row = b & 0xfff;
+ this->height = this->end_row - this->start_row + 1;
+ printf("Coords col: %d - %d row: %d - %d (%dx%d)\n",
+ this->start_col, this->end_col, this->start_row, this->end_row,
+ this->width, this->height);
+ off+=6;
+ break;
+ case 0x06:
+ /* Graphic lines */
+ this->current_nibble[0] = 2 * get_be16(this->packet + off);
+ this->current_nibble[1] = 2 * get_be16(this->packet + off + 2);
+ printf("Graphic offset 1: %d offset 2: %d\n",
+ this->current_nibble[0] / 2, this->current_nibble[1] / 2);
+ off+=4;
+ break;
+ case 0xff:
+ /* All done, bye-bye */
+ printf("Done!\n");
+ return;
+ break;
+ default:
+ printf("spudec: Error determining control type 0x%02x.\n",type);
+ return;
+ break;
+ }
+
+ /* printf("spudec: Processsed control type 0x%02x.\n",type); */
}
+ }
+}
- /* printf("spudec: Processsed control type 0x%02x.\n",type); */
- } while(off < size);
+static void spudec_decode(spudec_handle_t *this)
+{
+ spudec_process_control(this);
+ spudec_process_data(this);
}
-// SPU packet format: (guess only, by A'rpi)
-// 0 word whole packet size
-// 2 word x0 sub-packet size
-// 4 x0-2 pixel data
-// x0+2 word x1 sub-packet size
-// x0+4 x1-x0-2 process control data
-// x1 word lifetime
-// x1+2 word x1 sub-packet size again
-
-void spudec_decode(unsigned char *packet,int len){
- int x0, x1;
- int d1, d2;
- int lifetime;
- x0 = (packet[2] << 8) + packet[3];
- x1 = (packet[x0+2] << 8) + packet[x0+3];
-
- /* /Another/ sanity check. */
- if((packet[x1+2]<<8) + packet[x1+3] != x1) {
- printf("spudec: Incorrect packet.\n");
- return;
+
+void spudec_assemble(void *this, unsigned char *packet, int len, int pts100)
+{
+ spudec_handle_t *spu = (spudec_handle_t*)this;
+ spudec_heartbeat(this, 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->deinterlace_oddness = 0;
+ spu->packet_size = len2;
+ memcpy(spu->packet, packet, len);
+ spu->packet_offset = len;
+ }
+ } else {
+ // Continue current fragment
+ if (spu->packet_size < spu->packet_offset + len)
+ fprintf(stderr,"invalid fragment\n");
+ else {
+ memcpy(spu->packet + spu->packet_offset, packet, len);
+ spu->packet_offset += len;
+ }
}
- lifetime= ((packet[x1]<<8) + packet[x1+1]);
- printf("lifetime=%d\n",lifetime);
-
- d1 = d2 = -1;
- spudec_process_control(packet + x0 + 2, x1-x0-2, &d1, &d2);
-// if((d1 != -1) && (d2 != -1)) {
-// spudec_process_data(packet, x0, d1, d2);
-// }
+ if (spu->packet_offset == spu->packet_size) {
+ spudec_decode(spu);
+ spu->packet_offset = 0;
+ }
+}
+
+void spudec_heartbeat(void *this, int pts100)
+{
+ ((spudec_handle_t *)this)->now_pts = 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))
+{
+ spudec_handle_t *spu = (spudec_handle_t *)this;
+ if (spu->start_pts <= spu->now_pts && spu->now_pts < spu->end_pts && spu->image)
+ draw_alpha(spu->start_col, spu->start_row, spu->width, spu->height,
+ spu->image, spu->aimage, spu->width);
}
+void *spudec_new()
+{
+ spudec_handle_t *this = calloc(1, sizeof(spudec_handle_t));
+ if (this) {
+ ;
+ }
+ else
+ perror("FATAL: spudec_init: calloc");
+ return this;
+}
+
+void spudec_free(void *this)
+{
+ spudec_handle_t *spu = (spudec_handle_t*)this;
+ if (spu) {
+ if (spu->packet)
+ free(spu->packet);
+ if (spu->image)
+ free(spu->image);
+ free(spu);
+ }
+}