xref: /third_party/ffmpeg/libavformat/webpenc.c (revision cabdff1a)
1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * webp muxer
3cabdff1aSopenharmony_ci * Copyright (c) 2014 Michael Niedermayer
4cabdff1aSopenharmony_ci *
5cabdff1aSopenharmony_ci * This file is part of FFmpeg.
6cabdff1aSopenharmony_ci *
7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
11cabdff1aSopenharmony_ci *
12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15cabdff1aSopenharmony_ci * Lesser General Public License for more details.
16cabdff1aSopenharmony_ci *
17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20cabdff1aSopenharmony_ci */
21cabdff1aSopenharmony_ci
22cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h"
23cabdff1aSopenharmony_ci#include "libavutil/opt.h"
24cabdff1aSopenharmony_ci#include "avformat.h"
25cabdff1aSopenharmony_ci#include "internal.h"
26cabdff1aSopenharmony_ci
27cabdff1aSopenharmony_citypedef struct WebpContext{
28cabdff1aSopenharmony_ci    AVClass *class;
29cabdff1aSopenharmony_ci    int frame_count;
30cabdff1aSopenharmony_ci    AVPacket *last_pkt; /* Not owned by us */
31cabdff1aSopenharmony_ci    int loop;
32cabdff1aSopenharmony_ci    int wrote_webp_header;
33cabdff1aSopenharmony_ci    int using_webp_anim_encoder;
34cabdff1aSopenharmony_ci} WebpContext;
35cabdff1aSopenharmony_ci
36cabdff1aSopenharmony_cistatic int webp_init(AVFormatContext *s)
37cabdff1aSopenharmony_ci{
38cabdff1aSopenharmony_ci    WebpContext *const w = s->priv_data;
39cabdff1aSopenharmony_ci    AVStream *st;
40cabdff1aSopenharmony_ci
41cabdff1aSopenharmony_ci    w->last_pkt = ffformatcontext(s)->pkt;
42cabdff1aSopenharmony_ci
43cabdff1aSopenharmony_ci    if (s->nb_streams != 1) {
44cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Only exactly 1 stream is supported\n");
45cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
46cabdff1aSopenharmony_ci    }
47cabdff1aSopenharmony_ci    st = s->streams[0];
48cabdff1aSopenharmony_ci    if (st->codecpar->codec_id != AV_CODEC_ID_WEBP) {
49cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Only WebP is supported\n");
50cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
51cabdff1aSopenharmony_ci    }
52cabdff1aSopenharmony_ci    avpriv_set_pts_info(st, 24, 1, 1000);
53cabdff1aSopenharmony_ci
54cabdff1aSopenharmony_ci    return 0;
55cabdff1aSopenharmony_ci}
56cabdff1aSopenharmony_ci
57cabdff1aSopenharmony_cistatic int is_animated_webp_packet(AVPacket *pkt)
58cabdff1aSopenharmony_ci{
59cabdff1aSopenharmony_ci    int skip = 0;
60cabdff1aSopenharmony_ci    unsigned flags = 0;
61cabdff1aSopenharmony_ci
62cabdff1aSopenharmony_ci    if (pkt->size < 4)
63cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
64cabdff1aSopenharmony_ci    if (AV_RL32(pkt->data) == AV_RL32("RIFF"))
65cabdff1aSopenharmony_ci        skip = 12;
66cabdff1aSopenharmony_ci    // Safe to do this as a valid WebP bitstream is >=30 bytes.
67cabdff1aSopenharmony_ci    if (pkt->size < skip + 4)
68cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
69cabdff1aSopenharmony_ci    if (AV_RL32(pkt->data + skip) == AV_RL32("VP8X")) {
70cabdff1aSopenharmony_ci        flags |= pkt->data[skip + 4 + 4];
71cabdff1aSopenharmony_ci    }
72cabdff1aSopenharmony_ci
73cabdff1aSopenharmony_ci    if (flags & 2)  // ANIMATION_FLAG is on
74cabdff1aSopenharmony_ci        return 1;
75cabdff1aSopenharmony_ci    return 0;
76cabdff1aSopenharmony_ci}
77cabdff1aSopenharmony_ci
78cabdff1aSopenharmony_cistatic int flush(AVFormatContext *s, int trailer, int64_t pts)
79cabdff1aSopenharmony_ci{
80cabdff1aSopenharmony_ci    WebpContext *w = s->priv_data;
81cabdff1aSopenharmony_ci    AVStream *st = s->streams[0];
82cabdff1aSopenharmony_ci
83cabdff1aSopenharmony_ci    if (w->last_pkt->size) {
84cabdff1aSopenharmony_ci        int skip = 0;
85cabdff1aSopenharmony_ci        unsigned flags = 0;
86cabdff1aSopenharmony_ci        int vp8x = 0;
87cabdff1aSopenharmony_ci
88cabdff1aSopenharmony_ci        if (AV_RL32(w->last_pkt->data) == AV_RL32("RIFF"))
89cabdff1aSopenharmony_ci            skip = 12;
90cabdff1aSopenharmony_ci
91cabdff1aSopenharmony_ci        if (AV_RL32(w->last_pkt->data + skip) == AV_RL32("VP8X")) {
92cabdff1aSopenharmony_ci            flags |= w->last_pkt->data[skip + 4 + 4];
93cabdff1aSopenharmony_ci            vp8x = 1;
94cabdff1aSopenharmony_ci            skip += AV_RL32(w->last_pkt->data + skip + 4) + 8;
95cabdff1aSopenharmony_ci        }
96cabdff1aSopenharmony_ci
97cabdff1aSopenharmony_ci        if (!w->wrote_webp_header) {
98cabdff1aSopenharmony_ci            avio_write(s->pb, "RIFF\0\0\0\0WEBP", 12);
99cabdff1aSopenharmony_ci            w->wrote_webp_header = 1;
100cabdff1aSopenharmony_ci            if (w->frame_count > 1)  // first non-empty packet
101cabdff1aSopenharmony_ci                w->frame_count = 1;  // so we don't count previous empty packets.
102cabdff1aSopenharmony_ci        }
103cabdff1aSopenharmony_ci
104cabdff1aSopenharmony_ci        if (w->frame_count == 1) {
105cabdff1aSopenharmony_ci            if (!trailer) {
106cabdff1aSopenharmony_ci                vp8x = 1;
107cabdff1aSopenharmony_ci                flags |= 2 + 16;
108cabdff1aSopenharmony_ci            }
109cabdff1aSopenharmony_ci
110cabdff1aSopenharmony_ci            if (vp8x) {
111cabdff1aSopenharmony_ci                avio_write(s->pb, "VP8X", 4);
112cabdff1aSopenharmony_ci                avio_wl32(s->pb, 10);
113cabdff1aSopenharmony_ci                avio_w8(s->pb, flags);
114cabdff1aSopenharmony_ci                avio_wl24(s->pb, 0);
115cabdff1aSopenharmony_ci                avio_wl24(s->pb, st->codecpar->width - 1);
116cabdff1aSopenharmony_ci                avio_wl24(s->pb, st->codecpar->height - 1);
117cabdff1aSopenharmony_ci            }
118cabdff1aSopenharmony_ci            if (!trailer) {
119cabdff1aSopenharmony_ci                avio_write(s->pb, "ANIM", 4);
120cabdff1aSopenharmony_ci                avio_wl32(s->pb, 6);
121cabdff1aSopenharmony_ci                avio_wl32(s->pb, 0xFFFFFFFF);
122cabdff1aSopenharmony_ci                avio_wl16(s->pb, w->loop);
123cabdff1aSopenharmony_ci            }
124cabdff1aSopenharmony_ci        }
125cabdff1aSopenharmony_ci
126cabdff1aSopenharmony_ci        if (w->frame_count > trailer) {
127cabdff1aSopenharmony_ci            avio_write(s->pb, "ANMF", 4);
128cabdff1aSopenharmony_ci            avio_wl32(s->pb, 16 + w->last_pkt->size - skip);
129cabdff1aSopenharmony_ci            avio_wl24(s->pb, 0);
130cabdff1aSopenharmony_ci            avio_wl24(s->pb, 0);
131cabdff1aSopenharmony_ci            avio_wl24(s->pb, st->codecpar->width - 1);
132cabdff1aSopenharmony_ci            avio_wl24(s->pb, st->codecpar->height - 1);
133cabdff1aSopenharmony_ci            if (w->last_pkt->pts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE) {
134cabdff1aSopenharmony_ci                avio_wl24(s->pb, pts - w->last_pkt->pts);
135cabdff1aSopenharmony_ci            } else
136cabdff1aSopenharmony_ci                avio_wl24(s->pb, w->last_pkt->duration);
137cabdff1aSopenharmony_ci            avio_w8(s->pb, 0);
138cabdff1aSopenharmony_ci        }
139cabdff1aSopenharmony_ci        avio_write(s->pb, w->last_pkt->data + skip, w->last_pkt->size - skip);
140cabdff1aSopenharmony_ci        av_packet_unref(w->last_pkt);
141cabdff1aSopenharmony_ci    }
142cabdff1aSopenharmony_ci
143cabdff1aSopenharmony_ci    return 0;
144cabdff1aSopenharmony_ci}
145cabdff1aSopenharmony_ci
146cabdff1aSopenharmony_cistatic int webp_write_packet(AVFormatContext *s, AVPacket *pkt)
147cabdff1aSopenharmony_ci{
148cabdff1aSopenharmony_ci    WebpContext *w = s->priv_data;
149cabdff1aSopenharmony_ci    int ret;
150cabdff1aSopenharmony_ci
151cabdff1aSopenharmony_ci    if (!pkt->size)
152cabdff1aSopenharmony_ci        return 0;
153cabdff1aSopenharmony_ci    ret = is_animated_webp_packet(pkt);
154cabdff1aSopenharmony_ci    if (ret < 0)
155cabdff1aSopenharmony_ci        return ret;
156cabdff1aSopenharmony_ci    w->using_webp_anim_encoder |= ret;
157cabdff1aSopenharmony_ci
158cabdff1aSopenharmony_ci    if (w->using_webp_anim_encoder) {
159cabdff1aSopenharmony_ci        avio_write(s->pb, pkt->data, pkt->size);
160cabdff1aSopenharmony_ci        w->wrote_webp_header = 1;  // for good measure
161cabdff1aSopenharmony_ci    } else {
162cabdff1aSopenharmony_ci        int ret;
163cabdff1aSopenharmony_ci        if ((ret = flush(s, 0, pkt->pts)) < 0)
164cabdff1aSopenharmony_ci            return ret;
165cabdff1aSopenharmony_ci        av_packet_ref(w->last_pkt, pkt);
166cabdff1aSopenharmony_ci    }
167cabdff1aSopenharmony_ci    ++w->frame_count;
168cabdff1aSopenharmony_ci
169cabdff1aSopenharmony_ci    return 0;
170cabdff1aSopenharmony_ci}
171cabdff1aSopenharmony_ci
172cabdff1aSopenharmony_cistatic int webp_write_trailer(AVFormatContext *s)
173cabdff1aSopenharmony_ci{
174cabdff1aSopenharmony_ci    unsigned filesize;
175cabdff1aSopenharmony_ci    WebpContext *w = s->priv_data;
176cabdff1aSopenharmony_ci
177cabdff1aSopenharmony_ci    if (w->using_webp_anim_encoder) {
178cabdff1aSopenharmony_ci        if (w->loop) {  // Write loop count.
179cabdff1aSopenharmony_ci            avio_seek(s->pb, 42, SEEK_SET);
180cabdff1aSopenharmony_ci            avio_wl16(s->pb, w->loop);
181cabdff1aSopenharmony_ci        }
182cabdff1aSopenharmony_ci    } else {
183cabdff1aSopenharmony_ci        int ret;
184cabdff1aSopenharmony_ci        if ((ret = flush(s, 1, AV_NOPTS_VALUE)) < 0)
185cabdff1aSopenharmony_ci            return ret;
186cabdff1aSopenharmony_ci
187cabdff1aSopenharmony_ci        filesize = avio_tell(s->pb);
188cabdff1aSopenharmony_ci        avio_seek(s->pb, 4, SEEK_SET);
189cabdff1aSopenharmony_ci        avio_wl32(s->pb, filesize - 8);
190cabdff1aSopenharmony_ci        // Note: without the following, avio only writes 8 bytes to the file.
191cabdff1aSopenharmony_ci        avio_seek(s->pb, filesize, SEEK_SET);
192cabdff1aSopenharmony_ci    }
193cabdff1aSopenharmony_ci
194cabdff1aSopenharmony_ci    return 0;
195cabdff1aSopenharmony_ci}
196cabdff1aSopenharmony_ci
197cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(WebpContext, x)
198cabdff1aSopenharmony_ci#define ENC AV_OPT_FLAG_ENCODING_PARAM
199cabdff1aSopenharmony_cistatic const AVOption options[] = {
200cabdff1aSopenharmony_ci    { "loop", "Number of times to loop the output: 0 - infinite loop", OFFSET(loop),
201cabdff1aSopenharmony_ci      AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 65535, ENC },
202cabdff1aSopenharmony_ci    { NULL },
203cabdff1aSopenharmony_ci};
204cabdff1aSopenharmony_ci
205cabdff1aSopenharmony_cistatic const AVClass webp_muxer_class = {
206cabdff1aSopenharmony_ci    .class_name = "WebP muxer",
207cabdff1aSopenharmony_ci    .item_name  = av_default_item_name,
208cabdff1aSopenharmony_ci    .version    = LIBAVUTIL_VERSION_INT,
209cabdff1aSopenharmony_ci    .option     = options,
210cabdff1aSopenharmony_ci};
211cabdff1aSopenharmony_ciconst AVOutputFormat ff_webp_muxer = {
212cabdff1aSopenharmony_ci    .name           = "webp",
213cabdff1aSopenharmony_ci    .long_name      = NULL_IF_CONFIG_SMALL("WebP"),
214cabdff1aSopenharmony_ci    .extensions     = "webp",
215cabdff1aSopenharmony_ci    .priv_data_size = sizeof(WebpContext),
216cabdff1aSopenharmony_ci    .video_codec    = AV_CODEC_ID_WEBP,
217cabdff1aSopenharmony_ci    .init           = webp_init,
218cabdff1aSopenharmony_ci    .write_packet   = webp_write_packet,
219cabdff1aSopenharmony_ci    .write_trailer  = webp_write_trailer,
220cabdff1aSopenharmony_ci    .priv_class     = &webp_muxer_class,
221cabdff1aSopenharmony_ci    .flags          = AVFMT_VARIABLE_FPS,
222cabdff1aSopenharmony_ci};
223