Originally committed as revision 2195 to svn://svn.ffmpeg.org/ffmpeg/trunktags/v0.5
| @@ -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) | |||
| @@ -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 | |||
| @@ -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) \ | |||
| @@ -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, | |||
| }; | |||
| @@ -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 <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <unistd.h> | |||
| #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, | |||
| }; | |||
| @@ -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 <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <unistd.h> | |||
| #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, | |||
| }; | |||
| @@ -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 | |||
| @@ -52,6 +52,8 @@ void av_register_all(void) | |||
| flvenc_init(); | |||
| flvdec_init(); | |||
| str_init(); | |||
| roq_init(); | |||
| ipmovie_init(); | |||
| #ifdef AMR_NB | |||
| amr_init(); | |||
| @@ -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" | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||