X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fidcin.c;h=18da918f139b31ae66a52ce9456d32ebef37ef31;hb=3b199d29cd597a3518136d78860e172060b9e83d;hp=9d13aa56346fb77c0b285deb5dd8cf72a17bf601;hpb=2a2bbcb05fde486eef76d070f26eecddd4de0ace;p=ffmpeg diff --git a/libavformat/idcin.c b/libavformat/idcin.c index 9d13aa56346..18da918f139 100644 --- a/libavformat/idcin.c +++ b/libavformat/idcin.c @@ -1,26 +1,28 @@ /* - * Id Quake II CIN File Demuxer + * id Quake II CIN File Demuxer * Copyright (c) 2003 The ffmpeg Project * - * This library is free software; you can redistribute it and/or + * This file is part of Libav. + * + * Libav 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. + * version 2.1 of the License, or (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * Libav 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 + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** - * @file idcin.c - * Id Quake II CIN file demuxer by Mike Melanson (melanson@pcisys.net) - * For more information about the Id CIN format, visit: + * @file + * id Quake II CIN file demuxer by Mike Melanson (melanson@pcisys.net) + * For more information about the id CIN format, visit: * http://www.csse.monash.edu.au/~timf/ * * CIN is a somewhat quirky and ill-defined format. Here are some notes @@ -51,7 +53,7 @@ * audio frame #2: 787 * (bytes/sample) * (# channels) bytes in frame * audio frame #3: 788 * (bytes/sample) * (# channels) bytes in frame * - * Finally, not all Id CIN creation tools agree on the resolution of the + * Finally, not all id CIN creation tools agree on the resolution of the * color palette, apparently. Some creation tools specify red, green, and * blue palette components in terms of 6-bit VGA color DAC values which * range from 0..63. Other tools specify the RGB components as full 8-bit @@ -66,31 +68,27 @@ * transmitting them to the video decoder */ +#include "libavutil/audioconvert.h" +#include "libavutil/imgutils.h" +#include "libavutil/intreadwrite.h" #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]) +#include "internal.h" #define HUFFMAN_TABLE_SIZE (64 * 1024) -#define FRAME_PTS_INC (90000 / 14) +#define IDCIN_FPS 14 typedef struct IdcinDemuxContext { int video_stream_index; int audio_stream_index; int audio_chunk_size1; int audio_chunk_size2; + int block_align; /* demux state variables */ int current_audio_chunk; int next_chunk_is_video; int audio_present; - - int64_t pts; - - AVPaletteControl palctrl; + int64_t first_pkt_pos; } IdcinDemuxContext; static int idcin_probe(AVProbeData *p) @@ -98,7 +96,7 @@ static int idcin_probe(AVProbeData *p) unsigned int number; /* - * This is what you could call a "probabilistic" file check: Id CIN + * This is what you could call a "probabilistic" file check: id CIN * files don't have a definite file signature. In lieu of such a marker, * perform sanity checks on the 5 32-bit header fields: * width, height: greater than 0, less than or equal to 1024 @@ -108,32 +106,33 @@ static int idcin_probe(AVProbeData *p) * audio channels: 0 for no audio, or 1 or 2 */ - /* cannot proceed without 20 bytes */ + /* check we have enough data to do all checks, otherwise the + 0-padding may cause a wrong recognition */ if (p->buf_size < 20) return 0; /* check the video width */ - number = LE_32(&p->buf[0]); + number = AV_RL32(&p->buf[0]); if ((number == 0) || (number > 1024)) return 0; /* check the video height */ - number = LE_32(&p->buf[4]); + number = AV_RL32(&p->buf[4]); if ((number == 0) || (number > 1024)) return 0; /* check the audio sample rate */ - number = LE_32(&p->buf[8]); + number = AV_RL32(&p->buf[8]); if ((number != 0) && ((number < 8000) | (number > 48000))) return 0; /* check the audio bytes/sample */ - number = LE_32(&p->buf[12]); + number = AV_RL32(&p->buf[12]); if (number > 2) return 0; /* check the audio channels */ - number = LE_32(&p->buf[16]); + number = AV_RL32(&p->buf[16]); if (number > 2) return 0; @@ -141,59 +140,93 @@ static int idcin_probe(AVProbeData *p) return AVPROBE_SCORE_MAX / 2; } -static int idcin_read_header(AVFormatContext *s, - AVFormatParameters *ap) +static int idcin_read_header(AVFormatContext *s) { - ByteIOContext *pb = &s->pb; - IdcinDemuxContext *idcin = (IdcinDemuxContext *)s->priv_data; + AVIOContext *pb = s->pb; + IdcinDemuxContext *idcin = s->priv_data; AVStream *st; unsigned int width, height; unsigned int sample_rate, bytes_per_sample, channels; + int ret; /* get the 5 header parameters */ - width = get_le32(pb); - height = get_le32(pb); - sample_rate = get_le32(pb); - bytes_per_sample = get_le32(pb); - channels = get_le32(pb); + width = avio_rl32(pb); + height = avio_rl32(pb); + sample_rate = avio_rl32(pb); + bytes_per_sample = avio_rl32(pb); + channels = avio_rl32(pb); + + if (s->pb->eof_reached) { + av_log(s, AV_LOG_ERROR, "incomplete header\n"); + return s->pb->error ? s->pb->error : AVERROR_EOF; + } - st = av_new_stream(s, 0); + if (av_image_check_size(width, height, 0, s) < 0) + return AVERROR_INVALIDDATA; + if (sample_rate > 0) { + if (sample_rate < 14 || sample_rate > INT_MAX) { + av_log(s, AV_LOG_ERROR, "invalid sample rate: %u\n", sample_rate); + return AVERROR_INVALIDDATA; + } + if (bytes_per_sample < 1 || bytes_per_sample > 2) { + av_log(s, AV_LOG_ERROR, "invalid bytes per sample: %u\n", + bytes_per_sample); + return AVERROR_INVALIDDATA; + } + if (channels < 1 || channels > 2) { + av_log(s, AV_LOG_ERROR, "invalid channels: %u\n", channels); + return AVERROR_INVALIDDATA; + } + idcin->audio_present = 1; + } else { + /* if sample rate is 0, assume no audio */ + idcin->audio_present = 0; + } + + st = avformat_new_stream(s, NULL); if (!st) - return AVERROR_NOMEM; + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 33, 1, IDCIN_FPS); + st->start_time = 0; idcin->video_stream_index = st->index; - st->codec.codec_type = CODEC_TYPE_VIDEO; - st->codec.codec_id = CODEC_ID_IDCIN; - st->codec.codec_tag = 0; /* no fourcc */ - st->codec.width = width; - st->codec.height = height; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_IDCIN; + st->codec->codec_tag = 0; /* no fourcc */ + st->codec->width = width; + st->codec->height = height; /* load up the Huffman tables into extradata */ - st->codec.extradata_size = HUFFMAN_TABLE_SIZE; - st->codec.extradata = av_malloc(HUFFMAN_TABLE_SIZE); - if (get_buffer(pb, st->codec.extradata, HUFFMAN_TABLE_SIZE) != - HUFFMAN_TABLE_SIZE) - return -EIO; - /* save a reference in order to transport the palette */ - st->codec.palctrl = &idcin->palctrl; - - /* if sample rate is 0, assume no audio */ - if (sample_rate) { + st->codec->extradata_size = HUFFMAN_TABLE_SIZE; + st->codec->extradata = av_malloc(HUFFMAN_TABLE_SIZE); + ret = avio_read(pb, st->codec->extradata, HUFFMAN_TABLE_SIZE); + if (ret < 0) { + return ret; + } else if (ret != HUFFMAN_TABLE_SIZE) { + av_log(s, AV_LOG_ERROR, "incomplete header\n"); + return AVERROR(EIO); + } + + if (idcin->audio_present) { idcin->audio_present = 1; - st = av_new_stream(s, 0); + st = avformat_new_stream(s, NULL); if (!st) - return AVERROR_NOMEM; + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 63, 1, sample_rate); + st->start_time = 0; idcin->audio_stream_index = st->index; - st->codec.codec_type = CODEC_TYPE_AUDIO; - st->codec.codec_tag = 1; - st->codec.channels = channels; - st->codec.sample_rate = sample_rate; - st->codec.bits_per_sample = bytes_per_sample * 8; - st->codec.bit_rate = sample_rate * bytes_per_sample * 8 * channels; - st->codec.block_align = bytes_per_sample * channels; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_tag = 1; + st->codec->channels = channels; + st->codec->channel_layout = channels > 1 ? AV_CH_LAYOUT_STEREO : + AV_CH_LAYOUT_MONO; + st->codec->sample_rate = sample_rate; + st->codec->bits_per_coded_sample = bytes_per_sample * 8; + st->codec->bit_rate = sample_rate * bytes_per_sample * 8 * channels; + st->codec->block_align = idcin->block_align = bytes_per_sample * channels; if (bytes_per_sample == 1) - st->codec.codec_id = CODEC_ID_PCM_U8; + st->codec->codec_id = AV_CODEC_ID_PCM_U8; else - st->codec.codec_id = CODEC_ID_PCM_S16LE; + st->codec->codec_id = AV_CODEC_ID_PCM_S16LE; if (sample_rate % 14 != 0) { idcin->audio_chunk_size1 = (sample_rate / 14) * @@ -205,15 +238,10 @@ static int idcin_read_header(AVFormatContext *s, (sample_rate / 14) * bytes_per_sample * channels; } idcin->current_audio_chunk = 0; - } else - idcin->audio_present = 1; + } idcin->next_chunk_is_video = 1; - idcin->pts = 0; - - /* set the pts reference (1 pts = 1/90000) */ - s->pts_num = 1; - s->pts_den = 90000; + idcin->first_pkt_pos = avio_tell(s->pb); return 0; } @@ -224,25 +252,30 @@ static int idcin_read_packet(AVFormatContext *s, int ret; unsigned int command; unsigned int chunk_size; - IdcinDemuxContext *idcin = (IdcinDemuxContext *)s->priv_data; - ByteIOContext *pb = &s->pb; + IdcinDemuxContext *idcin = s->priv_data; + AVIOContext *pb = s->pb; int i; int palette_scale; unsigned char r, g, b; unsigned char palette_buffer[768]; + uint32_t palette[256]; - if (url_feof(&s->pb)) - return -EIO; + if (s->pb->eof_reached) + return s->pb->error ? s->pb->error : AVERROR_EOF; if (idcin->next_chunk_is_video) { - command = get_le32(pb); + command = avio_rl32(pb); if (command == 2) { - return -EIO; + return AVERROR(EIO); } else if (command == 1) { /* trigger a palette change */ - idcin->palctrl.palette_changed = 1; - if (get_buffer(pb, palette_buffer, 768) != 768) - return -EIO; + ret = avio_read(pb, palette_buffer, 768); + if (ret < 0) { + return ret; + } else if (ret != 768) { + av_log(s, AV_LOG_ERROR, "incomplete packet\n"); + return AVERROR(EIO); + } /* scale the palette as necessary */ palette_scale = 2; for (i = 0; i < 768; i++) @@ -255,63 +288,89 @@ static int idcin_read_packet(AVFormatContext *s, r = palette_buffer[i * 3 ] << palette_scale; g = palette_buffer[i * 3 + 1] << palette_scale; b = palette_buffer[i * 3 + 2] << palette_scale; - idcin->palctrl.palette[i] = (r << 16) | (g << 8) | (b); + palette[i] = (r << 16) | (g << 8) | (b); } } - chunk_size = get_le32(pb); + if (s->pb->eof_reached) { + av_log(s, AV_LOG_ERROR, "incomplete packet\n"); + return s->pb->error ? s->pb->error : AVERROR_EOF; + } + chunk_size = avio_rl32(pb); + if (chunk_size < 4 || chunk_size > INT_MAX - 4) { + av_log(s, AV_LOG_ERROR, "invalid chunk size: %u\n", chunk_size); + return AVERROR_INVALIDDATA; + } /* skip the number of decoded bytes (always equal to width * height) */ - url_fseek(pb, 4, SEEK_CUR); + avio_skip(pb, 4); chunk_size -= 4; - if (av_new_packet(pkt, chunk_size)) - ret = -EIO; + ret= av_get_packet(pb, pkt, chunk_size); + if (ret < 0) + return ret; + else if (ret != chunk_size) { + av_log(s, AV_LOG_ERROR, "incomplete packet\n"); + av_free_packet(pkt); + return AVERROR(EIO); + } + if (command == 1) { + uint8_t *pal; + + pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, + AVPALETTE_SIZE); + if (ret < 0) { + av_free_packet(pkt); + return ret; + } + memcpy(pal, palette, AVPALETTE_SIZE); + pkt->flags |= AV_PKT_FLAG_KEY; + } pkt->stream_index = idcin->video_stream_index; - pkt->pts = idcin->pts; - ret = get_buffer(pb, pkt->data, chunk_size); - if (ret != chunk_size) - ret = -EIO; + pkt->duration = 1; } else { /* send out the audio chunk */ if (idcin->current_audio_chunk) chunk_size = idcin->audio_chunk_size2; else chunk_size = idcin->audio_chunk_size1; - if (av_new_packet(pkt, chunk_size)) - return -EIO; + ret= av_get_packet(pb, pkt, chunk_size); + if (ret < 0) + return ret; pkt->stream_index = idcin->audio_stream_index; - pkt->pts = idcin->pts; - ret = get_buffer(&s->pb, pkt->data, chunk_size); - if (ret != chunk_size) - ret = -EIO; + pkt->duration = chunk_size / idcin->block_align; idcin->current_audio_chunk ^= 1; - idcin->pts += FRAME_PTS_INC; } if (idcin->audio_present) idcin->next_chunk_is_video ^= 1; - return ret; + return 0; } -static int idcin_read_close(AVFormatContext *s) +static int idcin_read_seek(AVFormatContext *s, int stream_index, + int64_t timestamp, int flags) { - - return 0; + IdcinDemuxContext *idcin = s->priv_data; + + if (idcin->first_pkt_pos > 0) { + int ret = avio_seek(s->pb, idcin->first_pkt_pos, SEEK_SET); + if (ret < 0) + return ret; + ff_update_cur_dts(s, s->streams[idcin->video_stream_index], 0); + idcin->next_chunk_is_video = 1; + idcin->current_audio_chunk = 0; + return 0; + } + return -1; } -static AVInputFormat idcin_iformat = { - "idcin", - "Id CIN format", - sizeof(IdcinDemuxContext), - idcin_probe, - idcin_read_header, - idcin_read_packet, - idcin_read_close, +AVInputFormat ff_idcin_demuxer = { + .name = "idcin", + .long_name = NULL_IF_CONFIG_SMALL("id Cinematic"), + .priv_data_size = sizeof(IdcinDemuxContext), + .read_probe = idcin_probe, + .read_header = idcin_read_header, + .read_packet = idcin_read_packet, + .read_seek = idcin_read_seek, + .flags = AVFMT_NO_BYTE_SEEK, }; - -int idcin_init(void) -{ - av_register_input_format(&idcin_iformat); - return 0; -}