yading@11: /* yading@11: * RL2 Format Demuxer yading@11: * Copyright (c) 2008 Sascha Sommer (saschasommer@freenet.de) 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: * RL2 file demuxer yading@11: * @file yading@11: * @author Sascha Sommer (saschasommer@freenet.de) yading@11: * @see http://wiki.multimedia.cx/index.php?title=RL2 yading@11: * yading@11: * extradata: yading@11: * 2 byte le initial drawing offset within 320x200 viewport yading@11: * 4 byte le number of used colors yading@11: * 256 * 3 bytes rgb palette yading@11: * optional background_frame yading@11: */ yading@11: yading@11: #include "libavutil/intreadwrite.h" yading@11: #include "libavutil/mathematics.h" yading@11: #include "avformat.h" yading@11: #include "internal.h" yading@11: yading@11: #define EXTRADATA1_SIZE (6 + 256 * 3) ///< video base, clr, palette yading@11: yading@11: #define FORM_TAG MKBETAG('F', 'O', 'R', 'M') yading@11: #define RLV2_TAG MKBETAG('R', 'L', 'V', '2') yading@11: #define RLV3_TAG MKBETAG('R', 'L', 'V', '3') yading@11: yading@11: typedef struct Rl2DemuxContext { yading@11: unsigned int index_pos[2]; ///< indexes in the sample tables yading@11: } Rl2DemuxContext; yading@11: yading@11: yading@11: /** yading@11: * check if the file is in rl2 format yading@11: * @param p probe buffer yading@11: * @return 0 when the probe buffer does not contain rl2 data, > 0 otherwise yading@11: */ yading@11: static int rl2_probe(AVProbeData *p) yading@11: { yading@11: yading@11: if(AV_RB32(&p->buf[0]) != FORM_TAG) yading@11: return 0; yading@11: yading@11: if(AV_RB32(&p->buf[8]) != RLV2_TAG && yading@11: AV_RB32(&p->buf[8]) != RLV3_TAG) yading@11: return 0; yading@11: yading@11: return AVPROBE_SCORE_MAX; yading@11: } yading@11: yading@11: /** yading@11: * read rl2 header data and setup the avstreams yading@11: * @param s demuxer context yading@11: * @return 0 on success, AVERROR otherwise yading@11: */ yading@11: static av_cold int rl2_read_header(AVFormatContext *s) yading@11: { yading@11: AVIOContext *pb = s->pb; yading@11: AVStream *st; yading@11: unsigned int frame_count; yading@11: unsigned int audio_frame_counter = 0; yading@11: unsigned int video_frame_counter = 0; yading@11: unsigned int back_size; yading@11: unsigned short sound_rate; yading@11: unsigned short rate; yading@11: unsigned short channels; yading@11: unsigned short def_sound_size; yading@11: unsigned int signature; yading@11: unsigned int pts_den = 11025; /* video only case */ yading@11: unsigned int pts_num = 1103; yading@11: unsigned int* chunk_offset = NULL; yading@11: int* chunk_size = NULL; yading@11: int* audio_size = NULL; yading@11: int i; yading@11: int ret = 0; yading@11: yading@11: avio_skip(pb,4); /* skip FORM tag */ yading@11: back_size = avio_rl32(pb); /**< get size of the background frame */ yading@11: signature = avio_rb32(pb); yading@11: avio_skip(pb, 4); /* data size */ yading@11: frame_count = avio_rl32(pb); yading@11: yading@11: /* disallow back_sizes and frame_counts that may lead to overflows later */ yading@11: if(back_size > INT_MAX/2 || frame_count > INT_MAX / sizeof(uint32_t)) yading@11: return AVERROR_INVALIDDATA; yading@11: yading@11: avio_skip(pb, 2); /* encoding mentod */ yading@11: sound_rate = avio_rl16(pb); yading@11: rate = avio_rl16(pb); yading@11: channels = avio_rl16(pb); yading@11: def_sound_size = avio_rl16(pb); yading@11: yading@11: /** setup video stream */ yading@11: st = avformat_new_stream(s, NULL); yading@11: if(!st) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: st->codec->codec_type = AVMEDIA_TYPE_VIDEO; yading@11: st->codec->codec_id = AV_CODEC_ID_RL2; yading@11: st->codec->codec_tag = 0; /* no fourcc */ yading@11: st->codec->width = 320; yading@11: st->codec->height = 200; yading@11: yading@11: /** allocate and fill extradata */ yading@11: st->codec->extradata_size = EXTRADATA1_SIZE; yading@11: yading@11: if(signature == RLV3_TAG && back_size > 0) yading@11: st->codec->extradata_size += back_size; yading@11: yading@11: st->codec->extradata = av_mallocz(st->codec->extradata_size + yading@11: FF_INPUT_BUFFER_PADDING_SIZE); yading@11: if(!st->codec->extradata) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: if(avio_read(pb,st->codec->extradata,st->codec->extradata_size) != yading@11: st->codec->extradata_size) yading@11: return AVERROR(EIO); yading@11: yading@11: /** setup audio stream if present */ yading@11: if(sound_rate){ yading@11: if(channels <= 0) yading@11: return AVERROR_INVALIDDATA; yading@11: yading@11: pts_num = def_sound_size; yading@11: pts_den = rate; yading@11: yading@11: st = avformat_new_stream(s, NULL); yading@11: if (!st) yading@11: return AVERROR(ENOMEM); yading@11: st->codec->codec_type = AVMEDIA_TYPE_AUDIO; yading@11: st->codec->codec_id = AV_CODEC_ID_PCM_U8; yading@11: st->codec->codec_tag = 1; yading@11: st->codec->channels = channels; yading@11: st->codec->bits_per_coded_sample = 8; yading@11: st->codec->sample_rate = rate; yading@11: st->codec->bit_rate = st->codec->channels * st->codec->sample_rate * yading@11: st->codec->bits_per_coded_sample; yading@11: st->codec->block_align = st->codec->channels * yading@11: st->codec->bits_per_coded_sample / 8; yading@11: avpriv_set_pts_info(st,32,1,rate); yading@11: } yading@11: yading@11: avpriv_set_pts_info(s->streams[0], 32, pts_num, pts_den); yading@11: yading@11: chunk_size = av_malloc(frame_count * sizeof(uint32_t)); yading@11: audio_size = av_malloc(frame_count * sizeof(uint32_t)); yading@11: chunk_offset = av_malloc(frame_count * sizeof(uint32_t)); yading@11: yading@11: if(!chunk_size || !audio_size || !chunk_offset){ yading@11: av_free(chunk_size); yading@11: av_free(audio_size); yading@11: av_free(chunk_offset); yading@11: return AVERROR(ENOMEM); yading@11: } yading@11: yading@11: /** read offset and size tables */ yading@11: for(i=0; i < frame_count;i++) yading@11: chunk_size[i] = avio_rl32(pb); yading@11: for(i=0; i < frame_count;i++) yading@11: chunk_offset[i] = avio_rl32(pb); yading@11: for(i=0; i < frame_count;i++) yading@11: audio_size[i] = avio_rl32(pb) & 0xFFFF; yading@11: yading@11: /** build the sample index */ yading@11: for(i=0;i chunk_size[i]){ yading@11: ret = AVERROR_INVALIDDATA; yading@11: break; yading@11: } yading@11: yading@11: if(sound_rate && audio_size[i]){ yading@11: av_add_index_entry(s->streams[1], chunk_offset[i], yading@11: audio_frame_counter,audio_size[i], 0, AVINDEX_KEYFRAME); yading@11: audio_frame_counter += audio_size[i] / channels; yading@11: } yading@11: av_add_index_entry(s->streams[0], chunk_offset[i] + audio_size[i], yading@11: video_frame_counter,chunk_size[i]-audio_size[i],0,AVINDEX_KEYFRAME); yading@11: ++video_frame_counter; yading@11: } yading@11: yading@11: yading@11: av_free(chunk_size); yading@11: av_free(audio_size); yading@11: av_free(chunk_offset); yading@11: yading@11: return ret; yading@11: } yading@11: yading@11: /** yading@11: * read a single audio or video packet yading@11: * @param s demuxer context yading@11: * @param pkt the packet to be filled yading@11: * @return 0 on success, AVERROR otherwise yading@11: */ yading@11: static int rl2_read_packet(AVFormatContext *s, yading@11: AVPacket *pkt) yading@11: { yading@11: Rl2DemuxContext *rl2 = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: AVIndexEntry *sample = NULL; yading@11: int i; yading@11: int ret = 0; yading@11: int stream_id = -1; yading@11: int64_t pos = INT64_MAX; yading@11: yading@11: /** check if there is a valid video or audio entry that can be used */ yading@11: for(i=0; inb_streams; i++){ yading@11: if(rl2->index_pos[i] < s->streams[i]->nb_index_entries yading@11: && s->streams[i]->index_entries[ rl2->index_pos[i] ].pos < pos){ yading@11: sample = &s->streams[i]->index_entries[ rl2->index_pos[i] ]; yading@11: pos= sample->pos; yading@11: stream_id= i; yading@11: } yading@11: } yading@11: yading@11: if(stream_id == -1) yading@11: return AVERROR_EOF; yading@11: yading@11: ++rl2->index_pos[stream_id]; yading@11: yading@11: /** position the stream (will probably be there anyway) */ yading@11: avio_seek(pb, sample->pos, SEEK_SET); yading@11: yading@11: /** fill the packet */ yading@11: ret = av_get_packet(pb, pkt, sample->size); yading@11: if(ret != sample->size){ yading@11: av_free_packet(pkt); yading@11: return AVERROR(EIO); yading@11: } yading@11: yading@11: pkt->stream_index = stream_id; yading@11: pkt->pts = sample->timestamp; yading@11: yading@11: return ret; yading@11: } yading@11: yading@11: /** yading@11: * seek to a new timestamp yading@11: * @param s demuxer context yading@11: * @param stream_index index of the stream that should be seeked yading@11: * @param timestamp wanted timestamp yading@11: * @param flags direction and seeking mode yading@11: * @return 0 on success, -1 otherwise yading@11: */ yading@11: static int rl2_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) yading@11: { yading@11: AVStream *st = s->streams[stream_index]; yading@11: Rl2DemuxContext *rl2 = s->priv_data; yading@11: int i; yading@11: int index = av_index_search_timestamp(st, timestamp, flags); yading@11: if(index < 0) yading@11: return -1; yading@11: yading@11: rl2->index_pos[stream_index] = index; yading@11: timestamp = st->index_entries[index].timestamp; yading@11: yading@11: for(i=0; i < s->nb_streams; i++){ yading@11: AVStream *st2 = s->streams[i]; yading@11: index = av_index_search_timestamp(st2, yading@11: av_rescale_q(timestamp, st->time_base, st2->time_base), yading@11: flags | AVSEEK_FLAG_BACKWARD); yading@11: yading@11: if(index < 0) yading@11: index = 0; yading@11: yading@11: rl2->index_pos[i] = index; yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: AVInputFormat ff_rl2_demuxer = { yading@11: .name = "rl2", yading@11: .long_name = NULL_IF_CONFIG_SMALL("RL2"), yading@11: .priv_data_size = sizeof(Rl2DemuxContext), yading@11: .read_probe = rl2_probe, yading@11: .read_header = rl2_read_header, yading@11: .read_packet = rl2_read_packet, yading@11: .read_seek = rl2_read_seek, yading@11: };