785 lines
29 KiB
C
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, ©_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
|
||
|
|
)
|