1/*
2 * WebP encoding support via libwebp
3 * Copyright (c) 2015 Urvang Joshi
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/**
23 * @file
24 * WebP encoder using libwebp (WebPAnimEncoder API)
25 */
26
27#include "config.h"
28#include "codec_internal.h"
29#include "encode.h"
30#include "libwebpenc_common.h"
31
32#include <webp/mux.h>
33
34typedef struct LibWebPAnimContext {
35    LibWebPContextCommon cc;
36    WebPAnimEncoder *enc;     // the main AnimEncoder object
37    int64_t first_frame_pts;  // pts of the first encoded frame.
38    int done;                 // If true, we have assembled the bitstream already
39} LibWebPAnimContext;
40
41static av_cold int libwebp_anim_encode_init(AVCodecContext *avctx)
42{
43    int ret = ff_libwebp_encode_init_common(avctx);
44    if (!ret) {
45        LibWebPAnimContext *s = avctx->priv_data;
46        WebPAnimEncoderOptions enc_options = { { 0 } };
47        WebPAnimEncoderOptionsInit(&enc_options);
48        enc_options.verbose = av_log_get_level() >= AV_LOG_VERBOSE;
49        // TODO(urvang): Expose some options on command-line perhaps.
50        s->enc = WebPAnimEncoderNew(avctx->width, avctx->height, &enc_options);
51        if (!s->enc)
52            return AVERROR(EINVAL);
53        s->first_frame_pts = AV_NOPTS_VALUE;
54        s->done = 0;
55    }
56    return ret;
57}
58
59static int libwebp_anim_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
60                                     const AVFrame *frame, int *got_packet) {
61    LibWebPAnimContext *s = avctx->priv_data;
62    int ret;
63
64    if (!frame) {
65        if (s->done) {  // Second flush: return empty package to denote finish.
66            *got_packet = 0;
67            return 0;
68        } else {  // First flush: assemble bitstream and return it.
69            WebPData assembled_data = { 0 };
70            ret = WebPAnimEncoderAssemble(s->enc, &assembled_data);
71            if (ret) {
72                ret = ff_get_encode_buffer(avctx, pkt, assembled_data.size, 0);
73                if (ret < 0) {
74                    WebPDataClear(&assembled_data);
75                    return ret;
76                }
77                memcpy(pkt->data, assembled_data.bytes, assembled_data.size);
78                WebPDataClear(&assembled_data);
79                s->done = 1;
80                pkt->pts = pkt->dts = s->first_frame_pts;
81                *got_packet = 1;
82                return 0;
83            } else {
84                WebPDataClear(&assembled_data);
85                av_log(s, AV_LOG_ERROR,
86                       "WebPAnimEncoderAssemble() failed with error: %d\n",
87                       VP8_ENC_ERROR_OUT_OF_MEMORY);
88                return AVERROR(ENOMEM);
89            }
90        }
91    } else {
92        int timestamp_ms;
93        WebPPicture *pic = NULL;
94        AVFrame *alt_frame = NULL;
95        ret = ff_libwebp_get_frame(avctx, &s->cc, frame, &alt_frame, &pic);
96        if (ret < 0)
97            goto end;
98
99        timestamp_ms =
100            avctx->time_base.num * frame->pts * 1000 / avctx->time_base.den;
101        ret = WebPAnimEncoderAdd(s->enc, pic, timestamp_ms, &s->cc.config);
102        if (!ret) {
103                av_log(avctx, AV_LOG_ERROR,
104                       "Encoding WebP frame failed with error: %d\n",
105                   pic->error_code);
106            ret = ff_libwebp_error_to_averror(pic->error_code);
107            goto end;
108        }
109
110        if (!avctx->frame_number)
111            s->first_frame_pts = frame->pts;
112        ret = 0;
113        *got_packet = 0;
114
115end:
116        WebPPictureFree(pic);
117        av_freep(&pic);
118        av_frame_free(&alt_frame);
119        return ret;
120    }
121}
122
123static int libwebp_anim_encode_close(AVCodecContext *avctx)
124{
125    LibWebPAnimContext *s = avctx->priv_data;
126    av_frame_free(&s->cc.ref);
127    WebPAnimEncoderDelete(s->enc);
128
129    return 0;
130}
131
132const FFCodec ff_libwebp_anim_encoder = {
133    .p.name         = "libwebp_anim",
134    .p.long_name    = NULL_IF_CONFIG_SMALL("libwebp WebP image"),
135    .p.type         = AVMEDIA_TYPE_VIDEO,
136    .p.id           = AV_CODEC_ID_WEBP,
137    .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY,
138    .p.pix_fmts     = ff_libwebpenc_pix_fmts,
139    .p.priv_class   = &ff_libwebpenc_class,
140    .p.wrapper_name = "libwebp",
141    .priv_data_size = sizeof(LibWebPAnimContext),
142    .defaults       = ff_libwebp_defaults,
143    .init           = libwebp_anim_encode_init,
144    FF_CODEC_ENCODE_CB(libwebp_anim_encode_frame),
145    .close          = libwebp_anim_encode_close,
146};
147