avformat: add webp muxer
[ffmpeg.git] / libavformat / webpenc.c
1 /*
2  * webp muxer
3  * Copyright (c) 2014 Michael Niedermayer
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 #include "libavutil/intreadwrite.h"
23 #include "libavutil/opt.h"
24 #include "avformat.h"
25 #include "internal.h"
26
27 typedef struct WebpContext{
28     AVClass *class;
29     int frame_count;
30     AVPacket last_pkt;
31     int loop;
32 } WebpContext;
33
34 static int webp_write_header(AVFormatContext *s)
35 {
36     AVStream *st;
37
38     if (s->nb_streams != 1) {
39         av_log(s, AV_LOG_ERROR, "Only exactly 1 stream is supported\n");
40         return AVERROR(EINVAL);
41     }
42     st = s->streams[0];
43     if (st->codec->codec_id != AV_CODEC_ID_WEBP) {
44         av_log(s, AV_LOG_ERROR, "Only WebP is supported\n");
45         return AVERROR(EINVAL);
46     }
47     avpriv_set_pts_info(st, 24, 1, 1000);
48
49     avio_write(s->pb, "RIFF\0\0\0\0WEBP", 12);
50
51     return 0;
52 }
53
54 static int flush(AVFormatContext *s, int trailer, int64_t pts)
55 {
56     WebpContext *w = s->priv_data;
57     AVStream *st = s->streams[0];
58
59     if (w->last_pkt.size) {
60         int skip = 0;
61
62         if (AV_RL32(w->last_pkt.data) == AV_RL32("RIFF"))
63             skip = 12;
64         if (AV_RL32(w->last_pkt.data + skip) == AV_RL32("VP8X"))
65             skip += AV_RL32(w->last_pkt.data + skip + 4) + 8;
66
67         w->frame_count ++;
68         if (w->frame_count == 1 && !trailer) {
69             avio_write(s->pb, "VP8X", 4);
70             avio_wl32(s->pb, 10);
71             avio_w8(s->pb, 2+16);
72             avio_wl24(s->pb, 0);
73             avio_wl24(s->pb, st->codec->width - 1);
74             avio_wl24(s->pb, st->codec->height - 1);
75
76             avio_write(s->pb, "ANIM", 4);
77             avio_wl32(s->pb, 6);
78             avio_wl32(s->pb, 0xFFFFFFFF);
79             avio_wl16(s->pb, w->loop);
80         }
81
82         if (w->frame_count > trailer) {
83             avio_write(s->pb, "ANMF", 4);
84             avio_wl32(s->pb, 16 + w->last_pkt.size - skip);
85             avio_wl24(s->pb, 0);
86             avio_wl24(s->pb, 0);
87             avio_wl24(s->pb, st->codec->width - 1);
88             avio_wl24(s->pb, st->codec->height - 1);
89             if (w->last_pkt.pts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE) {
90                 avio_wl24(s->pb, pts - w->last_pkt.pts);
91             } else
92                 avio_wl24(s->pb, w->last_pkt.duration);
93             avio_w8(s->pb, 0);
94         }
95         avio_write(s->pb, w->last_pkt.data + skip, w->last_pkt.size - skip);
96         av_free_packet(&w->last_pkt);
97     }
98
99     return 0;
100 }
101
102 static int webp_write_packet(AVFormatContext *s, AVPacket *pkt)
103 {
104     WebpContext *w = s->priv_data;
105     int ret;
106
107     if ((ret = flush(s, 0, pkt->pts)) < 0)
108         return ret;
109
110     av_copy_packet(&w->last_pkt, pkt);
111
112     return 0;
113 }
114
115 static int webp_write_trailer(AVFormatContext *s)
116 {
117     WebpContext *w = s->priv_data;
118     unsigned filesize;
119     int ret;
120
121     if ((ret = flush(s, 1, AV_NOPTS_VALUE)) < 0)
122         return ret;
123
124     filesize = avio_tell(s->pb);
125     avio_seek(s->pb, 4, SEEK_SET);
126     avio_wl32(s->pb, filesize - 8);
127
128     return 0;
129 }
130
131 #define OFFSET(x) offsetof(WebpContext, x)
132 #define ENC AV_OPT_FLAG_ENCODING_PARAM
133 static const AVOption options[] = {
134     { "loop", "Number of times to loop the output: 0 - infinite loop", OFFSET(loop),
135       AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 65535, ENC },
136     { NULL },
137 };
138
139 static const AVClass webp_muxer_class = {
140     .class_name = "WebP muxer",
141     .item_name  = av_default_item_name,
142     .version    = LIBAVUTIL_VERSION_INT,
143     .option     = options,
144 };
145 AVOutputFormat ff_webp_muxer = {
146     .name           = "webp",
147     .long_name      = NULL_IF_CONFIG_SMALL("WebP"),
148     .extensions     = "webp",
149     .priv_data_size = sizeof(WebpContext),
150     .video_codec    = AV_CODEC_ID_WEBP,
151     .write_header   = webp_write_header,
152     .write_packet   = webp_write_packet,
153     .write_trailer  = webp_write_trailer,
154     .priv_class     = &webp_muxer_class,
155     .flags          = AVFMT_VARIABLE_FPS,
156 };