FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/sga.c
Date: 2024-11-20 23:03:26
Exec Total Coverage
Lines: 72 293 24.6%
Functions: 1 10 10.0%
Branches: 75 230 32.6%

Line Branch Exec Source
1 /*
2 * Digital Pictures SGA game demuxer
3 *
4 * Copyright (C) 2021 Paul B Mahol
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include "libavutil/intreadwrite.h"
24 #include "libavutil/avassert.h"
25 #include "libavutil/channel_layout.h"
26 #include "libavutil/internal.h"
27 #include "avformat.h"
28 #include "demux.h"
29 #include "internal.h"
30 #include "avio_internal.h"
31
32 #define SEGA_CD_PCM_NUM 12500000
33 #define SEGA_CD_PCM_DEN 786432
34
35 typedef struct SGADemuxContext {
36 int video_stream_index;
37 int audio_stream_index;
38
39 uint8_t sector[65536 * 2];
40 int sector_headers;
41 int sample_rate;
42 int first_audio_size;
43 int payload_size;
44 int packet_type;
45 int flags;
46 int idx;
47 int left;
48 int64_t pkt_pos;
49 } SGADemuxContext;
50
51 7186 static int sga_probe(const AVProbeData *p)
52 {
53 7186 const uint8_t *src = p->buf;
54 7186 int score = 0, sectors = 1;
55 7186 int last_left = 0;
56 7186 int sample_rate = -1;
57
58
2/2
✓ Branch 0 taken 367 times.
✓ Branch 1 taken 6819 times.
7186 if (p->buf_size < 2048)
59 367 return 0;
60
61
2/2
✓ Branch 0 taken 8745 times.
✓ Branch 1 taken 1387 times.
10132 for (int i = 0; i + 2 < p->buf_size; i += 2048) {
62 8745 int header = AV_RB16(src + i);
63
64
6/6
✓ Branch 0 taken 5841 times.
✓ Branch 1 taken 2904 times.
✓ Branch 2 taken 588 times.
✓ Branch 3 taken 5253 times.
✓ Branch 4 taken 588 times.
✓ Branch 5 taken 2904 times.
8745 if ((header > 0x07FE && header < 0x8100) ||
65
4/4
✓ Branch 0 taken 486 times.
✓ Branch 1 taken 102 times.
✓ Branch 2 taken 486 times.
✓ Branch 3 taken 2904 times.
3492 (header > 0x8200 && header < 0xA100) ||
66
2/2
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 409 times.
486 (header > 0xA200 && header < 0xC100)) {
67 5432 sectors = 0;
68 5432 break;
69 }
70 }
71
72
2/2
✓ Branch 0 taken 6859 times.
✓ Branch 1 taken 610 times.
7469 for (int i = 0; i + 4 < p->buf_size;) {
73 6859 int header = AV_RB16(src + i);
74 6859 int left = AV_RB16(src + i + 2);
75 int offset, type, size;
76
77
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6859 times.
6859 if (last_left < 0)
78 return 0;
79
6/6
✓ Branch 0 taken 1427 times.
✓ Branch 1 taken 5432 times.
✓ Branch 2 taken 269 times.
✓ Branch 3 taken 1158 times.
✓ Branch 4 taken 267 times.
✓ Branch 5 taken 2 times.
6859 if (sectors && header && last_left == 0) {
80
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 155 times.
267 if (header >> 12) {
81 112 last_left = left;
82 } else {
83 155 last_left = left = header;
84 }
85
4/4
✓ Branch 0 taken 1160 times.
✓ Branch 1 taken 5432 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1158 times.
6592 } else if (sectors && header) {
86 2 left = header;
87 2 last_left -= left;
88
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (header != 0x7FE && left < 7)
89 return 0;
90
2/2
✓ Branch 0 taken 1158 times.
✓ Branch 1 taken 5432 times.
6590 } else if (sectors) {
91
2/2
✓ Branch 0 taken 517 times.
✓ Branch 1 taken 641 times.
1158 if (left <= 8)
92 517 return 0;
93
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 641 times.
641 i += sectors ? 2048 : left + 4;
94 641 last_left = 0;
95 641 continue;
96 }
97
98
6/6
✓ Branch 0 taken 269 times.
✓ Branch 1 taken 5432 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 253 times.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 9 times.
5701 if (sectors && (i > 0 && left < 0x7fe) &&
99
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 (i + left + 14 < p->buf_size)) {
100 7 offset = i + left + 2;
101
4/4
✓ Branch 0 taken 262 times.
✓ Branch 1 taken 5432 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 253 times.
5694 } else if (sectors && i > 0) {
102 9 i += 2048;
103 9 last_left -= FFMIN(last_left, 2046);
104 9 continue;
105 } else {
106 5685 offset = 0;
107 5685 last_left = left;
108 }
109
110 5692 header = AV_RB16(src + offset);
111 5692 size = AV_RB16(src + offset + 2) + 4;
112
113
2/2
✓ Branch 0 taken 104813 times.
✓ Branch 1 taken 5692 times.
110505 while ((header & 0xFF00) == 0) {
114 104813 offset++;
115
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 104813 times.
104813 if (offset + 4 >= p->buf_size)
116 break;
117 104813 header = AV_RB16(src + offset);
118 104813 size = AV_RB16(src + offset + 2) + 4;
119 }
120
121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5692 times.
5692 if (offset + 12 >= p->buf_size)
122 break;
123
2/2
✓ Branch 0 taken 5427 times.
✓ Branch 1 taken 265 times.
5692 if ((header & 0xFF) > 1)
124 5427 return 0;
125 265 type = header >> 8;
126
127
2/4
✓ Branch 0 taken 265 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 265 times.
✗ Branch 3 not taken.
265 if (type == 0xAA ||
128
1/2
✓ Branch 0 taken 265 times.
✗ Branch 1 not taken.
265 type == 0xA1 ||
129
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 265 times.
265 type == 0xA2 ||
130 type == 0xA3) {
131 int new_rate;
132
133 if (size <= 12)
134 return 0;
135 new_rate = AV_RB16(src + offset + 8);
136 if (sample_rate < 0)
137 sample_rate = new_rate;
138 if (sample_rate == 0 || new_rate != sample_rate)
139 return 0;
140 if (src[offset + 10] != 1)
141 return 0;
142
143 score += 10;
144
2/4
✓ Branch 0 taken 265 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 265 times.
✗ Branch 3 not taken.
265 } else if (type == 0xC1 ||
145
2/2
✓ Branch 0 taken 264 times.
✓ Branch 1 taken 1 times.
265 type == 0xC6 ||
146
1/2
✓ Branch 0 taken 264 times.
✗ Branch 1 not taken.
264 type == 0xC7 ||
147
2/2
✓ Branch 0 taken 263 times.
✓ Branch 1 taken 1 times.
264 type == 0xC8 ||
148
1/2
✓ Branch 0 taken 263 times.
✗ Branch 1 not taken.
263 type == 0xC9 ||
149
1/2
✓ Branch 0 taken 263 times.
✗ Branch 1 not taken.
263 type == 0xCB ||
150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 263 times.
263 type == 0xCD ||
151 type == 0xE7) {
152 2 int nb_pals = src[offset + 9];
153 2 int tiles_w = src[offset + 10];
154 2 int tiles_h = src[offset + 11];
155
156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (size <= 12)
157 return 0;
158
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (nb_pals == 0 || nb_pals > 4)
159 return 0;
160
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (tiles_w == 0 || tiles_w > 80)
161 2 return 0;
162 if (tiles_h == 0 || tiles_h > 60)
163 return 0;
164
165 score += 10;
166
1/2
✓ Branch 0 taken 263 times.
✗ Branch 1 not taken.
263 } else if (header == 0x7FE) {
167 ;
168 } else {
169 263 return 0;
170 }
171
172 i += sectors ? 2048 : size + 4;
173 last_left -= FFMIN(last_left, 2046);
174
175 if (score < 0)
176 break;
177 }
178
179 610 return av_clip(score, 0, AVPROBE_SCORE_MAX);
180 }
181
182 static int sga_read_header(AVFormatContext *s)
183 {
184 SGADemuxContext *sga = s->priv_data;
185 AVIOContext *pb = s->pb;
186
187 sga->sector_headers = 1;
188 sga->first_audio_size = 0;
189 sga->video_stream_index = -1;
190 sga->audio_stream_index = -1;
191 sga->left = 2048;
192 sga->idx = 0;
193
194 s->ctx_flags |= AVFMTCTX_NOHEADER;
195
196 if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
197 while (!avio_feof(pb)) {
198 int header = avio_rb16(pb);
199 int type = header >> 8;
200 int skip = 2046;
201 int clock;
202
203 if (!sga->first_audio_size &&
204 (type == 0xAA ||
205 type == 0xA1 ||
206 type == 0xA2 ||
207 type == 0xA3)) {
208 sga->first_audio_size = avio_rb16(pb);
209 avio_skip(pb, 4);
210 clock = avio_rb16(pb);
211 sga->sample_rate = av_rescale(clock,
212 SEGA_CD_PCM_NUM,
213 SEGA_CD_PCM_DEN);
214 skip -= 8;
215 }
216 if ((header > 0x07FE && header < 0x8100) ||
217 (header > 0x8200 && header < 0xA100) ||
218 (header > 0xA200 && header < 0xC100)) {
219 sga->sector_headers = 0;
220 break;
221 }
222
223 avio_skip(pb, skip);
224 }
225
226 avio_seek(pb, 0, SEEK_SET);
227 }
228
229 return 0;
230 }
231
232 static void print_stats(AVFormatContext *s, const char *where)
233 {
234 SGADemuxContext *sga = s->priv_data;
235
236 av_log(s, AV_LOG_DEBUG, "START %s\n", where);
237 av_log(s, AV_LOG_DEBUG, "pos: %"PRIX64"\n", avio_tell(s->pb));
238 av_log(s, AV_LOG_DEBUG, "idx: %X\n", sga->idx);
239 av_log(s, AV_LOG_DEBUG, "packet_type: %X\n", sga->packet_type);
240 av_log(s, AV_LOG_DEBUG, "payload_size: %X\n", sga->payload_size);
241 av_log(s, AV_LOG_DEBUG, "SECTOR: %016"PRIX64"\n", AV_RB64(sga->sector));
242 av_log(s, AV_LOG_DEBUG, "stream: %X\n", sga->sector[1]);
243 av_log(s, AV_LOG_DEBUG, "END %s\n", where);
244 }
245
246 static void update_type_size(AVFormatContext *s)
247 {
248 SGADemuxContext *sga = s->priv_data;
249
250 if (sga->idx >= 4) {
251 sga->packet_type = sga->sector[0];
252 sga->payload_size = AV_RB16(sga->sector + 2);
253 } else {
254 sga->packet_type = 0;
255 sga->payload_size = 0;
256 }
257 }
258
259 static int sga_video_packet(AVFormatContext *s, AVPacket *pkt)
260 {
261 SGADemuxContext *sga = s->priv_data;
262 int ret;
263
264 if (sga->payload_size <= 8)
265 return AVERROR_INVALIDDATA;
266
267 if (sga->video_stream_index == -1) {
268 AVRational frame_rate;
269
270 AVStream *st = avformat_new_stream(s, NULL);
271 if (!st)
272 return AVERROR(ENOMEM);
273
274 st->start_time = 0;
275 st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
276 st->codecpar->codec_tag = 0;
277 st->codecpar->codec_id = AV_CODEC_ID_SGA_VIDEO;
278 sga->video_stream_index = st->index;
279
280 if (sga->first_audio_size > 0 && sga->sample_rate > 0) {
281 frame_rate.num = sga->sample_rate;
282 frame_rate.den = sga->first_audio_size;
283 } else {
284 frame_rate.num = 15;
285 frame_rate.den = 1;
286 }
287 avpriv_set_pts_info(st, 64, frame_rate.den, frame_rate.num);
288 }
289
290 ret = av_new_packet(pkt, sga->payload_size + 4);
291 if (ret < 0)
292 return AVERROR(ENOMEM);
293 memcpy(pkt->data, sga->sector, sga->payload_size + 4);
294 av_assert0(sga->idx >= sga->payload_size + 4);
295 memmove(sga->sector, sga->sector + sga->payload_size + 4, sga->idx - sga->payload_size - 4);
296
297 pkt->stream_index = sga->video_stream_index;
298 pkt->duration = 1;
299 pkt->pos = sga->pkt_pos;
300 pkt->flags |= sga->flags;
301 sga->idx -= sga->payload_size + 4;
302 sga->flags = 0;
303 update_type_size(s);
304
305 av_log(s, AV_LOG_DEBUG, "VIDEO PACKET: %d:%016"PRIX64" i:%X\n", pkt->size, AV_RB64(sga->sector), sga->idx);
306
307 return 0;
308 }
309
310 static int sga_audio_packet(AVFormatContext *s, AVPacket *pkt)
311 {
312 SGADemuxContext *sga = s->priv_data;
313 int ret;
314
315 if (sga->payload_size <= 8)
316 return AVERROR_INVALIDDATA;
317
318 if (sga->audio_stream_index == -1) {
319 AVStream *st = avformat_new_stream(s, NULL);
320 if (!st)
321 return AVERROR(ENOMEM);
322
323 st->start_time = 0;
324 st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
325 st->codecpar->codec_tag = 0;
326 st->codecpar->codec_id = AV_CODEC_ID_PCM_SGA;
327 st->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;
328 st->codecpar->sample_rate = av_rescale(AV_RB16(sga->sector + 8),
329 SEGA_CD_PCM_NUM,
330 SEGA_CD_PCM_DEN);
331 sga->audio_stream_index = st->index;
332
333 avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
334 }
335
336 ret = av_new_packet(pkt, sga->payload_size - 8);
337 if (ret < 0)
338 return AVERROR(ENOMEM);
339 memcpy(pkt->data, sga->sector + 12, sga->payload_size - 8);
340 av_assert0(sga->idx >= sga->payload_size + 4);
341 memmove(sga->sector, sga->sector + sga->payload_size + 4, sga->idx - sga->payload_size - 4);
342
343 pkt->stream_index = sga->audio_stream_index;
344 pkt->duration = pkt->size;
345 pkt->pos = sga->pkt_pos;
346 pkt->flags |= sga->flags;
347 sga->idx -= sga->payload_size + 4;
348 sga->flags = 0;
349 update_type_size(s);
350
351 av_log(s, AV_LOG_DEBUG, "AUDIO PACKET: %d:%016"PRIX64" i:%X\n", pkt->size, AV_RB64(sga->sector), sga->idx);
352
353 return 0;
354 }
355
356 static int sga_packet(AVFormatContext *s, AVPacket *pkt)
357 {
358 SGADemuxContext *sga = s->priv_data;
359 int ret = 0;
360
361 if (sga->packet_type == 0xCD ||
362 sga->packet_type == 0xCB ||
363 sga->packet_type == 0xC9 ||
364 sga->packet_type == 0xC8 ||
365 sga->packet_type == 0xC7 ||
366 sga->packet_type == 0xC6 ||
367 sga->packet_type == 0xC1 ||
368 sga->packet_type == 0xE7) {
369 ret = sga_video_packet(s, pkt);
370 } else if (sga->packet_type == 0xA1 ||
371 sga->packet_type == 0xA2 ||
372 sga->packet_type == 0xA3 ||
373 sga->packet_type == 0xAA) {
374 ret = sga_audio_packet(s, pkt);
375 } else {
376 if (sga->idx == 0)
377 return AVERROR_EOF;
378 if (sga->sector[0])
379 return AVERROR_INVALIDDATA;
380 memmove(sga->sector, sga->sector + 1, sga->idx - 1);
381 sga->idx--;
382 return AVERROR(EAGAIN);
383 }
384
385 return ret;
386 }
387
388 static int try_packet(AVFormatContext *s, AVPacket *pkt)
389 {
390 SGADemuxContext *sga = s->priv_data;
391 int ret = AVERROR(EAGAIN);
392
393 update_type_size(s);
394 if (sga->idx >= sga->payload_size + 4) {
395 print_stats(s, "before sga_packet");
396 ret = sga_packet(s, pkt);
397 print_stats(s, "after sga_packet");
398 if (ret != AVERROR(EAGAIN))
399 return ret;
400 }
401
402 return sga->idx < sga->payload_size + 4 ? AVERROR(EAGAIN) : ret;
403 }
404
405 static int sga_read_packet(AVFormatContext *s, AVPacket *pkt)
406 {
407 SGADemuxContext *sga = s->priv_data;
408 AVIOContext *pb = s->pb;
409 int header, ret = 0;
410
411 sga->pkt_pos = avio_tell(pb);
412
413 retry:
414 update_type_size(s);
415
416 print_stats(s, "start");
417 if (avio_feof(pb) &&
418 (!sga->payload_size || sga->idx < sga->payload_size + 4))
419 return AVERROR_EOF;
420
421 if (sga->idx < sga->payload_size + 4) {
422 ret = ffio_ensure_seekback(pb, 2);
423 if (ret < 0)
424 return ret;
425
426 print_stats(s, "before read header");
427 header = avio_rb16(pb);
428 if (!header) {
429 avio_skip(pb, 2046);
430 sga->left = 0;
431 } else if (!avio_feof(pb) &&
432 ((header >> 15) ||
433 !sga->sector_headers)) {
434 avio_seek(pb, -2, SEEK_CUR);
435 sga->flags = AV_PKT_FLAG_KEY;
436 sga->left = 2048;
437 } else {
438 sga->left = 2046;
439 }
440
441 av_assert0(sga->idx + sga->left < sizeof(sga->sector));
442 ret = avio_read(pb, sga->sector + sga->idx, sga->left);
443 if (ret > 0)
444 sga->idx += ret;
445 else if (ret != AVERROR_EOF && ret)
446 return ret;
447 print_stats(s, "after read header");
448
449 update_type_size(s);
450 }
451
452 ret = try_packet(s, pkt);
453 if (ret == AVERROR(EAGAIN))
454 goto retry;
455
456 return ret;
457 }
458
459 static int sga_seek(AVFormatContext *s, int stream_index,
460 int64_t timestamp, int flags)
461 {
462 SGADemuxContext *sga = s->priv_data;
463
464 sga->packet_type = sga->payload_size = sga->idx = 0;
465 memset(sga->sector, 0, sizeof(sga->sector));
466
467 return -1;
468 }
469
470 const FFInputFormat ff_sga_demuxer = {
471 .p.name = "sga",
472 .p.long_name = NULL_IF_CONFIG_SMALL("Digital Pictures SGA"),
473 .p.extensions = "sga",
474 .p.flags = AVFMT_GENERIC_INDEX,
475 .priv_data_size = sizeof(SGADemuxContext),
476 .read_probe = sga_probe,
477 .read_header = sga_read_header,
478 .read_packet = sga_read_packet,
479 .read_seek = sga_seek,
480 };
481