2 * Microsoft Windows ICO muxer
3 * Copyright (c) 2012 Michael Bradshaw <mjbshaw gmail com>
5 * This file is part of FFmpeg.
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.
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.
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
24 * Microsoft Windows ICO muxer
27 #include "libavutil/intreadwrite.h"
28 #include "libavutil/pixdesc.h"
45 static int ico_check_attributes(AVFormatContext
*s
, const AVCodecContext
*c
)
47 if (c
->codec_id
== AV_CODEC_ID_BMP
) {
48 if (c
->pix_fmt
== AV_PIX_FMT_PAL8
&& AV_PIX_FMT_RGB32
!= AV_PIX_FMT_BGRA
) {
49 av_log(s
, AV_LOG_ERROR
, "Wrong endianness for bmp pixel format\n");
50 return AVERROR(EINVAL
);
51 } else if (c
->pix_fmt
!= AV_PIX_FMT_PAL8
&&
52 c
->pix_fmt
!= AV_PIX_FMT_RGB555LE
&&
53 c
->pix_fmt
!= AV_PIX_FMT_BGR24
&&
54 c
->pix_fmt
!= AV_PIX_FMT_BGRA
) {
55 av_log(s
, AV_LOG_ERROR
, "BMP must be 1bit, 4bit, 8bit, 16bit, 24bit, or 32bit\n");
56 return AVERROR(EINVAL
);
58 } else if (c
->codec_id
== AV_CODEC_ID_PNG
) {
59 if (c
->pix_fmt
!= AV_PIX_FMT_RGBA
) {
60 av_log(s
, AV_LOG_ERROR
, "PNG in ico requires pixel format to be rgba\n");
61 return AVERROR(EINVAL
);
64 const AVCodecDescriptor
*codesc
= avcodec_descriptor_get(c
->codec_id
);
65 av_log(s
, AV_LOG_ERROR
, "Unsupported codec %s\n", codesc
? codesc
->name
: "");
66 return AVERROR(EINVAL
);
71 av_log(s
, AV_LOG_ERROR
, "Unsupported dimensions %dx%d (dimensions cannot exceed 256x256)\n", c
->width
, c
->height
);
72 return AVERROR(EINVAL
);
78 static int ico_write_header(AVFormatContext
*s
)
80 IcoMuxContext
*ico
= s
->priv_data
;
81 AVIOContext
*pb
= s
->pb
;
86 av_log(s
, AV_LOG_ERROR
, "Output is not seekable\n");
87 return AVERROR(EINVAL
);
90 ico
->current_image
= 0;
91 ico
->nb_images
= s
->nb_streams
;
93 avio_wl16(pb
, 0); // reserved
94 avio_wl16(pb
, 1); // 1 == icon
95 avio_skip(pb
, 2); // skip the number of images
97 for (i
= 0; i
< s
->nb_streams
; i
++) {
98 if (ret
= ico_check_attributes(s
, s
->streams
[i
]->codec
))
101 // Fill in later when writing trailer...
105 ico
->images
= av_mallocz_array(ico
->nb_images
, sizeof(IcoMuxContext
));
107 return AVERROR(ENOMEM
);
114 static int ico_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
116 IcoMuxContext
*ico
= s
->priv_data
;
118 AVIOContext
*pb
= s
->pb
;
119 AVCodecContext
*c
= s
->streams
[pkt
->stream_index
]->codec
;
122 if (ico
->current_image
>= ico
->nb_images
) {
123 av_log(s
, AV_LOG_ERROR
, "ICO already contains %d images\n", ico
->current_image
);
127 image
= &ico
->images
[ico
->current_image
++];
129 image
->offset
= avio_tell(pb
);
130 image
->width
= (c
->width
== 256) ? 0 : c
->width
;
131 image
->height
= (c
->height
== 256) ? 0 : c
->height
;
133 if (c
->codec_id
== AV_CODEC_ID_PNG
) {
134 image
->bits
= c
->bits_per_coded_sample
;
135 image
->size
= pkt
->size
;
137 avio_write(pb
, pkt
->data
, pkt
->size
);
139 if (AV_RL32(pkt
->data
+ 14) != 40) { // must be BITMAPINFOHEADER
140 av_log(s
, AV_LOG_ERROR
, "Invalid BMP\n");
141 return AVERROR(EINVAL
);
144 image
->bits
= AV_RL16(pkt
->data
+ 28); // allows things like 1bit and 4bit images to be preserved
145 image
->size
= pkt
->size
- 14 + c
->height
* (c
->width
+ 7) / 8;
147 avio_write(pb
, pkt
->data
+ 14, 8); // Skip the BITMAPFILEHEADER header
148 avio_wl32(pb
, AV_RL32(pkt
->data
+ 22) * 2); // rewrite height as 2 * height
149 avio_write(pb
, pkt
->data
+ 26, pkt
->size
- 26);
151 for (i
= 0; i
< c
->height
* (c
->width
+ 7) / 8; ++i
)
152 avio_w8(pb
, 0x00); // Write bitmask (opaque)
158 static int ico_write_trailer(AVFormatContext
*s
)
160 IcoMuxContext
*ico
= s
->priv_data
;
161 AVIOContext
*pb
= s
->pb
;
164 avio_seek(pb
, 4, SEEK_SET
);
166 avio_wl16(pb
, ico
->current_image
);
168 for (i
= 0; i
< ico
->nb_images
; i
++) {
169 avio_w8(pb
, ico
->images
[i
].width
);
170 avio_w8(pb
, ico
->images
[i
].height
);
172 if (s
->streams
[i
]->codec
->codec_id
== AV_CODEC_ID_BMP
&&
173 s
->streams
[i
]->codec
->pix_fmt
== AV_PIX_FMT_PAL8
) {
174 avio_w8(pb
, (ico
->images
[i
].bits
>= 8) ? 0 : 1 << ico
->images
[i
].bits
);
179 avio_w8(pb
, 0); // reserved
180 avio_wl16(pb
, 1); // color planes
181 avio_wl16(pb
, ico
->images
[i
].bits
);
182 avio_wl32(pb
, ico
->images
[i
].size
);
183 avio_wl32(pb
, ico
->images
[i
].offset
);
186 av_freep(&ico
->images
);
191 AVOutputFormat ff_ico_muxer
= {
193 .long_name
= NULL_IF_CONFIG_SMALL("Microsoft Windows ICO"),
194 .priv_data_size
= sizeof(IcoMuxContext
),
195 .mime_type
= "image/vnd.microsoft.icon",
197 .audio_codec
= AV_CODEC_ID_NONE
,
198 .video_codec
= AV_CODEC_ID_BMP
,
199 .write_header
= ico_write_header
,
200 .write_packet
= ico_write_packet
,
201 .write_trailer
= ico_write_trailer
,
202 .flags
= AVFMT_NOTIMESTAMPS
,