From 3ef8be2bfc89672060a2a2f2b8926b3decb08172 Mon Sep 17 00:00:00 2001 From: Mike Melanson Date: Tue, 2 Sep 2003 04:32:02 +0000 Subject: initial commit for Id RoQ and Interplay MVE multimedia subsystems Originally committed as revision 2195 to svn://svn.ffmpeg.org/ffmpeg/trunk --- libavcodec/Makefile | 3 +- libavcodec/allcodecs.c | 4 + libavcodec/avcodec.h | 10 + libavcodec/dpcm.c | 200 ++++++++++++++ libavcodec/interplayvideo.c | 145 +++++++++++ libavcodec/roqvideo.c | 406 +++++++++++++++++++++++++++++ libavformat/Makefile | 2 +- libavformat/allformats.c | 2 + libavformat/avformat.h | 6 + libavformat/idroq.c | 299 +++++++++++++++++++++ libavformat/ipmovie.c | 618 ++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 1693 insertions(+), 2 deletions(-) create mode 100644 libavcodec/dpcm.c create mode 100644 libavcodec/interplayvideo.c create mode 100644 libavcodec/roqvideo.c create mode 100644 libavformat/idroq.c create mode 100644 libavformat/ipmovie.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 1d90426d76..33e8857183 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -17,7 +17,8 @@ OBJS= common.o utils.o mem.o allcodecs.o \ mpeg12.o mpegaudiodec.o pcm.o simple_idct.o \ ratecontrol.o adpcm.o eval.o dv.o error_resilience.o \ fft.o mdct.o mace.o huffyuv.o cyuv.o opts.o raw.o h264.o golomb.o \ - vp3.o asv1.o 4xm.o cabac.o ffv1.o ra144.o ra288.o vcr1.o cljr.o + vp3.o asv1.o 4xm.o cabac.o ffv1.o ra144.o ra288.o vcr1.o cljr.o \ + roqvideo.o dpcm.o interplayvideo.o ifeq ($(AMR_NB),yes) ifeq ($(AMR_NB_FIXED),yes) diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 81f6f9626d..c687ecf5e2 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -121,11 +121,15 @@ void avcodec_register_all(void) register_avcodec(&cljr_decoder); register_avcodec(&fourxm_decoder); register_avcodec(&mdec_decoder); + register_avcodec(&roq_decoder); + register_avcodec(&interplay_video_decoder); #ifdef CONFIG_AC3 register_avcodec(&ac3_decoder); #endif register_avcodec(&ra_144_decoder); register_avcodec(&ra_288_decoder); + register_avcodec(&roq_dpcm_decoder); + register_avcodec(&interplay_dpcm_decoder); #endif /* CONFIG_DECODERS */ #ifdef AMR_NB diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 4a810013fa..26a48e4aad 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -66,6 +66,8 @@ enum CodecID { CODEC_ID_VCR1, CODEC_ID_CLJR, CODEC_ID_MDEC, + CODEC_ID_ROQ, + CODEC_ID_INTERPLAY_VIDEO, /* various pcm "codecs" */ CODEC_ID_PCM_S16LE, @@ -88,6 +90,10 @@ enum CodecID { /* RealAudio codecs*/ CODEC_ID_RA_144, CODEC_ID_RA_288, + + /* various DPCM codecs */ + CODEC_ID_ROQ_DPCM, + CODEC_ID_INTERPLAY_DPCM, }; enum CodecType { @@ -1347,8 +1353,12 @@ extern AVCodec cljr_decoder; extern AVCodec ffv1_decoder; extern AVCodec fourxm_decoder; extern AVCodec mdec_decoder; +extern AVCodec roq_decoder; +extern AVCodec interplay_video_decoder; extern AVCodec ra_144_decoder; extern AVCodec ra_288_decoder; +extern AVCodec roq_dpcm_decoder; +extern AVCodec interplay_dpcm_decoder; /* pcm codecs */ #define PCM_CODEC(id, name) \ diff --git a/libavcodec/dpcm.c b/libavcodec/dpcm.c new file mode 100644 index 0000000000..bd3ad7fd0f --- /dev/null +++ b/libavcodec/dpcm.c @@ -0,0 +1,200 @@ +/* + * Assorted DPCM codecs + * Copyright (c) 2003 The ffmpeg Project. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * @file: dpcm.c + * Assorted DPCM (differential pulse code modulation) audio codecs + * by Mike Melanson (melanson@pcisys.net) + * for more information on the specific data formats, visit: + * http://www.pcisys.net/~melanson/codecs/simpleaudio.html + */ + +#include "avcodec.h" + +typedef struct DPCMContext { + int channels; + short roq_square_array[256]; + int last_delta[2]; +} DPCMContext; + +#define SATURATE_S16(x) if (x < -32768) x = -32768; \ + else if (x > 32767) x = 32767; +#define SE_16BIT(x) if (x & 0x8000) x -= 0x10000; +#define LE_16(x) ((((uint8_t*)(x))[1] << 8) | ((uint8_t*)(x))[0]) +#define LE_32(x) ((((uint8_t*)(x))[3] << 24) | \ + (((uint8_t*)(x))[2] << 16) | \ + (((uint8_t*)(x))[1] << 8) | \ + ((uint8_t*)(x))[0]) + +static int interplay_delta_table[] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 47, 51, 56, 61, + 66, 72, 79, 86, 94, 102, 112, 122, + 133, 145, 158, 173, 189, 206, 225, 245, + 267, 292, 318, 348, 379, 414, 452, 493, + 538, 587, 640, 699, 763, 832, 908, 991, + 1081, 1180, 1288, 1405, 1534, 1673, 1826, 1993, + 2175, 2373, 2590, 2826, 3084, 3365, 3672, 4008, + 4373, 4772, 5208, 5683, 6202, 6767, 7385, 8059, + 8794, 9597, 10472, 11428, 12471, 13609, 14851, 16206, + 17685, 19298, 21060, 22981, 25078, 27367, 29864, 32589, + -29973, -26728, -23186, -19322, -15105, -10503, -5481, -1, + 1, 1, 5481, 10503, 15105, 19322, 23186, 26728, + 29973, -32589, -29864, -27367, -25078, -22981, -21060, -19298, + -17685, -16206, -14851, -13609, -12471, -11428, -10472, -9597, + -8794, -8059, -7385, -6767, -6202, -5683, -5208, -4772, + -4373, -4008, -3672, -3365, -3084, -2826, -2590, -2373, + -2175, -1993, -1826, -1673, -1534, -1405, -1288, -1180, + -1081, -991, -908, -832, -763, -699, -640, -587, + -538, -493, -452, -414, -379, -348, -318, -292, + -267, -245, -225, -206, -189, -173, -158, -145, + -133, -122, -112, -102, -94, -86, -79, -72, + -66, -61, -56, -51, -47, -43, -42, -41, + -40, -39, -38, -37, -36, -35, -34, -33, + -32, -31, -30, -29, -28, -27, -26, -25, + -24, -23, -22, -21, -20, -19, -18, -17, + -16, -15, -14, -13, -12, -11, -10, -9, + -8, -7, -6, -5, -4, -3, -2, -1 + +}; + +static int dpcm_decode_init(AVCodecContext *avctx) +{ + DPCMContext *s = avctx->priv_data; + int i; + short square; + + s->channels = avctx->channels; + + switch(avctx->codec->id) { + + case CODEC_ID_ROQ_DPCM: + /* initialize square table */ + for (i = 0; i < 128; i++) { + square = i * i; + s->roq_square_array[i] = square; + s->roq_square_array[i + 128] = -square; + } + break; + + default: + break; + } + + return 0; +} + +static int dpcm_decode_frame(AVCodecContext *avctx, + void *data, int *data_size, + uint8_t *buf, int buf_size) +{ + DPCMContext *s = avctx->priv_data; + int in, out = 0; + int i; + int predictor[2]; + int channel_number = 0; + short *output_samples = data; + int sequence_number; + + switch(avctx->codec->id) { + + case CODEC_ID_ROQ_DPCM: + if (s->channels == 1) + predictor[0] = LE_16(&buf[6]); + else { + predictor[0] = buf[7] << 8; + predictor[1] = buf[6] << 8; + } + SE_16BIT(predictor[0]); + SE_16BIT(predictor[1]); + + /* decode the samples */ + for (in = 8, out = 0; in < buf_size; in++, out++) { + predictor[channel_number] += s->roq_square_array[buf[in]]; + SATURATE_S16(predictor[channel_number]); + output_samples[out] = predictor[channel_number]; + + /* toggle channel */ + channel_number ^= s->channels - 1; + } + break; + + case CODEC_ID_INTERPLAY_DPCM: + in = 0; + sequence_number = LE_16(&buf[in]); + in += 6; /* skip over the stream mask and stream length */ + if (sequence_number == 1) { + predictor[0] = LE_16(&buf[in]); + in += 2; + SE_16BIT(predictor[0]) + if (s->channels == 2) { + predictor[1] = LE_16(&buf[in]); + SE_16BIT(predictor[1]) + in += 2; + } + } else { + for (i = 0; i < s->channels; i++) + predictor[i] = s->last_delta[i]; + } + + while (in < buf_size) { + predictor[channel_number] += interplay_delta_table[buf[in++]]; + SATURATE_S16(predictor[channel_number]); + output_samples[out++] = predictor[channel_number]; + + /* toggle channel */ + channel_number ^= s->channels - 1; + } + + /* save predictors for next round */ + for (i = 0; i < s->channels; i++) + s->last_delta[i] = predictor[i]; + + break; + } + + *data_size = out * sizeof(short); + return buf_size; +} + +AVCodec roq_dpcm_decoder = { + "roq_dpcm", + CODEC_TYPE_AUDIO, + CODEC_ID_ROQ_DPCM, + sizeof(DPCMContext), + dpcm_decode_init, + NULL, + NULL, + dpcm_decode_frame, +}; + +AVCodec interplay_dpcm_decoder = { + "interplay_dpcm", + CODEC_TYPE_AUDIO, + CODEC_ID_INTERPLAY_DPCM, + sizeof(DPCMContext), + dpcm_decode_init, + NULL, + NULL, + dpcm_decode_frame, +}; diff --git a/libavcodec/interplayvideo.c b/libavcodec/interplayvideo.c new file mode 100644 index 0000000000..c94f16f357 --- /dev/null +++ b/libavcodec/interplayvideo.c @@ -0,0 +1,145 @@ +/* + * Interplay MVE Video Decoder + * Copyright (C) 2003 the ffmpeg project + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/** + * @file roqvideo.c + * Interplay MVE Video Decoder by Mike Melanson + * For more information about the Interplay MVE format, visit: + * http://www.pcisys.net/~melanson/codecs/ + */ + +#include +#include +#include +#include + +#include "common.h" +#include "avcodec.h" +#include "dsputil.h" + +typedef struct IpvideoContext { + + AVCodecContext *avctx; + DSPContext dsp; + AVFrame last_frame; + AVFrame current_frame; + int first_frame; + int receiving_decoding_map; + unsigned char *decoding_map; + int decoding_map_size; + + unsigned char *buf; + int size; + +} IpvideoContext; + +static int ipvideo_decode_init(AVCodecContext *avctx) +{ + IpvideoContext *s = avctx->priv_data; + + s->avctx = avctx; + avctx->pix_fmt = PIX_FMT_YUV444P; + avctx->has_b_frames = 0; + dsputil_init(&s->dsp, avctx); + + s->first_frame = 1; + s->receiving_decoding_map = 1; /* decoding map will be received first */ + + /* decoding map contains 4 bits of information per 8x8 block */ + s->decoding_map_size = avctx->width * avctx->height / (8 * 8 * 2); + s->decoding_map = av_malloc(s->decoding_map_size); + + return 0; +} + +static int ipvideo_decode_frame(AVCodecContext *avctx, + void *data, int *data_size, + uint8_t *buf, int buf_size) +{ + IpvideoContext *s = avctx->priv_data; + + if (s->receiving_decoding_map) { + /* receiving the decoding map on this iteration */ + s->receiving_decoding_map = 0; + + if (buf_size != s->decoding_map_size) + printf (" Interplay video: buf_size != decoding_map_size (%d != %d)\n", + buf_size, s->decoding_map_size); + else + memcpy(s->decoding_map, buf, buf_size); + + *data_size = 0; + *(AVFrame*)data = s->last_frame; + } else { + /* receiving the compressed video data on this iteration */ + s->receiving_decoding_map = 1; + s->buf = buf; + s->size = buf_size; + + if (avctx->get_buffer(avctx, &s->current_frame)) { + printf (" Interplay Video: get_buffer() failed\n"); + return -1; + } + +// ipvideo_decode_frame(s); +memset(s->current_frame.data[0], 0x80, s->current_frame.linesize[0] * avctx->height); +memset(s->current_frame.data[1], 0x80, s->current_frame.linesize[1] * avctx->height / 4); +memset(s->current_frame.data[2], 0x80, s->current_frame.linesize[2] * avctx->height / 4); + + /* release the last frame if it is allocated */ + if (s->first_frame) + s->first_frame = 0; + else + avctx->release_buffer(avctx, &s->last_frame); + + /* shuffle frames */ + s->last_frame = s->current_frame; + + *data_size = sizeof(AVFrame); + *(AVFrame*)data = s->current_frame; + } + + /* always report that the buffer was completely consumed */ + return buf_size; +} + +static int ipvideo_decode_end(AVCodecContext *avctx) +{ + IpvideoContext *s = avctx->priv_data; + + /* release the last frame */ + avctx->release_buffer(avctx, &s->last_frame); + + av_free(s->decoding_map); + + return 0; +} + +AVCodec interplay_video_decoder = { + "interplayvideo", + CODEC_TYPE_VIDEO, + CODEC_ID_INTERPLAY_VIDEO, + sizeof(IpvideoContext), + ipvideo_decode_init, + NULL, + ipvideo_decode_end, + ipvideo_decode_frame, + CODEC_CAP_DR1, +}; diff --git a/libavcodec/roqvideo.c b/libavcodec/roqvideo.c new file mode 100644 index 0000000000..5816588c3e --- /dev/null +++ b/libavcodec/roqvideo.c @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2003 the ffmpeg project + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/** + * @file roqvideo.c + * Id RoQ Video Decoder by Dr. Tim Ferguson + * For more information about the Id RoQ format, visit: + * http://www.csse.monash.edu.au/~timf/ + */ + +#include +#include +#include +#include + +#include "common.h" +#include "avcodec.h" +#include "dsputil.h" + +typedef struct { + unsigned char y0, y1, y2, y3, u, v; +} roq_cell; + +typedef struct { + int idx[4]; +} roq_qcell; + + +typedef struct RoqContext { + + AVCodecContext *avctx; + DSPContext dsp; + AVFrame last_frame; + AVFrame current_frame; + int first_frame; + int y_stride; + int c_stride; + + roq_cell cells[256]; + roq_qcell qcells[256]; + + unsigned char *buf; + int size; + +} RoqContext; + +#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]))) + + +static void apply_vector_2x2(RoqContext *ri, int x, int y, roq_cell *cell) +{ + unsigned char *yptr; + + yptr = ri->current_frame.data[0] + (y * ri->y_stride) + x; + *yptr++ = cell->y0; + *yptr++ = cell->y1; + yptr += (ri->y_stride - 2); + *yptr++ = cell->y2; + *yptr++ = cell->y3; + ri->current_frame.data[1][(y/2) * (ri->c_stride) + x/2] = cell->u; + ri->current_frame.data[2][(y/2) * (ri->c_stride) + x/2] = cell->v; +} + +static void apply_vector_4x4(RoqContext *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->current_frame.data[0] + (y * ri->y_stride) + x; + uptr = ri->current_frame.data[1] + (y/2) * (ri->c_stride) + x/2; + vptr = ri->current_frame.data[2] + (y/2) * (ri->c_stride) + x/2; + + row_inc = ri->y_stride - 4; + c_row_inc = (ri->c_stride) - 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(RoqContext *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->current_frame.data[0] + (y * ri->y_stride) + x; + pb = ri->last_frame.data[0] + (my * ri->y_stride) + 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->y_stride; + pb += ri->y_stride; + } + + pa = ri->current_frame.data[1] + (y/2) * (ri->c_stride) + x/2; + pb = ri->last_frame.data[1] + (my/2) * (ri->c_stride) + (mx + 1)/2; + for(i = 0; i < 2; i++) { + pa[0] = pb[0]; + pa[1] = pb[1]; + pa += ri->c_stride; + pb += ri->c_stride; + } + + pa = ri->current_frame.data[2] + (y/2) * (ri->c_stride) + x/2; + pb = ri->last_frame.data[2] + (my/2) * (ri->c_stride) + (mx + 1)/2; + for(i = 0; i < 2; i++) { + pa[0] = pb[0]; + pa[1] = pb[1]; + pa += ri->c_stride; + pb += ri->c_stride; + } +} + +static void apply_motion_8x8(RoqContext *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->current_frame.data[0] + (y * ri->y_stride) + x; + pb = ri->last_frame.data[0] + (my * ri->y_stride) + 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->y_stride; + pb += ri->y_stride; + } + + pa = ri->current_frame.data[1] + (y/2) * (ri->c_stride) + x/2; + pb = ri->last_frame.data[1] + (my/2) * (ri->c_stride) + (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->c_stride; + pb += ri->c_stride; + } + + pa = ri->current_frame.data[2] + (y/2) * (ri->c_stride) + x/2; + pb = ri->last_frame.data[2] + (my/2) * (ri->c_stride) + (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->c_stride; + pb += ri->c_stride; + } +} + +static void roqvideo_decode_frame(RoqContext *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->avctx->width) { + xpos -= ri->avctx->width; + ypos += 16; + } + if(ypos >= ri->avctx->height) + break; + } +} + + +static int roq_decode_init(AVCodecContext *avctx) +{ + RoqContext *s = avctx->priv_data; + + s->avctx = avctx; + s->first_frame = 1; + avctx->pix_fmt = PIX_FMT_YUV420P; + avctx->has_b_frames = 0; + dsputil_init(&s->dsp, avctx); + + return 0; +} + +static int roq_decode_frame(AVCodecContext *avctx, + void *data, int *data_size, + uint8_t *buf, int buf_size) +{ + RoqContext *s = avctx->priv_data; + + *data_size = 0; + + if (avctx->get_buffer(avctx, &s->current_frame)) { + printf (" RoQ: get_buffer() failed\n"); + return -1; + } + s->y_stride = s->current_frame.linesize[0]; + s->c_stride = s->current_frame.linesize[1]; + + s->buf = buf; + s->size = buf_size; + roqvideo_decode_frame(s); + + /* release the last frame if it is allocated */ + if (s->first_frame) + s->first_frame = 0; + else + avctx->release_buffer(avctx, &s->last_frame); + + /* shuffle frames */ + s->last_frame = s->current_frame; + + *data_size = sizeof(AVFrame); + *(AVFrame*)data = s->current_frame; + + return buf_size; +} + +static int roq_decode_end(AVCodecContext *avctx) +{ + RoqContext *s = avctx->priv_data; + + /* release the last frame */ + avctx->release_buffer(avctx, &s->last_frame); + + return 0; +} + +AVCodec roq_decoder = { + "roqvideo", + CODEC_TYPE_VIDEO, + CODEC_ID_ROQ, + sizeof(RoqContext), + roq_decode_init, + NULL, + roq_decode_end, + roq_decode_frame, + CODEC_CAP_DR1, +}; diff --git a/libavformat/Makefile b/libavformat/Makefile index 15f7d07b6e..256edeccc7 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -14,7 +14,7 @@ PPOBJS= # mux and demuxes OBJS+=mpeg.o mpegts.o mpegtsenc.o ffm.o crc.o img.o raw.o rm.o \ avienc.o avidec.o wav.o swf.o au.o gif.o mov.o mpjpeg.o dvcore.o dv.o \ - yuv4mpeg.o 4xm.o flvenc.o flvdec.o movenc.o psxstr.o + yuv4mpeg.o 4xm.o flvenc.o flvdec.o movenc.o psxstr.o idroq.o ipmovie.o ifeq ($(CONFIG_RISKY),yes) OBJS+= asf.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 0fde3a2642..7496032647 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -52,6 +52,8 @@ void av_register_all(void) flvenc_init(); flvdec_init(); str_init(); + roq_init(); + ipmovie_init(); #ifdef AMR_NB amr_init(); diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 30cc1a5ff8..6ade72fee1 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -383,6 +383,12 @@ int fourxm_init(void); /* psxstr.c */ int str_init(void); +/* idroq.c */ +int roq_init(void); + +/* ipmovie.c */ +int ipmovie_init(void); + #include "rtp.h" #include "rtsp.h" diff --git a/libavformat/idroq.c b/libavformat/idroq.c new file mode 100644 index 0000000000..15c6d55131 --- /dev/null +++ b/libavformat/idroq.c @@ -0,0 +1,299 @@ +/* + * Id RoQ (.roq) File Demuxer + * Copyright (c) 2003 The ffmpeg Project + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * @file idroq.c + * Id RoQ format file demuxer + * by Mike Melanson (melanson@pcisys.net) + * for more information on the .roq file format, visit: + * http://www.csse.monash.edu.au/~timf/ + */ + +#include "avformat.h" + +#define LE_16(x) ((((uint8_t*)(x))[1] << 8) | ((uint8_t*)(x))[0]) +#define LE_32(x) ((((uint8_t*)(x))[3] << 24) | \ + (((uint8_t*)(x))[2] << 16) | \ + (((uint8_t*)(x))[1] << 8) | \ + ((uint8_t*)(x))[0]) + +#define RoQ_MAGIC_NUMBER 0x1084 +#define RoQ_CHUNK_PREAMBLE_SIZE 8 +#define RoQ_AUDIO_SAMPLE_RATE 22050 +#define RoQ_CHUNKS_TO_SCAN 30 + +#define RoQ_INFO 0x1001 +#define RoQ_QUAD_CODEBOOK 0x1002 +#define RoQ_QUAD_VQ 0x1011 +#define RoQ_SOUND_MONO 0x1020 +#define RoQ_SOUND_STEREO 0x1021 + +typedef struct RoqDemuxContext { + + int width; + int height; + int audio_channels; + int framerate; + int frame_pts_inc; + + int video_stream_index; + int audio_stream_index; + + int64_t video_pts; + unsigned int audio_frame_count; + +} RoqDemuxContext; + +static int roq_probe(AVProbeData *p) +{ + if ((LE_16(&p->buf[0]) != RoQ_MAGIC_NUMBER) || + (LE_32(&p->buf[2]) != 0xFFFFFFFF)) + return 0; + + return AVPROBE_SCORE_MAX; +} + +static int roq_read_header(AVFormatContext *s, + AVFormatParameters *ap) +{ + RoqDemuxContext *roq = s->priv_data; + ByteIOContext *pb = &s->pb; + AVStream *st; + unsigned char preamble[RoQ_CHUNK_PREAMBLE_SIZE]; + int i; + unsigned int chunk_size; + unsigned int chunk_type; + + /* get the main header */ + if (get_buffer(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE) != + RoQ_CHUNK_PREAMBLE_SIZE) + return AVERROR_IO; + roq->framerate = LE_16(&preamble[6]); + roq->frame_pts_inc = 90000 / roq->framerate; + + /* set the pts reference (1 pts = 1/90000) */ + s->pts_num = 1; + s->pts_den = 90000; + + /* init private context parameters */ + roq->width = roq->height = roq->audio_channels = roq->video_pts = + roq->audio_frame_count = 0; + + /* scan the first n chunks searching for A/V parameters */ + for (i = 0; i < RoQ_CHUNKS_TO_SCAN; i++) { + if (get_buffer(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE) != + RoQ_CHUNK_PREAMBLE_SIZE) + return AVERROR_IO; + + chunk_type = LE_16(&preamble[0]); + chunk_size = LE_32(&preamble[2]); + + switch (chunk_type) { + + case RoQ_INFO: + /* fetch the width and height; reuse the preamble bytes */ + if (get_buffer(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE) != + RoQ_CHUNK_PREAMBLE_SIZE) + return AVERROR_IO; + roq->width = LE_16(&preamble[0]); + roq->height = LE_16(&preamble[2]); + break; + + case RoQ_QUAD_CODEBOOK: + case RoQ_QUAD_VQ: + /* ignore during this scan */ + url_fseek(pb, chunk_size, SEEK_CUR); + break; + + case RoQ_SOUND_MONO: + roq->audio_channels = 1; + url_fseek(pb, chunk_size, SEEK_CUR); + break; + + case RoQ_SOUND_STEREO: + roq->audio_channels = 2; + url_fseek(pb, chunk_size, SEEK_CUR); + break; + + default: + printf (" unknown RoQ chunk type (%04X)\n", LE_16(&preamble[0])); + return AVERROR_INVALIDDATA; + break; + } + + /* if all necessary parameters have been gathered, exit early */ + if ((roq->width && roq->height) && roq->audio_channels) + break; + } + + /* seek back to the first chunk */ + url_fseek(pb, RoQ_CHUNK_PREAMBLE_SIZE, SEEK_SET); + + /* initialize the decoders */ + st = av_new_stream(s, 0); + if (!st) + return AVERROR_NOMEM; + roq->video_stream_index = st->index; + st->codec.codec_type = CODEC_TYPE_VIDEO; + st->codec.codec_id = CODEC_ID_ROQ; + st->codec.codec_tag = 0; /* no fourcc */ + st->codec.width = roq->width; + st->codec.height = roq->height; + + if (roq->audio_channels) { + st = av_new_stream(s, 0); + if (!st) + return AVERROR_NOMEM; + roq->audio_stream_index = st->index; + st->codec.codec_type = CODEC_TYPE_AUDIO; + st->codec.codec_id = CODEC_ID_ROQ_DPCM; + st->codec.codec_tag = 0; /* no tag */ + st->codec.channels = roq->audio_channels; + st->codec.sample_rate = RoQ_AUDIO_SAMPLE_RATE; + st->codec.bits_per_sample = 16; + st->codec.bit_rate = st->codec.channels * st->codec.sample_rate * + st->codec.bits_per_sample; + st->codec.block_align = st->codec.channels * st->codec.bits_per_sample; + } +printf (" video is %d x %d, audio is %d channels\n", + roq->width, roq->height, roq->audio_channels); + + return 0; +} + +static int roq_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + RoqDemuxContext *roq = s->priv_data; + ByteIOContext *pb = &s->pb; + int ret = 0; + unsigned int chunk_size; + unsigned int chunk_type; + unsigned int codebook_size; + unsigned char preamble[RoQ_CHUNK_PREAMBLE_SIZE]; + int packet_read = 0; + offset_t codebook_offset; + + while (!packet_read) { + + if (url_feof(&s->pb)) + return -EIO; + + /* get the next chunk preamble */ + if ((ret = get_buffer(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE)) != + RoQ_CHUNK_PREAMBLE_SIZE) + return -EIO; + + chunk_type = LE_16(&preamble[0]); + chunk_size = LE_32(&preamble[2]); + + switch (chunk_type) { + + case RoQ_INFO: + /* don't care about this chunk anymore */ + url_fseek(pb, RoQ_CHUNK_PREAMBLE_SIZE, SEEK_CUR); + break; + + case RoQ_QUAD_CODEBOOK: + /* packet needs to contain both this codebook and next VQ chunk */ + codebook_offset = url_ftell(pb) - RoQ_CHUNK_PREAMBLE_SIZE; + codebook_size = chunk_size; + url_fseek(pb, codebook_size, SEEK_CUR); + if (get_buffer(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE) != + RoQ_CHUNK_PREAMBLE_SIZE) + return -EIO; + chunk_size = LE_32(&preamble[2]) + RoQ_CHUNK_PREAMBLE_SIZE * 2 + + codebook_size; + + /* rewind */ + url_fseek(pb, codebook_offset, SEEK_SET); + + /* load up the packet */ + if (av_new_packet(pkt, chunk_size)) + return -EIO; + pkt->stream_index = roq->video_stream_index; + pkt->pts = roq->video_pts; + ret = get_buffer(pb, pkt->data, chunk_size); + if (ret != chunk_size) + ret = -EIO; + + roq->video_pts += roq->frame_pts_inc; + packet_read = 1; + break; + + case RoQ_SOUND_MONO: + case RoQ_SOUND_STEREO: + case RoQ_QUAD_VQ: + /* load up the packet */ + if (av_new_packet(pkt, chunk_size + RoQ_CHUNK_PREAMBLE_SIZE)) + return -EIO; + /* copy over preamble */ + memcpy(pkt->data, preamble, RoQ_CHUNK_PREAMBLE_SIZE); + + if (chunk_type == RoQ_QUAD_VQ) { + pkt->stream_index = roq->video_stream_index; + pkt->pts = roq->video_pts; + roq->video_pts += roq->frame_pts_inc; + } else { + pkt->stream_index = roq->audio_stream_index; + pkt->pts = roq->audio_frame_count; + pkt->pts *= 90000; + pkt->pts /= RoQ_AUDIO_SAMPLE_RATE; + roq->audio_frame_count += (chunk_size / roq->audio_channels); + } + + ret = get_buffer(pb, pkt->data, chunk_size); + if (ret != chunk_size) + ret = -EIO; + + packet_read = 1; + break; + + default: + printf (" unknown RoQ chunk (%04X)\n", chunk_type); + return AVERROR_INVALIDDATA; + break; + } + } + + return ret; +} + +static int roq_read_close(AVFormatContext *s) +{ +// RoqDemuxContext *roq = (RoqDemuxContext *)s->priv_data; + + return 0; +} + +static AVInputFormat roq_iformat = { + "RoQ", + "Id RoQ format", + sizeof(RoqDemuxContext), + roq_probe, + roq_read_header, + roq_read_packet, + roq_read_close, +}; + +int roq_init(void) +{ + av_register_input_format(&roq_iformat); + return 0; +} diff --git a/libavformat/ipmovie.c b/libavformat/ipmovie.c new file mode 100644 index 0000000000..ce37dedf06 --- /dev/null +++ b/libavformat/ipmovie.c @@ -0,0 +1,618 @@ +/* + * Interplay MVE File Demuxer + * Copyright (c) 2003 The ffmpeg Project + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * @file ipmovie.c + * Interplay MVE file demuxer + * by Mike Melanson (melanson@pcisys.net) + * For more information regarding the Interplay MVE file format, visit: + * http://www.pcisys.net/~melanson/codecs/ + * The aforementioned site also contains a command line utility for parsing + * IP MVE files so that you can get a good idea of the typical structure of + * such files. This demuxer is not the best example to use if you are trying + * to write your own as it uses a rather roundabout approach for splitting + * up and sending out the chunks. + */ + +#include "avformat.h" + +/* debugging support: #define DEBUG_IPMOVIE as non-zero to see extremely + * verbose information about the demux process */ +#define DEBUG_IPMOVIE 0 + +#if DEBUG_IPMOVIE +#define debug_ipmovie printf +#else +static inline void debug_ipmovie(const char *format, ...) { } +#endif + + +#define LE_16(x) ((((uint8_t*)(x))[1] << 8) | ((uint8_t*)(x))[0]) +#define LE_32(x) ((((uint8_t*)(x))[3] << 24) | \ + (((uint8_t*)(x))[2] << 16) | \ + (((uint8_t*)(x))[1] << 8) | \ + ((uint8_t*)(x))[0]) + +#define IPMOVIE_SIGNATURE "Interplay MVE File\x1A\0" +#define IPMOVIE_SIGNATURE_SIZE 20 +#define CHUNK_PREAMBLE_SIZE 4 +#define OPCODE_PREAMBLE_SIZE 4 + +#define CHUNK_INIT_AUDIO 0x0000 +#define CHUNK_AUDIO_ONLY 0x0001 +#define CHUNK_INIT_VIDEO 0x0002 +#define CHUNK_VIDEO 0x0003 +#define CHUNK_SHUTDOWN 0x0004 +#define CHUNK_END 0x0005 +/* these last types are used internally */ +#define CHUNK_DONE 0xFFFC +#define CHUNK_NOMEM 0xFFFD +#define CHUNK_EOF 0xFFFE +#define CHUNK_BAD 0xFFFF + +#define OPCODE_END_OF_STREAM 0x00 +#define OPCODE_END_OF_CHUNK 0x01 +#define OPCODE_CREATE_TIMER 0x02 +#define OPCODE_INIT_AUDIO_BUFFERS 0x03 +#define OPCODE_START_STOP_AUDIO 0x04 +#define OPCODE_INIT_VIDEO_BUFFERS 0x05 +#define OPCODE_UNKNOWN_06 0x06 +#define OPCODE_SEND_BUFFER 0x07 +#define OPCODE_AUDIO_FRAME 0x08 +#define OPCODE_SILENCE_FRAME 0x09 +#define OPCODE_INIT_VIDEO_MODE 0x0A +#define OPCODE_CREATE_GRADIENT 0x0B +#define OPCODE_SET_PALETTE 0x0C +#define OPCODE_SET_PALETTE_COMPRESSED 0x0D +#define OPCODE_UNKNOWN_0E 0x0E +#define OPCODE_SET_DECODING_MAP 0x0F +#define OPCODE_UNKNOWN_10 0x10 +#define OPCODE_VIDEO_DATA 0x11 +#define OPCODE_UNKNOWN_12 0x12 +#define OPCODE_UNKNOWN_13 0x13 +#define OPCODE_UNKNOWN_14 0x14 +#define OPCODE_UNKNOWN_15 0x15 + +#define PALETTE_COUNT 256 + +typedef struct IPMVEContext { + + unsigned char *buf; + int buf_size; + + int fps; + int frame_pts_inc; + + unsigned int video_width; + unsigned int video_height; + int64_t video_pts; + + unsigned int audio_bits; + unsigned int audio_channels; + unsigned int audio_sample_rate; + unsigned int audio_type; + unsigned int audio_frame_count; + + int video_stream_index; + int audio_stream_index; + + offset_t audio_chunk_offset; + int audio_chunk_size; + offset_t video_chunk_offset; + int video_chunk_size; + offset_t decode_map_chunk_offset; + int decode_map_chunk_size; + + offset_t next_chunk_offset; + +} IPMVEContext; + +static int load_ipmovie_packet(IPMVEContext *s, ByteIOContext *pb, + AVPacket *pkt) { + + int chunk_type; + int64_t audio_pts = 0; + + if (s->audio_chunk_offset) { + + url_fseek(pb, s->audio_chunk_offset, SEEK_SET); + s->audio_chunk_offset = 0; + + /* figure out the audio pts */ + audio_pts = 90000; + audio_pts *= s->audio_frame_count; + audio_pts /= s->audio_sample_rate; + + if (av_new_packet(pkt, s->audio_chunk_size)) + return CHUNK_NOMEM; + + pkt->stream_index = s->audio_stream_index; + pkt->pts = audio_pts; + if (get_buffer(pb, pkt->data, s->audio_chunk_size) != + s->audio_chunk_size) { + av_free_packet(pkt); + return CHUNK_EOF; + } + + /* audio frame maintenance */ + if (s->audio_type != CODEC_ID_INTERPLAY_DPCM) + s->audio_frame_count += + (s->audio_chunk_size / s->audio_channels / (s->audio_bits / 8)); + else + s->audio_frame_count += + (s->audio_chunk_size - 6) / s->audio_channels; + + debug_ipmovie("sending audio frame with pts %lld (%d audio frames)\n", + audio_pts, s->audio_frame_count); + + chunk_type = CHUNK_VIDEO; + + } else if (s->decode_map_chunk_offset) { + + url_fseek(pb, s->decode_map_chunk_offset, SEEK_SET); + s->decode_map_chunk_offset = 0; + + if (av_new_packet(pkt, s->decode_map_chunk_size)) + return CHUNK_NOMEM; + + pkt->stream_index = s->video_stream_index; + pkt->pts = s->video_pts; + if (get_buffer(pb, pkt->data, s->decode_map_chunk_size) != + s->decode_map_chunk_size) { + av_free_packet(pkt); + return CHUNK_EOF; + } + + chunk_type = CHUNK_VIDEO; + + } else if (s->video_chunk_offset) { + + url_fseek(pb, s->video_chunk_offset, SEEK_SET); + s->video_chunk_offset = 0; + + if (av_new_packet(pkt, s->video_chunk_size)) + return CHUNK_NOMEM; + + pkt->stream_index = s->video_stream_index; + pkt->pts = s->video_pts; + if (get_buffer(pb, pkt->data, s->video_chunk_size) != + s->video_chunk_size) { + av_free_packet(pkt); + return CHUNK_EOF; + } + + s->video_pts += s->frame_pts_inc; + + chunk_type = CHUNK_VIDEO; + + } else { + + url_fseek(pb, s->next_chunk_offset, SEEK_SET); + chunk_type = CHUNK_DONE; + + } + + return chunk_type; +} + +/* This function loads and processes a single chunk in an IP movie file. + * It returns the type of chunk that was processed. */ +static int process_ipmovie_chunk(IPMVEContext *s, ByteIOContext *pb, + AVPacket *pkt) +{ + unsigned char chunk_preamble[CHUNK_PREAMBLE_SIZE]; + int chunk_type; + int chunk_size; + unsigned char opcode_preamble[OPCODE_PREAMBLE_SIZE]; + unsigned char opcode_type; + unsigned char opcode_version; + int opcode_size; + unsigned char scratch[1024]; + int j; + int first_color, last_color; + int audio_flags; + + /* see if there are any pending packets */ + chunk_type = load_ipmovie_packet(s, pb, pkt); + if ((chunk_type == CHUNK_VIDEO) && (chunk_type != CHUNK_DONE)) + return chunk_type; + + /* read the next chunk, wherever the file happens to be pointing */ + if (url_feof(pb)) + return CHUNK_EOF; + if (get_buffer(pb, chunk_preamble, CHUNK_PREAMBLE_SIZE) != + CHUNK_PREAMBLE_SIZE) + return CHUNK_BAD; + chunk_size = LE_16(&chunk_preamble[0]); + chunk_type = LE_16(&chunk_preamble[2]); + + debug_ipmovie("chunk type 0x%04X, 0x%04X bytes: ", chunk_type, chunk_size); + + switch (chunk_type) { + + case CHUNK_INIT_AUDIO: + debug_ipmovie("initialize audio\n"); + break; + + case CHUNK_AUDIO_ONLY: + debug_ipmovie("audio only\n"); + break; + + case CHUNK_INIT_VIDEO: + debug_ipmovie("initialize video\n"); + break; + + case CHUNK_VIDEO: + debug_ipmovie("video (and audio)\n"); + break; + + case CHUNK_SHUTDOWN: + debug_ipmovie("shutdown\n"); + break; + + case CHUNK_END: + debug_ipmovie("end\n"); + break; + + default: + debug_ipmovie("invalid chunk\n"); + chunk_type = CHUNK_BAD; + break; + + } + + while ((chunk_size > 0) && (chunk_type != CHUNK_BAD)) { + + /* read the next chunk, wherever the file happens to be pointing */ + if (url_feof(pb)) { + chunk_type = CHUNK_EOF; + break; + } + if (get_buffer(pb, opcode_preamble, CHUNK_PREAMBLE_SIZE) != + CHUNK_PREAMBLE_SIZE) { + chunk_type = CHUNK_BAD; + break; + } + + opcode_size = LE_16(&opcode_preamble[0]); + opcode_type = opcode_preamble[2]; + opcode_version = opcode_preamble[3]; + + chunk_size -= OPCODE_PREAMBLE_SIZE; + chunk_size -= opcode_size; + if (chunk_size < 0) { + debug_ipmovie("chunk_size countdown just went negative\n"); + chunk_type = CHUNK_BAD; + break; + } + + debug_ipmovie(" opcode type %02X, version %d, 0x%04X bytes: ", + opcode_type, opcode_version, opcode_size); + switch (opcode_type) { + + case OPCODE_END_OF_STREAM: + debug_ipmovie("end of stream\n"); + url_fseek(pb, opcode_size, SEEK_CUR); + break; + + case OPCODE_END_OF_CHUNK: + debug_ipmovie("end of chunk\n"); + url_fseek(pb, opcode_size, SEEK_CUR); + break; + + case OPCODE_CREATE_TIMER: + debug_ipmovie("create timer\n"); + if ((opcode_version > 0) || (opcode_size > 6)) { + debug_ipmovie("bad create_timer opcode\n"); + chunk_type = CHUNK_BAD; + break; + } + if (get_buffer(pb, scratch, opcode_size) != + opcode_size) { + chunk_type = CHUNK_BAD; + break; + } + s->fps = 1000000 / (LE_32(&scratch[0]) * LE_16(&scratch[4])); + s->fps++; /* above calculation usually yields 14.9; we need 15 */ + s->frame_pts_inc = 90000 / s->fps; + debug_ipmovie("%d frames/second (timer div = %d, subdiv = %d)\n", + s->fps, LE_32(&scratch[0]), LE_16(&scratch[4])); + break; + + case OPCODE_INIT_AUDIO_BUFFERS: + debug_ipmovie("initialize audio buffers\n"); + if ((opcode_version > 1) || (opcode_size > 10)) { + debug_ipmovie("bad init_audio_buffers opcode\n"); + chunk_type = CHUNK_BAD; + break; + } + if (get_buffer(pb, scratch, opcode_size) != + opcode_size) { + chunk_type = CHUNK_BAD; + break; + } + s->audio_sample_rate = LE_16(&scratch[4]); + audio_flags = LE_16(&scratch[2]); + /* bit 0 of the flags: 0 = mono, 1 = stereo */ + s->audio_channels = (audio_flags & 1) + 1; + /* bit 1 of the flags: 0 = 8 bit, 1 = 16 bit */ + s->audio_bits = (((audio_flags >> 1) & 1) + 1) * 8; + /* bit 2 indicates compressed audio in version 1 opcode */ + if ((opcode_version == 1) && (audio_flags & 0x4)) + s->audio_type = CODEC_ID_INTERPLAY_DPCM; + else if (s->audio_bits == 16) + s->audio_type = CODEC_ID_PCM_S16LE; + else + s->audio_type = CODEC_ID_PCM_U8; + debug_ipmovie("audio: %d bits, %d Hz, %s, %s format\n", + s->audio_bits, + s->audio_sample_rate, + (s->audio_channels == 2) ? "stereo" : "mono", + (s->audio_type == CODEC_ID_INTERPLAY_DPCM) ? + "Interplay audio" : "PCM"); + break; + + case OPCODE_START_STOP_AUDIO: + debug_ipmovie("start/stop audio\n"); + url_fseek(pb, opcode_size, SEEK_CUR); + break; + + case OPCODE_INIT_VIDEO_BUFFERS: + debug_ipmovie("initialize video buffers\n"); + if ((opcode_version > 2) || (opcode_size > 8)) { + debug_ipmovie("bad init_video_buffers opcode\n"); + chunk_type = CHUNK_BAD; + break; + } + if (get_buffer(pb, scratch, opcode_size) != + opcode_size) { + chunk_type = CHUNK_BAD; + break; + } + s->video_width = LE_16(&scratch[0]) * 8; + s->video_height = LE_16(&scratch[2]) * 8; + debug_ipmovie("video resolution: %d x %d\n", + s->video_width, s->video_height); + break; + + case OPCODE_UNKNOWN_06: + case OPCODE_UNKNOWN_0E: + case OPCODE_UNKNOWN_10: + case OPCODE_UNKNOWN_12: + case OPCODE_UNKNOWN_13: + case OPCODE_UNKNOWN_14: + case OPCODE_UNKNOWN_15: + debug_ipmovie("unknown (but documented) opcode %02X\n", opcode_type); + url_fseek(pb, opcode_size, SEEK_CUR); + break; + + case OPCODE_SEND_BUFFER: + debug_ipmovie("send buffer\n"); + url_fseek(pb, opcode_size, SEEK_CUR); + break; + + case OPCODE_AUDIO_FRAME: + debug_ipmovie("audio frame\n"); + + /* log position and move on for now */ + s->audio_chunk_offset = url_ftell(pb); + s->audio_chunk_size = opcode_size; + url_fseek(pb, opcode_size, SEEK_CUR); + break; + + case OPCODE_SILENCE_FRAME: + debug_ipmovie("silence frame\n"); + url_fseek(pb, opcode_size, SEEK_CUR); + break; + + case OPCODE_INIT_VIDEO_MODE: + debug_ipmovie("initialize video mode\n"); + url_fseek(pb, opcode_size, SEEK_CUR); + break; + + case OPCODE_CREATE_GRADIENT: + debug_ipmovie("create gradient\n"); + url_fseek(pb, opcode_size, SEEK_CUR); + break; + + case OPCODE_SET_PALETTE: + debug_ipmovie("set palette\n"); + /* check for the logical maximum palette size + * (3 * 256 + 4 bytes) */ + if (opcode_size > 0x304) { + debug_ipmovie("demux_ipmovie: set_palette opcode too large\n"); + chunk_type = CHUNK_BAD; + break; + } + if (get_buffer(pb, scratch, opcode_size) != opcode_size) { + chunk_type = CHUNK_BAD; + break; + } + + /* load the palette into internal data structure */ + first_color = LE_16(&scratch[0]); + last_color = LE_16(&scratch[2]); + /* sanity check (since they are 16 bit values) */ + if ((first_color > 0xFF) || (last_color > 0xFF)) { + debug_ipmovie("demux_ipmovie: set_palette indices out of range (%d -> %d)\n", + first_color, last_color); + chunk_type = CHUNK_BAD; + break; + } + j = 4; /* offset of first palette data */ +#if 0 + for (i = first_color; i <= last_color; i++) { + s->palette[i].r = scratch[j++] * 4; + s->palette[i].g = scratch[j++] * 4; + s->palette[i].b = scratch[j++] * 4; + } +#endif + break; + + case OPCODE_SET_PALETTE_COMPRESSED: + debug_ipmovie("set palette compressed\n"); + url_fseek(pb, opcode_size, SEEK_CUR); + break; + + case OPCODE_SET_DECODING_MAP: + debug_ipmovie("set decoding map\n"); + + /* log position and move on for now */ + s->decode_map_chunk_offset = url_ftell(pb); + s->decode_map_chunk_size = opcode_size; + url_fseek(pb, opcode_size, SEEK_CUR); + break; + + case OPCODE_VIDEO_DATA: + debug_ipmovie("set video data\n"); + + /* log position and move on for now */ + s->video_chunk_offset = url_ftell(pb); + s->video_chunk_size = opcode_size; + url_fseek(pb, opcode_size, SEEK_CUR); + break; + + default: + debug_ipmovie("*** unknown opcode type\n"); + chunk_type = CHUNK_BAD; + break; + + } + } + + /* make a note of where the stream is sitting */ + s->next_chunk_offset = url_ftell(pb); + + /* dispatch the first of any pending packets */ + if ((chunk_type == CHUNK_VIDEO) || (chunk_type == CHUNK_AUDIO_ONLY)) + chunk_type = load_ipmovie_packet(s, pb, pkt); + + return chunk_type; +} + +static int ipmovie_probe(AVProbeData *p) +{ + if (p->buf_size < IPMOVIE_SIGNATURE_SIZE) + return 0; + if (strncmp(p->buf, IPMOVIE_SIGNATURE, IPMOVIE_SIGNATURE_SIZE) != 0) + return 0; + + return AVPROBE_SCORE_MAX; +} + +static int ipmovie_read_header(AVFormatContext *s, + AVFormatParameters *ap) +{ + IPMVEContext *ipmovie = (IPMVEContext *)s->priv_data; + ByteIOContext *pb = &s->pb; + AVPacket pkt; + AVStream *st; + + /* initialize private context members */ + ipmovie->video_pts = ipmovie->audio_frame_count = 0; + ipmovie->audio_chunk_offset = ipmovie->video_chunk_offset = + ipmovie->decode_map_chunk_offset = 0; + + /* on the first read, this will position the stream at the first chunk */ + ipmovie->next_chunk_offset = IPMOVIE_SIGNATURE_SIZE + 6; + + /* process the first chunk which should be CHUNK_INIT_VIDEO */ + if (process_ipmovie_chunk(ipmovie, pb, &pkt) != CHUNK_INIT_VIDEO) + return AVERROR_INVALIDDATA; + + /* process the next chunk which should be CHUNK_INIT_AUDIO */ + if (process_ipmovie_chunk(ipmovie, pb, &pkt) != CHUNK_INIT_AUDIO) + return AVERROR_INVALIDDATA; + + /* set the pts reference (1 pts = 1/90000) */ + s->pts_num = 1; + s->pts_den = 90000; + + /* initialize the stream decoders */ + st = av_new_stream(s, 0); + if (!st) + return AVERROR_NOMEM; + ipmovie->video_stream_index = st->index; + st->codec.codec_type = CODEC_TYPE_VIDEO; + st->codec.codec_id = CODEC_ID_INTERPLAY_VIDEO; + st->codec.codec_tag = 0; /* no fourcc */ + st->codec.width = ipmovie->video_width; + st->codec.height = ipmovie->video_height; + + st = av_new_stream(s, 0); + if (!st) + return AVERROR_NOMEM; + ipmovie->audio_stream_index = st->index; + st->codec.codec_type = CODEC_TYPE_AUDIO; + st->codec.codec_id = ipmovie->audio_type; + st->codec.codec_tag = 0; /* no tag */ + st->codec.channels = ipmovie->audio_channels; + st->codec.sample_rate = ipmovie->audio_sample_rate; + st->codec.bits_per_sample = ipmovie->audio_bits; + st->codec.bit_rate = st->codec.channels * st->codec.sample_rate * + st->codec.bits_per_sample / + (st->codec.codec_id == CODEC_ID_INTERPLAY_DPCM) ? 2 : 1; + st->codec.block_align = st->codec.channels * st->codec.bits_per_sample; + + return 0; +} + +static int ipmovie_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + IPMVEContext *ipmovie = (IPMVEContext *)s->priv_data; + ByteIOContext *pb = &s->pb; + int ret; + + ret = process_ipmovie_chunk(ipmovie, pb, pkt); + if (ret == CHUNK_BAD) + ret = AVERROR_INVALIDDATA; + else if (ret == CHUNK_EOF) + ret = -EIO; + else if (ret == CHUNK_NOMEM) + ret = AVERROR_NOMEM; + else + ret = 0; + + return ret; +} + +static int ipmovie_read_close(AVFormatContext *s) +{ +// IPMVEContext *ipmovie = (IPMVEContext *)s->priv_data; + + return 0; +} + +static AVInputFormat ipmovie_iformat = { + "ipmovie", + "Interplay MVE format", + sizeof(IPMVEContext), + ipmovie_probe, + ipmovie_read_header, + ipmovie_read_packet, + ipmovie_read_close, +}; + +int ipmovie_init(void) +{ + av_register_input_format(&ipmovie_iformat); + return 0; +} + -- cgit v1.2.3