00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00027 #include "libavutil/intreadwrite.h"
00028 #include "libavutil/pixdesc.h"
00029 #include "avformat.h"
00030
00031 typedef struct {
00032 int offset;
00033 int size;
00034 unsigned char width;
00035 unsigned char height;
00036 short bits;
00037 } IcoImage;
00038
00039 typedef struct {
00040 int current_image;
00041 int nb_images;
00042 IcoImage *images;
00043 } IcoMuxContext;
00044
00045 static int ico_check_attributes(AVFormatContext *s, const AVCodecContext *c)
00046 {
00047 if (c->codec_id == CODEC_ID_BMP) {
00048 if (c->pix_fmt == PIX_FMT_PAL8 && PIX_FMT_RGB32 != PIX_FMT_BGRA) {
00049 av_log(s, AV_LOG_ERROR, "Wrong endianness for bmp pixel format\n");
00050 return AVERROR(EINVAL);
00051 } else if (c->pix_fmt != PIX_FMT_PAL8 &&
00052 c->pix_fmt != PIX_FMT_RGB555LE &&
00053 c->pix_fmt != PIX_FMT_BGR24 &&
00054 c->pix_fmt != PIX_FMT_BGRA) {
00055 av_log(s, AV_LOG_ERROR, "BMP must be 1bit, 4bit, 8bit, 16bit, 24bit, or 32bit\n");
00056 return AVERROR(EINVAL);
00057 }
00058 } else if (c->codec_id == CODEC_ID_PNG) {
00059 if (c->pix_fmt != PIX_FMT_RGBA) {
00060 av_log(s, AV_LOG_ERROR, "PNG in ico requires pixel format to be rgba\n");
00061 return AVERROR(EINVAL);
00062 }
00063 } else {
00064 av_log(s, AV_LOG_ERROR, "Unsupported codec %s\n", c->codec_name);
00065 return AVERROR(EINVAL);
00066 }
00067
00068 if (c->width > 256 ||
00069 c->height > 256) {
00070 av_log(s, AV_LOG_ERROR, "Unsupported dimensions %dx%d (dimensions cannot exceed 256x256)\n", c->width, c->height);
00071 return AVERROR(EINVAL);
00072 }
00073
00074 return 0;
00075 }
00076
00077 static int ico_write_header(AVFormatContext *s)
00078 {
00079 IcoMuxContext *ico = s->priv_data;
00080 AVIOContext *pb = s->pb;
00081 int ret;
00082 int i;
00083
00084 if (!pb->seekable) {
00085 av_log(s, AV_LOG_ERROR, "Output is not seekable\n");
00086 return AVERROR(EINVAL);
00087 }
00088
00089 ico->current_image = 0;
00090 ico->nb_images = s->nb_streams;
00091
00092 avio_wl16(pb, 0);
00093 avio_wl16(pb, 1);
00094 avio_skip(pb, 2);
00095
00096 for (i = 0; i < s->nb_streams; i++) {
00097 if (ret = ico_check_attributes(s, s->streams[i]->codec))
00098 return ret;
00099
00100
00101 avio_skip(pb, 16);
00102 }
00103
00104 ico->images = av_mallocz(ico->nb_images * sizeof(IcoMuxContext));
00105 if (!ico->images)
00106 return AVERROR(ENOMEM);
00107
00108 avio_flush(pb);
00109
00110 return 0;
00111 }
00112
00113 static int ico_write_packet(AVFormatContext *s, AVPacket *pkt)
00114 {
00115 IcoMuxContext *ico = s->priv_data;
00116 IcoImage *image;
00117 AVIOContext *pb = s->pb;
00118 AVCodecContext *c = s->streams[pkt->stream_index]->codec;
00119 int i;
00120
00121 if (ico->current_image >= ico->nb_images) {
00122 av_log(s, AV_LOG_ERROR, "ICO already contains %d images\n", ico->current_image);
00123 return AVERROR(EIO);
00124 }
00125
00126 image = &ico->images[ico->current_image++];
00127
00128 image->offset = avio_tell(pb);
00129 image->width = (c->width == 256) ? 0 : c->width;
00130 image->height = (c->height == 256) ? 0 : c->height;
00131
00132 if (c->codec_id == CODEC_ID_PNG) {
00133 image->bits = c->bits_per_coded_sample;
00134 image->size = pkt->size;
00135
00136 avio_write(pb, pkt->data, pkt->size);
00137 } else {
00138 if (AV_RL32(pkt->data + 14) != 40) {
00139 av_log(s, AV_LOG_ERROR, "Invalid BMP\n");
00140 return AVERROR(EINVAL);
00141 }
00142
00143 image->bits = AV_RL16(pkt->data + 28);
00144 image->size = pkt->size - 14 + c->height * (c->width + 7) / 8;
00145
00146 avio_write(pb, pkt->data + 14, 8);
00147 avio_wl32(pb, AV_RL32(pkt->data + 22) * 2);
00148 avio_write(pb, pkt->data + 26, pkt->size - 26);
00149
00150 for (i = 0; i < c->height * (c->width + 7) / 8; ++i)
00151 avio_w8(pb, 0x00);
00152 }
00153
00154 avio_flush(pb);
00155
00156 return 0;
00157 }
00158
00159 static int ico_write_trailer(AVFormatContext *s)
00160 {
00161 IcoMuxContext *ico = s->priv_data;
00162 AVIOContext *pb = s->pb;
00163 int i;
00164
00165 avio_seek(pb, 4, SEEK_SET);
00166
00167 avio_wl16(pb, ico->current_image);
00168
00169 for (i = 0; i < ico->nb_images; i++) {
00170 avio_w8(pb, ico->images[i].width);
00171 avio_w8(pb, ico->images[i].height);
00172
00173 if (s->streams[i]->codec->codec_id == CODEC_ID_BMP &&
00174 s->streams[i]->codec->pix_fmt == PIX_FMT_PAL8) {
00175 avio_w8(pb, (ico->images[i].bits >= 8) ? 0 : 1 << ico->images[i].bits);
00176 } else {
00177 avio_w8(pb, 0);
00178 }
00179
00180 avio_w8(pb, 0);
00181 avio_wl16(pb, 1);
00182 avio_wl16(pb, ico->images[i].bits);
00183 avio_wl32(pb, ico->images[i].size);
00184 avio_wl32(pb, ico->images[i].offset);
00185 }
00186
00187 av_freep(&ico->images);
00188
00189 return 0;
00190 }
00191
00192 AVOutputFormat ff_ico_muxer = {
00193 .name = "ico",
00194 .long_name = NULL_IF_CONFIG_SMALL("Microsoft Windows ICO"),
00195 .priv_data_size = sizeof(IcoMuxContext),
00196 .mime_type = "image/vnd.microsoft.icon",
00197 .extensions = "ico",
00198 .audio_codec = CODEC_ID_NONE,
00199 .video_codec = CODEC_ID_BMP,
00200 .write_header = ico_write_header,
00201 .write_packet = ico_write_packet,
00202 .write_trailer = ico_write_trailer,
00203 .flags = AVFMT_NOTIMESTAMPS,
00204 };