Files
2026-08-24 10:03:24 +08:00

785 lines
29 KiB
C

/*
* Copyright (C) 2020 a.wlodarczyk <a.wlodarczyk@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Alternatively, the contents of this file may be used under the
* GNU Lesser General Public License Version 2.1 (the "LGPL"), in
* which case the following provisions apply instead of the ones
* mentioned above:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <gst/allocators/gstdmabuf.h>
#include <gst/allocators/gstsecmemallocator.h>
#include "gstsechwenc.h"
GST_DEBUG_CATEGORY_STATIC (gst_sec_hwenc_debug);
#define GST_CAT_DEFAULT gst_sec_hwenc_debug
/* enc signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_BITRATE,
PROP_GOP
};
// encode input fmt 1:nv12 2:nv21 3:yv12 4:rgb888 5:bgr888
enum
{
GST_SECHWENC_FMT_NONE,
GST_SECHWENC_FMT_NV12,
GST_SECHWENC_FMT_NV21,
GST_SECHWENC_FMT_YV12,
GST_SECHWENC_FMT_RGB888,
GST_SECHWENC_FMT_BGR888,
GST_SECHWENC_FMT_UNSET
};
#define PARAM_UNSET (-1)
#define PROP_BITRATE_DEFAULT (4000000)
#define PROP_GOP_DEFAULT (5)
static GstStaticPadTemplate gst_sec_hwenc_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ I420, YV12, NV12, NV21, RGB, BGR }"))
);
static GstStaticPadTemplate gst_sec_hwenc_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-h264, " "profile = (string) {constrained-baseline, baseline, main}")
);
static guint gst_sec_hwenc_fif_signal;
#define gst_sec_hwenc_parent_class parent_class
G_DEFINE_TYPE (GstSecHwEnc, gst_sec_hwenc, GST_TYPE_VIDEO_ENCODER);
static void gst_sec_hwenc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_sec_hwenc_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static GstFlowReturn gst_sec_hwenc_handle_frame (GstVideoEncoder * video_enc, GstVideoCodecFrame * frame);
gboolean gst_sec_hwenc_close(GstVideoEncoder * video_enc);
static gboolean gst_sec_hwenc_set_format (GstVideoEncoder * video_enc, GstVideoCodecState * state);
static void print_encoder_params( GstSecHwEncParam *params, const char *params_name );
static void set_param_from_caps(GstVideoInfo *info, GstSecHwEncParam *param);
static void gst_sec_hwenc_force_i_frame(GstSecHwEnc *enc);
static gint gst_sec_hwenc_gst_to_vl_video_format(GstVideoFormat format);
/* For output buffer size it is assumed that there would be output with no compression,
1080 resolution, and 4:2:0 format. Those assumptions produce: 1.5 x 1920 x 1080 = 3 110 400.
Size calculated above eventually rounded up to 2MB constitute an output buffer size */
#define OUTPUT_BUFFER_SIZE ( 2 * 1024 * 1024 )
/* Recommended Quantization parameters */
#define I_QUANTIZATION_PARAMETER_MIN (0)
#define I_QUANTIZATION_PARAMETER_MAX (51)
#define P_QUANTIZATION_PARAMETER_MIN (0)
#define P_QUANTIZATION_PARAMETER_MAX (51)
/* GObject vmethod implementations */
/* initialize the sec hw encoder's class */
static void
gst_sec_hwenc_class_init (GstSecHwEncClass * klass)
{
GST_INFO("%s - following argument was passed : %p ",__func__,klass);
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstVideoEncoderClass *gstvienc_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstvienc_class = (GstVideoEncoderClass *) klass;
gobject_class->set_property = gst_sec_hwenc_set_property;
gobject_class->get_property = gst_sec_hwenc_get_property;
g_object_class_install_property (gobject_class, PROP_BITRATE,
g_param_spec_uint ("bitrate", "br", "rate of encoding quality",
1, G_MAXUINT, PROP_BITRATE_DEFAULT, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (gobject_class, PROP_GOP,
g_param_spec_uint ("gop", "gop", "length of group of picture in frames",
0, G_MAXUINT, PROP_GOP_DEFAULT, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
gstvienc_class->handle_frame = gst_sec_hwenc_handle_frame;
gstvienc_class->close = gst_sec_hwenc_close;
gstvienc_class->set_format = gst_sec_hwenc_set_format;
gst_element_class_set_static_metadata (gstelement_class,
"Sec h264 video encoder",
"Codec/Encoder/Video",
"Encode video streams from raw to h264 format",
"Arkadiusz Wlodarczyk <a.wlodarczyk@samsung.com>");
gst_element_class_add_static_pad_template (gstelement_class,
&gst_sec_hwenc_src_template);
gst_element_class_add_static_pad_template (gstelement_class,
&gst_sec_hwenc_sink_template);
/* install force-i-frame signal */
gst_sec_hwenc_fif_signal =
g_signal_new("force-i-frame",
G_TYPE_FROM_CLASS(klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET(GstSecHwEncClass, force_i_frame),
NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
klass->force_i_frame = gst_sec_hwenc_force_i_frame;
}
/* force-i-frame signal handler */
static void
gst_sec_hwenc_force_i_frame(GstSecHwEnc *enc)
{
GST_DEBUG_OBJECT(enc, "force-i-frame signal received");
enc->request_fif = 1;
}
/* initialize the new element
* instantiate pads and add them to element
* set pad calback functions
* initialize instance structure
*/
static void
gst_sec_hwenc_init (GstSecHwEnc * enc)
{
GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_ENCODER_SINK_PAD (enc));
GstSecHwEncParam *params = &enc->vl_enc_param;
enc->vl_output_buffer = g_malloc(OUTPUT_BUFFER_SIZE);
if (NULL == enc->vl_output_buffer)
GST_ERROR("Output buffer allocation failed ( requested size: %u )", OUTPUT_BUFFER_SIZE);
enc->vl_enc_handle = 0;
enc->request_fif = 0;
params->width = PARAM_UNSET;
params->height = PARAM_UNSET;
params->framerate = PARAM_UNSET;
params->bitrate = PROP_BITRATE_DEFAULT;
params->gop = PROP_GOP_DEFAULT;
params->fmt = GST_SECHWENC_FMT_UNSET; // fmt : encode input fmt 0:nv12,nv21 1:yv12 2:rgb888 3:bgr888
enc->secure_en = FALSE;
enc->allocator = NULL;
}
static void
gst_sec_hwenc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstSecHwEnc *enc = GST_SECHWENC (object);
switch (prop_id) {
case PROP_BITRATE:
enc->vl_enc_param.bitrate = g_value_get_uint (value);
break;
case PROP_GOP:
enc->vl_enc_param.gop = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_sec_hwenc_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstSecHwEnc *enc = GST_SECHWENC (object);
switch (prop_id) {
case PROP_BITRATE:
g_value_set_uint(value, enc->vl_enc_param.bitrate);
break;
case PROP_GOP:
g_value_set_uint(value, enc->vl_enc_param.gop);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/* entry point to initialize the plug-in
* initialize the plug-in itself
* register the element factories and other features
*/
static gboolean
sec_hwenc_init (GstPlugin * plugin)
{
GST_DEBUG_CATEGORY_INIT (gst_sec_hwenc_debug, "sec_h264hwenc",
0, "hardware based h264 encoder for mezon board");
return gst_element_register (plugin, "sec_h264hwenc", GST_RANK_NONE,
GST_TYPE_SECHWENC);
}
gboolean gst_sec_hwenc_close(GstVideoEncoder * video_enc)
{
int rv;
GstSecHwEnc *encoder = GST_SECHWENC(video_enc);
GST_LOG("In %s called with for: video_enc : %p", __func__, video_enc);
if (encoder->vl_enc_handle) {
rv = vl_video_encoder_destroy(encoder->vl_enc_handle);
if (1 != rv)
GST_ERROR("%s failed - following arguments were passed: ( handle: %ld )", "vl_video_encoder_destroy", encoder->vl_enc_handle);
}
if (encoder->vl_output_buffer)
g_free(encoder->vl_output_buffer);
if ( encoder->input_state )
gst_video_codec_state_unref(encoder->input_state);
if (encoder->input_state)
encoder->input_state = NULL;
if (encoder->allocator) {
gst_object_unref(encoder->allocator);
}
return TRUE;
}
static gboolean gst_sec_secmem_copybyhandle(GstMemory *mem, GstMemory *src_mem, SEC_CopyIndex *copy_array, uint32_t num_indexes)
{
int i = 0;
uint32_t dst_offset[num_indexes];
uint32_t src_offset[num_indexes];
uint32_t size[num_indexes];
uint32_t src_handle;
if (NULL == mem || NULL == src_mem) {
GST_ERROR("Null pointer arg encountered\n");
return FALSE;
}
/* overflow check */
for (i = 0; i < num_indexes; i++) {
if (copy_array[i].offset_in_target + copy_array[i].bytes_to_copy > mem->size) {
GST_ERROR("attempt to write beyond opaque buffer boundary in %zd\n", i);
goto error;
}
}
src_handle = gst_secmem_memory_get_handle(src_mem);
for (i = 0; i < num_indexes; i++) {
dst_offset[i] = copy_array[i].offset_in_target;
src_offset[i] = copy_array[i].offset_in_src;
size[i] = copy_array[i].bytes_to_copy;
}
return gst_secmem_copybyhandle_without_release(mem, src_handle, num_indexes, dst_offset, src_offset, size);//_底层有free_
error:
return FALSE;
}
static GstFlowReturn gst_sec_hwenc_handle_frame (GstVideoEncoder * video_enc, GstVideoCodecFrame * frame)
{
GstVideoFrame vframe;
GstSecHwEnc *encoder = GST_SECHWENC (video_enc);
GstVideoInfo *info = &encoder->input_state->info;
int datalen = 0;
int headerlen = 0;
int rv = 0;
guint8 ui1_plane_num = 1;
vl_dma_info_t dma_info;
int buf_type = 0;
GstFlowReturn flowrv = GST_FLOW_OK;
GstBuffer *out_buf = NULL;
vl_frame_info_t frame_info;
vl_frame_type_t frame_type = FRAME_TYPE_NONE;
vl_enc_result_e result = ENC_FAILED;
GstBuffer *outbuf_temp = NULL;
GstBuffer *outbuf_es = NULL;
GstBuffer *outbuf_header = NULL;
int fd_es = 0;
GST_LOG("In %s called with following arguments: video_enc : %p, frame : %p", __func__, video_enc, frame);
g_assert(encoder->vl_enc_handle); // program logic shall asure that one can get here only with initialized encoder
if (NULL == encoder->vl_output_buffer)
{
GST_ERROR("Output buffer not allocated");
flowrv = GST_FLOW_ERROR;
goto exceptional_out;
}
GstMemory *memory = gst_buffer_get_memory(frame->input_buffer, 0);
gboolean is_dmabuf = gst_is_dmabuf_memory(memory);
GST_LOG("is_dmabuf[%d] width[%d] height[%d]",is_dmabuf,info->width,info->height);
memset(&frame_info, 0, sizeof(frame_info));
memset(&dma_info, 0, sizeof(dma_info));
frame_info.frame_type = FRAME_TYPE_AUTO;
frame_info.frame_size = encoder->vl_enc_param.in_size;
frame_info.fmt = (vl_img_format_t)encoder->vl_enc_param.fmt;
frame_info.pitch = encoder->vl_enc_param.width;
frame_info.height = encoder->vl_enc_param.height;
dma_info.num_planes = 0;
dma_info.shared_fd[0] = -1;
dma_info.shared_fd[1] = -1;
dma_info.shared_fd[2] = -1;
if (is_dmabuf) {
encoder->fd[0] = gst_dmabuf_memory_get_fd(memory);
gst_memory_unref(memory);
ui1_plane_num = gst_buffer_n_memory(frame->input_buffer);
if (ui1_plane_num > 1) {
GstMemory *memory_u = gst_buffer_get_memory(frame->input_buffer, 1);
if (gst_is_dmabuf_memory(memory_u)) {
encoder->fd[1] = gst_dmabuf_memory_get_fd(memory_u);
gst_memory_unref(memory_u);
}
}
if (ui1_plane_num > 2) {
GstMemory *memory_v = gst_buffer_get_memory(frame->input_buffer, 2);
if (gst_is_dmabuf_memory(memory_v)) {
encoder->fd[2] = gst_dmabuf_memory_get_fd(memory_v);
gst_memory_unref(memory_v);
}
}
dma_info.shared_fd[0] = encoder->fd[0];
dma_info.shared_fd[1] = encoder->fd[1];
dma_info.shared_fd[2] = encoder->fd[2];
dma_info.num_planes = ui1_plane_num;
frame_info.type = DMA_BUFF_TYPE;
frame_info.plane_num = dma_info.num_planes;
frame_info.YCbCr[0] = dma_info.shared_fd[0];
frame_info.YCbCr[1] = dma_info.shared_fd[1];
frame_info.YCbCr[2] = dma_info.shared_fd[2];
GST_LOG("Set DMA buffer planes %d fd[%d, %d, %d]",
dma_info.num_planes, dma_info.shared_fd[0],
dma_info.shared_fd[1], dma_info.shared_fd[2]);
} else {
gst_memory_unref(memory);
rv = gst_video_frame_map (&vframe, info, frame->input_buffer, GST_MAP_READ);
GST_LOG("The %s returned: %d", "gst_video_frame_map", rv);
if (!rv)
{
GST_ERROR("%s failed - following arguments were passed: ( %p, %p, %p, %d )", "gst_video_frame_map", &vframe, info, frame->input_buffer, GST_MAP_READ);
flowrv = GST_FLOW_ERROR;
goto exceptional_out;
}
guint8 *pixel = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0);
frame_info.YCbCr[0] = (ulong) (pixel);
switch (GST_VIDEO_INFO_FORMAT(info)) {
case GST_VIDEO_FORMAT_NV12:
case GST_VIDEO_FORMAT_NV21:
pixel = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 1);
frame_info.YCbCr[1] = (ulong) (pixel);
break;
case GST_VIDEO_FORMAT_I420:
case GST_VIDEO_FORMAT_YV12:
pixel = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 1);
frame_info.YCbCr[1] = (ulong) (pixel);
pixel = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 2);
frame_info.YCbCr[2] = (ulong) (pixel);
break;
default: //RGB
break;
}
frame_info.type = VMALLOC_BUFFER_TYPE;
}
if (encoder->request_fif)
{
GST_LOG("force I-frame request detected and will be passed to encoder");
frame_info.frame_type = FRAME_TYPE_I;
encoder->request_fif = 0;
}
if (encoder->secure_en)
{
outbuf_temp = gst_buffer_new_allocate(encoder->allocator, OUTPUT_BUFFER_SIZE, NULL);
g_return_val_if_fail(outbuf_temp != NULL, GST_FLOW_ERROR);
GstMemory *mem = gst_buffer_peek_memory(outbuf_temp, 0);
g_return_val_if_fail(gst_is_secmem_memory(mem), GST_FLOW_ERROR);
fd_es = gst_fd_memory_get_fd(mem);
frame_info.secure_en = 1;
frame_info.secure_fd = fd_es;
}
frame_info.bitrate = encoder->vl_enc_param.bitrate;
frame_info.frame_rate = encoder->vl_enc_param.framerate * 1000;
result = vl_video_encoder_encode_frame(encoder->vl_enc_handle,
frame_info,
encoder->vl_output_buffer,
&datalen,
&headerlen,
&frame_type);/*get frame_type*/
GST_LOG( "%s returned : %d - following arguments were passed: ( handle: %ld, type: %d, in : 0x%lx, in_size : %d, out : %p, img_format : %d )",
"vl_video_encoder_encode", datalen,
encoder->vl_enc_handle,
frame_info.frame_type,
frame_info.YCbCr[0],
encoder->vl_enc_param.in_size,
encoder->vl_output_buffer,
encoder->vl_enc_param.fmt );
if (!is_dmabuf)
gst_video_frame_unmap(&vframe);
if (result == ENC_FAILED)
{
flowrv = GST_FLOW_ERROR;
goto exceptional_out;
}
if (encoder->secure_en == 0)
{
if (0 == datalen)
{
static unsigned int cnt;
frame->output_buffer = NULL;
++cnt;
GST_LOG("%s returned no data %d %s", "vl_video_encoder_encode", cnt, cnt == 1 ? "time" : "times");
flowrv = GST_FLOW_OK; // frames not always appears at encoder output, so this situation shall not stop streaming
goto exceptional_out;
}
out_buf = gst_buffer_new_allocate (NULL, datalen, NULL);
GST_LOG("Output buffer allocation: %s returned : %p - following arguments were passed: ( allocator: %p, size: %u, params: %p )", "gst_buffer_new_allocate", out_buf, NULL, datalen, NULL);
g_assert( out_buf );
rv = gst_buffer_fill(out_buf, 0, encoder->vl_output_buffer, datalen);
g_assert(rv == datalen);
frame->output_buffer = out_buf;
}
else {
/*secure mode requires sending headers*/
if (frame_type == FRAME_TYPE_IDR) {
if (0 != headerlen)
{
outbuf_header = gst_buffer_new_allocate(NULL, headerlen, NULL);
g_return_val_if_fail(outbuf_header != NULL, GST_FLOW_ERROR);
rv = gst_buffer_fill(outbuf_header, 0, encoder->vl_output_buffer, headerlen);
g_assert(rv == headerlen);
outbuf_header = gst_buffer_make_writable (outbuf_header);
GST_BUFFER_FLAG_SET (outbuf_header, GST_BUFFER_FLAG_HEADER);
gst_pad_push (video_enc->srcpad, gst_buffer_ref (outbuf_header));
gst_buffer_unref (outbuf_header);
}
}
GstMemory *mem_es;
outbuf_es = gst_buffer_new_allocate(encoder->allocator, datalen, NULL);
g_return_val_if_fail(outbuf_es != NULL, GST_FLOW_ERROR);
mem_es = gst_buffer_peek_memory(outbuf_es, 0);
g_return_val_if_fail(gst_is_secmem_memory(mem_es), GST_FLOW_ERROR);
GstMemory *src_mem = gst_buffer_peek_memory(outbuf_temp, 0);
g_return_val_if_fail(gst_is_secmem_memory(src_mem), GST_FLOW_ERROR);
SEC_CopyIndex copy_array;
copy_array.offset_in_src = 0;
copy_array.offset_in_target = 0;
copy_array.bytes_to_copy = datalen;
if (!gst_sec_secmem_copybyhandle(mem_es, src_mem, &copy_array, 1)) {
gst_buffer_unref(outbuf_temp);//gst_sec_secmem_copybyhandle()底层有free, 待改
gst_buffer_unref(outbuf_es);
flowrv = GST_FLOW_ERROR;
goto exceptional_out;
}
frame->output_buffer = outbuf_es;
gst_buffer_unref(outbuf_temp);//gst_sec_secmem_copybyhandle()底层有free, 待改
}
/*
During encoder raw yuv file,and frame have no pts.
so need fill it in order to avoid mux plugin fail.
*/
if ((GST_CLOCK_TIME_NONE == GST_BUFFER_TIMESTAMP (frame->input_buffer))
&& info->fps_n && info->fps_d) {
GST_LOG ("add for add pts end[%d] [%d]",info->fps_n,info->fps_d);
GST_BUFFER_TIMESTAMP (frame->input_buffer) = gst_util_uint64_scale (encoder->pts_cnt++, GST_SECOND*info->fps_d, info->fps_n);
GST_BUFFER_DURATION (frame->input_buffer) = gst_util_uint64_scale (1, GST_SECOND*info->fps_d, info->fps_n);
frame->pts = GST_BUFFER_TIMESTAMP (frame->input_buffer);
//FIXME later for first_pts_index
if (encoder->pts_cnt == PTS_UINT_4_RESET) {
GST_DEBUG_OBJECT (encoder, "PTS rollback");
encoder->pts_cnt = 0;
}
}
frame->dts = frame->pts;
GST_LOG ("output: dts %" G_GINT64_FORMAT " pts %" G_GINT64_FORMAT,
(gint64) frame->dts, (gint64) frame->pts);
if (frame_type == FRAME_TYPE_IDR) {
GST_DEBUG_OBJECT (encoder, "Output keyframe");
GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
} else {
GST_VIDEO_CODEC_FRAME_UNSET_SYNC_POINT (frame);
}
flowrv = gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (encoder), frame);
GST_LOG("%s returned : %d - following arguments were passed: ( encoder: %p, frame: %p ) ", "gst_video_encoder_finish_frame", flowrv, GST_VIDEO_ENCODER(encoder), frame);
return flowrv;
exceptional_out:
frame->output_buffer = NULL;
gst_video_encoder_finish_frame(GST_VIDEO_ENCODER(encoder), frame);
return flowrv;
}
static gint
gst_sec_hwenc_gst_to_vl_video_format( GstVideoFormat format )
{
switch (format) {
case GST_VIDEO_FORMAT_I420:
case GST_VIDEO_FORMAT_YV12:
return GST_SECHWENC_FMT_YV12;
case GST_VIDEO_FORMAT_NV12:
return GST_SECHWENC_FMT_NV12;
case GST_VIDEO_FORMAT_NV21:
return GST_SECHWENC_FMT_NV21;
case GST_VIDEO_FORMAT_RGB:
return GST_SECHWENC_FMT_RGB888;
case GST_VIDEO_FORMAT_BGR:
return GST_SECHWENC_FMT_BGR888;
default:
g_return_val_if_reached(GST_SECHWENC_FMT_UNSET);
}
}
static void set_param_from_caps( GstVideoInfo *info, GstSecHwEncParam *param)
{
GstVideoFormat caps_format = info->finfo->format;
param->codec_id = CODEC_ID_H264;
param->width = info->width;
param->height = info->height;
param->framerate = info->fps_n / info->fps_d;
param->fmt = gst_sec_hwenc_gst_to_vl_video_format( caps_format );
param->in_size = info->size;
}
static gboolean check_params(GstSecHwEncParam *param)
{
return (param->width != PARAM_UNSET &&
param->height != PARAM_UNSET &&
param->framerate != PARAM_UNSET &&
param->fmt != GST_SECHWENC_FMT_UNSET);
}
static gboolean gst_sec_hwenc_set_format( GstVideoEncoder * video_enc, GstVideoCodecState * state )
{
GstSecHwEnc *encoder = GST_SECHWENC( video_enc );
GstVideoInfo *info = &state->info;
GstCaps *out_caps = NULL;
GstVideoCodecState *nstate;
gboolean reinit = FALSE;
vl_init_params_t init_param;
/* If there is previous encoder handle, than this is change of format, so previous encoder has to be destroyed */
if (0 != encoder->vl_enc_handle) {
reinit = TRUE;
vl_video_encoder_destroy(encoder->vl_enc_handle);
}
if (encoder->input_state)
gst_video_codec_state_unref(encoder->input_state);
encoder->input_state = gst_video_codec_state_ref (state);
GST_DEBUG("received format : finfo: %p, interlace_mode : %d, flags : %d, width : %d, height : %d"
" size : %lu, views : %d, chroma_site : %d, par_n : %d, par_d : %d"
" fps_n : %d, fps_d : %d, offset : %p --> %lu, stride : %p --> %d",
info->finfo,
info->interlace_mode,
info->flags,
info->width,
info->height,
(unsigned long)info->size,
info->views,
(int)info->chroma_site,
info->par_n,
info->par_d,
info->fps_n,
info->fps_d,
info->offset, (unsigned long)*info->offset,
info->stride, *info->stride
);
encoder->vl_enc_param.profile = ENC_AVC_MAIN;
GstStructure *s;
const gchar *profile;
GstCaps *template_caps;
GstCaps *allowed_caps = NULL;
template_caps = gst_static_pad_template_get_caps (&gst_sec_hwenc_src_template);
allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder));
if (allowed_caps && allowed_caps != template_caps) {
GstStructure *s;
const gchar *profile;
if (gst_caps_is_empty (allowed_caps)) {
gst_caps_unref (allowed_caps);
gst_caps_unref (template_caps);
return FALSE;
}
allowed_caps = gst_caps_make_writable (allowed_caps);
allowed_caps = gst_caps_fixate (allowed_caps);
s = gst_caps_get_structure (allowed_caps, 0);
profile = gst_structure_get_string (s, "profile");
if (profile) {
if (!strcmp (profile, "constrained-baseline") ||
!strcmp (profile, "baseline")) {
encoder->vl_enc_param.profile = ENC_AVC_BASELINE;
} else {
encoder->vl_enc_param.profile = ENC_AVC_MAIN;
}
}
if (gst_structure_has_field(s, "secure")) {
gboolean is_secure = FALSE;
gst_structure_get_boolean(s, "secure", &is_secure);
encoder->secure_en = is_secure;
GST_DEBUG("is secure es:%d", encoder->secure_en);
encoder->allocator = gst_secmem_allocator_new(FALSE, FALSE);
g_return_val_if_fail(encoder->allocator != NULL, GST_FLOW_ERROR);
}
gst_caps_unref (allowed_caps);
}
gst_caps_unref (template_caps);
set_param_from_caps( info, &encoder->vl_enc_param );
if (!check_params(&encoder->vl_enc_param))
{
GST_ERROR("Found unset parameter/parameters :");
print_encoder_params(&encoder->vl_enc_param, "encoder->vl_enc_param");
return FALSE;
}
memset(&init_param, 0, sizeof(init_param));
init_param.width = encoder->vl_enc_param.width;
init_param.height = encoder->vl_enc_param.height;
init_param.frame_rate = encoder->vl_enc_param.framerate;
init_param.bit_rate = encoder->vl_enc_param.bitrate;
init_param.gop = encoder->vl_enc_param.gop;
init_param.i_qp_min = I_QUANTIZATION_PARAMETER_MIN;
init_param.i_qp_max = I_QUANTIZATION_PARAMETER_MAX;
init_param.p_qp_min = P_QUANTIZATION_PARAMETER_MIN;
init_param.p_qp_max = P_QUANTIZATION_PARAMETER_MAX;
init_param.profile = encoder->vl_enc_param.profile;
init_param.level = ENC_AVC_LEVEL4;
init_param.secure_en = encoder->secure_en;
encoder->vl_enc_handle = vl_video_encoder_init(
encoder->vl_enc_param.codec_id,
init_param);
GST_LOG("%s returned : %ld - following arguments were passed: ( codec_id : %d, width : %d, height : %d, frame_rate : %d"
"bit_rate : %d, gop : %d, img_format : %d, qp : %d %d %d %d",
"vl_video_encoder_init", encoder->vl_enc_handle,
encoder->vl_enc_param.codec_id,
encoder->vl_enc_param.width,
encoder->vl_enc_param.height,
encoder->vl_enc_param.framerate,
encoder->vl_enc_param.bitrate,
encoder->vl_enc_param.gop,
IMG_FMT_NV12,
I_QUANTIZATION_PARAMETER_MIN, I_QUANTIZATION_PARAMETER_MAX,
P_QUANTIZATION_PARAMETER_MIN, P_QUANTIZATION_PARAMETER_MAX
);
print_encoder_params( &encoder->vl_enc_param, "encoder->vl_enc_param" );
if (!encoder->vl_enc_handle)
{
GST_ERROR("Video encoder not initialized; %s returned NULL","vl_video_encoder_init");
return FALSE;
}
if (!reinit)
{
out_caps = gst_caps_new_simple("video/x-h264", "stream-format", G_TYPE_STRING, "byte-stream", NULL);
nstate = gst_video_encoder_set_output_state(GST_VIDEO_ENCODER(encoder), out_caps, encoder->input_state);
GST_LOG("output caps: %p : %"GST_PTR_FORMAT, state->caps, state->caps);
gst_video_codec_state_unref(nstate);
}
return TRUE;
}
static void print_encoder_params( GstSecHwEncParam *params, const char *params_name )
{
GST_TRACE( "%s params : ( codec_id: %d, width : %d, height : %d, frame_rate : %d, bit_rate : %d"
" gop : %d, img_format : %d, fmt : %d, in_size : %d )",
params_name,
params->codec_id,
params->width,
params->height,
params->framerate,
params->bitrate,
params->gop,
params->input_img_format,
params->fmt,
params->in_size );
}
/* Plugin register information */
#define PACKAGE_VERSION "0.0.3"
#define PACKAGE "gst-sec-hwenc-plugin"
#define GST_LICENSE "LGPL"
#define GST_API_VERSION "1.0"
#define GST_PACKAGE_NAME "GStreamer SecHw Plug-in"
#define GST_PACKAGE_ORIGIN "https://github.sec.samsung.net"
GST_PLUGIN_DEFINE (
GST_VERSION_MAJOR,
GST_VERSION_MINOR,
sec_hwenc,
"Plugin providing hardware based video decoding elements",
sec_hwenc_init,
PACKAGE_VERSION,
GST_LICENSE,
GST_PACKAGE_NAME,
GST_PACKAGE_ORIGIN
)