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