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 
34 typedef 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 
libwebp_anim_encode_init(AVCodecContext *avctx)41 static 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 
libwebp_anim_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet)59 static 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 
115 end:
116         WebPPictureFree(pic);
117         av_freep(&pic);
118         av_frame_free(&alt_frame);
119         return ret;
120     }
121 }
122 
libwebp_anim_encode_close(AVCodecContext *avctx)123 static 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 
132 const 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