yading@11: /* yading@11: * Interplay MVE File Demuxer yading@11: * Copyright (c) 2003 The ffmpeg Project yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: /** yading@11: * @file yading@11: * Interplay MVE file demuxer yading@11: * by Mike Melanson (melanson@pcisys.net) yading@11: * For more information regarding the Interplay MVE file format, visit: yading@11: * http://www.pcisys.net/~melanson/codecs/ yading@11: * The aforementioned site also contains a command line utility for parsing yading@11: * IP MVE files so that you can get a good idea of the typical structure of yading@11: * such files. This demuxer is not the best example to use if you are trying yading@11: * to write your own as it uses a rather roundabout approach for splitting yading@11: * up and sending out the chunks. yading@11: */ yading@11: yading@11: #include "libavutil/channel_layout.h" yading@11: #include "libavutil/intreadwrite.h" yading@11: #include "avformat.h" yading@11: #include "internal.h" yading@11: yading@11: #define CHUNK_PREAMBLE_SIZE 4 yading@11: #define OPCODE_PREAMBLE_SIZE 4 yading@11: yading@11: #define CHUNK_INIT_AUDIO 0x0000 yading@11: #define CHUNK_AUDIO_ONLY 0x0001 yading@11: #define CHUNK_INIT_VIDEO 0x0002 yading@11: #define CHUNK_VIDEO 0x0003 yading@11: #define CHUNK_SHUTDOWN 0x0004 yading@11: #define CHUNK_END 0x0005 yading@11: /* these last types are used internally */ yading@11: #define CHUNK_DONE 0xFFFC yading@11: #define CHUNK_NOMEM 0xFFFD yading@11: #define CHUNK_EOF 0xFFFE yading@11: #define CHUNK_BAD 0xFFFF yading@11: yading@11: #define OPCODE_END_OF_STREAM 0x00 yading@11: #define OPCODE_END_OF_CHUNK 0x01 yading@11: #define OPCODE_CREATE_TIMER 0x02 yading@11: #define OPCODE_INIT_AUDIO_BUFFERS 0x03 yading@11: #define OPCODE_START_STOP_AUDIO 0x04 yading@11: #define OPCODE_INIT_VIDEO_BUFFERS 0x05 yading@11: #define OPCODE_UNKNOWN_06 0x06 yading@11: #define OPCODE_SEND_BUFFER 0x07 yading@11: #define OPCODE_AUDIO_FRAME 0x08 yading@11: #define OPCODE_SILENCE_FRAME 0x09 yading@11: #define OPCODE_INIT_VIDEO_MODE 0x0A yading@11: #define OPCODE_CREATE_GRADIENT 0x0B yading@11: #define OPCODE_SET_PALETTE 0x0C yading@11: #define OPCODE_SET_PALETTE_COMPRESSED 0x0D yading@11: #define OPCODE_UNKNOWN_0E 0x0E yading@11: #define OPCODE_SET_DECODING_MAP 0x0F yading@11: #define OPCODE_UNKNOWN_10 0x10 yading@11: #define OPCODE_VIDEO_DATA 0x11 yading@11: #define OPCODE_UNKNOWN_12 0x12 yading@11: #define OPCODE_UNKNOWN_13 0x13 yading@11: #define OPCODE_UNKNOWN_14 0x14 yading@11: #define OPCODE_UNKNOWN_15 0x15 yading@11: yading@11: #define PALETTE_COUNT 256 yading@11: yading@11: typedef struct IPMVEContext { yading@11: yading@11: unsigned char *buf; yading@11: int buf_size; yading@11: yading@11: uint64_t frame_pts_inc; yading@11: yading@11: unsigned int video_bpp; yading@11: unsigned int video_width; yading@11: unsigned int video_height; yading@11: int64_t video_pts; yading@11: uint32_t palette[256]; yading@11: int has_palette; yading@11: int changed; yading@11: yading@11: unsigned int audio_bits; yading@11: unsigned int audio_channels; yading@11: unsigned int audio_sample_rate; yading@11: enum AVCodecID audio_type; yading@11: unsigned int audio_frame_count; yading@11: yading@11: int video_stream_index; yading@11: int audio_stream_index; yading@11: yading@11: int64_t audio_chunk_offset; yading@11: int audio_chunk_size; yading@11: int64_t video_chunk_offset; yading@11: int video_chunk_size; yading@11: int64_t decode_map_chunk_offset; yading@11: int decode_map_chunk_size; yading@11: yading@11: int64_t next_chunk_offset; yading@11: yading@11: } IPMVEContext; yading@11: yading@11: static int load_ipmovie_packet(IPMVEContext *s, AVIOContext *pb, yading@11: AVPacket *pkt) { yading@11: yading@11: int chunk_type; yading@11: yading@11: if (s->audio_chunk_offset && s->audio_channels && s->audio_bits) { yading@11: if (s->audio_type == AV_CODEC_ID_NONE) { yading@11: av_log(NULL, AV_LOG_ERROR, "Can not read audio packet before" yading@11: "audio codec is known\n"); yading@11: return CHUNK_BAD; yading@11: } yading@11: yading@11: /* adjust for PCM audio by skipping chunk header */ yading@11: if (s->audio_type != AV_CODEC_ID_INTERPLAY_DPCM) { yading@11: s->audio_chunk_offset += 6; yading@11: s->audio_chunk_size -= 6; yading@11: } yading@11: yading@11: avio_seek(pb, s->audio_chunk_offset, SEEK_SET); yading@11: s->audio_chunk_offset = 0; yading@11: yading@11: if (s->audio_chunk_size != av_get_packet(pb, pkt, s->audio_chunk_size)) yading@11: return CHUNK_EOF; yading@11: yading@11: pkt->stream_index = s->audio_stream_index; yading@11: pkt->pts = s->audio_frame_count; yading@11: yading@11: /* audio frame maintenance */ yading@11: if (s->audio_type != AV_CODEC_ID_INTERPLAY_DPCM) yading@11: s->audio_frame_count += yading@11: (s->audio_chunk_size / s->audio_channels / (s->audio_bits / 8)); yading@11: else yading@11: s->audio_frame_count += yading@11: (s->audio_chunk_size - 6 - s->audio_channels) / s->audio_channels; yading@11: yading@11: av_dlog(NULL, "sending audio frame with pts %"PRId64" (%d audio frames)\n", yading@11: pkt->pts, s->audio_frame_count); yading@11: yading@11: chunk_type = CHUNK_VIDEO; yading@11: yading@11: } else if (s->decode_map_chunk_offset) { yading@11: yading@11: /* send both the decode map and the video data together */ yading@11: yading@11: if (av_new_packet(pkt, s->decode_map_chunk_size + s->video_chunk_size)) yading@11: return CHUNK_NOMEM; yading@11: yading@11: if (s->has_palette) { yading@11: uint8_t *pal; yading@11: yading@11: pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, yading@11: AVPALETTE_SIZE); yading@11: if (pal) { yading@11: memcpy(pal, s->palette, AVPALETTE_SIZE); yading@11: s->has_palette = 0; yading@11: } yading@11: } yading@11: yading@11: if (s->changed) { yading@11: ff_add_param_change(pkt, 0, 0, 0, s->video_width, s->video_height); yading@11: s->changed = 0; yading@11: } yading@11: pkt->pos= s->decode_map_chunk_offset; yading@11: avio_seek(pb, s->decode_map_chunk_offset, SEEK_SET); yading@11: s->decode_map_chunk_offset = 0; yading@11: yading@11: if (avio_read(pb, pkt->data, s->decode_map_chunk_size) != yading@11: s->decode_map_chunk_size) { yading@11: av_free_packet(pkt); yading@11: return CHUNK_EOF; yading@11: } yading@11: yading@11: avio_seek(pb, s->video_chunk_offset, SEEK_SET); yading@11: s->video_chunk_offset = 0; yading@11: yading@11: if (avio_read(pb, pkt->data + s->decode_map_chunk_size, yading@11: s->video_chunk_size) != s->video_chunk_size) { yading@11: av_free_packet(pkt); yading@11: return CHUNK_EOF; yading@11: } yading@11: yading@11: pkt->stream_index = s->video_stream_index; yading@11: pkt->pts = s->video_pts; yading@11: yading@11: av_dlog(NULL, "sending video frame with pts %"PRId64"\n", pkt->pts); yading@11: yading@11: s->video_pts += s->frame_pts_inc; yading@11: yading@11: chunk_type = CHUNK_VIDEO; yading@11: yading@11: } else { yading@11: yading@11: avio_seek(pb, s->next_chunk_offset, SEEK_SET); yading@11: chunk_type = CHUNK_DONE; yading@11: yading@11: } yading@11: yading@11: return chunk_type; yading@11: } yading@11: yading@11: /* This function loads and processes a single chunk in an IP movie file. yading@11: * It returns the type of chunk that was processed. */ yading@11: static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb, yading@11: AVPacket *pkt) yading@11: { yading@11: unsigned char chunk_preamble[CHUNK_PREAMBLE_SIZE]; yading@11: int chunk_type; yading@11: int chunk_size; yading@11: unsigned char opcode_preamble[OPCODE_PREAMBLE_SIZE]; yading@11: unsigned char opcode_type; yading@11: unsigned char opcode_version; yading@11: int opcode_size; yading@11: unsigned char scratch[1024]; yading@11: int i, j; yading@11: int first_color, last_color; yading@11: int audio_flags; yading@11: unsigned char r, g, b; yading@11: unsigned int width, height; yading@11: yading@11: /* see if there are any pending packets */ yading@11: chunk_type = load_ipmovie_packet(s, pb, pkt); yading@11: if (chunk_type != CHUNK_DONE) yading@11: return chunk_type; yading@11: yading@11: /* read the next chunk, wherever the file happens to be pointing */ yading@11: if (url_feof(pb)) yading@11: return CHUNK_EOF; yading@11: if (avio_read(pb, chunk_preamble, CHUNK_PREAMBLE_SIZE) != yading@11: CHUNK_PREAMBLE_SIZE) yading@11: return CHUNK_BAD; yading@11: chunk_size = AV_RL16(&chunk_preamble[0]); yading@11: chunk_type = AV_RL16(&chunk_preamble[2]); yading@11: yading@11: av_dlog(NULL, "chunk type 0x%04X, 0x%04X bytes: ", chunk_type, chunk_size); yading@11: yading@11: switch (chunk_type) { yading@11: yading@11: case CHUNK_INIT_AUDIO: yading@11: av_dlog(NULL, "initialize audio\n"); yading@11: break; yading@11: yading@11: case CHUNK_AUDIO_ONLY: yading@11: av_dlog(NULL, "audio only\n"); yading@11: break; yading@11: yading@11: case CHUNK_INIT_VIDEO: yading@11: av_dlog(NULL, "initialize video\n"); yading@11: break; yading@11: yading@11: case CHUNK_VIDEO: yading@11: av_dlog(NULL, "video (and audio)\n"); yading@11: break; yading@11: yading@11: case CHUNK_SHUTDOWN: yading@11: av_dlog(NULL, "shutdown\n"); yading@11: break; yading@11: yading@11: case CHUNK_END: yading@11: av_dlog(NULL, "end\n"); yading@11: break; yading@11: yading@11: default: yading@11: av_dlog(NULL, "invalid chunk\n"); yading@11: chunk_type = CHUNK_BAD; yading@11: break; yading@11: yading@11: } yading@11: yading@11: while ((chunk_size > 0) && (chunk_type != CHUNK_BAD)) { yading@11: yading@11: /* read the next chunk, wherever the file happens to be pointing */ yading@11: if (url_feof(pb)) { yading@11: chunk_type = CHUNK_EOF; yading@11: break; yading@11: } yading@11: if (avio_read(pb, opcode_preamble, CHUNK_PREAMBLE_SIZE) != yading@11: CHUNK_PREAMBLE_SIZE) { yading@11: chunk_type = CHUNK_BAD; yading@11: break; yading@11: } yading@11: yading@11: opcode_size = AV_RL16(&opcode_preamble[0]); yading@11: opcode_type = opcode_preamble[2]; yading@11: opcode_version = opcode_preamble[3]; yading@11: yading@11: chunk_size -= OPCODE_PREAMBLE_SIZE; yading@11: chunk_size -= opcode_size; yading@11: if (chunk_size < 0) { yading@11: av_dlog(NULL, "chunk_size countdown just went negative\n"); yading@11: chunk_type = CHUNK_BAD; yading@11: break; yading@11: } yading@11: yading@11: av_dlog(NULL, " opcode type %02X, version %d, 0x%04X bytes: ", yading@11: opcode_type, opcode_version, opcode_size); yading@11: switch (opcode_type) { yading@11: yading@11: case OPCODE_END_OF_STREAM: yading@11: av_dlog(NULL, "end of stream\n"); yading@11: avio_skip(pb, opcode_size); yading@11: break; yading@11: yading@11: case OPCODE_END_OF_CHUNK: yading@11: av_dlog(NULL, "end of chunk\n"); yading@11: avio_skip(pb, opcode_size); yading@11: break; yading@11: yading@11: case OPCODE_CREATE_TIMER: yading@11: av_dlog(NULL, "create timer\n"); yading@11: if ((opcode_version > 0) || (opcode_size > 6)) { yading@11: av_dlog(NULL, "bad create_timer opcode\n"); yading@11: chunk_type = CHUNK_BAD; yading@11: break; yading@11: } yading@11: if (avio_read(pb, scratch, opcode_size) != yading@11: opcode_size) { yading@11: chunk_type = CHUNK_BAD; yading@11: break; yading@11: } yading@11: s->frame_pts_inc = ((uint64_t)AV_RL32(&scratch[0])) * AV_RL16(&scratch[4]); yading@11: av_dlog(NULL, " %.2f frames/second (timer div = %d, subdiv = %d)\n", yading@11: 1000000.0 / s->frame_pts_inc, AV_RL32(&scratch[0]), yading@11: AV_RL16(&scratch[4])); yading@11: break; yading@11: yading@11: case OPCODE_INIT_AUDIO_BUFFERS: yading@11: av_dlog(NULL, "initialize audio buffers\n"); yading@11: if ((opcode_version > 1) || (opcode_size > 10)) { yading@11: av_dlog(NULL, "bad init_audio_buffers opcode\n"); yading@11: chunk_type = CHUNK_BAD; yading@11: break; yading@11: } yading@11: if (avio_read(pb, scratch, opcode_size) != yading@11: opcode_size) { yading@11: chunk_type = CHUNK_BAD; yading@11: break; yading@11: } yading@11: s->audio_sample_rate = AV_RL16(&scratch[4]); yading@11: audio_flags = AV_RL16(&scratch[2]); yading@11: /* bit 0 of the flags: 0 = mono, 1 = stereo */ yading@11: s->audio_channels = (audio_flags & 1) + 1; yading@11: /* bit 1 of the flags: 0 = 8 bit, 1 = 16 bit */ yading@11: s->audio_bits = (((audio_flags >> 1) & 1) + 1) * 8; yading@11: /* bit 2 indicates compressed audio in version 1 opcode */ yading@11: if ((opcode_version == 1) && (audio_flags & 0x4)) yading@11: s->audio_type = AV_CODEC_ID_INTERPLAY_DPCM; yading@11: else if (s->audio_bits == 16) yading@11: s->audio_type = AV_CODEC_ID_PCM_S16LE; yading@11: else yading@11: s->audio_type = AV_CODEC_ID_PCM_U8; yading@11: av_dlog(NULL, "audio: %d bits, %d Hz, %s, %s format\n", yading@11: s->audio_bits, s->audio_sample_rate, yading@11: (s->audio_channels == 2) ? "stereo" : "mono", yading@11: (s->audio_type == AV_CODEC_ID_INTERPLAY_DPCM) ? yading@11: "Interplay audio" : "PCM"); yading@11: break; yading@11: yading@11: case OPCODE_START_STOP_AUDIO: yading@11: av_dlog(NULL, "start/stop audio\n"); yading@11: avio_skip(pb, opcode_size); yading@11: break; yading@11: yading@11: case OPCODE_INIT_VIDEO_BUFFERS: yading@11: av_dlog(NULL, "initialize video buffers\n"); yading@11: if ((opcode_version > 2) || (opcode_size > 8)) { yading@11: av_dlog(NULL, "bad init_video_buffers opcode\n"); yading@11: chunk_type = CHUNK_BAD; yading@11: break; yading@11: } yading@11: if (avio_read(pb, scratch, opcode_size) != yading@11: opcode_size) { yading@11: chunk_type = CHUNK_BAD; yading@11: break; yading@11: } yading@11: width = AV_RL16(&scratch[0]) * 8; yading@11: height = AV_RL16(&scratch[2]) * 8; yading@11: if (width != s->video_width) { yading@11: s->video_width = width; yading@11: s->changed++; yading@11: } yading@11: if (height != s->video_height) { yading@11: s->video_height = height; yading@11: s->changed++; yading@11: } yading@11: if (opcode_version < 2 || !AV_RL16(&scratch[6])) { yading@11: s->video_bpp = 8; yading@11: } else { yading@11: s->video_bpp = 16; yading@11: } yading@11: av_dlog(NULL, "video resolution: %d x %d\n", yading@11: s->video_width, s->video_height); yading@11: break; yading@11: yading@11: case OPCODE_UNKNOWN_06: yading@11: case OPCODE_UNKNOWN_0E: yading@11: case OPCODE_UNKNOWN_10: yading@11: case OPCODE_UNKNOWN_12: yading@11: case OPCODE_UNKNOWN_13: yading@11: case OPCODE_UNKNOWN_14: yading@11: case OPCODE_UNKNOWN_15: yading@11: av_dlog(NULL, "unknown (but documented) opcode %02X\n", opcode_type); yading@11: avio_skip(pb, opcode_size); yading@11: break; yading@11: yading@11: case OPCODE_SEND_BUFFER: yading@11: av_dlog(NULL, "send buffer\n"); yading@11: avio_skip(pb, opcode_size); yading@11: break; yading@11: yading@11: case OPCODE_AUDIO_FRAME: yading@11: av_dlog(NULL, "audio frame\n"); yading@11: yading@11: /* log position and move on for now */ yading@11: s->audio_chunk_offset = avio_tell(pb); yading@11: s->audio_chunk_size = opcode_size; yading@11: avio_skip(pb, opcode_size); yading@11: break; yading@11: yading@11: case OPCODE_SILENCE_FRAME: yading@11: av_dlog(NULL, "silence frame\n"); yading@11: avio_skip(pb, opcode_size); yading@11: break; yading@11: yading@11: case OPCODE_INIT_VIDEO_MODE: yading@11: av_dlog(NULL, "initialize video mode\n"); yading@11: avio_skip(pb, opcode_size); yading@11: break; yading@11: yading@11: case OPCODE_CREATE_GRADIENT: yading@11: av_dlog(NULL, "create gradient\n"); yading@11: avio_skip(pb, opcode_size); yading@11: break; yading@11: yading@11: case OPCODE_SET_PALETTE: yading@11: av_dlog(NULL, "set palette\n"); yading@11: /* check for the logical maximum palette size yading@11: * (3 * 256 + 4 bytes) */ yading@11: if (opcode_size > 0x304) { yading@11: av_dlog(NULL, "demux_ipmovie: set_palette opcode too large\n"); yading@11: chunk_type = CHUNK_BAD; yading@11: break; yading@11: } yading@11: if (avio_read(pb, scratch, opcode_size) != opcode_size) { yading@11: chunk_type = CHUNK_BAD; yading@11: break; yading@11: } yading@11: yading@11: /* load the palette into internal data structure */ yading@11: first_color = AV_RL16(&scratch[0]); yading@11: last_color = first_color + AV_RL16(&scratch[2]) - 1; yading@11: /* sanity check (since they are 16 bit values) */ yading@11: if ((first_color > 0xFF) || (last_color > 0xFF)) { yading@11: av_dlog(NULL, "demux_ipmovie: set_palette indexes out of range (%d -> %d)\n", yading@11: first_color, last_color); yading@11: chunk_type = CHUNK_BAD; yading@11: break; yading@11: } yading@11: j = 4; /* offset of first palette data */ yading@11: for (i = first_color; i <= last_color; i++) { yading@11: /* the palette is stored as a 6-bit VGA palette, thus each yading@11: * component is shifted up to a 8-bit range */ yading@11: r = scratch[j++] * 4; yading@11: g = scratch[j++] * 4; yading@11: b = scratch[j++] * 4; yading@11: s->palette[i] = (0xFFU << 24) | (r << 16) | (g << 8) | (b); yading@11: s->palette[i] |= s->palette[i] >> 6 & 0x30303; yading@11: } yading@11: s->has_palette = 1; yading@11: break; yading@11: yading@11: case OPCODE_SET_PALETTE_COMPRESSED: yading@11: av_dlog(NULL, "set palette compressed\n"); yading@11: avio_skip(pb, opcode_size); yading@11: break; yading@11: yading@11: case OPCODE_SET_DECODING_MAP: yading@11: av_dlog(NULL, "set decoding map\n"); yading@11: yading@11: /* log position and move on for now */ yading@11: s->decode_map_chunk_offset = avio_tell(pb); yading@11: s->decode_map_chunk_size = opcode_size; yading@11: avio_skip(pb, opcode_size); yading@11: break; yading@11: yading@11: case OPCODE_VIDEO_DATA: yading@11: av_dlog(NULL, "set video data\n"); yading@11: yading@11: /* log position and move on for now */ yading@11: s->video_chunk_offset = avio_tell(pb); yading@11: s->video_chunk_size = opcode_size; yading@11: avio_skip(pb, opcode_size); yading@11: break; yading@11: yading@11: default: yading@11: av_dlog(NULL, "*** unknown opcode type\n"); yading@11: chunk_type = CHUNK_BAD; yading@11: break; yading@11: yading@11: } yading@11: } yading@11: yading@11: /* make a note of where the stream is sitting */ yading@11: s->next_chunk_offset = avio_tell(pb); yading@11: yading@11: /* dispatch the first of any pending packets */ yading@11: if ((chunk_type == CHUNK_VIDEO) || (chunk_type == CHUNK_AUDIO_ONLY)) yading@11: chunk_type = load_ipmovie_packet(s, pb, pkt); yading@11: yading@11: return chunk_type; yading@11: } yading@11: yading@11: static const char signature[] = "Interplay MVE File\x1A\0\x1A"; yading@11: yading@11: static int ipmovie_probe(AVProbeData *p) yading@11: { yading@11: const uint8_t *b = p->buf; yading@11: const uint8_t *b_end = p->buf + p->buf_size - sizeof(signature); yading@11: do { yading@11: if (b[0] == signature[0] && memcmp(b, signature, sizeof(signature)) == 0) yading@11: return AVPROBE_SCORE_MAX; yading@11: b++; yading@11: } while (b < b_end); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int ipmovie_read_header(AVFormatContext *s) yading@11: { yading@11: IPMVEContext *ipmovie = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: AVPacket pkt; yading@11: AVStream *st; yading@11: unsigned char chunk_preamble[CHUNK_PREAMBLE_SIZE]; yading@11: int chunk_type, i; yading@11: uint8_t signature_buffer[sizeof(signature)]; yading@11: yading@11: avio_read(pb, signature_buffer, sizeof(signature_buffer)); yading@11: while (memcmp(signature_buffer, signature, sizeof(signature))) { yading@11: memmove(signature_buffer, signature_buffer + 1, sizeof(signature_buffer) - 1); yading@11: signature_buffer[sizeof(signature_buffer) - 1] = avio_r8(pb); yading@11: if (url_feof(pb)) yading@11: return AVERROR_EOF; yading@11: } yading@11: /* initialize private context members */ yading@11: ipmovie->video_pts = ipmovie->audio_frame_count = 0; yading@11: ipmovie->audio_chunk_offset = ipmovie->video_chunk_offset = yading@11: ipmovie->decode_map_chunk_offset = 0; yading@11: yading@11: /* on the first read, this will position the stream at the first chunk */ yading@11: ipmovie->next_chunk_offset = avio_tell(pb) + 4; yading@11: yading@11: for (i = 0; i < 256; i++) yading@11: ipmovie->palette[i] = 0xFFU << 24; yading@11: yading@11: /* process the first chunk which should be CHUNK_INIT_VIDEO */ yading@11: if (process_ipmovie_chunk(ipmovie, pb, &pkt) != CHUNK_INIT_VIDEO) yading@11: return AVERROR_INVALIDDATA; yading@11: yading@11: /* peek ahead to the next chunk-- if it is an init audio chunk, process yading@11: * it; if it is the first video chunk, this is a silent file */ yading@11: if (avio_read(pb, chunk_preamble, CHUNK_PREAMBLE_SIZE) != yading@11: CHUNK_PREAMBLE_SIZE) yading@11: return AVERROR(EIO); yading@11: chunk_type = AV_RL16(&chunk_preamble[2]); yading@11: avio_seek(pb, -CHUNK_PREAMBLE_SIZE, SEEK_CUR); yading@11: yading@11: if (chunk_type == CHUNK_VIDEO) yading@11: ipmovie->audio_type = AV_CODEC_ID_NONE; /* no audio */ yading@11: else if (process_ipmovie_chunk(ipmovie, pb, &pkt) != CHUNK_INIT_AUDIO) yading@11: return AVERROR_INVALIDDATA; yading@11: yading@11: /* initialize the stream decoders */ yading@11: st = avformat_new_stream(s, NULL); yading@11: if (!st) yading@11: return AVERROR(ENOMEM); yading@11: avpriv_set_pts_info(st, 63, 1, 1000000); yading@11: ipmovie->video_stream_index = st->index; yading@11: st->codec->codec_type = AVMEDIA_TYPE_VIDEO; yading@11: st->codec->codec_id = AV_CODEC_ID_INTERPLAY_VIDEO; yading@11: st->codec->codec_tag = 0; /* no fourcc */ yading@11: st->codec->width = ipmovie->video_width; yading@11: st->codec->height = ipmovie->video_height; yading@11: st->codec->bits_per_coded_sample = ipmovie->video_bpp; yading@11: yading@11: if (ipmovie->audio_type) { yading@11: st = avformat_new_stream(s, NULL); yading@11: if (!st) yading@11: return AVERROR(ENOMEM); yading@11: avpriv_set_pts_info(st, 32, 1, ipmovie->audio_sample_rate); yading@11: ipmovie->audio_stream_index = st->index; yading@11: st->codec->codec_type = AVMEDIA_TYPE_AUDIO; yading@11: st->codec->codec_id = ipmovie->audio_type; yading@11: st->codec->codec_tag = 0; /* no tag */ yading@11: st->codec->channels = ipmovie->audio_channels; yading@11: st->codec->channel_layout = st->codec->channels == 1 ? AV_CH_LAYOUT_MONO : yading@11: AV_CH_LAYOUT_STEREO; yading@11: st->codec->sample_rate = ipmovie->audio_sample_rate; yading@11: st->codec->bits_per_coded_sample = ipmovie->audio_bits; yading@11: st->codec->bit_rate = st->codec->channels * st->codec->sample_rate * yading@11: st->codec->bits_per_coded_sample; yading@11: if (st->codec->codec_id == AV_CODEC_ID_INTERPLAY_DPCM) yading@11: st->codec->bit_rate /= 2; yading@11: st->codec->block_align = st->codec->channels * st->codec->bits_per_coded_sample; yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int ipmovie_read_packet(AVFormatContext *s, yading@11: AVPacket *pkt) yading@11: { yading@11: IPMVEContext *ipmovie = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: int ret; yading@11: yading@11: for (;;) { yading@11: ret = process_ipmovie_chunk(ipmovie, pb, pkt); yading@11: if (ret == CHUNK_BAD) yading@11: ret = AVERROR_INVALIDDATA; yading@11: else if (ret == CHUNK_EOF) yading@11: ret = AVERROR(EIO); yading@11: else if (ret == CHUNK_NOMEM) yading@11: ret = AVERROR(ENOMEM); yading@11: else if (ret == CHUNK_VIDEO) yading@11: ret = 0; yading@11: else if (ret == CHUNK_INIT_VIDEO || ret == CHUNK_INIT_AUDIO) yading@11: continue; yading@11: else yading@11: ret = -1; yading@11: yading@11: return ret; yading@11: } yading@11: } yading@11: yading@11: AVInputFormat ff_ipmovie_demuxer = { yading@11: .name = "ipmovie", yading@11: .long_name = NULL_IF_CONFIG_SMALL("Interplay MVE"), yading@11: .priv_data_size = sizeof(IPMVEContext), yading@11: .read_probe = ipmovie_probe, yading@11: .read_header = ipmovie_read_header, yading@11: .read_packet = ipmovie_read_packet, yading@11: };