linuxOS_D21X/source/artinchip/gst1-plugins-aic/gstvedec.c

579 lines
16 KiB
C
Raw Normal View History

2024-11-29 08:13:19 +00:00
/*
* Copyright (C) 2020-2023 ArtInChip Technology Co., Ltd.
*
* Authors: <qi.xu@artinchip.com>
*/
#include <string.h>
#include <gst/gst.h>
#include <gst/video/video.h>
#include "mpp_decoder.h"
#include "gstvedec.h"
#define gst_ve_dec_parent_class parent_class
G_DEFINE_TYPE (GstVeDec, gst_ve_dec, GST_TYPE_VIDEO_DECODER);
#define GST_VE_DEC_TASK_STARTED(decoder) \
(gst_pad_get_task_state ((decoder)->srcpad) == GST_TASK_STARTED)
struct gst_mpp_format {
GstVideoFormat gst_format;
enum mpp_pixel_format mpp_format;
};
struct gst_mpp_format gst_mpp_formats[] = {
{GST_VIDEO_FORMAT_I420, MPP_FMT_YUV420P},
{GST_VIDEO_FORMAT_NV12, MPP_FMT_NV12},
{GST_VIDEO_FORMAT_NV21, MPP_FMT_NV21},
{GST_VIDEO_FORMAT_RGB, MPP_FMT_RGB_888},
{GST_VIDEO_FORMAT_BGR, MPP_FMT_BGR_888},
{GST_VIDEO_FORMAT_RGBA, MPP_FMT_RGBA_8888},
{GST_VIDEO_FORMAT_BGRA, MPP_FMT_BGRA_8888},
{GST_VIDEO_FORMAT_ARGB, MPP_FMT_ARGB_8888},
{GST_VIDEO_FORMAT_ABGR, MPP_FMT_ABGR_8888},
};
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
#endif
#define GET_GST_FORMAT(format) ({ \
struct gst_mpp_format *_tmp; \
for (guint i = 0; i < ARRAY_SIZE (gst_mpp_formats); i++) { \
_tmp = &gst_mpp_formats[i]; \
if (_tmp->mpp_format == format) break;\
}; _tmp->gst_format; \
})
static gboolean gst_ve_dec_open (GstVideoDecoder * bdec)
{
GST_DEBUG_OBJECT(bdec, "gst_ve_dec_open");
return TRUE;
}
static gboolean gst_ve_dec_close (GstVideoDecoder * bdec)
{
GST_DEBUG_OBJECT(bdec, "gst_ve_dec_close");
return TRUE;
}
static gboolean gst_ve_dec_start (GstVideoDecoder * bdec)
{
GstVeDec *dec = GST_VE_DEC(bdec);
dec->pts2frame = g_hash_table_new(NULL, NULL);
return TRUE;
}
static gboolean gst_ve_dec_stop (GstVideoDecoder * bdec)
{
GstVeDec *dec = GST_VE_DEC(bdec);
if(dec->pts2frame) {
g_hash_table_destroy(dec->pts2frame);
dec->pts2frame = NULL;
}
dec->stop_flag = 1;
if (dec->input_state) {
gst_video_codec_state_unref (dec->input_state);
dec->input_state = NULL;
}
gst_pad_stop_task (bdec->srcpad);
gst_task_stop(dec->dec_task);
#if USE_RETURN_BUF
pthread_mutex_destroy(&dec->frames_lock);
#endif
return TRUE;
}
static void gst_decode_loop (GstVeDec * self)
{
int ret;
if (self->stop_flag)
return;
ret = mpp_decoder_decode(self->mpp_dec);
if(ret < 0) {
GST_ERROR_OBJECT(self, "decode error");
self->dec_task_ret = GST_FLOW_ERROR;
} else if(ret > 0) {
g_usleep(1000);
}
}
static gboolean update_video_info(GstVideoDecoder * decoder, GstVideoFormat format,
guint width, guint height)
{
GstVeDec *self = GST_VE_DEC (decoder);
GstVideoInfo *info = &self->info;
GstVideoCodecState *output_state;
output_state = gst_video_decoder_set_output_state (decoder, format,
GST_ROUND_UP_2 (width), GST_ROUND_UP_2 (height), self->input_state);
output_state->caps = gst_video_info_to_caps (&output_state->info);
*info = output_state->info;
gst_video_codec_state_unref (output_state);
if (!gst_video_decoder_negotiate (decoder))
return FALSE;
return TRUE;
}
static void gst_render_loop (GstVideoDecoder * decoder)
{
GstVeDec *self = GST_VE_DEC (decoder);
GstMapInfo output_minfo;
GstVideoCodecFrame *out_frame;
struct mpp_frame frame;
int dec_ret = 0;
if (self->stop_flag) {
GST_DEBUG_OBJECT(self, "eos");
self->task_ret = GST_FLOW_EOS;
goto out;
}
dec_ret = mpp_decoder_get_frame(self->mpp_dec, &frame);
if(dec_ret == DEC_NO_RENDER_FRAME || dec_ret == DEC_ERR_FM_NOT_CREATE
|| dec_ret == DEC_NO_EMPTY_FRAME) {
g_usleep(1000);
return;
} else if(dec_ret) {
GST_ERROR_OBJECT(self, "mpp_dec_get_frame error, ret: %x", dec_ret);
return;
}
if (self->width != frame.buf.size.width || self->height != frame.buf.size.height) {
GstVideoFormat gst_format = GET_GST_FORMAT(frame.buf.format);
update_video_info(decoder, gst_format, frame.buf.size.width, frame.buf.size.height);
self->width = frame.buf.size.width;
self->height = frame.buf.size.height;
}
out_frame = g_hash_table_lookup(self->pts2frame, (gpointer)frame.pts);
if(out_frame == NULL) {
GST_ERROR_OBJECT(self, "cannot find out_frame, maybe error");
}
g_hash_table_remove(self->pts2frame, (gpointer)frame.pts);
if (out_frame->output_buffer == NULL)
out_frame->output_buffer = gst_buffer_new_allocate(NULL, sizeof(struct mpp_frame), NULL);
//get_gst_buffer(&frame);
out_frame->output_buffer->offset = 0;
out_frame->output_buffer->pts = frame.pts;
out_frame->output_buffer->dts = out_frame->input_buffer->dts;
out_frame->output_buffer->duration = out_frame->duration;
GST_ERROR_OBJECT(self, "frame.pts: %lld, %ld, id: %d, duration: %ld",
frame.pts, out_frame->pts, frame.id, out_frame->duration);
gst_buffer_map (out_frame->output_buffer, &output_minfo, GST_MAP_READ | GST_MAP_WRITE);
memcpy(output_minfo.data, &frame, sizeof(struct mpp_frame));
#if USE_RETURN_BUF
// find an empty frame from frame info array
pthread_mutex_lock(&self->frames_lock);
int id = frame.id;
if (self->render_frames[id].used) {
GST_ERROR_OBJECT(self, "it is used now, maybe error");
}
GST_ERROR_OBJECT(self, "render frame id: %d", id);
memcpy(&self->render_frames[id].frame, &frame, sizeof(struct mpp_frame));
self->render_frames[id].used = 1;
pthread_mutex_unlock(&self->frames_lock);
#endif
gst_buffer_unmap (out_frame->output_buffer, &output_minfo);
gst_video_codec_frame_ref(out_frame);
gst_video_decoder_finish_frame(decoder, out_frame);
GST_DEBUG_OBJECT(self, "video dec ts: %" GST_TIME_FORMAT ", dur:%" GST_TIME_FORMAT" \n",
GST_TIME_ARGS (GST_BUFFER_PTS (out_frame->input_buffer)),
GST_TIME_ARGS (GST_BUFFER_DURATION (out_frame->input_buffer)));
GST_DEBUG_OBJECT(self, "out video dec ts: %" GST_TIME_FORMAT ", dur:%" GST_TIME_FORMAT" \n",
GST_TIME_ARGS (GST_BUFFER_PTS (out_frame->output_buffer)),
GST_TIME_ARGS (GST_BUFFER_DURATION (out_frame->output_buffer)));
#if (!USE_RETURN_BUF)
mpp_decoder_put_frame(self->mpp_dec, &frame);
#endif
out:
if (self->task_ret != GST_FLOW_OK) {
gst_pad_pause_task(decoder->srcpad);
}
}
static GstFlowReturn gst_ve_dec_handle_frame (GstVideoDecoder * bdec, GstVideoCodecFrame * in_frame)
{
GstVeDec *dec = GST_VE_DEC(bdec);
GstMapInfo input_minfo;
struct mpp_packet packet;
int timeout_us = 2000000; // 2s
int wait_time_us = 10000;
GstFlowReturn ret = GST_FLOW_OK;
memset(&packet, 0, sizeof(struct mpp_packet));
if (!GST_VE_DEC_TASK_STARTED(bdec)) {
gst_pad_start_task (bdec->srcpad,
(GstTaskFunction) gst_render_loop, bdec, NULL);
gst_task_start(dec->dec_task);
}
if (in_frame == NULL) {
GST_ERROR_OBJECT(dec, "frame is NULL");
return GST_FLOW_ERROR;
}
gst_buffer_map (in_frame->input_buffer, &input_minfo, GST_MAP_READ);
GST_VIDEO_DECODER_STREAM_UNLOCK (bdec);
while (1) {
int ret = mpp_decoder_get_packet(dec->mpp_dec, &packet, input_minfo.size);
if (ret == 0) {
break;
}
g_usleep(wait_time_us);
timeout_us -= wait_time_us;
if(timeout_us < 0) {
GST_DEBUG_OBJECT(dec, "fail to get packet");
ret = GST_FLOW_ERROR;
gst_video_decoder_release_frame (bdec, in_frame);
goto done;
}
}
packet.size = input_minfo.size;
memcpy(packet.data, input_minfo.data, input_minfo.size);
packet.pts = in_frame->pts;
mpp_decoder_put_packet(dec->mpp_dec, &packet);
g_hash_table_insert(dec->pts2frame, (gpointer)in_frame->pts, (gpointer)in_frame);
GST_VIDEO_DECODER_STREAM_LOCK (bdec);
done:
gst_buffer_unmap (in_frame->input_buffer, &input_minfo);
gst_video_codec_frame_unref (in_frame);
return ret == GST_FLOW_ERROR ? GST_FLOW_ERROR : dec->task_ret;
}
static gboolean gst_ve_dec_set_format (GstVideoDecoder * bdec, GstVideoCodecState * state)
{
GstVeDec *dec = GST_VE_DEC(bdec);
const gchar *mimetype = gst_structure_get_name (gst_caps_get_structure (state->caps, 0));
if(!strcmp(mimetype, "video/x-h264")) {
dec->dec_type = MPP_CODEC_VIDEO_DECODER_H264;
} else if(!strcmp(mimetype, "image/jpeg")) {
dec->dec_type = MPP_CODEC_VIDEO_DECODER_MJPEG;
} else if(!strcmp(mimetype, "image/png")) {
dec->dec_type = MPP_CODEC_VIDEO_DECODER_PNG;
}
if(dec->mpp_dec) {
mpp_decoder_destory(dec->mpp_dec);
dec->mpp_dec = NULL;
}
dec->mpp_dec = mpp_decoder_create(dec->dec_type);
struct decode_config config;
config.bitstream_buffer_size = 1024*1024;
config.extra_frame_num = 1;
config.packet_count = 10;
if(dec->dec_type == MPP_CODEC_VIDEO_DECODER_PNG)
config.pix_fmt = MPP_FMT_RGBA_8888;
else
config.pix_fmt = MPP_FMT_YUV420P;
//* 2. init mpp_decoder
mpp_decoder_init(dec->mpp_dec, &config);
//* put extra data
GstMapInfo minfo;
gst_buffer_map (state->codec_data, &minfo, GST_MAP_READ);
struct mpp_packet packet;
memset(&packet, 0, sizeof(struct mpp_packet));
mpp_decoder_get_packet(dec->mpp_dec, &packet, minfo.size);
memcpy(packet.data, minfo.data, minfo.size);
packet.size = minfo.size;
packet.flag = PACKET_FLAG_EXTRA_DATA;
mpp_decoder_put_packet(dec->mpp_dec, &packet);
gst_buffer_unmap (state->codec_data, &minfo);
// decode extradata
if(mpp_decoder_decode(dec->mpp_dec) < 0) {
GST_ERROR_OBJECT(dec, "decode extradata failed");
}
dec->input_state = gst_video_codec_state_ref (state);
return TRUE;
}
static GstFlowReturn gst_ve_dec_finish (GstVideoDecoder * bdec)
{
GstVeDec *dec = GST_VE_DEC(bdec);
dec->stop_flag = 1;
return TRUE;
}
static gboolean gst_ve_dec_decide_allocation (GstVideoDecoder * bdec, GstQuery * query)
{
GstVeDec *decoder = GST_VE_DEC(bdec);
GstCaps *outcaps = NULL;
GstBufferPool *pool = NULL;
guint size, min, max;
GstAllocator *allocator = NULL;
GstAllocationParams params;
GstStructure *config;
gboolean update_pool, update_allocator;
GstVideoInfo vinfo;
gst_query_parse_allocation (query, &outcaps, NULL);
gst_video_info_init (&vinfo);
if (outcaps)
gst_video_info_from_caps (&vinfo, outcaps);
/* we got configuration from our peer or the decide_allocation method,
* parse them */
if (gst_query_get_n_allocation_params (query) > 0) {
/* try the allocator */
gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
update_allocator = TRUE;
} else {
allocator = NULL;
gst_allocation_params_init (&params);
update_allocator = FALSE;
}
if (gst_query_get_n_allocation_pools (query) > 0) {
gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
size = MAX (size, vinfo.size);
update_pool = TRUE;
} else {
pool = NULL;
size = vinfo.size;
min = max = 0;
update_pool = FALSE;
}
if (pool == NULL) {
/* no pool, we can make our own */
GST_DEBUG_OBJECT (decoder, "no pool, making new pool");
pool = gst_video_buffer_pool_new ();
}
/* now configure */
config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
gst_buffer_pool_config_set_allocator (config, allocator, &params);
GST_DEBUG_OBJECT (decoder,
"setting config %" GST_PTR_FORMAT " in pool %" GST_PTR_FORMAT, config,
pool);
if (!gst_buffer_pool_set_config (pool, config)) {
config = gst_buffer_pool_get_config (pool);
/* If change are not acceptable, fallback to generic pool */
if (!gst_buffer_pool_config_validate_params (config, outcaps, size, min,
max)) {
GST_DEBUG_OBJECT (decoder, "unsupported pool, making new pool");
gst_object_unref (pool);
pool = gst_video_buffer_pool_new ();
gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
gst_buffer_pool_config_set_allocator (config, allocator, &params);
}
if (!gst_buffer_pool_set_config (pool, config))
goto config_failed;
}
if (update_allocator)
gst_query_set_nth_allocation_param (query, 0, allocator, &params);
else
gst_query_add_allocation_param (query, allocator, &params);
if (allocator)
gst_object_unref (allocator);
if (update_pool)
gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
else
gst_query_add_allocation_pool (query, pool, size, min, max);
if (pool)
gst_object_unref (pool);
return TRUE;
config_failed:
if (allocator)
gst_object_unref (allocator);
if (pool)
gst_object_unref (pool);
GST_ELEMENT_ERROR (decoder, RESOURCE, SETTINGS,
("Failed to configure the buffer pool"),
("Configuration is most likely invalid, please report this issue."));
return FALSE;
}
static gboolean gst_ve_dec_reset (GstVideoDecoder * bdec, gboolean hard)
{
return TRUE;
}
static GstCaps *get_sink_caps (void)
{
static GstCaps *caps = NULL;
caps = gst_caps_from_string ("video/x-h264");
GstCaps *newcaps = gst_caps_from_string ("image/jpeg");
gst_caps_append (caps, newcaps);
newcaps = gst_caps_from_string ("image/png");
gst_caps_append (caps, newcaps);
return gst_caps_ref (caps);
}
static GstCaps *get_src_caps (void)
{
static GstCaps *caps = NULL;
if (caps == NULL) {
caps = gst_caps_from_string (GST_VIDEO_CAPS_MAKE ("{ NV12, I420, YV12, NV16, Y444}"));
}
return gst_caps_ref (caps);
}
static void gst_ve_dec_finalize (GObject * object)
{
GstVeDec *self = (GstVeDec*)object;
gst_task_join(self->dec_task);
gst_object_unref(self->dec_task);
g_rec_mutex_clear (&self->lock);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean gst_ve_dec_sink_event (GstVideoDecoder * decoder,
GstEvent * event)
{
int ret;
GstVeDec *self = (GstVeDec*)decoder;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CUSTOM_UPSTREAM:
{
GST_ERROR_OBJECT(self, "======> sink event\n");
}
default:
break;
}
ret = GST_VIDEO_DECODER_CLASS (parent_class)->sink_event (decoder, event);
return ret;
}
static gboolean gst_ve_dec_src_event (GstVideoDecoder * decoder,
GstEvent * event)
{
int ret;
GstVeDec *self = (GstVeDec*)decoder;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CUSTOM_UPSTREAM:
{
const GstStructure *str = gst_event_get_structure (event);
if (gst_structure_has_name(str, "return-frame")) {
int val;
gst_structure_get_int(str, "id", &val);
GST_DEBUG_OBJECT(self, "return frame.id: %d\n", val);
#if USE_RETURN_BUF
int i;
mpp_decoder_put_frame(self->mpp_dec, &self->render_frames[val].frame);
pthread_mutex_lock(&self->frames_lock);
if (self->render_frames[val].used == 0) {
GST_ERROR_OBJECT(self, "this frame(%d) is not used, maybe error\n", i);
}
self->render_frames[val].used = 0;
pthread_mutex_unlock(&self->frames_lock);
#endif
}
}
default:
break;
}
ret = GST_VIDEO_DECODER_CLASS (parent_class)->src_event (decoder, event);
return ret;
}
static void gst_ve_dec_class_init (GstVeDecClass * klass)
{
GstElementClass *element_class;
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstVideoDecoderClass *vdec_class;
element_class = GST_ELEMENT_CLASS (klass);
vdec_class = GST_VIDEO_DECODER_CLASS (klass);
gst_element_class_add_pad_template (element_class,
gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, get_sink_caps()));
gst_element_class_add_pad_template (element_class,
gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, get_src_caps()));
// we must set metadata below, or plugin init failed
gst_element_class_set_static_metadata (element_class,
"VE-based video decoder", "Codec/Decoder/Video",
"Decode compressed video to raw data",
"<qi.xu@artinchip.com>");
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_ve_dec_finalize);
vdec_class->src_event = GST_DEBUG_FUNCPTR(gst_ve_dec_src_event);
vdec_class->sink_event = GST_DEBUG_FUNCPTR(gst_ve_dec_sink_event);
vdec_class->open = GST_DEBUG_FUNCPTR (gst_ve_dec_open);
vdec_class->close = GST_DEBUG_FUNCPTR (gst_ve_dec_close);
vdec_class->start = GST_DEBUG_FUNCPTR (gst_ve_dec_start);
vdec_class->stop = GST_DEBUG_FUNCPTR (gst_ve_dec_stop);
vdec_class->set_format = GST_DEBUG_FUNCPTR (gst_ve_dec_set_format);
vdec_class->handle_frame = GST_DEBUG_FUNCPTR (gst_ve_dec_handle_frame);
vdec_class->finish = GST_DEBUG_FUNCPTR (gst_ve_dec_finish);
vdec_class->decide_allocation = GST_DEBUG_FUNCPTR (gst_ve_dec_decide_allocation);
vdec_class->reset = GST_DEBUG_FUNCPTR (gst_ve_dec_reset);
GST_DEBUG_OBJECT(vdec_class, "gst_ve_dec_class_init end");
}
static void gst_ve_dec_init (GstVeDec * self)
{
/* As Ve can support stream mode. need call parser before decode */
gst_video_decoder_set_packetized (GST_VIDEO_DECODER (self), TRUE);
self->task_ret = GST_FLOW_OK;
self->dec_task = gst_task_new((GstTaskFunction) gst_decode_loop, self, NULL);
g_rec_mutex_init (&self->lock);
gst_task_set_lock (self->dec_task, &self->lock);
#if USE_RETURN_BUF
pthread_mutex_init(&self->frames_lock, NULL);
#endif
}