• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
libkvazaar_init(AVCodecContext * avctx)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 
libkvazaar_close(AVCodecContext * avctx)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     if (avctx->extradata)
160         av_freep(&avctx->extradata);
161 
162     return 0;
163 }
164 
libkvazaar_encode(AVCodecContext * avctx,AVPacket * avpkt,const AVFrame * frame,int * got_packet_ptr)165 static int libkvazaar_encode(AVCodecContext *avctx,
166                              AVPacket *avpkt,
167                              const AVFrame *frame,
168                              int *got_packet_ptr)
169 {
170     LibkvazaarContext *ctx = avctx->priv_data;
171     kvz_picture *input_pic = NULL;
172     kvz_picture *recon_pic = NULL;
173     kvz_frame_info frame_info;
174     kvz_data_chunk *data_out = NULL;
175     uint32_t len_out = 0;
176     int retval = 0;
177     int pict_type;
178 
179     *got_packet_ptr = 0;
180 
181     if (frame) {
182         if (frame->width != ctx->config->width ||
183             frame->height != ctx->config->height) {
184             av_log(avctx, AV_LOG_ERROR,
185                    "Changing video dimensions during encoding is not supported. "
186                    "(changed from %dx%d to %dx%d)\n",
187                    ctx->config->width, ctx->config->height,
188                    frame->width, frame->height);
189             retval = AVERROR_INVALIDDATA;
190             goto done;
191         }
192 
193         if (frame->format != avctx->pix_fmt) {
194             av_log(avctx, AV_LOG_ERROR,
195                    "Changing pixel format during encoding is not supported. "
196                    "(changed from %s to %s)\n",
197                    av_get_pix_fmt_name(avctx->pix_fmt),
198                    av_get_pix_fmt_name(frame->format));
199             retval = AVERROR_INVALIDDATA;
200             goto done;
201         }
202 
203         // Allocate input picture for kvazaar.
204         input_pic = ctx->api->picture_alloc(frame->width, frame->height);
205         if (!input_pic) {
206             av_log(avctx, AV_LOG_ERROR, "Failed to allocate picture.\n");
207             retval = AVERROR(ENOMEM);
208             goto done;
209         }
210 
211         // Copy pixels from frame to input_pic.
212         {
213             int dst_linesizes[4] = {
214               frame->width,
215               frame->width / 2,
216               frame->width / 2,
217               0
218             };
219             av_image_copy(input_pic->data, dst_linesizes,
220                           (const uint8_t **)frame->data, frame->linesize,
221                           frame->format, frame->width, frame->height);
222         }
223 
224         input_pic->pts = frame->pts;
225     }
226 
227     retval = ctx->api->encoder_encode(ctx->encoder,
228                                       input_pic,
229                                       &data_out, &len_out,
230                                       &recon_pic, NULL,
231                                       &frame_info);
232     if (!retval) {
233         av_log(avctx, AV_LOG_ERROR, "Failed to encode frame.\n");
234         retval = AVERROR_INVALIDDATA;
235         goto done;
236     } else
237         retval = 0; /* kvazaar returns 1 on success */
238 
239     if (data_out) {
240         kvz_data_chunk *chunk = NULL;
241         uint64_t written = 0;
242 
243         retval = ff_alloc_packet2(avctx, avpkt, len_out, len_out);
244         if (retval < 0) {
245             av_log(avctx, AV_LOG_ERROR, "Failed to allocate output packet.\n");
246             goto done;
247         }
248 
249         for (chunk = data_out; chunk != NULL; chunk = chunk->next) {
250             av_assert0(written + chunk->len <= len_out);
251             memcpy(avpkt->data + written, chunk->data, chunk->len);
252             written += chunk->len;
253         }
254 
255         avpkt->pts = recon_pic->pts;
256         avpkt->dts = recon_pic->dts;
257         avpkt->flags = 0;
258         // IRAP VCL NAL unit types span the range
259         // [BLA_W_LP (16), RSV_IRAP_VCL23 (23)].
260         if (frame_info.nal_unit_type >= KVZ_NAL_BLA_W_LP &&
261             frame_info.nal_unit_type <= KVZ_NAL_RSV_IRAP_VCL23) {
262             avpkt->flags |= AV_PKT_FLAG_KEY;
263         }
264 
265         switch (frame_info.slice_type) {
266         case KVZ_SLICE_I:
267             pict_type = AV_PICTURE_TYPE_I;
268             break;
269         case KVZ_SLICE_P:
270             pict_type = AV_PICTURE_TYPE_P;
271             break;
272         case KVZ_SLICE_B:
273             pict_type = AV_PICTURE_TYPE_B;
274             break;
275         default:
276             av_log(avctx, AV_LOG_ERROR, "Unknown picture type encountered.\n");
277             return AVERROR_EXTERNAL;
278         }
279 #if FF_API_CODED_FRAME
280 FF_DISABLE_DEPRECATION_WARNINGS
281         avctx->coded_frame->pict_type = pict_type;
282 FF_ENABLE_DEPRECATION_WARNINGS
283 #endif
284 
285         ff_side_data_set_encoder_stats(avpkt, frame_info.qp * FF_QP2LAMBDA, NULL, 0, pict_type);
286 
287 #if FF_API_CODED_FRAME
288 FF_DISABLE_DEPRECATION_WARNINGS
289         avctx->coded_frame->quality = frame_info.qp * FF_QP2LAMBDA;
290 FF_ENABLE_DEPRECATION_WARNINGS
291 #endif
292 
293         *got_packet_ptr = 1;
294     }
295 
296 done:
297     ctx->api->picture_free(input_pic);
298     ctx->api->picture_free(recon_pic);
299     ctx->api->chunk_free(data_out);
300     return retval;
301 }
302 
303 static const enum AVPixelFormat pix_fmts[] = {
304     AV_PIX_FMT_YUV420P,
305     AV_PIX_FMT_NONE
306 };
307 
308 #define OFFSET(x) offsetof(LibkvazaarContext, x)
309 #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
310 static const AVOption options[] = {
311     { "kvazaar-params", "Set kvazaar parameters as a comma-separated list of key=value pairs.",
312         OFFSET(kvz_params), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VE },
313     { NULL },
314 };
315 
316 static const AVClass class = {
317     .class_name = "libkvazaar",
318     .item_name  = av_default_item_name,
319     .option     = options,
320     .version    = LIBAVUTIL_VERSION_INT,
321 };
322 
323 static const AVCodecDefault defaults[] = {
324     { "b", "0" },
325     { NULL },
326 };
327 
328 AVCodec ff_libkvazaar_encoder = {
329     .name             = "libkvazaar",
330     .long_name        = NULL_IF_CONFIG_SMALL("libkvazaar H.265 / HEVC"),
331     .type             = AVMEDIA_TYPE_VIDEO,
332     .id               = AV_CODEC_ID_HEVC,
333     .capabilities     = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS,
334     .pix_fmts         = pix_fmts,
335 
336     .priv_class       = &class,
337     .priv_data_size   = sizeof(LibkvazaarContext),
338     .defaults         = defaults,
339 
340     .init             = libkvazaar_init,
341     .encode2          = libkvazaar_encode,
342     .close            = libkvazaar_close,
343 
344     .caps_internal    = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP |
345                         FF_CODEC_CAP_AUTO_THREADS,
346 
347     .wrapper_name     = "libkvazaar",
348 };
349