<commit_before>/*
 * Copyright (C) 2009 <NAME>
 *
 * Authors:
 * <NAME> <<EMAIL>>
 * <NAME> <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation
 * version 2.1 of the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include "gstdspvdec.h"
#include "plugin.h"

#include "dsp_bridge.h"

#include <string.h> /* for memcpy */

#include "log.h"

#define GST_CAT_DEFAULT gstdsp_debug
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

static inline bool
send_buffer(GstDspVDec *self,
	    dmm_buffer_t *buffer,
	    unsigned int id,
	    size_t len);

static inline void
map_buffer(GstDspVDec *self,
	   GstBuffer *g_buf,
	   dmm_buffer_t *d_buf);

static inline du_port_t *
du_port_new(guint buffer_count)
{
	du_port_t *p;
	p = calloc(1, sizeof(*p));

	p->buffer_count = buffer_count;
	p->sem = g_sem_new(0);

	return p;
}

static inline void
du_port_free(du_port_t *p)
{
	if (!p)
		return;

	g_sem_free(p->sem);

	free(p);
}

static inline void
g_sem_down_status(GSem *sem,
		  const GstFlowReturn *status)
{
	GstFlowReturn ret;
	g_mutex_lock(sem->mutex);

	while (sem->count == 0 &&
	       (ret = g_atomic_int_get(status)) == GST_FLOW_OK)
		g_cond_wait(sem->condition, sem->mutex);

	if (ret == GST_FLOW_OK)
		sem->count--;

	g_mutex_unlock(sem->mutex);
}

static inline void
g_sem_signal(GSem *sem)
{
	g_mutex_lock(sem->mutex);
	g_cond_signal(sem->condition);
	g_mutex_unlock(sem->mutex);
}

static inline void
g_sem_reset(GSem *sem,
	    guint count)
{
	g_mutex_lock(sem->mutex);
	sem->count = count;
	g_mutex_unlock(sem->mutex);
}

typedef struct
{
	uint32_t buffer_data;
	uint32_t buffer_size;
	uint32_t param_data;
	uint32_t param_size;
	uint32_t buffer_len;
	uint32_t silly_eos;
	uint32_t silly_buf_state;
	uint32_t silly_buf_active;
	uint32_t silly_buf_id;
	uint32_t reserved;
	uint32_t silly_arm_arg;
	uint32_t silly_arm_buffer_arg;
	uint32_t silly_arm_param_arg;
	uint32_t silly_out_buffer_index;
	uint32_t silly_in_buffer_index;
	uint32_t user_data;
	uint32_t stream_id;
} dsp_comm_t;

static GstElementClass *parent_class;

static inline GstCaps *
generate_sink_template(void)
{
	GstCaps *caps;
	GstStructure *struc;

	caps = gst_caps_new_empty();

	struc = gst_structure_new("video/mpeg",
				  "mpegversion", G_TYPE_INT, 4,
				  "systemstream", G_TYPE_BOOLEAN, FALSE,
				  NULL);

	gst_caps_append_structure(caps, struc);

	struc = gst_structure_new("video/x-divx",
				  "divxversion", GST_TYPE_INT_RANGE, 4, 5,
				  NULL);

	gst_caps_append_structure(caps, struc);

	struc = gst_structure_new("video/x-xvid",
				  NULL);

	gst_caps_append_structure(caps, struc);

	struc = gst_structure_new("video/x-3ivx",
				  NULL);

	gst_caps_append_structure(caps, struc);

	struc = gst_structure_new("video/x-h263",
				  "variant", G_TYPE_STRING, "itu",
				  NULL);

	gst_caps_append_structure(caps, struc);

	struc = gst_structure_new("video/x-h264",
				  NULL);

	gst_caps_append_structure(caps, struc);

	return caps;
}

static inline GstCaps *
generate_src_template(void)
{
	GstCaps *caps;
	GstStructure *struc;

	caps = gst_caps_new_empty();

	struc = gst_structure_new("video/x-raw-yuv",
				  "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('U', 'Y', 'V', 'Y'),
				  NULL);

	gst_caps_append_structure(caps, struc);

	return caps;
}

static inline void
got_message(GstDspVDec *self,
	    dsp_msg_t *msg)
{
	uint32_t id;
	uint32_t command_id;

	id = msg->cmd & 0x000000ff;
	command_id = msg->cmd & 0xffffff00;

	switch (command_id) {
		case 0x0500:
			pr_debug(self, "got flush");
			g_sem_up(self->flush);
			break;
		case 0x0600:
			{
				dmm_buffer_t *b;
				dmm_buffer_t *cur = self->port[id]->buffer;
				dsp_comm_t *msg_data = cur->data;

				pr_debug(self, "got %s buffer", id == 0 ? "input" : "output");

				b = (void *) msg_data->user_data;

				if (id == 1) {
					self->out_buffer = b;
					if (g_atomic_int_get(&self->status) == GST_FLOW_OK)
						g_sem_up(self->port[1]->sem);
				}
				else {
					if (g_atomic_int_get(&self->status) == GST_FLOW_OK)
						g_sem_up(self->port[0]->sem);
					if (b->user_data)
						gst_buffer_unref(b->user_data);
					dmm_buffer_free(b);
				}
			}
			break;
		default:
			pr_warning(self, "unhandled command: %u", command_id);
	}
}

static inline dmm_buffer_t *
get_slot(GstDspVDec *self,
	 GstBuffer *new_buf)
{
	guint i;
	dmm_buffer_t *b = NULL;

	for (i = 0; i < ARRAY_SIZE(self->array); i++) {
		dmm_buffer_t *cur = self->array[i];
		if (cur && !cur->used) {
			if (cur->data == GST_BUFFER_DATA(new_buf)) {
				b = cur;
				b->user_data = new_buf;
				return b;
			}
		}
	}

	for (i = 0; i < ARRAY_SIZE(self->array); i++) {
		dmm_buffer_t *cur = self->array[i];
		if (!cur) {
			b = dmm_buffer_new(self->dsp_handle, self->proc);
			self->array[i] = b;
			goto found;
		}
	}

	for (i = 0; i < ARRAY_SIZE(self->array); i++) {
		dmm_buffer_t *cur = self->array[i];
		if (cur && !cur->used) {
			b = cur;
			goto found;
		}
	}

	return NULL;

found:
	map_buffer(self, new_buf, b);
	return b;
}

static void
output_loop(gpointer data)
{
	GstPad *pad;
	GstDspVDec *self;
	GstFlowReturn ret = GST_FLOW_OK;
	GstBuffer *new_buf, *out_buf;
	dmm_buffer_t *b;

	pad = data;
	self = GST_DSP_VDEC(gst_pad_get_parent(pad));

	pr_debug(self, "begin");
	g_sem_down_status(self->port[1]->sem, &self->status);

	b = self->out_buffer;

	if (!b) {
		pr_err(self, "out_buffer = NULL!");
		ret = GST_FLOW_ERROR;
		goto leave;
	}

	b->used = FALSE;

	ret = gst_pad_alloc_buffer_and_set_caps(self->srcpad,
						GST_BUFFER_OFFSET_NONE,
						self->output_buffer_size,
						GST_PAD_CAPS(self->srcpad),
						&new_buf);

	if (G_UNLIKELY(ret != GST_FLOW_OK)) {
		pr_err(self, "couldn't allocate buffer: %s", gst_flow_get_name(ret));
		goto leave;
	}

	dmm_buffer_invalidate(b, b->size);

	if (b->user_data) {
		dmm_buffer_t *tmp;
		out_buf = b->user_data;
		b->user_data = NULL;
		tmp = get_slot(self, new_buf);
		if (tmp)
			b = tmp;
		b->used = TRUE;
	}
	else {
		out_buf = new_buf;

		if (b->need_copy) {
			pr_info(self, "copy");
			memcpy(GST_BUFFER_DATA(out_buf), b->data, b->size);
		}
	}

	g_mutex_lock(self->ts_mutex);
	if (self->ts == GST_CLOCK_TIME_NONE)
		pr_err(self, "dang!");
	GST_BUFFER_TIMESTAMP(out_buf) = self->ts;
#ifdef TS_COUNT
	self->ts_count--;
	if (self->ts_count > 2 || self->ts_count < 1)
		pr_info(self, "tsc=%lu", self->ts_count);
#endif
	g_mutex_unlock(self->ts_mutex);

	send_buffer(self, b, 1, 0);

	ret = gst_pad_push(self->srcpad, out_buf);
	if (G_UNLIKELY(ret != GST_FLOW_OK)) {
		pr_info(self, "pad push failed: %s", gst_flow_get_name(ret));
		goto leave;
	}

leave:
	if (ret != GST_FLOW_OK) {
		g_atomic_int_set(&self->status, ret);
		gst_pad_pause_task(self->srcpad);
	}

	pr_debug(self, "end");
}

gpointer
dsp_thread(gpointer data)
{
	GstDspVDec *self = data;

	pr_info(self, "begin");

	while (!self->done) {
		unsigned int index = 0;
		pr_debug(self, "waiting for events");
		if (!dsp_wait_for_events(self->dsp_handle, self->events, 1, &index, 10000)) {
			pr_warning(self, "failed waiting for events");
			continue;
		}

		if (index == 0) {
			dsp_msg_t msg;
			while (true) {
				if (!dsp_node_get_message(self->dsp_handle, self->node, &msg, 100))
					break;
				pr_debug(self, "got dsp message: 0x%0x 0x%0x 0x%0x",
					 msg.cmd, msg.arg_1, msg.arg_2);
				got_message(self, &msg);
			}
		}
		else {
			pr_err(self, "wrong event index");
			g_atomic_int_set(&self->status, GST_FLOW_ERROR);
			goto leave;
		}
	}

leave:
	pr_info(self, "end");

	return NULL;
}

struct foo_data {
	unsigned long size;
	unsigned short data[42];
};

struct mp4vdec_args
{
	uint16_t num_streams;

	uint16_t in_id;
	uint16_t in_type;
	uint16_t in_count;

	uint16_t out_id;
	uint16_t out_type;
	uint16_t out_count;

	uint16_t reserved;

	uint32_t max_width;
	uint32_t max_height;
	uint32_t color_format;
	uint32_t max_framerate;
	uint32_t max_bitrate;
	uint32_t endianness;
	uint32_t profile;
	int32_t max_level;
	uint32_t mode;
	int32_t preroll;
	uint32_t display_width;
};

static inline void *
get_mp4v_args(GstDspVDec *self)
{
	struct mp4vdec_args args = {
		.num_streams = 2,
		.in_id = 0,
		.in_type = 0,
		.in_count = self->port[0]->buffer_count,
		.out_id = 1,
		.out_type = 0,
		.out_count = self->port[1]->buffer_count,
		.max_width = 848,
		.max_height = 480,
		.color_format = 4,
		.max_framerate = 0,
		.max_bitrate = -1,
		.endianness = 1,
		.profile = 0,
		.max_level = -1,
		.mode = 0,
		.preroll = 0,
		.display_width = 0,
	};

	struct foo_data *cb_data;

	cb_data = malloc(sizeof(*cb_data));
	cb_data->size = sizeof(args);
	memcpy(&cb_data->data, &args, sizeof(args));

	return cb_data;
}

struct h264vdec_args
{
	uint16_t num_streams;

	uint16_t in_id;
	uint16_t in_type;
	uint16_t in_count;

	uint16_t out_id;
	uint16_t out_type;
	uint16_t out_count;

	uint16_t reserved;

	uint32_t max_width;
	uint32_t max_height;
	uint32_t color_format;
	uint32_t max_framerate;
	uint32_t max_bitrate;
	uint32_t endianness;
	uint32_t profile;
	int32_t max_level;
	uint32_t mode;
	int32_t preroll;
	uint32_t stream_format;
	uint32_t display_width;
};

static inline void *
get_h264_args(GstDspVDec *self)
{
	struct h264vdec_args args = {
		.num_streams = 2,
		.in_id = 0,
		.in_type = 0,
		.in_count = self->port[0]->buffer_count,
		.out_id = 1,
		.out_type = 0,
		.out_count = self->port[1]->buffer_count,
		.max_width = 848,
		.max_height = 480,
		.color_format = 1,
		.max_framerate = 0,
		.max_bitrate = -1,
		.endianness = 1,
		.profile = 0,
		.max_level = -1,
		.mode = 0,
		.preroll = 0,
		.stream_format = 0,
		.display_width = 0,
	};

	struct foo_data *cb_data;

	cb_data = malloc(sizeof(*cb_data));
	cb_data->size = sizeof(args);
	memcpy(&cb_data->data, &args, sizeof(args));

	return cb_data;
}

static inline void *
create_node(GstDspVDec *self,
	    int dsp_handle,
	    void *proc)
{
	void *node;
	const dsp_uuid_t *alg_uuid;
	const char *alg_fn;
	const dsp_uuid_t mp4v_dec_uuid = { 0x7e4b8541, 0x47a1, 0x11d6, 0xb1, 0x56,
		{ 0x00, 0xb0, 0xd0, 0x17, 0x67, 0x4b } };

	const dsp_uuid_t h264v_dec_uuid = { 0xCB1E9F0F, 0x9D5A, 0x4434, 0x84, 0x49,
	    { 0x1F, 0xED, 0x2F, 0x99, 0x2D, 0xF7 } };

	const dsp_uuid_t usn_uuid = { 0x79A3C8B3, 0x95F2, 0x403F, 0x9A, 0x4B,
		{ 0xCF, 0x80, 0x57, 0x73, 0x05, 0x41 } };

	const dsp_uuid_t ringio_uuid = { 0x47698bfb, 0xa7ee, 0x417e, 0xa6, 0x7a,
		{ 0x41, 0xc0, 0x27, 0x9e, 0xb8, 0x05 } };

	if (!dsp_register(dsp_handle, &ringio_uuid, DSP_DCD_LIBRARYTYPE, "/lib/dsp/ringio.dll64P")) {
		pr_err(self, "failed to register ringio node library");
		return NULL;
	}

	if (!dsp_register(dsp_handle, &usn_uuid, DSP_DCD_LIBRARYTYPE, "/lib/dsp/usn.dll64P")) {
		pr_err(self, "failed to register usn node library");
		return NULL;
	}

	switch (self->alg) {
		case GSTDSP_MPEG4VDEC:
			alg_uuid = &mp4v_dec_uuid;
			alg_fn = "/lib/dsp/mp4vdec_sn.dll64P";
			break;
		case GSTDSP_H264DEC:
			alg_uuid = &h264v_dec_uuid;
			alg_fn = "/lib/dsp/h264vdec_sn.dll64P";
			break;
		default:
			pr_err(self, "unknown algorithm");
			return NULL;
	}

	if (!dsp_register(dsp_handle, alg_uuid, DSP_DCD_LIBRARYTYPE, alg_fn)) {
		pr_err(self, "failed to register mp4vdec node library");
		return NULL;
	}

	if (!dsp_register(dsp_handle, alg_uuid, DSP_DCD_NODETYPE, alg_fn)) {
		pr_err(self, "failed to register mp4vdec node");
		return NULL;
	}

	{
		struct dsp_node_attr_in attrs = {
			.cb = sizeof(attrs),
			.priority = 5,
			.timeout = 1000,
			.heap_size = 0,
			.gpp_va = 0,
		};
		void *cb_data;

		switch (self->alg) {
			case GSTDSP_MPEG4VDEC:<commit_msg>vdec: cleanup<commit_after>/*
 * Copyright (C) 2009 <NAME>
 *
 * Authors:
 * <NAME> <<EMAIL>>
 * <NAME> <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation
 * version 2.1 of the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include "gstdspvdec.h"
#include "plugin.h"

#include "dsp_bridge.h"

#include <string.h> /* for memcpy */

#include "log.h"

#define GST_CAT_DEFAULT gstdsp_debug
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

static inline bool
send_buffer(GstDspVDec *self,
	    dmm_buffer_t *buffer,
	    unsigned int id,
	    size_t len);

static inline void
map_buffer(GstDspVDec *self,
	   GstBuffer *g_buf,
	   dmm_buffer_t *d_buf);

static inline du_port_t *
du_port_new(guint buffer_count)
{
	du_port_t *p;
	p = calloc(1, sizeof(*p));

	p->buffer_count = buffer_count;
	p->sem = g_sem_new(0);

	return p;
}

static inline void
du_port_free(du_port_t *p)
{
	if (!p)
		return;

	g_sem_free(p->sem);

	free(p);
}

static inline void
g_sem_down_status(GSem *sem,
		  const GstFlowReturn *status)
{
	GstFlowReturn ret;
	g_mutex_lock(sem->mutex);

	while (sem->count == 0 &&
	       (ret = g_atomic_int_get(status)) == GST_FLOW_OK)
		g_cond_wait(sem->condition, sem->mutex);

	if (ret == GST_FLOW_OK)
		sem->count--;

	g_mutex_unlock(sem->mutex);
}

static inline void
g_sem_signal(GSem *sem)
{
	g_mutex_lock(sem->mutex);
	g_cond_signal(sem->condition);
	g_mutex_unlock(sem->mutex);
}

static inline void
g_sem_reset(GSem *sem,
	    guint count)
{
	g_mutex_lock(sem->mutex);
	sem->count = count;
	g_mutex_unlock(sem->mutex);
}

typedef struct
{
	uint32_t buffer_data;
	uint32_t buffer_size;
	uint32_t param_data;
	uint32_t param_size;
	uint32_t buffer_len;
	uint32_t silly_eos;
	uint32_t silly_buf_state;
	uint32_t silly_buf_active;
	uint32_t silly_buf_id;
	uint32_t reserved;
	uint32_t silly_arm_arg;
	uint32_t silly_arm_buffer_arg;
	uint32_t silly_arm_param_arg;
	uint32_t silly_out_buffer_index;
	uint32_t silly_in_buffer_index;
	uint32_t user_data;
	uint32_t stream_id;
} dsp_comm_t;

static GstElementClass *parent_class;

static inline GstCaps *
generate_sink_template(void)
{
	GstCaps *caps;
	GstStructure *struc;

	caps = gst_caps_new_empty();

	struc = gst_structure_new("video/mpeg",
				  "mpegversion", G_TYPE_INT, 4,
				  "systemstream", G_TYPE_BOOLEAN, FALSE,
				  NULL);

	gst_caps_append_structure(caps, struc);

	struc = gst_structure_new("video/x-divx",
				  "divxversion", GST_TYPE_INT_RANGE, 4, 5,
				  NULL);

	gst_caps_append_structure(caps, struc);

	struc = gst_structure_new("video/x-xvid",
				  NULL);

	gst_caps_append_structure(caps, struc);

	struc = gst_structure_new("video/x-3ivx",
				  NULL);

	gst_caps_append_structure(caps, struc);

	struc = gst_structure_new("video/x-h263",
				  "variant", G_TYPE_STRING, "itu",
				  NULL);

	gst_caps_append_structure(caps, struc);

	struc = gst_structure_new("video/x-h264",
				  NULL);

	gst_caps_append_structure(caps, struc);

	return caps;
}

static inline GstCaps *
generate_src_template(void)
{
	GstCaps *caps;
	GstStructure *struc;

	caps = gst_caps_new_empty();

	struc = gst_structure_new("video/x-raw-yuv",
				  "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('U', 'Y', 'V', 'Y'),
				  NULL);

	gst_caps_append_structure(caps, struc);

	return caps;
}

static inline void
got_message(GstDspVDec *self,
	    dsp_msg_t *msg)
{
	uint32_t id;
	uint32_t command_id;

	id = msg->cmd & 0x000000ff;
	command_id = msg->cmd & 0xffffff00;

	switch (command_id) {
		case 0x0500:
			pr_debug(self, "got flush");
			g_sem_up(self->flush);
			break;
		case 0x0600:
			{
				dmm_buffer_t *b;
				dmm_buffer_t *cur = self->port[id]->buffer;
				dsp_comm_t *msg_data = cur->data;

				pr_debug(self, "got %s buffer", id == 0 ? "input" : "output");

				b = (void *) msg_data->user_data;

				if (id == 1) {
					self->out_buffer = b;
					if (g_atomic_int_get(&self->status) == GST_FLOW_OK)
						g_sem_up(self->port[1]->sem);
				}
				else {
					if (g_atomic_int_get(&self->status) == GST_FLOW_OK)
						g_sem_up(self->port[0]->sem);
					if (b->user_data)
						gst_buffer_unref(b->user_data);
					dmm_buffer_free(b);
				}
			}
			break;
		default:
			pr_warning(self, "unhandled command: %u", command_id);
	}
}

static inline dmm_buffer_t *
get_slot(GstDspVDec *self,
	 GstBuffer *new_buf)
{
	guint i;
	dmm_buffer_t *b = NULL;

	for (i = 0; i < ARRAY_SIZE(self->array); i++) {
		dmm_buffer_t *cur = self->array[i];
		if (cur && !cur->used) {
			if (cur->data == GST_BUFFER_DATA(new_buf)) {
				b = cur;
				b->user_data = new_buf;
				return b;
			}
		}
	}

	for (i = 0; i < ARRAY_SIZE(self->array); i++) {
		dmm_buffer_t *cur = self->array[i];
		if (!cur) {
			b = dmm_buffer_new(self->dsp_handle, self->proc);
			self->array[i] = b;
			goto found;
		}
	}

	for (i = 0; i < ARRAY_SIZE(self->array); i++) {
		dmm_buffer_t *cur = self->array[i];
		if (cur && !cur->used) {
			b = cur;
			goto found;
		}
	}

	return NULL;

found:
	map_buffer(self, new_buf, b);
	return b;
}

static void
output_loop(gpointer data)
{
	GstPad *pad;
	GstDspVDec *self;
	GstFlowReturn ret = GST_FLOW_OK;
	GstBuffer *new_buf, *out_buf;
	dmm_buffer_t *b;

	pad = data;
	self = GST_DSP_VDEC(gst_pad_get_parent(pad));

	pr_debug(self, "begin");
	g_sem_down_status(self->port[1]->sem, &self->status);

	b = self->out_buffer;

	if (!b) {
		pr_err(self, "out_buffer = NULL!");
		ret = GST_FLOW_ERROR;
		goto leave;
	}

	b->used = FALSE;

	ret = gst_pad_alloc_buffer_and_set_caps(self->srcpad,
						GST_BUFFER_OFFSET_NONE,
						self->output_buffer_size,
						GST_PAD_CAPS(self->srcpad),
						&new_buf);

	if (G_UNLIKELY(ret != GST_FLOW_OK)) {
		pr_err(self, "couldn't allocate buffer: %s", gst_flow_get_name(ret));
		goto leave;
	}

	dmm_buffer_invalidate(b, b->size);

	if (b->user_data) {
		dmm_buffer_t *tmp;
		out_buf = b->user_data;
		b->user_data = NULL;
		tmp = get_slot(self, new_buf);
		if (tmp)
			b = tmp;
		else
			map_buffer(self, new_buf, b);
		b->used = TRUE;
	}
	else {
		out_buf = new_buf;

		if (b->need_copy) {
			pr_info(self, "copy");
			memcpy(GST_BUFFER_DATA(out_buf), b->data, b->size);
		}
	}

	g_mutex_lock(self->ts_mutex);
	if (self->ts == GST_CLOCK_TIME_NONE)
		pr_err(self, "dang!");
	GST_BUFFER_TIMESTAMP(out_buf) = self->ts;
#ifdef TS_COUNT
	self->ts_count--;
	if (self->ts_count > 2 || self->ts_count < 1)
		pr_info(self, "tsc=%lu", self->ts_count);
#endif
	g_mutex_unlock(self->ts_mutex);

	send_buffer(self, b, 1, 0);

	ret = gst_pad_push(self->srcpad, out_buf);
	if (G_UNLIKELY(ret != GST_FLOW_OK)) {
		pr_info(self, "pad push failed: %s", gst_flow_get_name(ret));
		goto leave;
	}

leave:
	if (ret != GST_FLOW_OK) {
		g_atomic_int_set(&self->status, ret);
		gst_pad_pause_task(self->srcpad);
	}

	pr_debug(self, "end");
}

gpointer
dsp_thread(gpointer data)
{
	GstDspVDec *self = data;

	pr_info(self, "begin");

	while (!self->done) {
		unsigned int index = 0;
		pr_debug(self, "waiting for events");
		if (!dsp_wait_for_events(self->dsp_handle, self->events, 1, &index, 10000)) {
			pr_warning(self, "failed waiting for events");
			continue;
		}

		if (index == 0) {
			dsp_msg_t msg;
			while (true) {
				if (!dsp_node_get_message(self->dsp_handle, self->node, &msg, 100))
					break;
				pr_debug(self, "got dsp message: 0x%0x 0x%0x 0x%0x",
					 msg.cmd, msg.arg_1, msg.arg_2);
				got_message(self, &msg);
			}
		}
		else {
			pr_err(self, "wrong event index");
			g_atomic_int_set(&self->status, GST_FLOW_ERROR);
			goto leave;
		}
	}

leave:
	pr_info(self, "end");

	return NULL;
}

struct foo_data {
	unsigned long size;
	unsigned short data[42];
};

struct mp4vdec_args
{
	uint16_t num_streams;

	uint16_t in_id;
	uint16_t in_type;
	uint16_t in_count;

	uint16_t out_id;
	uint16_t out_type;
	uint16_t out_count;

	uint16_t reserved;

	uint32_t max_width;
	uint32_t max_height;
	uint32_t color_format;
	uint32_t max_framerate;
	uint32_t max_bitrate;
	uint32_t endianness;
	uint32_t profile;
	int32_t max_level;
	uint32_t mode;
	int32_t preroll;
	uint32_t display_width;
};

static inline void *
get_mp4v_args(GstDspVDec *self)
{
	struct mp4vdec_args args = {
		.num_streams = 2,
		.in_id = 0,
		.in_type = 0,
		.in_count = self->port[0]->buffer_count,
		.out_id = 1,
		.out_type = 0,
		.out_count = self->port[1]->buffer_count,
		.max_width = 848,
		.max_height = 480,
		.color_format = 4,
		.max_framerate = 0,
		.max_bitrate = -1,
		.endianness = 1,
		.profile = 0,
		.max_level = -1,
		.mode = 0,
		.preroll = 0,
		.display_width = 0,
	};

	struct foo_data *cb_data;

	cb_data = malloc(sizeof(*cb_data));
	cb_data->size = sizeof(args);
	memcpy(&cb_data->data, &args, sizeof(args));

	return cb_data;
}

struct h264vdec_args
{
	uint16_t num_streams;

	uint16_t in_id;
	uint16_t in_type;
	uint16_t in_count;

	uint16_t out_id;
	uint16_t out_type;
	uint16_t out_count;

	uint16_t reserved;

	uint32_t max_width;
	uint32_t max_height;
	uint32_t color_format;
	uint32_t max_framerate;
	uint32_t max_bitrate;
	uint32_t endianness;
	uint32_t profile;
	int32_t max_level;
	uint32_t mode;
	int32_t preroll;
	uint32_t stream_format;
	uint32_t display_width;
};

static inline void *
get_h264_args(GstDspVDec *self)
{
	struct h264vdec_args args = {
		.num_streams = 2,
		.in_id = 0,
		.in_type = 0,
		.in_count = self->port[0]->buffer_count,
		.out_id = 1,
		.out_type = 0,
		.out_count = self->port[1]->buffer_count,
		.max_width = 848,
		.max_height = 480,
		.color_format = 1,
		.max_framerate = 0,
		.max_bitrate = -1,
		.endianness = 1,
		.profile = 0,
		.max_level = -1,
		.mode = 0,
		.preroll = 0,
		.stream_format = 0,
		.display_width = 0,
	};

	struct foo_data *cb_data;

	cb_data = malloc(sizeof(*cb_data));
	cb_data->size = sizeof(args);
	memcpy(&cb_data->data, &args, sizeof(args));

	return cb_data;
}

static inline void *
create_node(GstDspVDec *self,
	    int dsp_handle,
	    void *proc)
{
	void *node;
	const dsp_uuid_t *alg_uuid;
	const char *alg_fn;
	const dsp_uuid_t mp4v_dec_uuid = { 0x7e4b8541, 0x47a1, 0x11d6, 0xb1, 0x56,
		{ 0x00, 0xb0, 0xd0, 0x17, 0x67, 0x4b } };

	const dsp_uuid_t h264v_dec_uuid = { 0xCB1E9F0F, 0x9D5A, 0x4434, 0x84, 0x49,
	    { 0x1F, 0xED, 0x2F, 0x99, 0x2D, 0xF7 } };

	const dsp_uuid_t usn_uuid = { 0x79A3C8B3, 0x95F2, 0x403F, 0x9A, 0x4B,
		{ 0xCF, 0x80, 0x57, 0x73, 0x05, 0x41 } };

	const dsp_uuid_t ringio_uuid = { 0x47698bfb, 0xa7ee, 0x417e, 0xa6, 0x7a,
		{ 0x41, 0xc0, 0x27, 0x9e, 0xb8, 0x05 } };

	if (!dsp_register(dsp_handle, &ringio_uuid, DSP_DCD_LIBRARYTYPE, "/lib/dsp/ringio.dll64P")) {
		pr_err(self, "failed to register ringio node library");
		return NULL;
	}

	if (!dsp_register(dsp_handle, &usn_uuid, DSP_DCD_LIBRARYTYPE, "/lib/dsp/usn.dll64P")) {
		pr_err(self, "failed to register usn node library");
		return NULL;
	}

	switch (self->alg) {
		case GSTDSP_MPEG4VDEC:
			alg_uuid = &mp4v_dec_uuid;
			alg_fn = "/lib/dsp/mp4vdec_sn.dll64P";
			break;
		case GSTDSP_H264DEC:
			alg_uuid = &h264v_dec_uuid;
			alg_fn = "/lib/dsp/h264vdec_sn.dll64P";
			break;
		default:
			pr_err(self, "unknown algorithm");
			return NULL;
	}

	if (!dsp_register(dsp_handle, alg_uuid, DSP_DCD_LIBRARYTYPE, alg_fn)) {
		pr_err(self, "failed to register algo node library");
		return NULL;
	}

	if (!dsp_register(dsp_handle, alg_uuid, DSP_DCD_NODETYPE, alg_fn)) {
		pr_err(self, "failed to register algo node");
		return NULL;
	}

	{
		struct dsp_node_attr_in attrs = {
			.cb = sizeof(attrs),
			.priority = 5,
			.timeout = 1000,
			.heap_size = 0,
			.gpp_va = 0,
		};
		void *cb_data;

		switch (self->alg) {
			case GSTDSP_MPEG4VDEC:
{'fraction_non_alphanumeric': 0.13442107006791454, 'fraction_numerical': 0.03478548948153056, 'mean_word_length': 3.945525291828794, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 203, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1397650', 'n_tokens_mistral': 11798, 'n_tokens_neox': 10372, 'n_words': 2134}