/* 2004.02.01 first released source code for IOMP */ /* 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.2 2003/11/25 04:26:07 georgedon 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 #include #include #include #include #include "xine_internal.h" #include "video_out.h" #include "buffer.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 { video_decoder_class_t decoder_class; } roqvideo_class_t; typedef struct roq_decoder_s { video_decoder_t video_decoder; roqvideo_class_t *class; xine_stream_t *stream; int video_step; int skipframes; unsigned char *buf; int bufsize; int size; int width; int height; double ratio; 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]; int y_size; int c_size; unsigned char *cur_y, *cur_u, *cur_v; unsigned char *prev_y, *prev_u, *prev_v; /* this is either 0 or 1 indicating the cur_y points to y[0] or y[1], * same for u and v */ int current_planes; } roqvideo_decoder_t; /************************************************************************** * RoQ video specific decode functions *************************************************************************/ static void apply_vector_2x2(roqvideo_decoder_t *ri, int x, int y, roq_cell *cell) { unsigned char *yptr; yptr = ri->cur_y + (y * ri->width) + x; *yptr++ = cell->y0; *yptr++ = cell->y1; yptr += (ri->width - 2); *yptr++ = cell->y2; *yptr++ = cell->y3; ri->cur_u[(y/2) * (ri->width/2) + x/2] = cell->u; ri->cur_v[(y/2) * (ri->width/2) + x/2] = cell->v; } static void apply_vector_4x4(roqvideo_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->cur_y + (y * ri->width) + x; uptr = ri->cur_u + (y/2) * (ri->width/2) + x/2; vptr = ri->cur_v + (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(roqvideo_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->cur_y + (y * ri->width) + x; pb = ri->prev_y + (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->cur_u + (y/2) * (ri->width/2) + x/2; pb = ri->prev_u + (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->cur_v + (y/2) * (ri->width/2) + x/2; pb = ri->prev_v + (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(roqvideo_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->cur_y + (y * ri->width) + x; pb = ri->prev_y + (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->cur_u + (y/2) * (ri->width/2) + x/2; pb = ri->prev_u + (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->cur_v + (y/2) * (ri->width/2) + x/2; pb = ri->prev_v + (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 roqvideo_decode_frame(roqvideo_decoder_t *ri) { 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); 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: apply_motion_8x8(ri, xp, yp, 0, 8, 8); 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: apply_motion_4x4(ri, x, y, 0, 8, 8); 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; } } /************************************************************************** * xine video plugin functions *************************************************************************/ static void roqvideo_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { roqvideo_decoder_t *this = (roqvideo_decoder_t *) this_gen; vo_frame_t *img; /* video out frame */ if (buf->decoder_flags & BUF_FLAG_PREVIEW) return; if (buf->decoder_flags & BUF_FLAG_HEADER) { /* need to initialize */ this->stream->video_out->open (this->stream->video_out, this->stream); 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->ratio = (double)this->width/(double)this->height; this->skipframes = 0; this->video_step = buf->decoder_info[1]; this->current_planes = 0; this->y_size = this->width * this->height; this->c_size = (this->width * this->height) / 4; this->y[0] = xine_xmalloc(this->y_size); this->y[1] = xine_xmalloc(this->y_size); memset(this->y[0], 0x00, this->y_size); memset(this->y[1], 0x00, this->y_size); this->u[0] = xine_xmalloc(this->c_size); this->u[1] = xine_xmalloc(this->c_size); memset(this->u[0], 0x80, this->c_size); memset(this->u[1], 0x80, this->c_size); this->v[0] = xine_xmalloc(this->c_size); this->v[1] = xine_xmalloc(this->c_size); memset(this->v[0], 0x80, this->c_size); memset(this->v[1], 0x80, this->c_size); /* load the stream/meta info */ this->stream->meta_info[XINE_META_INFO_VIDEOCODEC] = strdup("RoQ VQ Video"); 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->stream->video_out->get_frame (this->stream->video_out, this->width, this->height, this->ratio, XINE_IMGFMT_YV12, VO_BOTH_FIELDS); img->pts = buf->pts; img->duration = this->video_step; if (this->current_planes == 0) { this->cur_y = this->y[0]; this->cur_u = this->u[0]; this->cur_v = this->v[0]; this->prev_y = this->y[1]; this->prev_u = this->u[1]; this->prev_v = this->v[1]; this->current_planes = 1; } else { this->cur_y = this->y[1]; this->cur_u = this->u[1]; this->cur_v = this->v[1]; this->prev_y = this->y[0]; this->prev_u = this->u[0]; this->prev_v = this->v[0]; this->current_planes = 0; } roqvideo_decode_frame(this); xine_fast_memcpy(img->base[0], this->cur_y, this->y_size); xine_fast_memcpy(img->base[1], this->cur_u, this->c_size); xine_fast_memcpy(img->base[2], this->cur_v, this->c_size); this->skipframes = img->draw(img, this->stream); if( this->skipframes < 0 ) this->skipframes = 0; img->free(img); this->size = 0; } } static void roqvideo_flush (video_decoder_t *this_gen) { } static void roqvideo_reset (video_decoder_t *this_gen) { } static void roqvideo_discontinuity (video_decoder_t *this_gen) { } static void roqvideo_dispose (video_decoder_t *this_gen) { roqvideo_decoder_t *this = (roqvideo_decoder_t *) this_gen; this->stream->video_out->close(this->stream->video_out, this->stream); 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_gen); } static video_decoder_t *open_plugin (video_decoder_class_t *class_gen, xine_stream_t *stream) { roqvideo_decoder_t *this ; this = (roqvideo_decoder_t *) xine_xmalloc (sizeof (roqvideo_decoder_t)); this->video_decoder.decode_data = roqvideo_decode_data; this->video_decoder.flush = roqvideo_flush; this->video_decoder.reset = roqvideo_reset; this->video_decoder.discontinuity = roqvideo_discontinuity; this->video_decoder.dispose = roqvideo_dispose; this->size = 0; this->stream = stream; this->class = (roqvideo_class_t *) class_gen; this->buf = NULL; return &this->video_decoder; } static char *get_identifier (video_decoder_class_t *this) { return "RoQ Video"; } static char *get_description (video_decoder_class_t *this) { return "Id RoQ video decoder plugin"; } static void dispose_class (video_decoder_class_t *this) { free (this); } static void *init_plugin (xine_t *xine, void *data) { roqvideo_class_t *this; this = (roqvideo_class_t *) xine_xmalloc (sizeof (roqvideo_class_t)); this->decoder_class.open_plugin = open_plugin; this->decoder_class.get_identifier = get_identifier; this->decoder_class.get_description = get_description; this->decoder_class.dispose = dispose_class; return this; } /* * exported plugin catalog entry */ static uint32_t video_types[] = { BUF_VIDEO_ROQ, 0 }; static decoder_info_t dec_info_video = { video_types, /* supported types */ 1 /* priority */ }; plugin_info_t xine_plugin_info[] = { /* type, API, "name", version, special_info, init_function */ { PLUGIN_VIDEO_DECODER, 15, "roq", XINE_VERSION_CODE, &dec_info_video, init_plugin }, { PLUGIN_NONE, 0, "", 0, NULL, NULL } };