home *** CD-ROM | disk | FTP | other *** search
- /*
- * StreamDetector.cpp: implementation of the CStreamDetector class.
- *
- * Copyright (C) Alberto Vigata - January 2000 - ultraflask@yahoo.com
- *
- * This file is part of FlasKMPEG, a free MPEG to MPEG/AVI converter
- *
- * FlasKMPEG 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, or (at your option)
- * any later version.
- *
- * FlasKMPEG 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 GNU Make; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
- #include <crtdbg.h>
- #include "subpic.h"
- extern "C"
- {
- #include "..\video\global.h"
- }
-
- struct subpic_t {
- unsigned char *bitmap;
- int x, y; /* dimensions */
- int sX, sY; /* coordinates */
- i64 start, stop; /* start and stop time */
-
- unsigned char colors[4]; /* palette */
- unsigned char alpha[4]; /* alpha channel */
- struct subpic_t *next, *prev;
- };
-
- static struct subpic_t *pics = NULL;
-
- /* grab a nibble from buf */
- static unsigned char grab_nibble(const unsigned char *buf, unsigned &offset, bool &aligned)
- {
- unsigned char c;
- if(aligned) c = buf[offset] >> 4;
- else {
- c = buf[offset] & 0xF;
- offset++;
- }
- aligned = !aligned;
-
- return c;
- }
-
- static void free_subpic(struct subpic_t *p)
- {
- if(p == NULL) return;
- if(p->bitmap) free(p->bitmap);
- free(p);
- }
-
- /* free all subpics */
- void subpic_free(void)
- {
- struct subpic_t *s;
- for(s = pics; s; s=s->next) {
- free_subpic(s);
- }
- pics = NULL;
- }
- #include "..\Input\IFOParser\IFOParser.h"
-
- static char palette[16][4];
- extern CIFOParser *IFOGlobal;
- static void subpic_apply(VBitmap *p, i64 tm, struct subpic_t *s)
- {
- for(int i = 0; i < 16; i++) {
- int crv = 117504, cbu = 138453, cgu = 13954, cgv = 34903;
-
- int u = (unsigned char)IFOGlobal->pgcs[0].clut[i][3] - 128;
- int v = (unsigned char)IFOGlobal->pgcs[0].clut[i][2] - 128;
- int y = 76309 * ((unsigned char)IFOGlobal->pgcs[0].clut[i][1] - 16);
-
- palette[i][0] = Clip[(y + crv*v + 32768)>>16];
- palette[i][1] = Clip[(y - cgu*u - cgv*v + 32768)>>16];
- palette[i][2] = Clip[(y + cbu*u + 32786)>>16];
- }
-
- for(int x = 0; x < s->x; x++) {
- for(int y = 0; y < s->y; y++) {
- int X = s->sX + x; /* index into p of this pixel of the subpic */
- int Y = p->h - (s->sY + y);
-
- int pcolor = s->bitmap[y*s->x + x]; /* subpic palette index (0-3) */
-
- int alpha = s->alpha[pcolor];
- if(alpha == 0x0) continue; /* don't waste time on completely transparent pixels */
-
- pcolor = s->colors[pcolor]; /* convert subpic palette index into master palette */
-
- unsigned char color[2][4];
- memcpy(color[0], &p->data[Y * p->w + X], 4); /* original color */
- memcpy(color[1], palette[pcolor], 4); /* real subpic color */
-
- for(int i = 0; i < 3; i++) {
- int x, y;
-
- x = (color[0][i] * (0xFF - alpha)) / 0xFF;
- y = (color[1][i] * alpha) / 0xFF;
-
- color[0][i] = x + y;
- }
-
- p->data[Y * p->w + X] = (color[0][0] << 16) + (color[0][1] << 8) + (color[0][2]);
- }
- }
- }
-
- void subpic_apply(VBitmap *p, i64 tm)
- {
- // tm, current time
- struct subpic_t *s, *next;
- for(s = pics; s; s=next) {
- next=s->next;
-
- if(tm < s->start) continue; /* not yet */
-
- if(tm < s->stop) {
- subpic_apply(p, tm, s);
- continue;
- }
- /* pic done */
- if(s->prev) s->prev->next = s->next;
- if(s->next) s->next->prev = s->prev;
- if(s == pics) pics=pics->next;
- next = s->next;
- free_subpic(s);
- }
- }
-
- /* Parse a buffer into a bufpic. Return -1 on error (can't parse), 0 on incomplete
- * buffer, or 1 on success. On success, the subpic will be in pic; on error, pic
- * may have a bitmap allocated ( != NULL), and should be freed.
- *
- * buf is the actual buffer; bufsiz is its length.
- * tm is the time, in ms, of this packet, from the start of the stream; used for
- * computing start and stop times. */
-
- static int do_subpic_decode(const unsigned char *buf, size_t bufsiz, i64 tm,
- struct subpic_t *pic)
- {
- unsigned dataLen;
-
- if(bufsiz < 4) return -1; /* way too short */
- /* grab the length of the data packet */
- dataLen = (buf[2] << 8) + buf[3];
-
- if(dataLen > bufsiz) return 0; /* incomplete */
-
- /* set default values */
-
- memset(pic, 0, sizeof(struct subpic_t));
- pic->x = pic->y = pic->sX = pic->sY = -1; /* required values */
- pic->start = pic->stop = -1;
-
- /* parse the control packet */
- unsigned offset[2];
- bool control_done = false;
- unsigned i = dataLen;
-
- /* for dupe offset checking */
- unsigned offsets_done[255];
- int offsets_done_n = 0;
-
- while(!control_done) {
- unsigned start = i; /* offset of this sequence */
-
- /* decode each sequence of the control block */
- bool control_sequence_done = false;
-
- if(i+4 >= bufsiz) return -1;
-
- i64 seq_time = 0;
- seq_time += buf[i++] << 8;
- seq_time += buf[i++];
- seq_time *= (27000000.0 / 88.0); /* it's in 1/100ths of a second; we want refrences to
- the 27Mhz MPEG2 reference clock*/
-
- unsigned next = 0; /* offset to the next sequence, or to this one if last */
- next += buf[i++] << 8;
- next += buf[i++];
- if(next > bufsiz) return 0; /* incomplete */
-
- /* if it's the same as this, then this is the last */
- if(next == start) control_done = true;
-
- /* if next is in the data packet, we have a problem */
- if(next < dataLen) {
- _RPT2(0, "Invalid next %u (dataLen %u)\n", next, dataLen);
- return -1;
- }
-
- /* make sure we're not looping */
- for(int n = 0; n < offsets_done_n; n++) {
- if(offsets_done[n] == start) {
- /* oops */
- _RPT1(0, "next %u encountered twice; looping\n", next);
- return -1;
- }
- }
- /* add after the check: it's OK to receive the *current* start as
- * the next (it indicates last packet */
- offsets_done[offsets_done_n++] = start;
-
- while(!control_sequence_done) {
- unsigned char c;
- if(i >= bufsiz) return -1;
- c = buf[i++];
- bool aligned = true;
-
- switch(c) {
- case 1: /* start time */
- pic->start = seq_time + tm;
- break;
-
- case 2: /* stop time */
- pic->stop = seq_time + tm;
- break;
-
- case 3: {
- /* palette */
- if(i+1 >= bufsiz) return -1;
- for(int p = 0; p < 4; p++) {
- pic->colors[3-p] = grab_nibble(buf, i, aligned);
- }
-
- break;
- }
- case 4: {
- /* transparency palette (?) */
- if(i+1 >= bufsiz) return -1;
-
- /* we want 8 bit alpha, not 4, for future features */
- for(int p = 0; p < 4; p++) {
- /* this is a silly way to do this, but I'm drawing a blank
- * as to a cleaner way, and this is accurate, so ... */
- pic->alpha[3-p] = (int)( ((double)grab_nibble(buf, i, aligned) / 15.0) * 255);
- }
-
- break;
- }
- case 5: /* image coordinates */
- /* width */
- int endX, endY;
- if(i+5 >= bufsiz) return -1;
-
- pic->sX = (grab_nibble(buf, i, aligned) << 8) +
- (grab_nibble(buf, i, aligned) << 4) +
- grab_nibble(buf, i, aligned);
-
- endX = (grab_nibble(buf, i, aligned) << 8) +
- (grab_nibble(buf, i, aligned) << 4) +
- grab_nibble(buf, i, aligned);
-
- pic->sY = (grab_nibble(buf, i, aligned) << 8) +
- (grab_nibble(buf, i, aligned) << 4) +
- grab_nibble(buf, i, aligned);
-
- endY = (grab_nibble(buf, i, aligned) << 8) +
- (grab_nibble(buf, i, aligned) << 4) +
- grab_nibble(buf, i, aligned);
-
- pic->x = 1 + endX - pic->sX;
- pic->y = 1 + endY - pic->sY;
-
- break;
- case 0x06: /* image 1 / image 2 offsets */
- if(i+1 >= bufsiz) return -1;
- offset[0] = ((unsigned int)buf[i++]) << 8;
- offset[0] += buf[i++];
- offset[1] = ((unsigned int)buf[i++]) << 8;
- offset[1] += buf[i++];
- break;
- case 0x07: /* unknown; appears to be 3 bytes */
- if(i+2 >= bufsiz) return -1;
- i += 3;
- break;
-
- case 0xff: /* end of sequence */
- control_sequence_done = true;
- break;
-
- default:
- /* we can't continue parsing past unknown commands
- * because we need their length. we can try to display
- * them anyway, hoping we already have sufficient data--
- * if we don't, we'll bail anyway. at least in my test DVDs,
- * control sequences appear generally sorted numerically, so
- * unless we hit c == 0 somehow, we might be OK */
- _RPT1(0, "Unknown subpic command: %2.2x\n", c);
- control_done = control_sequence_done = true;
- }
- }
- i = next;
- }
-
- /* sanity */
- if(pic->x == -1 || pic->y == -1 || pic->sX == -1 || pic->sY == -1 ||
- pic->start == -1 || pic->stop == -1) {
- /* some required piece of data is missing */
- _RPT0(0, "subpic data missing\n");
- return -1;
- }
-
- /* now we can alloc the bitmap */
- pic->bitmap = (unsigned char *)malloc(pic->x * pic->y);
- memset(pic->bitmap, 0, pic->x * pic->y);
-
- int x = 0, y = 0;
- /* note: all Y wrapping is done in twos, since the pic is interlaced.
- * do even.scanlines first, then odd
- */
-
- int state = 0;
- bool aligned = true;
- if(offset[0] >= bufsiz) return -1;
- if(offset[1] >= bufsiz) return -1;
-
- i = offset[state];
- while(1) {
- unsigned int val;
-
- do{
- if(i >= bufsiz) return -1;
- val = grab_nibble(buf, i, aligned);
- if(val >= 0x004) break; /* one-byte */
-
- if(i >= bufsiz) return -1;
- val = (val << 4) + grab_nibble(buf, i, aligned);
- if(val >= 0x010) break; /* two-byte, 1x .. 3x */
-
- if(i >= bufsiz) return -1;
- val = (val << 4) + grab_nibble(buf, i, aligned);
- if(val >= 0x040) break; /* three-byte, 04x .. 0fx */
-
- if(i >= bufsiz) return -1;
- val = (val << 4) + grab_nibble(buf, i, aligned);
- if(val >= 0x100) break; /* four-byte, 01xx .. 03xx */
- /* four-byte, 00xx */
- if(!aligned) {
- if(i >= bufsiz) return -1;
- grab_nibble(buf, i, aligned);
- }
- } while(0);
- int pixNum = val >> 2;
- int color = val & 3;
- if(pixNum == 0) pixNum = pic->x - x; /* line feed */
- if(pixNum + x > pic->x)
- pixNum = pic->x - x; /* shouldn't happen: cap pixNum */
-
- while(pixNum--) {
- pic->bitmap[y * pic->x + x] = color;
- x++;
- if(x == pic->x) {
- /* wrap */
- x = 0;
- y += 2;
- if(y > pic->y) break;
- }
- }
- if(y >= pic->y) {
- /* hit the bottom */
- state++;
- if(state == 2) break; /* done */
- /* finished even; do odd */
- y = 1;
- i = offset[state];
- }
- }
-
- return 1;
- }
-
- static unsigned char subPicBuf[64000];
- static int subPicLen = 0;
-
- int subpic_decode(const unsigned char *buf, size_t bufsiz, i64 tm)
- {
- struct subpic_t *pic = new struct subpic_t;
- static i64 start_time;
-
- pic->bitmap = NULL;
-
- memmove(subPicBuf+subPicLen, buf, bufsiz);
- subPicLen += bufsiz;
-
- if(tm!=0)
- start_time=tm;
- int ret = do_subpic_decode(subPicBuf, subPicLen, start_time, pic);
-
- if(ret == -1 || ret > 0) {
- if(ret == -1)
- int z = 1; /* debug breakpoint target */
- subPicLen = 0; /* error or success; clear the buffer */
- }
- if(ret <= 0) {
- /* error or incomplete */
- if(pic->bitmap != NULL) free(pic->bitmap);
- delete pic;
-
- return ret;
- }
-
- /* success; store it */
- pic->next = pics;
- pic->prev = NULL;
- if(pic->next) pic->next->prev = pic;
-
- pics = pic;
-
- return ret;
- }
-
-