1382 lines
45 KiB
C
1382 lines
45 KiB
C
/*
|
|
* Copyright (C) 2014-2019 Amlogic, Inc. All rights reserved.
|
|
*
|
|
* All information contained herein is Amlogic confidential.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:element-amlvenc
|
|
*
|
|
* FIXME:Describe amlvenc here.
|
|
*
|
|
* <refsect2>
|
|
* <title>Example launch line</title>
|
|
* |[
|
|
* gst-launch -v -m fakesrc ! amlvenc ! fakesink silent=TRUE
|
|
* ]|
|
|
* </refsect2>
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <gst/gstdrmbufferpool.h>
|
|
#include <gst/allocators/gstdmabuf.h>
|
|
#include <gst/gst.h>
|
|
#include <gst/pbutils/pbutils.h>
|
|
#include <gst/video/gstvideometa.h>
|
|
#include <gst/video/gstvideopool.h>
|
|
#include <gst/video/gstvideosink.h>
|
|
#include <gst/video/video.h>
|
|
#include <string.h>
|
|
|
|
#include "gstamlvenc.h"
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (gst_amlvenc_debug);
|
|
#define GST_CAT_DEFAULT gst_amlvenc_debug
|
|
|
|
static gboolean
|
|
gst_amlvenc_add_v_chroma_format (GstAmlVEnc *encoder, GstStructure * s)
|
|
{
|
|
GValue fmts = G_VALUE_INIT;
|
|
GValue fmt = G_VALUE_INIT;
|
|
gboolean ret = FALSE;
|
|
|
|
g_value_init (&fmts, GST_TYPE_LIST);
|
|
g_value_init (&fmt, G_TYPE_STRING);
|
|
|
|
g_value_set_string (&fmt, "NV12");
|
|
gst_value_list_append_value (&fmts, &fmt);
|
|
g_value_set_string (&fmt, "NV21");
|
|
gst_value_list_append_value (&fmts, &fmt);
|
|
g_value_set_string (&fmt, "I420");
|
|
gst_value_list_append_value (&fmts, &fmt);
|
|
g_value_set_string (&fmt, "YV12");
|
|
gst_value_list_append_value (&fmts, &fmt);
|
|
g_value_set_string (&fmt, "RGB");
|
|
gst_value_list_append_value (&fmts, &fmt);
|
|
g_value_set_string (&fmt, "BGR");
|
|
gst_value_list_append_value (&fmts, &fmt);
|
|
|
|
if (gst_value_list_get_size (&fmts) != 0) {
|
|
gst_structure_take_value (s, "format", &fmts);
|
|
ret = TRUE;
|
|
} else {
|
|
g_value_unset (&fmts);
|
|
}
|
|
|
|
g_value_unset (&fmt);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define PROP_IDR_PERIOD_DEFAULT 30
|
|
#define PROP_FRAMERATE_DEFAULT 30
|
|
#define PROP_BITRATE_DEFAULT 2000
|
|
#define PROP_BITRATE_MAX 12000
|
|
#define PROP_MIN_BUFFERS_DEFAULT 2
|
|
#define PROP_MAX_BUFFERS_DEFAULT 6
|
|
#define PROP_ENCODER_BUFFER_SIZE_DEFAULT 2048
|
|
#define PROP_ENCODER_BUFFER_SIZE_MIN 1024
|
|
#define PROP_ENCODER_BUFFER_SIZE_MAX 4096
|
|
|
|
#define DRMBP_EXTRA_BUF_SIZE_FOR_DISPLAY 1
|
|
#define DRMBP_LIMIT_MAX_BUFSIZE_TO_BUFSIZE 1
|
|
|
|
#define ENC_ENABLE_ROI_FEATURE 0x1 // Enable RIO feature
|
|
#define ENC_ENABLE_PARA_UPDATE 0x2 // Encode parameter update on the fly
|
|
#define ENC_ENABLE_LONG_TERM_REF 0x80 // Encode long term references feature
|
|
|
|
#define GST_AML_V4L2_CC_IMPORT_QUARK gst_aml_v4l2_buffer_pool_cc_import_quark()
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_GOP,
|
|
PROP_FRAMERATE,
|
|
PROP_BITRATE,
|
|
PROP_MIN_BUFFERS,
|
|
PROP_MAX_BUFFERS,
|
|
PROP_ENCODER_BUFSIZE,
|
|
};
|
|
|
|
#define COMMON_SRC_PADS \
|
|
"framerate = (fraction) [0/1, MAX], " \
|
|
"width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ], " \
|
|
"stream-format = (string) { byte-stream }, " \
|
|
"alignment = (string) au, " \
|
|
"profile = (string) { high-4:4:4, high-4:2:2, high-10, high, main," \
|
|
" baseline, constrained-baseline, high-4:4:4-intra, high-4:2:2-intra," \
|
|
" high-10-intra }"
|
|
|
|
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS ("video/x-h264, "
|
|
COMMON_SRC_PADS "; "
|
|
"video/x-h265, "
|
|
COMMON_SRC_PADS)
|
|
);
|
|
|
|
static GQuark
|
|
gst_aml_v4l2_buffer_pool_cc_import_quark (void)
|
|
{
|
|
static GQuark quark = 0;
|
|
|
|
if (quark == 0)
|
|
quark = g_quark_from_string ("GstAmlV4l2BufferPoolCcUsePtrData");
|
|
|
|
return quark;
|
|
}
|
|
|
|
static void gst_amlvenc_finalize (GObject * object);
|
|
static gboolean gst_amlvenc_start (GstVideoEncoder * encoder);
|
|
static gboolean gst_amlvenc_stop (GstVideoEncoder * encoder);
|
|
static gboolean gst_amlvenc_flush (GstVideoEncoder * encoder);
|
|
|
|
static gboolean gst_amlvenc_init_encoder (GstAmlVEnc * encoder);
|
|
static void gst_amlvenc_close_encoder (GstAmlVEnc * encoder);
|
|
|
|
static GstFlowReturn gst_amlvenc_finish (GstVideoEncoder * encoder);
|
|
static GstFlowReturn gst_amlvenc_handle_frame (GstVideoEncoder * encoder,
|
|
GstVideoCodecFrame * frame);
|
|
static GstFlowReturn gst_amlvenc_encode_frame (GstAmlVEnc * encoder,
|
|
GstVideoCodecFrame * frame);
|
|
static gboolean gst_amlvenc_set_format (GstVideoEncoder * video_enc,
|
|
GstVideoCodecState * state);
|
|
|
|
static gboolean gst_amlvenc_propose_allocation (GstVideoEncoder * encoder,
|
|
GstQuery * query);
|
|
|
|
static void gst_amlvenc_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec);
|
|
static void gst_amlvenc_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec);
|
|
|
|
#define gst_amlvenc_parent_class parent_class
|
|
G_DEFINE_TYPE_WITH_CODE (GstAmlVEnc, gst_amlvenc, GST_TYPE_VIDEO_ENCODER,
|
|
G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL));
|
|
|
|
#if SUPPORT_SCALE
|
|
static gint SRC1_PIXFORMAT = PIXEL_FORMAT_YCrCb_420_SP;
|
|
static gint SRC2_PIXFORMAT = PIXEL_FORMAT_YCrCb_420_SP;
|
|
static gint DST_PIXFORMAT = PIXEL_FORMAT_YCrCb_420_SP;
|
|
|
|
static GE2DOP OP = AML_GE2D_STRETCHBLIT;
|
|
|
|
#define g_align32(a) ((((a)+31)>>5)<<5)
|
|
|
|
static gint do_strechblit(aml_ge2d_info_t* pge2dinfo, GstVideoInfo *info)
|
|
{
|
|
gint ret = -1;
|
|
pge2dinfo->src_info[0].memtype = GE2D_CANVAS_ALLOC;
|
|
pge2dinfo->dst_info.memtype = GE2D_CANVAS_ALLOC;
|
|
pge2dinfo->src_info[0].canvas_w = info->width;
|
|
pge2dinfo->src_info[0].canvas_h = info->height;
|
|
pge2dinfo->src_info[0].format = SRC1_PIXFORMAT;
|
|
|
|
pge2dinfo->dst_info.canvas_w = g_align32(info->width);
|
|
pge2dinfo->dst_info.canvas_h = info->height;
|
|
pge2dinfo->dst_info.format = DST_PIXFORMAT;
|
|
|
|
pge2dinfo->src_info[0].rect.x = 0;
|
|
pge2dinfo->src_info[0].rect.y = 0;
|
|
pge2dinfo->src_info[0].rect.w = info->width;
|
|
pge2dinfo->src_info[0].rect.h = info->height;
|
|
pge2dinfo->dst_info.rect.x = 0;
|
|
pge2dinfo->dst_info.rect.y = 0;
|
|
pge2dinfo->dst_info.rect.w = g_align32(info->width);
|
|
pge2dinfo->dst_info.rect.h = info->height;
|
|
pge2dinfo->dst_info.rotation = GE2D_ROTATION_0;
|
|
ret = aml_ge2d_process(pge2dinfo);
|
|
return ret;
|
|
}
|
|
|
|
static void set_ge2dinfo(aml_ge2d_info_t* pge2dinfo,
|
|
GstVideoInfo *info)
|
|
{
|
|
pge2dinfo->src_info[0].memtype = GE2D_CANVAS_ALLOC;
|
|
pge2dinfo->src_info[0].canvas_w = info->width;
|
|
pge2dinfo->src_info[0].canvas_h = info->height;
|
|
pge2dinfo->src_info[0].format = SRC1_PIXFORMAT;
|
|
pge2dinfo->src_info[1].memtype = GE2D_CANVAS_TYPE_INVALID;
|
|
pge2dinfo->src_info[1].canvas_w = 0;
|
|
pge2dinfo->src_info[1].canvas_h = 0;
|
|
pge2dinfo->src_info[1].format = SRC2_PIXFORMAT;
|
|
pge2dinfo->dst_info.memtype = GE2D_CANVAS_ALLOC;
|
|
pge2dinfo->dst_info.canvas_w = g_align32(info->width);
|
|
pge2dinfo->dst_info.canvas_h = info->height;
|
|
pge2dinfo->dst_info.format = DST_PIXFORMAT;
|
|
pge2dinfo->dst_info.rotation = GE2D_ROTATION_0;
|
|
pge2dinfo->offset = 0;
|
|
pge2dinfo->ge2d_op = OP;
|
|
pge2dinfo->blend_mode = BLEND_MODE_PREMULTIPLIED;
|
|
}
|
|
|
|
static gboolean ge2d_colorFormat(GstVideoFormat vfmt)
|
|
{
|
|
switch (vfmt) {
|
|
case GST_VIDEO_FORMAT_BGR:
|
|
SRC1_PIXFORMAT = PIXEL_FORMAT_BGR_888;
|
|
SRC2_PIXFORMAT = PIXEL_FORMAT_BGR_888;
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static amvenc_img_format_t img_format_convert (GstVideoFormat vfmt)
|
|
{
|
|
amvenc_img_format_t fmt;
|
|
switch (vfmt) {
|
|
case GST_VIDEO_FORMAT_NV12:
|
|
fmt = AML_IMG_FMT_NV12;
|
|
break;
|
|
case GST_VIDEO_FORMAT_NV21:
|
|
fmt = AML_IMG_FMT_NV21;
|
|
break;
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
fmt = AML_IMG_FMT_YUV420P;
|
|
break;
|
|
case GST_VIDEO_FORMAT_RGB:
|
|
fmt = AML_IMG_FMT_RGB888;
|
|
break;
|
|
case GST_VIDEO_FORMAT_BGR:
|
|
fmt = AML_IMG_FMT_NV21;
|
|
break;
|
|
default:
|
|
fmt = AML_IMG_FMT_NONE;
|
|
break;
|
|
}
|
|
return fmt;
|
|
}
|
|
|
|
/* allowed input caps depending on whether libv was built for 8 or 10 bits */
|
|
static GstCaps *
|
|
gst_amlvenc_sink_getcaps (GstVideoEncoder * enc, GstCaps * filter)
|
|
{
|
|
GstCaps *supported_incaps;
|
|
GstCaps *allowed;
|
|
GstCaps *filter_caps, *fcaps;
|
|
gint i, j;
|
|
|
|
supported_incaps =
|
|
gst_pad_get_pad_template_caps (GST_VIDEO_ENCODER_SINK_PAD (enc));
|
|
|
|
/* Allow downstream to specify width/height/framerate/PAR constraints
|
|
* and forward them upstream for video converters to handle
|
|
*/
|
|
allowed = gst_pad_get_allowed_caps (enc->srcpad);
|
|
|
|
if (!allowed || gst_caps_is_empty (allowed) || gst_caps_is_any (allowed)) {
|
|
fcaps = supported_incaps;
|
|
goto done;
|
|
}
|
|
|
|
GST_LOG_OBJECT (enc, "template caps %" GST_PTR_FORMAT, supported_incaps);
|
|
GST_LOG_OBJECT (enc, "allowed caps %" GST_PTR_FORMAT, allowed);
|
|
|
|
filter_caps = gst_caps_new_empty ();
|
|
|
|
for (i = 0; i < gst_caps_get_size (supported_incaps); i++) {
|
|
GQuark q_name =
|
|
gst_structure_get_name_id (gst_caps_get_structure (supported_incaps,
|
|
i));
|
|
|
|
for (j = 0; j < gst_caps_get_size (allowed); j++) {
|
|
const GstStructure *allowed_s = gst_caps_get_structure (allowed, j);
|
|
const GValue *val;
|
|
GstStructure *s;
|
|
const gchar* allowed_mime_name = gst_structure_get_name (allowed_s);
|
|
GstAmlVEnc *venc = GST_AMLVENC (enc);
|
|
|
|
if (!g_strcmp0 (allowed_mime_name, "video/x-h265"))
|
|
{
|
|
venc->codec.id = AML_CODEC_ID_H265;
|
|
} else if (!g_strcmp0 (allowed_mime_name, "video/x-h264")) {
|
|
venc->codec.id = AML_CODEC_ID_H264;
|
|
}
|
|
|
|
s = gst_structure_new_id_empty (q_name);
|
|
if ((val = gst_structure_get_value (allowed_s, "width")))
|
|
gst_structure_set_value (s, "width", val);
|
|
if ((val = gst_structure_get_value (allowed_s, "height")))
|
|
gst_structure_set_value (s, "height", val);
|
|
if ((val = gst_structure_get_value (allowed_s, "framerate")))
|
|
gst_structure_set_value (s, "framerate", val);
|
|
if ((val = gst_structure_get_value (allowed_s, "pixel-aspect-ratio")))
|
|
gst_structure_set_value (s, "pixel-aspect-ratio", val);
|
|
|
|
gst_amlvenc_add_v_chroma_format (venc, s);
|
|
|
|
filter_caps = gst_caps_merge_structure (filter_caps, s);
|
|
}
|
|
}
|
|
|
|
fcaps = gst_caps_intersect (filter_caps, supported_incaps);
|
|
gst_caps_unref (filter_caps);
|
|
gst_caps_unref (supported_incaps);
|
|
|
|
if (filter) {
|
|
GST_LOG_OBJECT (enc, "intersecting with %" GST_PTR_FORMAT, filter);
|
|
filter_caps = gst_caps_intersect (fcaps, filter);
|
|
gst_caps_unref (fcaps);
|
|
fcaps = filter_caps;
|
|
}
|
|
|
|
done:
|
|
gst_caps_replace (&allowed, NULL);
|
|
|
|
GST_LOG_OBJECT (enc, "proxy caps %" GST_PTR_FORMAT, fcaps);
|
|
|
|
return fcaps;
|
|
}
|
|
|
|
static gboolean
|
|
gst_amlvenc_sink_query (GstVideoEncoder * enc, GstQuery * query)
|
|
{
|
|
GstPad *pad = GST_VIDEO_ENCODER_SINK_PAD (enc);
|
|
gboolean ret = FALSE;
|
|
|
|
GST_DEBUG ("Received %s query on sinkpad, %" GST_PTR_FORMAT,
|
|
GST_QUERY_TYPE_NAME (query), query);
|
|
|
|
switch (GST_QUERY_TYPE (query)) {
|
|
case GST_QUERY_ACCEPT_CAPS:{
|
|
GstCaps *acceptable, *caps;
|
|
|
|
acceptable = gst_pad_get_pad_template_caps (pad);
|
|
|
|
gst_query_parse_accept_caps (query, &caps);
|
|
|
|
gst_query_set_accept_caps_result (query,
|
|
gst_caps_is_subset (caps, acceptable));
|
|
gst_caps_unref (acceptable);
|
|
ret = TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
ret = GST_VIDEO_ENCODER_CLASS (parent_class)->sink_query (enc, query);
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
gst_amlvenc_class_init (GstAmlVEncClass * klass)
|
|
{
|
|
GObjectClass *gobject_class;
|
|
GstElementClass *element_class;
|
|
GstVideoEncoderClass *gstencoder_class;
|
|
GstPadTemplate *sink_templ;
|
|
GstCaps *supported_sinkcaps;
|
|
|
|
gobject_class = G_OBJECT_CLASS (klass);
|
|
element_class = GST_ELEMENT_CLASS (klass);
|
|
gstencoder_class = GST_VIDEO_ENCODER_CLASS (klass);
|
|
|
|
gobject_class->set_property = gst_amlvenc_set_property;
|
|
gobject_class->get_property = gst_amlvenc_get_property;
|
|
gobject_class->finalize = gst_amlvenc_finalize;
|
|
|
|
gstencoder_class->set_format = GST_DEBUG_FUNCPTR (gst_amlvenc_set_format);
|
|
gstencoder_class->handle_frame =
|
|
GST_DEBUG_FUNCPTR (gst_amlvenc_handle_frame);
|
|
gstencoder_class->start = GST_DEBUG_FUNCPTR (gst_amlvenc_start);
|
|
gstencoder_class->stop = GST_DEBUG_FUNCPTR (gst_amlvenc_stop);
|
|
gstencoder_class->flush = GST_DEBUG_FUNCPTR (gst_amlvenc_flush);
|
|
gstencoder_class->finish = GST_DEBUG_FUNCPTR (gst_amlvenc_finish);
|
|
gstencoder_class->getcaps = GST_DEBUG_FUNCPTR (gst_amlvenc_sink_getcaps);
|
|
gstencoder_class->propose_allocation =
|
|
GST_DEBUG_FUNCPTR (gst_amlvenc_propose_allocation);
|
|
gstencoder_class->sink_query = GST_DEBUG_FUNCPTR (gst_amlvenc_sink_query);
|
|
|
|
g_object_class_install_property (gobject_class, PROP_GOP,
|
|
g_param_spec_int ("gop", "GOP", "IDR frame refresh interval",
|
|
-1, 1000, PROP_IDR_PERIOD_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_FRAMERATE,
|
|
g_param_spec_int ("framerate", "Framerate", "framerate(fps)",
|
|
0, 30, PROP_FRAMERATE_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_BITRATE,
|
|
g_param_spec_int ("bitrate", "Bitrate", "bitrate(kbps)",
|
|
0, PROP_BITRATE_MAX, PROP_BITRATE_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_MIN_BUFFERS,
|
|
g_param_spec_int ("min-buffers", "Min-Buffers", "min number of input buffer",
|
|
0, 2, PROP_MIN_BUFFERS_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_MAX_BUFFERS,
|
|
g_param_spec_int ("max-buffers", "Max-Buffers", "max number of input buffer",
|
|
3, 10, PROP_MAX_BUFFERS_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_ENCODER_BUFSIZE,
|
|
g_param_spec_int ("encoder-buffer-size", "Encoder-Buffer-Size", "Encoder Buffer Size(KBytes)",
|
|
PROP_ENCODER_BUFFER_SIZE_MIN, PROP_ENCODER_BUFFER_SIZE_MAX, PROP_ENCODER_BUFFER_SIZE_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
gst_element_class_set_static_metadata (element_class,
|
|
"Amlogic h264/h265 Multi-Encoder",
|
|
"Codec/Encoder/Video",
|
|
"Amlogic h264/h265 Multi-Encoder Plugin",
|
|
"Jemy Zhang <[email protected]>");
|
|
|
|
supported_sinkcaps = gst_caps_new_simple ("video/x-raw",
|
|
"framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1,
|
|
"width", GST_TYPE_INT_RANGE, 16, G_MAXINT,
|
|
"height", GST_TYPE_INT_RANGE, 16, G_MAXINT, NULL);
|
|
|
|
gst_amlvenc_add_v_chroma_format (NULL, gst_caps_get_structure (supported_sinkcaps, 0));
|
|
|
|
sink_templ = gst_pad_template_new ("sink",
|
|
GST_PAD_SINK, GST_PAD_ALWAYS, supported_sinkcaps);
|
|
|
|
gst_caps_unref (supported_sinkcaps);
|
|
|
|
gst_element_class_add_pad_template (element_class, sink_templ);
|
|
gst_element_class_add_static_pad_template (element_class, &src_factory);
|
|
}
|
|
|
|
/* initialize the new element
|
|
* instantiate pads and add them to element
|
|
* set functions
|
|
* initialize structure
|
|
*/
|
|
static void
|
|
gst_amlvenc_init (GstAmlVEnc * encoder)
|
|
{
|
|
encoder->gop = PROP_IDR_PERIOD_DEFAULT;
|
|
encoder->framerate = PROP_FRAMERATE_DEFAULT;
|
|
encoder->bitrate = PROP_BITRATE_DEFAULT;
|
|
encoder->max_buffers = PROP_MAX_BUFFERS_DEFAULT;
|
|
encoder->min_buffers = PROP_MIN_BUFFERS_DEFAULT;
|
|
encoder->encoder_bufsize = PROP_ENCODER_BUFFER_SIZE_DEFAULT * 1024;
|
|
encoder->codec.id = AML_CODEC_ID_NONE;
|
|
encoder->bitrate_bak = 0;
|
|
|
|
encoder->fd[0] = -1;
|
|
encoder->fd[1] = -1;
|
|
encoder->fd[2] = -1;
|
|
|
|
encoder->u4_first_pts_index = 0;
|
|
}
|
|
|
|
static void
|
|
gst_amlvenc_finalize (GObject * object)
|
|
{
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
|
|
static gboolean
|
|
gst_amlvenc_start (GstVideoEncoder * encoder)
|
|
{
|
|
GstAmlVEnc *venc = GST_AMLVENC (encoder);
|
|
|
|
if (venc->codec.buf == NULL) {
|
|
venc->codec.buf = g_new (guchar, venc->encoder_bufsize);
|
|
}
|
|
/* make sure that we have enough time for first DTS,
|
|
this is probably overkill for most streams */
|
|
gst_video_encoder_set_min_pts (encoder, GST_MSECOND * 30);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_amlvenc_stop (GstVideoEncoder * encoder)
|
|
{
|
|
GstAmlVEnc *venc = GST_AMLVENC (encoder);
|
|
|
|
gst_amlvenc_close_encoder (venc);
|
|
|
|
if (venc->input_state) {
|
|
gst_video_codec_state_unref (venc->input_state);
|
|
venc->input_state = NULL;
|
|
}
|
|
|
|
if (venc->codec.buf) {
|
|
g_free((gpointer)venc->codec.buf);
|
|
venc->codec.buf = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
gst_amlvenc_flush (GstVideoEncoder * encoder)
|
|
{
|
|
GstAmlVEnc *venc = GST_AMLVENC (encoder);
|
|
|
|
gst_amlvenc_init_encoder (venc);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* gst_amlvenc_init_encoder
|
|
* @encoder: Encoder which should be initialized.
|
|
*
|
|
* Initialize v encoder.
|
|
*
|
|
*/
|
|
static gboolean gst_amlvenc_init_encoder (GstAmlVEnc * encoder)
|
|
{
|
|
GstVideoInfo *info;
|
|
gdouble cur_fps = 0.0;
|
|
|
|
if (!encoder->input_state) {
|
|
GST_DEBUG_OBJECT (encoder, "Have no input state yet");
|
|
return FALSE;
|
|
}
|
|
|
|
info = &encoder->input_state->info;
|
|
|
|
/* make sure that the encoder is closed */
|
|
gst_amlvenc_close_encoder (encoder);
|
|
|
|
GST_OBJECT_LOCK (encoder);
|
|
|
|
encoder->bitrate_bak = encoder->bitrate;
|
|
|
|
GST_OBJECT_UNLOCK (encoder);
|
|
|
|
amvenc_info_t encode_info;
|
|
memset (&encode_info, 0, sizeof(encode_info));
|
|
|
|
encode_info.width = info->width;
|
|
encode_info.height = info->height;
|
|
encode_info.frame_rate = encoder->framerate;
|
|
if (info->fps_d != 0 && info->fps_n != 0) {
|
|
gst_util_fraction_to_double (info->fps_n, info->fps_d, &cur_fps);
|
|
if (cur_fps <= 0.0) {
|
|
GST_WARNING_OBJECT (encoder, "Framerate %f out of range, using default", cur_fps);
|
|
cur_fps = 30.0;
|
|
}
|
|
encode_info.frame_rate = (int)(cur_fps + 0.5);
|
|
}
|
|
encode_info.bit_rate = encoder->bitrate * 1000;
|
|
encode_info.gop = encoder->gop;
|
|
encode_info.img_format = img_format_convert(GST_VIDEO_INFO_FORMAT(info));
|
|
encode_info.prepend_spspps_to_idr_frames = TRUE;
|
|
encode_info.enc_feature_opts |= ENC_ENABLE_ROI_FEATURE; // enable roi function
|
|
encode_info.enc_feature_opts |= ENC_ENABLE_PARA_UPDATE; // enable dynamic update parameters
|
|
|
|
amvenc_qp_param_t qp_tbl;
|
|
memset(&qp_tbl, 0, sizeof(qp_tbl));
|
|
|
|
qp_tbl.qp_min = 0;
|
|
qp_tbl.qp_max = 51;
|
|
qp_tbl.qp_I_base = 30;
|
|
qp_tbl.qp_I_min = 0;
|
|
qp_tbl.qp_I_max = 51;
|
|
qp_tbl.qp_P_base = 30;
|
|
qp_tbl.qp_P_min = 0;
|
|
qp_tbl.qp_P_max = 51;
|
|
|
|
encoder->codec.handle = amvenc_init(encoder->codec.id, encode_info, &qp_tbl);
|
|
|
|
if (encoder->codec.handle == 0) {
|
|
GST_ELEMENT_ERROR (encoder, STREAM, ENCODE,
|
|
("Can not initialize v encoder."), (NULL));
|
|
return FALSE;
|
|
}
|
|
|
|
#if SUPPORT_SCALE
|
|
if (GST_VIDEO_INFO_FORMAT(info) == GST_VIDEO_FORMAT_BGR) {
|
|
memset(&encoder->amlge2d,0x0,sizeof(aml_ge2d_t));
|
|
aml_ge2d_info_t *pge2dinfo = &encoder->amlge2d.ge2dinfo;
|
|
memset(pge2dinfo, 0, sizeof(aml_ge2d_info_t));
|
|
memset(&(pge2dinfo->src_info[0]), 0, sizeof(buffer_info_t));
|
|
memset(&(pge2dinfo->src_info[1]), 0, sizeof(buffer_info_t));
|
|
memset(&(pge2dinfo->dst_info), 0, sizeof(buffer_info_t));
|
|
|
|
set_ge2dinfo(pge2dinfo, info);
|
|
|
|
gint ret = aml_ge2d_init(&encoder->amlge2d);
|
|
if (ret < 0) {
|
|
GST_ELEMENT_ERROR (encoder, STREAM, ENCODE,
|
|
("encode open ge2d failed"), (NULL));
|
|
return FALSE;
|
|
}
|
|
encoder->ge2d_initial_done = 1;
|
|
encoder->INIT_GE2D = TRUE;
|
|
}
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* gst_amlvenc_close_encoder
|
|
* @encoder: Encoder which should close.
|
|
*
|
|
* Close v encoder.
|
|
*/
|
|
static void gst_amlvenc_close_encoder (GstAmlVEnc * encoder)
|
|
{
|
|
if (encoder->codec.handle != 0) {
|
|
amvenc_destroy(encoder->codec.handle);
|
|
encoder->codec.handle = 0;
|
|
}
|
|
#if SUPPORT_SCALE
|
|
if (encoder->ge2d_initial_done) {
|
|
aml_ge2d_mem_free(&encoder->amlge2d);
|
|
aml_ge2d_exit(&encoder->amlge2d);
|
|
GST_DEBUG_OBJECT(encoder, "ge2d exit!!!");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static gboolean
|
|
gst_amlvenc_set_profile_and_level (GstAmlVEnc * encoder, GstCaps * caps)
|
|
{
|
|
GstStructure *s;
|
|
const gchar *profile;
|
|
GstCaps *allowed_caps;
|
|
GstStructure *s2;
|
|
const gchar *allowed_profile;
|
|
|
|
/* Constrained baseline is a strict subset of baseline. If downstream
|
|
* wanted baseline and we produced constrained baseline, we can just
|
|
* set the profile to baseline in the caps to make negotiation happy.
|
|
* Same goes for baseline as subset of main profile and main as a subset
|
|
* of high profile.
|
|
*/
|
|
s = gst_caps_get_structure (caps, 0);
|
|
profile = gst_structure_get_string (s, "profile");
|
|
|
|
allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder));
|
|
|
|
if (allowed_caps == NULL)
|
|
goto no_peer;
|
|
|
|
if (!gst_caps_can_intersect (allowed_caps, caps)) {
|
|
allowed_caps = gst_caps_make_writable (allowed_caps);
|
|
allowed_caps = gst_caps_truncate (allowed_caps);
|
|
s2 = gst_caps_get_structure (allowed_caps, 0);
|
|
gst_structure_fixate_field_string (s2, "profile", profile);
|
|
allowed_profile = gst_structure_get_string (s2, "profile");
|
|
if (!g_strcmp0 (allowed_profile, "high")) {
|
|
if (!g_strcmp0 (profile, "constrained-baseline")
|
|
|| !g_strcmp0 (profile, "baseline") || !g_strcmp0 (profile, "main")) {
|
|
gst_structure_set (s, "profile", G_TYPE_STRING, "high", NULL);
|
|
GST_INFO_OBJECT (encoder, "downstream requested high profile, but "
|
|
"encoder will now output %s profile (which is a subset), due "
|
|
"to how it's been configured", profile);
|
|
}
|
|
} else if (!g_strcmp0 (allowed_profile, "main")) {
|
|
if (!g_strcmp0 (profile, "constrained-baseline")
|
|
|| !g_strcmp0 (profile, "baseline")) {
|
|
gst_structure_set (s, "profile", G_TYPE_STRING, "main", NULL);
|
|
GST_INFO_OBJECT (encoder, "downstream requested main profile, but "
|
|
"encoder will now output %s profile (which is a subset), due "
|
|
"to how it's been configured", profile);
|
|
}
|
|
} else if (!g_strcmp0 (allowed_profile, "baseline")) {
|
|
if (!g_strcmp0 (profile, "constrained-baseline"))
|
|
gst_structure_set (s, "profile", G_TYPE_STRING, "baseline", NULL);
|
|
}
|
|
}
|
|
gst_caps_unref (allowed_caps);
|
|
|
|
no_peer:
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* gst_amlvenc_set_src_caps
|
|
* Returns: TRUE on success.
|
|
*/
|
|
static gboolean
|
|
gst_amlvenc_set_src_caps (GstAmlVEnc * encoder, GstCaps * caps)
|
|
{
|
|
GstCaps *outcaps;
|
|
GstStructure *structure;
|
|
GstVideoCodecState *state;
|
|
GstTagList *tags;
|
|
const gchar* mime_str = "video/x-h264";
|
|
|
|
if (encoder->codec.id == AML_CODEC_ID_H265) {
|
|
mime_str = "video/x-h265";
|
|
}
|
|
outcaps = gst_caps_new_empty_simple (mime_str);
|
|
structure = gst_caps_get_structure (outcaps, 0);
|
|
|
|
gst_structure_set (structure, "stream-format", G_TYPE_STRING, "byte-stream",
|
|
NULL);
|
|
gst_structure_set (structure, "alignment", G_TYPE_STRING, "au", NULL);
|
|
|
|
if (!gst_amlvenc_set_profile_and_level (encoder, outcaps)) {
|
|
gst_caps_unref (outcaps);
|
|
return FALSE;
|
|
}
|
|
|
|
state = gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (encoder),
|
|
outcaps, encoder->input_state);
|
|
GST_DEBUG_OBJECT (encoder, "output caps: %" GST_PTR_FORMAT, state->caps);
|
|
|
|
gst_video_codec_state_unref (state);
|
|
|
|
tags = gst_tag_list_new_empty ();
|
|
gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, "v",
|
|
GST_TAG_MAXIMUM_BITRATE, encoder->bitrate * 1000,
|
|
GST_TAG_NOMINAL_BITRATE, encoder->bitrate * 1000, NULL);
|
|
gst_video_encoder_merge_tags (GST_VIDEO_ENCODER (encoder), tags,
|
|
GST_TAG_MERGE_REPLACE);
|
|
gst_tag_list_unref (tags);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_amlvenc_set_latency (GstAmlVEnc * encoder)
|
|
{
|
|
GstVideoInfo *info = &encoder->input_state->info;
|
|
gint max_delayed_frames;
|
|
GstClockTime latency;
|
|
|
|
max_delayed_frames = 0;
|
|
|
|
if (info->fps_n) {
|
|
latency = gst_util_uint64_scale_ceil (GST_SECOND * info->fps_d,
|
|
max_delayed_frames, info->fps_n);
|
|
} else {
|
|
/* FIXME: Assume 25fps. This is better than reporting no latency at
|
|
* all and then later failing in live pipelines
|
|
*/
|
|
latency = gst_util_uint64_scale_ceil (GST_SECOND * 1,
|
|
max_delayed_frames, 25);
|
|
}
|
|
|
|
GST_INFO_OBJECT (encoder,
|
|
"Updating latency to %" GST_TIME_FORMAT " (%d frames)",
|
|
GST_TIME_ARGS (latency), max_delayed_frames);
|
|
|
|
gst_video_encoder_set_latency (GST_VIDEO_ENCODER (encoder), latency, latency);
|
|
}
|
|
|
|
static gboolean
|
|
gst_amlvenc_set_format (GstVideoEncoder * video_enc,
|
|
GstVideoCodecState * state)
|
|
{
|
|
GstAmlVEnc *encoder = GST_AMLVENC (video_enc);
|
|
GstVideoInfo *info = &state->info;
|
|
GstCaps *template_caps;
|
|
GstCaps *allowed_caps = NULL;
|
|
const gchar* allowed_mime_name = NULL;
|
|
|
|
/* If the encoder is initialized, do not reinitialize it again if not
|
|
* necessary */
|
|
if (encoder->codec.handle) {
|
|
GstVideoInfo *old = &encoder->input_state->info;
|
|
|
|
if (info->finfo->format == old->finfo->format
|
|
&& info->width == old->width && info->height == old->height
|
|
&& info->fps_n == old->fps_n && info->fps_d == old->fps_d
|
|
&& info->par_n == old->par_n && info->par_d == old->par_d) {
|
|
gst_video_codec_state_unref (encoder->input_state);
|
|
encoder->input_state = gst_video_codec_state_ref (state);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (encoder->input_state)
|
|
gst_video_codec_state_unref (encoder->input_state);
|
|
|
|
encoder->input_state = gst_video_codec_state_ref (state);
|
|
|
|
template_caps = gst_static_pad_template_get_caps (&src_factory);
|
|
allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder));
|
|
|
|
if (allowed_caps && allowed_caps != template_caps && encoder->codec.id == AML_CODEC_ID_NONE) {
|
|
GstStructure *s;
|
|
|
|
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);
|
|
allowed_mime_name = gst_structure_get_name (s);
|
|
|
|
if (!g_strcmp0 (allowed_mime_name, "video/x-h265"))
|
|
{
|
|
encoder->codec.id = AML_CODEC_ID_H265;
|
|
} else {
|
|
encoder->codec.id = AML_CODEC_ID_H264;
|
|
}
|
|
|
|
gst_caps_unref (allowed_caps);
|
|
}
|
|
|
|
gst_caps_unref (template_caps);
|
|
|
|
if (!gst_amlvenc_init_encoder (encoder))
|
|
return FALSE;
|
|
|
|
if (!gst_amlvenc_set_src_caps (encoder, state->caps)) {
|
|
gst_amlvenc_close_encoder (encoder);
|
|
return FALSE;
|
|
}
|
|
|
|
gst_amlvenc_set_latency (encoder);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_amlvenc_finish (GstVideoEncoder * encoder)
|
|
{
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
static gboolean
|
|
gst_amlvenc_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
|
|
{
|
|
GstAmlVEnc *self = GST_AMLVENC (encoder);
|
|
GstVideoInfo *info;
|
|
guint size, min = 0, max = 0;
|
|
GstCaps *caps;
|
|
GstBufferPool *pool = NULL;
|
|
gboolean need_pool = FALSE;
|
|
|
|
if (!self->input_state)
|
|
return FALSE;
|
|
|
|
info = &self->input_state->info;
|
|
|
|
if (info) {
|
|
switch (GST_VIDEO_INFO_FORMAT(info)) {
|
|
case GST_VIDEO_FORMAT_NV12:
|
|
case GST_VIDEO_FORMAT_NV21:
|
|
{
|
|
GST_DEBUG_OBJECT(encoder, "choose gst_drm_bufferpool");
|
|
gst_query_parse_allocation(query, &caps, &need_pool);
|
|
GST_DEBUG_OBJECT(encoder, "need_pool: %d", need_pool);
|
|
|
|
if (need_pool) {
|
|
pool = gst_drm_bufferpool_new(FALSE, GST_DRM_BUFFERPOOL_TYPE_VIDEO_PLANE);
|
|
GST_DEBUG_OBJECT(encoder, "new gst_drm_bufferpool");
|
|
}
|
|
|
|
gst_query_add_allocation_pool(query, pool, info->size, DRMBP_EXTRA_BUF_SIZE_FOR_DISPLAY, DRMBP_LIMIT_MAX_BUFSIZE_TO_BUFSIZE);
|
|
GST_DEBUG_OBJECT(encoder, "info->size: %d", info->size);
|
|
if (pool)
|
|
g_object_unref(pool);
|
|
|
|
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
|
|
|
|
break;
|
|
}
|
|
default: //hanle not NV12/NV21
|
|
{
|
|
GST_DEBUG_OBJECT(encoder, "choose fake bufferpool");
|
|
if (gst_query_get_n_allocation_pools (query) > 0) {
|
|
gst_query_parse_nth_allocation_pool (query, 0, NULL, &size, &min, &max);
|
|
size = MAX (size, info->size);
|
|
gst_query_set_nth_allocation_pool (query, 0, NULL, size, self->min_buffers, self->max_buffers);
|
|
} else {
|
|
gst_query_add_allocation_pool (query, NULL, info->size, self->min_buffers, self->max_buffers);
|
|
GST_DEBUG_OBJECT(encoder, "info->size: %d", info->size);
|
|
}
|
|
|
|
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
GST_DEBUG_OBJECT(encoder, "can not get videoinfo");
|
|
return FALSE;
|
|
}
|
|
|
|
return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder,
|
|
query);
|
|
}
|
|
|
|
static void
|
|
gst_amlvenc_add_cc (GstBuffer * buffer, amvenc_frame_info_t * pic_in)
|
|
{
|
|
guint8 *cc_data = NULL;
|
|
gsize cc_data_cnt = 0;
|
|
gint cc_count = 0;
|
|
gpointer cc_meta_data = NULL;
|
|
gint cc_meta_size = 0;
|
|
GstMapInfo map;
|
|
|
|
cc_data = (guint8 *)gst_mini_object_get_qdata(GST_MINI_OBJECT(buffer),GST_AML_V4L2_CC_IMPORT_QUARK);
|
|
/*parse cc data*/
|
|
if (cc_data) {
|
|
cc_data_cnt = gst_buffer_get_size(cc_data);
|
|
gst_buffer_map(cc_data, &map, GST_MAP_READ);
|
|
/*mpeg2*/
|
|
if (cc_data_cnt > 7 &&
|
|
map.data[0] == 0x47 &&
|
|
map.data[1] == 0x41 &&
|
|
map.data[2] == 0x39 &&
|
|
map.data[3] == 0x34)
|
|
{
|
|
if (map.data[4] == 0x03 /* 0x03 indicates cc_data */)
|
|
{
|
|
cc_count = map.data[5]&0x1F;
|
|
cc_meta_size = cc_count*3;
|
|
cc_meta_data = map.data+7; /*cc_data[6] 8-bits reserved*/
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((cc_meta_data) && (cc_meta_size > 0) && (cc_data_cnt >= cc_meta_size)) {
|
|
pic_in->sei_payload.payload_size = cc_meta_size + 11;
|
|
pic_in->sei_payload.payload =
|
|
g_malloc0 (pic_in->sei_payload.payload_size);
|
|
pic_in->sei_payload.payload_type = 4; /* Registered user data */
|
|
memcpy (pic_in->sei_payload.payload + 10, cc_meta_data,
|
|
cc_meta_size);
|
|
pic_in->sei_payload.payload[0] = 181; /* 8-bits itu_t_t35_country_code */
|
|
pic_in->sei_payload.payload[1] = 0; /* 16-bits itu_t_t35_provider_code */
|
|
pic_in->sei_payload.payload[2] = 49;
|
|
pic_in->sei_payload.payload[3] = 'G'; /* 32-bits ATSC_user_identifier */
|
|
pic_in->sei_payload.payload[4] = 'A';
|
|
pic_in->sei_payload.payload[5] = '9';
|
|
pic_in->sei_payload.payload[6] = '4';
|
|
pic_in->sei_payload.payload[7] = 3; /* 8-bits ATSC1_data_user_data_type_code */
|
|
/* 8-bits:
|
|
* 1 bit process_em_data_flag (0)
|
|
* 1 bit process_cc_data_flag (1)
|
|
* 1 bit additional_data_flag (0)
|
|
* 5-bits cc_count
|
|
*/
|
|
pic_in->sei_payload.payload[8] =
|
|
((cc_meta_size / 3) & 0x1f) | 0x40;
|
|
pic_in->sei_payload.payload[9] = 255; /* 8 bits em_data, unused */
|
|
pic_in->sei_payload.payload[cc_meta_size + 10] = 255; /* 8 marker bits */
|
|
}
|
|
if (cc_data)
|
|
gst_buffer_unmap(cc_data, &map);
|
|
}
|
|
|
|
|
|
/* chain function
|
|
* this function does the actual processing
|
|
*/
|
|
static GstFlowReturn
|
|
gst_amlvenc_handle_frame (GstVideoEncoder * video_enc,
|
|
GstVideoCodecFrame * frame)
|
|
{
|
|
GstAmlVEnc *encoder = GST_AMLVENC (video_enc);
|
|
GstFlowReturn ret;
|
|
|
|
if (G_UNLIKELY (encoder->codec.handle == 0))
|
|
goto not_inited;
|
|
|
|
ret = gst_amlvenc_encode_frame (encoder, frame);
|
|
|
|
/* input buffer is released later on */
|
|
return ret;
|
|
|
|
/* ERRORS */
|
|
not_inited:
|
|
{
|
|
GST_WARNING_OBJECT (encoder, "Got buffer before set_caps was called");
|
|
return GST_FLOW_NOT_NEGOTIATED;
|
|
}
|
|
}
|
|
|
|
static GstFlowReturn gst_amlvenc_encode_frame (GstAmlVEnc * encoder, GstVideoCodecFrame * frame)
|
|
{
|
|
amvenc_frame_type_t frame_type = AML_FRAME_TYPE_AUTO;
|
|
GstVideoInfo *info = &encoder->input_state->info;
|
|
GstMapInfo map;
|
|
guint8 ui1_plane_num = 1;
|
|
gint encode_data_len = -1;
|
|
guint bitrate = 0;
|
|
GstVideoMeta *vmeta = NULL;
|
|
|
|
if (G_UNLIKELY (encoder->codec.handle == 0)) {
|
|
if (frame)
|
|
gst_video_codec_frame_unref (frame);
|
|
return GST_FLOW_NOT_NEGOTIATED;
|
|
}
|
|
GST_OBJECT_LOCK (encoder);
|
|
if (encoder->bitrate_bak != encoder->bitrate) {
|
|
bitrate = encoder->bitrate * 1000;
|
|
amvenc_change_bitrate(encoder->codec.handle, bitrate);
|
|
encoder->bitrate_bak = encoder->bitrate;
|
|
}
|
|
GST_OBJECT_UNLOCK (encoder);
|
|
|
|
if (frame) {
|
|
if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) {
|
|
GST_INFO_OBJECT (encoder, "Forcing key frame");
|
|
frame_type = AML_FRAME_TYPE_IDR;
|
|
}
|
|
}
|
|
|
|
amvenc_buffer_info_t inbuf_info;
|
|
amvenc_buffer_info_t retbuf_info;
|
|
|
|
GstMemory *memory = gst_buffer_get_memory(frame->input_buffer, 0);
|
|
gboolean is_dmabuf = gst_is_dmabuf_memory(memory);
|
|
|
|
GST_DEBUG_OBJECT(encoder, "is_dmabuf[%d] width[%d] height[%d]",is_dmabuf,info->width,info->height);
|
|
|
|
memset(&inbuf_info, 0, sizeof(inbuf_info));
|
|
inbuf_info.buf_fmt = img_format_convert(GST_VIDEO_INFO_FORMAT(info));
|
|
inbuf_info.buf_stride = info->width;
|
|
|
|
#if SUPPORT_SCALE
|
|
if (GST_VIDEO_INFO_FORMAT(info) == GST_VIDEO_FORMAT_BGR) {
|
|
if (encoder->INIT_GE2D) {
|
|
if (ge2d_colorFormat(GST_VIDEO_INFO_FORMAT(info)) == TRUE) {
|
|
GST_DEBUG_OBJECT(encoder, "The color format that venc need ge2d to change!");
|
|
} else {
|
|
GST_DEBUG_OBJECT(encoder, "Encoder only not support fmt: %d", GST_VIDEO_INFO_FORMAT(info));
|
|
if (frame)
|
|
gst_video_codec_frame_unref (frame);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
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);
|
|
vmeta = gst_buffer_get_video_meta(frame->input_buffer);
|
|
if (vmeta)
|
|
{
|
|
encoder->stride[0] = vmeta->stride[0];
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
#if SUPPORT_SCALE
|
|
if (GST_VIDEO_INFO_FORMAT(info) == GST_VIDEO_FORMAT_BGR) {
|
|
if (encoder->INIT_GE2D) {
|
|
encoder->INIT_GE2D = FALSE;
|
|
encoder->amlge2d.ge2dinfo.src_info[0].format = SRC1_PIXFORMAT;
|
|
encoder->amlge2d.ge2dinfo.src_info[1].format = SRC2_PIXFORMAT;
|
|
encoder->amlge2d.ge2dinfo.dst_info.plane_number = 1;
|
|
gint ret = aml_ge2d_mem_alloc(&encoder->amlge2d);
|
|
if (ret < 0) {
|
|
GST_DEBUG_OBJECT(encoder, "encode ge2d mem alloc failed, ret=0x%x", ret);
|
|
if (frame)
|
|
gst_video_codec_frame_unref (frame);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
GST_DEBUG_OBJECT(encoder, "ge2d init successful!");
|
|
}
|
|
encoder->amlge2d.ge2dinfo.src_info[0].shared_fd[0] = encoder->fd[0];
|
|
|
|
do_strechblit(&encoder->amlge2d.ge2dinfo, info);
|
|
aml_ge2d_invalid_cache(&encoder->amlge2d.ge2dinfo);
|
|
|
|
ui1_plane_num = 1;
|
|
inbuf_info.buf_type = AML_DMA_TYPE;
|
|
inbuf_info.buf_info.dma_info.shared_fd[0] = encoder->amlge2d.ge2dinfo.dst_info.shared_fd[0];
|
|
inbuf_info.buf_info.dma_info.num_planes = ui1_plane_num;
|
|
encoder->amlge2d.ge2dinfo.src_info[0].shared_fd[0] = -1;
|
|
GST_DEBUG_OBJECT(encoder, "Set DMA buffer planes %d fd[%d]",
|
|
inbuf_info.buf_info.dma_info.num_planes, inbuf_info.buf_info.dma_info.shared_fd[0]);
|
|
} else
|
|
#endif
|
|
{
|
|
inbuf_info.buf_type = AML_DMA_TYPE;
|
|
if (encoder->stride[0])
|
|
inbuf_info.buf_stride = encoder->stride[0];
|
|
inbuf_info.buf_info.dma_info.shared_fd[0] = encoder->fd[0];
|
|
inbuf_info.buf_info.dma_info.shared_fd[1] = encoder->fd[1];
|
|
inbuf_info.buf_info.dma_info.shared_fd[2] = encoder->fd[2];
|
|
inbuf_info.buf_info.dma_info.num_planes = ui1_plane_num;
|
|
GST_DEBUG_OBJECT(encoder, "Set DMA buffer planes %d fd[%d, %d, %d]",
|
|
inbuf_info.buf_info.dma_info.num_planes, inbuf_info.buf_info.dma_info.shared_fd[0],
|
|
inbuf_info.buf_info.dma_info.shared_fd[1], inbuf_info.buf_info.dma_info.shared_fd[2]);
|
|
}
|
|
} else {
|
|
gst_memory_unref(memory);
|
|
GstVideoFrame video_frame;
|
|
gst_video_frame_map(&video_frame, info, frame->input_buffer, GST_MAP_READ);
|
|
|
|
guint8 *pixel = GST_VIDEO_FRAME_PLANE_DATA (&video_frame, 0);
|
|
#if SUPPORT_SCALE
|
|
if (GST_VIDEO_INFO_FORMAT(info) == GST_VIDEO_FORMAT_BGR) {
|
|
if (encoder->INIT_GE2D) {
|
|
encoder->INIT_GE2D = false;
|
|
|
|
encoder->amlge2d.ge2dinfo.src_info[0].format = SRC1_PIXFORMAT;
|
|
encoder->amlge2d.ge2dinfo.src_info[1].format = SRC2_PIXFORMAT;
|
|
encoder->amlge2d.ge2dinfo.src_info[0].plane_number = 1;
|
|
encoder->amlge2d.ge2dinfo.dst_info.plane_number = 1;
|
|
gint ret = aml_ge2d_mem_alloc(&encoder->amlge2d);
|
|
if (ret < 0) {
|
|
GST_DEBUG_OBJECT(encoder, "encode ge2d mem alloc failed, ret=0x%x", ret);
|
|
if (frame)
|
|
gst_video_codec_frame_unref (frame);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
GST_DEBUG_OBJECT(encoder, "ge2d init successful!");
|
|
}
|
|
|
|
if (GST_VIDEO_INFO_FORMAT(info) == GST_VIDEO_FORMAT_BGR) {
|
|
memcpy((void *)encoder->amlge2d.ge2dinfo.src_info[0].vaddr[0], (void *)pixel, info->size);
|
|
}
|
|
|
|
do_strechblit(&encoder->amlge2d.ge2dinfo, info);
|
|
aml_ge2d_invalid_cache(&encoder->amlge2d.ge2dinfo);
|
|
ui1_plane_num = 1;
|
|
inbuf_info.buf_type = AML_DMA_TYPE;
|
|
inbuf_info.buf_info.dma_info.shared_fd[0] = encoder->amlge2d.ge2dinfo.dst_info.shared_fd[0];
|
|
inbuf_info.buf_info.dma_info.num_planes = ui1_plane_num;
|
|
GST_DEBUG_OBJECT(encoder, "Set DMA buffer planes %d fd[%d]",
|
|
inbuf_info.buf_info.dma_info.num_planes, inbuf_info.buf_info.dma_info.shared_fd[0]);
|
|
gst_video_frame_unmap (&video_frame);
|
|
} else
|
|
#endif
|
|
{
|
|
inbuf_info.buf_info.in_ptr[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 (&video_frame, 1);
|
|
inbuf_info.buf_info.in_ptr[1] = (ulong) (pixel);
|
|
break;
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
pixel = GST_VIDEO_FRAME_PLANE_DATA (&video_frame, 1);
|
|
inbuf_info.buf_info.in_ptr[1] = (ulong) (pixel);
|
|
pixel = GST_VIDEO_FRAME_PLANE_DATA (&video_frame, 2);
|
|
inbuf_info.buf_info.in_ptr[2] = (ulong) (pixel);
|
|
break;
|
|
default: //RGB
|
|
break;
|
|
}
|
|
inbuf_info.buf_type = AML_VMALLOC_TYPE;
|
|
gst_video_frame_unmap (&video_frame);
|
|
}
|
|
}
|
|
amvenc_frame_info_t frame_info;
|
|
memset(&frame_info, 0, sizeof(frame_info));
|
|
|
|
frame_info.frame_size = info->size;
|
|
|
|
frame_info.pitch = info->width;
|
|
frame_info.height = info->height;
|
|
|
|
if (encoder->stride[0])
|
|
frame_info.pitch = encoder->stride[0];
|
|
|
|
//frame_info.coding_timestamp = 0;
|
|
//frame_info.canvas = 0;
|
|
//frame_info.scale_width = 0;
|
|
//frame_info.scale_height = 0;
|
|
//frame_info.crop_left = 0;
|
|
//frame_info.crop_right = 0;
|
|
//frame_info.crop_top = 0;
|
|
//frame_info.crop_bottom = 0;
|
|
frame_info.bitrate = encoder->bitrate * 1000;
|
|
frame_info.frame_rate = encoder->framerate;
|
|
|
|
gst_amlvenc_add_cc(frame->input_buffer, &frame_info);
|
|
|
|
amvenc_metadata_t meta =
|
|
amvenc_encode(encoder->codec.handle, frame_info, frame_type,
|
|
encoder->codec.buf, &inbuf_info, &retbuf_info);
|
|
if (frame_info.sei_payload.payload) {
|
|
g_free(frame_info.sei_payload.payload);
|
|
frame_info.sei_payload.payload = NULL;
|
|
}
|
|
if (!meta.is_valid) {
|
|
if (frame) {
|
|
GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, ("Encode v frame failed."),
|
|
("gst_amlvencoder_encode return code=%d", encode_data_len));
|
|
gst_video_codec_frame_unref (frame);
|
|
return GST_FLOW_ERROR;
|
|
} else {
|
|
return GST_FLOW_EOS;
|
|
}
|
|
}
|
|
|
|
if (frame) {
|
|
gst_video_codec_frame_unref (frame);
|
|
}
|
|
|
|
//frame = gst_video_encoder_get_frame (GST_VIDEO_ENCODER (encoder), input_frame->system_frame_number);
|
|
frame = gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (encoder));
|
|
if (!frame) {
|
|
gst_video_codec_frame_unref (frame);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
frame->output_buffer = gst_video_encoder_allocate_output_buffer(
|
|
GST_VIDEO_ENCODER(encoder), meta.encoded_data_length_in_bytes);
|
|
gst_buffer_map(frame->output_buffer, &map, GST_MAP_WRITE);
|
|
memcpy (map.data, encoder->codec.buf, meta.encoded_data_length_in_bytes);
|
|
gst_buffer_unmap (frame->output_buffer, &map);
|
|
|
|
/*
|
|
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_OBJECT (encoder, "add for add pts end[%d] [%d]",info->fps_n,info->fps_d);
|
|
GST_BUFFER_TIMESTAMP (frame->input_buffer) = gst_util_uint64_scale (encoder->u4_first_pts_index++, GST_SECOND, info->fps_n/info->fps_d);
|
|
GST_BUFFER_DURATION (frame->input_buffer) = gst_util_uint64_scale (1, GST_SECOND, info->fps_n/info->fps_d);
|
|
frame->pts = GST_BUFFER_TIMESTAMP (frame->input_buffer);
|
|
|
|
//FIXME later for first_pts_index
|
|
if (encoder->u4_first_pts_index == PTS_UINT_4_RESET) {
|
|
GST_DEBUG_OBJECT (encoder, "PTS rollback");
|
|
encoder->u4_first_pts_index = 0;
|
|
}
|
|
}
|
|
|
|
frame->dts = frame->pts;
|
|
|
|
GST_LOG_OBJECT (encoder,
|
|
"output: dts %" G_GINT64_FORMAT " pts %" G_GINT64_FORMAT,
|
|
(gint64) frame->dts, (gint64) frame->pts);
|
|
|
|
if (frame_type == AML_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);
|
|
}
|
|
|
|
return gst_video_encoder_finish_frame ( GST_VIDEO_ENCODER(encoder), frame);
|
|
}
|
|
|
|
static void
|
|
gst_amlvenc_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstAmlVEnc *encoder = GST_AMLVENC (object);
|
|
|
|
GST_OBJECT_LOCK (encoder);
|
|
switch (prop_id) {
|
|
case PROP_GOP:
|
|
g_value_set_int (value, encoder->gop);
|
|
break;
|
|
case PROP_FRAMERATE:
|
|
g_value_set_int (value, encoder->framerate);
|
|
break;
|
|
case PROP_BITRATE:
|
|
g_value_set_int (value, encoder->bitrate);
|
|
break;
|
|
case PROP_MIN_BUFFERS:
|
|
g_value_set_int (value, encoder->min_buffers);
|
|
break;
|
|
case PROP_MAX_BUFFERS:
|
|
g_value_set_int (value, encoder->max_buffers);
|
|
break;
|
|
case PROP_ENCODER_BUFSIZE:
|
|
g_value_set_int (value, encoder->encoder_bufsize / 1024);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
GST_OBJECT_UNLOCK (encoder);
|
|
}
|
|
|
|
static void
|
|
gst_amlvenc_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstAmlVEnc *encoder = GST_AMLVENC (object);
|
|
gboolean roi_set_flag = false;
|
|
|
|
GST_OBJECT_LOCK (encoder);
|
|
|
|
switch (prop_id) {
|
|
case PROP_GOP:
|
|
encoder->gop = g_value_get_int (value);
|
|
break;
|
|
case PROP_FRAMERATE:
|
|
encoder->framerate = g_value_get_int (value);
|
|
break;
|
|
case PROP_BITRATE:
|
|
encoder->bitrate = g_value_get_int (value);
|
|
break;
|
|
case PROP_MIN_BUFFERS:
|
|
encoder->min_buffers = g_value_get_int (value);
|
|
break;
|
|
case PROP_MAX_BUFFERS:
|
|
encoder->max_buffers = g_value_get_int (value);
|
|
break;
|
|
case PROP_ENCODER_BUFSIZE:
|
|
encoder->encoder_bufsize = g_value_get_int (value) * 1024;
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
|
|
GST_OBJECT_UNLOCK (encoder);
|
|
return;
|
|
}
|
|
|
|
static gboolean
|
|
amlvenc_init (GstPlugin * amlvenc)
|
|
{
|
|
GST_DEBUG_CATEGORY_INIT (gst_amlvenc_debug, "amlvenc", 0,
|
|
"amlogic h264/h265 encoding element");
|
|
|
|
return gst_element_register (amlvenc, "amlvenc", GST_RANK_PRIMARY,
|
|
GST_TYPE_AMLVENC);
|
|
}
|
|
|
|
#ifndef VERSION
|
|
#define VERSION "1.0.0"
|
|
#endif
|
|
|
|
#ifndef PACKAGE
|
|
#define PACKAGE "aml_package"
|
|
#endif
|
|
#ifndef PACKAGE_NAME
|
|
#define PACKAGE_NAME "aml_package"
|
|
#endif
|
|
#ifndef GST_PACKAGE_ORIGIN
|
|
#define GST_PACKAGE_ORIGIN "http://amlogic.com/"
|
|
#endif
|
|
|
|
GST_PLUGIN_DEFINE (
|
|
GST_VERSION_MAJOR,
|
|
GST_VERSION_MINOR,
|
|
amlvenc,
|
|
"Amlogic h264/h265 encoder plugins",
|
|
amlvenc_init,
|
|
VERSION,
|
|
"LGPL",
|
|
"amlogic h264/h265 encoding",
|
|
"http://openlinux.amlogic.com"
|
|
)
|