home *** CD-ROM | disk | FTP | other *** search
- /*
- * Subpic.cpp: subpic decoding
- *
- * Copyright (C) Glenn Maynard - May 2000 - glennm@mediaone.net
- *
- * With the help of Helmet (ezrd@hotmail.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\config.h"
- #include "..\video\global.h"
- }
-
- class subpic_t {
- public:
- subpic_t(){
- bitmap = NULL;
- frRecons = NULL;
- frReconsOut = NULL;
- }
- ~subpic_t(){
- if(bitmap) delete []bitmap;
- if(frRecons) delete frRecons;
- if(frReconsOut) delete frReconsOut;
- }
-
- unsigned char *bitmap;
- int x, y; /* dimensions */
- int sX, sY; /* coordinates */
- int start, stop; /* start and stop time */
-
- CFrame *frRecons; /* reconstructed picture */
- CFrame *frReconsOut; /* reconstructed picture in output format */
-
- unsigned char colors[4]; /* palette */
- unsigned char alpha[4]; /* alpha channel */
- class subpic_t *next, *prev;
- };
-
- #include "..\runstate.h"
- extern TRunState rs;
- static class 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 class subpic_t *free_subpic(class subpic_t *s)
- {
- class subpic_t *next;
-
- if(s == NULL) return NULL;
-
- next = s->next;
-
- if(s->prev) s->prev->next = s->next;
- if(s->next) s->next->prev = s->prev;
- if(s == pics) pics=pics->next;
-
- delete s;
-
- return next;
- }
-
- /* free all subpics */
- void subpic_free(void)
- {
- while(pics) pics=free_subpic(pics);
-
- pics = NULL;
- }
-
- /* subpic init */
- /* A.V. modif */
- static unsigned char palette[16][4];
- static unsigned char yuvpalette[16][4];
-
- void subpic_init( unsigned char (*clut)[16][4] )
- {
- // Work out RGB palette from IFO YUV palette
- for(int i = 0; i < 16; i++) {
- // Copy the palette
- palette[i][0] = (unsigned char)(*clut)[i][0];
- palette[i][1] = (unsigned char)(*clut)[i][1];
- palette[i][2] = (unsigned char)(*clut)[i][2];
- palette[i][3] = (unsigned char)(*clut)[i][3];
- }
- }
-
-
- static void subpic_apply(CFrame *fr, int tm, class subpic_t *s)
- {
- // Apply subpic as an overlay. The y coordinate has bottom as origin in subpic.
- if(!s->frReconsOut)
- {
- s->frReconsOut = new CFrame;
- s->frReconsOut->Set( s->x, s->y, FRAME_YV12A );
- s->frReconsOut->SetFrame( s->frRecons );
- }
-
- fr->Overlay( s->sX, s->sY, s->frReconsOut );
- }
-
- void subpic_apply(CFrame *fr, int tm)
- {
- class 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(fr, tm, s);
- continue;
- }
-
- /* pic done */
- 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, int tm,
- class 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 */
- 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;
-
- int seq_time = 0;
- seq_time += buf[i++] << 8;
- seq_time += buf[i++];
-
- #define SUB_PRECISION 88.0
- /* subpic timing is in 1/SUB_PRECISION of a second; convert to
- * ms */
-
- seq_time = (int) (((double)seq_time / SUB_PRECISION) * 1000.0);
-
- /* convert PTS to it's in 1/SUB_PRECISION of a second; we want references to
- * the 27Mhz MPEG2 reference clock */
- // seq_time *= 27000;
-
- 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;
- _RPT1(0, "Subpic starts on %x\n", pic->start);
- break;
-
- case 2: /* stop time */
- pic->stop = seq_time + tm;
- break;
-
- case 3: {
- /* palette */
- if(i+1 >= bufsiz) return -1;
- for(int p = 3; p >= 0; p--) {
- pic->colors[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 = 3; p >= 0; p--) {
- pic->alpha[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 = new unsigned char[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 short 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 we are here, we have to run till the end of the line
- with the color in the low nibble of val. Setup val accordingly */
- val &= 3;
- val |= ((pic->x - x)<<2);
-
- } 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(!aligned) {
- if(i >= bufsiz) return -1;
- grab_nibble(buf, i, aligned);
- }
- }
- }
- if(y >= pic->y) {
- /* hit the bottom */
- state++;
- if(state == 2) break; /* done */
- /* finished even; do odd */
- y = 1;
- i = offset[state];
- }
- }
-
- return 1;
- }
-
- /* subpics often have lots of unused blank space on the borders.
- * remove it. */
- static void subpic_squeeze(class subpic_t *s)
- {
- int newX = 0, newY = 0,
- newSX = s->sX, newSY = s->sY; /* newX and newY is the new size; newSX and newSY
- * are the offsets from the top and left of the old
- * image */
- bool nontrans = false;
- int x, y;
- for(y = 0; y < s->y; y++) {
- for(x = 0; x < s->x; x++) {
- int pcolor = s->bitmap[y*s->x + x]; /* subpic palette index (0-3) */
- if(s->alpha[pcolor] != 0x0) {
- nontrans = true;
- break;
- }
- }
- if(nontrans) break;
- }
- newSY = y;
-
- /* bottom */
- nontrans = false;
- for(y = s->y-1; y >= newSY; y--) {
- for(x = 0; x < s->x; x++) {
- int pcolor = s->bitmap[y*s->x + x]; /* subpic palette index (0-3) */
- if(s->alpha[pcolor] != 0x0) {
- nontrans = true;
- break;
- }
- }
- if(nontrans) break;
- }
- newY = y - newSY + 1;
-
- /* left */
- nontrans = false;
- for(x = 0; x < s->x; x++) {
- for(y = 0; y < s->y; y++) {
- int pcolor = s->bitmap[y*s->x + x]; /* subpic palette index (0-3) */
- if(s->alpha[pcolor] != 0x0) {
- nontrans = true;
- break;
- }
- }
- if(nontrans) break;
- }
-
- newSX = x;
-
- /* right */
- nontrans = false;
- for(x = s->x-1; x > newSX; x--) {
- for(y = 0; y < s->y; y++) {
- int pcolor = s->bitmap[y*s->x + x]; /* subpic palette index (0-3) */
- if(s->alpha[pcolor] != 0x0) {
- nontrans = true;
- break;
- }
- }
- if(nontrans) break;
- }
- newX = x - newSX + 1;
-
- /* make sure the pixel count is even */
- if(newX==s->x) newX = newX%2 ? newX-1 : newX;
- if(newY==s->y) newY = newY%2 ? newY-1 : newY;
-
- newX = newX%2 ? newX+1 : newX;
- newY = newY%2 ? newY+1 : newY;
-
- /* recons new image */
- /* This stuff is deleted in subpic_free() */
- TYUVImage yuv;
- s->frRecons = new CFrame;
- s->frRecons->Set( newX, newY, FRAME_YUV444A );
-
- s->frRecons->GetYuvInfo( &yuv );
-
-
- unsigned char *yu = yuv.y;
- unsigned char *u = yuv.u;
- unsigned char *v = yuv.v;
- unsigned char *a = yuv.a;
-
- for(int j = 0; j < newY; j++) {
- for(int i = 0; i < newX; i++) {
- int pcolor = s->bitmap[(newSY+j)*s->x+(newSX+i)];
-
- *a++ = s->alpha[pcolor];
-
- pcolor = s->colors[pcolor]; /* convert subpic palette index into master palette */
-
- *yu++ = palette[pcolor][1] ;
- *u++ = palette[pcolor][2] ;
- *v++ = palette[pcolor][3];
- }
- }
-
- s->x = newX;
- s->y = newY;
- s->sX += newSX;
- s->sY += newSY;
-
- delete []s->bitmap;
- s->bitmap = NULL;
- }
-
- static unsigned char subPicBuf[64000];
- static int subPicLen = 0;
-
- int subpic_decode(const unsigned char *buf, size_t bufsiz, int tm)
- {
- class subpic_t *pic = new class subpic_t;
- static int start_time = 0;
-
- pic->bitmap = NULL;
-
- if(subPicLen + bufsiz >= 64000) {
- /* overflow */
- bufsiz = 0;
- return -1;
- }
-
- if(!start_time && !tm) {
- /* this isn't a start packet, and we don't have one yet; it's
- * mid-packet. discard it. */
- return -1;
- }
-
- if(tm)
- {
- start_time=tm;
- subPicLen = 0;
- }
- _RPT1(0, "? Subpic starts on %x\n", tm);
- memmove(subPicBuf+subPicLen, buf, bufsiz);
- subPicLen += bufsiz;
-
- 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 */
- delete pic;
-
- return ret;
- }
-
- /* success; store it */
- start_time = 0;
- subpic_squeeze(pic);
-
- pic->next = pics;
- pic->prev = NULL;
- if(pic->next) pic->next->prev = pic;
-
- pics = pic;
-
- return ret;
- }
-
-