mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-23 18:21:04 +00:00
nvh265enc: Enable HDR related SEI nal insertion
If upstream provides the HDR related information, create SEI message nals and pass them to NVENC.
This commit is contained in:
parent
b624c6a067
commit
8dbaed0af7
2 changed files with 195 additions and 0 deletions
|
@ -25,6 +25,7 @@
|
||||||
#include "gstnvh265enc.h"
|
#include "gstnvh265enc.h"
|
||||||
|
|
||||||
#include <gst/pbutils/codec-utils.h>
|
#include <gst/pbutils/codec-utils.h>
|
||||||
|
#include <gst/base/gstbytewriter.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -36,6 +37,7 @@ G_DEFINE_TYPE (GstNvH265Enc, gst_nv_h265_enc, GST_TYPE_NV_BASE_ENC);
|
||||||
|
|
||||||
static gboolean gst_nv_h265_enc_open (GstVideoEncoder * enc);
|
static gboolean gst_nv_h265_enc_open (GstVideoEncoder * enc);
|
||||||
static gboolean gst_nv_h265_enc_close (GstVideoEncoder * enc);
|
static gboolean gst_nv_h265_enc_close (GstVideoEncoder * enc);
|
||||||
|
static gboolean gst_nv_h265_enc_stop (GstVideoEncoder * enc);
|
||||||
static gboolean gst_nv_h265_enc_set_src_caps (GstNvBaseEnc * nvenc,
|
static gboolean gst_nv_h265_enc_set_src_caps (GstNvBaseEnc * nvenc,
|
||||||
GstVideoCodecState * state);
|
GstVideoCodecState * state);
|
||||||
static gboolean gst_nv_h265_enc_set_encoder_config (GstNvBaseEnc * nvenc,
|
static gboolean gst_nv_h265_enc_set_encoder_config (GstNvBaseEnc * nvenc,
|
||||||
|
@ -62,6 +64,7 @@ gst_nv_h265_enc_class_init (GstNvH265EncClass * klass)
|
||||||
|
|
||||||
videoenc_class->open = GST_DEBUG_FUNCPTR (gst_nv_h265_enc_open);
|
videoenc_class->open = GST_DEBUG_FUNCPTR (gst_nv_h265_enc_open);
|
||||||
videoenc_class->close = GST_DEBUG_FUNCPTR (gst_nv_h265_enc_close);
|
videoenc_class->close = GST_DEBUG_FUNCPTR (gst_nv_h265_enc_close);
|
||||||
|
videoenc_class->stop = GST_DEBUG_FUNCPTR (gst_nv_h265_enc_stop);
|
||||||
|
|
||||||
nvenc_class->codec_id = NV_ENC_CODEC_HEVC_GUID;
|
nvenc_class->codec_id = NV_ENC_CODEC_HEVC_GUID;
|
||||||
nvenc_class->set_encoder_config = gst_nv_h265_enc_set_encoder_config;
|
nvenc_class->set_encoder_config = gst_nv_h265_enc_set_encoder_config;
|
||||||
|
@ -126,6 +129,32 @@ gst_nv_h265_enc_close (GstVideoEncoder * enc)
|
||||||
return GST_VIDEO_ENCODER_CLASS (gst_nv_h265_enc_parent_class)->close (enc);
|
return GST_VIDEO_ENCODER_CLASS (gst_nv_h265_enc_parent_class)->close (enc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_nv_h265_enc_clear_stream_data (GstNvH265Enc * h265enc)
|
||||||
|
{
|
||||||
|
gint i;
|
||||||
|
|
||||||
|
if (!h265enc->sei_payload)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < h265enc->num_sei_payload; i++)
|
||||||
|
g_free (h265enc->sei_payload[i].payload);
|
||||||
|
|
||||||
|
g_free (h265enc->sei_payload);
|
||||||
|
h265enc->sei_payload = NULL;
|
||||||
|
h265enc->num_sei_payload = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_nv_h265_enc_stop (GstVideoEncoder * enc)
|
||||||
|
{
|
||||||
|
GstNvH265Enc *h265enc = GST_NV_H265_ENC (enc);
|
||||||
|
|
||||||
|
gst_nv_h265_enc_clear_stream_data (h265enc);
|
||||||
|
|
||||||
|
return GST_VIDEO_ENCODER_CLASS (gst_nv_h265_enc_parent_class)->stop (enc);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_nv_h265_enc_set_level_tier_and_profile (GstNvH265Enc * nvenc,
|
gst_nv_h265_enc_set_level_tier_and_profile (GstNvH265Enc * nvenc,
|
||||||
GstCaps * caps)
|
GstCaps * caps)
|
||||||
|
@ -198,6 +227,120 @@ gst_nv_h265_enc_set_src_caps (GstNvBaseEnc * nvenc, GstVideoCodecState * state)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static guint8 *
|
||||||
|
gst_nv_h265_enc_create_mastering_display_sei_nal (GstNvH265Enc * h265enc,
|
||||||
|
GstVideoMasteringDisplayInfo * minfo, guint * size)
|
||||||
|
{
|
||||||
|
guint sei_size;
|
||||||
|
guint16 primary_x[3];
|
||||||
|
guint16 primary_y[3];
|
||||||
|
guint16 white_x, white_y;
|
||||||
|
guint32 max_luma;
|
||||||
|
guint32 min_luma;
|
||||||
|
const guint chroma_scale = 50000;
|
||||||
|
const guint luma_scale = 10000;
|
||||||
|
gint i;
|
||||||
|
GstByteWriter br;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (h265enc, "Apply mastering display info");
|
||||||
|
GST_LOG_OBJECT (h265enc, "\tRed (%u/%u, %u/%u)", minfo->Rx_n,
|
||||||
|
minfo->Rx_d, minfo->Ry_n, minfo->Ry_d);
|
||||||
|
GST_LOG_OBJECT (h265enc, "\tGreen(%u/%u, %u/%u)", minfo->Gx_n,
|
||||||
|
minfo->Gx_d, minfo->Gy_n, minfo->Gy_d);
|
||||||
|
GST_LOG_OBJECT (h265enc, "\tBlue (%u/%u, %u/%u)", minfo->Bx_n,
|
||||||
|
minfo->Bx_d, minfo->By_n, minfo->By_d);
|
||||||
|
GST_LOG_OBJECT (h265enc, "\tWhite(%u/%u, %u/%u)", minfo->Wx_n,
|
||||||
|
minfo->Wx_d, minfo->Wy_n, minfo->Wy_d);
|
||||||
|
GST_LOG_OBJECT (h265enc,
|
||||||
|
"\tmax_luminance:(%u/%u), min_luminance:(%u/%u)",
|
||||||
|
minfo->max_luma_n, minfo->max_luma_d, minfo->min_luma_n,
|
||||||
|
minfo->min_luma_d);
|
||||||
|
|
||||||
|
primary_x[0] =
|
||||||
|
(guint16) gst_util_uint64_scale_round (minfo->Gx_n, chroma_scale,
|
||||||
|
minfo->Gx_d);
|
||||||
|
primary_x[1] =
|
||||||
|
(guint16) gst_util_uint64_scale_round (minfo->Bx_n, chroma_scale,
|
||||||
|
minfo->Bx_d);
|
||||||
|
primary_x[2] =
|
||||||
|
(guint16) gst_util_uint64_scale_round (minfo->Rx_n, chroma_scale,
|
||||||
|
minfo->Rx_d);
|
||||||
|
|
||||||
|
primary_y[0] =
|
||||||
|
(guint16) gst_util_uint64_scale_round (minfo->Gy_n, chroma_scale,
|
||||||
|
minfo->Gy_d);
|
||||||
|
primary_y[1] =
|
||||||
|
(guint16) gst_util_uint64_scale_round (minfo->By_n, chroma_scale,
|
||||||
|
minfo->By_d);
|
||||||
|
primary_y[2] =
|
||||||
|
(guint16) gst_util_uint64_scale_round (minfo->Ry_n, chroma_scale,
|
||||||
|
minfo->Ry_d);
|
||||||
|
|
||||||
|
white_x =
|
||||||
|
(guint16) gst_util_uint64_scale_round (minfo->Wx_n, chroma_scale,
|
||||||
|
minfo->Wx_d);
|
||||||
|
white_y =
|
||||||
|
(guint16) gst_util_uint64_scale_round (minfo->Wy_n, chroma_scale,
|
||||||
|
minfo->Wy_d);
|
||||||
|
max_luma =
|
||||||
|
(guint32) gst_util_uint64_scale_round (minfo->max_luma_n, luma_scale,
|
||||||
|
minfo->max_luma_d);
|
||||||
|
min_luma =
|
||||||
|
(guint32) gst_util_uint64_scale_round (minfo->min_luma_n, luma_scale,
|
||||||
|
minfo->min_luma_d);
|
||||||
|
|
||||||
|
/* x, y 16bits per RGB channel
|
||||||
|
* x, y 16bits white point
|
||||||
|
* max, min luminance 32bits
|
||||||
|
*/
|
||||||
|
sei_size = (2 * 2 * 3) + (2 * 2) + (4 * 2);
|
||||||
|
gst_byte_writer_init_with_size (&br, sei_size, TRUE);
|
||||||
|
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
gst_byte_writer_put_uint16_be (&br, primary_x[i]);
|
||||||
|
gst_byte_writer_put_uint16_be (&br, primary_y[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_byte_writer_put_uint16_be (&br, white_x);
|
||||||
|
gst_byte_writer_put_uint16_be (&br, white_y);
|
||||||
|
|
||||||
|
gst_byte_writer_put_uint32_be (&br, max_luma);
|
||||||
|
gst_byte_writer_put_uint32_be (&br, min_luma);
|
||||||
|
|
||||||
|
*size = sei_size;
|
||||||
|
|
||||||
|
return gst_byte_writer_reset_and_get_data (&br);
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint8 *
|
||||||
|
gst_nv_h265_enc_create_content_light_level_sei_nal (GstNvH265Enc * h265enc,
|
||||||
|
GstVideoContentLightLevel * linfo, guint * size)
|
||||||
|
{
|
||||||
|
gdouble val;
|
||||||
|
guint sei_size;
|
||||||
|
GstByteWriter br;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (h265enc, "Apply content light level");
|
||||||
|
GST_LOG_OBJECT (h265enc, "content light level found");
|
||||||
|
GST_LOG_OBJECT (h265enc,
|
||||||
|
"\tmaxCLL:(%u/%u), maxFALL:(%u/%u)", linfo->maxCLL_n, linfo->maxCLL_d,
|
||||||
|
linfo->maxFALL_n, linfo->maxFALL_d);
|
||||||
|
|
||||||
|
/* maxCLL and maxFALL per 16bits */
|
||||||
|
sei_size = 2 * 2;
|
||||||
|
gst_byte_writer_init_with_size (&br, sei_size, TRUE);
|
||||||
|
|
||||||
|
gst_util_fraction_to_double (linfo->maxCLL_n, linfo->maxCLL_d, &val);
|
||||||
|
gst_byte_writer_put_uint16_be (&br, (guint16) val);
|
||||||
|
|
||||||
|
gst_util_fraction_to_double (linfo->maxFALL_n, linfo->maxFALL_d, &val);
|
||||||
|
gst_byte_writer_put_uint16_be (&br, (guint16) val);
|
||||||
|
|
||||||
|
*size = sei_size;
|
||||||
|
|
||||||
|
return gst_byte_writer_reset_and_get_data (&br);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_nv_h265_enc_set_encoder_config (GstNvBaseEnc * nvenc,
|
gst_nv_h265_enc_set_encoder_config (GstNvBaseEnc * nvenc,
|
||||||
GstVideoCodecState * state, NV_ENC_CONFIG * config)
|
GstVideoCodecState * state, NV_ENC_CONFIG * config)
|
||||||
|
@ -303,6 +446,46 @@ gst_nv_h265_enc_set_encoder_config (GstNvBaseEnc * nvenc,
|
||||||
vui->transferCharacteristics =
|
vui->transferCharacteristics =
|
||||||
gst_video_color_transfer_to_iso (info->colorimetry.transfer);
|
gst_video_color_transfer_to_iso (info->colorimetry.transfer);
|
||||||
|
|
||||||
|
gst_nv_h265_enc_clear_stream_data (h265enc);
|
||||||
|
|
||||||
|
{
|
||||||
|
GstVideoMasteringDisplayInfo minfo;
|
||||||
|
GstVideoContentLightLevel linfo;
|
||||||
|
gboolean have_mastering;
|
||||||
|
gboolean have_cll;
|
||||||
|
guint size;
|
||||||
|
gint i = 0;
|
||||||
|
|
||||||
|
have_mastering =
|
||||||
|
gst_video_mastering_display_info_from_caps (&minfo, state->caps);
|
||||||
|
have_cll = gst_video_content_light_level_from_caps (&linfo, state->caps);
|
||||||
|
|
||||||
|
if (have_mastering)
|
||||||
|
h265enc->num_sei_payload++;
|
||||||
|
if (have_cll)
|
||||||
|
h265enc->num_sei_payload++;
|
||||||
|
|
||||||
|
h265enc->sei_payload =
|
||||||
|
g_new0 (NV_ENC_SEI_PAYLOAD, h265enc->num_sei_payload);
|
||||||
|
|
||||||
|
if (have_mastering) {
|
||||||
|
h265enc->sei_payload[i].payload =
|
||||||
|
gst_nv_h265_enc_create_mastering_display_sei_nal (h265enc,
|
||||||
|
&minfo, &size);
|
||||||
|
h265enc->sei_payload[i].payloadSize = size;
|
||||||
|
h265enc->sei_payload[i].payloadType = 137;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_cll) {
|
||||||
|
h265enc->sei_payload[i].payload =
|
||||||
|
gst_nv_h265_enc_create_content_light_level_sei_nal (h265enc,
|
||||||
|
&linfo, &size);
|
||||||
|
h265enc->sei_payload[i].payloadSize = size;
|
||||||
|
h265enc->sei_payload[i].payloadType = 144;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,10 +493,19 @@ static gboolean
|
||||||
gst_nv_h265_enc_set_pic_params (GstNvBaseEnc * enc, GstVideoCodecFrame * frame,
|
gst_nv_h265_enc_set_pic_params (GstNvBaseEnc * enc, GstVideoCodecFrame * frame,
|
||||||
NV_ENC_PIC_PARAMS * pic_params)
|
NV_ENC_PIC_PARAMS * pic_params)
|
||||||
{
|
{
|
||||||
|
GstNvH265Enc *h265enc = GST_NV_H265_ENC (enc);
|
||||||
|
|
||||||
/* encode whole picture in one single slice */
|
/* encode whole picture in one single slice */
|
||||||
pic_params->codecPicParams.hevcPicParams.sliceMode = 0;
|
pic_params->codecPicParams.hevcPicParams.sliceMode = 0;
|
||||||
pic_params->codecPicParams.hevcPicParams.sliceModeData = 0;
|
pic_params->codecPicParams.hevcPicParams.sliceModeData = 0;
|
||||||
|
|
||||||
|
if (h265enc->sei_payload) {
|
||||||
|
pic_params->codecPicParams.hevcPicParams.seiPayloadArray =
|
||||||
|
h265enc->sei_payload;
|
||||||
|
pic_params->codecPicParams.hevcPicParams.seiPayloadArrayCnt =
|
||||||
|
h265enc->num_sei_payload;
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,9 @@
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
GstNvBaseEnc base_nvenc;
|
GstNvBaseEnc base_nvenc;
|
||||||
|
|
||||||
|
NV_ENC_SEI_PAYLOAD *sei_payload;
|
||||||
|
guint num_sei_payload;
|
||||||
} GstNvH265Enc;
|
} GstNvH265Enc;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
Loading…
Reference in a new issue