2014-11-30 01:04:37 +02:00
|
|
|
/*
|
|
|
|
* H.264 hardware encoding using nvidia nvenc
|
|
|
|
* Copyright (c) 2014 Timo Rothenpieler <timo@rothenpieler.org>
|
|
|
|
*
|
|
|
|
* This file is part of FFmpeg.
|
|
|
|
*
|
|
|
|
* FFmpeg is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* FFmpeg is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with FFmpeg; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
2016-05-25 15:04:28 +02:00
|
|
|
#include "config.h"
|
|
|
|
|
2014-12-25 15:55:31 +02:00
|
|
|
#if defined(_WIN32)
|
2014-11-30 01:04:37 +02:00
|
|
|
#include <windows.h>
|
|
|
|
#else
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "libavutil/imgutils.h"
|
|
|
|
#include "libavutil/avassert.h"
|
|
|
|
#include "libavutil/mem.h"
|
2016-05-21 00:08:06 +02:00
|
|
|
#include "libavutil/hwcontext.h"
|
2014-11-30 01:04:37 +02:00
|
|
|
#include "internal.h"
|
|
|
|
#include "thread.h"
|
|
|
|
|
2016-05-25 15:04:28 +02:00
|
|
|
#include "nvenc.h"
|
2016-05-21 00:08:06 +02:00
|
|
|
|
|
|
|
#if CONFIG_CUDA
|
|
|
|
#include "libavutil/hwcontext_cuda.h"
|
|
|
|
#endif
|
|
|
|
|
2014-12-25 15:55:31 +02:00
|
|
|
#if defined(_WIN32)
|
2014-11-30 01:04:37 +02:00
|
|
|
#define LOAD_FUNC(l, s) GetProcAddress(l, s)
|
|
|
|
#define DL_CLOSE_FUNC(l) FreeLibrary(l)
|
|
|
|
#else
|
|
|
|
#define LOAD_FUNC(l, s) dlsym(l, s)
|
|
|
|
#define DL_CLOSE_FUNC(l) dlclose(l)
|
|
|
|
#endif
|
|
|
|
|
2016-05-25 15:04:28 +02:00
|
|
|
const enum AVPixelFormat ff_nvenc_pix_fmts[] = {
|
|
|
|
AV_PIX_FMT_YUV420P,
|
|
|
|
AV_PIX_FMT_NV12,
|
|
|
|
AV_PIX_FMT_YUV444P,
|
|
|
|
#if CONFIG_CUDA
|
|
|
|
AV_PIX_FMT_CUDA,
|
|
|
|
#endif
|
|
|
|
AV_PIX_FMT_NONE
|
|
|
|
};
|
2014-11-30 01:04:37 +02:00
|
|
|
|
|
|
|
typedef struct NvencData
|
|
|
|
{
|
|
|
|
union {
|
|
|
|
int64_t timestamp;
|
2016-05-20 17:37:00 +02:00
|
|
|
NvencSurface *surface;
|
2015-07-14 22:58:54 +02:00
|
|
|
} u;
|
2014-11-30 01:04:37 +02:00
|
|
|
} NvencData;
|
|
|
|
|
2015-04-04 13:34:14 +02:00
|
|
|
typedef struct NvencValuePair
|
|
|
|
{
|
|
|
|
const char *str;
|
|
|
|
uint32_t num;
|
|
|
|
} NvencValuePair;
|
|
|
|
|
|
|
|
static const NvencValuePair nvenc_h264_level_pairs[] = {
|
|
|
|
{ "auto", NV_ENC_LEVEL_AUTOSELECT },
|
|
|
|
{ "1" , NV_ENC_LEVEL_H264_1 },
|
|
|
|
{ "1.0" , NV_ENC_LEVEL_H264_1 },
|
|
|
|
{ "1b" , NV_ENC_LEVEL_H264_1b },
|
|
|
|
{ "1.0b", NV_ENC_LEVEL_H264_1b },
|
|
|
|
{ "1.1" , NV_ENC_LEVEL_H264_11 },
|
|
|
|
{ "1.2" , NV_ENC_LEVEL_H264_12 },
|
|
|
|
{ "1.3" , NV_ENC_LEVEL_H264_13 },
|
|
|
|
{ "2" , NV_ENC_LEVEL_H264_2 },
|
|
|
|
{ "2.0" , NV_ENC_LEVEL_H264_2 },
|
|
|
|
{ "2.1" , NV_ENC_LEVEL_H264_21 },
|
|
|
|
{ "2.2" , NV_ENC_LEVEL_H264_22 },
|
|
|
|
{ "3" , NV_ENC_LEVEL_H264_3 },
|
|
|
|
{ "3.0" , NV_ENC_LEVEL_H264_3 },
|
|
|
|
{ "3.1" , NV_ENC_LEVEL_H264_31 },
|
|
|
|
{ "3.2" , NV_ENC_LEVEL_H264_32 },
|
|
|
|
{ "4" , NV_ENC_LEVEL_H264_4 },
|
|
|
|
{ "4.0" , NV_ENC_LEVEL_H264_4 },
|
|
|
|
{ "4.1" , NV_ENC_LEVEL_H264_41 },
|
|
|
|
{ "4.2" , NV_ENC_LEVEL_H264_42 },
|
|
|
|
{ "5" , NV_ENC_LEVEL_H264_5 },
|
|
|
|
{ "5.0" , NV_ENC_LEVEL_H264_5 },
|
|
|
|
{ "5.1" , NV_ENC_LEVEL_H264_51 },
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
2015-06-06 20:00:45 +02:00
|
|
|
static const NvencValuePair nvenc_hevc_level_pairs[] = {
|
2015-04-04 13:34:14 +02:00
|
|
|
{ "auto", NV_ENC_LEVEL_AUTOSELECT },
|
|
|
|
{ "1" , NV_ENC_LEVEL_HEVC_1 },
|
|
|
|
{ "1.0" , NV_ENC_LEVEL_HEVC_1 },
|
|
|
|
{ "2" , NV_ENC_LEVEL_HEVC_2 },
|
|
|
|
{ "2.0" , NV_ENC_LEVEL_HEVC_2 },
|
|
|
|
{ "2.1" , NV_ENC_LEVEL_HEVC_21 },
|
|
|
|
{ "3" , NV_ENC_LEVEL_HEVC_3 },
|
|
|
|
{ "3.0" , NV_ENC_LEVEL_HEVC_3 },
|
|
|
|
{ "3.1" , NV_ENC_LEVEL_HEVC_31 },
|
|
|
|
{ "4" , NV_ENC_LEVEL_HEVC_4 },
|
|
|
|
{ "4.0" , NV_ENC_LEVEL_HEVC_4 },
|
|
|
|
{ "4.1" , NV_ENC_LEVEL_HEVC_41 },
|
|
|
|
{ "5" , NV_ENC_LEVEL_HEVC_5 },
|
|
|
|
{ "5.0" , NV_ENC_LEVEL_HEVC_5 },
|
|
|
|
{ "5.1" , NV_ENC_LEVEL_HEVC_51 },
|
|
|
|
{ "5.2" , NV_ENC_LEVEL_HEVC_52 },
|
|
|
|
{ "6" , NV_ENC_LEVEL_HEVC_6 },
|
|
|
|
{ "6.0" , NV_ENC_LEVEL_HEVC_6 },
|
|
|
|
{ "6.1" , NV_ENC_LEVEL_HEVC_61 },
|
|
|
|
{ "6.2" , NV_ENC_LEVEL_HEVC_62 },
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
2016-05-20 17:37:00 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-04-04 13:34:14 +02:00
|
|
|
static int input_string_to_uint32(AVCodecContext *avctx, const NvencValuePair *pair, const char *input, uint32_t *output)
|
|
|
|
{
|
|
|
|
for (; pair->str; ++pair) {
|
|
|
|
if (!strcmp(input, pair->str)) {
|
|
|
|
*output = pair->num;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
}
|
|
|
|
|
2016-05-20 18:13:20 +02:00
|
|
|
static void timestamp_queue_enqueue(AVFifoBuffer* queue, int64_t timestamp)
|
2014-11-30 01:04:37 +02:00
|
|
|
{
|
2016-05-20 18:13:20 +02:00
|
|
|
av_fifo_generic_write(queue, ×tamp, sizeof(timestamp), NULL);
|
2014-11-30 01:04:37 +02:00
|
|
|
}
|
|
|
|
|
2016-05-20 18:13:20 +02:00
|
|
|
static int64_t timestamp_queue_dequeue(AVFifoBuffer* queue)
|
2014-11-30 01:04:37 +02:00
|
|
|
{
|
2016-05-20 18:13:20 +02:00
|
|
|
int64_t timestamp = AV_NOPTS_VALUE;
|
|
|
|
if (av_fifo_size(queue) > 0)
|
|
|
|
av_fifo_generic_read(queue, ×tamp, sizeof(timestamp), NULL);
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 18:13:20 +02:00
|
|
|
return timestamp;
|
2014-11-30 01:04:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#define CHECK_LOAD_FUNC(t, f, s) \
|
|
|
|
do { \
|
|
|
|
(f) = (t)LOAD_FUNC(dl_fn->cuda_lib, s); \
|
|
|
|
if (!(f)) { \
|
|
|
|
av_log(avctx, AV_LOG_FATAL, "Failed loading %s from CUDA library\n", s); \
|
|
|
|
goto error; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
static av_cold int nvenc_dyload_cuda(AVCodecContext *avctx)
|
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
|
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
#if CONFIG_CUDA
|
|
|
|
dl_fn->cu_init = cuInit;
|
|
|
|
dl_fn->cu_device_get_count = cuDeviceGetCount;
|
|
|
|
dl_fn->cu_device_get = cuDeviceGet;
|
|
|
|
dl_fn->cu_device_get_name = cuDeviceGetName;
|
|
|
|
dl_fn->cu_device_compute_capability = cuDeviceComputeCapability;
|
|
|
|
dl_fn->cu_ctx_create = cuCtxCreate_v2;
|
|
|
|
dl_fn->cu_ctx_pop_current = cuCtxPopCurrent_v2;
|
|
|
|
dl_fn->cu_ctx_destroy = cuCtxDestroy_v2;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
#else
|
2014-11-30 01:04:37 +02:00
|
|
|
if (dl_fn->cuda_lib)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
dl_fn->cuda_lib = LoadLibrary(TEXT("nvcuda.dll"));
|
|
|
|
#else
|
|
|
|
dl_fn->cuda_lib = dlopen("libcuda.so", RTLD_LAZY);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!dl_fn->cuda_lib) {
|
|
|
|
av_log(avctx, AV_LOG_FATAL, "Failed loading CUDA library\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECK_LOAD_FUNC(PCUINIT, dl_fn->cu_init, "cuInit");
|
|
|
|
CHECK_LOAD_FUNC(PCUDEVICEGETCOUNT, dl_fn->cu_device_get_count, "cuDeviceGetCount");
|
|
|
|
CHECK_LOAD_FUNC(PCUDEVICEGET, dl_fn->cu_device_get, "cuDeviceGet");
|
|
|
|
CHECK_LOAD_FUNC(PCUDEVICEGETNAME, dl_fn->cu_device_get_name, "cuDeviceGetName");
|
|
|
|
CHECK_LOAD_FUNC(PCUDEVICECOMPUTECAPABILITY, dl_fn->cu_device_compute_capability, "cuDeviceComputeCapability");
|
|
|
|
CHECK_LOAD_FUNC(PCUCTXCREATE, dl_fn->cu_ctx_create, "cuCtxCreate_v2");
|
|
|
|
CHECK_LOAD_FUNC(PCUCTXPOPCURRENT, dl_fn->cu_ctx_pop_current, "cuCtxPopCurrent_v2");
|
|
|
|
CHECK_LOAD_FUNC(PCUCTXDESTROY, dl_fn->cu_ctx_destroy, "cuCtxDestroy_v2");
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
|
|
|
if (dl_fn->cuda_lib)
|
|
|
|
DL_CLOSE_FUNC(dl_fn->cuda_lib);
|
|
|
|
|
|
|
|
dl_fn->cuda_lib = NULL;
|
|
|
|
|
|
|
|
return 0;
|
2016-05-21 00:08:06 +02:00
|
|
|
#endif
|
2014-11-30 01:04:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static av_cold int check_cuda_errors(AVCodecContext *avctx, CUresult err, const char *func)
|
|
|
|
{
|
|
|
|
if (err != CUDA_SUCCESS) {
|
|
|
|
av_log(avctx, AV_LOG_FATAL, ">> %s - failed with error code 0x%x\n", func, err);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#define check_cuda_errors(f) if (!check_cuda_errors(avctx, f, #f)) goto error
|
|
|
|
|
|
|
|
static av_cold int nvenc_check_cuda(AVCodecContext *avctx)
|
|
|
|
{
|
|
|
|
int device_count = 0;
|
|
|
|
CUdevice cu_device = 0;
|
|
|
|
char gpu_name[128];
|
|
|
|
int smminor = 0, smmajor = 0;
|
2015-03-24 06:34:59 +02:00
|
|
|
int i, smver, target_smver;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
|
|
|
|
|
2015-03-24 06:34:59 +02:00
|
|
|
switch (avctx->codec->id) {
|
|
|
|
case AV_CODEC_ID_H264:
|
2016-05-21 00:08:06 +02:00
|
|
|
target_smver = ctx->data_pix_fmt == AV_PIX_FMT_YUV444P ? 0x52 : 0x30;
|
2015-03-24 06:34:59 +02:00
|
|
|
break;
|
|
|
|
case AV_CODEC_ID_H265:
|
|
|
|
target_smver = 0x52;
|
|
|
|
break;
|
|
|
|
default:
|
2015-09-11 11:07:10 +02:00
|
|
|
av_log(avctx, AV_LOG_FATAL, "Unknown codec name\n");
|
2015-03-24 06:34:59 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
if (ctx->preset >= PRESET_LOSSLESS_DEFAULT)
|
2016-05-25 12:15:03 +02:00
|
|
|
target_smver = 0x52;
|
|
|
|
|
2014-11-30 01:04:37 +02:00
|
|
|
if (!nvenc_dyload_cuda(avctx))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (dl_fn->nvenc_device_count > 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
check_cuda_errors(dl_fn->cu_init(0));
|
|
|
|
|
|
|
|
check_cuda_errors(dl_fn->cu_device_get_count(&device_count));
|
|
|
|
|
|
|
|
if (!device_count) {
|
|
|
|
av_log(avctx, AV_LOG_FATAL, "No CUDA capable devices found\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, "%d CUDA capable devices found\n", device_count);
|
|
|
|
|
|
|
|
dl_fn->nvenc_device_count = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < device_count; ++i) {
|
|
|
|
check_cuda_errors(dl_fn->cu_device_get(&cu_device, i));
|
|
|
|
check_cuda_errors(dl_fn->cu_device_get_name(gpu_name, sizeof(gpu_name), cu_device));
|
|
|
|
check_cuda_errors(dl_fn->cu_device_compute_capability(&smmajor, &smminor, cu_device));
|
|
|
|
|
|
|
|
smver = (smmajor << 4) | smminor;
|
|
|
|
|
2015-03-24 06:34:59 +02:00
|
|
|
av_log(avctx, AV_LOG_VERBOSE, "[ GPU #%d - < %s > has Compute SM %d.%d, NVENC %s ]\n", i, gpu_name, smmajor, smminor, (smver >= target_smver) ? "Available" : "Not Available");
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2015-03-24 06:34:59 +02:00
|
|
|
if (smver >= target_smver)
|
2014-11-30 01:04:37 +02:00
|
|
|
dl_fn->nvenc_devices[dl_fn->nvenc_device_count++] = cu_device;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dl_fn->nvenc_device_count) {
|
|
|
|
av_log(avctx, AV_LOG_FATAL, "No NVENC capable devices found\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
|
|
|
dl_fn->nvenc_device_count = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static av_cold int nvenc_dyload_nvenc(AVCodecContext *avctx)
|
|
|
|
{
|
|
|
|
PNVENCODEAPICREATEINSTANCE nvEncodeAPICreateInstance = 0;
|
|
|
|
NVENCSTATUS nvstatus;
|
|
|
|
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
|
|
|
|
|
|
|
|
if (!nvenc_check_cuda(avctx))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (dl_fn->nvenc_lib)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
if (sizeof(void*) == 8) {
|
|
|
|
dl_fn->nvenc_lib = LoadLibrary(TEXT("nvEncodeAPI64.dll"));
|
|
|
|
} else {
|
|
|
|
dl_fn->nvenc_lib = LoadLibrary(TEXT("nvEncodeAPI.dll"));
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
dl_fn->nvenc_lib = dlopen("libnvidia-encode.so.1", RTLD_LAZY);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!dl_fn->nvenc_lib) {
|
|
|
|
av_log(avctx, AV_LOG_FATAL, "Failed loading the nvenc library\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
nvEncodeAPICreateInstance = (PNVENCODEAPICREATEINSTANCE)LOAD_FUNC(dl_fn->nvenc_lib, "NvEncodeAPICreateInstance");
|
|
|
|
|
|
|
|
if (!nvEncodeAPICreateInstance) {
|
|
|
|
av_log(avctx, AV_LOG_FATAL, "Failed to load nvenc entrypoint\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
dl_fn->nvenc_funcs.version = NV_ENCODE_API_FUNCTION_LIST_VER;
|
|
|
|
|
|
|
|
nvstatus = nvEncodeAPICreateInstance(&dl_fn->nvenc_funcs);
|
|
|
|
|
|
|
|
if (nvstatus != NV_ENC_SUCCESS) {
|
2016-05-20 17:37:00 +02:00
|
|
|
nvenc_print_error(avctx, nvstatus, "Failed to create nvenc instance");
|
2014-11-30 01:04:37 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, "Nvenc initialized successfully\n");
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (dl_fn->nvenc_lib)
|
|
|
|
DL_CLOSE_FUNC(dl_fn->nvenc_lib);
|
|
|
|
|
|
|
|
dl_fn->nvenc_lib = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static av_cold void nvenc_unload_nvenc(AVCodecContext *avctx)
|
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
|
|
|
|
|
|
|
|
DL_CLOSE_FUNC(dl_fn->nvenc_lib);
|
|
|
|
dl_fn->nvenc_lib = NULL;
|
|
|
|
|
|
|
|
dl_fn->nvenc_device_count = 0;
|
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
#if !CONFIG_CUDA
|
2014-11-30 01:04:37 +02:00
|
|
|
DL_CLOSE_FUNC(dl_fn->cuda_lib);
|
|
|
|
dl_fn->cuda_lib = NULL;
|
2016-05-21 00:08:06 +02:00
|
|
|
#endif
|
2014-11-30 01:04:37 +02:00
|
|
|
|
|
|
|
dl_fn->cu_init = NULL;
|
|
|
|
dl_fn->cu_device_get_count = NULL;
|
|
|
|
dl_fn->cu_device_get = NULL;
|
|
|
|
dl_fn->cu_device_get_name = NULL;
|
|
|
|
dl_fn->cu_device_compute_capability = NULL;
|
|
|
|
dl_fn->cu_ctx_create = NULL;
|
|
|
|
dl_fn->cu_ctx_pop_current = NULL;
|
|
|
|
dl_fn->cu_ctx_destroy = NULL;
|
|
|
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, "Nvenc unloaded\n");
|
|
|
|
}
|
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
static av_cold int nvenc_setup_device(AVCodecContext *avctx)
|
2014-11-30 01:04:37 +02:00
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
|
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
CUresult cu_res;
|
|
|
|
CUcontext cu_context_curr;
|
2015-01-16 02:02:40 +02:00
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
switch (avctx->codec->id) {
|
|
|
|
case AV_CODEC_ID_H264:
|
|
|
|
ctx->init_encode_params.encodeGUID = NV_ENC_CODEC_H264_GUID;
|
|
|
|
break;
|
|
|
|
case AV_CODEC_ID_HEVC:
|
|
|
|
ctx->init_encode_params.encodeGUID = NV_ENC_CODEC_HEVC_GUID;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return AVERROR_BUG;
|
|
|
|
}
|
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
ctx->data_pix_fmt = avctx->pix_fmt;
|
|
|
|
|
|
|
|
#if CONFIG_CUDA
|
|
|
|
if (avctx->pix_fmt == AV_PIX_FMT_CUDA) {
|
|
|
|
AVHWFramesContext *frames_ctx;
|
|
|
|
AVCUDADeviceContext *device_hwctx;
|
|
|
|
|
|
|
|
if (!avctx->hw_frames_ctx) {
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "hw_frames_ctx must be set when using GPU frames as input\n");
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
|
|
|
|
device_hwctx = frames_ctx->device_ctx->hwctx;
|
|
|
|
ctx->cu_context = device_hwctx->cuda_ctx;
|
|
|
|
ctx->data_pix_fmt = frames_ctx->sw_format;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-11-30 01:04:37 +02:00
|
|
|
if (ctx->gpu >= dl_fn->nvenc_device_count) {
|
|
|
|
av_log(avctx, AV_LOG_FATAL, "Requested GPU %d, but only %d GPUs are available!\n", ctx->gpu, dl_fn->nvenc_device_count);
|
2016-05-20 16:49:24 +02:00
|
|
|
return AVERROR(EINVAL);
|
2014-11-30 01:04:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ctx->cu_context = NULL;
|
2016-05-21 00:08:06 +02:00
|
|
|
cu_res = dl_fn->cu_ctx_create(&ctx->cu_context_internal, 4, dl_fn->nvenc_devices[ctx->gpu]); // CU_CTX_SCHED_BLOCKING_SYNC=4, avoid CPU spins
|
2014-11-30 01:04:37 +02:00
|
|
|
|
|
|
|
if (cu_res != CUDA_SUCCESS) {
|
|
|
|
av_log(avctx, AV_LOG_FATAL, "Failed creating CUDA context for NVENC: 0x%x\n", (int)cu_res);
|
2016-05-20 16:49:24 +02:00
|
|
|
return AVERROR_EXTERNAL;
|
2014-11-30 01:04:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
cu_res = dl_fn->cu_ctx_pop_current(&cu_context_curr);
|
|
|
|
|
|
|
|
if (cu_res != CUDA_SUCCESS) {
|
|
|
|
av_log(avctx, AV_LOG_FATAL, "Failed popping CUDA context: 0x%x\n", (int)cu_res);
|
2016-05-20 16:49:24 +02:00
|
|
|
return AVERROR_EXTERNAL;
|
2014-11-30 01:04:37 +02:00
|
|
|
}
|
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
ctx->cu_context = ctx->cu_context_internal;
|
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static av_cold int nvenc_open_session(AVCodecContext *avctx)
|
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
|
|
|
|
NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
|
|
|
|
|
|
|
|
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS encode_session_params = { 0 };
|
|
|
|
NVENCSTATUS nv_status;
|
|
|
|
|
|
|
|
encode_session_params.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER;
|
|
|
|
encode_session_params.apiVersion = NVENCAPI_VERSION;
|
2014-11-30 01:04:37 +02:00
|
|
|
encode_session_params.device = ctx->cu_context;
|
|
|
|
encode_session_params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
|
|
|
|
|
|
|
|
nv_status = p_nvenc->nvEncOpenEncodeSessionEx(&encode_session_params, &ctx->nvencoder);
|
|
|
|
if (nv_status != NV_ENC_SUCCESS) {
|
|
|
|
ctx->nvencoder = NULL;
|
2016-05-20 17:37:00 +02:00
|
|
|
return nvenc_print_error(avctx, nv_status, "OpenEncodeSessionEx failed");
|
2016-05-20 16:49:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
typedef struct GUIDTuple {
|
|
|
|
const GUID guid;
|
|
|
|
int flags;
|
|
|
|
} GUIDTuple;
|
|
|
|
|
|
|
|
static void nvenc_map_preset(NvencContext *ctx)
|
|
|
|
{
|
|
|
|
GUIDTuple presets[] = {
|
|
|
|
{ NV_ENC_PRESET_DEFAULT_GUID },
|
|
|
|
{ NV_ENC_PRESET_HQ_GUID, NVENC_TWO_PASSES }, /* slow */
|
|
|
|
{ NV_ENC_PRESET_HQ_GUID, NVENC_ONE_PASS }, /* medium */
|
|
|
|
{ NV_ENC_PRESET_HP_GUID, NVENC_ONE_PASS }, /* fast */
|
|
|
|
{ NV_ENC_PRESET_HP_GUID },
|
|
|
|
{ NV_ENC_PRESET_HQ_GUID },
|
|
|
|
{ NV_ENC_PRESET_BD_GUID },
|
|
|
|
{ NV_ENC_PRESET_LOW_LATENCY_DEFAULT_GUID, NVENC_LOWLATENCY },
|
|
|
|
{ NV_ENC_PRESET_LOW_LATENCY_HQ_GUID, NVENC_LOWLATENCY },
|
|
|
|
{ NV_ENC_PRESET_LOW_LATENCY_HP_GUID, NVENC_LOWLATENCY },
|
|
|
|
{ NV_ENC_PRESET_LOSSLESS_DEFAULT_GUID, NVENC_LOSSLESS },
|
|
|
|
{ NV_ENC_PRESET_LOSSLESS_HP_GUID, NVENC_LOSSLESS },
|
|
|
|
};
|
|
|
|
|
|
|
|
GUIDTuple *t = &presets[ctx->preset];
|
|
|
|
|
|
|
|
ctx->init_encode_params.presetGUID = t->guid;
|
|
|
|
ctx->flags = t->flags;
|
|
|
|
}
|
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
static av_cold void set_constqp(AVCodecContext *avctx)
|
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
|
|
|
|
ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;
|
|
|
|
ctx->encode_config.rcParams.constQP.qpInterB = avctx->global_quality;
|
|
|
|
ctx->encode_config.rcParams.constQP.qpInterP = avctx->global_quality;
|
|
|
|
ctx->encode_config.rcParams.constQP.qpIntra = avctx->global_quality;
|
|
|
|
}
|
|
|
|
|
|
|
|
static av_cold void set_vbr(AVCodecContext *avctx)
|
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
|
|
|
|
ctx->encode_config.rcParams.enableMinQP = 1;
|
|
|
|
ctx->encode_config.rcParams.enableMaxQP = 1;
|
|
|
|
|
|
|
|
ctx->encode_config.rcParams.minQP.qpInterB = avctx->qmin;
|
|
|
|
ctx->encode_config.rcParams.minQP.qpInterP = avctx->qmin;
|
|
|
|
ctx->encode_config.rcParams.minQP.qpIntra = avctx->qmin;
|
|
|
|
|
|
|
|
ctx->encode_config.rcParams.maxQP.qpInterB = avctx->qmax;
|
|
|
|
ctx->encode_config.rcParams.maxQP.qpInterP = avctx->qmax;
|
|
|
|
ctx->encode_config.rcParams.maxQP.qpIntra = avctx->qmax;
|
|
|
|
}
|
|
|
|
|
|
|
|
static av_cold void set_lossless(AVCodecContext *avctx)
|
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
|
|
|
|
ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;
|
|
|
|
ctx->encode_config.rcParams.constQP.qpInterB = 0;
|
|
|
|
ctx->encode_config.rcParams.constQP.qpInterP = 0;
|
|
|
|
ctx->encode_config.rcParams.constQP.qpIntra = 0;
|
|
|
|
}
|
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
static av_cold void nvenc_setup_rate_control(AVCodecContext *avctx)
|
2016-05-20 16:49:24 +02:00
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
|
|
|
|
int qp_inter_p;
|
|
|
|
|
|
|
|
if (avctx->bit_rate > 0) {
|
|
|
|
ctx->encode_config.rcParams.averageBitRate = avctx->bit_rate;
|
|
|
|
} else if (ctx->encode_config.rcParams.averageBitRate > 0) {
|
|
|
|
ctx->encode_config.rcParams.maxBitRate = ctx->encode_config.rcParams.averageBitRate;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (avctx->rc_max_rate > 0)
|
|
|
|
ctx->encode_config.rcParams.maxBitRate = avctx->rc_max_rate;
|
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
if (ctx->flags & NVENC_LOSSLESS) {
|
2016-05-20 16:49:24 +02:00
|
|
|
if (avctx->codec->id == AV_CODEC_ID_H264)
|
|
|
|
ctx->encode_config.encodeCodecConfig.h264Config.qpPrimeYZeroTransformBypassFlag = 1;
|
|
|
|
|
|
|
|
set_lossless(avctx);
|
|
|
|
|
|
|
|
avctx->qmin = -1;
|
|
|
|
avctx->qmax = -1;
|
|
|
|
} else if (ctx->cbr) {
|
|
|
|
if (!ctx->twopass) {
|
|
|
|
ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR;
|
|
|
|
} else {
|
|
|
|
ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_2_PASS_QUALITY;
|
|
|
|
|
|
|
|
if (avctx->codec->id == AV_CODEC_ID_H264) {
|
|
|
|
ctx->encode_config.encodeCodecConfig.h264Config.adaptiveTransformMode = NV_ENC_H264_ADAPTIVE_TRANSFORM_ENABLE;
|
|
|
|
ctx->encode_config.encodeCodecConfig.h264Config.fmoMode = NV_ENC_H264_FMO_DISABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (avctx->codec->id == AV_CODEC_ID_H264) {
|
|
|
|
ctx->encode_config.encodeCodecConfig.h264Config.outputBufferingPeriodSEI = 1;
|
|
|
|
ctx->encode_config.encodeCodecConfig.h264Config.outputPictureTimingSEI = 1;
|
|
|
|
} else if (avctx->codec->id == AV_CODEC_ID_H265) {
|
|
|
|
ctx->encode_config.encodeCodecConfig.hevcConfig.outputBufferingPeriodSEI = 1;
|
|
|
|
ctx->encode_config.encodeCodecConfig.hevcConfig.outputPictureTimingSEI = 1;
|
|
|
|
}
|
|
|
|
} else if (avctx->global_quality > 0) {
|
|
|
|
set_constqp(avctx);
|
|
|
|
|
|
|
|
avctx->qmin = -1;
|
|
|
|
avctx->qmax = -1;
|
|
|
|
} else {
|
|
|
|
if (avctx->qmin >= 0 && avctx->qmax >= 0) {
|
|
|
|
set_vbr(avctx);
|
|
|
|
|
|
|
|
qp_inter_p = (avctx->qmax + 3 * avctx->qmin) / 4; // biased towards Qmin
|
|
|
|
|
|
|
|
if (ctx->twopass) {
|
|
|
|
ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_2_PASS_VBR;
|
|
|
|
if (avctx->codec->id == AV_CODEC_ID_H264) {
|
|
|
|
ctx->encode_config.encodeCodecConfig.h264Config.adaptiveTransformMode = NV_ENC_H264_ADAPTIVE_TRANSFORM_ENABLE;
|
|
|
|
ctx->encode_config.encodeCodecConfig.h264Config.fmoMode = NV_ENC_H264_FMO_DISABLE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_VBR_MINQP;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
qp_inter_p = 26; // default to 26
|
|
|
|
|
|
|
|
if (ctx->twopass) {
|
|
|
|
ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_2_PASS_VBR;
|
|
|
|
} else {
|
|
|
|
ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_VBR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx->encode_config.rcParams.enableInitialRCQP = 1;
|
|
|
|
ctx->encode_config.rcParams.initialRCQP.qpInterP = qp_inter_p;
|
|
|
|
|
|
|
|
if (avctx->i_quant_factor != 0.0 && avctx->b_quant_factor != 0.0) {
|
|
|
|
ctx->encode_config.rcParams.initialRCQP.qpIntra = av_clip(
|
|
|
|
qp_inter_p * fabs(avctx->i_quant_factor) + avctx->i_quant_offset, 0, 51);
|
|
|
|
ctx->encode_config.rcParams.initialRCQP.qpInterB = av_clip(
|
|
|
|
qp_inter_p * fabs(avctx->b_quant_factor) + avctx->b_quant_offset, 0, 51);
|
|
|
|
} else {
|
|
|
|
ctx->encode_config.rcParams.initialRCQP.qpIntra = qp_inter_p;
|
|
|
|
ctx->encode_config.rcParams.initialRCQP.qpInterB = qp_inter_p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (avctx->rc_buffer_size > 0) {
|
|
|
|
ctx->encode_config.rcParams.vbvBufferSize = avctx->rc_buffer_size;
|
|
|
|
} else if (ctx->encode_config.rcParams.averageBitRate > 0) {
|
|
|
|
ctx->encode_config.rcParams.vbvBufferSize = 2 * ctx->encode_config.rcParams.averageBitRate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
static av_cold int nvenc_setup_h264_config(AVCodecContext *avctx)
|
2016-05-20 16:49:24 +02:00
|
|
|
{
|
2016-05-25 16:05:50 +02:00
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NV_ENC_CONFIG *cc = &ctx->encode_config;
|
|
|
|
NV_ENC_CONFIG_H264 *h264 = &cc->encodeCodecConfig.h264Config;
|
|
|
|
NV_ENC_CONFIG_H264_VUI_PARAMETERS *vui = &h264->h264VUIParameters;
|
2016-05-20 16:49:24 +02:00
|
|
|
int res;
|
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
vui->colourMatrix = avctx->colorspace;
|
|
|
|
vui->colourPrimaries = avctx->color_primaries;
|
|
|
|
vui->transferCharacteristics = avctx->color_trc;
|
|
|
|
vui->videoFullRangeFlag = (avctx->color_range == AVCOL_RANGE_JPEG
|
2016-05-21 00:08:06 +02:00
|
|
|
|| ctx->data_pix_fmt == AV_PIX_FMT_YUVJ420P || ctx->data_pix_fmt == AV_PIX_FMT_YUVJ422P || ctx->data_pix_fmt == AV_PIX_FMT_YUVJ444P);
|
2016-05-20 16:49:24 +02:00
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
vui->colourDescriptionPresentFlag =
|
2016-05-20 16:49:24 +02:00
|
|
|
(avctx->colorspace != 2 || avctx->color_primaries != 2 || avctx->color_trc != 2);
|
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
vui->videoSignalTypePresentFlag =
|
|
|
|
(vui->colourDescriptionPresentFlag
|
|
|
|
|| vui->videoFormat != 5
|
|
|
|
|| vui->videoFullRangeFlag != 0);
|
2016-05-20 16:49:24 +02:00
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
h264->sliceMode = 3;
|
|
|
|
h264->sliceModeData = 1;
|
2016-05-20 16:49:24 +02:00
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
h264->disableSPSPPS = (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) ? 1 : 0;
|
|
|
|
h264->repeatSPSPPS = (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) ? 0 : 1;
|
2016-05-20 16:49:24 +02:00
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
h264->outputAUD = 1;
|
2016-05-20 16:49:24 +02:00
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
if (!ctx->profile && !(ctx->flags & NVENC_LOSSLESS)) {
|
2016-05-20 16:49:24 +02:00
|
|
|
switch (avctx->profile) {
|
|
|
|
case FF_PROFILE_H264_HIGH_444_PREDICTIVE:
|
2016-05-25 16:05:50 +02:00
|
|
|
cc->profileGUID = NV_ENC_H264_PROFILE_HIGH_444_GUID;
|
2016-05-20 16:49:24 +02:00
|
|
|
break;
|
|
|
|
case FF_PROFILE_H264_BASELINE:
|
2016-05-25 16:05:50 +02:00
|
|
|
cc->profileGUID = NV_ENC_H264_PROFILE_BASELINE_GUID;
|
2016-05-20 16:49:24 +02:00
|
|
|
break;
|
|
|
|
case FF_PROFILE_H264_MAIN:
|
2016-05-25 16:05:50 +02:00
|
|
|
cc->profileGUID = NV_ENC_H264_PROFILE_MAIN_GUID;
|
2016-05-20 16:49:24 +02:00
|
|
|
break;
|
|
|
|
case FF_PROFILE_H264_HIGH:
|
|
|
|
case FF_PROFILE_UNKNOWN:
|
2016-05-25 16:05:50 +02:00
|
|
|
cc->profileGUID = NV_ENC_H264_PROFILE_HIGH_GUID;
|
2016-05-20 16:49:24 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
av_log(avctx, AV_LOG_WARNING, "Unsupported profile requested, falling back to high\n");
|
2016-05-25 16:05:50 +02:00
|
|
|
cc->profileGUID = NV_ENC_H264_PROFILE_HIGH_GUID;
|
2016-05-20 16:49:24 +02:00
|
|
|
break;
|
|
|
|
}
|
2016-05-25 16:05:50 +02:00
|
|
|
} else if (!(ctx->flags & NVENC_LOSSLESS)) {
|
2016-05-20 16:49:24 +02:00
|
|
|
if (!strcmp(ctx->profile, "high")) {
|
2016-05-25 16:05:50 +02:00
|
|
|
cc->profileGUID = NV_ENC_H264_PROFILE_HIGH_GUID;
|
2016-05-20 16:49:24 +02:00
|
|
|
avctx->profile = FF_PROFILE_H264_HIGH;
|
|
|
|
} else if (!strcmp(ctx->profile, "main")) {
|
2016-05-25 16:05:50 +02:00
|
|
|
cc->profileGUID = NV_ENC_H264_PROFILE_MAIN_GUID;
|
2016-05-20 16:49:24 +02:00
|
|
|
avctx->profile = FF_PROFILE_H264_MAIN;
|
|
|
|
} else if (!strcmp(ctx->profile, "baseline")) {
|
2016-05-25 16:05:50 +02:00
|
|
|
cc->profileGUID = NV_ENC_H264_PROFILE_BASELINE_GUID;
|
2016-05-20 16:49:24 +02:00
|
|
|
avctx->profile = FF_PROFILE_H264_BASELINE;
|
|
|
|
} else if (!strcmp(ctx->profile, "high444p")) {
|
2016-05-25 16:05:50 +02:00
|
|
|
cc->profileGUID = NV_ENC_H264_PROFILE_HIGH_444_GUID;
|
2016-05-20 16:49:24 +02:00
|
|
|
avctx->profile = FF_PROFILE_H264_HIGH_444_PREDICTIVE;
|
|
|
|
} else {
|
|
|
|
av_log(avctx, AV_LOG_FATAL, "Profile \"%s\" is unknown! Supported profiles: high, main, baseline\n", ctx->profile);
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// force setting profile as high444p if input is AV_PIX_FMT_YUV444P
|
2016-05-21 00:08:06 +02:00
|
|
|
if (ctx->data_pix_fmt == AV_PIX_FMT_YUV444P) {
|
2016-05-25 16:05:50 +02:00
|
|
|
cc->profileGUID = NV_ENC_H264_PROFILE_HIGH_444_GUID;
|
2016-05-20 16:49:24 +02:00
|
|
|
avctx->profile = FF_PROFILE_H264_HIGH_444_PREDICTIVE;
|
2014-11-30 01:04:37 +02:00
|
|
|
}
|
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
h264->chromaFormatIDC = avctx->profile == FF_PROFILE_H264_HIGH_444_PREDICTIVE ? 3 : 1;
|
2016-05-20 16:49:24 +02:00
|
|
|
|
|
|
|
if (ctx->level) {
|
2016-05-25 16:05:50 +02:00
|
|
|
res = input_string_to_uint32(avctx, nvenc_h264_level_pairs, ctx->level, &h264->level);
|
2016-05-20 16:49:24 +02:00
|
|
|
|
|
|
|
if (res) {
|
|
|
|
av_log(avctx, AV_LOG_FATAL, "Level \"%s\" is unknown! Supported levels: auto, 1, 1b, 1.1, 1.2, 1.3, 2, 2.1, 2.2, 3, 3.1, 3.2, 4, 4.1, 4.2, 5, 5.1\n", ctx->level);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
} else {
|
2016-05-25 16:05:50 +02:00
|
|
|
h264->level = NV_ENC_LEVEL_AUTOSELECT;
|
2016-05-20 16:49:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static av_cold int nvenc_setup_hevc_config(AVCodecContext *avctx)
|
|
|
|
{
|
2016-05-25 16:05:50 +02:00
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NV_ENC_CONFIG *cc = &ctx->encode_config;
|
|
|
|
NV_ENC_CONFIG_HEVC *hevc = &cc->encodeCodecConfig.hevcConfig;
|
|
|
|
NV_ENC_CONFIG_HEVC_VUI_PARAMETERS *vui = &hevc->hevcVUIParameters;
|
2016-05-20 16:49:24 +02:00
|
|
|
int res;
|
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
vui->colourMatrix = avctx->colorspace;
|
|
|
|
vui->colourPrimaries = avctx->color_primaries;
|
|
|
|
vui->transferCharacteristics = avctx->color_trc;
|
|
|
|
vui->videoFullRangeFlag = (avctx->color_range == AVCOL_RANGE_JPEG
|
2016-05-21 00:08:06 +02:00
|
|
|
|| ctx->data_pix_fmt == AV_PIX_FMT_YUVJ420P || ctx->data_pix_fmt == AV_PIX_FMT_YUVJ422P || ctx->data_pix_fmt == AV_PIX_FMT_YUVJ444P);
|
2016-05-20 16:49:24 +02:00
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
vui->colourDescriptionPresentFlag =
|
2016-05-20 16:49:24 +02:00
|
|
|
(avctx->colorspace != 2 || avctx->color_primaries != 2 || avctx->color_trc != 2);
|
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
vui->videoSignalTypePresentFlag =
|
|
|
|
(vui->colourDescriptionPresentFlag
|
|
|
|
|| vui->videoFormat != 5
|
|
|
|
|| vui->videoFullRangeFlag != 0);
|
2016-05-20 16:49:24 +02:00
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
hevc->sliceMode = 3;
|
|
|
|
hevc->sliceModeData = 1;
|
2016-05-20 16:49:24 +02:00
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
hevc->disableSPSPPS = (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) ? 1 : 0;
|
|
|
|
hevc->repeatSPSPPS = (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) ? 0 : 1;
|
2016-05-20 16:49:24 +02:00
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
hevc->outputAUD = 1;
|
2016-05-20 16:49:24 +02:00
|
|
|
|
|
|
|
/* No other profile is supported in the current SDK version 5 */
|
2016-05-25 16:05:50 +02:00
|
|
|
cc->profileGUID = NV_ENC_HEVC_PROFILE_MAIN_GUID;
|
2016-05-20 16:49:24 +02:00
|
|
|
avctx->profile = FF_PROFILE_HEVC_MAIN;
|
|
|
|
|
|
|
|
if (ctx->level) {
|
2016-05-25 16:05:50 +02:00
|
|
|
res = input_string_to_uint32(avctx, nvenc_hevc_level_pairs, ctx->level, &hevc->level);
|
2016-05-20 16:49:24 +02:00
|
|
|
|
|
|
|
if (res) {
|
|
|
|
av_log(avctx, AV_LOG_FATAL, "Level \"%s\" is unknown! Supported levels: auto, 1, 2, 2.1, 3, 3.1, 4, 4.1, 5, 5.1, 5.2, 6, 6.1, 6.2\n", ctx->level);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
} else {
|
2016-05-25 16:05:50 +02:00
|
|
|
hevc->level = NV_ENC_LEVEL_AUTOSELECT;
|
2016-05-20 16:49:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->tier) {
|
|
|
|
if (!strcmp(ctx->tier, "main")) {
|
2016-05-25 16:05:50 +02:00
|
|
|
hevc->tier = NV_ENC_TIER_HEVC_MAIN;
|
2016-05-20 16:49:24 +02:00
|
|
|
} else if (!strcmp(ctx->tier, "high")) {
|
2016-05-25 16:05:50 +02:00
|
|
|
hevc->tier = NV_ENC_TIER_HEVC_HIGH;
|
2016-05-20 16:49:24 +02:00
|
|
|
} else {
|
|
|
|
av_log(avctx, AV_LOG_FATAL, "Tier \"%s\" is unknown! Supported tiers: main, high\n", ctx->tier);
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
static av_cold int nvenc_setup_codec_config(AVCodecContext *avctx)
|
2016-05-20 16:49:24 +02:00
|
|
|
{
|
|
|
|
switch (avctx->codec->id) {
|
|
|
|
case AV_CODEC_ID_H264:
|
2016-05-25 16:05:50 +02:00
|
|
|
return nvenc_setup_h264_config(avctx);
|
|
|
|
case AV_CODEC_ID_HEVC:
|
2016-05-20 16:49:24 +02:00
|
|
|
return nvenc_setup_hevc_config(avctx);
|
|
|
|
/* Earlier switch/case will return if unknown codec is passed. */
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static av_cold int nvenc_setup_encoder(AVCodecContext *avctx)
|
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
|
|
|
|
NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
|
|
|
|
|
|
|
|
NV_ENC_PRESET_CONFIG preset_config = { 0 };
|
|
|
|
NVENCSTATUS nv_status = NV_ENC_SUCCESS;
|
|
|
|
AVCPBProperties *cpb_props;
|
|
|
|
int num_mbs;
|
|
|
|
int res = 0;
|
|
|
|
int dw, dh;
|
|
|
|
|
|
|
|
ctx->last_dts = AV_NOPTS_VALUE;
|
|
|
|
|
|
|
|
ctx->encode_config.version = NV_ENC_CONFIG_VER;
|
|
|
|
ctx->init_encode_params.version = NV_ENC_INITIALIZE_PARAMS_VER;
|
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
ctx->init_encode_params.encodeHeight = avctx->height;
|
|
|
|
ctx->init_encode_params.encodeWidth = avctx->width;
|
|
|
|
|
|
|
|
ctx->init_encode_params.encodeConfig = &ctx->encode_config;
|
|
|
|
|
|
|
|
nvenc_map_preset(ctx);
|
|
|
|
|
|
|
|
if (ctx->flags & NVENC_ONE_PASS)
|
|
|
|
ctx->twopass = 0;
|
|
|
|
if (ctx->flags & NVENC_TWO_PASSES)
|
|
|
|
ctx->twopass = 1;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2015-09-11 11:07:10 +02:00
|
|
|
if (ctx->twopass < 0) {
|
2016-05-25 16:05:50 +02:00
|
|
|
ctx->twopass = (ctx->flags & NVENC_LOWLATENCY) != 0;
|
2015-09-11 11:07:10 +02:00
|
|
|
}
|
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
preset_config.version = NV_ENC_PRESET_CONFIG_VER;
|
|
|
|
preset_config.presetCfg.version = NV_ENC_CONFIG_VER;
|
2015-03-24 06:34:59 +02:00
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
nv_status = p_nvenc->nvEncGetEncodePresetConfig(ctx->nvencoder,
|
|
|
|
ctx->init_encode_params.encodeGUID,
|
|
|
|
ctx->init_encode_params.presetGUID,
|
|
|
|
&preset_config);
|
|
|
|
if (nv_status != NV_ENC_SUCCESS)
|
|
|
|
return nvenc_print_error(avctx, nv_status, "Cannot get the preset configuration");
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
memcpy(&ctx->encode_config, &preset_config.presetCfg, sizeof(ctx->encode_config));
|
|
|
|
|
|
|
|
ctx->encode_config.version = NV_ENC_CONFIG_VER;
|
2015-01-26 14:28:22 +02:00
|
|
|
|
|
|
|
if (avctx->sample_aspect_ratio.num && avctx->sample_aspect_ratio.den &&
|
|
|
|
(avctx->sample_aspect_ratio.num != 1 || avctx->sample_aspect_ratio.num != 1)) {
|
|
|
|
av_reduce(&dw, &dh,
|
|
|
|
avctx->width * avctx->sample_aspect_ratio.num,
|
|
|
|
avctx->height * avctx->sample_aspect_ratio.den,
|
|
|
|
1024 * 1024);
|
|
|
|
ctx->init_encode_params.darHeight = dh;
|
|
|
|
ctx->init_encode_params.darWidth = dw;
|
|
|
|
} else {
|
|
|
|
ctx->init_encode_params.darHeight = avctx->height;
|
|
|
|
ctx->init_encode_params.darWidth = avctx->width;
|
|
|
|
}
|
|
|
|
|
avcodec/nvenc: De-compensate aspect ratio compensation of DVD-like content.
For reasons we are not privy to, nvidia decided that the nvenc encoder
should apply aspect ratio compensation to 'DVD like' content, assuming that
the content is not bt.601 compliant, but needs to be bt.601 compliant. In
this context, that means that they make the following, questionable,
assumptions:
1) If the input dimensions are 720x480 or 720x576, assume the content has
an active area of 704x480 or 704x576.
2) Assume that whatever the input sample aspect ratio is, it does not account
for the difference between 'physical' and 'active' dimensions.
From, these assumptions, they then conclude that they can 'help', by adjusting
the sample aspect ratio by a factor of 45/44. And indeed, if you wanted to
display only the 704 wide active area with the same aspect ratio as the full
720 wide image - this would be the correct adjustment factor, but what if you
don't? And more importantly, what if you're used to ffmpeg not making this kind
of adjustment at encode time - because none of the other encoders do this!
And, what if you had already accounted for bt.601 and your input had the
correct attributes? Well, it's going to apply the compensation anyway!
So, if you take some content, and feed it through nvenc repeatedly, it
will keep scaling the aspect ratio every time, stretching your video out
more and more and more.
So, clearly, regardless of whether you want to apply bt.601 aspect ratio
adjustments or not, this is not the way to do it. With any other ffmpeg
encoder, you would do it as part of defining your input paramters or
do the adjustment at playback time, and there's no reason by nvenc
should be any different.
This change adds some logic to undo the compensation that nvenc would
otherwise do.
nvidia engineers have told us that they will work to make this
compensation mechanism optional in a future release of the nvenc
SDK. At that point, we can adapt accordingly.
Signed-off-by: Philip Langdale <philipl@overt.org>
Reviewed-by: Timo Rothenpieler <timo@rothenpieler.org>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
2015-01-28 19:05:53 +02:00
|
|
|
// De-compensate for hardware, dubiously, trying to compensate for
|
|
|
|
// playback at 704 pixel width.
|
|
|
|
if (avctx->width == 720 &&
|
|
|
|
(avctx->height == 480 || avctx->height == 576)) {
|
|
|
|
av_reduce(&dw, &dh,
|
|
|
|
ctx->init_encode_params.darWidth * 44,
|
|
|
|
ctx->init_encode_params.darHeight * 45,
|
2015-05-27 03:35:15 +02:00
|
|
|
1024 * 1024);
|
avcodec/nvenc: De-compensate aspect ratio compensation of DVD-like content.
For reasons we are not privy to, nvidia decided that the nvenc encoder
should apply aspect ratio compensation to 'DVD like' content, assuming that
the content is not bt.601 compliant, but needs to be bt.601 compliant. In
this context, that means that they make the following, questionable,
assumptions:
1) If the input dimensions are 720x480 or 720x576, assume the content has
an active area of 704x480 or 704x576.
2) Assume that whatever the input sample aspect ratio is, it does not account
for the difference between 'physical' and 'active' dimensions.
From, these assumptions, they then conclude that they can 'help', by adjusting
the sample aspect ratio by a factor of 45/44. And indeed, if you wanted to
display only the 704 wide active area with the same aspect ratio as the full
720 wide image - this would be the correct adjustment factor, but what if you
don't? And more importantly, what if you're used to ffmpeg not making this kind
of adjustment at encode time - because none of the other encoders do this!
And, what if you had already accounted for bt.601 and your input had the
correct attributes? Well, it's going to apply the compensation anyway!
So, if you take some content, and feed it through nvenc repeatedly, it
will keep scaling the aspect ratio every time, stretching your video out
more and more and more.
So, clearly, regardless of whether you want to apply bt.601 aspect ratio
adjustments or not, this is not the way to do it. With any other ffmpeg
encoder, you would do it as part of defining your input paramters or
do the adjustment at playback time, and there's no reason by nvenc
should be any different.
This change adds some logic to undo the compensation that nvenc would
otherwise do.
nvidia engineers have told us that they will work to make this
compensation mechanism optional in a future release of the nvenc
SDK. At that point, we can adapt accordingly.
Signed-off-by: Philip Langdale <philipl@overt.org>
Reviewed-by: Timo Rothenpieler <timo@rothenpieler.org>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
2015-01-28 19:05:53 +02:00
|
|
|
ctx->init_encode_params.darHeight = dh;
|
|
|
|
ctx->init_encode_params.darWidth = dw;
|
|
|
|
}
|
|
|
|
|
2014-11-30 01:04:37 +02:00
|
|
|
ctx->init_encode_params.frameRateNum = avctx->time_base.den;
|
|
|
|
ctx->init_encode_params.frameRateDen = avctx->time_base.num * avctx->ticks_per_frame;
|
|
|
|
|
|
|
|
num_mbs = ((avctx->width + 15) >> 4) * ((avctx->height + 15) >> 4);
|
|
|
|
ctx->max_surface_count = (num_mbs >= 8160) ? 32 : 48;
|
|
|
|
|
2015-07-25 23:20:28 +02:00
|
|
|
if (ctx->buffer_delay >= ctx->max_surface_count)
|
|
|
|
ctx->buffer_delay = ctx->max_surface_count - 1;
|
|
|
|
|
2014-11-30 01:04:37 +02:00
|
|
|
ctx->init_encode_params.enableEncodeAsync = 0;
|
|
|
|
ctx->init_encode_params.enablePTD = 1;
|
|
|
|
|
2015-01-24 22:52:58 +02:00
|
|
|
if (avctx->refs >= 0) {
|
|
|
|
/* 0 means "let the hardware decide" */
|
2015-03-24 06:34:59 +02:00
|
|
|
switch (avctx->codec->id) {
|
|
|
|
case AV_CODEC_ID_H264:
|
|
|
|
ctx->encode_config.encodeCodecConfig.h264Config.maxNumRefFrames = avctx->refs;
|
|
|
|
break;
|
|
|
|
case AV_CODEC_ID_H265:
|
|
|
|
ctx->encode_config.encodeCodecConfig.hevcConfig.maxNumRefFramesInDPB = avctx->refs;
|
|
|
|
break;
|
|
|
|
/* Earlier switch/case will return if unknown codec is passed. */
|
|
|
|
}
|
2015-01-24 22:52:58 +02:00
|
|
|
}
|
|
|
|
|
2015-01-26 14:28:21 +02:00
|
|
|
if (avctx->gop_size > 0) {
|
|
|
|
if (avctx->max_b_frames >= 0) {
|
|
|
|
/* 0 is intra-only, 1 is I/P only, 2 is one B Frame, 3 two B frames, and so on. */
|
|
|
|
ctx->encode_config.frameIntervalP = avctx->max_b_frames + 1;
|
|
|
|
}
|
|
|
|
|
2014-11-30 01:04:37 +02:00
|
|
|
ctx->encode_config.gopLength = avctx->gop_size;
|
2015-03-24 06:34:59 +02:00
|
|
|
switch (avctx->codec->id) {
|
|
|
|
case AV_CODEC_ID_H264:
|
|
|
|
ctx->encode_config.encodeCodecConfig.h264Config.idrPeriod = avctx->gop_size;
|
|
|
|
break;
|
|
|
|
case AV_CODEC_ID_H265:
|
2016-05-20 16:49:24 +02:00
|
|
|
ctx->encode_config.encodeCodecConfig.hevcConfig.idrPeriod = avctx->gop_size;
|
|
|
|
break;
|
|
|
|
/* Earlier switch/case will return if unknown codec is passed. */
|
|
|
|
}
|
|
|
|
} else if (avctx->gop_size == 0) {
|
|
|
|
ctx->encode_config.frameIntervalP = 0;
|
|
|
|
ctx->encode_config.gopLength = 1;
|
|
|
|
switch (avctx->codec->id) {
|
|
|
|
case AV_CODEC_ID_H264:
|
|
|
|
ctx->encode_config.encodeCodecConfig.h264Config.idrPeriod = 1;
|
|
|
|
break;
|
|
|
|
case AV_CODEC_ID_H265:
|
|
|
|
ctx->encode_config.encodeCodecConfig.hevcConfig.idrPeriod = 1;
|
|
|
|
break;
|
|
|
|
/* Earlier switch/case will return if unknown codec is passed. */
|
2015-09-11 11:07:10 +02:00
|
|
|
}
|
2014-11-30 01:04:37 +02:00
|
|
|
}
|
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
/* when there're b frames, set dts offset */
|
|
|
|
if (ctx->encode_config.frameIntervalP >= 2)
|
|
|
|
ctx->last_dts = -2;
|
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
nvenc_setup_rate_control(avctx);
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2015-07-27 21:14:31 +02:00
|
|
|
if (avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT) {
|
2014-11-30 01:04:37 +02:00
|
|
|
ctx->encode_config.frameFieldMode = NV_ENC_PARAMS_FRAME_FIELD_MODE_FIELD;
|
|
|
|
} else {
|
|
|
|
ctx->encode_config.frameFieldMode = NV_ENC_PARAMS_FRAME_FIELD_MODE_FRAME;
|
|
|
|
}
|
|
|
|
|
2016-05-25 16:05:50 +02:00
|
|
|
res = nvenc_setup_codec_config(avctx);
|
2016-05-20 16:49:24 +02:00
|
|
|
if (res)
|
|
|
|
return res;
|
2016-03-04 11:00:48 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
nv_status = p_nvenc->nvEncInitializeEncoder(ctx->nvencoder, &ctx->init_encode_params);
|
|
|
|
if (nv_status != NV_ENC_SUCCESS) {
|
2016-05-20 17:37:00 +02:00
|
|
|
return nvenc_print_error(avctx, nv_status, "InitializeEncoder failed");
|
2016-05-20 16:49:24 +02:00
|
|
|
}
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
if (ctx->encode_config.frameIntervalP > 1)
|
|
|
|
avctx->has_b_frames = 2;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
if (ctx->encode_config.rcParams.averageBitRate > 0)
|
|
|
|
avctx->bit_rate = ctx->encode_config.rcParams.averageBitRate;
|
2015-12-14 11:27:36 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
cpb_props = ff_add_cpb_side_data(avctx);
|
|
|
|
if (!cpb_props)
|
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
cpb_props->max_bitrate = ctx->encode_config.rcParams.maxBitRate;
|
|
|
|
cpb_props->avg_bitrate = avctx->bit_rate;
|
|
|
|
cpb_props->buffer_size = ctx->encode_config.rcParams.vbvBufferSize;
|
2015-04-02 00:04:07 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2016-03-30 12:03:59 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
static av_cold int nvenc_alloc_surface(AVCodecContext *avctx, int idx)
|
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
|
|
|
|
NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
|
2015-04-04 13:34:14 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
NVENCSTATUS nv_status;
|
|
|
|
NV_ENC_CREATE_BITSTREAM_BUFFER allocOut = { 0 };
|
|
|
|
allocOut.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER;
|
2015-07-02 06:09:57 +02:00
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
switch (ctx->data_pix_fmt) {
|
2016-05-20 16:49:24 +02:00
|
|
|
case AV_PIX_FMT_YUV420P:
|
2016-05-21 00:08:06 +02:00
|
|
|
ctx->surfaces[idx].format = NV_ENC_BUFFER_FORMAT_YV12_PL;
|
2015-03-24 06:34:59 +02:00
|
|
|
break;
|
2016-03-04 11:00:48 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
case AV_PIX_FMT_NV12:
|
2016-05-21 00:08:06 +02:00
|
|
|
ctx->surfaces[idx].format = NV_ENC_BUFFER_FORMAT_NV12_PL;
|
2016-05-20 16:49:24 +02:00
|
|
|
break;
|
2016-03-04 11:00:48 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
case AV_PIX_FMT_YUV444P:
|
2016-05-21 00:08:06 +02:00
|
|
|
ctx->surfaces[idx].format = NV_ENC_BUFFER_FORMAT_YUV444_PL;
|
2016-05-20 16:49:24 +02:00
|
|
|
break;
|
2016-03-04 11:00:48 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
default:
|
|
|
|
av_log(avctx, AV_LOG_FATAL, "Invalid input pixel format\n");
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
}
|
2015-12-14 11:27:36 +02:00
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
if (avctx->pix_fmt == AV_PIX_FMT_CUDA) {
|
|
|
|
ctx->surfaces[idx].in_ref = av_frame_alloc();
|
|
|
|
if (!ctx->surfaces[idx].in_ref)
|
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
} else {
|
|
|
|
NV_ENC_CREATE_INPUT_BUFFER allocSurf = { 0 };
|
|
|
|
allocSurf.version = NV_ENC_CREATE_INPUT_BUFFER_VER;
|
|
|
|
allocSurf.width = (avctx->width + 31) & ~31;
|
|
|
|
allocSurf.height = (avctx->height + 31) & ~31;
|
|
|
|
allocSurf.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED;
|
|
|
|
allocSurf.bufferFmt = ctx->surfaces[idx].format;
|
|
|
|
|
|
|
|
nv_status = p_nvenc->nvEncCreateInputBuffer(ctx->nvencoder, &allocSurf);
|
|
|
|
if (nv_status != NV_ENC_SUCCESS) {
|
|
|
|
return nvenc_print_error(avctx, nv_status, "CreateInputBuffer failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx->surfaces[idx].input_surface = allocSurf.inputBuffer;
|
|
|
|
ctx->surfaces[idx].width = allocSurf.width;
|
|
|
|
ctx->surfaces[idx].height = allocSurf.height;
|
2016-05-20 16:49:24 +02:00
|
|
|
}
|
2015-04-02 00:04:07 +02:00
|
|
|
|
2016-05-20 17:37:00 +02:00
|
|
|
ctx->surfaces[idx].lockCount = 0;
|
2016-03-30 12:03:59 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
/* 1MB is large enough to hold most output frames. NVENC increases this automaticaly if it's not enough. */
|
|
|
|
allocOut.size = 1024 * 1024;
|
2015-04-04 13:34:14 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
allocOut.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED;
|
2015-04-04 13:34:14 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
nv_status = p_nvenc->nvEncCreateBitstreamBuffer(ctx->nvencoder, &allocOut);
|
|
|
|
if (nv_status != NV_ENC_SUCCESS) {
|
2016-05-20 17:37:00 +02:00
|
|
|
int err = nvenc_print_error(avctx, nv_status, "CreateBitstreamBuffer failed");
|
2016-05-21 00:08:06 +02:00
|
|
|
if (avctx->pix_fmt != AV_PIX_FMT_CUDA)
|
|
|
|
p_nvenc->nvEncDestroyInputBuffer(ctx->nvencoder, ctx->surfaces[idx].input_surface);
|
|
|
|
av_frame_free(&ctx->surfaces[idx].in_ref);
|
2016-05-20 17:37:00 +02:00
|
|
|
return err;
|
2016-05-20 16:49:24 +02:00
|
|
|
}
|
2015-04-04 13:34:14 +02:00
|
|
|
|
2016-05-20 17:37:00 +02:00
|
|
|
ctx->surfaces[idx].output_surface = allocOut.bitstreamBuffer;
|
|
|
|
ctx->surfaces[idx].size = allocOut.size;
|
2015-04-04 13:34:14 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
static av_cold int nvenc_setup_surfaces(AVCodecContext *avctx, int* surfaceCount)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 17:37:00 +02:00
|
|
|
ctx->surfaces = av_malloc(ctx->max_surface_count * sizeof(*ctx->surfaces));
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 17:37:00 +02:00
|
|
|
if (!ctx->surfaces) {
|
2016-05-20 16:49:24 +02:00
|
|
|
return AVERROR(ENOMEM);
|
2014-11-30 01:04:37 +02:00
|
|
|
}
|
|
|
|
|
2016-05-20 18:13:20 +02:00
|
|
|
ctx->timestamp_list = av_fifo_alloc(ctx->max_surface_count * sizeof(int64_t));
|
|
|
|
if (!ctx->timestamp_list)
|
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
ctx->output_surface_queue = av_fifo_alloc(ctx->max_surface_count * sizeof(NvencSurface*));
|
|
|
|
if (!ctx->output_surface_queue)
|
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
ctx->output_surface_ready_queue = av_fifo_alloc(ctx->max_surface_count * sizeof(NvencSurface*));
|
|
|
|
if (!ctx->output_surface_ready_queue)
|
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
for (*surfaceCount = 0; *surfaceCount < ctx->max_surface_count; ++*surfaceCount) {
|
|
|
|
res = nvenc_alloc_surface(avctx, *surfaceCount);
|
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
}
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
static av_cold int nvenc_setup_extradata(AVCodecContext *avctx)
|
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
|
|
|
|
NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
NVENCSTATUS nv_status;
|
|
|
|
uint32_t outSize = 0;
|
|
|
|
char tmpHeader[256];
|
|
|
|
NV_ENC_SEQUENCE_PARAM_PAYLOAD payload = { 0 };
|
|
|
|
payload.version = NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
payload.spsppsBuffer = tmpHeader;
|
|
|
|
payload.inBufferSize = sizeof(tmpHeader);
|
|
|
|
payload.outSPSPPSPayloadSize = &outSize;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
nv_status = p_nvenc->nvEncGetSequenceParams(ctx->nvencoder, &payload);
|
|
|
|
if (nv_status != NV_ENC_SUCCESS) {
|
2016-05-20 17:37:00 +02:00
|
|
|
return nvenc_print_error(avctx, nv_status, "GetSequenceParams failed");
|
2016-05-20 16:49:24 +02:00
|
|
|
}
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
avctx->extradata_size = outSize;
|
|
|
|
avctx->extradata = av_mallocz(outSize + AV_INPUT_BUFFER_PADDING_SIZE);
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
if (!avctx->extradata) {
|
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
}
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
memcpy(avctx->extradata, tmpHeader, outSize);
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-25 15:04:28 +02:00
|
|
|
av_cold int ff_nvenc_encode_init(AVCodecContext *avctx)
|
2016-05-20 16:49:24 +02:00
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
|
|
|
|
NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
int res;
|
|
|
|
int i;
|
|
|
|
int surfaceCount = 0;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
if (!nvenc_dyload_nvenc(avctx))
|
|
|
|
return AVERROR_EXTERNAL;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
res = nvenc_setup_device(avctx);
|
|
|
|
if (res)
|
|
|
|
goto error;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
res = nvenc_open_session(avctx);
|
|
|
|
if (res)
|
|
|
|
goto error;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
res = nvenc_setup_encoder(avctx);
|
|
|
|
if (res)
|
|
|
|
goto error;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
res = nvenc_setup_surfaces(avctx, &surfaceCount);
|
|
|
|
if (res)
|
|
|
|
goto error;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
|
|
|
|
res = nvenc_setup_extradata(avctx);
|
|
|
|
if (res)
|
|
|
|
goto error;
|
|
|
|
}
|
2015-05-30 16:40:13 +02:00
|
|
|
|
2014-11-30 01:04:37 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
2016-05-20 18:13:20 +02:00
|
|
|
av_fifo_freep(&ctx->timestamp_list);
|
|
|
|
av_fifo_freep(&ctx->output_surface_ready_queue);
|
|
|
|
av_fifo_freep(&ctx->output_surface_queue);
|
2014-11-30 01:04:37 +02:00
|
|
|
|
|
|
|
for (i = 0; i < surfaceCount; ++i) {
|
2016-05-21 00:08:06 +02:00
|
|
|
if (avctx->pix_fmt != AV_PIX_FMT_CUDA)
|
|
|
|
p_nvenc->nvEncDestroyInputBuffer(ctx->nvencoder, ctx->surfaces[i].input_surface);
|
|
|
|
av_frame_free(&ctx->surfaces[i].in_ref);
|
2016-05-20 17:37:00 +02:00
|
|
|
p_nvenc->nvEncDestroyBitstreamBuffer(ctx->nvencoder, ctx->surfaces[i].output_surface);
|
2014-11-30 01:04:37 +02:00
|
|
|
}
|
2016-05-20 18:13:20 +02:00
|
|
|
av_freep(&ctx->surfaces);
|
2014-11-30 01:04:37 +02:00
|
|
|
|
|
|
|
if (ctx->nvencoder)
|
|
|
|
p_nvenc->nvEncDestroyEncoder(ctx->nvencoder);
|
2016-05-21 00:08:06 +02:00
|
|
|
ctx->nvencoder = NULL;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
if (ctx->cu_context_internal)
|
|
|
|
dl_fn->cu_ctx_destroy(ctx->cu_context_internal);
|
|
|
|
ctx->cu_context = ctx->cu_context_internal = NULL;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
|
|
|
nvenc_unload_nvenc(avctx);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2016-05-25 15:04:28 +02:00
|
|
|
av_cold int ff_nvenc_encode_close(AVCodecContext *avctx)
|
2014-11-30 01:04:37 +02:00
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
|
|
|
|
NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
|
|
|
|
int i;
|
|
|
|
|
2016-05-20 18:13:20 +02:00
|
|
|
av_fifo_freep(&ctx->timestamp_list);
|
|
|
|
av_fifo_freep(&ctx->output_surface_ready_queue);
|
|
|
|
av_fifo_freep(&ctx->output_surface_queue);
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
if (avctx->pix_fmt == AV_PIX_FMT_CUDA) {
|
|
|
|
for (i = 0; i < ctx->max_surface_count; ++i) {
|
|
|
|
if (ctx->surfaces[i].input_surface) {
|
|
|
|
p_nvenc->nvEncUnmapInputResource(ctx->nvencoder, ctx->surfaces[i].in_map.mappedResource);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < ctx->nb_registered_frames; i++) {
|
|
|
|
if (ctx->registered_frames[i].regptr)
|
|
|
|
p_nvenc->nvEncUnregisterResource(ctx->nvencoder, ctx->registered_frames[i].regptr);
|
|
|
|
}
|
|
|
|
ctx->nb_registered_frames = 0;
|
|
|
|
}
|
|
|
|
|
2014-11-30 01:04:37 +02:00
|
|
|
for (i = 0; i < ctx->max_surface_count; ++i) {
|
2016-05-21 00:08:06 +02:00
|
|
|
if (avctx->pix_fmt != AV_PIX_FMT_CUDA)
|
|
|
|
p_nvenc->nvEncDestroyInputBuffer(ctx->nvencoder, ctx->surfaces[i].input_surface);
|
|
|
|
av_frame_free(&ctx->surfaces[i].in_ref);
|
2016-05-20 17:37:00 +02:00
|
|
|
p_nvenc->nvEncDestroyBitstreamBuffer(ctx->nvencoder, ctx->surfaces[i].output_surface);
|
2014-11-30 01:04:37 +02:00
|
|
|
}
|
2016-05-20 18:13:20 +02:00
|
|
|
av_freep(&ctx->surfaces);
|
2014-11-30 01:04:37 +02:00
|
|
|
ctx->max_surface_count = 0;
|
|
|
|
|
|
|
|
p_nvenc->nvEncDestroyEncoder(ctx->nvencoder);
|
|
|
|
ctx->nvencoder = NULL;
|
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
if (ctx->cu_context_internal)
|
|
|
|
dl_fn->cu_ctx_destroy(ctx->cu_context_internal);
|
|
|
|
ctx->cu_context = ctx->cu_context_internal = NULL;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
|
|
|
nvenc_unload_nvenc(avctx);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-20 17:37:00 +02:00
|
|
|
static NvencSurface *get_free_frame(NvencContext *ctx)
|
2016-05-20 16:49:24 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ctx->max_surface_count; ++i) {
|
2016-05-20 17:37:00 +02:00
|
|
|
if (!ctx->surfaces[i].lockCount) {
|
|
|
|
ctx->surfaces[i].lockCount = 1;
|
|
|
|
return &ctx->surfaces[i];
|
2016-05-20 16:49:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-05-20 17:37:00 +02:00
|
|
|
static int nvenc_copy_frame(AVCodecContext *avctx, NvencSurface *inSurf,
|
2016-05-20 16:49:24 +02:00
|
|
|
NV_ENC_LOCK_INPUT_BUFFER *lockBufferParams, const AVFrame *frame)
|
|
|
|
{
|
|
|
|
uint8_t *buf = lockBufferParams->bufferDataPtr;
|
|
|
|
int off = inSurf->height * lockBufferParams->pitch;
|
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
if (frame->format == AV_PIX_FMT_YUV420P) {
|
2016-05-20 16:49:24 +02:00
|
|
|
av_image_copy_plane(buf, lockBufferParams->pitch,
|
|
|
|
frame->data[0], frame->linesize[0],
|
|
|
|
avctx->width, avctx->height);
|
|
|
|
|
|
|
|
buf += off;
|
|
|
|
|
|
|
|
av_image_copy_plane(buf, lockBufferParams->pitch >> 1,
|
|
|
|
frame->data[2], frame->linesize[2],
|
|
|
|
avctx->width >> 1, avctx->height >> 1);
|
|
|
|
|
|
|
|
buf += off >> 2;
|
|
|
|
|
|
|
|
av_image_copy_plane(buf, lockBufferParams->pitch >> 1,
|
|
|
|
frame->data[1], frame->linesize[1],
|
|
|
|
avctx->width >> 1, avctx->height >> 1);
|
2016-05-21 00:08:06 +02:00
|
|
|
} else if (frame->format == AV_PIX_FMT_NV12) {
|
2016-05-20 16:49:24 +02:00
|
|
|
av_image_copy_plane(buf, lockBufferParams->pitch,
|
|
|
|
frame->data[0], frame->linesize[0],
|
|
|
|
avctx->width, avctx->height);
|
|
|
|
|
|
|
|
buf += off;
|
|
|
|
|
|
|
|
av_image_copy_plane(buf, lockBufferParams->pitch,
|
|
|
|
frame->data[1], frame->linesize[1],
|
|
|
|
avctx->width, avctx->height >> 1);
|
2016-05-21 00:08:06 +02:00
|
|
|
} else if (frame->format == AV_PIX_FMT_YUV444P) {
|
2016-05-20 16:49:24 +02:00
|
|
|
av_image_copy_plane(buf, lockBufferParams->pitch,
|
|
|
|
frame->data[0], frame->linesize[0],
|
|
|
|
avctx->width, avctx->height);
|
|
|
|
|
|
|
|
buf += off;
|
|
|
|
|
|
|
|
av_image_copy_plane(buf, lockBufferParams->pitch,
|
|
|
|
frame->data[1], frame->linesize[1],
|
|
|
|
avctx->width, avctx->height);
|
|
|
|
|
|
|
|
buf += off;
|
|
|
|
|
|
|
|
av_image_copy_plane(buf, lockBufferParams->pitch,
|
|
|
|
frame->data[2], frame->linesize[2],
|
|
|
|
avctx->width, avctx->height);
|
|
|
|
} else {
|
|
|
|
av_log(avctx, AV_LOG_FATAL, "Invalid pixel format!\n");
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
static int nvenc_find_free_reg_resource(AVCodecContext *avctx)
|
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
|
|
|
|
NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (ctx->nb_registered_frames == FF_ARRAY_ELEMS(ctx->registered_frames)) {
|
|
|
|
for (i = 0; i < ctx->nb_registered_frames; i++) {
|
|
|
|
if (!ctx->registered_frames[i].mapped) {
|
|
|
|
if (ctx->registered_frames[i].regptr) {
|
|
|
|
p_nvenc->nvEncUnregisterResource(ctx->nvencoder,
|
|
|
|
ctx->registered_frames[i].regptr);
|
|
|
|
ctx->registered_frames[i].regptr = NULL;
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return ctx->nb_registered_frames++;
|
|
|
|
}
|
|
|
|
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Too many registered CUDA frames\n");
|
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nvenc_register_frame(AVCodecContext *avctx, const AVFrame *frame)
|
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
|
|
|
|
NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
|
|
|
|
|
|
|
|
AVHWFramesContext *frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
|
|
|
|
NV_ENC_REGISTER_RESOURCE reg;
|
|
|
|
int i, idx, ret;
|
|
|
|
|
|
|
|
for (i = 0; i < ctx->nb_registered_frames; i++) {
|
|
|
|
if (ctx->registered_frames[i].ptr == (CUdeviceptr)frame->data[0])
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = nvenc_find_free_reg_resource(avctx);
|
|
|
|
if (idx < 0)
|
|
|
|
return idx;
|
|
|
|
|
|
|
|
reg.version = NV_ENC_REGISTER_RESOURCE_VER;
|
|
|
|
reg.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR;
|
|
|
|
reg.width = frames_ctx->width;
|
|
|
|
reg.height = frames_ctx->height;
|
|
|
|
reg.bufferFormat = ctx->surfaces[0].format;
|
|
|
|
reg.pitch = frame->linesize[0];
|
|
|
|
reg.resourceToRegister = frame->data[0];
|
|
|
|
|
|
|
|
ret = p_nvenc->nvEncRegisterResource(ctx->nvencoder, ®);
|
|
|
|
if (ret != NV_ENC_SUCCESS) {
|
|
|
|
nvenc_print_error(avctx, ret, "Error registering an input resource");
|
|
|
|
return AVERROR_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx->registered_frames[idx].ptr = (CUdeviceptr)frame->data[0];
|
|
|
|
ctx->registered_frames[idx].regptr = reg.registeredResource;
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
static int nvenc_upload_frame(AVCodecContext *avctx, const AVFrame *frame,
|
2016-05-20 17:37:00 +02:00
|
|
|
NvencSurface *nvenc_frame)
|
2016-05-20 16:49:24 +02:00
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
|
|
|
|
NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
|
|
|
|
|
|
|
|
int res;
|
|
|
|
NVENCSTATUS nv_status;
|
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
if (avctx->pix_fmt == AV_PIX_FMT_CUDA) {
|
|
|
|
int reg_idx = nvenc_register_frame(avctx, frame);
|
|
|
|
if (reg_idx < 0) {
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Could not register an input CUDA frame\n");
|
|
|
|
return reg_idx;
|
|
|
|
}
|
2016-05-20 16:49:24 +02:00
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
res = av_frame_ref(nvenc_frame->in_ref, frame);
|
|
|
|
if (res < 0)
|
|
|
|
return res;
|
2016-05-20 16:49:24 +02:00
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
nvenc_frame->in_map.version = NV_ENC_MAP_INPUT_RESOURCE_VER;
|
|
|
|
nvenc_frame->in_map.registeredResource = ctx->registered_frames[reg_idx].regptr;
|
|
|
|
nv_status = p_nvenc->nvEncMapInputResource(ctx->nvencoder, &nvenc_frame->in_map);
|
|
|
|
if (nv_status != NV_ENC_SUCCESS) {
|
|
|
|
av_frame_unref(nvenc_frame->in_ref);
|
|
|
|
return nvenc_print_error(avctx, nv_status, "Error mapping an input resource");
|
|
|
|
}
|
2016-05-20 16:49:24 +02:00
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
ctx->registered_frames[reg_idx].mapped = 1;
|
|
|
|
nvenc_frame->reg_idx = reg_idx;
|
|
|
|
nvenc_frame->input_surface = nvenc_frame->in_map.mappedResource;
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
NV_ENC_LOCK_INPUT_BUFFER lockBufferParams = { 0 };
|
2016-05-20 16:49:24 +02:00
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
lockBufferParams.version = NV_ENC_LOCK_INPUT_BUFFER_VER;
|
|
|
|
lockBufferParams.inputBuffer = nvenc_frame->input_surface;
|
|
|
|
|
|
|
|
nv_status = p_nvenc->nvEncLockInputBuffer(ctx->nvencoder, &lockBufferParams);
|
|
|
|
if (nv_status != NV_ENC_SUCCESS) {
|
|
|
|
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) {
|
|
|
|
return nvenc_print_error(avctx, nv_status, "Failed unlocking input buffer!");
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
2016-05-20 16:49:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void nvenc_codec_specific_pic_params(AVCodecContext *avctx,
|
|
|
|
NV_ENC_PIC_PARAMS *params)
|
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
|
|
|
|
switch (avctx->codec->id) {
|
|
|
|
case AV_CODEC_ID_H264:
|
|
|
|
params->codecPicParams.h264PicParams.sliceMode = ctx->encode_config.encodeCodecConfig.h264Config.sliceMode;
|
|
|
|
params->codecPicParams.h264PicParams.sliceModeData = ctx->encode_config.encodeCodecConfig.h264Config.sliceModeData;
|
|
|
|
break;
|
|
|
|
case AV_CODEC_ID_H265:
|
|
|
|
params->codecPicParams.hevcPicParams.sliceMode = ctx->encode_config.encodeCodecConfig.hevcConfig.sliceMode;
|
|
|
|
params->codecPicParams.hevcPicParams.sliceModeData = ctx->encode_config.encodeCodecConfig.hevcConfig.sliceModeData;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-20 17:37:00 +02:00
|
|
|
static int process_output_surface(AVCodecContext *avctx, AVPacket *pkt, NvencSurface *tmpoutsurf)
|
2014-11-30 01:04:37 +02:00
|
|
|
{
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
|
|
|
|
NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
|
|
|
|
|
2015-03-24 06:34:59 +02:00
|
|
|
uint32_t slice_mode_data;
|
|
|
|
uint32_t *slice_offsets;
|
2014-11-30 01:04:37 +02:00
|
|
|
NV_ENC_LOCK_BITSTREAM lock_params = { 0 };
|
|
|
|
NVENCSTATUS nv_status;
|
|
|
|
int res = 0;
|
|
|
|
|
2016-03-08 01:47:56 +02:00
|
|
|
enum AVPictureType pict_type;
|
|
|
|
|
2015-03-24 06:34:59 +02:00
|
|
|
switch (avctx->codec->id) {
|
|
|
|
case AV_CODEC_ID_H264:
|
|
|
|
slice_mode_data = ctx->encode_config.encodeCodecConfig.h264Config.sliceModeData;
|
|
|
|
break;
|
|
|
|
case AV_CODEC_ID_H265:
|
|
|
|
slice_mode_data = ctx->encode_config.encodeCodecConfig.hevcConfig.sliceModeData;
|
|
|
|
break;
|
|
|
|
default:
|
2015-09-11 11:07:10 +02:00
|
|
|
av_log(avctx, AV_LOG_ERROR, "Unknown codec name\n");
|
2015-03-24 06:34:59 +02:00
|
|
|
res = AVERROR(EINVAL);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
slice_offsets = av_mallocz(slice_mode_data * sizeof(*slice_offsets));
|
|
|
|
|
2014-11-30 01:04:37 +02:00
|
|
|
if (!slice_offsets)
|
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
|
|
|
|
lock_params.version = NV_ENC_LOCK_BITSTREAM_VER;
|
|
|
|
|
|
|
|
lock_params.doNotWait = 0;
|
|
|
|
lock_params.outputBitstream = tmpoutsurf->output_surface;
|
|
|
|
lock_params.sliceOffsets = slice_offsets;
|
|
|
|
|
|
|
|
nv_status = p_nvenc->nvEncLockBitstream(ctx->nvencoder, &lock_params);
|
|
|
|
if (nv_status != NV_ENC_SUCCESS) {
|
2016-05-20 17:37:00 +02:00
|
|
|
res = nvenc_print_error(avctx, nv_status, "Failed locking bitstream buffer");
|
2014-11-30 01:04:37 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2015-09-11 11:07:10 +02:00
|
|
|
if (res = ff_alloc_packet2(avctx, pkt, lock_params.bitstreamSizeInBytes,0)) {
|
2014-11-30 01:04:37 +02:00
|
|
|
p_nvenc->nvEncUnlockBitstream(ctx->nvencoder, tmpoutsurf->output_surface);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(pkt->data, lock_params.bitstreamBufferPtr, lock_params.bitstreamSizeInBytes);
|
|
|
|
|
|
|
|
nv_status = p_nvenc->nvEncUnlockBitstream(ctx->nvencoder, tmpoutsurf->output_surface);
|
|
|
|
if (nv_status != NV_ENC_SUCCESS)
|
2016-05-20 17:37:00 +02:00
|
|
|
nvenc_print_error(avctx, nv_status, "Failed unlocking bitstream buffer, expect the gates of mordor to open");
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2016-05-21 00:08:06 +02:00
|
|
|
|
|
|
|
if (avctx->pix_fmt == AV_PIX_FMT_CUDA) {
|
|
|
|
p_nvenc->nvEncUnmapInputResource(ctx->nvencoder, tmpoutsurf->in_map.mappedResource);
|
|
|
|
av_frame_unref(tmpoutsurf->in_ref);
|
|
|
|
ctx->registered_frames[tmpoutsurf->reg_idx].mapped = 0;
|
|
|
|
|
|
|
|
tmpoutsurf->input_surface = NULL;
|
|
|
|
}
|
|
|
|
|
2014-11-30 01:04:37 +02:00
|
|
|
switch (lock_params.pictureType) {
|
|
|
|
case NV_ENC_PIC_TYPE_IDR:
|
|
|
|
pkt->flags |= AV_PKT_FLAG_KEY;
|
|
|
|
case NV_ENC_PIC_TYPE_I:
|
2016-03-08 01:47:56 +02:00
|
|
|
pict_type = AV_PICTURE_TYPE_I;
|
2014-11-30 01:04:37 +02:00
|
|
|
break;
|
|
|
|
case NV_ENC_PIC_TYPE_P:
|
2016-03-08 01:47:56 +02:00
|
|
|
pict_type = AV_PICTURE_TYPE_P;
|
2014-11-30 01:04:37 +02:00
|
|
|
break;
|
|
|
|
case NV_ENC_PIC_TYPE_B:
|
2016-03-08 01:47:56 +02:00
|
|
|
pict_type = AV_PICTURE_TYPE_B;
|
2014-11-30 01:04:37 +02:00
|
|
|
break;
|
|
|
|
case NV_ENC_PIC_TYPE_BI:
|
2016-03-08 01:47:56 +02:00
|
|
|
pict_type = AV_PICTURE_TYPE_BI;
|
2014-11-30 01:04:37 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Unknown picture type encountered, expect the output to be broken.\n");
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Please report this error and include as much information on how to reproduce it as possible.\n");
|
|
|
|
res = AVERROR_EXTERNAL;
|
|
|
|
goto error;
|
2016-03-08 01:47:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if FF_API_CODED_FRAME
|
|
|
|
FF_DISABLE_DEPRECATION_WARNINGS
|
|
|
|
avctx->coded_frame->pict_type = pict_type;
|
2015-07-15 19:41:22 +02:00
|
|
|
FF_ENABLE_DEPRECATION_WARNINGS
|
|
|
|
#endif
|
2016-03-08 01:47:56 +02:00
|
|
|
|
|
|
|
ff_side_data_set_encoder_stats(pkt,
|
|
|
|
(lock_params.frameAvgQP - 1) * FF_QP2LAMBDA, NULL, 0, pict_type);
|
2014-11-30 01:04:37 +02:00
|
|
|
|
|
|
|
pkt->pts = lock_params.outputTimeStamp;
|
2016-05-20 18:13:20 +02:00
|
|
|
pkt->dts = timestamp_queue_dequeue(ctx->timestamp_list);
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2015-01-26 14:28:21 +02:00
|
|
|
/* when there're b frame(s), set dts offset */
|
2015-01-07 11:19:32 +02:00
|
|
|
if (ctx->encode_config.frameIntervalP >= 2)
|
|
|
|
pkt->dts -= 1;
|
|
|
|
|
2014-11-30 01:04:37 +02:00
|
|
|
if (pkt->dts > pkt->pts)
|
|
|
|
pkt->dts = pkt->pts;
|
|
|
|
|
|
|
|
if (ctx->last_dts != AV_NOPTS_VALUE && pkt->dts <= ctx->last_dts)
|
|
|
|
pkt->dts = ctx->last_dts + 1;
|
|
|
|
|
|
|
|
ctx->last_dts = pkt->dts;
|
|
|
|
|
|
|
|
av_free(slice_offsets);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
|
|
|
av_free(slice_offsets);
|
2016-05-20 18:13:20 +02:00
|
|
|
timestamp_queue_dequeue(ctx->timestamp_list);
|
2014-11-30 01:04:37 +02:00
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2016-05-20 18:13:20 +02:00
|
|
|
static int output_ready(NvencContext *ctx, int flush)
|
|
|
|
{
|
|
|
|
int nb_ready, nb_pending;
|
|
|
|
|
|
|
|
nb_ready = av_fifo_size(ctx->output_surface_ready_queue) / sizeof(NvencSurface*);
|
|
|
|
nb_pending = av_fifo_size(ctx->output_surface_queue) / sizeof(NvencSurface*);
|
|
|
|
return nb_ready > 0 && (flush || nb_ready + nb_pending >= ctx->buffer_delay);
|
|
|
|
}
|
|
|
|
|
2016-05-25 15:04:28 +02:00
|
|
|
int ff_nvenc_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
|
2014-11-30 01:04:37 +02:00
|
|
|
const AVFrame *frame, int *got_packet)
|
|
|
|
{
|
|
|
|
NVENCSTATUS nv_status;
|
2016-05-20 17:37:00 +02:00
|
|
|
NvencSurface *tmpoutsurf, *inSurf;
|
|
|
|
int res;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
|
|
|
NvencContext *ctx = avctx->priv_data;
|
|
|
|
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
|
|
|
|
NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
|
|
|
|
|
|
|
|
NV_ENC_PIC_PARAMS pic_params = { 0 };
|
|
|
|
pic_params.version = NV_ENC_PIC_PARAMS_VER;
|
|
|
|
|
|
|
|
if (frame) {
|
2016-05-20 16:49:24 +02:00
|
|
|
inSurf = get_free_frame(ctx);
|
2014-11-30 01:04:37 +02:00
|
|
|
av_assert0(inSurf);
|
|
|
|
|
2016-05-20 16:49:24 +02:00
|
|
|
res = nvenc_upload_frame(avctx, frame, inSurf);
|
|
|
|
if (res) {
|
|
|
|
inSurf->lockCount = 0;
|
|
|
|
return res;
|
2014-11-30 01:04:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pic_params.inputBuffer = inSurf->input_surface;
|
|
|
|
pic_params.bufferFmt = inSurf->format;
|
|
|
|
pic_params.inputWidth = avctx->width;
|
|
|
|
pic_params.inputHeight = avctx->height;
|
2016-05-20 17:37:00 +02:00
|
|
|
pic_params.outputBitstream = inSurf->output_surface;
|
2014-11-30 01:04:37 +02:00
|
|
|
pic_params.completionEvent = 0;
|
|
|
|
|
2015-06-29 21:59:37 +02:00
|
|
|
if (avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT) {
|
2014-11-30 01:04:37 +02:00
|
|
|
if (frame->top_field_first) {
|
|
|
|
pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FIELD_TOP_BOTTOM;
|
|
|
|
} else {
|
|
|
|
pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FIELD_BOTTOM_TOP;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FRAME;
|
|
|
|
}
|
|
|
|
|
|
|
|
pic_params.encodePicFlags = 0;
|
|
|
|
pic_params.inputTimeStamp = frame->pts;
|
|
|
|
pic_params.inputDuration = 0;
|
2016-05-20 16:49:24 +02:00
|
|
|
|
|
|
|
nvenc_codec_specific_pic_params(avctx, &pic_params);
|
2015-01-16 02:02:40 +02:00
|
|
|
|
2016-05-20 18:13:20 +02:00
|
|
|
timestamp_queue_enqueue(ctx->timestamp_list, frame->pts);
|
2014-11-30 01:04:37 +02:00
|
|
|
} else {
|
|
|
|
pic_params.encodePicFlags = NV_ENC_PIC_FLAG_EOS;
|
|
|
|
}
|
|
|
|
|
|
|
|
nv_status = p_nvenc->nvEncEncodePicture(ctx->nvencoder, &pic_params);
|
|
|
|
|
2016-05-20 18:13:20 +02:00
|
|
|
if (frame && nv_status == NV_ENC_ERR_NEED_MORE_INPUT)
|
|
|
|
av_fifo_generic_write(ctx->output_surface_queue, &inSurf, sizeof(inSurf), NULL);
|
2014-11-30 01:04:37 +02:00
|
|
|
|
|
|
|
if (nv_status != NV_ENC_SUCCESS && nv_status != NV_ENC_ERR_NEED_MORE_INPUT) {
|
2016-05-20 17:37:00 +02:00
|
|
|
return nvenc_print_error(avctx, nv_status, "EncodePicture failed!");
|
2014-11-30 01:04:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (nv_status != NV_ENC_ERR_NEED_MORE_INPUT) {
|
2016-05-20 18:13:20 +02:00
|
|
|
while (av_fifo_size(ctx->output_surface_queue) > 0) {
|
|
|
|
av_fifo_generic_read(ctx->output_surface_queue, &tmpoutsurf, sizeof(tmpoutsurf), NULL);
|
|
|
|
av_fifo_generic_write(ctx->output_surface_ready_queue, &tmpoutsurf, sizeof(tmpoutsurf), NULL);
|
2014-11-30 01:04:37 +02:00
|
|
|
}
|
|
|
|
|
2016-05-20 18:13:20 +02:00
|
|
|
if (frame)
|
|
|
|
av_fifo_generic_write(ctx->output_surface_ready_queue, &inSurf, sizeof(inSurf), NULL);
|
2014-11-30 01:04:37 +02:00
|
|
|
}
|
|
|
|
|
2016-05-20 18:13:20 +02:00
|
|
|
if (output_ready(ctx, !frame)) {
|
|
|
|
av_fifo_generic_read(ctx->output_surface_ready_queue, &tmpoutsurf, sizeof(tmpoutsurf), NULL);
|
2014-11-30 01:04:37 +02:00
|
|
|
|
2015-07-25 23:26:42 +02:00
|
|
|
res = process_output_surface(avctx, pkt, tmpoutsurf);
|
2014-11-30 01:04:37 +02:00
|
|
|
|
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
|
2016-05-20 17:37:00 +02:00
|
|
|
av_assert0(tmpoutsurf->lockCount);
|
|
|
|
tmpoutsurf->lockCount--;
|
2014-11-30 01:04:37 +02:00
|
|
|
|
|
|
|
*got_packet = 1;
|
|
|
|
} else {
|
|
|
|
*got_packet = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|