]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/libkvazaar.c
Merge commit '4dfbc7a7559ccab666a8fd39de4224eb4b02c768'
[ffmpeg] / libavcodec / libkvazaar.c
index 7430e0ab7b2118f9311f4b27266c5e221b8fad00..e58405d27e4f7efc3f0a2360b64f4a3d2bf0d0ee 100644 (file)
 
 #include "libavutil/avassert.h"
 #include "libavutil/dict.h"
+#include "libavutil/error.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/internal.h"
+#include "libavutil/pixdesc.h"
 #include "libavutil/opt.h"
+
 #include "avcodec.h"
 #include "internal.h"
 
@@ -41,40 +46,39 @@ typedef struct LibkvazaarContext {
 
 static av_cold int libkvazaar_init(AVCodecContext *avctx)
 {
-    int retval = 0;
+    LibkvazaarContext *const ctx = avctx->priv_data;
+    const kvz_api *const api = ctx->api = kvz_api_get(8);
     kvz_config *cfg = NULL;
     kvz_encoder *enc = NULL;
-    const kvz_api *const api = kvz_api_get(8);
 
-    LibkvazaarContext *const ctx = avctx->priv_data;
-
-    // Kvazaar requires width and height to be multiples of eight.
+    /* Kvazaar requires width and height to be multiples of eight. */
     if (avctx->width % 8 || avctx->height % 8) {
-        av_log(avctx, AV_LOG_ERROR, "Video dimensions are not a multiple of 8.\n");
-        retval = AVERROR_INVALIDDATA;
-        goto done;
+        av_log(avctx, AV_LOG_ERROR,
+               "Video dimensions are not a multiple of 8 (%dx%d).\n",
+               avctx->width, avctx->height);
+        return AVERROR(ENOSYS);
     }
 
-    cfg = api->config_alloc();
+    ctx->config = cfg = api->config_alloc();
     if (!cfg) {
-        av_log(avctx, AV_LOG_ERROR, "Could not allocate kvazaar config structure.\n");
-        retval = AVERROR(ENOMEM);
-        goto done;
+        av_log(avctx, AV_LOG_ERROR,
+               "Could not allocate kvazaar config structure.\n");
+        return AVERROR(ENOMEM);
     }
 
     if (!api->config_init(cfg)) {
-        av_log(avctx, AV_LOG_ERROR, "Could not initialize kvazaar config structure.\n");
-        retval = AVERROR_EXTERNAL;
-        goto done;
+        av_log(avctx, AV_LOG_ERROR,
+               "Could not initialize kvazaar config structure.\n");
+        return AVERROR_BUG;
     }
 
-    cfg->width = avctx->width;
+    cfg->width  = avctx->width;
     cfg->height = avctx->height;
+
     cfg->framerate =
-      (double)(avctx->time_base.num * avctx->ticks_per_frame) / avctx->time_base.den;
-    cfg->threads = avctx->thread_count;
+      avctx->time_base.den / (double)(avctx->time_base.num * avctx->ticks_per_frame);
     cfg->target_bitrate = avctx->bit_rate;
-    cfg->vui.sar_width = avctx->sample_aspect_ratio.num;
+    cfg->vui.sar_width  = avctx->sample_aspect_ratio.num;
     cfg->vui.sar_height = avctx->sample_aspect_ratio.den;
 
     if (ctx->kvz_params) {
@@ -83,8 +87,7 @@ static av_cold int libkvazaar_init(AVCodecContext *avctx)
             AVDictionaryEntry *entry = NULL;
             while ((entry = av_dict_get(dict, "", entry, AV_DICT_IGNORE_SUFFIX))) {
                 if (!api->config_parse(cfg, entry->key, entry->value)) {
-                    av_log(avctx, AV_LOG_WARNING,
-                           "Invalid option: %s=%s.\n",
+                    av_log(avctx, AV_LOG_WARNING, "Invalid option: %s=%s.\n",
                            entry->key, entry->value);
                 }
             }
@@ -92,40 +95,51 @@ static av_cold int libkvazaar_init(AVCodecContext *avctx)
         }
     }
 
-    enc = api->encoder_open(cfg);
+    ctx->encoder = enc = api->encoder_open(cfg);
     if (!enc) {
         av_log(avctx, AV_LOG_ERROR, "Could not open kvazaar encoder.\n");
-        retval = AVERROR_EXTERNAL;
-        goto done;
+        return AVERROR_BUG;
     }
 
-    ctx->api = api;
-    ctx->encoder = enc;
-    ctx->config = cfg;
-    enc = NULL;
-    cfg = NULL;
+    if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
+        kvz_data_chunk *data_out = NULL;
+        kvz_data_chunk *chunk = NULL;
+        uint32_t len_out;
+        uint8_t *p;
 
-done:
-    if (cfg) api->config_destroy(cfg);
-    if (enc) api->encoder_close(enc);
+        if (!api->encoder_headers(enc, &data_out, &len_out))
+            return AVERROR(ENOMEM);
 
-    return retval;
+        avctx->extradata = p = av_mallocz(len_out + AV_INPUT_BUFFER_PADDING_SIZE);
+        if (!p) {
+            ctx->api->chunk_free(data_out);
+            return AVERROR(ENOMEM);
+        }
+
+        avctx->extradata_size = len_out;
+
+        for (chunk = data_out; chunk != NULL; chunk = chunk->next) {
+            memcpy(p, chunk->data, chunk->len);
+            p += chunk->len;
+        }
+
+        ctx->api->chunk_free(data_out);
+    }
+
+    return 0;
 }
 
 static av_cold int libkvazaar_close(AVCodecContext *avctx)
 {
     LibkvazaarContext *ctx = avctx->priv_data;
-    if (!ctx->api) return 0;
 
-    if (ctx->encoder) {
-        ctx->api->encoder_close(ctx->encoder);
-        ctx->encoder = NULL;
+    if (ctx->api) {
+      ctx->api->encoder_close(ctx->encoder);
+      ctx->api->config_destroy(ctx->config);
     }
 
-    if (ctx->config) {
-        ctx->api->config_destroy(ctx->config);
-        ctx->config = NULL;
-    }
+    if (avctx->extradata)
+        av_freep(&avctx->extradata);
 
     return 0;
 }
@@ -135,55 +149,74 @@ static int libkvazaar_encode(AVCodecContext *avctx,
                              const AVFrame *frame,
                              int *got_packet_ptr)
 {
-    int retval = 0;
-    kvz_picture *img_in = NULL;
-
+    LibkvazaarContext *ctx = avctx->priv_data;
+    kvz_picture *input_pic = NULL;
+    kvz_picture *recon_pic = NULL;
+    kvz_frame_info frame_info;
     kvz_data_chunk *data_out = NULL;
     uint32_t len_out = 0;
-    kvz_frame_info frame_info;
-
-    LibkvazaarContext *ctx = avctx->priv_data;
+    int retval = 0;
 
     *got_packet_ptr = 0;
 
     if (frame) {
-        int i = 0;
+        if (frame->width != ctx->config->width ||
+                frame->height != ctx->config->height) {
+            av_log(avctx, AV_LOG_ERROR,
+                   "Changing video dimensions during encoding is not supported. "
+                   "(changed from %dx%d to %dx%d)\n",
+                   ctx->config->width, ctx->config->height,
+                   frame->width, frame->height);
+            retval = AVERROR_INVALIDDATA;
+            goto done;
+        }
 
-        av_assert0(frame->width == ctx->config->width);
-        av_assert0(frame->height == ctx->config->height);
-        av_assert0(frame->format == avctx->pix_fmt);
+        if (frame->format != avctx->pix_fmt) {
+            av_log(avctx, AV_LOG_ERROR,
+                   "Changing pixel format during encoding is not supported. "
+                   "(changed from %s to %s)\n",
+                   av_get_pix_fmt_name(avctx->pix_fmt),
+                   av_get_pix_fmt_name(frame->format));
+            retval = AVERROR_INVALIDDATA;
+            goto done;
+        }
 
         // Allocate input picture for kvazaar.
-        img_in = ctx->api->picture_alloc(frame->width, frame->height);
-        if (!img_in) {
+        input_pic = ctx->api->picture_alloc(frame->width, frame->height);
+        if (!input_pic) {
             av_log(avctx, AV_LOG_ERROR, "Failed to allocate picture.\n");
             retval = AVERROR(ENOMEM);
             goto done;
         }
 
-        // Copy pixels from frame to img_in.
-        for (i = 0; i < 3; ++i) {
-            uint8_t *dst = img_in->data[i];
-            uint8_t *src = frame->data[i];
-            int width = (i == 0) ? frame->width : (frame->width / 2);
-            int height = (i == 0) ? frame->height : (frame->height / 2);
-            int y = 0;
-            for (y = 0; y < height; ++y) {
-                memcpy(dst, src, width);
-                src += frame->linesize[i];
-                dst += width;
-            }
+        // Copy pixels from frame to input_pic.
+        {
+            int dst_linesizes[4] = {
+              frame->width,
+              frame->width / 2,
+              frame->width / 2,
+              0
+            };
+            av_image_copy(input_pic->data, dst_linesizes,
+                          frame->data, frame->linesize,
+                          frame->format, frame->width, frame->height);
         }
+
+        input_pic->pts = frame->pts;
     }
 
-    if (!ctx->api->encoder_encode(ctx->encoder, img_in,
-                                  &data_out, &len_out,
-                                  NULL, NULL,
-                                  &frame_info)) {
+    retval = ctx->api->encoder_encode(ctx->encoder,
+                                      input_pic,
+                                      &data_out, &len_out,
+                                      &recon_pic, NULL,
+                                      &frame_info);
+    if (!retval) {
         av_log(avctx, AV_LOG_ERROR, "Failed to encode frame.\n");
-        retval = AVERROR_EXTERNAL;
+        retval = AVERROR_INVALIDDATA;
         goto done;
     }
+    else
+        retval = 0; /* kvazaar returns 1 on success */
 
     if (data_out) {
         kvz_data_chunk *chunk = NULL;
@@ -200,11 +233,9 @@ static int libkvazaar_encode(AVCodecContext *avctx,
             memcpy(avpkt->data + written, chunk->data, chunk->len);
             written += chunk->len;
         }
-        *got_packet_ptr = 1;
-
-        ctx->api->chunk_free(data_out);
-        data_out = NULL;
 
+        avpkt->pts = recon_pic->pts;
+        avpkt->dts = recon_pic->dts;
         avpkt->flags = 0;
         // IRAP VCL NAL unit types span the range
         // [BLA_W_LP (16), RSV_IRAP_VCL23 (23)].
@@ -212,11 +243,14 @@ static int libkvazaar_encode(AVCodecContext *avctx,
                 frame_info.nal_unit_type <= KVZ_NAL_RSV_IRAP_VCL23) {
             avpkt->flags |= AV_PKT_FLAG_KEY;
         }
+
+        *got_packet_ptr = 1;
     }
 
 done:
-    if (img_in) ctx->api->picture_free(img_in);
-    if (data_out) ctx->api->chunk_free(data_out);
+    ctx->api->picture_free(input_pic);
+    ctx->api->picture_free(recon_pic);
+    ctx->api->chunk_free(data_out);
     return retval;
 }
 
@@ -225,10 +259,11 @@ static const enum AVPixelFormat pix_fmts[] = {
     AV_PIX_FMT_NONE
 };
 
+#define OFFSET(x) offsetof(LibkvazaarContext, x)
+#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
 static const AVOption options[] = {
-    { "kvazaar-params", "Set kvazaar parameters as a comma-separated list of name=value pairs.",
-      offsetof(LibkvazaarContext, kvz_params), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0,
-      AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
+    { "kvazaar-params", "Set kvazaar parameters as a comma-separated list of key=value pairs.",
+        OFFSET(kvz_params), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VE },
     { NULL },
 };
 
@@ -259,4 +294,6 @@ AVCodec ff_libkvazaar_encoder = {
     .init             = libkvazaar_init,
     .encode2          = libkvazaar_encode,
     .close            = libkvazaar_close,
+
+    .caps_internal    = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
 };