diff options
author | Mike Melanson <mike@multimedia.cx> | 2002-05-27 21:17:57 +0000 |
---|---|---|
committer | Mike Melanson <mike@multimedia.cx> | 2002-05-27 21:17:57 +0000 |
commit | 5eafaa7d80a1de6da53b5cf53b86427ee537d414 (patch) | |
tree | 81369f57b90c1500b90c71d8b6410d4511a677d8 | |
parent | 076c86cb284e7f18d13b66b49711d16f8080d791 (diff) | |
download | xine-lib-5eafaa7d80a1de6da53b5cf53b86427ee537d414.tar.gz xine-lib-5eafaa7d80a1de6da53b5cf53b86427ee537d414.tar.bz2 |
initial commit for Dr. Tim's RoQ video decoder
CVS patchset: 1955
CVS date: 2002/05/27 21:17:57
-rw-r--r-- | src/libxinevdec/Makefile.am | 5 | ||||
-rw-r--r-- | src/libxinevdec/roqvideo.c | 502 |
2 files changed, 506 insertions, 1 deletions
diff --git a/src/libxinevdec/Makefile.am b/src/libxinevdec/Makefile.am index 76a489dbe..31f7256d4 100644 --- a/src/libxinevdec/Makefile.am +++ b/src/libxinevdec/Makefile.am @@ -6,7 +6,7 @@ LIBTOOL = $(SHELL) $(top_builddir)/libtool-nofpic libdir = $(XINE_PLUGINDIR) lib_LTLIBRARIES = xineplug_decode_cinepak.la xineplug_decode_cyuv.la \ - xineplug_decode_msvc.la + xineplug_decode_msvc.la xineplug_decode_roqvideo.la xineplug_decode_cinepak_la_SOURCES = cinepak.c xineplug_decode_cinepak_la_LDFLAGS = -avoid-version -module @@ -17,6 +17,9 @@ xineplug_decode_cyuv_la_LDFLAGS = -avoid-version -module xineplug_decode_msvc_la_SOURCES = msvc.c xineplug_decode_msvc_la_LDFLAGS = -avoid-version -module +xineplug_decode_roqvideo_la_SOURCES = roqvideo.c +xineplug_decode_roqvideo_la_LDFLAGS = -avoid-version -module + debug: @$(MAKE) CFLAGS="$(DEBUG_CFLAGS)" diff --git a/src/libxinevdec/roqvideo.c b/src/libxinevdec/roqvideo.c new file mode 100644 index 000000000..4391c4ff1 --- /dev/null +++ b/src/libxinevdec/roqvideo.c @@ -0,0 +1,502 @@ +/* This is the standard xine header: */ +/* + * Copyright (C) 2000-2002 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: roqvideo.c,v 1.1 2002/05/27 21:17:57 tmmm Exp $ + */ + +/* And this is the header that came with the RoQ video decoder: */ +/* ------------------------------------------------------------------------ + * Id Software's RoQ video file format decoder + * + * Dr. Tim Ferguson, 2001. + * For more details on the algorithm: + * http://www.csse.monash.edu.au/~timf/videocodec.html + * + * This is a simple decoder for the Id Software RoQ video format. In + * this format, audio samples are DPCM coded and the video frames are + * coded using motion blocks and vector quantisation. + * + * Note: All information on the RoQ file format has been obtained through + * pure reverse engineering. This was achieved by giving known input + * audio and video frames to the roq.exe encoder and analysing the + * resulting output text and RoQ file. No decompiling of the Quake III + * Arena game was required. + * + * You may freely use this source code. I only ask that you reference its + * source in your projects documentation: + * Tim Ferguson: http://www.csse.monash.edu.au/~timf/ + * ------------------------------------------------------------------------ */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#include "video_out.h" +#include "buffer.h" +#include "xine_internal.h" +#include "xineutils.h" + +#define VIDEOBUFSIZE 128*1024 + +#define RoQ_INFO 0x1001 +#define RoQ_QUAD_CODEBOOK 0x1002 +#define RoQ_QUAD_VQ 0x1011 +#define RoQ_SOUND_MONO 0x1020 +#define RoQ_SOUND_STEREO 0x1021 + +#define RoQ_ID_MOT 0x00 +#define RoQ_ID_FCC 0x01 +#define RoQ_ID_SLD 0x02 +#define RoQ_ID_CCC 0x03 + +#define get_byte(in_buffer) *(in_buffer++) +#define get_word(in_buffer) ((unsigned short)(in_buffer += 2, \ + (in_buffer[-1] << 8 | in_buffer[-2]))) +#define get_long(in_buffer) ((unsigned long)(in_buffer += 4, \ + (in_buffer[-1] << 24 | in_buffer[-2] << 16 | in_buffer[-3] << 8 | in_buffer[-4]))) + +typedef struct { + unsigned char y0, y1, y2, y3, u, v; +} roq_cell; + +typedef struct { + int idx[4]; +} roq_qcell; + +typedef struct roq_decoder_s { + video_decoder_t video_decoder; + + vo_instance_t *video_out; + int video_step; + int skipframes; + unsigned char *buf; + int bufsize; + int size; + int width; + int height; + + roq_cell cells[256]; + roq_qcell qcells[256]; + long roq_start, aud_pos, vid_pos; + long *frame_offset; + unsigned long num_frames, num_audio_bytes; + unsigned char *y[2], *u[2], *v[2]; +} roq_decoder_t; + +static void apply_vector_2x2(roq_decoder_t *ri, int x, int y, roq_cell *cell) { + unsigned char *yptr; + + yptr = ri->y[0] + (y * ri->width) + x; + *yptr++ = cell->y0; + *yptr++ = cell->y1; + yptr += (ri->width - 2); + *yptr++ = cell->y2; + *yptr++ = cell->y3; + ri->u[0][(y/2) * (ri->width/2) + x/2] = cell->u; + ri->v[0][(y/2) * (ri->width/2) + x/2] = cell->v; +} + +static void apply_vector_4x4(roq_decoder_t *ri, int x, int y, roq_cell *cell) { + unsigned long row_inc, c_row_inc; + register unsigned char y0, y1, u, v; + unsigned char *yptr, *uptr, *vptr; + + yptr = ri->y[0] + (y * ri->width) + x; + uptr = ri->u[0] + (y/2) * (ri->width/2) + x/2; + vptr = ri->v[0] + (y/2) * (ri->width/2) + x/2; + + row_inc = ri->width - 4; + c_row_inc = (ri->width/2) - 2; + *yptr++ = y0 = cell->y0; *uptr++ = u = cell->u; *vptr++ = v = cell->v; + *yptr++ = y0; + *yptr++ = y1 = cell->y1; *uptr++ = u; *vptr++ = v; + *yptr++ = y1; + + yptr += row_inc; + + *yptr++ = y0; + *yptr++ = y0; + *yptr++ = y1; + *yptr++ = y1; + + yptr += row_inc; uptr += c_row_inc; vptr += c_row_inc; + + *yptr++ = y0 = cell->y2; *uptr++ = u; *vptr++ = v; + *yptr++ = y0; + *yptr++ = y1 = cell->y3; *uptr++ = u; *vptr++ = v; + *yptr++ = y1; + + yptr += row_inc; + + *yptr++ = y0; + *yptr++ = y0; + *yptr++ = y1; + *yptr++ = y1; +} + +static void apply_motion_4x4(roq_decoder_t *ri, int x, int y, unsigned char mv, + char mean_x, char mean_y) +{ + int i, mx, my; + unsigned char *pa, *pb; + + mx = x + 8 - (mv >> 4) - mean_x; + my = y + 8 - (mv & 0xf) - mean_y; + + pa = ri->y[0] + (y * ri->width) + x; + pb = ri->y[1] + (my * ri->width) + mx; + for(i = 0; i < 4; i++) { + pa[0] = pb[0]; + pa[1] = pb[1]; + pa[2] = pb[2]; + pa[3] = pb[3]; + pa += ri->width; + pb += ri->width; + } + + pa = ri->u[0] + (y/2) * (ri->width/2) + x/2; + pb = ri->u[1] + (my/2) * (ri->width/2) + (mx + 1)/2; + for(i = 0; i < 2; i++) { + pa[0] = pb[0]; + pa[1] = pb[1]; + pa += ri->width/2; + pb += ri->width/2; + } + + pa = ri->v[0] + (y/2) * (ri->width/2) + x/2; + pb = ri->v[1] + (my/2) * (ri->width/2) + (mx + 1)/2; + for(i = 0; i < 2; i++) { + pa[0] = pb[0]; + pa[1] = pb[1]; + pa += ri->width/2; + pb += ri->width/2; + } +} + +static void apply_motion_8x8(roq_decoder_t *ri, int x, int y, + unsigned char mv, char mean_x, char mean_y) { + + int mx, my, i; + unsigned char *pa, *pb; + + mx = x + 8 - (mv >> 4) - mean_x; + my = y + 8 - (mv & 0xf) - mean_y; + + pa = ri->y[0] + (y * ri->width) + x; + pb = ri->y[1] + (my * ri->width) + mx; + for(i = 0; i < 8; i++) { + pa[0] = pb[0]; + pa[1] = pb[1]; + pa[2] = pb[2]; + pa[3] = pb[3]; + pa[4] = pb[4]; + pa[5] = pb[5]; + pa[6] = pb[6]; + pa[7] = pb[7]; + pa += ri->width; + pb += ri->width; + } + + pa = ri->u[0] + (y/2) * (ri->width/2) + x/2; + pb = ri->u[1] + (my/2) * (ri->width/2) + (mx + 1)/2; + for(i = 0; i < 4; i++) { + pa[0] = pb[0]; + pa[1] = pb[1]; + pa[2] = pb[2]; + pa[3] = pb[3]; + pa += ri->width/2; + pb += ri->width/2; + } + + pa = ri->v[0] + (y/2) * (ri->width/2) + x/2; + pb = ri->v[1] + (my/2) * (ri->width/2) + (mx + 1)/2; + for(i = 0; i < 4; i++) { + pa[0] = pb[0]; + pa[1] = pb[1]; + pa[2] = pb[2]; + pa[3] = pb[3]; + pa += ri->width/2; + pb += ri->width/2; + } +} + +static void roq_decode_frame(roq_decoder_t *ri, vo_frame_t *img) { + unsigned int chunk_id = 0, chunk_arg = 0; + unsigned long chunk_size = 0; + int i, j, k, nv1, nv2, vqflg = 0, vqflg_pos = -1; + int vqid, bpos, xpos, ypos, xp, yp, x, y; + int frame_stats[2][4] = {{0},{0}}; + roq_qcell *qcell; + unsigned char *buf = ri->buf; + unsigned char *buf_end = ri->buf + ri->size; + + while (buf < buf_end) { + chunk_id = get_word(buf); + chunk_size = get_long(buf); + chunk_arg = get_word(buf); +//printf (" type %X, %lX bytes\n", chunk_id, chunk_size); + if(chunk_id == RoQ_QUAD_VQ) + break; + if(chunk_id == RoQ_QUAD_CODEBOOK) { + if((nv1 = chunk_arg >> 8) == 0) + nv1 = 256; + if((nv2 = chunk_arg & 0xff) == 0 && nv1 * 6 < chunk_size) + nv2 = 256; + for(i = 0; i < nv1; i++) { + ri->cells[i].y0 = get_byte(buf); + ri->cells[i].y1 = get_byte(buf); + ri->cells[i].y2 = get_byte(buf); + ri->cells[i].y3 = get_byte(buf); + ri->cells[i].u = get_byte(buf); + ri->cells[i].v = get_byte(buf); + } + for(i = 0; i < nv2; i++) + for(j = 0; j < 4; j++) + ri->qcells[i].idx[j] = get_byte(buf); + } + } + + bpos = xpos = ypos = 0; + while(bpos < chunk_size) { + for (yp = ypos; yp < ypos + 16; yp += 8) + for (xp = xpos; xp < xpos + 16; xp += 8) { + if (vqflg_pos < 0) { + vqflg = buf[bpos++]; vqflg |= (buf[bpos++] << 8); + vqflg_pos = 7; + } + vqid = (vqflg >> (vqflg_pos * 2)) & 0x3; + frame_stats[0][vqid]++; + vqflg_pos--; + + switch(vqid) { + case RoQ_ID_MOT: + break; + case RoQ_ID_FCC: + apply_motion_8x8(ri, xp, yp, buf[bpos++], chunk_arg >> 8, + chunk_arg & 0xff); + break; + case RoQ_ID_SLD: + qcell = ri->qcells + buf[bpos++]; + apply_vector_4x4(ri, xp, yp, ri->cells + qcell->idx[0]); + apply_vector_4x4(ri, xp+4, yp, ri->cells + qcell->idx[1]); + apply_vector_4x4(ri, xp, yp+4, ri->cells + qcell->idx[2]); + apply_vector_4x4(ri, xp+4, yp+4, ri->cells + qcell->idx[3]); + break; + case RoQ_ID_CCC: + for (k = 0; k < 4; k++) { + x = xp; y = yp; + if(k & 0x01) x += 4; + if(k & 0x02) y += 4; + + if (vqflg_pos < 0) { + vqflg = buf[bpos++]; + vqflg |= (buf[bpos++] << 8); + vqflg_pos = 7; + } + vqid = (vqflg >> (vqflg_pos * 2)) & 0x3; + frame_stats[1][vqid]++; + vqflg_pos--; + switch(vqid) { + case RoQ_ID_MOT: + break; + case RoQ_ID_FCC: + apply_motion_4x4(ri, x, y, buf[bpos++], chunk_arg >> 8, + chunk_arg & 0xff); + break; + case RoQ_ID_SLD: + qcell = ri->qcells + buf[bpos++]; + apply_vector_2x2(ri, x, y, ri->cells + qcell->idx[0]); + apply_vector_2x2(ri, x+2, y, ri->cells + qcell->idx[1]); + apply_vector_2x2(ri, x, y+2, ri->cells + qcell->idx[2]); + apply_vector_2x2(ri, x+2, y+2, ri->cells + qcell->idx[3]); + break; + case RoQ_ID_CCC: + apply_vector_2x2(ri, x, y, ri->cells + buf[bpos]); + apply_vector_2x2(ri, x+2, y, ri->cells + buf[bpos+1]); + apply_vector_2x2(ri, x, y+2, ri->cells + buf[bpos+2]); + apply_vector_2x2(ri, x+2, y+2, ri->cells + buf[bpos+3]); + bpos += 4; + break; + } + } + break; + default: + printf("Unknown vq code: %d\n", vqid); + } + } + + xpos += 16; + if (xpos >= ri->width) { + xpos -= ri->width; + ypos += 16; + } + if(ypos >= ri->height) + break; + } + + /* there has to be a more efficient way to do the next 2 copy blocks */ + + /* preserve the planes for motion compensation in the next frame decode */ + memcpy(ri->y[1], ri->y[0], ri->width * ri->height); + memcpy(ri->u[1], ri->u[0], (ri->width * ri->height)/4); + memcpy(ri->v[1], ri->v[0], (ri->width * ri->height)/4); + + /* copy the planes to the output planes */ + memcpy(img->base[0], ri->y[0], ri->width * ri->height); + memcpy(img->base[1], ri->u[0], (ri->width * ri->height)/4); + memcpy(img->base[2], ri->v[0], (ri->width * ri->height)/4); +} + +static int roq_can_handle (video_decoder_t *this_gen, int buf_type) { + return (buf_type == BUF_VIDEO_ROQ); +} + +static void roq_init (video_decoder_t *this_gen, vo_instance_t *video_out) { + roq_decoder_t *this = (roq_decoder_t *) this_gen; + +printf("roq_init\n"); + this->video_out = video_out; + this->buf = NULL; +} + +static void roq_decode_data (video_decoder_t *this_gen, + buf_element_t *buf) { + + roq_decoder_t *this = (roq_decoder_t *) this_gen; + vo_frame_t *img; /* video out frame */ + +//printf("roq_decode_data\n"); + if (buf->decoder_flags & BUF_FLAG_PREVIEW) + return; + + if (buf->decoder_flags & BUF_FLAG_HEADER) { /* need to initialize */ + this->video_out->open (this->video_out); + + if(this->buf) + free(this->buf); + + this->buf = xine_xmalloc(VIDEOBUFSIZE); + this->bufsize = VIDEOBUFSIZE; + this->size = 0; + this->width = (buf->content[0] << 8) | buf->content[1]; + this->height = (buf->content[2] << 8) | buf->content[3]; + this->skipframes = 0; + this->video_step = buf->decoder_info[1]; + + this->y[0] = xine_xmalloc(this->width * this->height); + this->y[1] = xine_xmalloc(this->width * this->height); + this->u[0] = xine_xmalloc((this->width * this->height) / 4); + this->u[1] = xine_xmalloc((this->width * this->height) / 4); + this->v[0] = xine_xmalloc((this->width * this->height) / 4); + this->v[1] = xine_xmalloc((this->width * this->height) / 4); + + return; + } + + if( this->size + buf->size > this->bufsize ) { + this->bufsize = this->size + 2 * buf->size; + printf("RoQ: increasing source buffer to %d to avoid overflow.\n", + this->bufsize); + this->buf = realloc( this->buf, this->bufsize ); + } + + xine_fast_memcpy (&this->buf[this->size], buf->content, buf->size); + this->size += buf->size; + + if (buf->decoder_flags & BUF_FLAG_FRAMERATE) + this->video_step = buf->decoder_info[0]; + + if (buf->decoder_flags & BUF_FLAG_FRAME_END) { /* time to decode a frame */ + img = this->video_out->get_frame (this->video_out, this->width, + this->height, XINE_ASPECT_RATIO_SQUARE, IMGFMT_YV12, + VO_BOTH_FIELDS); + + img->pts = buf->pts; + img->duration = this->video_step; + + roq_decode_frame(this, img); + + this->skipframes = img->draw(img); + if( this->skipframes < 0 ) + this->skipframes = 0; + img->free(img); + + this->size = 0; + } +} + +static void roq_flush (video_decoder_t *this_gen) { +} + +static void roq_reset (video_decoder_t *this_gen) { +} + +static void roq_close (video_decoder_t *this_gen) { + + roq_decoder_t *this = (roq_decoder_t *) this_gen; + + this->video_out->close(this->video_out); + + free(this->y[0]); + free(this->y[1]); + free(this->u[0]); + free(this->u[1]); + free(this->v[0]); + free(this->v[1]); + free(this); +} + +static char *roq_get_id(void) { + return "RoQ Video"; +} + +static void roq_dispose (video_decoder_t *this_gen) { + free (this_gen); +} + +video_decoder_t *init_video_decoder_plugin (int iface_version, xine_t *xine) { + + roq_decoder_t *this ; + + if (iface_version != 9) { + printf( "RoQ: plugin doesn't support plugin API version %d.\n" + "RoQ: this means there's a version mismatch between xine and this " + "RoQ: decoder plugin.\nInstalling current plugins should help.\n", + iface_version); + return NULL; + } + + this = (roq_decoder_t *) malloc (sizeof (roq_decoder_t)); + + this->video_decoder.interface_version = iface_version; + this->video_decoder.can_handle = roq_can_handle; + this->video_decoder.init = roq_init; + this->video_decoder.decode_data = roq_decode_data; + this->video_decoder.flush = roq_flush; + this->video_decoder.reset = roq_reset; + this->video_decoder.close = roq_close; + this->video_decoder.get_identifier = roq_get_id; + this->video_decoder.dispose = roq_dispose; + this->video_decoder.priority = 1; + + return (video_decoder_t *) this; +} |