1508 lines
47 KiB
C
1508 lines
47 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 <gmodule.h>
|
|
//#include <gst/allocators/gstamlionallocator.h>
|
|
#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 "gstamlh264venc.h"
|
|
#include "imgproc.h"
|
|
|
|
#include "gstamlionallocator.h"
|
|
#include <stdio.h>
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (gst_amlh264venc_debug);
|
|
#define GST_CAT_DEFAULT gst_amlh264venc_debug
|
|
|
|
static gboolean
|
|
gst_amlh264venc_add_v_chroma_format (GstAmlH264VEnc *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 PROP_ROI_ID_DEFAULT 0
|
|
#define PROP_ROI_ENABLED_DEFAULT TRUE
|
|
#define PROP_ROI_WIDTH_DEFAULT 0.00
|
|
#define PROP_ROI_HEIGHT_DEFAULT 0.00
|
|
#define PROP_ROI_X_DEFAULT 0.00
|
|
#define PROP_ROI_Y_DEFAULT 0.00
|
|
#define PROP_ROI_QUALITY_DEFAULT 51
|
|
#define DRMBP_EXTRA_BUF_SIZE_FOR_DISPLAY 1
|
|
#define DRMBP_LIMIT_MAX_BUFSIZE_TO_BUFSIZE 1
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_GOP,
|
|
PROP_FRAMERATE,
|
|
PROP_BITRATE,
|
|
PROP_MIN_BUFFERS,
|
|
PROP_MAX_BUFFERS,
|
|
PROP_ENCODER_BUFSIZE,
|
|
PROP_ROI_ID,
|
|
PROP_ROI_ENABLED,
|
|
PROP_ROI_WIDTH,
|
|
PROP_ROI_HEIGHT,
|
|
PROP_ROI_X,
|
|
PROP_ROI_Y,
|
|
PROP_ROI_QUALITY,
|
|
PROD_ENABLE_DMALLOCATOR
|
|
};
|
|
|
|
struct aml_roi_location {
|
|
gfloat left;
|
|
gfloat top;
|
|
gfloat width;
|
|
gfloat height;
|
|
};
|
|
|
|
struct RoiParamInfo {
|
|
struct listnode list;
|
|
gint id;
|
|
gint quality;
|
|
struct aml_roi_location location;
|
|
};
|
|
|
|
#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)
|
|
);
|
|
|
|
static void gst_amlh264venc_finalize (GObject * object);
|
|
static gboolean gst_amlh264venc_start (GstVideoEncoder * encoder);
|
|
static gboolean gst_amlh264venc_stop (GstVideoEncoder * encoder);
|
|
static gboolean gst_amlh264venc_flush (GstVideoEncoder * encoder);
|
|
|
|
static gboolean gst_amlh264venc_init_encoder (GstAmlH264VEnc * encoder);
|
|
static gboolean gst_amlh264venc_set_roi(GstAmlH264VEnc * encoder);
|
|
static void gst_amlh264venc_fill_roi_buffer(guchar* buffer, gint buffer_w, gint buffer_h,
|
|
struct RoiParamInfo *param_info, gint vframe_w, gint vframe_h, gint block_size);
|
|
static void gst_amlh264venc_close_encoder (GstAmlH264VEnc * encoder);
|
|
|
|
static GstFlowReturn gst_amlh264venc_finish (GstVideoEncoder * encoder);
|
|
static GstFlowReturn gst_amlh264venc_handle_frame (GstVideoEncoder * encoder,
|
|
GstVideoCodecFrame * frame);
|
|
static GstFlowReturn gst_amlh264venc_encode_frame (GstAmlH264VEnc * encoder,
|
|
GstVideoCodecFrame * frame);
|
|
static gboolean gst_amlh264venc_set_format (GstVideoEncoder * video_enc,
|
|
GstVideoCodecState * state);
|
|
|
|
static gboolean gst_amlh264venc_propose_allocation (GstVideoEncoder * encoder,
|
|
GstQuery * query);
|
|
|
|
static void gst_amlh264venc_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec);
|
|
static void gst_amlh264venc_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec);
|
|
|
|
#define gst_amlh264venc_parent_class parent_class
|
|
G_DEFINE_TYPE_WITH_CODE (GstAmlH264VEnc, gst_amlh264venc, GST_TYPE_VIDEO_ENCODER,
|
|
G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL));
|
|
|
|
static vl_img_format_t
|
|
img_format_convert (GstVideoFormat vfmt) {
|
|
vl_img_format_t fmt;
|
|
switch (vfmt) {
|
|
case GST_VIDEO_FORMAT_NV12:
|
|
fmt = IMG_FMT_NV12;
|
|
break;
|
|
case GST_VIDEO_FORMAT_NV21:
|
|
fmt = IMG_FMT_NV21;
|
|
break;
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
fmt = IMG_FMT_YV12;
|
|
break;
|
|
case GST_VIDEO_FORMAT_RGB:
|
|
case GST_VIDEO_FORMAT_BGR:
|
|
// use ge2d for internal conversation
|
|
fmt = IMG_FMT_NV12;
|
|
break;
|
|
default:
|
|
fmt = IMG_FMT_NV12;
|
|
break;
|
|
}
|
|
return fmt;
|
|
}
|
|
|
|
/* allowed input caps depending on whether libv was built for 8 or 10 bits */
|
|
static GstCaps *
|
|
gst_amlh264venc_sink_getcaps (GstVideoEncoder * enc, GstCaps * filter)
|
|
{
|
|
GstCaps *supported_incaps;
|
|
GstCaps *allowed;
|
|
GstCaps *filter_caps, *fcaps;
|
|
guint 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);
|
|
GstAmlH264VEnc *venc = GST_AMLH264VENC (enc);
|
|
|
|
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_amlh264venc_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_amlh264venc_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 cleanup_roi_param_list (GstAmlH264VEnc *encoder) {
|
|
struct listnode *pos, *q;
|
|
if (!list_empty(&encoder->roi.param_info)) {
|
|
list_for_each_safe(pos, q, &encoder->roi.param_info) {
|
|
struct RoiParamInfo *param_info =
|
|
list_entry (pos, struct RoiParamInfo, list);
|
|
list_remove (pos);
|
|
|
|
g_free(param_info);
|
|
}
|
|
}
|
|
}
|
|
|
|
static struct RoiParamInfo *retrieve_roi_param_info(GstAmlH264VEnc *encoder, gint id) {
|
|
GstAmlH264VEnc *self = GST_AMLH264VENC (encoder);
|
|
struct RoiParamInfo *ret = NULL;
|
|
if (!list_empty (&self->roi.param_info)) {
|
|
struct listnode *pos;
|
|
list_for_each (pos, &self->roi.param_info) {
|
|
struct RoiParamInfo *param_info =
|
|
list_entry (pos, struct RoiParamInfo, list);
|
|
if (param_info->id == id) {
|
|
ret = param_info;
|
|
}
|
|
}
|
|
}
|
|
if (ret == NULL) {
|
|
ret = g_new(struct RoiParamInfo, 1);
|
|
list_init (&ret->list);
|
|
list_add_tail(&self->roi.param_info, &ret->list);
|
|
ret->id = id;
|
|
ret->location.left = PROP_ROI_X_DEFAULT;
|
|
ret->location.top = PROP_ROI_Y_DEFAULT;
|
|
ret->location.width = PROP_ROI_WIDTH_DEFAULT;
|
|
ret->location.height = PROP_ROI_HEIGHT_DEFAULT;
|
|
ret->quality = PROP_ROI_QUALITY_DEFAULT;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
gst_amlh264venc_class_init (GstAmlH264VEncClass * 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_amlh264venc_set_property;
|
|
gobject_class->get_property = gst_amlh264venc_get_property;
|
|
gobject_class->finalize = gst_amlh264venc_finalize;
|
|
|
|
gstencoder_class->set_format = GST_DEBUG_FUNCPTR (gst_amlh264venc_set_format);
|
|
gstencoder_class->handle_frame =
|
|
GST_DEBUG_FUNCPTR (gst_amlh264venc_handle_frame);
|
|
gstencoder_class->start = GST_DEBUG_FUNCPTR (gst_amlh264venc_start);
|
|
gstencoder_class->stop = GST_DEBUG_FUNCPTR (gst_amlh264venc_stop);
|
|
gstencoder_class->flush = GST_DEBUG_FUNCPTR (gst_amlh264venc_flush);
|
|
gstencoder_class->finish = GST_DEBUG_FUNCPTR (gst_amlh264venc_finish);
|
|
gstencoder_class->getcaps = GST_DEBUG_FUNCPTR (gst_amlh264venc_sink_getcaps);
|
|
gstencoder_class->propose_allocation =
|
|
GST_DEBUG_FUNCPTR (gst_amlh264venc_propose_allocation);
|
|
gstencoder_class->sink_query = GST_DEBUG_FUNCPTR (gst_amlh264venc_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));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_ROI_ENABLED,
|
|
g_param_spec_boolean ("roi-enabled", "roi-enabled", "Enable/Disable the roi function",
|
|
PROP_ROI_ENABLED_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property(gobject_class, PROP_ROI_ID,
|
|
g_param_spec_int("roi-id", "roi-id", "Current roi operation id",
|
|
0, G_MAXINT32, PROP_ROI_ID_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_ROI_WIDTH,
|
|
g_param_spec_float("roi-width", "roi-width", "Relative width of the roi rectangle",
|
|
0.00, G_MAXFLOAT, PROP_ROI_WIDTH_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_ROI_HEIGHT,
|
|
g_param_spec_float("roi-height", "roi-height", "Relative height of the roi rectangle",
|
|
0.00, G_MAXFLOAT, PROP_ROI_HEIGHT_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_ROI_X,
|
|
g_param_spec_float("roi-x", "roi-x", "Relative horizontal start position of the roi rectangle",
|
|
0.00, G_MAXFLOAT, PROP_ROI_X_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_ROI_Y,
|
|
g_param_spec_float("roi-y", "roi-y", "Relative vertical start position of the roi rectangle",
|
|
0.00, G_MAXFLOAT, PROP_ROI_Y_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_ROI_QUALITY,
|
|
g_param_spec_int("roi-quality", "roi-quality", "Quality of roi area",
|
|
0, 51, PROP_ROI_QUALITY_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROD_ENABLE_DMALLOCATOR,
|
|
g_param_spec_boolean ("enable-dmallocator", "enable-dmallocator", "Enable/Disable dmallocator",
|
|
FALSE,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
gst_element_class_set_static_metadata (element_class,
|
|
"Amlogic h264 Encoder",
|
|
"Codec/Encoder/Video",
|
|
"Amlogic h264 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_amlh264venc_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_amlh264venc_init (GstAmlH264VEnc * 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 = CODEC_ID_H264;
|
|
|
|
list_init(&encoder->roi.param_info);
|
|
encoder->roi.srcid = 0;
|
|
encoder->roi.block_size = 16; // H264
|
|
encoder->roi.enabled = PROP_ROI_ENABLED_DEFAULT;
|
|
encoder->roi.id = PROP_ROI_ID_DEFAULT;
|
|
encoder->roi.buffer_info.data = NULL;
|
|
encoder->fd[0] = -1;
|
|
encoder->fd[1] = -1;
|
|
encoder->fd[2] = -1;
|
|
|
|
encoder->u4_first_pts_index = 0;
|
|
encoder->b_enable_dmallocator = FALSE;
|
|
}
|
|
|
|
static void
|
|
gst_amlh264venc_finalize (GObject * object)
|
|
{
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static gboolean
|
|
gst_amlh264venc_start (GstVideoEncoder * encoder)
|
|
{
|
|
GstAmlH264VEnc *venc = GST_AMLH264VENC (encoder);
|
|
|
|
venc->dmabuf_alloc = gst_amlion_allocator_obtain();
|
|
|
|
if (venc->codec.buf == NULL) {
|
|
venc->codec.buf = g_new (guchar, venc->encoder_bufsize);
|
|
}
|
|
venc->imgproc.input.memory = NULL;
|
|
venc->imgproc.output.memory = NULL;
|
|
/* 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_amlh264venc_stop (GstVideoEncoder * encoder)
|
|
{
|
|
GstAmlH264VEnc *venc = GST_AMLH264VENC (encoder);
|
|
|
|
gst_amlh264venc_close_encoder (venc);
|
|
|
|
if (venc->roi.srcid)
|
|
g_source_remove (venc->roi.srcid);
|
|
|
|
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;
|
|
}
|
|
|
|
if (venc->dmabuf_alloc) {
|
|
gst_object_unref(venc->dmabuf_alloc);
|
|
venc->dmabuf_alloc = NULL;
|
|
}
|
|
|
|
if (venc->imgproc.input.memory) {
|
|
gst_memory_unref(venc->imgproc.input.memory);
|
|
venc->imgproc.input.memory = NULL;
|
|
}
|
|
|
|
if (venc->imgproc.output.memory) {
|
|
gst_memory_unref(venc->imgproc.output.memory);
|
|
venc->imgproc.output.memory = NULL;
|
|
}
|
|
|
|
if (venc->roi.buffer_info.data) {
|
|
g_free((gpointer)venc->roi.buffer_info.data);
|
|
venc->roi.buffer_info.data = NULL;
|
|
}
|
|
|
|
cleanup_roi_param_list(venc);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
gst_amlh264venc_flush (GstVideoEncoder * encoder)
|
|
{
|
|
GstAmlH264VEnc *venc = GST_AMLH264VENC (encoder);
|
|
|
|
gst_amlh264venc_init_encoder (venc);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* gst_amlh264venc_init_encoder
|
|
* @encoder: Encoder which should be initialized.
|
|
*
|
|
* Initialize v encoder.
|
|
*
|
|
*/
|
|
static gboolean
|
|
gst_amlh264venc_init_encoder (GstAmlH264VEnc * encoder)
|
|
{
|
|
GstVideoInfo *info;
|
|
|
|
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_amlh264venc_close_encoder (encoder);
|
|
|
|
GST_OBJECT_LOCK (encoder);
|
|
|
|
|
|
GST_OBJECT_UNLOCK (encoder);
|
|
|
|
vl_encode_info_t encode_info;
|
|
memset (&encode_info, 0, sizeof(vl_encode_info_t));
|
|
|
|
encode_info.width = info->width;
|
|
encode_info.height = info->height;
|
|
encode_info.frame_rate = encoder->framerate;
|
|
encode_info.bit_rate = encoder->bitrate * 1000;
|
|
encode_info.gop = encoder->gop;
|
|
encode_info.img_format = img_format_convert(GST_VIDEO_INFO_FORMAT(info));
|
|
|
|
qp_param_t qp_tbl;
|
|
memset(&qp_tbl, 0, sizeof(qp_param_t));
|
|
|
|
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 = vl_video_encoder_init(encoder->codec.id, encode_info.width,encode_info.height,encode_info.frame_rate,encode_info.bit_rate,encode_info.gop,encode_info.img_format, qp_tbl.qp_I_min, qp_tbl.qp_I_max,qp_tbl.qp_P_min,qp_tbl.qp_P_max);
|
|
|
|
if (encoder->codec.handle == 0) {
|
|
GST_ELEMENT_ERROR (encoder, STREAM, ENCODE,
|
|
("Can not initialize v encoder."), (NULL));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_amlh264venc_set_roi (encoder)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (GST_VIDEO_INFO_FORMAT(info) == GST_VIDEO_FORMAT_RGB ||
|
|
GST_VIDEO_INFO_FORMAT(info) == GST_VIDEO_FORMAT_BGR) {
|
|
encoder->imgproc.handle = imgproc_init();
|
|
if (encoder->imgproc.handle == NULL) {
|
|
GST_ELEMENT_ERROR (encoder, STREAM, ENCODE,
|
|
("Can not initialize imgproc."), (NULL));
|
|
return FALSE;
|
|
}
|
|
encoder->imgproc.outbuf_size = (info->width * info->height * 3) / 2;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* gst_amlh264venc_set_roi
|
|
* @encoder: update encoder roi value.
|
|
*
|
|
* Set roi value
|
|
*/
|
|
static gboolean
|
|
gst_amlh264venc_set_roi(GstAmlH264VEnc * encoder)
|
|
{
|
|
GstVideoInfo *info;
|
|
|
|
if (!encoder->input_state) {
|
|
GST_DEBUG_OBJECT (encoder, "Have no input state yet");
|
|
return FALSE;
|
|
}
|
|
|
|
info = &encoder->input_state->info;
|
|
gint vframe_w = info->width;
|
|
gint vframe_h = info->height;
|
|
|
|
gint buffer_w = encoder->roi.buffer_info.width;
|
|
gint buffer_h = encoder->roi.buffer_info.height;
|
|
|
|
if (encoder->roi.enabled) {
|
|
struct listnode *pos = NULL;
|
|
struct RoiParamInfo *param_info = NULL;
|
|
list_for_each(pos, &encoder->roi.param_info) {
|
|
param_info = list_entry(pos, struct RoiParamInfo, list);
|
|
GST_DEBUG("roi-id:%d, roi-left:%.6f, roi-top:%.6f, roi-width:%.6f, roi-height:%.6f, roi-quality:%d\n",
|
|
param_info->id,
|
|
param_info->location.left,
|
|
param_info->location.top,
|
|
param_info->location.width,
|
|
param_info->location.height,
|
|
param_info->quality);
|
|
|
|
gst_amlh264venc_fill_roi_buffer(
|
|
encoder->roi.buffer_info.data,
|
|
buffer_w, buffer_h,
|
|
param_info,
|
|
vframe_w,
|
|
vframe_h,
|
|
encoder->roi.block_size
|
|
);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
gint ret;
|
|
if (encoder->codec.handle) {
|
|
if ((ret = vl_video_encoder_update_qp_hint(
|
|
encoder->codec.handle,
|
|
encoder->roi.buffer_info.data,
|
|
buffer_w * buffer_h)) != 0) {
|
|
GST_DEBUG_OBJECT (encoder, "update roi value failed, ret:%d\n", ret);
|
|
return FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_amlh264venc_fill_roi_buffer(guchar* buffer, gint buffer_w, gint buffer_h,
|
|
struct RoiParamInfo *param_info, gint vframe_w, gint vframe_h, gint block_size) {
|
|
if (buffer == NULL || param_info == NULL) return;
|
|
|
|
gint left = param_info->location.left * vframe_w;
|
|
gint top = param_info->location.top * vframe_h;
|
|
gint width = param_info->location.width * vframe_w;
|
|
gint height = param_info->location.height * vframe_h;
|
|
|
|
gint right = left + width;
|
|
gint bottom = top + height;
|
|
|
|
gint limit = block_size / 2;
|
|
|
|
gint start_row = top / block_size;
|
|
gint start_col = left / block_size;
|
|
if ((left % block_size) > limit) start_col += 1;
|
|
if ((top % block_size) > limit) start_row += 1;
|
|
|
|
gint stop_row = bottom / block_size;
|
|
gint stop_col = right / block_size;
|
|
if ((right % block_size) >= limit) stop_col += 1;
|
|
if ((bottom % block_size) >= limit) stop_row += 1;
|
|
|
|
if (start_row <= stop_row && start_col <= stop_col) {
|
|
for (int i_row = start_row; i_row < stop_row; i_row++) {
|
|
for (int j_col = start_col; j_col < stop_col; j_col++) {
|
|
buffer[i_row * buffer_w + j_col] = param_info->quality;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
idle_set_roi(GstAmlH264VEnc * self) {
|
|
if (self != NULL) {
|
|
gst_amlh264venc_set_roi (self);
|
|
}
|
|
|
|
self->roi.srcid = 0;
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
|
|
/* gst_amlh264venc_close_encoder
|
|
* @encoder: Encoder which should close.
|
|
*
|
|
* Close v encoder.
|
|
*/
|
|
static void
|
|
gst_amlh264venc_close_encoder (GstAmlH264VEnc * encoder)
|
|
{
|
|
if (encoder->codec.handle != 0) {
|
|
vl_video_encoder_destroy(encoder->codec.handle);
|
|
encoder->codec.handle = 0;
|
|
}
|
|
if (encoder->imgproc.handle) {
|
|
imgproc_deinit(encoder->imgproc.handle);
|
|
encoder->imgproc.handle = NULL;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gst_amlh264venc_set_profile_and_level (GstAmlH264VEnc * 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_amlh264venc_set_src_caps
|
|
* Returns: TRUE on success.
|
|
*/
|
|
static gboolean
|
|
gst_amlh264venc_set_src_caps (GstAmlH264VEnc * encoder, GstCaps * caps)
|
|
{
|
|
GstCaps *outcaps;
|
|
GstStructure *structure;
|
|
GstVideoCodecState *state;
|
|
GstTagList *tags;
|
|
const gchar* mime_str = "video/x-h264";
|
|
|
|
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_amlh264venc_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_amlh264venc_set_latency (GstAmlH264VEnc * 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_amlh264venc_set_format (GstVideoEncoder * video_enc,
|
|
GstVideoCodecState * state)
|
|
{
|
|
GstAmlH264VEnc *encoder = GST_AMLH264VENC (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 == 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);
|
|
gst_caps_unref (allowed_caps);
|
|
}
|
|
|
|
gst_caps_unref (template_caps);
|
|
|
|
encoder->roi.buffer_info.width =
|
|
(info->width + encoder->roi.block_size - 1) / encoder->roi.block_size;
|
|
encoder->roi.buffer_info.height =
|
|
(info->height + encoder->roi.block_size - 1) / encoder->roi.block_size;
|
|
GST_DEBUG("info->width:%d, info->height:%d, roi_buffer_w:%d, roi_buffer_h:%d",
|
|
info->width, info->height, encoder->roi.buffer_info.width, encoder->roi.buffer_info.height);
|
|
if (encoder->roi.buffer_info.data == NULL) {
|
|
encoder->roi.buffer_info.data =
|
|
g_new(guchar, encoder->roi.buffer_info.width * encoder->roi.buffer_info.width );
|
|
memset(encoder->roi.buffer_info.data,
|
|
PROP_ROI_QUALITY_DEFAULT,
|
|
encoder->roi.buffer_info.width * encoder->roi.buffer_info.height);
|
|
}
|
|
|
|
if (!gst_amlh264venc_init_encoder (encoder))
|
|
return FALSE;
|
|
|
|
if (!gst_amlh264venc_set_src_caps (encoder, state->caps)) {
|
|
gst_amlh264venc_close_encoder (encoder);
|
|
return FALSE;
|
|
}
|
|
|
|
gst_amlh264venc_set_latency (encoder);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_amlh264venc_finish (GstVideoEncoder * encoder)
|
|
{
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
static gboolean
|
|
gst_amlh264venc_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
|
|
{
|
|
GstAmlH264VEnc *self = GST_AMLH264VENC (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;
|
|
}
|
|
|
|
if (self->b_enable_dmallocator) {
|
|
GstAllocator *allocator = NULL;
|
|
GstAllocationParams params;
|
|
/* we got configuration from our peer or the decide_allocation method,
|
|
* parse them */
|
|
if (gst_query_get_n_allocation_params (query) > 0) {
|
|
gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms);
|
|
} else {
|
|
allocator = NULL;
|
|
gst_allocation_params_init (¶ms);
|
|
}
|
|
|
|
/* For the camera + jpegdec + encoder case,jpegdec use videobuffer pool which is software allocate.
|
|
caused cp memory to dmabuffer during encoder frame.
|
|
currently,provide the dmaallocator to upstreamer element to avoid copy(optimize)
|
|
Try to update allocator*/
|
|
if (self->dmabuf_alloc)
|
|
gst_query_add_allocation_param (query, self->dmabuf_alloc, ¶ms);
|
|
|
|
if (allocator)
|
|
gst_object_unref (allocator);
|
|
}
|
|
|
|
return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder,
|
|
query);
|
|
}
|
|
|
|
/* chain function
|
|
* this function does the actual processing
|
|
*/
|
|
static GstFlowReturn
|
|
gst_amlh264venc_handle_frame (GstVideoEncoder * video_enc,
|
|
GstVideoCodecFrame * frame)
|
|
{
|
|
GstAmlH264VEnc *encoder = GST_AMLH264VENC (video_enc);
|
|
GstFlowReturn ret;
|
|
|
|
if (G_UNLIKELY (encoder->codec.handle == 0))
|
|
goto not_inited;
|
|
|
|
ret = gst_amlh264venc_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_amlh264venc_encode_frame (GstAmlH264VEnc * encoder,
|
|
GstVideoCodecFrame * frame)
|
|
{
|
|
vl_frame_type_t frame_type = FRAME_TYPE_AUTO;
|
|
GstVideoInfo *info = &encoder->input_state->info;
|
|
GstMapInfo map;
|
|
vl_dma_info_t s_dma_info;
|
|
guint8 ui1_plane_num = 1;
|
|
gint i4_encoder_buf_len = -1;
|
|
guint8 i1_format = 0;
|
|
|
|
memset(&s_dma_info,0,sizeof(vl_dma_info_t));
|
|
|
|
if (G_UNLIKELY (encoder->codec.handle == 0)) {
|
|
if (frame)
|
|
gst_video_codec_frame_unref (frame);
|
|
return GST_FLOW_NOT_NEGOTIATED;
|
|
}
|
|
|
|
if (frame) {
|
|
if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) {
|
|
GST_INFO_OBJECT (encoder, "Forcing key frame");
|
|
frame_type = FRAME_TYPE_IDR;
|
|
}
|
|
}
|
|
|
|
GstMemory *memory = gst_buffer_get_memory(frame->input_buffer, 0);
|
|
gboolean is_dmabuf = gst_is_dmabuf_memory(memory);
|
|
gint fd = -1;
|
|
GstMapInfo minfo;
|
|
GST_DEBUG_OBJECT(encoder, "is_dmabuf[%d] width[%d] height[%d]",is_dmabuf,info->width,info->height);
|
|
|
|
if (is_dmabuf) {
|
|
switch (GST_VIDEO_INFO_FORMAT(info)) {
|
|
case GST_VIDEO_FORMAT_NV12:
|
|
case GST_VIDEO_FORMAT_NV21:
|
|
{
|
|
/* handle dma case scenario media convet encoder/hdmi rx encoder scenario*/
|
|
encoder->fd[0] = gst_dmabuf_memory_get_fd(memory);
|
|
gst_memory_unref(memory);
|
|
GstMemory *memory_uv = gst_buffer_get_memory(frame->input_buffer, 1);
|
|
encoder->fd[1] = gst_dmabuf_memory_get_fd(memory_uv);
|
|
gst_memory_unref(memory_uv);
|
|
ui1_plane_num = 2;
|
|
break;
|
|
}
|
|
default: //hanle I420/YV12/RGB
|
|
{
|
|
/*
|
|
Currently,For 420sp and RGB case,use one plane.
|
|
420sp for usb camera case,usb camera y/u/v address is continuous and no alignment requiremnet.
|
|
Therefore,use one plane.
|
|
*/
|
|
encoder->fd[0] = gst_dmabuf_memory_get_fd(memory);
|
|
gst_memory_unref(memory);
|
|
ui1_plane_num = 1;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
gst_memory_unref(memory);
|
|
/*
|
|
non dmabuf case,due to encoder driver only handle dmabuf,so need convert to dma buffer case below.
|
|
*/
|
|
if (encoder->imgproc.input.memory == NULL) {
|
|
encoder->imgproc.input.memory =
|
|
gst_allocator_alloc(encoder->dmabuf_alloc, info->size, NULL);
|
|
if (encoder->imgproc.input.memory == NULL) {
|
|
GST_DEBUG_OBJECT(encoder, "failed to allocate new dma buffer");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
encoder->imgproc.input.fd = gst_dmabuf_memory_get_fd(encoder->imgproc.input.memory);
|
|
}
|
|
|
|
memory = encoder->imgproc.input.memory;
|
|
fd = encoder->imgproc.input.fd;
|
|
if (gst_memory_map(memory, &minfo, GST_MAP_WRITE)) {
|
|
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);
|
|
memcpy(minfo.data, pixel, info->size);
|
|
|
|
gst_video_frame_unmap (&video_frame);
|
|
gst_memory_unmap(memory, &minfo);
|
|
}
|
|
encoder->fd[0] = fd;
|
|
}
|
|
|
|
if (encoder->imgproc.handle) {
|
|
// format conversation needed
|
|
if (encoder->dmabuf_alloc == NULL) {
|
|
encoder->dmabuf_alloc = gst_amlion_allocator_obtain();
|
|
}
|
|
|
|
struct imgproc_buf inbuf, outbuf;
|
|
inbuf.fd = fd;
|
|
inbuf.is_ionbuf = gst_is_amlionbuf_memory(memory);
|
|
|
|
if (encoder->imgproc.output.memory == NULL) {
|
|
encoder->imgproc.output.memory = gst_allocator_alloc(
|
|
encoder->dmabuf_alloc, encoder->imgproc.outbuf_size, NULL);
|
|
if (encoder->imgproc.output.memory == NULL) {
|
|
GST_ERROR_OBJECT(encoder, "failed to allocate new dma buffer");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
encoder->imgproc.output.fd = gst_dmabuf_memory_get_fd(encoder->imgproc.output.memory);
|
|
}
|
|
|
|
fd = encoder->imgproc.output.fd;
|
|
|
|
outbuf.fd = fd;
|
|
outbuf.is_ionbuf = TRUE;
|
|
|
|
struct imgproc_pos inpos = {
|
|
0, 0, info->width, info->height, info->width, info->height};
|
|
struct imgproc_pos outpos = {
|
|
0, 0, info->width, info->height, info->width, info->height};
|
|
|
|
imgproc_crop(encoder->imgproc.handle, inbuf, inpos,
|
|
GST_VIDEO_INFO_FORMAT(info), outbuf, outpos,
|
|
GST_VIDEO_FORMAT_NV12);
|
|
|
|
encoder->fd[0] = fd;
|
|
}
|
|
|
|
s_dma_info.num_planes = ui1_plane_num;
|
|
s_dma_info.shared_fd[0] = encoder->fd[0];
|
|
s_dma_info.shared_fd[1] = encoder->fd[1];
|
|
s_dma_info.shared_fd[2] = encoder->fd[2];
|
|
|
|
switch (GST_VIDEO_INFO_FORMAT(info)) {
|
|
case GST_VIDEO_FORMAT_NV12:
|
|
i1_format = 0;
|
|
break;
|
|
case GST_VIDEO_FORMAT_NV21:
|
|
i1_format = 1;
|
|
break;
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
i1_format = 2;
|
|
break;
|
|
default:
|
|
i1_format = 0;
|
|
break;
|
|
}
|
|
|
|
i4_encoder_buf_len =
|
|
vl_video_encoder_encode(encoder->codec.handle, frame_type,NULL,info->size,
|
|
encoder->codec.buf, i1_format,DMA_BUFF,&s_dma_info);
|
|
|
|
if (i4_encoder_buf_len <= 0) {
|
|
if (frame) {
|
|
GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, ("Encode v frame failed."),
|
|
("gst_amlvencoder_encode return code=%d", i4_encoder_buf_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), i4_encoder_buf_len);
|
|
gst_buffer_map(frame->output_buffer, &map, GST_MAP_WRITE);
|
|
memcpy (map.data, encoder->codec.buf, i4_encoder_buf_len);
|
|
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 == 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_amlh264venc_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstAmlH264VEnc *encoder = GST_AMLH264VENC (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;
|
|
case PROP_ROI_ENABLED:
|
|
g_value_set_boolean (value, encoder->roi.enabled);
|
|
break;
|
|
case PROP_ROI_ID:
|
|
g_value_set_int (value, encoder->roi.id);
|
|
break;
|
|
case PROP_ROI_WIDTH: {
|
|
struct RoiParamInfo *param_info = retrieve_roi_param_info(encoder, encoder->roi.id);
|
|
g_value_set_float (value, param_info->location.width);
|
|
} break;
|
|
case PROP_ROI_HEIGHT: {
|
|
struct RoiParamInfo *param_info = retrieve_roi_param_info(encoder, encoder->roi.id);
|
|
g_value_set_float (value, param_info->location.height);
|
|
} break;
|
|
case PROP_ROI_X: {
|
|
struct RoiParamInfo *param_info = retrieve_roi_param_info(encoder, encoder->roi.id);
|
|
g_value_set_float (value, param_info->location.left);
|
|
} break;
|
|
case PROP_ROI_Y: {
|
|
struct RoiParamInfo *param_info = retrieve_roi_param_info(encoder, encoder->roi.id);
|
|
g_value_set_float (value, param_info->location.top);
|
|
} break;
|
|
case PROP_ROI_QUALITY: {
|
|
struct RoiParamInfo *param_info = retrieve_roi_param_info(encoder, encoder->roi.id);
|
|
g_value_set_int (value, param_info->quality);
|
|
} break;
|
|
case PROD_ENABLE_DMALLOCATOR:
|
|
g_value_set_boolean (value, encoder->b_enable_dmallocator);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
GST_OBJECT_UNLOCK (encoder);
|
|
}
|
|
|
|
static void
|
|
gst_amlh264venc_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstAmlH264VEnc *encoder = GST_AMLH264VENC (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;
|
|
case PROP_ROI_ENABLED: {
|
|
gboolean enabled = g_value_get_boolean (value);
|
|
if (!enabled) {
|
|
cleanup_roi_param_list(encoder);
|
|
memset(encoder->roi.buffer_info.data,
|
|
PROP_ROI_QUALITY_DEFAULT,
|
|
encoder->roi.buffer_info.width * encoder->roi.buffer_info.height);
|
|
}
|
|
if (enabled != encoder->roi.enabled) {
|
|
encoder->roi.enabled = enabled;
|
|
roi_set_flag = TRUE;
|
|
}
|
|
} break;
|
|
case PROP_ROI_ID: {
|
|
gint id = g_value_get_int (value);
|
|
if (id != encoder->roi.id) {
|
|
encoder->roi.id = id;
|
|
}
|
|
} break;
|
|
case PROP_ROI_WIDTH: {
|
|
struct RoiParamInfo *param_info = retrieve_roi_param_info(encoder, encoder->roi.id);
|
|
param_info->location.width = g_value_get_float (value);
|
|
} break;
|
|
case PROP_ROI_HEIGHT: {
|
|
struct RoiParamInfo *param_info = retrieve_roi_param_info(encoder, encoder->roi.id);
|
|
param_info->location.height = g_value_get_float (value);
|
|
} break;
|
|
case PROP_ROI_X: {
|
|
struct RoiParamInfo *param_info = retrieve_roi_param_info(encoder, encoder->roi.id);
|
|
param_info->location.left = g_value_get_float (value);
|
|
} break;
|
|
case PROP_ROI_Y: {
|
|
struct RoiParamInfo *param_info = retrieve_roi_param_info(encoder, encoder->roi.id);
|
|
param_info->location.top = g_value_get_float (value);
|
|
} break;
|
|
case PROP_ROI_QUALITY: {
|
|
gint quality = g_value_get_int (value);
|
|
struct RoiParamInfo *param_info = retrieve_roi_param_info(encoder, encoder->roi.id);
|
|
if (quality != param_info->quality) {
|
|
param_info->quality = quality;
|
|
roi_set_flag = TRUE;
|
|
}
|
|
} break;
|
|
case PROD_ENABLE_DMALLOCATOR: {
|
|
gboolean enabled = g_value_get_boolean (value);
|
|
encoder->b_enable_dmallocator = enabled;
|
|
break;
|
|
}
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
|
|
if (roi_set_flag) {
|
|
if (encoder->roi.srcid)
|
|
g_source_remove (encoder->roi.srcid);
|
|
encoder->roi.srcid = g_idle_add((GSourceFunc)idle_set_roi, encoder);
|
|
}
|
|
|
|
GST_OBJECT_UNLOCK (encoder);
|
|
return;
|
|
}
|
|
|
|
static gboolean
|
|
amlh264venc_init (GstPlugin * amlh264venc)
|
|
{
|
|
GST_DEBUG_CATEGORY_INIT (gst_amlh264venc_debug, "amlh264venc", 0,
|
|
"amlogic h264 encoding element");
|
|
|
|
return gst_element_register (amlh264venc, "amlh264venc", GST_RANK_PRIMARY,
|
|
GST_TYPE_AMLH264VENC);
|
|
}
|
|
|
|
#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,
|
|
amlh264venc,
|
|
"Amlogic h264 encoder plugins",
|
|
amlh264venc_init,
|
|
VERSION,
|
|
"LGPL",
|
|
"amlogic h264 ecoding",
|
|
"http://openlinux.amlogic.com"
|
|
)
|