diff options
Diffstat (limited to 'src/libspudec/spudec_old.c')
-rw-r--r-- | src/libspudec/spudec_old.c | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/src/libspudec/spudec_old.c b/src/libspudec/spudec_old.c new file mode 100644 index 000000000..9b61d0e01 --- /dev/null +++ b/src/libspudec/spudec_old.c @@ -0,0 +1,612 @@ + +static spudec_globals gSpudec; + +clut_t *palette[4] = { + NULL, NULL, NULL, NULL +}; + +uint8_t alpha[4] = { + 0xff, 0x00, 0x00, 0x00 +}; + +static clut_t* default_clut = (clut_t*) default_palette; + +/* Maximum packets we can keep in the queue. Should be fine */ +#define MAX_PACKETS 200 + +typedef struct { + unsigned char *packet; /* The actual packet of data */ + uint32_t pts; /* The PTS of the packet */ + uint16_t size; /* The packet size */ +} spudec_packet; + +/* Implement the SPU packet queue as a ring queuefer */ +spudec_packet* spudec_packet_queue[MAX_PACKETS]; +int16_t spudec_queue_size = 0; /* Queue length (items) */ +int16_t spudec_queue_pos = 0; /* Start of queue position */ + +/* Forward declarations */ +void spudec_process_packet(unsigned char *packet, int size); + +/* Pushes a packet on the end of the queue */ +void spudec_queue_packet(spudec_packet *packet) { + if(spudec_queue_size + 1 > MAX_PACKETS) { + /* Too many packets */ + printf("spudec: Too many packets.\n"); + return; + } + + spudec_packet_queue[(spudec_queue_pos + spudec_queue_size) % MAX_PACKETS] = packet; + spudec_queue_size++; +} + +/* Gets the next packet but does /not/ remove it */ +spudec_packet* spudec_peek_next_packet() { + if(spudec_queue_size <= 0) { + /* No packet in queue */ + printf("spudec: No more packets.\n"); + return NULL; + } + + return spudec_packet_queue[spudec_queue_pos]; +} + +/* Like peek_next but removes the packet from the queue */ +spudec_packet* spudec_get_next_packet() { + spudec_packet *packet; + + if((packet = spudec_peek_next_packet()) != NULL) { + spudec_queue_pos = (spudec_queue_pos + 1) % MAX_PACKETS; + spudec_queue_size --; + } + + return packet; +} + +#if 0 +void spudec_init(clut_t *clut) { + gSpudec.bInitialised = 1; + + spudec_reset(); + + if(clut == NULL) { + gSpudec.clut = default_clut; + } else { + gSpudec.clut = clut; + } + + palette[0] = palette[1] = palette[2] = palette[3] = &(gSpudec.clut[0]); +} +#endif + +void spudec_tick() +{ + uint32_t pts; + if(!gSpudec.bInitialised) + return; + + pts = metronom_got_spu_packet(0); + + /* See if we have any SPUs queued */ + if(spudec_queue_size != 0) { + spudec_packet *p; + + p = spudec_peek_next_packet(); + if(pts >= p->pts) { + /* Process */ + p = spudec_get_next_packet(); + + spudec_process_packet(p->packet, p->size); + /* Assume it was displayed correctly */ + gSpudec.displayPTS = p->pts; + + free(p->packet); + free(p); + } + } + + if((gSpudec.geom.bIsVisible) && (pts - gSpudec.lifetime >= gSpudec.displayPTS)) { + gSpudec.geom.bIsVisible = 0; + gSpudec.lifetime = 0; + gSpudec.lastPTS = pts; + return; + } + + if(pts < gSpudec.lastPTS) { + /* Something screwey. */ + gSpudec.lastPTS = pts; + return; + } + + gSpudec.lastPTS = pts; +} + +spudec_geometry* spudec_get_geometry() +{ + return &(gSpudec.geom); +} + +int spudec_set_images(vo_image_buffer_t* overlay, + vo_image_buffer_t* mask, + int width, int height, + int format) +{ + if(format != IMGFMT_YV12) { + printf("Error, SPUDEC only supports YV12 overlays.\n"); + gSpudec.bInitialised = 0; + + return 0; + } + + gSpudec.overlay = overlay; + gSpudec.mask = mask; + gSpudec.width = width; + gSpudec.height = height; + gSpudec.format = format; + gSpudec.geom.bIsVisible = 0; + + /* Clear images initially */ + if(gSpudec.format == IMGFMT_YV12) { + /* Set initial image to empty & clear mask */ + memset(mask->mem[0], 0xff, (gSpudec.width*gSpudec.height)); + memset(overlay->mem[0], 0x00, (gSpudec.width*gSpudec.height)); + memset(mask->mem[1], 0xff, (gSpudec.width*gSpudec.height) >> 2); + memset(overlay->mem[1], 0x00, (gSpudec.width >> 1)*(gSpudec.height >> 1)); + memset(mask->mem[2], 0xff, (gSpudec.width >> 1)*(gSpudec.height >> 1)); + memset(overlay->mem[2], 0x00, (gSpudec.width >> 1)*(gSpudec.height >> 1)); + } + + gSpudec.geom.start_col = 0; + gSpudec.geom.end_col = gSpudec.width-1; + gSpudec.geom.start_row = 0; + gSpudec.geom.end_row = gSpudec.height-1; + + return gSpudec.bInitialised = 1; +} + +#define nibble(data, index) ((index & 1) ? data[index >> 1] & 0xf : (data[index >> 1] >> 4) & 0xf) + +void spudec_process_data(unsigned char *data, int size, int d1, int d2) +{ + /* This does the 'hard' work of processing the image data */ + + long off,line_base, line_base2,y,na,nb; + int n; /* The code word */ + + na = d1<<1; + nb = d2<<1; + + /* Align on even row */ + if (gSpudec.geom.start_row & 1) { + gSpudec.geom.start_row--; + gSpudec.geom.end_row--; + } + if (gSpudec.geom.start_col & 1) { + gSpudec.geom.start_col--; + gSpudec.geom.end_col--; + } + y = gSpudec.geom.start_row; + + while(y <= gSpudec.geom.end_row) { + line_base = gSpudec.width * y + gSpudec.geom.start_col; + line_base2 = (gSpudec.width>>1) * (y>>1) + (gSpudec.geom.start_col >> 1); + + off = 0; + do { + int num; + clut_t *col; + + n = nibble(data, na); + na++; + if(n < 0x4) { + n = (n<<4) | nibble(data, na); + na++; + if(n < 0x10) { + n = (n<<4) | nibble(data, na); + na++; + if(n < 0x40) { + n = (n<<4) | nibble(data, na); + na++; + if(n < 0x100) + n = 0; /* Carriage return */ + } + } + } + // printf("Code: 0x%04x\n",n); + + num = n >> 2; col = palette[n & 0x3]; + if(col == NULL) { + printf("Error in palette\n"); + } + + if(num != 0) { + if(alpha[n & 0x3] & 0x80) { + memset(&(gSpudec.mask->mem[0][line_base + off]), 0x00, num); + memset(&(gSpudec.overlay->mem[0][line_base + off]), col->y, num); + memset(&(gSpudec.mask->mem[1][line_base2 + (off>>1)]), 0x00, num>>1); + memset(&(gSpudec.overlay->mem[1][line_base2 + (off>>1)]), col->cr, num>>1); + memset(&(gSpudec.mask->mem[2][line_base2 + (off>>1)]), 0x00, num>>1); + memset(&(gSpudec.overlay->mem[2][line_base2 + (off>>1)]), col->cb, num>>1); + } else { + memset(&(gSpudec.mask->mem[0][line_base + off]), 0xff, num); + memset(&(gSpudec.overlay->mem[0][line_base + off]), 0x00, num); + memset(&(gSpudec.mask->mem[1][line_base2 + (off>>1)]), 0xff, num>>1); + memset(&(gSpudec.overlay->mem[1][line_base2 + (off>>1)]), 0x00, num>>1); + memset(&(gSpudec.mask->mem[2][line_base2 + (off>>1)]), 0xff, num>>1); + memset(&(gSpudec.overlay->mem[2][line_base2 + (off>>1)]), 0x00, num>>1); + } + } + off+=num; + } while((n != 0) && (off <= gSpudec.geom.end_col - gSpudec.geom.start_col)); + + if((n == 0) && (off <= gSpudec.geom.end_col - gSpudec.geom.start_col)) { + /* Clear to end of line if carriage return */ + int len = gSpudec.geom.start_col + gSpudec.geom.end_col - off; + memset(&(gSpudec.mask->mem[0][line_base + off]), 0xff, len); + memset(&(gSpudec.overlay->mem[0][line_base + off]), 0x00, len); + memset(&(gSpudec.mask->mem[0][line_base + off]) + gSpudec.width, 0xff, len); + memset(&(gSpudec.overlay->mem[0][line_base + off]) + gSpudec.width, 0x00, len); + memset(&(gSpudec.mask->mem[1][line_base2 + (off>>1)]), 0xff, len>>1); + memset(&(gSpudec.overlay->mem[1][line_base2 + (off>>1)]), 0x00, len>>1); + memset(&(gSpudec.mask->mem[2][line_base2 + (off>>1)]), 0xff, len>>1); + memset(&(gSpudec.overlay->mem[2][line_base2 + (off>>1)]), 0x00, len>>1); + } + + if((na & 1)) + na ++; /* Re-align */ + + line_base += gSpudec.width; + y++; + if (y > gSpudec.geom.end_row) + break; + + off = 0; + do { + int num; + clut_t *col; + + n = nibble(data, nb); + nb++; + if(n < 0x4) { + n = (n<<4) | nibble(data, nb); + nb++; + if(n < 0x10) { + n = (n<<4) | nibble(data, nb); + nb++; + if(n < 0x40) { + n = (n<<4) | nibble(data, nb); + nb++; + if(n < 0x100) + n = 0; /* Carriage return */ + } + } + } + // printf("Code: 0x%04x\n",n); + + num = n >> 2; col = palette[n & 0x3]; + if(col == NULL) { + printf("Error in palette\n"); + } + + if(num != 0) { + if(alpha[n & 0x3] & 0x80) { + memset(&(gSpudec.mask->mem[0][line_base + off]), 0x00, num); + memset(&(gSpudec.overlay->mem[0][line_base + off]), col->y, num); + } else { + memset(&(gSpudec.mask->mem[0][line_base + off]), 0xff, num); + memset(&(gSpudec.overlay->mem[0][line_base + off]), 0x00, num); + } + } + off+=num; + } while((n != 0) && (off <= gSpudec.geom.end_col - gSpudec.geom.start_col)); + + if((n == 0) && (off <= gSpudec.geom.end_col - gSpudec.geom.start_col)) { + /* Clear to end of line if carriage return */ + int len = gSpudec.geom.start_col + gSpudec.geom.end_col - off; + memset(&(gSpudec.mask->mem[0][line_base + off]), 0xff, len); + memset(&(gSpudec.overlay->mem[0][line_base + off]), 0x00, len); + } + + if((nb & 1)) + nb ++; /* Re-align */ + + y++; + } +} + +void spudec_process_control(unsigned char *control, int size, int* d1, int* d2) +{ + int off = 2; + int a,b; /* Temporary vars */ + + do { + int type = control[off]; + off++; + + switch(type) { + case 0x00: + /* Menu ID, 1 byte */ + break; + case 0x01: + /* Start display */ + gSpudec.geom.bIsVisible = 1; + break; + case 0x03: + /* Palette */ + 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 */ + 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]; + + 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]; + off+=4; + break; + case 0xff: + /* All done, bye-bye */ + return; + break; + default: + printf("spudec: Error determining control type 0x%02x.\n",type); + return; + break; + } + + /* printf("spudec: Processsed control type 0x%02x.\n",type); */ + } while(off < size); +} + +void spudec_process_packet(unsigned char *packet, int size) +{ + int x0, x1; + int d1, d2; + + /* Check packet */ + if((packet[0] << 8) + packet[1] != size) { + printf("Packet size mismatch:\n"); + printf("Packet reports size 0x%04x\n", (packet[0] << 8) + packet[1]); + printf("I reckon 0x%04x\n", size); + return; + } + + 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; + } + + /* End sequence, FIXME: why do we need the division by 2? */ + gSpudec.lifetime = (metronom_get_video_rate() >> 1)* ((packet[x1]<<8) + packet[x1+1]); + + 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); + } +} + +void spudec_decode(unsigned char *data, int size, uint32_t pts) { + if(!gSpudec.bInitialised) + return; + + if(gSpudec.packet == NULL) { + if(pts != 0) { + /* Allocate a packet buffer */ + gSpudec.packet = xmalloc((data[0] << 8) + data[1]); + gSpudec.pts = pts; + + if(gSpudec.packet == NULL) { + printf("Error allocating packet buffer.\n"); + return; + } + + gSpudec.packet_size = 0; + } else { + printf("spudec: Error, we are half way through a packet I don't know\n"); + } + } + + /* Prevent buffer overruns */ + if(gSpudec.packet_size >= 2) { /* If the /packet/ knows how big it is */ + if((gSpudec.packet_size + size) > + (gSpudec.packet[0]<<8) + gSpudec.packet[1]) { + printf("spudec: Mismatched buffer size (0x%04x to big), truncating.\n", + (gSpudec.packet_size + size) - + ((gSpudec.packet[0]<<8) + gSpudec.packet[1])); + size = (gSpudec.packet[0]<<8) + gSpudec.packet[1] - gSpudec.packet_size; + } + } + + + memcpy(gSpudec.packet + gSpudec.packet_size, data, size); + gSpudec.packet_size += size; + + if(gSpudec.packet_size >= (gSpudec.packet[0]<<8) + gSpudec.packet[1]) { + /* If packet complete then queue */ + spudec_packet *p; + + p = xmalloc(sizeof(spudec_packet)); + p->packet = gSpudec.packet; + p->size = gSpudec.packet_size; + p->pts = gSpudec.pts; + + spudec_queue_packet(p); + + gSpudec.packet = NULL; + gSpudec.packet_size = -1; + gSpudec.pts = 0; + } +} + +void spudec_reset() { + /* Clear any packet being assembled */ + if(gSpudec.packet != NULL) { + gSpudec.packet_size = 0; + free(gSpudec.packet); + gSpudec.packet = NULL; + gSpudec.pts = 0; + } + + /* Remove any current subtitle */ + gSpudec.geom.bIsVisible = 0; + gSpudec.lifetime = 0; + + /* Clear packet queue */ + while(spudec_queue_size > 0) { + spudec_packet *p = spudec_get_next_packet(); + + free(p->packet); + free(p); + } +} + +void spudec_overlay_yuv (uint8_t *y, uint8_t *u, uint8_t *v) { + + /* + * Mix in SPU + */ + + /* Tick SPUdec */ + spudec_tick(); + + if(gVO.bOverlayImage) { + /* This code is pretty nasty but quite quick which is important here! */ + /* APPROACH: Since 32bit processors hande data, well 32bits at a time, + * we overlay the image word by word rather than byte by byte and then + * tidy up the odd bytes at the end. This effectively cuts the number of + * loop itterations by a factor of 4. */ + + /* FIXME: Optimise for 64bit machines as well? */ + + if((gVO.spu_geom->bIsVisible) && + (gVO.format == IMGFMT_YV12)) { + /* Overlay the image. */ + long i, off; + uint8_t *img_l_8, *ovl_l_8, *msk_l_8; + uint32_t *img_l_32, *ovl_l_32, *msk_l_32; + uint8_t *img_y_8, *ovl_y_8, *msk_y_8; + uint32_t *img_y_32, *ovl_y_32, *msk_y_32; + uint8_t *img_v_8, *ovl_v_8, *msk_v_8; + uint32_t *img_v_32, *ovl_v_32, *msk_v_32; + + img_l_8 = img->mem[0]; + ovl_l_8 = gVO.overlay_image->mem[0]; + msk_l_8 = gVO.mask_image->mem[0]; + img_l_32 = ((uint32_t*)img->mem[0]); + ovl_l_32 = ((uint32_t*)gVO.overlay_image->mem[0]); + msk_l_32 = ((uint32_t*)gVO.mask_image->mem[0]); + img_y_8 = img->mem[1]; + ovl_y_8 = gVO.overlay_image->mem[1]; + msk_y_8 = gVO.mask_image->mem[1]; + img_y_32 = ((uint32_t*)img->mem[1]); + ovl_y_32 = ((uint32_t*)gVO.overlay_image->mem[1]); + msk_y_32 = ((uint32_t*)gVO.mask_image->mem[1]); + img_v_8 = img->mem[2]; + ovl_v_8 = gVO.overlay_image->mem[2]; + msk_v_8 = gVO.mask_image->mem[2]; + img_v_32 = ((uint32_t*)img->mem[2]); + ovl_v_32 = ((uint32_t*)gVO.overlay_image->mem[2]); + msk_v_32 = ((uint32_t*)gVO.mask_image->mem[2]); + + /* luminance */ + for(i=gVO.width*gVO.spu_geom->start_row; + i<=gVO.width*gVO.spu_geom->end_row; i+=gVO.width) { + /* i is address of begining of line. */ + + /* Firstly, draw the start odd bytes. */ + for(off = i+gVO.spu_geom->start_col; (off & 3) != 0; off++) { + if(msk_l_8[off] != 0xff) { + img_l_8[off] &= msk_l_8[off]; + img_l_8[off] |= ovl_l_8[off]; + } + } + + /* Now words */ + for(; off<=i+gVO.spu_geom->end_col-3; off+=4) { + if(msk_l_32[off>>2] != 0xffffffff) { + img_l_32[off>>2] &= msk_l_32[off>>2]; + img_l_32[off>>2] |= ovl_l_32[off>>2]; + } + } + off -= 4; + + /* Now end odd bytes */ + for(; off<=i+gVO.spu_geom->end_col; off++) { + if(msk_l_8[off] != 0xff) { + img_l_8[off] &= msk_l_8[off]; + img_l_8[off] |= ovl_l_8[off]; + } + } + } + /* colour */ + for(i=(gVO.width>>1)*(gVO.spu_geom->start_row>>1); + i<=(gVO.width>>1)*(gVO.spu_geom->end_row>>1); i+=(gVO.width)>>1) { + /* i is address of begining of line. */ + + /* Firstly, draw the start odd bytes. */ + for(off = i+((gVO.spu_geom->start_col)>>1); (off & 3) != 0; off++) { + if(msk_y_8[off] != 0xff) { + img_y_8[off] &= msk_y_8[off]; + img_y_8[off] |= ovl_y_8[off]; + } + if(msk_v_8[off] != 0xff) { + img_v_8[off] &= msk_v_8[off]; + img_v_8[off] |= ovl_v_8[off]; + } + } + + /* Now words */ + for(; off<=i+((gVO.spu_geom->end_col)>>1)-3; off+=4) { + if(msk_y_32[off>>2] != 0xffffffff) { + img_y_32[off>>2] &= msk_y_32[off>>2]; + img_y_32[off>>2] |= ovl_y_32[off>>2]; + } + if(msk_v_32[off>>2] != 0xffffffff) { + img_v_32[off>>2] &= msk_v_32[off>>2]; + img_v_32[off>>2] |= ovl_v_32[off>>2]; + } + } + off -= 4; + + /* Final end odd bytes */ + for(; off<=i+((gVO.spu_geom->end_col)>>1); off++) { + if(msk_y_8[off] != 0xff) { + img_y_8[off] &= msk_y_8[off]; + img_y_8[off] |= ovl_y_8[off]; + } + if(msk_v_8[off] != 0xff) { + img_v_8[off] &= msk_v_8[off]; + img_v_8[off] |= ovl_v_8[off]; + } + } + } + } + } +} |