1 /*
2 * Microsoft Windows ICO demuxer
3 * Copyright (c) 2011 Peter Ross (pross@xvid.org)
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 * Microsoft Windows ICO demuxer
25 */
26
27 #include "libavutil/intreadwrite.h"
28 #include "libavcodec/bytestream.h"
29 #include "libavcodec/png.h"
30 #include "avformat.h"
31 #include "internal.h"
32
33 typedef struct {
34 int offset;
35 int size;
36 int nb_pal;
37 } IcoImage;
38
39 typedef struct {
40 int current_image;
41 int nb_images;
42 IcoImage * images;
43 } IcoDemuxContext;
44
probe(const AVProbeData *p)45 static int probe(const AVProbeData *p)
46 {
47 unsigned i, frames, checked = 0;
48
49 if (p->buf_size < 22 || AV_RL16(p->buf) || AV_RL16(p->buf + 2) != 1)
50 return 0;
51 frames = AV_RL16(p->buf + 4);
52 if (!frames)
53 return 0;
54 for (i = 0; i < frames && i * 16 + 22 <= p->buf_size; i++) {
55 unsigned offset;
56 if (AV_RL16(p->buf + 10 + i * 16) & ~1)
57 return FFMIN(i, AVPROBE_SCORE_MAX / 4);
58 if (p->buf[13 + i * 16])
59 return FFMIN(i, AVPROBE_SCORE_MAX / 4);
60 if (AV_RL32(p->buf + 14 + i * 16) < 40)
61 return FFMIN(i, AVPROBE_SCORE_MAX / 4);
62 offset = AV_RL32(p->buf + 18 + i * 16);
63 if (offset < 22)
64 return FFMIN(i, AVPROBE_SCORE_MAX / 4);
65 if (offset > p->buf_size - 8)
66 continue;
67 if (p->buf[offset] != 40 && AV_RB64(p->buf + offset) != PNGSIG)
68 return FFMIN(i, AVPROBE_SCORE_MAX / 4);
69 checked++;
70 }
71
72 if (checked < frames)
73 return AVPROBE_SCORE_MAX / 4 + FFMIN(checked, 1);
74 return AVPROBE_SCORE_MAX / 2 + 1;
75 }
76
read_header(AVFormatContext *s)77 static int read_header(AVFormatContext *s)
78 {
79 IcoDemuxContext *ico = s->priv_data;
80 AVIOContext *pb = s->pb;
81 int i, codec;
82
83 avio_skip(pb, 4);
84 ico->nb_images = avio_rl16(pb);
85
86 if (!ico->nb_images)
87 return AVERROR_INVALIDDATA;
88
89 ico->images = av_malloc_array(ico->nb_images, sizeof(IcoImage));
90 if (!ico->images)
91 return AVERROR(ENOMEM);
92
93 for (i = 0; i < ico->nb_images; i++) {
94 AVStream *st;
95 int tmp;
96
97 if (avio_seek(pb, 6 + i * 16, SEEK_SET) < 0)
98 return AVERROR_INVALIDDATA;
99
100 st = avformat_new_stream(s, NULL);
101 if (!st)
102 return AVERROR(ENOMEM);
103
104 st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
105 st->codecpar->width = avio_r8(pb);
106 st->codecpar->height = avio_r8(pb);
107 ico->images[i].nb_pal = avio_r8(pb);
108 if (ico->images[i].nb_pal == 255)
109 ico->images[i].nb_pal = 0;
110
111 avio_skip(pb, 5);
112
113 ico->images[i].size = avio_rl32(pb);
114 if (ico->images[i].size <= 0) {
115 av_log(s, AV_LOG_ERROR, "Invalid image size %d\n", ico->images[i].size);
116 return AVERROR_INVALIDDATA;
117 }
118 ico->images[i].offset = avio_rl32(pb);
119
120 if (avio_seek(pb, ico->images[i].offset, SEEK_SET) < 0)
121 return AVERROR_INVALIDDATA;
122
123 codec = avio_rl32(pb);
124 switch (codec) {
125 case MKTAG(0x89, 'P', 'N', 'G'):
126 st->codecpar->codec_id = AV_CODEC_ID_PNG;
127 st->codecpar->width = 0;
128 st->codecpar->height = 0;
129 break;
130 case 40:
131 if (ico->images[i].size < 40)
132 return AVERROR_INVALIDDATA;
133 st->codecpar->codec_id = AV_CODEC_ID_BMP;
134 tmp = avio_rl32(pb);
135 if (tmp)
136 st->codecpar->width = tmp;
137 tmp = avio_rl32(pb);
138 if (tmp)
139 st->codecpar->height = tmp / 2;
140 break;
141 default:
142 avpriv_request_sample(s, "codec %d", codec);
143 return AVERROR_INVALIDDATA;
144 }
145 }
146
147 return 0;
148 }
149
read_packet(AVFormatContext *s, AVPacket *pkt)150 static int read_packet(AVFormatContext *s, AVPacket *pkt)
151 {
152 IcoDemuxContext *ico = s->priv_data;
153 IcoImage *image;
154 AVIOContext *pb = s->pb;
155 AVStream *st;
156 int ret;
157
158 if (ico->current_image >= ico->nb_images)
159 return AVERROR_EOF;
160
161 st = s->streams[0];
162
163 image = &ico->images[ico->current_image];
164
165 if ((ret = avio_seek(pb, image->offset, SEEK_SET)) < 0)
166 return ret;
167
168 if (s->streams[ico->current_image]->codecpar->codec_id == AV_CODEC_ID_PNG) {
169 if ((ret = av_get_packet(pb, pkt, image->size)) < 0)
170 return ret;
171 } else {
172 uint8_t *buf;
173 if ((ret = av_new_packet(pkt, 14 + image->size)) < 0)
174 return ret;
175 buf = pkt->data;
176
177 /* add BMP header */
178 bytestream_put_byte(&buf, 'B');
179 bytestream_put_byte(&buf, 'M');
180 bytestream_put_le32(&buf, pkt->size);
181 bytestream_put_le16(&buf, 0);
182 bytestream_put_le16(&buf, 0);
183 bytestream_put_le32(&buf, 0);
184
185 if ((ret = avio_read(pb, buf, image->size)) != image->size) {
186 return ret < 0 ? ret : AVERROR_INVALIDDATA;
187 }
188
189 st->codecpar->bits_per_coded_sample = AV_RL16(buf + 14);
190
191 if (AV_RL32(buf + 32))
192 image->nb_pal = AV_RL32(buf + 32);
193
194 if (st->codecpar->bits_per_coded_sample <= 8 && !image->nb_pal) {
195 image->nb_pal = 1 << st->codecpar->bits_per_coded_sample;
196 AV_WL32(buf + 32, image->nb_pal);
197 }
198
199 if (image->nb_pal > INT_MAX / 4 - 14 - 40)
200 return AVERROR_INVALIDDATA;
201
202 AV_WL32(buf - 4, 14 + 40 + image->nb_pal * 4);
203 AV_WL32(buf + 8, AV_RL32(buf + 8) / 2);
204 }
205
206 pkt->stream_index = ico->current_image++;
207 pkt->flags |= AV_PKT_FLAG_KEY;
208
209 return 0;
210 }
211
ico_read_close(AVFormatContext * s)212 static int ico_read_close(AVFormatContext * s)
213 {
214 IcoDemuxContext *ico = s->priv_data;
215 av_freep(&ico->images);
216 return 0;
217 }
218
219 const AVInputFormat ff_ico_demuxer = {
220 .name = "ico",
221 .long_name = NULL_IF_CONFIG_SMALL("Microsoft Windows ICO"),
222 .priv_data_size = sizeof(IcoDemuxContext),
223 .flags_internal = FF_FMT_INIT_CLEANUP,
224 .read_probe = probe,
225 .read_header = read_header,
226 .read_packet = read_packet,
227 .read_close = ico_read_close,
228 .flags = AVFMT_NOTIMESTAMPS,
229 };
230