avcodec/nvenc: combine input and output surface structures
authorAndrey Turkin <andrey.turkin@gmail.com>
Fri, 20 May 2016 15:37:00 +0000 (18:37 +0300)
committerTimo Rothenpieler <timo@rothenpieler.org>
Wed, 25 May 2016 10:16:40 +0000 (12:16 +0200)
There is no point in separate structures as they have 1:1 relationship,
they are always used together and they have same lifetime.

Signed-off-by: Timo Rothenpieler <timo@rothenpieler.org>
libavcodec/nvenc.c

index 57b64d5..8624aac 100644 (file)
@@ -67,7 +67,7 @@ typedef CUresult(CUDAAPI *PCUCTXDESTROY)(CUcontext ctx);
 
 typedef NVENCSTATUS (NVENCAPI* PNVENCODEAPICREATEINSTANCE)(NV_ENCODE_API_FUNCTION_LIST *functionList);
 
-typedef struct NvencInputSurface
+typedef struct NvencSurface
 {
     NV_ENC_INPUT_PTR input_surface;
     int width;
@@ -76,23 +76,16 @@ typedef struct NvencInputSurface
     int lockCount;
 
     NV_ENC_BUFFER_FORMAT format;
-} NvencInputSurface;
 
-typedef struct NvencOutputSurface
-{
     NV_ENC_OUTPUT_PTR output_surface;
     int size;
-
-    NvencInputSurface* input_surface;
-
-    int busy;
-} NvencOutputSurface;
+} NvencSurface;
 
 typedef struct NvencData
 {
     union {
         int64_t timestamp;
-        NvencOutputSurface *surface;
+        NvencSurface *surface;
     } u;
 } NvencData;
 
@@ -146,8 +139,7 @@ typedef struct NvencContext
     CUcontext cu_context;
 
     int max_surface_count;
-    NvencInputSurface *input_surfaces;
-    NvencOutputSurface *output_surfaces;
+    NvencSurface *surfaces;
 
     NvencDataList output_surface_queue;
     NvencDataList output_surface_ready_queue;
@@ -217,6 +209,64 @@ static const NvencValuePair nvenc_hevc_level_pairs[] = {
     { NULL }
 };
 
+static const struct {
+    NVENCSTATUS nverr;
+    int         averr;
+    const char *desc;
+} nvenc_errors[] = {
+    { NV_ENC_SUCCESS,                      0,                "success"                  },
+    { NV_ENC_ERR_NO_ENCODE_DEVICE,         AVERROR(ENOENT),  "no encode device"         },
+    { NV_ENC_ERR_UNSUPPORTED_DEVICE,       AVERROR(ENOSYS),  "unsupported device"       },
+    { NV_ENC_ERR_INVALID_ENCODERDEVICE,    AVERROR(EINVAL),  "invalid encoder device"   },
+    { NV_ENC_ERR_INVALID_DEVICE,           AVERROR(EINVAL),  "invalid device"           },
+    { NV_ENC_ERR_DEVICE_NOT_EXIST,         AVERROR(EIO),     "device does not exist"    },
+    { NV_ENC_ERR_INVALID_PTR,              AVERROR(EFAULT),  "invalid ptr"              },
+    { NV_ENC_ERR_INVALID_EVENT,            AVERROR(EINVAL),  "invalid event"            },
+    { NV_ENC_ERR_INVALID_PARAM,            AVERROR(EINVAL),  "invalid param"            },
+    { NV_ENC_ERR_INVALID_CALL,             AVERROR(EINVAL),  "invalid call"             },
+    { NV_ENC_ERR_OUT_OF_MEMORY,            AVERROR(ENOMEM),  "out of memory"            },
+    { NV_ENC_ERR_ENCODER_NOT_INITIALIZED,  AVERROR(EINVAL),  "encoder not initialized"  },
+    { NV_ENC_ERR_UNSUPPORTED_PARAM,        AVERROR(ENOSYS),  "unsupported param"        },
+    { NV_ENC_ERR_LOCK_BUSY,                AVERROR(EAGAIN),  "lock busy"                },
+    { NV_ENC_ERR_NOT_ENOUGH_BUFFER,        AVERROR(ENOBUFS), "not enough buffer"        },
+    { NV_ENC_ERR_INVALID_VERSION,          AVERROR(EINVAL),  "invalid version"          },
+    { NV_ENC_ERR_MAP_FAILED,               AVERROR(EIO),     "map failed"               },
+    { NV_ENC_ERR_NEED_MORE_INPUT,          AVERROR(EAGAIN),  "need more input"          },
+    { NV_ENC_ERR_ENCODER_BUSY,             AVERROR(EAGAIN),  "encoder busy"             },
+    { NV_ENC_ERR_EVENT_NOT_REGISTERD,      AVERROR(EBADF),   "event not registered"     },
+    { NV_ENC_ERR_GENERIC,                  AVERROR_UNKNOWN,  "generic error"            },
+    { NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY,  AVERROR(EINVAL),  "incompatible client key"  },
+    { NV_ENC_ERR_UNIMPLEMENTED,            AVERROR(ENOSYS),  "unimplemented"            },
+    { NV_ENC_ERR_RESOURCE_REGISTER_FAILED, AVERROR(EIO),     "resource register failed" },
+    { NV_ENC_ERR_RESOURCE_NOT_REGISTERED,  AVERROR(EBADF),   "resource not registered"  },
+    { NV_ENC_ERR_RESOURCE_NOT_MAPPED,      AVERROR(EBADF),   "resource not mapped"      },
+};
+
+static int nvenc_map_error(NVENCSTATUS err, const char **desc)
+{
+    int i;
+    for (i = 0; i < FF_ARRAY_ELEMS(nvenc_errors); i++) {
+        if (nvenc_errors[i].nverr == err) {
+            if (desc)
+                *desc = nvenc_errors[i].desc;
+            return nvenc_errors[i].averr;
+        }
+    }
+    if (desc)
+        *desc = "unknown error";
+    return AVERROR_UNKNOWN;
+}
+
+static int nvenc_print_error(void *log_ctx, NVENCSTATUS err,
+                                     const char *error_string)
+{
+    const char *desc;
+    int ret;
+    ret = nvenc_map_error(err, &desc);
+    av_log(log_ctx, AV_LOG_ERROR, "%s: %s (%d)\n", error_string, desc, err);
+    return ret;
+}
+
 static int input_string_to_uint32(AVCodecContext *avctx, const NvencValuePair *pair, const char *input, uint32_t *output)
 {
     for (; pair->str; ++pair) {
@@ -294,7 +344,7 @@ static int data_queue_enqueue(NvencDataList* queue, NvencData *data)
     return 0;
 }
 
-static int out_surf_queue_enqueue(NvencDataList* queue, NvencOutputSurface* surface)
+static int out_surf_queue_enqueue(NvencDataList* queue, NvencSurface* surface)
 {
     NvencData data;
     data.u.surface = surface;
@@ -302,7 +352,7 @@ static int out_surf_queue_enqueue(NvencDataList* queue, NvencOutputSurface* surf
     return data_queue_enqueue(queue, &data);
 }
 
-static NvencOutputSurface* out_surf_queue_dequeue(NvencDataList* queue)
+static NvencSurface* out_surf_queue_dequeue(NvencDataList* queue)
 {
     NvencData* res = data_queue_dequeue(queue);
 
@@ -499,7 +549,7 @@ static av_cold int nvenc_dyload_nvenc(AVCodecContext *avctx)
     nvstatus = nvEncodeAPICreateInstance(&dl_fn->nvenc_funcs);
 
     if (nvstatus != NV_ENC_SUCCESS) {
-        av_log(avctx, AV_LOG_FATAL, "Failed to create nvenc instance\n");
+        nvenc_print_error(avctx, nvstatus, "Failed to create nvenc instance");
         goto error;
     }
 
@@ -589,8 +639,7 @@ static av_cold int nvenc_open_session(AVCodecContext *avctx)
     nv_status = p_nvenc->nvEncOpenEncodeSessionEx(&encode_session_params, &ctx->nvencoder);
     if (nv_status != NV_ENC_SUCCESS) {
         ctx->nvencoder = NULL;
-        av_log(avctx, AV_LOG_FATAL, "OpenEncodeSessionEx failed: 0x%x\n", (int)nv_status);
-        return AVERROR_EXTERNAL;
+        return nvenc_print_error(avctx, nv_status, "OpenEncodeSessionEx failed");
     }
 
     return 0;
@@ -963,8 +1012,7 @@ static av_cold int nvenc_setup_encoder(AVCodecContext *avctx)
 
     nv_status = p_nvenc->nvEncGetEncodePresetConfig(ctx->nvencoder, codec, encoder_preset, &preset_config);
     if (nv_status != NV_ENC_SUCCESS) {
-        av_log(avctx, AV_LOG_FATAL, "GetEncodePresetConfig failed: 0x%x\n", (int)nv_status);
-        return AVERROR_EXTERNAL;
+        return nvenc_print_error(avctx, nv_status, "GetEncodePresetConfig failed");
     }
 
     ctx->init_encode_params.encodeGUID = codec;
@@ -1075,8 +1123,7 @@ static av_cold int nvenc_setup_encoder(AVCodecContext *avctx)
 
     nv_status = p_nvenc->nvEncInitializeEncoder(ctx->nvencoder, &ctx->init_encode_params);
     if (nv_status != NV_ENC_SUCCESS) {
-        av_log(avctx, AV_LOG_FATAL, "InitializeEncoder failed: 0x%x\n", (int)nv_status);
-        return AVERROR_EXTERNAL;
+        return nvenc_print_error(avctx, nv_status, "InitializeEncoder failed");
     }
 
     if (ctx->encode_config.frameIntervalP > 1)
@@ -1133,15 +1180,14 @@ static av_cold int nvenc_alloc_surface(AVCodecContext *avctx, int idx)
 
     nv_status = p_nvenc->nvEncCreateInputBuffer(ctx->nvencoder, &allocSurf);
     if (nv_status != NV_ENC_SUCCESS) {
-        av_log(avctx, AV_LOG_FATAL, "CreateInputBuffer failed\n");
-        return AVERROR_EXTERNAL;
+        return nvenc_print_error(avctx, nv_status, "CreateInputBuffer failed");
     }
 
-    ctx->input_surfaces[idx].lockCount = 0;
-    ctx->input_surfaces[idx].input_surface = allocSurf.inputBuffer;
-    ctx->input_surfaces[idx].format = allocSurf.bufferFmt;
-    ctx->input_surfaces[idx].width = allocSurf.width;
-    ctx->input_surfaces[idx].height = allocSurf.height;
+    ctx->surfaces[idx].lockCount = 0;
+    ctx->surfaces[idx].input_surface = allocSurf.inputBuffer;
+    ctx->surfaces[idx].format = allocSurf.bufferFmt;
+    ctx->surfaces[idx].width = allocSurf.width;
+    ctx->surfaces[idx].height = allocSurf.height;
 
     /* 1MB is large enough to hold most output frames. NVENC increases this automaticaly if it's not enough. */
     allocOut.size = 1024 * 1024;
@@ -1150,14 +1196,13 @@ static av_cold int nvenc_alloc_surface(AVCodecContext *avctx, int idx)
 
     nv_status = p_nvenc->nvEncCreateBitstreamBuffer(ctx->nvencoder, &allocOut);
     if (nv_status != NV_ENC_SUCCESS) {
-        av_log(avctx, AV_LOG_FATAL, "CreateBitstreamBuffer failed\n");
-        p_nvenc->nvEncDestroyInputBuffer(ctx->nvencoder, ctx->input_surfaces[idx].input_surface);
-        return AVERROR_EXTERNAL;
+        int err = nvenc_print_error(avctx, nv_status, "CreateBitstreamBuffer failed");
+        p_nvenc->nvEncDestroyInputBuffer(ctx->nvencoder, ctx->surfaces[idx].input_surface);
+        return err;
     }
 
-    ctx->output_surfaces[idx].output_surface = allocOut.bitstreamBuffer;
-    ctx->output_surfaces[idx].size = allocOut.size;
-    ctx->output_surfaces[idx].busy = 0;
+    ctx->surfaces[idx].output_surface = allocOut.bitstreamBuffer;
+    ctx->surfaces[idx].size = allocOut.size;
 
     return 0;
 }
@@ -1167,15 +1212,9 @@ static av_cold int nvenc_setup_surfaces(AVCodecContext *avctx, int* surfaceCount
     int res;
     NvencContext *ctx = avctx->priv_data;
 
-    ctx->input_surfaces = av_malloc(ctx->max_surface_count * sizeof(*ctx->input_surfaces));
+    ctx->surfaces = av_malloc(ctx->max_surface_count * sizeof(*ctx->surfaces));
 
-    if (!ctx->input_surfaces) {
-        return AVERROR(ENOMEM);
-    }
-
-    ctx->output_surfaces = av_malloc(ctx->max_surface_count * sizeof(*ctx->output_surfaces));
-
-    if (!ctx->output_surfaces) {
+    if (!ctx->surfaces) {
         return AVERROR(ENOMEM);
     }
 
@@ -1206,8 +1245,7 @@ static av_cold int nvenc_setup_extradata(AVCodecContext *avctx)
 
     nv_status = p_nvenc->nvEncGetSequenceParams(ctx->nvencoder, &payload);
     if (nv_status != NV_ENC_SUCCESS) {
-        av_log(avctx, AV_LOG_FATAL, "GetSequenceParams failed\n");
-        return AVERROR_EXTERNAL;
+        return nvenc_print_error(avctx, nv_status, "GetSequenceParams failed");
     }
 
     avctx->extradata_size = outSize;
@@ -1262,9 +1300,8 @@ static av_cold int nvenc_encode_init(AVCodecContext *avctx)
 error:
 
     for (i = 0; i < surfaceCount; ++i) {
-        p_nvenc->nvEncDestroyInputBuffer(ctx->nvencoder, ctx->input_surfaces[i].input_surface);
-        if (ctx->output_surfaces[i].output_surface)
-            p_nvenc->nvEncDestroyBitstreamBuffer(ctx->nvencoder, ctx->output_surfaces[i].output_surface);
+        p_nvenc->nvEncDestroyInputBuffer(ctx->nvencoder, ctx->surfaces[i].input_surface);
+        p_nvenc->nvEncDestroyBitstreamBuffer(ctx->nvencoder, ctx->surfaces[i].output_surface);
     }
 
     if (ctx->nvencoder)
@@ -1293,8 +1330,8 @@ static av_cold int nvenc_encode_close(AVCodecContext *avctx)
     av_freep(&ctx->output_surface_queue.data);
 
     for (i = 0; i < ctx->max_surface_count; ++i) {
-        p_nvenc->nvEncDestroyInputBuffer(ctx->nvencoder, ctx->input_surfaces[i].input_surface);
-        p_nvenc->nvEncDestroyBitstreamBuffer(ctx->nvencoder, ctx->output_surfaces[i].output_surface);
+        p_nvenc->nvEncDestroyInputBuffer(ctx->nvencoder, ctx->surfaces[i].input_surface);
+        p_nvenc->nvEncDestroyBitstreamBuffer(ctx->nvencoder, ctx->surfaces[i].output_surface);
     }
     ctx->max_surface_count = 0;
 
@@ -1309,21 +1346,21 @@ static av_cold int nvenc_encode_close(AVCodecContext *avctx)
     return 0;
 }
 
-static NvencInputSurface *get_free_frame(NvencContext *ctx)
+static NvencSurface *get_free_frame(NvencContext *ctx)
 {
     int i;
 
     for (i = 0; i < ctx->max_surface_count; ++i) {
-        if (!ctx->input_surfaces[i].lockCount) {
-            ctx->input_surfaces[i].lockCount = 1;
-            return &ctx->input_surfaces[i];
+        if (!ctx->surfaces[i].lockCount) {
+            ctx->surfaces[i].lockCount = 1;
+            return &ctx->surfaces[i];
         }
     }
 
     return NULL;
 }
 
-static int nvenc_copy_frame(AVCodecContext *avctx, NvencInputSurface *inSurf,
+static int nvenc_copy_frame(AVCodecContext *avctx, NvencSurface *inSurf,
             NV_ENC_LOCK_INPUT_BUFFER *lockBufferParams, const AVFrame *frame)
 {
     uint8_t *buf = lockBufferParams->bufferDataPtr;
@@ -1380,7 +1417,7 @@ static int nvenc_copy_frame(AVCodecContext *avctx, NvencInputSurface *inSurf,
 }
 
 static int nvenc_upload_frame(AVCodecContext *avctx, const AVFrame *frame,
-                                      NvencInputSurface *nvenc_frame)
+                                      NvencSurface *nvenc_frame)
 {
     NvencContext *ctx = avctx->priv_data;
     NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
@@ -1395,16 +1432,14 @@ static int nvenc_upload_frame(AVCodecContext *avctx, const AVFrame *frame,
 
     nv_status = p_nvenc->nvEncLockInputBuffer(ctx->nvencoder, &lockBufferParams);
     if (nv_status != NV_ENC_SUCCESS) {
-        av_log(avctx, AV_LOG_ERROR, "Failed locking nvenc input buffer\n");
-        return AVERROR_EXTERNAL;
+        return nvenc_print_error(avctx, nv_status, "Failed locking nvenc input buffer");
     }
 
     res = nvenc_copy_frame(avctx, nvenc_frame, &lockBufferParams, frame);
 
     nv_status = p_nvenc->nvEncUnlockInputBuffer(ctx->nvencoder, nvenc_frame->input_surface);
     if (nv_status != NV_ENC_SUCCESS) {
-        av_log(avctx, AV_LOG_FATAL, "Failed unlocking input buffer!\n");
-        return AVERROR_EXTERNAL;
+        return nvenc_print_error(avctx, nv_status, "Failed unlocking input buffer!");
     }
 
     return res;
@@ -1427,7 +1462,7 @@ static void nvenc_codec_specific_pic_params(AVCodecContext *avctx,
     }
 }
 
-static int process_output_surface(AVCodecContext *avctx, AVPacket *pkt, NvencOutputSurface *tmpoutsurf)
+static int process_output_surface(AVCodecContext *avctx, AVPacket *pkt, NvencSurface *tmpoutsurf)
 {
     NvencContext *ctx = avctx->priv_data;
     NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
@@ -1466,8 +1501,7 @@ static int process_output_surface(AVCodecContext *avctx, AVPacket *pkt, NvencOut
 
     nv_status = p_nvenc->nvEncLockBitstream(ctx->nvencoder, &lock_params);
     if (nv_status != NV_ENC_SUCCESS) {
-        av_log(avctx, AV_LOG_ERROR, "Failed locking bitstream buffer\n");
-        res = AVERROR_EXTERNAL;
+        res = nvenc_print_error(avctx, nv_status, "Failed locking bitstream buffer");
         goto error;
     }
 
@@ -1480,7 +1514,7 @@ static int process_output_surface(AVCodecContext *avctx, AVPacket *pkt, NvencOut
 
     nv_status = p_nvenc->nvEncUnlockBitstream(ctx->nvencoder, tmpoutsurf->output_surface);
     if (nv_status != NV_ENC_SUCCESS)
-        av_log(avctx, AV_LOG_ERROR, "Failed unlocking bitstream buffer, expect the gates of mordor to open\n");
+        nvenc_print_error(avctx, nv_status, "Failed unlocking bitstream buffer, expect the gates of mordor to open");
 
     switch (lock_params.pictureType) {
     case NV_ENC_PIC_TYPE_IDR:
@@ -1544,8 +1578,8 @@ static int nvenc_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
     const AVFrame *frame, int *got_packet)
 {
     NVENCSTATUS nv_status;
-    NvencOutputSurface *tmpoutsurf;
-    int res, i = 0;
+    NvencSurface *tmpoutsurf, *inSurf;
+    int res;
 
     NvencContext *ctx = avctx->priv_data;
     NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
@@ -1555,8 +1589,6 @@ static int nvenc_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
     pic_params.version = NV_ENC_PIC_PARAMS_VER;
 
     if (frame) {
-        NvencInputSurface *inSurf;
-
         inSurf = get_free_frame(ctx);
         av_assert0(inSurf);
 
@@ -1566,23 +1598,11 @@ static int nvenc_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
             return res;
         }
 
-        for (i = 0; i < ctx->max_surface_count; ++i)
-            if (!ctx->output_surfaces[i].busy)
-                break;
-
-        if (i == ctx->max_surface_count) {
-            inSurf->lockCount = 0;
-            av_log(avctx, AV_LOG_FATAL, "No free output surface found!\n");
-            return AVERROR_EXTERNAL;
-        }
-
-        ctx->output_surfaces[i].input_surface = inSurf;
-
         pic_params.inputBuffer = inSurf->input_surface;
         pic_params.bufferFmt = inSurf->format;
         pic_params.inputWidth = avctx->width;
         pic_params.inputHeight = avctx->height;
-        pic_params.outputBitstream = ctx->output_surfaces[i].output_surface;
+        pic_params.outputBitstream = inSurf->output_surface;
         pic_params.completionEvent = 0;
 
         if (avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT) {
@@ -1614,17 +1634,14 @@ static int nvenc_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
     nv_status = p_nvenc->nvEncEncodePicture(ctx->nvencoder, &pic_params);
 
     if (frame && nv_status == NV_ENC_ERR_NEED_MORE_INPUT) {
-        res = out_surf_queue_enqueue(&ctx->output_surface_queue, &ctx->output_surfaces[i]);
+        res = out_surf_queue_enqueue(&ctx->output_surface_queue, inSurf);
 
         if (res)
             return res;
-
-        ctx->output_surfaces[i].busy = 1;
     }
 
     if (nv_status != NV_ENC_SUCCESS && nv_status != NV_ENC_ERR_NEED_MORE_INPUT) {
-        av_log(avctx, AV_LOG_ERROR, "EncodePicture failed!\n");
-        return AVERROR_EXTERNAL;
+        return nvenc_print_error(avctx, nv_status, "EncodePicture failed!");
     }
 
     if (nv_status != NV_ENC_ERR_NEED_MORE_INPUT) {
@@ -1637,12 +1654,10 @@ static int nvenc_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
         }
 
         if (frame) {
-            res = out_surf_queue_enqueue(&ctx->output_surface_ready_queue, &ctx->output_surfaces[i]);
+            res = out_surf_queue_enqueue(&ctx->output_surface_ready_queue, inSurf);
 
             if (res)
                 return res;
-
-            ctx->output_surfaces[i].busy = 1;
         }
     }
 
@@ -1654,9 +1669,8 @@ static int nvenc_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
         if (res)
             return res;
 
-        tmpoutsurf->busy = 0;
-        av_assert0(tmpoutsurf->input_surface->lockCount);
-        tmpoutsurf->input_surface->lockCount--;
+        av_assert0(tmpoutsurf->lockCount);
+        tmpoutsurf->lockCount--;
 
         *got_packet = 1;
     } else {