Changed all files to the latest version PH3NOM made, supposedly with audio and video sync

master
lerabot 4 years ago
parent 92ed51bf70
commit 4b44c06b8d
  1. 0
      LICENSE.KOS
  2. 83
      Makefile
  3. 213
      README
  4. 345
      dreamroq-player.c
  5. BIN
      dreamroq-player.elf
  6. 595
      dreamroqlib.c
  7. 86
      dreamroqlib.h
  8. BIN
      output.bin
  9. 131
      test-dreamroq.c

@ -1,38 +1,45 @@
# Put the filename of the output binary here
TARGET = dreamroq-player.elf
DIR=$(PWD)
# List all of your C files here, but change the extension to ".o"
OBJS = dreamroq-player.o dreamroqlib.o
all: rm-elf $(TARGET)
include $(KOS_BASE)/Makefile.rules
clean:
-rm -f $(TARGET) $(OBJS)
rm-elf:
-rm -f $(TARGET)
# If you don't need a ROMDISK, then remove "romdisk.o" from the next few
# lines. Also change the -l arguments to include everything you need,
# such as -lmp3, etc.. these will need to go _before_ $(KOS_LIBS)
$(TARGET): $(OBJS)
$(KOS_CC) $(KOS_CFLAGS) $(KOS_LDFLAGS) -o $(TARGET) $(KOS_START) \
$(OBJS) $(OBJEXTRA) $(KOS_LIBS)
cd: $(TARGET) $(OBJS)
#elf transform ---- make sure the *.elf name matches your .elf file.
sh-elf-objcopy -R .stack -O binary $(TARGET) output.bin
#scraming process
$(KOS_BASE)/utils/scramble/scramble output.bin 1ST_READ.bin
#creating iso -> -o outputname.iso
mkisofs -C 0,11702 -V DC_GAME -G IP.BIN -r -J -l -m '*.o' -x $(DIR)/builds -o $(DIR)/builds/$(TARGET).iso $(DIR)
run: $(CD)
lxdream builds/$(TARGET).iso
dist:
rm -f $(OBJS) romdisk.o romdisk.img
$(KOS_STRIP) $(TARGET)
# Put the filename of the output binary here
TARGET = dreamroq-player.elf
PROJECT_NAME = dreamroq_ph3nom
DIR=$(PWD)
# List all of your C files here, but change the extension to ".o"
OBJS = dreamroq-player.o dreamroqlib.o
#AICA Audio Driver
KOS_CFLAGS += -I. -Ilibdcmc/
OBJS += libdcmc/snddrv.o
#PVR Video Driver
OBJS += libdcmc/pvrdrv.o
all: rm-elf $(TARGET)
include $(KOS_BASE)/Makefile.rules
clean:
-rm -f $(TARGET) $(OBJS)
rm-elf:
-rm -f $(TARGET)
# If you don't need a ROMDISK, then remove "romdisk.o" from the next few
# lines. Also change the -l arguments to include everything you need,
# such as -lmp3, etc.. these will need to go _before_ $(KOS_LIBS)
$(TARGET): $(OBJS)
$(KOS_CC) $(KOS_CFLAGS) $(KOS_LDFLAGS) -o $(TARGET) $(KOS_START) \
$(OBJS) $(OBJEXTRA) $(KOS_LIBS)
run: $(TARGET)
$(KOS_LOADER) $(TARGET)
cd: $(TARGET)
@sh-elf-objcopy -R .stack -O binary $(TARGET) output.bin
@$(KOS_BASE)/utils/scramble/scramble output.bin 1ST_READ.BIN
@mkisofs -C 0,11702 -V Reaperi_Cycle -G $(KOS_BASE)/IP.BIN -r -J -l -m '*.o' -o ../$(PROJECT_NAME).iso $(DIR)
@$(KOS_BASE)/utils/cdi4dc/cdi4dc ../$(PROJECT_NAME).iso ../$(PROJECT_NAME).cdi -d > cdi4dc.log
../redream ../$(PROJECT_NAME).cdi
dist:
rm -f $(OBJS) romdisk.o romdisk.img
$(KOS_STRIP) $(TARGET)

213
README

@ -1,82 +1,131 @@
# Dreamroq Library
_This is a fork of the original project, we're currently trying to have the video play at the desired framerate._
### Introduction
Dreamroq is a RoQ playback library designed for the Sega Dreamcast video
game console.
RoQ is a relatively simple video file format developed for video-heavy
CD-ROM games. Read more about the format here:
http://wiki.multimedia.cx/index.php?title=RoQ
The Dreamroq library includes a player component that is designed to run
under the KallistiOS (KOS) open source operating system. Read more about
KOS at:
http://gamedev.allusion.net/softprj/kos/
The library also includes a sample testing utility that can be built
and executed on Unix systems. This utility is useful for debugging and
validation.
RoQ sample files can be found at:
http://samples.mplayerhq.hu/game-formats/idroq/
RoQ files can also be created using the Switchblade encoder:
http://icculus.org/~riot/
Or by using FFmpeg:
`ffmpeg -i input_video.mp4 -o output_video.roq`
http://ffmpeg.org/
### License
Dreamroq is meant to be license-compatible with the rest of the KallistiOS
operating system, which is a BSD-style open source license. You can read
the specific text in LICENSE.KOS.
### Building (KOS)
`make`
This is a standard KOS Makefile which assumes that a KOS build environment
is available. This is covered in the KOS documentation. This step will
build a file named dreamroq-player.elf which can be loaded onto a Dreamcast
console via standard means (also outside the scope of this document).
The file dreamcast-player.c contains a hardcoded RoQ file path in its
main function. It is best if this points to a file burned on an optical
disc. It is also viable to build a small RoQ file as a ROM disk into the
ELF file (which is well supported in KOS) and load the file from the '/rd'
mount point.
### Bugs, Issues, and Future Development
The player is just a proof of concept at this point. It doesn't try to
render frames with proper timing-- it just plays them as fast as possible
(which often isn't very fast, mostly due to the I/O bottleneck).
If the RoQ video is taller than it is wide, expect some odd rendering.
The API between the library and the client app leaves a bit to be desired.
Notably absent is some proper initialization and teardown mechanism.
Currently, the first call to the render callback initializes the PVR buffers
but they are never explicitly freed.
### Credits
Library originally written by Mike Melanson (mike -at- multimedia.cx)
Audio support added by Josh "PH3NOM" Pearson ([email protected])
Audio and framerate fix by MrNeo240 and lerabot
Dreamroq by Mike Melanson (mike -at- multimedia.cx)
R2 update by Josh Pearson ([email protected])
Introduction
============
Dreamroq is a RoQ playback library designed for the Sega Dreamcast video
game console.
RoQ is a relatively simple video file format developed for video-heavy
CD-ROM games. Read more about the format here:
http://wiki.multimedia.cx/index.php?title=RoQ
The Dreamroq library includes a player component that is designed to run
under the KallistiOS (KOS) open source operating system. Read more about
KOS at:
http://gamedev.allusion.net/softprj/kos/
The library also includes a sample testing utility that can be built
and executed on Unix systems. This utility is useful for debugging and
validation.
RoQ sample files can be found at:
http://samples.mplayerhq.hu/game-formats/idroq/
RoQ files can also be created using the Switchblade encoder:
http://icculus.org/~riot/
A version of Switchblade is also included in FFmpeg and many derivative
programs:
http://ffmpeg.org/
License
=======
Dreamroq is meant to be license-compatible with the rest of the KallistiOS
operating system, which is a BSD-style open source license. You can read
the specific text in LICENSE.KOS.
Building (Unix)
===============
To build and test on Linux/Mac OS X/Cygwin, simply type:
make
in the source directory. This will build the executable test-dreamroq. This
utility has the following usage:
./test-dreamroq <file.roq>
This will decode the RoQ file from the command line into a series of PNM
files in the current working directory (watch out-- this could take up a
lot of disk space).
Building (KOS)
==============
There are 2 Makefiles included with Dreamroq. The first -- implicitly
invoked when running a bare 'make' command as seen in the "Building (Unix)"
section -- builds the test utility. The second Makefile is Makefile.KOS,
invoked with:
make -f Makefile.KOS
This is a standard KOS Makefile which assumes that a KOS build environment
is available. This is covered in the KOS documentation. This step will
build a file named dreamroq-player.elf which can be loaded onto a Dreamcast
console via standard means (also outside the scope of this document).
The file dreamcast-player.c contains a hardcoded RoQ file path in its
main function. It is best if this points to a file burned on an optical
disc. It is also viable to build a small RoQ file as a ROM disk into the
ELF file (which is well supported in KOS) and load the file from the '/rd'
mount point.
Bugs, Issues, and Future Development
====================================
:R2 Release by PH3NOM:
I have updated the dreamroq library to support decoding of DPCM
audio samples encoded in the RoQ stream.
The KOS Dreamcast player has been updated to support streaming
of the decoded samples, directly using the Dreamcast's AICA
audio hardware, running in a seperate thread.
DMA transfers to the PVR and rendering of the frame is executed
in a seperate thread, so the decoder can continue to process the
next frame while the current frame is being processed by the PVR.
Furthermore, a frame-rate timer has been implemented, so the video
should be played back at correct speed. Also, the allocated PVR
memory is now freed on exit of the player.
====================================
/* deprecated */
The library only does video playback; no sound right now.
The video decoder is not 100% correct yet. You will likely notice a few
artifacts which need to be ironed out.
Many of the video rendering modes could be optimized slightly by copying
pairs of pixels as 32-bit units. This should work on any mode except motion
compensated blocks; the latter could experience alignment issues which
the DC's SH-4 can't handle.
The library is a tad slow. Profiling indicates that the main bottleneck
is loading data from disc; the video decoder itself is pretty fast. The
player might benefit by moving data loading (I/O-bound operation) into a
separate thread. For that matter, the phase which sends the decoded
texture to the graphics hardware via DMA and renders it should also be
in a separate thread.
The player is just a proof of concept at this point. It doesn't try to
render frames with proper timing-- it just plays them as fast as possible
(which often isn't very fast, mostly due to the I/O bottleneck).
If the RoQ video is taller than it is wide, expect some odd rendering.
The API between the library and the client app leaves a bit to be desired.
Notably absent is some proper initialization and teardown mechanism.
Currently, the first call to the render callback initializes the PVR buffers
but they are never explicitly freed.

@ -1,152 +1,193 @@
/*
* Dreamroq by Mike Melanson
*
* This is the sample Dreamcast player app, designed to be run under
* the KallistiOS operating system.
*/
#include <kos.h>
#include "dreamroqlib.h"
static pvr_ptr_t textures[2];
static int current_frame = 0;
static int render_cb(void *buf_ptr, int width, int height, int stride,
int texture_height, int colorspace)
{
pvr_poly_cxt_t cxt;
static pvr_poly_hdr_t hdr[2];
static pvr_vertex_t vert[4];
unsigned short *buf = (unsigned short*)buf_ptr;
float ratio;
/* screen coordinates of upper left and bottom right corners */
static int ul_x, ul_y, br_x, br_y;
static int graphics_initialized = 0;
if (colorspace != ROQ_RGB565)
return ROQ_RENDER_PROBLEM;
/* on first call, initialize textures and drawing coordinates */
if (!graphics_initialized)
{
textures[0] = pvr_mem_malloc(stride * texture_height * 2);
textures[1] = pvr_mem_malloc(stride * texture_height * 2);
if (!textures[0] || !textures[1])
{
return ROQ_RENDER_PROBLEM;
}
/* Precompile the poly headers */
pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_RGB565 | PVR_TXRFMT_NONTWIDDLED, stride, texture_height, textures[0], PVR_FILTER_NONE);
pvr_poly_compile(&hdr[0], &cxt);
pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_RGB565 | PVR_TXRFMT_NONTWIDDLED, stride, texture_height, textures[1], PVR_FILTER_NONE);
pvr_poly_compile(&hdr[1], &cxt);
/* this only works if width ratio <= height ratio */
ratio = 640.0 / width;
ul_x = 0;
br_x = (ratio * stride);
ul_y = ((480 - ratio * height) / 2);
br_y = ul_y + ratio * texture_height;
/* Things common to vertices */
vert[0].z = vert[1].z = vert[2].z = vert[3].z = 1.0f;
vert[0].argb = vert[1].argb = vert[2].argb = vert[3].argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
vert[0].oargb = vert[1].oargb = vert[2].oargb = vert[3].oargb = 0;
vert[0].flags = vert[1].flags = vert[2].flags = PVR_CMD_VERTEX;
vert[3].flags = PVR_CMD_VERTEX_EOL;
vert[0].x = ul_x;
vert[0].y = ul_y;
vert[0].u = 0.0;
vert[0].v = 0.0;
vert[1].x = br_x;
vert[1].y = ul_y;
vert[1].u = 1.0;
vert[1].v = 0.0;
vert[2].x = ul_x;
vert[2].y = br_y;
vert[2].u = 0.0;
vert[2].v = 1.0;
vert[3].x = br_x;
vert[3].y = br_y;
vert[3].u = 1.0;
vert[3].v = 1.0;
graphics_initialized = 1;
}
/* send the video frame as a texture over to video RAM */
pvr_txr_load(buf, textures[current_frame], stride * texture_height * 2);
pvr_wait_ready();
pvr_scene_begin();
pvr_list_begin(PVR_LIST_OP_POLY);
pvr_prim(&hdr[current_frame], sizeof(pvr_poly_hdr_t));
pvr_prim(&vert[0], sizeof(pvr_vertex_t));
pvr_prim(&vert[1], sizeof(pvr_vertex_t));
pvr_prim(&vert[2], sizeof(pvr_vertex_t));
pvr_prim(&vert[3], sizeof(pvr_vertex_t));
pvr_list_finish();
pvr_scene_finish();
if (current_frame)
current_frame = 0;
else
current_frame = 1;
return ROQ_SUCCESS;
}
int audio_cb(unsigned char *buf_rgb565, int samples, int channels)
{
return ROQ_SUCCESS;
}
static int quit_cb()
{
maple_device_t *cont; //Controller
cont_state_t *state; //State of inputs
/* check controller state */
state = maple_dev_status(cont);
// If the state/controller is unavailable
if(!state) {
printf("Error reading controller\n");
return(0);
}
state->buttons = ~state->buttons;
return (state->buttons & CONT_START);
}
int finish_cb()
{
return ROQ_SUCCESS;
}
int main()
{
int status;
roq_callbacks_t cbs;
cbs.render_cb = render_cb;
cbs.audio_cb = audio_cb;
cbs.quit_cb = quit_cb;
cbs.finish_cb = finish_cb;
vid_set_mode(DM_640x480_NTSC_IL, PM_RGB565);
pvr_init_defaults();
status = dreamroq_play("/cd/cube_v1.roq", ROQ_RGB565, 1, &cbs);
printf("dreamroq_play() status = %d\n", status);
return 0;
}
/*
* Dreamroq by Mike Melanson
* Updated by Josh Pearson to add audio support
*
* This is the sample Dreamcast player app, designed to be run under
* the KallistiOS operating system.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dc/maple.h>
#include <dc/maple/controller.h>
#include <kos/mutex.h>
#include <kos/thread.h>
#include "dreamroqlib.h"
#include "pvr_driver.h"
#include "snddrv.h"
/* Audio Global variables */
#define PCM_BUF_SIZE 1024*1024
static unsigned char *pcm_buf = NULL;
static int pcm_size = 0;
static int audio_init = 0;
static mutex_t * pcm_mut;
static long samples_done = 0;
/* Video Global variables */
static int graphics_initialized = 0;
static int frame=0;
static kthread_t * render_thread; /* Video thread */
static volatile int render_thd = 0; /* Video thread status */
static const float VIDEO_RATE = 30.0f; /* Video FPS */
/* PVR Driver Handle */
static struct pvr_frame roq_vram_ptr;
static struct pvr_frame_vertex roq_vram_vertex;
static float ATS = 0, VTS = 0; /* A/V Timestamps For Synchronization */
/* This is called by the AICA Sound Driver when it needs more samples */
void *audio_drv_cb ( snd_stream_hnd_t hnd, int pcm_needed, int * pcm_done )
{
/* Wait for RoQ Decoder to produce enough samples */
while( pcm_size < pcm_needed )
thd_pass();
/* Copy the Requested PCM Samples to the AICA Driver */
mutex_lock( pcm_mut );
memcpy( snddrv.pcm_buffer, pcm_buf, pcm_needed );
pcm_size -= pcm_needed;
memmove( pcm_buf, pcm_buf+pcm_needed, pcm_size );
mutex_unlock( pcm_mut );
samples_done += pcm_needed; /* Record the Audio Time Stamp */
ATS = (samples_done/(double)snddrv.rate)/((double)snddrv.channels*2.0);
snddrv.pcm_ptr = snddrv.pcm_buffer;
*pcm_done = pcm_needed;
return snddrv.pcm_ptr; /* Return the requested samples to the AICA driver */
}
static int audio_cb( unsigned char *buf, int size, int channels)
{
if(!audio_init)
{
/* allocate PCM buffer */
pcm_buf = malloc(PCM_BUF_SIZE);
if( pcm_buf == NULL )
return ROQ_NO_MEMORY;
/* Start AICA Driver */
snddrv_start_cb( 22050, channels, audio_drv_cb );
snddrv.dec_status = SNDDEC_STATUS_STREAMING;
/* Create a mutex to handle the double-threaded buffer */
mutex_init(pcm_mut, MUTEX_TYPE_NORMAL);
audio_init=1;
}
/* Copy the decoded PCM samples to our local PCM buffer */
mutex_lock( pcm_mut );
memcpy(pcm_buf+pcm_size, buf, size);
pcm_size += size;
mutex_unlock( pcm_mut );
return ROQ_SUCCESS;
}
static void *video_thd(void *ptr)
{
render_thd=1; /* Signal Thread is active */
/* Match the Auido and Video Time Stamps */
VTS = ++frame / VIDEO_RATE;
while( ATS < VTS ) thd_pass();
/* Draw the frame using the PVR */
pvr_draw_frame_dma(&roq_vram_ptr);
printf("Rendered Frame %u\n", frame);
render_thd=0; /* Signal Thread is finished */
}
static int render_cb(unsigned short *buf, int width, int height, int stride,
int texture_height)
{
/* on first call, initialize textures and drawing coordinates */
if (!graphics_initialized)
{
/* Allocate VRAM for current texture */
pvr_malloc( &roq_vram_ptr, width, height );
/* Compile the PVR Driver Handle */
//pvr_resize_resolution( roq_vram_ptr, &roq_vram_vertex );
pvr_set_resolution( roq_vram_ptr, 0, 0, 640, 480, &roq_vram_vertex );
pvr_compile_poly(roq_vram_vertex, &roq_vram_ptr,
PVR_LIST_OP_POLY, PVR_TXRFMT_RGB565 | PVR_TXRFMT_NONTWIDDLED, 10.0f);
graphics_initialized = 1;
}
/* Wait for last frame to finish render */
while(render_thd)
thd_pass();
/* Current decoded frame */
pvr_dma_load( (unsigned char *)buf, &roq_vram_ptr);
/* Create a thread to render the current frame */
//Try 1 onr 0 for first arg
render_thread = thd_create(0, video_thd, NULL);
return ROQ_SUCCESS;
}
maple_device_t *cont; //Controller
cont_state_t *state; //State of inputs
static int quit_cb()
{
cont = maple_enum_type(0, MAPLE_FUNC_CONTROLLER);
/* check controller state */
state = maple_dev_status(cont);
// If the state/controller is unavailable
if(!state) {
printf("Error reading controller\n");
return(0);
}
state->buttons = ~state->buttons;
return (state->buttons & CONT_START);
}
int main()
{
int status=0;
vid_set_mode(DM_640x480, PM_RGB565);
pvr_init_defaults();
pvr_dma_init();
printf("dreamroq_play(C) Multimedia Mike Melanson & Josh PH3NOM Pearson 2011\n");
/* To disable a callback, simply replace the function name by 0 */
status = dreamroq_play("/cd/sample.roq", 1, render_cb, 0, 0);
printf("dreamroq_play() status = %d\n", status);
if(audio_init)
{
free( pcm_buf );
pcm_buf = NULL;
pcm_size = 0;
samples_done = 0;
mutex_destroy(pcm_mut); /* Destroy the PCM mutex */
snddrv_exit(); /* Exit the AICA Driver */
}
if(graphics_initialized)
{
pvr_free( &roq_vram_ptr ); /* Free the PVR memory */
}
pvr_dma_shutdown();
return 0;
}

Binary file not shown.

@ -1,25 +1,62 @@
/*
* Dreamroq by Mike Melanson
* Audio support by Josh Pearson
* Updated by Josh Pearson to add audio support
*
* This is the main playback engine.
*/
#include <kos.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <malloc.h>
#include "dreamroqlib.h"
#ifdef __cplusplus
}
#endif /* extern "C" */
int roq_unpack_quad_codebook_rgb565(unsigned char *buf, int size,
int arg, roq_state *state)
#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_SIGNATURE 0x1084
#define CHUNK_HEADER_SIZE 8
#define LE_16(buf) (*buf | (*(buf+1) << 8))
#define LE_32(buf) (*buf | (*(buf+1) << 8) | (*(buf+2) << 16) | (*(buf+3) << 24))
#define MAX_BUF_SIZE (64 * 1024)
#define ROQ_CODEBOOK_SIZE 256
struct roq_audio
{
int pcm_samples;
int channels;
int position;
short snd_sqr_arr[260];
unsigned char pcm_sample[MAX_BUF_SIZE];
}roq_audio;
typedef struct
{
int width;
int height;
int mb_width;
int mb_height;
int mb_count;
int current_frame;
unsigned short *frame[2];
int stride;
int texture_height;
unsigned short cb2x2[ROQ_CODEBOOK_SIZE][4];
unsigned short cb4x4[ROQ_CODEBOOK_SIZE][16];
} roq_state;
static int roq_unpack_quad_codebook(unsigned char *buf, int size, int arg,
roq_state *state)
{
int y[4];
int yp, u, v;
@ -40,12 +77,8 @@ int roq_unpack_quad_codebook_rgb565(unsigned char *buf, int size,
if (!count4x4 && count2x2 * 6 < size)
count4x4 = ROQ_CODEBOOK_SIZE;
/* size sanity check, taking alpha into account */
if (state->alpha && (count2x2 * 10 + count4x4 * 4) != size)
{
return ROQ_BAD_CODEBOOK;
}
if (!state->alpha && (count2x2 * 6 + count4x4 * 4) != size)
/* size sanity check */
if ((count2x2 * 6 + count4x4 * 4) != size)
{
return ROQ_BAD_CODEBOOK;
}
@ -55,11 +88,7 @@ int roq_unpack_quad_codebook_rgb565(unsigned char *buf, int size,
{
/* unpack the YUV components from the bytestream */
for (j = 0; j < 4; j++)
{
y[j] = *buf++;
if (state->alpha)
buf++;
}
u = *buf++;
v = *buf++;
@ -78,7 +107,7 @@ int roq_unpack_quad_codebook_rgb565(unsigned char *buf, int size,
if (b < 0) b = 0;
if (b > 31) b = 31;
state->cb2x2_rgb565[i][j] = (
state->cb2x2[i][j] = (
(r << 11) |
(g << 5) |
(b << 0) );
@ -90,95 +119,8 @@ int roq_unpack_quad_codebook_rgb565(unsigned char *buf, int size,
{
for (j = 0; j < 4; j++)
{
v2x2 = state->cb2x2_rgb565[*buf++];
v4x4 = state->cb4x4_rgb565[i] + (j / 2) * 8 + (j % 2) * 2;
v4x4[0] = v2x2[0];
v4x4[1] = v2x2[1];
v4x4[4] = v2x2[2];
v4x4[5] = v2x2[3];
}
}
return ROQ_SUCCESS;
}
int roq_unpack_quad_codebook_rgba(unsigned char *buf, int size,
int arg, roq_state *state)
{
int y[4];
int a[4];
int yp, u, v;
int r, g, b;
int count2x2;
int count4x4;
int i, j;
unsigned int *v2x2;
unsigned int *v4x4;
count2x2 = (arg >> 8) & 0xFF;
count4x4 = arg & 0xFF;
if (!count2x2)
count2x2 = ROQ_CODEBOOK_SIZE;
/* 0x00 means 256 4x4 vectors iff there is enough space in the chunk
* after accounting for the 2x2 vectors */
if (!count4x4 && count2x2 * 6 < size)
count4x4 = ROQ_CODEBOOK_SIZE;
/* size sanity check, taking alpha into account */
if (state->alpha && (count2x2 * 10 + count4x4 * 4) != size)
{
return ROQ_BAD_CODEBOOK;
}
if (!state->alpha && (count2x2 * 6 + count4x4 * 4) != size)
{
return ROQ_BAD_CODEBOOK;
}
/* unpack the 2x2 vectors */
for (i = 0; i < count2x2; i++)
{
/* unpack the YUV components from the bytestream */
for (j = 0; j < 4; j++)
{
y[j] = *buf++;
if (state->alpha)
a[j] = *buf++;
else
a[j] = 255;
}
u = *buf++;
v = *buf++;
/* convert to RGBA */
for (j = 0; j < 4; j++)
{
yp = (y[j] - 16) * 1.164;
r = (yp + 1.596 * (v - 128));
g = (yp - 0.813 * (v - 128) - 0.391 * (u - 128));
b = (yp + 2.018 * (u - 128));
if (r < 0) r = 0;
if (r > 255) r = 255;
if (g < 0) g = 0;
if (g > 255) g = 255;
if (b < 0) b = 0;
if (b > 255) b = 255;
state->cb2x2_rgba[i][j] = (
(r << 24) |
(g << 16) |
(b << 8) |
a[j]);
}
}
/* unpack the 4x4 vectors */
for (i = 0; i < count4x4; i++)
{
for (j = 0; j < 4; j++)
{
v2x2 = state->cb2x2_rgba[*buf++];
v4x4 = state->cb4x4_rgba[i] + (j / 2) * 8 + (j % 2) * 2;
v2x2 = state->cb2x2[*buf++];
v4x4 = state->cb4x4[i] + (j / 2) * 8 + (j % 2) * 2;
v4x4[0] = v2x2[0];
v4x4[1] = v2x2[1];
v4x4[4] = v2x2[2];
@ -207,7 +149,7 @@ int roq_unpack_quad_codebook_rgb565(unsigned char *buf, int size,
mode_count -= 2; \
mode = (mode_set >> mode_count) & 0x03;
int roq_unpack_vq_rgb565(unsigned char *buf, int size, unsigned int arg,
static int roq_unpack_vq(unsigned char *buf, int size, unsigned int arg,
roq_state *state)
{
int status = ROQ_SUCCESS;
@ -229,6 +171,7 @@ int roq_unpack_vq_rgb565(unsigned char *buf, int size, unsigned int arg,
unsigned short *this_ptr;
unsigned int *this_ptr32;
unsigned short *last_ptr;
/*unsigned int *last_ptr32;*/
unsigned short *vector16;
unsigned int *vector32;
int stride32 = stride / 2;
@ -244,24 +187,20 @@ int roq_unpack_vq_rgb565(unsigned char *buf, int size, unsigned int arg,
int motion_x, motion_y;
unsigned char data_byte;
mx = (signed char)(arg >> 8);
my = (signed char)arg;
mx = (arg >> 8) & 0xFF;
my = arg & 0xFF;
if (state->current_frame & 1)
if (state->current_frame == 1)
{
this_frame = (unsigned short*)state->frame[1];
last_frame = (unsigned short*)state->frame[0];
state->current_frame = 0;
this_frame = state->frame[0];
last_frame = state->frame[1];
}
else
{
this_frame = (unsigned short*)state->frame[0];
last_frame = (unsigned short*)state->frame[1];
}
/* special case for frame 1, which needs to begin with frame 0's data */
if (state->current_frame == 1)
{
memcpy(state->frame[1], state->frame[0],
state->texture_height * state->stride * sizeof(unsigned short));
state->current_frame = 1;
this_frame = state->frame[1];
last_frame = state->frame[0];
}
for (mb_y = 0; mb_y < state->mb_height && status == ROQ_SUCCESS; mb_y++)
@ -307,7 +246,7 @@ int roq_unpack_vq_rgb565(unsigned char *buf, int size, unsigned int arg,
case 2: /* SLD: upsample 4x4 vector */
GET_BYTE(data_byte);
vector16 = state->cb4x4_rgb565[data_byte];
vector16 = state->cb4x4[data_byte];
for (i = 0; i < 4*4; i++)
{
this_ptr = this_frame + block_offset +
@ -316,7 +255,7 @@ int roq_unpack_vq_rgb565(unsigned char *buf, int size, unsigned int arg,
this_ptr[1] = *vector16;
this_ptr[stride+0] = *vector16;
this_ptr[stride+1] = *vector16;
vector16++;
vector16++;
}
break;
@ -329,7 +268,7 @@ int roq_unpack_vq_rgb565(unsigned char *buf, int size, unsigned int arg,
switch (mode)
{
case 0: /* MOT: skip */
break;
break;
case 1: /* FCC: motion compensation */
GET_BYTE(data_byte);
@ -352,7 +291,8 @@ int roq_unpack_vq_rgb565(unsigned char *buf, int size, unsigned int arg,
case 2: /* SLD: use 4x4 vector from codebook */
GET_BYTE(data_byte);
vector32 = (unsigned int*)state->cb4x4_rgb565[data_byte];
vector32 = (unsigned int*)state->cb4x4[data_byte];
this_ptr32 = (unsigned int*)this_frame;
this_ptr32 += subblock_offset / 2;
for (i = 0; i < 4; i++)
@ -360,21 +300,26 @@ int roq_unpack_vq_rgb565(unsigned char *buf, int size, unsigned int arg,
*this_ptr32++ = *vector32++;
*this_ptr32++ = *vector32++;
this_ptr32 += stride32 - 2;
}
break;
case 3: /* CCC: subdivide into 4 subblocks */
GET_BYTE(data_byte);
vector16 = state->cb2x2_rgb565[data_byte];
vector16 = state->cb2x2[data_byte];
this_ptr = this_frame + subblock_offset;
this_ptr[0] = vector16[0];
this_ptr[1] = vector16[1];
this_ptr[stride+0] = vector16[2];
this_ptr[stride+1] = vector16[3];
GET_BYTE(data_byte);
vector16 = state->cb2x2_rgb565[data_byte];
vector16 = state->cb2x2[data_byte];
this_ptr[2] = vector16[0];
this_ptr[3] = vector16[1];
this_ptr[stride+2] = vector16[2];
@ -383,14 +328,18 @@ int roq_unpack_vq_rgb565(unsigned char *buf, int size, unsigned int arg,
this_ptr += stride * 2;
GET_BYTE(data_byte);
vector16 = state->cb2x2_rgb565[data_byte];
vector16 = state->cb2x2[data_byte];
this_ptr[0] = vector16[0];
this_ptr[1] = vector16[1];
this_ptr[stride+0] = vector16[2];
this_ptr[stride+1] = vector16[3];
GET_BYTE(data_byte);
vector16 = state->cb2x2_rgb565[data_byte];
vector16 = state->cb2x2[data_byte];
this_ptr[2] = vector16[0];
this_ptr[3] = vector16[1];
this_ptr[stride+2] = vector16[2];
@ -414,226 +363,22 @@ int roq_unpack_vq_rgb565(unsigned char *buf, int size, unsigned int arg,
return status;
}
int roq_unpack_vq_rgba(unsigned char *buf, int size, unsigned int arg,
roq_state *state)
int dreamroq_play(char *filename, int loop, render_callback render_cb,
audio_callback audio_cb, quit_callback quit_cb)
{
int status = ROQ_SUCCESS;
int mb_x, mb_y;
int block; /* 8x8 blocks */
int subblock; /* 4x4 blocks */
int stride = state->stride;
int i;
/* frame and pixel management */
unsigned int *this_frame;
unsigned int *last_frame;
int line_offset;
int mb_offset;
int block_offset;
int subblock_offset;
unsigned int *this_ptr;
unsigned int *last_ptr;
unsigned int *vector;
/* bytestream management */
int index = 0;
int mode_set = 0;
int mode, mode_lo, mode_hi;
int mode_count = 0;
/* vectors */
int mx, my;
int motion_x, motion_y;
unsigned char data_byte;
mx = (signed char)(arg >> 8);
my = (signed char)arg;
if (state->current_frame & 1)
{
this_frame = (unsigned int*)state->frame[1];
last_frame = (unsigned int*)state->frame[0];
}
else
{
this_frame = (unsigned int*)state->frame[0];
last_frame = (unsigned int*)state->frame[1];
}
/* special case for frame 1, which needs to begin with frame 0's data */
if (state->current_frame == 1)
{
memcpy(state->frame[1], state->frame[0],
state->texture_height * state->stride * sizeof(unsigned int));
}
for (mb_y = 0; mb_y < state->mb_height && status == ROQ_SUCCESS; mb_y++)
{
line_offset = mb_y * 16 * stride;
for (mb_x = 0; mb_x < state->mb_width && status == ROQ_SUCCESS; mb_x++)
{
mb_offset = line_offset + mb_x * 16;
for (block = 0; block < 4 && status == ROQ_SUCCESS; block++)
{
block_offset = mb_offset + (block / 2 * 8 * stride) + (block % 2 * 8);
/* each 8x8 block gets a mode */
GET_MODE();
switch (mode)
{
case 0: /* MOT: skip */
break;
case 1: /* FCC: motion compensation */
GET_BYTE(data_byte);
motion_x = 8 - (data_byte >> 4) - mx;
motion_y = 8 - (data_byte & 0xF) - my;
last_ptr = last_frame + block_offset +
(motion_y * stride) + motion_x;
this_ptr = this_frame + block_offset;
for (i = 0; i < 8; i++)
{
*this_ptr++ = *last_ptr++;
*this_ptr++ = *last_ptr++;
*this_ptr++ = *last_ptr++;
*this_ptr++ = *last_ptr++;
*this_ptr++ = *last_ptr++;
*this_ptr++ = *last_ptr++;
*this_ptr++ = *last_ptr++;
*this_ptr++ = *last_ptr++;
last_ptr += stride - 8;
this_ptr += stride - 8;
}
break;
case 2: /* SLD: upsample 4x4 vector */
GET_BYTE(data_byte);
vector = state->cb4x4_rgba[data_byte];
for (i = 0; i < 4*4; i++)
{
this_ptr = this_frame + block_offset +
(i / 4 * 2 * stride) + (i % 4 * 2);
this_ptr[0] = *vector;
this_ptr[1] = *vector;
this_ptr[stride+0] = *vector;
this_ptr[stride+1] = *vector;
vector++;
}
break;
case 3: /* CCC: subdivide into 4 subblocks */
for (subblock = 0; subblock < 4; subblock++)
{
subblock_offset = block_offset + (subblock / 2 * 4 * stride) + (subblock % 2 * 4);
GET_MODE();
switch (mode)
{
case 0: /* MOT: skip */
break;
case 1: /* FCC: motion compensation */
GET_BYTE(data_byte);
motion_x = 8 - (data_byte >> 4) - mx;
motion_y = 8 - (data_byte & 0xF) - my;
last_ptr = last_frame + subblock_offset +
(motion_y * stride) + motion_x;
this_ptr = this_frame + subblock_offset;
for (i = 0; i < 4; i++)
{
*this_ptr++ = *last_ptr++;
*this_ptr++ = *last_ptr++;
*this_ptr++ = *last_ptr++;
*this_ptr++ = *last_ptr++;
last_ptr += stride - 4;
this_ptr += stride - 4;
}
break;
case 2: /* SLD: use 4x4 vector from codebook */
GET_BYTE(data_byte);
vector = state->cb4x4_rgba[data_byte];
this_ptr = this_frame + subblock_offset;
for (i = 0; i < 4; i++)
{
*this_ptr++ = *vector++;
*this_ptr++ = *vector++;
*this_ptr++ = *vector++;
*this_ptr++ = *vector++;
this_ptr += stride - 4;
}
break;
case 3: /* CCC: subdivide into 4 subblocks */
GET_BYTE(data_byte);
vector = state->cb2x2_rgba[data_byte];
this_ptr = this_frame + subblock_offset;
this_ptr[0] = vector[0];
this_ptr[1] = vector[1];
this_ptr[stride+0] = vector[2];
this_ptr[stride+1] = vector[3];
GET_BYTE(data_byte);
vector = state->cb2x2_rgba[data_byte];
this_ptr[2] = vector[0];
this_ptr[3] = vector[1];
this_ptr[stride+2] = vector[2];
this_ptr[stride+3] = vector[3];
this_ptr += stride * 2;
GET_BYTE(data_byte);
vector = state->cb2x2_rgba[data_byte];
this_ptr[0] = vector[0];
this_ptr[1] = vector[1];
this_ptr[stride+0] = vector[2];
this_ptr[stride+1] = vector[3];
GET_BYTE(data_byte);
vector = state->cb2x2_rgba[data_byte];
this_ptr[2] = vector[0];
this_ptr[3] = vector[1];
this_ptr[stride+2] = vector[2];
this_ptr[stride+3] = vector[3];
break;
}
}
break;
}
}
}
}
/* sanity check to see if the stream was fully consumed */
if (status == ROQ_SUCCESS && index < size-2)
{
status = ROQ_BAD_VQ_STREAM;
}
return status;
}
int dreamroq_play(char *filename, int colorspace, int loop, roq_callbacks_t *cbs)
{
FILE *f = 0;
FILE *f;
size_t file_ret;
int framerate;
int chunk_id;
unsigned int chunk_size;
unsigned int chunk_arg;
roq_state state;
roq_audio audio;
int status;
int initialized = 0;
unsigned char read_buffer[MAX_BUF_SIZE];
int i, snd_left, snd_right;
f = fopen( filename, "rb");
f = fopen(filename, "rb");
if (!f)
return ROQ_FILE_OPEN_FAILURE;
@ -643,6 +388,7 @@ int dreamroq_play(char *filename, int colorspace, int loop, roq_callbacks_t *cbs
fclose(f);
printf("\nROQ_FILE_READ_FAILURE\n\n");
return ROQ_FILE_READ_FAILURE;
}
chunk_id = LE_16(&read_buffer[0]);
chunk_size = LE_32(&read_buffer[2]);
@ -657,8 +403,8 @@ int dreamroq_play(char *filename, int colorspace, int loop, roq_callbacks_t *cbs
/* Initialize Audio SQRT Look-Up Table */
for(i = 0; i < 128; i++)
{
audio.snd_sqr_array[i] = i * i;
audio.snd_sqr_array[i + 128] = -(i * i);
roq_audio.snd_sqr_arr[i] = i * i;
roq_audio.snd_sqr_arr[i + 128] = -(roq_audio.snd_sqr_arr[i]);
}
status = ROQ_SUCCESS;
@ -666,36 +412,42 @@ int dreamroq_play(char *filename, int colorspace, int loop, roq_callbacks_t *cbs
{
/* if client program defined a quit callback, check if it's time
* to quit */
if (cbs->quit_cb && cbs->quit_cb())
break;
if (quit_cb && quit_cb()) {
printf("Quit CB\n");
break;
}
file_ret = fread(read_buffer, CHUNK_HEADER_SIZE, 1, f);
#ifdef FPSGRAPH
printf("r\n");
#endif
if (file_ret != 1)
{
/* if the read failed but the file is not EOF, there is a deeper
* problem; don't entertain the idea of looping */
if (!feof(f))
break;
else if (loop)
{
/* it shouldn't be necessary to close and re-open the file
* here; however, this works around a bug in KOS 1.2.0 in
* which seeking back doesn't clear the EOF flag */
fclose(f);
f = fopen(filename, "rb");
if (!f)
status = ROQ_FILE_OPEN_FAILURE;
else
{
/* skip the signature header */
fseek(f, 8, SEEK_SET);
continue;
}
}
else
printf("fread() problemc\n");
/* if the read failed but the file is not EOF, there is a deeper
* problem; don't entertain the idea of looping */
if (!feof(f))
break;
else if (loop)
{
/* it shouldn't be necessary to close and re-open the file
* here; however, this works around a bug in KOS 1.2.0 in
* which seeking back doesn't clear the EOF flag */
//fclose(f);
//f = fopen(filename, "rb");
if (!f)
status = ROQ_FILE_OPEN_FAILURE;
else
{
/* skip the signature header */
fseek(f, 8, SEEK_SET);
continue;
}
}
else
break;
}
//printf("Not end of file\n");
chunk_id = LE_16(&read_buffer[0]);
chunk_size = LE_32(&read_buffer[2]);
chunk_arg = LE_16(&read_buffer[6]);
@ -713,15 +465,12 @@ int dreamroq_play(char *filename, int colorspace, int loop, roq_callbacks_t *cbs
break;
}
state.colorspace = colorspace;
switch(chunk_id)
{
case RoQ_INFO:
if (initialized)
continue;
state.alpha = chunk_arg;
state.width = LE_16(&read_buffer[0]);
state.height = LE_16(&read_buffer[2]);
/* width and height each need to be divisible by 16 */
@ -749,19 +498,13 @@ int dreamroq_play(char *filename, int colorspace, int loop, roq_callbacks_t *cbs
while (state.texture_height < state.height)
state.texture_height <<= 1;
}
printf(" RoQ_INFO: dimensions = %dx%d, %dx%d; %d mbs, texture = %dx%d\n",
state.width, state.height, state.mb_width, state.mb_height,
state.mb_count, state.stride, state.texture_height);
if (colorspace == ROQ_RGB565)
{
state.frame[0] = (unsigned char*)malloc(state.texture_height * state.stride * sizeof(unsigned short));
state.frame[1] = (unsigned char*)malloc(state.texture_height * state.stride * sizeof(unsigned short));
}
else
{
state.frame[0] = (unsigned char*)malloc(state.texture_height * state.stride * sizeof(unsigned int));
state.frame[1] = (unsigned char*)malloc(state.texture_height * state.stride * sizeof(unsigned int));
}
state.frame[0] = (unsigned short*)memalign(32,state.texture_height * state.stride * sizeof(unsigned short));
state.frame[1] = (unsigned short*)memalign(32,state.texture_height * state.stride * sizeof(unsigned short));
state.current_frame = 0;
if (!state.frame[0] || !state.frame[1])
{
@ -779,82 +522,60 @@ int dreamroq_play(char *filename, int colorspace, int loop, roq_callbacks_t *cbs
break;
case RoQ_QUAD_CODEBOOK:
if (colorspace == ROQ_RGB565)
status = roq_unpack_quad_codebook_rgb565(read_buffer, chunk_size,
chunk_arg, &state);
else if (colorspace == ROQ_RGBA)
status = roq_unpack_quad_codebook_rgba(read_buffer, chunk_size,
chunk_arg, &state);
status = roq_unpack_quad_codebook(read_buffer, chunk_size,
chunk_arg, &state);
break;
case RoQ_QUAD_VQ:
if (colorspace == ROQ_RGB565)
status = roq_unpack_vq_rgb565(read_buffer, chunk_size,
chunk_arg, &state);
else if (colorspace == ROQ_RGBA)
status = roq_unpack_vq_rgba(read_buffer, chunk_size,
chunk_arg, &state);
if (cbs->render_cb)
status = cbs->render_cb(state.frame[state.current_frame & 1],
state.width, state.height, state.stride, state.texture_height,
colorspace);
state.current_frame++;
break;
case RoQ_JPEG:
status = roq_unpack_vq(read_buffer, chunk_size,
chunk_arg, &state);
if (render_cb)
status = render_cb(state.frame[state.current_frame],
state.width, state.height, state.stride, state.texture_height);
break;
case RoQ_SOUND_MONO:
audio.channels = 1;
audio.pcm_samples = chunk_size*2;
snd_left = chunk_arg;
for(i = 0; i < chunk_size; i++)
{
snd_left += audio.snd_sqr_array[read_buffer[i]];
audio.pcm_sample[i * 2] = snd_left & 0xff;
audio.pcm_sample[i * 2 + 1] = (snd_left & 0xff00) >> 8;
}
if (cbs->audio_cb)
status = cbs->audio_cb(audio.pcm_sample, audio.pcm_samples,
audio.channels);
roq_audio.channels = 1;
roq_audio.pcm_samples = chunk_size*2;
snd_left = chunk_arg;
for(i = 0; i < chunk_size; i++)
{
snd_left += roq_audio.snd_sqr_arr[read_buffer[i]];
roq_audio.pcm_sample[i * 2] = snd_left & 0xff;
roq_audio.pcm_sample[i * 2 + 1] = (snd_left & 0xff00) >> 8;
}
if (audio_cb)
status = audio_cb( roq_audio.pcm_sample, roq_audio.pcm_samples,
roq_audio.channels );
break;
case RoQ_SOUND_STEREO:
audio.channels = 2;
audio.pcm_samples = chunk_size*2;
snd_left = (chunk_arg & 0xFF00);
snd_right = (chunk_arg & 0xFF) << 8;
for(i = 0; i < chunk_size; i += 2)
{
snd_left += audio.snd_sqr_array[read_buffer[i]];
snd_right += audio.snd_sqr_array[read_buffer[i+1]];
audio.pcm_sample[i * 2] = snd_left & 0xff;
audio.pcm_sample[i * 2 + 1] = (snd_left & 0xff00) >> 8;
audio.pcm_sample[i * 2 + 2] = snd_right & 0xff;
audio.pcm_sample[i * 2 + 3] = (snd_right & 0xff00) >> 8;
}
if (cbs->audio_cb)
status = cbs->audio_cb( audio.pcm_sample, audio.pcm_samples,
audio.channels );
break;
case RoQ_PACKET:
/* still unimplemented */
roq_audio.channels = 2;
roq_audio.pcm_samples = chunk_size*2;
snd_left = (chunk_arg & 0xFF00);
snd_right = (chunk_arg & 0xFF) << 8;
for(i = 0; i < chunk_size; i += 2)
{
snd_left += roq_audio.snd_sqr_arr[read_buffer[i]];
snd_right += roq_audio.snd_sqr_arr[read_buffer[i+1]];
roq_audio.pcm_sample[i * 2] = snd_left & 0xff;
roq_audio.pcm_sample[i * 2 + 1] = (snd_left & 0xff00) >> 8;
roq_audio.pcm_sample[i * 2 + 2] = snd_right & 0xff;
roq_audio.pcm_sample[i * 2 + 3] = (snd_right & 0xff00) >> 8;
}
if (audio_cb)
status = audio_cb( roq_audio.pcm_sample, roq_audio.pcm_samples,
roq_audio.channels );
break;
default:
break;
}
}
printf("End of file\n");
free(state.frame[0]);
free(state.frame[1]);
fclose(f);
if (cbs->finish_cb)
cbs->finish_cb();
return status;
}

@ -4,14 +4,11 @@
* This is the header file to be included in the programs wishing to
* use the Dreamroq playback engine.
*/
#pragma once
#ifndef NEWROQ_H
#define NEWROQ_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ROQ_EXIT 0
#define ROQ_SUCCESS 0
#define ROQ_FILE_OPEN_FAILURE 1
#define ROQ_FILE_READ_FAILURE 2
@ -24,87 +21,18 @@
#define ROQ_RENDER_PROBLEM 9
#define ROQ_CLIENT_PROBLEM 10
#define ROQ_RGB565 0
#define ROQ_RGBA 1
#define RoQ_INFO 0x1001
#define RoQ_QUAD_CODEBOOK 0x1002
#define RoQ_QUAD_VQ 0x1011
#define RoQ_JPEG 0x1012
#define RoQ_SOUND_MONO 0x1020
#define RoQ_SOUND_STEREO 0x1021
#define RoQ_PACKET 0x1030
#define RoQ_SIGNATURE 0x1084
#define CHUNK_HEADER_SIZE 8
#define LE_16(buf) (*buf | (*(buf+1) << 8))
#define LE_32(buf) (*buf | (*(buf+1) << 8) | (*(buf+2) << 16) | (*(buf+3) << 24))
#define MAX_BUF_SIZE (64 * 1024)
#define ROQ_CODEBOOK_SIZE 256
#define SQR_ARRAY_SIZE 256
typedef struct
{
int pcm_samples;
int channels;
int position;
short snd_sqr_array[SQR_ARRAY_SIZE];
unsigned char pcm_sample[MAX_BUF_SIZE];
} roq_audio;
typedef struct
{
int width;
int height;
int mb_width;
int mb_height;
int mb_count;
int alpha;
int current_frame;
unsigned char *frame[2];
int stride;
int texture_height;
int colorspace;
unsigned short cb2x2_rgb565[ROQ_CODEBOOK_SIZE][4];
unsigned short cb4x4_rgb565[ROQ_CODEBOOK_SIZE][16];
unsigned int cb2x2_rgba[ROQ_CODEBOOK_SIZE][4];
unsigned int cb4x4_rgba[ROQ_CODEBOOK_SIZE][16];
} roq_state;
/* The library calls this function when it has a frame ready for display. */
typedef int (*render_callback)(void *buf, int width, int height,
int stride, int texture_height, int colorspace);
typedef int (*render_callback)(unsigned short *buf, int width, int height,
int stride, int texture_height);
/* The library calls this function when it has pcm samples ready for output. */
typedef int (*audio_callback)(unsigned char *buf, int samples, int channels);