yading@11: /* yading@11: * Tiertex Limited SEQ File Demuxer yading@11: * Copyright (c) 2006 Gregory Montoir (cyx@users.sourceforge.net) 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: * Tiertex Limited SEQ file demuxer yading@11: */ yading@11: yading@11: #include "libavutil/channel_layout.h" yading@11: #include "avformat.h" yading@11: #include "internal.h" yading@11: yading@11: #define SEQ_FRAME_SIZE 6144 yading@11: #define SEQ_FRAME_W 256 yading@11: #define SEQ_FRAME_H 128 yading@11: #define SEQ_NUM_FRAME_BUFFERS 30 yading@11: #define SEQ_AUDIO_BUFFER_SIZE 882 yading@11: #define SEQ_SAMPLE_RATE 22050 yading@11: #define SEQ_FRAME_RATE 25 yading@11: yading@11: yading@11: typedef struct TiertexSeqFrameBuffer { yading@11: int fill_size; yading@11: int data_size; yading@11: unsigned char *data; yading@11: } TiertexSeqFrameBuffer; yading@11: yading@11: typedef struct SeqDemuxContext { yading@11: int audio_stream_index; yading@11: int video_stream_index; yading@11: int current_frame_pts; yading@11: int current_frame_offs; yading@11: TiertexSeqFrameBuffer frame_buffers[SEQ_NUM_FRAME_BUFFERS]; yading@11: int frame_buffers_count; yading@11: unsigned int current_audio_data_size; yading@11: unsigned int current_audio_data_offs; yading@11: unsigned int current_pal_data_size; yading@11: unsigned int current_pal_data_offs; yading@11: unsigned int current_video_data_size; yading@11: unsigned char *current_video_data_ptr; yading@11: int audio_buffer_full; yading@11: } SeqDemuxContext; yading@11: yading@11: yading@11: static int seq_probe(AVProbeData *p) yading@11: { yading@11: int i; yading@11: yading@11: if (p->buf_size < 258) yading@11: return 0; yading@11: yading@11: /* there's no real header in a .seq file, the only thing they have in common */ yading@11: /* is the first 256 bytes of the file which are always filled with 0 */ yading@11: for (i = 0; i < 256; i++) yading@11: if (p->buf[i]) yading@11: return 0; yading@11: yading@11: if(p->buf[256]==0 && p->buf[257]==0) yading@11: return 0; yading@11: yading@11: /* only one fourth of the score since the previous check is too naive */ yading@11: return AVPROBE_SCORE_MAX / 4; yading@11: } yading@11: yading@11: static int seq_init_frame_buffers(SeqDemuxContext *seq, AVIOContext *pb) yading@11: { yading@11: int i, sz; yading@11: TiertexSeqFrameBuffer *seq_buffer; yading@11: yading@11: avio_seek(pb, 256, SEEK_SET); yading@11: yading@11: for (i = 0; i < SEQ_NUM_FRAME_BUFFERS; i++) { yading@11: sz = avio_rl16(pb); yading@11: if (sz == 0) yading@11: break; yading@11: else { yading@11: seq_buffer = &seq->frame_buffers[i]; yading@11: seq_buffer->fill_size = 0; yading@11: seq_buffer->data_size = sz; yading@11: seq_buffer->data = av_malloc(sz); yading@11: if (!seq_buffer->data) yading@11: return AVERROR(ENOMEM); yading@11: } yading@11: } yading@11: seq->frame_buffers_count = i; yading@11: return 0; yading@11: } yading@11: yading@11: static int seq_fill_buffer(SeqDemuxContext *seq, AVIOContext *pb, int buffer_num, unsigned int data_offs, int data_size) yading@11: { yading@11: TiertexSeqFrameBuffer *seq_buffer; yading@11: yading@11: if (buffer_num >= SEQ_NUM_FRAME_BUFFERS) yading@11: return AVERROR_INVALIDDATA; yading@11: yading@11: seq_buffer = &seq->frame_buffers[buffer_num]; yading@11: if (seq_buffer->fill_size + data_size > seq_buffer->data_size || data_size <= 0) yading@11: return AVERROR_INVALIDDATA; yading@11: yading@11: avio_seek(pb, seq->current_frame_offs + data_offs, SEEK_SET); yading@11: if (avio_read(pb, seq_buffer->data + seq_buffer->fill_size, data_size) != data_size) yading@11: return AVERROR(EIO); yading@11: yading@11: seq_buffer->fill_size += data_size; yading@11: return 0; yading@11: } yading@11: yading@11: static int seq_parse_frame_data(SeqDemuxContext *seq, AVIOContext *pb) yading@11: { yading@11: unsigned int offset_table[4], buffer_num[4]; yading@11: TiertexSeqFrameBuffer *seq_buffer; yading@11: int i, e, err; yading@11: yading@11: seq->current_frame_offs += SEQ_FRAME_SIZE; yading@11: avio_seek(pb, seq->current_frame_offs, SEEK_SET); yading@11: yading@11: /* sound data */ yading@11: seq->current_audio_data_offs = avio_rl16(pb); yading@11: if (seq->current_audio_data_offs) { yading@11: seq->current_audio_data_size = SEQ_AUDIO_BUFFER_SIZE * 2; yading@11: } else { yading@11: seq->current_audio_data_size = 0; yading@11: } yading@11: yading@11: /* palette data */ yading@11: seq->current_pal_data_offs = avio_rl16(pb); yading@11: if (seq->current_pal_data_offs) { yading@11: seq->current_pal_data_size = 768; yading@11: } else { yading@11: seq->current_pal_data_size = 0; yading@11: } yading@11: yading@11: /* video data */ yading@11: for (i = 0; i < 4; i++) yading@11: buffer_num[i] = avio_r8(pb); yading@11: yading@11: for (i = 0; i < 4; i++) yading@11: offset_table[i] = avio_rl16(pb); yading@11: yading@11: for (i = 0; i < 3; i++) { yading@11: if (offset_table[i]) { yading@11: for (e = i + 1; e < 3 && offset_table[e] == 0; e++); yading@11: err = seq_fill_buffer(seq, pb, buffer_num[1 + i], yading@11: offset_table[i], yading@11: offset_table[e] - offset_table[i]); yading@11: if (err) yading@11: return err; yading@11: } yading@11: } yading@11: yading@11: if (buffer_num[0] != 255) { yading@11: if (buffer_num[0] >= SEQ_NUM_FRAME_BUFFERS) yading@11: return AVERROR_INVALIDDATA; yading@11: yading@11: seq_buffer = &seq->frame_buffers[buffer_num[0]]; yading@11: seq->current_video_data_size = seq_buffer->fill_size; yading@11: seq->current_video_data_ptr = seq_buffer->data; yading@11: seq_buffer->fill_size = 0; yading@11: } else { yading@11: seq->current_video_data_size = 0; yading@11: seq->current_video_data_ptr = 0; yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int seq_read_header(AVFormatContext *s) yading@11: { yading@11: int i, rc; yading@11: SeqDemuxContext *seq = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: AVStream *st; yading@11: yading@11: /* init internal buffers */ yading@11: rc = seq_init_frame_buffers(seq, pb); yading@11: if (rc) yading@11: return rc; yading@11: yading@11: seq->current_frame_offs = 0; yading@11: yading@11: /* preload (no audio data, just buffer operations related data) */ yading@11: for (i = 1; i <= 100; i++) { yading@11: rc = seq_parse_frame_data(seq, pb); yading@11: if (rc) yading@11: return rc; yading@11: } yading@11: yading@11: seq->current_frame_pts = 0; yading@11: yading@11: seq->audio_buffer_full = 0; yading@11: yading@11: /* initialize the video decoder stream */ yading@11: st = avformat_new_stream(s, NULL); yading@11: if (!st) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: avpriv_set_pts_info(st, 32, 1, SEQ_FRAME_RATE); yading@11: seq->video_stream_index = st->index; yading@11: st->codec->codec_type = AVMEDIA_TYPE_VIDEO; yading@11: st->codec->codec_id = AV_CODEC_ID_TIERTEXSEQVIDEO; yading@11: st->codec->codec_tag = 0; /* no fourcc */ yading@11: st->codec->width = SEQ_FRAME_W; yading@11: st->codec->height = SEQ_FRAME_H; yading@11: yading@11: /* initialize the audio decoder stream */ yading@11: st = avformat_new_stream(s, NULL); yading@11: if (!st) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: st->start_time = 0; yading@11: avpriv_set_pts_info(st, 32, 1, SEQ_SAMPLE_RATE); yading@11: seq->audio_stream_index = st->index; yading@11: st->codec->codec_type = AVMEDIA_TYPE_AUDIO; yading@11: st->codec->codec_id = AV_CODEC_ID_PCM_S16BE; yading@11: st->codec->codec_tag = 0; /* no tag */ yading@11: st->codec->channels = 1; yading@11: st->codec->channel_layout = AV_CH_LAYOUT_MONO; yading@11: st->codec->sample_rate = SEQ_SAMPLE_RATE; yading@11: st->codec->bits_per_coded_sample = 16; yading@11: st->codec->bit_rate = st->codec->sample_rate * st->codec->bits_per_coded_sample * st->codec->channels; yading@11: st->codec->block_align = st->codec->channels * st->codec->bits_per_coded_sample / 8; yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int seq_read_packet(AVFormatContext *s, AVPacket *pkt) yading@11: { yading@11: int rc; yading@11: SeqDemuxContext *seq = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: yading@11: if (!seq->audio_buffer_full) { yading@11: rc = seq_parse_frame_data(seq, pb); yading@11: if (rc) yading@11: return rc; yading@11: yading@11: /* video packet */ yading@11: if (seq->current_pal_data_size + seq->current_video_data_size != 0) { yading@11: if (av_new_packet(pkt, 1 + seq->current_pal_data_size + seq->current_video_data_size)) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: pkt->data[0] = 0; yading@11: if (seq->current_pal_data_size) { yading@11: pkt->data[0] |= 1; yading@11: avio_seek(pb, seq->current_frame_offs + seq->current_pal_data_offs, SEEK_SET); yading@11: if (avio_read(pb, &pkt->data[1], seq->current_pal_data_size) != seq->current_pal_data_size) yading@11: return AVERROR(EIO); yading@11: } yading@11: if (seq->current_video_data_size) { yading@11: pkt->data[0] |= 2; yading@11: memcpy(&pkt->data[1 + seq->current_pal_data_size], yading@11: seq->current_video_data_ptr, yading@11: seq->current_video_data_size); yading@11: } yading@11: pkt->stream_index = seq->video_stream_index; yading@11: pkt->pts = seq->current_frame_pts; yading@11: yading@11: /* sound buffer will be processed on next read_packet() call */ yading@11: seq->audio_buffer_full = 1; yading@11: return 0; yading@11: } yading@11: } yading@11: yading@11: /* audio packet */ yading@11: if (seq->current_audio_data_offs == 0) /* end of data reached */ yading@11: return AVERROR(EIO); yading@11: yading@11: avio_seek(pb, seq->current_frame_offs + seq->current_audio_data_offs, SEEK_SET); yading@11: rc = av_get_packet(pb, pkt, seq->current_audio_data_size); yading@11: if (rc < 0) yading@11: return rc; yading@11: yading@11: pkt->stream_index = seq->audio_stream_index; yading@11: seq->current_frame_pts++; yading@11: yading@11: seq->audio_buffer_full = 0; yading@11: return 0; yading@11: } yading@11: yading@11: static int seq_read_close(AVFormatContext *s) yading@11: { yading@11: int i; yading@11: SeqDemuxContext *seq = s->priv_data; yading@11: yading@11: for (i = 0; i < SEQ_NUM_FRAME_BUFFERS; i++) yading@11: av_free(seq->frame_buffers[i].data); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: AVInputFormat ff_tiertexseq_demuxer = { yading@11: .name = "tiertexseq", yading@11: .long_name = NULL_IF_CONFIG_SMALL("Tiertex Limited SEQ"), yading@11: .priv_data_size = sizeof(SeqDemuxContext), yading@11: .read_probe = seq_probe, yading@11: .read_header = seq_read_header, yading@11: .read_packet = seq_read_packet, yading@11: .read_close = seq_read_close, yading@11: };