Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Tiertex Limited SEQ File Demuxer | ||
3 | * Copyright (c) 2006 Gregory Montoir (cyx@users.sourceforge.net) | ||
4 | * | ||
5 | * This file is part of FFmpeg. | ||
6 | * | ||
7 | * FFmpeg is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * FFmpeg is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with FFmpeg; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | /** | ||
23 | * @file | ||
24 | * Tiertex Limited SEQ file demuxer | ||
25 | */ | ||
26 | |||
27 | #include "libavutil/channel_layout.h" | ||
28 | #include "libavutil/mem.h" | ||
29 | #include "avformat.h" | ||
30 | #include "demux.h" | ||
31 | #include "internal.h" | ||
32 | |||
33 | #define SEQ_FRAME_SIZE 6144 | ||
34 | #define SEQ_FRAME_W 256 | ||
35 | #define SEQ_FRAME_H 128 | ||
36 | #define SEQ_NUM_FRAME_BUFFERS 30 | ||
37 | #define SEQ_AUDIO_BUFFER_SIZE 882 | ||
38 | #define SEQ_SAMPLE_RATE 22050 | ||
39 | #define SEQ_FRAME_RATE 25 | ||
40 | |||
41 | |||
42 | typedef struct TiertexSeqFrameBuffer { | ||
43 | int fill_size; | ||
44 | int data_size; | ||
45 | unsigned char *data; | ||
46 | } TiertexSeqFrameBuffer; | ||
47 | |||
48 | typedef struct SeqDemuxContext { | ||
49 | int audio_stream_index; | ||
50 | int video_stream_index; | ||
51 | int current_frame_pts; | ||
52 | int current_frame_offs; | ||
53 | TiertexSeqFrameBuffer frame_buffers[SEQ_NUM_FRAME_BUFFERS]; | ||
54 | int frame_buffers_count; | ||
55 | unsigned int current_audio_data_size; | ||
56 | unsigned int current_audio_data_offs; | ||
57 | unsigned int current_pal_data_size; | ||
58 | unsigned int current_pal_data_offs; | ||
59 | unsigned int current_video_data_size; | ||
60 | unsigned char *current_video_data_ptr; | ||
61 | int audio_buffer_full; | ||
62 | } SeqDemuxContext; | ||
63 | |||
64 | |||
65 | 7186 | static int seq_probe(const AVProbeData *p) | |
66 | { | ||
67 | int i; | ||
68 | |||
69 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 7164 times.
|
7186 | if (p->buf_size < 258) |
70 | 22 | return 0; | |
71 | |||
72 | /* there's no real header in a .seq file, the only thing they have in common */ | ||
73 | /* is the first 256 bytes of the file which are always filled with 0 */ | ||
74 |
2/2✓ Branch 0 taken 17146 times.
✓ Branch 1 taken 24 times.
|
17170 | for (i = 0; i < 256; i++) |
75 |
2/2✓ Branch 0 taken 7140 times.
✓ Branch 1 taken 10006 times.
|
17146 | if (p->buf[i]) |
76 | 7140 | return 0; | |
77 | |||
78 |
3/4✓ Branch 0 taken 14 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
|
24 | if(p->buf[256]==0 && p->buf[257]==0) |
79 | 14 | return 0; | |
80 | |||
81 | /* only one fourth of the score since the previous check is too naive */ | ||
82 | 10 | return AVPROBE_SCORE_MAX / 4; | |
83 | } | ||
84 | |||
85 | 1 | static int seq_init_frame_buffers(SeqDemuxContext *seq, AVIOContext *pb) | |
86 | { | ||
87 | int i, sz; | ||
88 | TiertexSeqFrameBuffer *seq_buffer; | ||
89 | |||
90 | 1 | avio_seek(pb, 256, SEEK_SET); | |
91 | |||
92 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | for (i = 0; i < SEQ_NUM_FRAME_BUFFERS; i++) { |
93 | 3 | sz = avio_rl16(pb); | |
94 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
|
3 | if (sz == 0) |
95 | 1 | break; | |
96 | else { | ||
97 | 2 | seq_buffer = &seq->frame_buffers[i]; | |
98 | 2 | seq_buffer->fill_size = 0; | |
99 | 2 | seq_buffer->data_size = sz; | |
100 | 2 | seq_buffer->data = av_malloc(sz); | |
101 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!seq_buffer->data) |
102 | ✗ | return AVERROR(ENOMEM); | |
103 | } | ||
104 | } | ||
105 | 1 | seq->frame_buffers_count = i; | |
106 | 1 | return 0; | |
107 | } | ||
108 | |||
109 | 58 | static int seq_fill_buffer(SeqDemuxContext *seq, AVIOContext *pb, int buffer_num, unsigned int data_offs, int data_size) | |
110 | { | ||
111 | TiertexSeqFrameBuffer *seq_buffer; | ||
112 | |||
113 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
|
58 | if (buffer_num >= SEQ_NUM_FRAME_BUFFERS) |
114 | ✗ | return AVERROR_INVALIDDATA; | |
115 | |||
116 | 58 | seq_buffer = &seq->frame_buffers[buffer_num]; | |
117 |
2/4✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 58 times.
|
58 | if (seq_buffer->fill_size + data_size > seq_buffer->data_size || data_size <= 0) |
118 | ✗ | return AVERROR_INVALIDDATA; | |
119 | |||
120 | 58 | avio_seek(pb, seq->current_frame_offs + data_offs, SEEK_SET); | |
121 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 58 times.
|
58 | if (avio_read(pb, seq_buffer->data + seq_buffer->fill_size, data_size) != data_size) |
122 | ✗ | return AVERROR(EIO); | |
123 | |||
124 | 58 | seq_buffer->fill_size += data_size; | |
125 | 58 | return 0; | |
126 | } | ||
127 | |||
128 | 178 | static int seq_parse_frame_data(SeqDemuxContext *seq, AVIOContext *pb) | |
129 | { | ||
130 | unsigned int offset_table[4], buffer_num[4]; | ||
131 | TiertexSeqFrameBuffer *seq_buffer; | ||
132 | int i, e, err; | ||
133 | |||
134 | 178 | seq->current_frame_offs += SEQ_FRAME_SIZE; | |
135 | 178 | avio_seek(pb, seq->current_frame_offs, SEEK_SET); | |
136 | |||
137 | /* sound data */ | ||
138 | 178 | seq->current_audio_data_offs = avio_rl16(pb); | |
139 |
2/2✓ Branch 0 taken 77 times.
✓ Branch 1 taken 101 times.
|
178 | if (seq->current_audio_data_offs) { |
140 | 77 | seq->current_audio_data_size = SEQ_AUDIO_BUFFER_SIZE * 2; | |
141 | } else { | ||
142 | 101 | seq->current_audio_data_size = 0; | |
143 | } | ||
144 | |||
145 | /* palette data */ | ||
146 | 178 | seq->current_pal_data_offs = avio_rl16(pb); | |
147 |
2/2✓ Branch 0 taken 21 times.
✓ Branch 1 taken 157 times.
|
178 | if (seq->current_pal_data_offs) { |
148 | 21 | seq->current_pal_data_size = 768; | |
149 | } else { | ||
150 | 157 | seq->current_pal_data_size = 0; | |
151 | } | ||
152 | |||
153 | /* video data */ | ||
154 |
2/2✓ Branch 0 taken 712 times.
✓ Branch 1 taken 178 times.
|
890 | for (i = 0; i < 4; i++) |
155 | 712 | buffer_num[i] = avio_r8(pb); | |
156 | |||
157 |
2/2✓ Branch 0 taken 712 times.
✓ Branch 1 taken 178 times.
|
890 | for (i = 0; i < 4; i++) |
158 | 712 | offset_table[i] = avio_rl16(pb); | |
159 | |||
160 |
2/2✓ Branch 0 taken 534 times.
✓ Branch 1 taken 178 times.
|
712 | for (i = 0; i < 3; i++) { |
161 |
2/2✓ Branch 0 taken 58 times.
✓ Branch 1 taken 476 times.
|
534 | if (offset_table[i]) { |
162 |
4/4✓ Branch 0 taken 110 times.
✓ Branch 1 taken 55 times.
✓ Branch 2 taken 107 times.
✓ Branch 3 taken 3 times.
|
165 | for (e = i + 1; e < 3 && offset_table[e] == 0; e++); |
163 | 58 | err = seq_fill_buffer(seq, pb, buffer_num[1 + i], | |
164 | offset_table[i], | ||
165 | 58 | offset_table[e] - offset_table[i]); | |
166 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
|
58 | if (err) |
167 | ✗ | return err; | |
168 | } | ||
169 | } | ||
170 | |||
171 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 156 times.
|
178 | if (buffer_num[0] != 255) { |
172 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
|
22 | if (buffer_num[0] >= SEQ_NUM_FRAME_BUFFERS) |
173 | ✗ | return AVERROR_INVALIDDATA; | |
174 | |||
175 | 22 | seq_buffer = &seq->frame_buffers[buffer_num[0]]; | |
176 | 22 | seq->current_video_data_size = seq_buffer->fill_size; | |
177 | 22 | seq->current_video_data_ptr = seq_buffer->data; | |
178 | 22 | seq_buffer->fill_size = 0; | |
179 | } else { | ||
180 | 156 | seq->current_video_data_size = 0; | |
181 | 156 | seq->current_video_data_ptr = 0; | |
182 | } | ||
183 | |||
184 | 178 | return 0; | |
185 | } | ||
186 | |||
187 | 1 | static int seq_read_close(AVFormatContext *s) | |
188 | { | ||
189 | int i; | ||
190 | 1 | SeqDemuxContext *seq = s->priv_data; | |
191 | |||
192 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 1 times.
|
31 | for (i = 0; i < SEQ_NUM_FRAME_BUFFERS; i++) |
193 | 30 | av_freep(&seq->frame_buffers[i].data); | |
194 | |||
195 | 1 | return 0; | |
196 | } | ||
197 | |||
198 | 1 | static int seq_read_header(AVFormatContext *s) | |
199 | { | ||
200 | int i, rc; | ||
201 | 1 | SeqDemuxContext *seq = s->priv_data; | |
202 | 1 | AVIOContext *pb = s->pb; | |
203 | AVStream *st; | ||
204 | |||
205 | /* init internal buffers */ | ||
206 | 1 | rc = seq_init_frame_buffers(seq, pb); | |
207 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (rc < 0) |
208 | ✗ | return rc; | |
209 | |||
210 | 1 | seq->current_frame_offs = 0; | |
211 | |||
212 | /* preload (no audio data, just buffer operations related data) */ | ||
213 |
2/2✓ Branch 0 taken 100 times.
✓ Branch 1 taken 1 times.
|
101 | for (i = 1; i <= 100; i++) { |
214 | 100 | rc = seq_parse_frame_data(seq, pb); | |
215 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 100 times.
|
100 | if (rc < 0) |
216 | ✗ | return rc; | |
217 | } | ||
218 | |||
219 | 1 | seq->current_frame_pts = 0; | |
220 | |||
221 | 1 | seq->audio_buffer_full = 0; | |
222 | |||
223 | /* initialize the video decoder stream */ | ||
224 | 1 | st = avformat_new_stream(s, NULL); | |
225 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!st) |
226 | ✗ | return AVERROR(ENOMEM); | |
227 | |||
228 | 1 | avpriv_set_pts_info(st, 32, 1, SEQ_FRAME_RATE); | |
229 | 1 | seq->video_stream_index = st->index; | |
230 | 1 | st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; | |
231 | 1 | st->codecpar->codec_id = AV_CODEC_ID_TIERTEXSEQVIDEO; | |
232 | 1 | st->codecpar->codec_tag = 0; /* no fourcc */ | |
233 | 1 | st->codecpar->width = SEQ_FRAME_W; | |
234 | 1 | st->codecpar->height = SEQ_FRAME_H; | |
235 | |||
236 | /* initialize the audio decoder stream */ | ||
237 | 1 | st = avformat_new_stream(s, NULL); | |
238 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!st) |
239 | ✗ | return AVERROR(ENOMEM); | |
240 | |||
241 | 1 | st->start_time = 0; | |
242 | 1 | avpriv_set_pts_info(st, 32, 1, SEQ_SAMPLE_RATE); | |
243 | 1 | seq->audio_stream_index = st->index; | |
244 | 1 | st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; | |
245 | 1 | st->codecpar->codec_id = AV_CODEC_ID_PCM_S16BE; | |
246 | 1 | st->codecpar->codec_tag = 0; /* no tag */ | |
247 | 1 | st->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; | |
248 | 1 | st->codecpar->sample_rate = SEQ_SAMPLE_RATE; | |
249 | 1 | st->codecpar->bits_per_coded_sample = 16; | |
250 | 1 | st->codecpar->bit_rate = st->codecpar->sample_rate * st->codecpar->bits_per_coded_sample * st->codecpar->ch_layout.nb_channels; | |
251 | 1 | st->codecpar->block_align = st->codecpar->ch_layout.nb_channels * st->codecpar->bits_per_coded_sample / 8; | |
252 | |||
253 | 1 | return 0; | |
254 | } | ||
255 | |||
256 | 99 | static int seq_read_packet(AVFormatContext *s, AVPacket *pkt) | |
257 | { | ||
258 | int rc; | ||
259 | 99 | SeqDemuxContext *seq = s->priv_data; | |
260 | 99 | AVIOContext *pb = s->pb; | |
261 | |||
262 |
2/2✓ Branch 0 taken 78 times.
✓ Branch 1 taken 21 times.
|
99 | if (!seq->audio_buffer_full) { |
263 | 78 | rc = seq_parse_frame_data(seq, pb); | |
264 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
|
78 | if (rc) |
265 | ✗ | return rc; | |
266 | |||
267 | /* video packet */ | ||
268 |
2/2✓ Branch 0 taken 21 times.
✓ Branch 1 taken 57 times.
|
78 | if (seq->current_pal_data_size + seq->current_video_data_size != 0) { |
269 | 21 | rc = av_new_packet(pkt, 1 + seq->current_pal_data_size | |
270 | 21 | + seq->current_video_data_size); | |
271 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
|
21 | if (rc < 0) |
272 | ✗ | return rc; | |
273 | |||
274 | 21 | pkt->data[0] = 0; | |
275 |
1/2✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
|
21 | if (seq->current_pal_data_size) { |
276 | 21 | pkt->data[0] |= 1; | |
277 | 21 | avio_seek(pb, seq->current_frame_offs + seq->current_pal_data_offs, SEEK_SET); | |
278 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
|
21 | if (avio_read(pb, &pkt->data[1], seq->current_pal_data_size) != seq->current_pal_data_size) |
279 | ✗ | return AVERROR(EIO); | |
280 | } | ||
281 |
1/2✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
|
21 | if (seq->current_video_data_size) { |
282 | 21 | pkt->data[0] |= 2; | |
283 | 21 | memcpy(&pkt->data[1 + seq->current_pal_data_size], | |
284 | 21 | seq->current_video_data_ptr, | |
285 | 21 | seq->current_video_data_size); | |
286 | } | ||
287 | 21 | pkt->stream_index = seq->video_stream_index; | |
288 | 21 | pkt->pts = seq->current_frame_pts; | |
289 | |||
290 | /* sound buffer will be processed on next read_packet() call */ | ||
291 | 21 | seq->audio_buffer_full = 1; | |
292 | 21 | return 0; | |
293 | } | ||
294 | } | ||
295 | |||
296 | /* audio packet */ | ||
297 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 77 times.
|
78 | if (seq->current_audio_data_offs == 0) /* end of data reached */ |
298 | 1 | return AVERROR(EIO); | |
299 | |||
300 | 77 | avio_seek(pb, seq->current_frame_offs + seq->current_audio_data_offs, SEEK_SET); | |
301 | 77 | rc = av_get_packet(pb, pkt, seq->current_audio_data_size); | |
302 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 77 times.
|
77 | if (rc < 0) |
303 | ✗ | return rc; | |
304 | |||
305 | 77 | pkt->stream_index = seq->audio_stream_index; | |
306 | 77 | seq->current_frame_pts++; | |
307 | |||
308 | 77 | seq->audio_buffer_full = 0; | |
309 | 77 | return 0; | |
310 | } | ||
311 | |||
312 | const FFInputFormat ff_tiertexseq_demuxer = { | ||
313 | .p.name = "tiertexseq", | ||
314 | .p.long_name = NULL_IF_CONFIG_SMALL("Tiertex Limited SEQ"), | ||
315 | .priv_data_size = sizeof(SeqDemuxContext), | ||
316 | .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, | ||
317 | .read_probe = seq_probe, | ||
318 | .read_header = seq_read_header, | ||
319 | .read_packet = seq_read_packet, | ||
320 | .read_close = seq_read_close, | ||
321 | }; | ||
322 |