FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/tiertexseq.c
Date: 2026-04-30 18:27:06
Exec Total Coverage
Lines: 132 146 90.4%
Functions: 7 7 100.0%
Branches: 57 76 75.0%

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