/* 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.4 2002/06/12 12:22:37 f1rmb 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 "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); if (img->copy) { int height = img->height; int stride = img->width; uint8_t* src[3]; src[0] = img->base[0]; src[1] = img->base[1]; src[2] = img->base[2]; while ((height -= 16) >= 0) { img->copy(img, src); src[0] += 16 * stride; src[1] += 4 * stride; src[2] += 4 * stride; } } 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]); } 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; }