]> git.sesse.net Git - ffmpeg/blob - libavformat/webpenc.c
Merge commit 'e2ce16392205d8efe9143329ed3fb5fcb15498fa'
[ffmpeg] / 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         unsigned flags = 0;
62         int vp8x = 0;
63
64         if (AV_RL32(w->last_pkt.data) == AV_RL32("RIFF"))
65             skip = 12;
66         if (AV_RL32(w->last_pkt.data + skip) == AV_RL32("VP8X")) {
67             flags |= w->last_pkt.data[skip + 4 + 4];
68             vp8x = 1;
69             skip += AV_RL32(w->last_pkt.data + skip + 4) + 8;
70         }
71
72         w->frame_count ++;
73
74         if (w->frame_count == 1) {
75             if (!trailer) {
76                 vp8x = 1;
77                 flags |= 2 + 16;
78             }
79
80             if (vp8x) {
81                 avio_write(s->pb, "VP8X", 4);
82                 avio_wl32(s->pb, 10);
83                 avio_w8(s->pb, flags);
84                 avio_wl24(s->pb, 0);
85                 avio_wl24(s->pb, st->codec->width - 1);
86                 avio_wl24(s->pb, st->codec->height - 1);
87             }
88             if (!trailer) {
89                 avio_write(s->pb, "ANIM", 4);
90                 avio_wl32(s->pb, 6);
91                 avio_wl32(s->pb, 0xFFFFFFFF);
92                 avio_wl16(s->pb, w->loop);
93             }
94         }
95
96         if (w->frame_count > trailer) {
97             avio_write(s->pb, "ANMF", 4);
98             avio_wl32(s->pb, 16 + w->last_pkt.size - skip);
99             avio_wl24(s->pb, 0);
100             avio_wl24(s->pb, 0);
101             avio_wl24(s->pb, st->codec->width - 1);
102             avio_wl24(s->pb, st->codec->height - 1);
103             if (w->last_pkt.pts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE) {
104                 avio_wl24(s->pb, pts - w->last_pkt.pts);
105             } else
106                 avio_wl24(s->pb, w->last_pkt.duration);
107             avio_w8(s->pb, 0);
108         }
109         avio_write(s->pb, w->last_pkt.data + skip, w->last_pkt.size - skip);
110         av_free_packet(&w->last_pkt);
111     }
112
113     return 0;
114 }
115
116 static int webp_write_packet(AVFormatContext *s, AVPacket *pkt)
117 {
118     WebpContext *w = s->priv_data;
119     int ret;
120
121     if ((ret = flush(s, 0, pkt->pts)) < 0)
122         return ret;
123
124     av_copy_packet(&w->last_pkt, pkt);
125
126     return 0;
127 }
128
129 static int webp_write_trailer(AVFormatContext *s)
130 {
131     unsigned filesize;
132     int ret;
133
134     if ((ret = flush(s, 1, AV_NOPTS_VALUE)) < 0)
135         return ret;
136
137     filesize = avio_tell(s->pb);
138     avio_seek(s->pb, 4, SEEK_SET);
139     avio_wl32(s->pb, filesize - 8);
140
141     return 0;
142 }
143
144 #define OFFSET(x) offsetof(WebpContext, x)
145 #define ENC AV_OPT_FLAG_ENCODING_PARAM
146 static const AVOption options[] = {
147     { "loop", "Number of times to loop the output: 0 - infinite loop", OFFSET(loop),
148       AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 65535, ENC },
149     { NULL },
150 };
151
152 static const AVClass webp_muxer_class = {
153     .class_name = "WebP muxer",
154     .item_name  = av_default_item_name,
155     .version    = LIBAVUTIL_VERSION_INT,
156     .option     = options,
157 };
158 AVOutputFormat ff_webp_muxer = {
159     .name           = "webp",
160     .long_name      = NULL_IF_CONFIG_SMALL("WebP"),
161     .extensions     = "webp",
162     .priv_data_size = sizeof(WebpContext),
163     .video_codec    = AV_CODEC_ID_WEBP,
164     .write_header   = webp_write_header,
165     .write_packet   = webp_write_packet,
166     .write_trailer  = webp_write_trailer,
167     .priv_class     = &webp_muxer_class,
168     .flags          = AVFMT_VARIABLE_FPS,
169 };