avcodec: add libkvazaar HECV encoder
[ffmpeg.git] / libavcodec / libkvazaar.c
1 /*
2  * libkvazaar encoder
3  *
4  * Copyright (c) 2015 Tampere University of Technology
5  *
6  * This file is part of FFmpeg.
7  *
8  * FFmpeg is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * FFmpeg is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with FFmpeg; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22
23 #include <kvazaar.h>
24 #include <string.h>
25
26 #include "libavutil/avassert.h"
27 #include "libavutil/dict.h"
28 #include "libavutil/opt.h"
29 #include "avcodec.h"
30 #include "internal.h"
31
32 typedef struct LibkvazaarContext {
33     const AVClass *class;
34
35     const kvz_api *api;
36     kvz_encoder *encoder;
37     kvz_config *config;
38
39     char *kvz_params;
40 } LibkvazaarContext;
41
42 static av_cold int libkvazaar_init(AVCodecContext *avctx)
43 {
44     int retval = 0;
45     kvz_config *cfg = NULL;
46     kvz_encoder *enc = NULL;
47     const kvz_api *const api = kvz_api_get(8);
48
49     LibkvazaarContext *const ctx = avctx->priv_data;
50
51     // Kvazaar requires width and height to be multiples of eight.
52     if (avctx->width % 8 || avctx->height % 8) {
53         av_log(avctx, AV_LOG_ERROR, "Video dimensions are not a multiple of 8.\n");
54         retval = AVERROR_INVALIDDATA;
55         goto done;
56     }
57
58     cfg = api->config_alloc();
59     if (!cfg) {
60         av_log(avctx, AV_LOG_ERROR, "Could not allocate kvazaar config structure.\n");
61         retval = AVERROR(ENOMEM);
62         goto done;
63     }
64
65     if (!api->config_init(cfg)) {
66         av_log(avctx, AV_LOG_ERROR, "Could not initialize kvazaar config structure.\n");
67         retval = AVERROR_EXTERNAL;
68         goto done;
69     }
70
71     cfg->width = avctx->width;
72     cfg->height = avctx->height;
73     cfg->framerate =
74       (double)(avctx->time_base.num * avctx->ticks_per_frame) / avctx->time_base.den;
75     cfg->threads = avctx->thread_count;
76     cfg->target_bitrate = avctx->bit_rate;
77     cfg->vui.sar_width = avctx->sample_aspect_ratio.num;
78     cfg->vui.sar_height = avctx->sample_aspect_ratio.den;
79
80     if (ctx->kvz_params) {
81         AVDictionary *dict = NULL;
82         if (!av_dict_parse_string(&dict, ctx->kvz_params, "=", ",", 0)) {
83             AVDictionaryEntry *entry = NULL;
84             while ((entry = av_dict_get(dict, "", entry, AV_DICT_IGNORE_SUFFIX))) {
85                 if (!api->config_parse(cfg, entry->key, entry->value)) {
86                     av_log(avctx, AV_LOG_WARNING,
87                            "Invalid option: %s=%s.\n",
88                            entry->key, entry->value);
89                 }
90             }
91             av_dict_free(&dict);
92         }
93     }
94
95     enc = api->encoder_open(cfg);
96     if (!enc) {
97         av_log(avctx, AV_LOG_ERROR, "Could not open kvazaar encoder.\n");
98         retval = AVERROR_EXTERNAL;
99         goto done;
100     }
101
102     ctx->api = api;
103     ctx->encoder = enc;
104     ctx->config = cfg;
105     enc = NULL;
106     cfg = NULL;
107
108 done:
109     if (cfg) api->config_destroy(cfg);
110     if (enc) api->encoder_close(enc);
111
112     return retval;
113 }
114
115 static av_cold int libkvazaar_close(AVCodecContext *avctx)
116 {
117     LibkvazaarContext *ctx = avctx->priv_data;
118     if (!ctx->api) return 0;
119
120     if (ctx->encoder) {
121         ctx->api->encoder_close(ctx->encoder);
122         ctx->encoder = NULL;
123     }
124
125     if (ctx->config) {
126         ctx->api->config_destroy(ctx->config);
127         ctx->config = NULL;
128     }
129
130     return 0;
131 }
132
133 static int libkvazaar_encode(AVCodecContext *avctx,
134                              AVPacket *avpkt,
135                              const AVFrame *frame,
136                              int *got_packet_ptr)
137 {
138     int retval = 0;
139     kvz_picture *img_in = NULL;
140     kvz_data_chunk *data_out = NULL;
141     uint32_t len_out = 0;
142     LibkvazaarContext *ctx = avctx->priv_data;
143
144     *got_packet_ptr = 0;
145
146     if (frame) {
147         int i = 0;
148
149         av_assert0(frame->width == ctx->config->width);
150         av_assert0(frame->height == ctx->config->height);
151         av_assert0(frame->format == avctx->pix_fmt);
152
153         // Allocate input picture for kvazaar.
154         img_in = ctx->api->picture_alloc(frame->width, frame->height);
155         if (!img_in) {
156             av_log(avctx, AV_LOG_ERROR, "Failed to allocate picture.\n");
157             retval = AVERROR(ENOMEM);
158             goto done;
159         }
160
161         // Copy pixels from frame to img_in.
162         for (i = 0; i < 3; ++i) {
163             uint8_t *dst = img_in->data[i];
164             uint8_t *src = frame->data[i];
165             int width = (i == 0) ? frame->width : (frame->width / 2);
166             int height = (i == 0) ? frame->height : (frame->height / 2);
167             int y = 0;
168             for (y = 0; y < height; ++y) {
169                 memcpy(dst, src, width);
170                 src += frame->linesize[i];
171                 dst += width;
172             }
173         }
174     }
175
176     if (!ctx->api->encoder_encode(ctx->encoder, img_in, &data_out, &len_out, NULL)) {
177         av_log(avctx, AV_LOG_ERROR, "Failed to encode frame.\n");
178         retval = AVERROR_EXTERNAL;
179         goto done;
180     }
181
182     if (data_out) {
183         kvz_data_chunk *chunk = NULL;
184         uint64_t written = 0;
185
186         retval = ff_alloc_packet(avpkt, len_out);
187         if (retval < 0) {
188             av_log(avctx, AV_LOG_ERROR, "Failed to allocate output packet.\n");
189             goto done;
190         }
191
192         for (chunk = data_out; chunk != NULL; chunk = chunk->next) {
193             av_assert0(written + chunk->len <= len_out);
194             memcpy(avpkt->data + written, chunk->data, chunk->len);
195             written += chunk->len;
196         }
197         *got_packet_ptr = 1;
198
199         ctx->api->chunk_free(data_out);
200         data_out = NULL;
201     }
202
203 done:
204     if (img_in) ctx->api->picture_free(img_in);
205     if (data_out) ctx->api->chunk_free(data_out);
206     return retval;
207 }
208
209 static const enum AVPixelFormat pix_fmts[] = {
210     AV_PIX_FMT_YUV420P,
211     AV_PIX_FMT_NONE
212 };
213
214 static const AVOption options[] = {
215     { "kvazaar-params", "Set kvazaar parameters as a comma-separated list of name=value pairs.",
216       offsetof(LibkvazaarContext, kvz_params), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0,
217       AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
218     { NULL },
219 };
220
221 static const AVClass class = {
222     .class_name = "libkvazaar",
223     .item_name  = av_default_item_name,
224     .option     = options,
225     .version    = LIBAVUTIL_VERSION_INT,
226 };
227
228 static const AVCodecDefault defaults[] = {
229     { "b", "0" },
230     { NULL },
231 };
232
233 AVCodec ff_libkvazaar_encoder = {
234     .name             = "libkvazaar",
235     .long_name        = NULL_IF_CONFIG_SMALL("libkvazaar H.265 / HEVC"),
236     .type             = AVMEDIA_TYPE_VIDEO,
237     .id               = AV_CODEC_ID_HEVC,
238     .capabilities     = CODEC_CAP_DELAY,
239     .pix_fmts         = pix_fmts,
240
241     .priv_class       = &class,
242     .priv_data_size   = sizeof(LibkvazaarContext),
243     .defaults         = defaults,
244
245     .init             = libkvazaar_init,
246     .encode2          = libkvazaar_encode,
247     .close            = libkvazaar_close,
248 };