Coverage Report

Created: 2024-09-06 07:53

/src/ffmpeg/libavformat/flic.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * FLI/FLC Animation File Demuxer
3
 * Copyright (c) 2003 The FFmpeg project
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
 * FLI/FLC file demuxer
25
 * by Mike Melanson (melanson@pcisys.net)
26
 * for more information on the .fli/.flc file format and all of its many
27
 * variations, visit:
28
 *   http://www.compuphase.com/flic.htm
29
 *
30
 * This demuxer handles standard 0xAF11- and 0xAF12-type FLIs. It also handles
31
 * special FLIs from the PC games "Magic Carpet" and "X-COM: Terror from the Deep".
32
 */
33
34
#include "libavutil/channel_layout.h"
35
#include "libavutil/intreadwrite.h"
36
#include "avformat.h"
37
#include "demux.h"
38
#include "internal.h"
39
40
220k
#define FLIC_FILE_MAGIC_1 0xAF11
41
220k
#define FLIC_FILE_MAGIC_2 0xAF12
42
220k
#define FLIC_FILE_MAGIC_3 0xAF44  /* Flic Type for Extended FLX Format which
43
                                     originated in Dave's Targa Animator (DTA) */
44
1.23M
#define FLIC_CHUNK_MAGIC_1 0xF1FA
45
508k
#define FLIC_CHUNK_MAGIC_2 0xF5FA
46
201
#define FLIC_MC_SPEED 5  /* speed for Magic Carpet game FLIs */
47
65
#define FLIC_DEFAULT_SPEED 5  /* for FLIs that have 0 speed */
48
509k
#define FLIC_TFTD_CHUNK_AUDIO 0xAAAA /* Audio chunk. Used in Terror from the Deep.
49
                                        Has 10 B extra header not accounted for in the chunk header */
50
1.62k
#define FLIC_TFTD_SAMPLE_RATE 22050
51
52
374k
#define FLIC_HEADER_SIZE 128
53
6.10M
#define FLIC_PREAMBLE_SIZE 6
54
55
typedef struct FlicDemuxContext {
56
    int video_stream_index;
57
    int audio_stream_index;
58
    int frame_number;
59
} FlicDemuxContext;
60
61
static int flic_probe(const AVProbeData *p)
62
358k
{
63
358k
    int magic_number;
64
65
358k
    if(p->buf_size < FLIC_HEADER_SIZE)
66
138k
        return 0;
67
68
220k
    magic_number = AV_RL16(&p->buf[4]);
69
220k
    if ((magic_number != FLIC_FILE_MAGIC_1) &&
70
220k
        (magic_number != FLIC_FILE_MAGIC_2) &&
71
220k
        (magic_number != FLIC_FILE_MAGIC_3))
72
219k
        return 0;
73
74
683
    if(AV_RL16(&p->buf[0x10]) != FLIC_CHUNK_MAGIC_1){
75
680
        if(AV_RL32(&p->buf[0x10]) > 2000)
76
345
            return 0;
77
680
    }
78
79
338
    if(   AV_RL16(&p->buf[0x08]) > 4096
80
338
       || AV_RL16(&p->buf[0x0A]) > 4096)
81
249
        return 0;
82
83
84
89
    return AVPROBE_SCORE_MAX - 1;
85
338
}
86
87
static int flic_read_header(AVFormatContext *s)
88
6.14k
{
89
6.14k
    FlicDemuxContext *flic = s->priv_data;
90
6.14k
    AVIOContext *pb = s->pb;
91
6.14k
    unsigned char header[FLIC_HEADER_SIZE];
92
6.14k
    AVStream *st, *ast;
93
6.14k
    int speed, ret;
94
6.14k
    int magic_number;
95
6.14k
    unsigned char preamble[FLIC_PREAMBLE_SIZE];
96
97
6.14k
    flic->frame_number = 0;
98
99
    /* load the whole header and pull out the width and height */
100
6.14k
    if (avio_read(pb, header, FLIC_HEADER_SIZE) != FLIC_HEADER_SIZE)
101
4.56k
        return AVERROR(EIO);
102
103
1.58k
    magic_number = AV_RL16(&header[4]);
104
1.58k
    speed = AV_RL32(&header[0x10]);
105
1.58k
    if (speed == 0)
106
65
        speed = FLIC_DEFAULT_SPEED;
107
108
    /* initialize the decoder streams */
109
1.58k
    st = avformat_new_stream(s, NULL);
110
1.58k
    if (!st)
111
0
        return AVERROR(ENOMEM);
112
1.58k
    flic->video_stream_index = st->index;
113
1.58k
    st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
114
1.58k
    st->codecpar->codec_id = AV_CODEC_ID_FLIC;
115
1.58k
    st->codecpar->codec_tag = 0;  /* no fourcc */
116
1.58k
    st->codecpar->width = AV_RL16(&header[0x08]);
117
1.58k
    st->codecpar->height = AV_RL16(&header[0x0A]);
118
119
1.58k
    if (!st->codecpar->width || !st->codecpar->height) {
120
        /* Ugly hack needed for the following sample: */
121
        /* http://samples.mplayerhq.hu/fli-flc/fli-bugs/specular.flc */
122
262
        av_log(s, AV_LOG_WARNING,
123
262
               "File with no specified width/height. Trying 640x480.\n");
124
262
        st->codecpar->width  = 640;
125
262
        st->codecpar->height = 480;
126
262
    }
127
128
    /* send over the whole 128-byte FLIC header */
129
1.58k
    if ((ret = ff_alloc_extradata(st->codecpar, FLIC_HEADER_SIZE)) < 0)
130
0
        return ret;
131
1.58k
    memcpy(st->codecpar->extradata, header, FLIC_HEADER_SIZE);
132
133
    /* peek at the preamble to detect TFTD videos - they seem to always start with an audio chunk */
134
1.58k
    if (avio_read(pb, preamble, FLIC_PREAMBLE_SIZE) != FLIC_PREAMBLE_SIZE) {
135
42
        av_log(s, AV_LOG_ERROR, "Failed to peek at preamble\n");
136
42
        return AVERROR(EIO);
137
42
    }
138
139
1.54k
    avio_seek(pb, -FLIC_PREAMBLE_SIZE, SEEK_CUR);
140
141
    /* Time to figure out the framerate:
142
     * If the first preamble's magic number is 0xAAAA then this file is from
143
     * X-COM: Terror from the Deep. If on the other hand there is a FLIC chunk
144
     * magic number at offset 0x10 assume this file is from Magic Carpet instead.
145
     * If neither of the above is true then this is a normal FLIC file.
146
     */
147
1.54k
    if (AV_RL16(&preamble[4]) == FLIC_TFTD_CHUNK_AUDIO) {
148
        /* TFTD videos have an extra 22050 Hz 8-bit mono audio stream */
149
541
        ast = avformat_new_stream(s, NULL);
150
541
        if (!ast)
151
0
            return AVERROR(ENOMEM);
152
153
541
        flic->audio_stream_index = ast->index;
154
155
        /* all audio frames are the same size, so use the size of the first chunk for block_align */
156
541
        ast->codecpar->block_align = AV_RL32(&preamble[0]);
157
541
        ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
158
541
        ast->codecpar->codec_id = AV_CODEC_ID_PCM_U8;
159
541
        ast->codecpar->codec_tag = 0;
160
541
        ast->codecpar->sample_rate = FLIC_TFTD_SAMPLE_RATE;
161
541
        ast->codecpar->bit_rate = st->codecpar->sample_rate * 8;
162
541
        ast->codecpar->bits_per_coded_sample = 8;
163
541
        ast->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;
164
541
        ast->codecpar->extradata_size = 0;
165
166
        /* Since the header information is incorrect we have to figure out the
167
         * framerate using block_align and the fact that the audio is 22050 Hz.
168
         * We usually have two cases: 2205 -> 10 fps and 1470 -> 15 fps */
169
541
        avpriv_set_pts_info(st, 64, ast->codecpar->block_align, FLIC_TFTD_SAMPLE_RATE);
170
541
        avpriv_set_pts_info(ast, 64, 1, FLIC_TFTD_SAMPLE_RATE);
171
1.00k
    } else if (AV_RL16(&header[0x10]) == FLIC_CHUNK_MAGIC_1) {
172
201
        avpriv_set_pts_info(st, 64, FLIC_MC_SPEED, 70);
173
174
        /* rewind the stream since the first chunk is at offset 12 */
175
201
        avio_seek(pb, 12, SEEK_SET);
176
177
        /* send over abbreviated FLIC header chunk */
178
201
        if ((ret = ff_alloc_extradata(st->codecpar, 12)) < 0)
179
0
            return ret;
180
201
        memcpy(st->codecpar->extradata, header, 12);
181
182
800
    } else if (magic_number == FLIC_FILE_MAGIC_1) {
183
145
        avpriv_set_pts_info(st, 64, speed, 70);
184
655
    } else if ((magic_number == FLIC_FILE_MAGIC_2) ||
185
655
               (magic_number == FLIC_FILE_MAGIC_3)) {
186
526
        avpriv_set_pts_info(st, 64, speed, 1000);
187
526
    } else {
188
129
        av_log(s, AV_LOG_ERROR, "Invalid or unsupported magic chunk in file\n");
189
129
        return AVERROR_INVALIDDATA;
190
129
    }
191
192
1.41k
    return 0;
193
1.54k
}
194
195
static int flic_read_packet(AVFormatContext *s,
196
                            AVPacket *pkt)
197
976k
{
198
976k
    FlicDemuxContext *flic = s->priv_data;
199
976k
    AVIOContext *pb = s->pb;
200
976k
    int packet_read = 0;
201
976k
    unsigned int size;
202
976k
    int magic;
203
976k
    int ret = 0;
204
976k
    unsigned char preamble[FLIC_PREAMBLE_SIZE];
205
976k
    int64_t pos = avio_tell(pb);
206
207
2.21M
    while (!packet_read && !avio_feof(pb)) {
208
209
1.23M
        if ((ret = avio_read(pb, preamble, FLIC_PREAMBLE_SIZE)) !=
210
1.23M
            FLIC_PREAMBLE_SIZE) {
211
992
            ret = AVERROR(EIO);
212
992
            break;
213
992
        }
214
215
1.23M
        size = AV_RL32(&preamble[0]);
216
1.23M
        magic = AV_RL16(&preamble[4]);
217
218
1.23M
        if (((magic == FLIC_CHUNK_MAGIC_1) || (magic == FLIC_CHUNK_MAGIC_2)) && size > FLIC_PREAMBLE_SIZE) {
219
725k
            if ((ret = av_new_packet(pkt, size)) < 0)
220
63
                return ret;
221
222
725k
            pkt->stream_index = flic->video_stream_index;
223
725k
            pkt->pos = pos;
224
725k
            memcpy(pkt->data, preamble, FLIC_PREAMBLE_SIZE);
225
725k
            ret = avio_read(pb, pkt->data + FLIC_PREAMBLE_SIZE,
226
725k
                size - FLIC_PREAMBLE_SIZE);
227
725k
            if (ret != size - FLIC_PREAMBLE_SIZE) {
228
81
                ret = AVERROR(EIO);
229
81
            }
230
725k
            pkt->flags = flic->frame_number == 0 ? AV_PKT_FLAG_KEY : 0;
231
725k
            pkt->pts = flic->frame_number;
232
725k
            if (flic->frame_number == 0)
233
915
                av_add_index_entry(s->streams[flic->video_stream_index], pkt->pos, pkt->pts, pkt->size, 0, AVINDEX_KEYFRAME);
234
725k
            packet_read = 1;
235
725k
            flic->frame_number++;
236
725k
        } else if (magic == FLIC_TFTD_CHUNK_AUDIO) {
237
248k
            if ((ret = av_new_packet(pkt, size)) < 0)
238
89
                return ret;
239
240
            /* skip useless 10B sub-header (yes, it's not accounted for in the chunk header) */
241
248k
            avio_skip(pb, 10);
242
243
248k
            pkt->stream_index = flic->audio_stream_index;
244
248k
            pkt->pos = pos;
245
248k
            pkt->flags = AV_PKT_FLAG_KEY;
246
248k
            ret = avio_read(pb, pkt->data, size);
247
248
248k
            if (ret != size) {
249
82
                ret = AVERROR(EIO);
250
82
                break;
251
82
            }
252
253
248k
            packet_read = 1;
254
259k
        } else {
255
            /* not interested in this chunk */
256
259k
            avio_skip(pb, size - 6);
257
259k
        }
258
1.23M
    }
259
260
976k
    return avio_feof(pb) ? AVERROR_EOF : ret;
261
976k
}
262
263
static int flic_read_seek(AVFormatContext *s, int stream_index,
264
                          int64_t pts, int flags)
265
0
{
266
0
    FlicDemuxContext *flic = s->priv_data;
267
0
    AVStream *st = s->streams[stream_index];
268
0
    FFStream *const sti = ffstream(st);
269
0
    int64_t pos, ts;
270
0
    int index;
271
272
0
    if (!sti->index_entries || stream_index != flic->video_stream_index)
273
0
        return -1;
274
275
0
    index = av_index_search_timestamp(st, pts, flags);
276
277
0
    if (index < 0)
278
0
        index = av_index_search_timestamp(st, pts, flags ^ AVSEEK_FLAG_BACKWARD);
279
0
    if (index < 0)
280
0
        return -1;
281
282
0
    pos = sti->index_entries[index].pos;
283
0
    ts  = sti->index_entries[index].timestamp;
284
0
    flic->frame_number = ts;
285
0
    avio_seek(s->pb, pos, SEEK_SET);
286
0
    return 0;
287
0
}
288
289
const FFInputFormat ff_flic_demuxer = {
290
    .p.name         = "flic",
291
    .p.long_name    = NULL_IF_CONFIG_SMALL("FLI/FLC/FLX animation"),
292
    .priv_data_size = sizeof(FlicDemuxContext),
293
    .read_probe     = flic_probe,
294
    .read_header    = flic_read_header,
295
    .read_packet    = flic_read_packet,
296
    .read_seek      = flic_read_seek,
297
};