]> git.sesse.net Git - ffmpeg/blob - libavcodec/libkvazaar.c
avcodec: Remove redundant freeing of extradata of encoders
[ffmpeg] / 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 <stdint.h>
25 #include <string.h>
26
27 #include "libavutil/attributes.h"
28 #include "libavutil/avassert.h"
29 #include "libavutil/dict.h"
30 #include "libavutil/error.h"
31 #include "libavutil/imgutils.h"
32 #include "libavutil/internal.h"
33 #include "libavutil/log.h"
34 #include "libavutil/mem.h"
35 #include "libavutil/pixdesc.h"
36 #include "libavutil/opt.h"
37
38 #include "avcodec.h"
39 #include "internal.h"
40 #include "packet_internal.h"
41
42 typedef struct LibkvazaarContext {
43     const AVClass *class;
44
45     const kvz_api *api;
46     kvz_encoder *encoder;
47     kvz_config *config;
48
49     char *kvz_params;
50 } LibkvazaarContext;
51
52 static av_cold int libkvazaar_init(AVCodecContext *avctx)
53 {
54     LibkvazaarContext *const ctx = avctx->priv_data;
55     const kvz_api *const api = ctx->api = kvz_api_get(8);
56     kvz_config *cfg = NULL;
57     kvz_encoder *enc = NULL;
58
59     /* Kvazaar requires width and height to be multiples of eight. */
60     if (avctx->width % 8 || avctx->height % 8) {
61         av_log(avctx, AV_LOG_ERROR,
62                "Video dimensions are not a multiple of 8 (%dx%d).\n",
63                avctx->width, avctx->height);
64         return AVERROR(ENOSYS);
65     }
66
67     ctx->config = cfg = api->config_alloc();
68     if (!cfg) {
69         av_log(avctx, AV_LOG_ERROR,
70                "Could not allocate kvazaar config structure.\n");
71         return AVERROR(ENOMEM);
72     }
73
74     if (!api->config_init(cfg)) {
75         av_log(avctx, AV_LOG_ERROR,
76                "Could not initialize kvazaar config structure.\n");
77         return AVERROR_BUG;
78     }
79
80     cfg->width  = avctx->width;
81     cfg->height = avctx->height;
82
83     if (avctx->framerate.num > 0 && avctx->framerate.den > 0) {
84         cfg->framerate_num   = avctx->framerate.num;
85         cfg->framerate_denom = avctx->framerate.den;
86     } else {
87         if (avctx->ticks_per_frame > INT_MAX / avctx->time_base.num) {
88             av_log(avctx, AV_LOG_ERROR,
89                    "Could not set framerate for kvazaar: integer overflow\n");
90             return AVERROR(EINVAL);
91         }
92         cfg->framerate_num   = avctx->time_base.den;
93         cfg->framerate_denom = avctx->time_base.num * avctx->ticks_per_frame;
94     }
95     cfg->target_bitrate = avctx->bit_rate;
96     cfg->vui.sar_width  = avctx->sample_aspect_ratio.num;
97     cfg->vui.sar_height = avctx->sample_aspect_ratio.den;
98     if (avctx->bit_rate) {
99         cfg->rc_algorithm = KVZ_LAMBDA;
100     }
101
102     if (ctx->kvz_params) {
103         AVDictionary *dict = NULL;
104         if (!av_dict_parse_string(&dict, ctx->kvz_params, "=", ",", 0)) {
105             AVDictionaryEntry *entry = NULL;
106             while ((entry = av_dict_get(dict, "", entry, AV_DICT_IGNORE_SUFFIX))) {
107                 if (!api->config_parse(cfg, entry->key, entry->value)) {
108                     av_log(avctx, AV_LOG_WARNING, "Invalid option: %s=%s.\n",
109                            entry->key, entry->value);
110                 }
111             }
112         }
113         av_dict_free(&dict);
114     }
115
116     ctx->encoder = enc = api->encoder_open(cfg);
117     if (!enc) {
118         av_log(avctx, AV_LOG_ERROR, "Could not open kvazaar encoder.\n");
119         return AVERROR_BUG;
120     }
121
122     if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
123         kvz_data_chunk *data_out = NULL;
124         kvz_data_chunk *chunk = NULL;
125         uint32_t len_out;
126         uint8_t *p;
127
128         if (!api->encoder_headers(enc, &data_out, &len_out))
129             return AVERROR(ENOMEM);
130
131         avctx->extradata = p = av_mallocz(len_out + AV_INPUT_BUFFER_PADDING_SIZE);
132         if (!p) {
133             ctx->api->chunk_free(data_out);
134             return AVERROR(ENOMEM);
135         }
136
137         avctx->extradata_size = len_out;
138
139         for (chunk = data_out; chunk != NULL; chunk = chunk->next) {
140             memcpy(p, chunk->data, chunk->len);
141             p += chunk->len;
142         }
143
144         ctx->api->chunk_free(data_out);
145     }
146
147     return 0;
148 }
149
150 static av_cold int libkvazaar_close(AVCodecContext *avctx)
151 {
152     LibkvazaarContext *ctx = avctx->priv_data;
153
154     if (ctx->api) {
155         ctx->api->encoder_close(ctx->encoder);
156         ctx->api->config_destroy(ctx->config);
157     }
158
159     return 0;
160 }
161
162 static int libkvazaar_encode(AVCodecContext *avctx,
163                              AVPacket *avpkt,
164                              const AVFrame *frame,
165                              int *got_packet_ptr)
166 {
167     LibkvazaarContext *ctx = avctx->priv_data;
168     kvz_picture *input_pic = NULL;
169     kvz_picture *recon_pic = NULL;
170     kvz_frame_info frame_info;
171     kvz_data_chunk *data_out = NULL;
172     uint32_t len_out = 0;
173     int retval = 0;
174     int pict_type;
175
176     *got_packet_ptr = 0;
177
178     if (frame) {
179         if (frame->width != ctx->config->width ||
180             frame->height != ctx->config->height) {
181             av_log(avctx, AV_LOG_ERROR,
182                    "Changing video dimensions during encoding is not supported. "
183                    "(changed from %dx%d to %dx%d)\n",
184                    ctx->config->width, ctx->config->height,
185                    frame->width, frame->height);
186             retval = AVERROR_INVALIDDATA;
187             goto done;
188         }
189
190         if (frame->format != avctx->pix_fmt) {
191             av_log(avctx, AV_LOG_ERROR,
192                    "Changing pixel format during encoding is not supported. "
193                    "(changed from %s to %s)\n",
194                    av_get_pix_fmt_name(avctx->pix_fmt),
195                    av_get_pix_fmt_name(frame->format));
196             retval = AVERROR_INVALIDDATA;
197             goto done;
198         }
199
200         // Allocate input picture for kvazaar.
201         input_pic = ctx->api->picture_alloc(frame->width, frame->height);
202         if (!input_pic) {
203             av_log(avctx, AV_LOG_ERROR, "Failed to allocate picture.\n");
204             retval = AVERROR(ENOMEM);
205             goto done;
206         }
207
208         // Copy pixels from frame to input_pic.
209         {
210             int dst_linesizes[4] = {
211               frame->width,
212               frame->width / 2,
213               frame->width / 2,
214               0
215             };
216             av_image_copy(input_pic->data, dst_linesizes,
217                           (const uint8_t **)frame->data, frame->linesize,
218                           frame->format, frame->width, frame->height);
219         }
220
221         input_pic->pts = frame->pts;
222     }
223
224     retval = ctx->api->encoder_encode(ctx->encoder,
225                                       input_pic,
226                                       &data_out, &len_out,
227                                       &recon_pic, NULL,
228                                       &frame_info);
229     if (!retval) {
230         av_log(avctx, AV_LOG_ERROR, "Failed to encode frame.\n");
231         retval = AVERROR_INVALIDDATA;
232         goto done;
233     } else
234         retval = 0; /* kvazaar returns 1 on success */
235
236     if (data_out) {
237         kvz_data_chunk *chunk = NULL;
238         uint64_t written = 0;
239
240         retval = ff_alloc_packet2(avctx, avpkt, len_out, len_out);
241         if (retval < 0) {
242             av_log(avctx, AV_LOG_ERROR, "Failed to allocate output packet.\n");
243             goto done;
244         }
245
246         for (chunk = data_out; chunk != NULL; chunk = chunk->next) {
247             av_assert0(written + chunk->len <= len_out);
248             memcpy(avpkt->data + written, chunk->data, chunk->len);
249             written += chunk->len;
250         }
251
252         avpkt->pts = recon_pic->pts;
253         avpkt->dts = recon_pic->dts;
254         avpkt->flags = 0;
255         // IRAP VCL NAL unit types span the range
256         // [BLA_W_LP (16), RSV_IRAP_VCL23 (23)].
257         if (frame_info.nal_unit_type >= KVZ_NAL_BLA_W_LP &&
258             frame_info.nal_unit_type <= KVZ_NAL_RSV_IRAP_VCL23) {
259             avpkt->flags |= AV_PKT_FLAG_KEY;
260         }
261
262         switch (frame_info.slice_type) {
263         case KVZ_SLICE_I:
264             pict_type = AV_PICTURE_TYPE_I;
265             break;
266         case KVZ_SLICE_P:
267             pict_type = AV_PICTURE_TYPE_P;
268             break;
269         case KVZ_SLICE_B:
270             pict_type = AV_PICTURE_TYPE_B;
271             break;
272         default:
273             av_log(avctx, AV_LOG_ERROR, "Unknown picture type encountered.\n");
274             return AVERROR_EXTERNAL;
275         }
276
277         ff_side_data_set_encoder_stats(avpkt, frame_info.qp * FF_QP2LAMBDA, NULL, 0, pict_type);
278
279         *got_packet_ptr = 1;
280     }
281
282 done:
283     ctx->api->picture_free(input_pic);
284     ctx->api->picture_free(recon_pic);
285     ctx->api->chunk_free(data_out);
286     return retval;
287 }
288
289 static const enum AVPixelFormat pix_fmts[] = {
290     AV_PIX_FMT_YUV420P,
291     AV_PIX_FMT_NONE
292 };
293
294 #define OFFSET(x) offsetof(LibkvazaarContext, x)
295 #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
296 static const AVOption options[] = {
297     { "kvazaar-params", "Set kvazaar parameters as a comma-separated list of key=value pairs.",
298         OFFSET(kvz_params), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VE },
299     { NULL },
300 };
301
302 static const AVClass class = {
303     .class_name = "libkvazaar",
304     .item_name  = av_default_item_name,
305     .option     = options,
306     .version    = LIBAVUTIL_VERSION_INT,
307 };
308
309 static const AVCodecDefault defaults[] = {
310     { "b", "0" },
311     { NULL },
312 };
313
314 const AVCodec ff_libkvazaar_encoder = {
315     .name             = "libkvazaar",
316     .long_name        = NULL_IF_CONFIG_SMALL("libkvazaar H.265 / HEVC"),
317     .type             = AVMEDIA_TYPE_VIDEO,
318     .id               = AV_CODEC_ID_HEVC,
319     .capabilities     = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS,
320     .pix_fmts         = pix_fmts,
321
322     .priv_class       = &class,
323     .priv_data_size   = sizeof(LibkvazaarContext),
324     .defaults         = defaults,
325
326     .init             = libkvazaar_init,
327     .encode2          = libkvazaar_encode,
328     .close            = libkvazaar_close,
329
330     .caps_internal    = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP |
331                         FF_CODEC_CAP_AUTO_THREADS,
332
333     .wrapper_name     = "libkvazaar",
334 };