Files
2026-08-24 10:03:24 +08:00

1189 lines
33 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* GStreamer
* Copyright (C) 2023 <[email protected]>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "gstamldmxwrap.h"
#include "aml_defs.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <glib/gstdio.h>
#include <poll.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
GST_DEBUG_CATEGORY_EXTERN(gst_amlhwdmx_dmxwrap_debug);
#ifdef GST_CAT_DEFAULT
#undef GST_CAT_DEFAULT
#endif
#define GST_CAT_DEFAULT gst_amlhwdmx_dmxwrap_debug
#define DMX_FILTER_COUNT (32 * DMX_DEV_NUM)
#define MAX_READ_SIZE (4096)
#define DMX_POLL_TIMEOUT (200)
#define DMX_THREAD_SLEEP_TIME (1000) //1ms
#define RETRY_TIMES (30)
#define AMLHWDMXWRAP_GET_LOCK(dev) (&((dev)->lock))
#define AMLHWDMXWRAP_CONTEXT_LOCK(dev) \
G_STMT_START \
{ \
GST_TRACE("Locking amldmxwrap lock from thread %p", g_thread_self()); \
g_mutex_lock(AMLHWDMXWRAP_GET_LOCK(dev)); \
GST_TRACE("Locked amldmxwrap lock from thread %p", g_thread_self()); \
} \
G_STMT_END
#define AMLHWDMXWRAP_CONTEXT_UNLOCK(dev) \
G_STMT_START \
{ \
GST_TRACE("Unlocking amldmxwrap lock from thread %p", g_thread_self()); \
g_mutex_unlock(AMLHWDMXWRAP_GET_LOCK(dev)); \
GST_TRACE("Unlocked amldmxwrap lock from thread %p", g_thread_self()); \
} \
G_STMT_END
typedef struct
{
gint dev_no;
gint fd;
gint used;
gint enable;
gint need_free;
guint flags;
gst_amldmxwrap_data_cb cb;
void *user_data;
} dvb_dmx_filter_t;
typedef struct
{
gint dmx_fd;
gint dmx_src_fd;
gint dev_no;
gint running;
gboolean is_primary;
GstTask *task;
GRecMutex tlock;
GMutex lock;
dvb_dmx_filter_t filter[DMX_FILTER_COUNT];
} dvb_dmx_t;
static dvb_dmx_t dmx_devices[DMX_DEV_NUM];
static int amsysfs_set_sysfs_str(const char *path, const char *val)
{
int fd;
int res;
fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644);
if (fd >= 0)
{
res = write(fd, val, strlen(val));
GST_DEBUG("write (res:%d)", res);
close(fd);
return 0;
}
return -1;
}
static int set_dmx_source()
{
amsysfs_set_sysfs_str("/sys/class/stb/source", "dmx0");
amsysfs_set_sysfs_str("/sys/class/stb/demux0_source", "hiu");
return 0;
}
static inline DVB_RESULT dmx_get_dev(gint dev_no, dvb_dmx_t **dev)
{
if ((dev_no < 0) || (dev_no >= DMX_DEV_NUM))
{
GST_DEBUG("invalid demux device number %d, must in(%d~%d)", dev_no, 0, DMX_DEV_NUM - 1);
return DVB_FAILURE;
}
*dev = &dmx_devices[dev_no];
return DVB_SUCCESS;
}
static void *gst_amldmxwrap_data_thread(void *arg)
{
gint i, fid;
gint ret;
gint cnt, have_read;
gchar *read_buf = NULL;
gint fids[DMX_FILTER_COUNT];
struct pollfd fds[DMX_FILTER_COUNT];
dvb_dmx_filter_t *filter = NULL;
dvb_dmx_t *dmx = (dvb_dmx_t *)arg;
gint lb_ret;
read_buf = (gchar *)malloc(MAX_READ_SIZE);
prctl(PR_SET_NAME, "gst_amldmxwrap_data_thread");
lb_ret = AMLHWDMX_FILTER_ALL;
while (g_atomic_int_get(&dmx->running))
{
cnt = 0;
AMLHWDMXWRAP_CONTEXT_LOCK(dmx);
for (fid = 0; fid < DMX_FILTER_COUNT; fid++)
{
if (dmx->filter[fid].need_free)
{
filter = &dmx->filter[fid];
close(filter->fd);
filter->used = 0;
filter->need_free = 0;
filter->cb = NULL;
}
if (dmx->filter[fid].used && ((dmx->filter[fid].flags & DMX_CHECK_CRC) || AMLHWDMX_FILTER_ALL == lb_ret || lb_ret == fid))
{
fds[cnt].events = POLLIN | POLLERR;
// It is not necessary to protect code block below with
// Record_DeviceContext_t.lock, so the following annotation
// is given to suppress related Coverity complaint.
// coverity[missing_lock]
fds[cnt].fd = dmx->filter[fid].fd;
fids[cnt] = fid;
cnt++;
}
}
AMLHWDMXWRAP_CONTEXT_UNLOCK(dmx);
if (!cnt)
{
g_usleep(DMX_THREAD_SLEEP_TIME);
continue;
}
ret = poll(fds, cnt, DMX_POLL_TIMEOUT);
if (ret <= 0)
{
GST_TRACE("poll dmx timeout");
continue;
}
for (i = 0; i < cnt; i++)
{
if (fds[i].revents & (POLLIN | POLLERR))
{
AMLHWDMXWRAP_CONTEXT_LOCK(dmx);
filter = &dmx->filter[fids[i]];
if (!filter->enable || !filter->used || filter->need_free)
{
GST_DEBUG("ch[%d] not used, not read", fids[i]);
have_read = 0;
}
else
{
gint read_len = 0;
gint left = 0;
gint tmp = 0;
have_read = 0;
gint retry_time = 0;
if ((filter->flags & DMX_ES_OUTPUT))
{
if (!(filter->flags & DMX_OUTPUT_RAW_MODE))
read_len = NON_SEC_BUF_HEADER_SIZE;
else
read_len = SEC_BUF_HEADER_SIZE;
left = read_len;
while (left > 0)
{
tmp = read(filter->fd, read_buf + have_read, left);
if (tmp > 0)
{
have_read += tmp;
left -= have_read;
}
else
{
g_usleep(DMX_THREAD_SLEEP_TIME);
retry_time++;
if (retry_time >= RETRY_TIMES)
{
GST_ERROR("read demux filter[%d] failed (%s) %d", fids[i], strerror(errno), errno);
return NULL;
}
}
GST_TRACE("have_read: %d tmp:%d left:%d", have_read, tmp, left);
}
}
else
{
have_read = read(filter->fd, read_buf, MAX_READ_SIZE);
if (have_read <= 0)
{
GST_ERROR("read demux filter[%d] failed (%s) %d", fids[i], strerror(errno), errno);
}
if (G_UNLIKELY(have_read == MAX_READ_SIZE))
GST_WARNING("please check carefully , this size may be small");
GST_TRACE("have read: %d", have_read);
}
}
AMLHWDMXWRAP_CONTEXT_UNLOCK(dmx);
GST_TRACE("tid[%x] ch[%d] cnt[%d] %x bytes", read_buf[0], fids[i], cnt, have_read);
if (have_read > 0 && filter->cb)
{
lb_ret = filter->cb(filter->dev_no, fids[i], (const guint *)read_buf, have_read, filter->user_data);
GST_TRACE("lb_ret:%d", lb_ret);
}
}
}
}
if (read_buf)
{
free(read_buf);
}
GST_TRACE("trace out data thread");
return NULL;
}
static dvb_dmx_filter_t *dmx_get_filter(dvb_dmx_t *dev, gint fhandle)
{
if (fhandle >= DMX_FILTER_COUNT)
{
GST_DEBUG("wrong filter no");
return NULL;
}
if (!dev->filter[fhandle].used)
{
GST_DEBUG("filter %d not allocated", fhandle);
return NULL;
}
return &dev->filter[fhandle];
}
static DVB_RESULT gst_amldmxwrap_node_cfg( const gchar **paths, const gchar *cfg)
{
GStatBuf fileInfo;
gint fd = -1;
int res;
g_return_val_if_fail(paths, DVB_FAILURE);
g_return_val_if_fail(cfg, DVB_FAILURE);
memset(&fileInfo, 0, sizeof(fileInfo));
for (guint i = 0; paths[i] != NULL; i++)
{
if (g_stat(paths[i], &fileInfo) == 0)
{
fd = open(paths[i], O_CREAT | O_RDWR | O_TRUNC, 0644);
if (fd < 0)
return DVB_FAILURE;
res = write(fd, cfg, strlen(cfg));
close(fd);
GST_DEBUG("config %s in to %s (res:%d)", cfg, paths[i], res);
return DVB_SUCCESS;
}
}
return DVB_FAILURE;
}
/**\brief dmx device init, creat dmx thread
* \param dmx device number
* \return DVB_SUCCESS On success, DVB_FAILURE on error.
*/
DVB_RESULT gst_amldmxwrap_open(gint dev_no)
{
dvb_dmx_t *dev = NULL;
char node[32] = {0};
char node2[20] = {0};
if (dmx_get_dev(dev_no, &dev))
return DVB_FAILURE;
if (g_atomic_int_get(&dev->running))
{
GST_DEBUG("dmx already initialized");
return DVB_FAILURE;
}
set_dmx_source();
snprintf(node, sizeof(node), "/dev/dvb0.dvr%d", dev_no);
snprintf(node2, sizeof(node2), "/dev/dvb0.demux%d", dev_no);
dev->dmx_src_fd = open(node, O_WRONLY);
dev->dmx_fd = open(node2, O_RDONLY);
if (dev->dmx_src_fd < 0 || dev->dmx_fd < 0)
{
GST_DEBUG("open dmx device fail");
return DVB_FAILURE;
}
dev->dev_no = dev_no;
g_atomic_int_set(&dev->running, 1);
gst_amldmxwrap_start_fetch_data(dev_no);
return DVB_SUCCESS;
}
DVB_RESULT gst_amldmxwrap_start_fetch_data(gint dev_no)
{
dvb_dmx_t *dev = NULL;
if (dmx_get_dev(dev_no, &dev))
return DVB_FAILURE;
if (!dev->task)
{
dev->task = gst_task_new((GstTaskFunction)gst_amldmxwrap_data_thread, dev, NULL);
g_rec_mutex_init(&dev->tlock);
g_mutex_init(&dev->lock);
gst_task_set_lock(dev->task, &dev->tlock);
}
gst_task_start(dev->task);
return DVB_SUCCESS;
}
DVB_RESULT gst_amldmxwrap_pause_fetch_data(gint dev_no)
{
dvb_dmx_t *dev = NULL;
if (dmx_get_dev(dev_no, &dev))
return DVB_FAILURE;
if (!dev->task)
return DVB_FAILURE;
gst_task_pause(dev->task);
return DVB_SUCCESS;
}
DVB_RESULT gst_amldmxwrap_set_flushing(gint dev_no, gboolean flushing)
{
dvb_dmx_t *dev = NULL;
if (dmx_get_dev(dev_no, &dev))
return DVB_FAILURE;
if (flushing)
g_atomic_int_set(&dev->running, 0);
else
g_atomic_int_set(&dev->running, 1);
return DVB_SUCCESS;
}
/**\brief get dma fd from dmx drive
* \param @dev_no: input parm, device number
* \param @fid: input parm, filter id
* \param @secure_es: input parm, secure es data head
* \param @dmafd: output parm, dma fd
* \param @size: output parm, data size
* \return DVB_SUCCESS On success, DVB_FAILURE on error.
*/
DVB_RESULT gst_amldmxwrap_get_dmafd(gint dev_no,int fid, struct dmx_sec_es_data *secure_es, gint *dmafd, guint *size)
{
dvb_dmx_t *dev = NULL;
guint data_size = 0;
struct dmx_dma_buf_info info = {0};
g_return_val_if_fail(secure_es, DVB_FAILURE);
g_return_val_if_fail(size, DVB_FAILURE);
/* Remove assert 0 for pts_dts_flag.
* Since H/W DMX enables the unpacking function by default, a PES will be split into multiple bufs for reporting.
* Only the first buf in the same PES carries valid pts_dts_flag/pts/dts,
* and the pts_dts_flag/pts/dts values ​​of the remaining packets are all 0.
*/
if (secure_es->pts_dts_flag < 0 || secure_es->pts_dts_flag > 3)
{
GST_ERROR("sec es header error flag: %d!",secure_es->pts_dts_flag);
return DVB_FAILURE;
}
if (dmx_get_dev(dev_no, &dev))
return DVB_FAILURE;
GST_MEMDUMP("sec-header: ", (const guint8 *)secure_es, sizeof(struct dmx_sec_es_data));
if (secure_es->data_end > secure_es->data_start)
data_size = secure_es->data_end - secure_es->data_start;
else
data_size = (secure_es->data_end - secure_es->buf_start) + (secure_es->buf_end - secure_es->data_start);
*size = data_size;
info.paddr = (__u64)secure_es->data_start;
info.size = *size;
info.handle = 0;
info.fd = -1;
info.dmxes.magic_num = DEMUX_ES_MAGIC_NUM;
info.dmxes.pts_dts_flag = secure_es->pts_dts_flag;
info.dmxes.video_pts = secure_es->pts;
info.dmxes.video_dts = secure_es->dts;
info.dmxes.buf_start = (__u64)secure_es->buf_start;
info.dmxes.buf_end = (__u64)secure_es->buf_end;
info.dmxes.data_start = (__u64)secure_es->data_start;
info.dmxes.data_end = (__u64)secure_es->data_end;
info.dmxes.token = (__u32)fid;
info.dmxes.extend_addr = 0;
info.dmxes.extend_size = 0;
GST_TRACE("paddr: %llu. size: %u. handle: %u. token %u. buf_start: %llu buf_end: %llu data_end: %llu "
,info.paddr, info.size, info.handle, info.dmxes.token, info.dmxes.buf_start, info.dmxes.buf_end, info.dmxes.data_end);
GST_DEBUG("pts:%llu dts:%llu",info.dmxes.video_pts,info.dmxes.video_dts);
if (ioctl(dev->dmx_fd, DMX_GET_DMA_BUF_FD, &info) < 0)
{
GST_ERROR("get dmafd error");
return DVB_FAILURE;
}
*dmafd = info.fd;
return DVB_SUCCESS;
}
/**\brief set dmx config
* \param dmx device number
* \param is secure scenes
* \return DVB_SUCCESS On success, DVB_FAILURE on error.
*/
DVB_RESULT gst_amldmxwrap_set_config(gint dev_no, gboolean is_secure)
{
dvb_dmx_t *dev = NULL;
DVB_RESULT dmxret = DVB_SUCCESS;
if (dmx_get_dev(dev_no, &dev))
{
GST_DEBUG("demux allocate failed, wrong dmx device no %d", dev_no);
return DVB_FAILURE;
}
if (is_secure)
{
dmxret |= ioctl(dev->dmx_src_fd, DMX_SET_INPUT, INPUT_LOCAL_SEC);
dmxret |= ioctl(dev->dmx_fd, DMX_SET_INPUT, INPUT_LOCAL_SEC);
}
else
{
dmxret |= ioctl(dev->dmx_src_fd, DMX_SET_INPUT, INPUT_LOCAL);
dmxret |= ioctl(dev->dmx_fd, DMX_SET_INPUT, INPUT_LOCAL);
}
enum dmx_input_source src = DMA_0;
if (0 == dev_no)
src = DMA_0;
else if (1 == dev_no)
src = DMA_1;
else if (2 == dev_no)
src = DMA_2;
else if (3 == dev_no)
src = DMA_3;
else if (4 == dev_no)
src = DMA_4;
else if (5 == dev_no)
src = DMA_5;
else if (6 == dev_no)
src = DMA_6;
else if (7 == dev_no)
src = DMA_7;
else
GST_ERROR("dmx[%d] dmx_fd:%d, dmx_src_fd:%d, out of bouds for this dmx id", dev_no, dev->dmx_fd, dev->dmx_src_fd);
GST_DEBUG("dmx[%d] dmx_fd:%d, dmx_src_fd:%d, select DMX_SET_HW_SOURCE %d", dev_no, dev->dmx_fd, dev->dmx_src_fd, src);
dmxret |= ioctl(dev->dmx_fd, DMX_SET_HW_SOURCE, src);
return dmxret;
}
DVB_RESULT gst_amldmxwrap_set_primary(gint dev_no, gboolean is_primary)
{
dvb_dmx_t *dev = NULL;
DVB_RESULT dmxret = DVB_SUCCESS;
if (dmx_get_dev(dev_no, &dev))
{
GST_DEBUG("demux allocate failed, wrong dmx device no %d", dev_no);
return DVB_FAILURE;
}
dev->is_primary = is_primary;
GST_DEBUG("dmx[%d] dmx_fd:%d, dmx_src_fd:%d, set primary to %d", dev_no, dev->dmx_fd, dev->dmx_src_fd, dev->is_primary);
return dmxret;
}
/**\brief write data into dmx
* \param dmx device number
* \param data data ptr
* \param size data size
* \return DVB_SUCCESS On success, DVB_FAILURE on error.
*/
DVB_RESULT gst_amldmxwrap_write(gint dev_no, gchar *data, guint size)
{
dvb_dmx_t *dev = NULL;
DVB_RESULT dmxret = DVB_SUCCESS;
gchar *offset = 0;
ssize_t actual_write = 0;
ssize_t remaining = 0;
ssize_t segmented_size = 0;
if (dmx_get_dev(dev_no, &dev))
{
GST_DEBUG("demux allocate failed, wrong dmx device no %d", dev_no);
return DVB_FAILURE;
}
offset = data;
remaining = size;
if (dev->is_primary)
segmented_size = remaining;
else
segmented_size = AMLHWDMX_SEC_AUDIO_TS_SEGMENTED_BUF_SIZE;
GST_DEBUG("try to write ts data into DMX. offset:%p, size:%d, segmented_size:%d", offset, remaining, segmented_size);
while (remaining)
{
ssize_t write_size = remaining < segmented_size ? remaining : segmented_size;
actual_write = write(dev->dmx_src_fd, (const void *)offset, (size_t)write_size);
if (actual_write == -1)
{
GST_DEBUG("write ts data error. It is likely to be caused by insufficient es buffer. Need sleep and try again");
g_usleep(50 * 1000);
}
else if (actual_write <= write_size)
{
if (actual_write == write_size)
{
GST_DEBUG("write ts data done for this sub sgement.");
}
else if (actual_write < write_size)
{
GST_DEBUG("write ts data continue. It is likely to be caused by insufficient es buffer. Need sleep and try again");
g_usleep(50 * 1000);
}
remaining -= actual_write;
offset += actual_write;
if (remaining)
{
g_usleep(10 * 1000);
continue;
}
GST_DEBUG("write ts data done.");
}
else if (actual_write > write_size)
{
GST_ERROR("write ts data out of bounds. Meet real errors");
dmxret = DVB_FAILURE;
break;
}
}
GST_DEBUG("write return:%d", dmxret);
return dmxret;
}
/**\brief allocate dmx filter
* \param dmx device number
* \param get dmx filter index
* \return DVB_SUCCESS On success, DVB_FAILURE on error.
*/
DVB_RESULT gst_amldmxwrap_alloc_filter(gint dev_no, gint *fhandle)
{
gint fd;
gint fid;
dvb_dmx_filter_t *filter = NULL;
char dev_name[32];
dvb_dmx_t *dev = NULL;
if (dmx_get_dev(dev_no, &dev))
{
GST_DEBUG("demux allocate failed, wrong dmx device no %d", dev_no);
return DVB_FAILURE;
}
AMLHWDMXWRAP_CONTEXT_LOCK(dev);
filter = &dev->filter[0];
for (fid = 0; fid < DMX_FILTER_COUNT; fid++)
{
if (!filter[fid].used)
{
break;
}
}
if (fid >= DMX_FILTER_COUNT)
{
GST_DEBUG("filter id:%d, have no filter to alloc", fid);
AMLHWDMXWRAP_CONTEXT_UNLOCK(dev);
return DVB_FAILURE;
}
memset(dev_name, 0, sizeof(dev_name));
sprintf(dev_name, "/dev/dvb0.demux%d", dev_no);
fd = open(dev_name, O_RDWR);
if (fd == -1)
{
GST_DEBUG("cannot open \"%s\" (%s)", dev_name, strerror(errno));
AMLHWDMXWRAP_CONTEXT_UNLOCK(dev);
return DVB_FAILURE;
}
memset(&filter[fid], 0, sizeof(dvb_dmx_filter_t));
filter[fid].dev_no = dev_no;
filter[fid].fd = fd;
filter[fid].used = 1;
*fhandle = fid;
AMLHWDMXWRAP_CONTEXT_UNLOCK(dev);
// GST_DEBUG("fhandle = %d", fid);
return DVB_SUCCESS;
}
/**\brief set demux section filter
* \param dmx device number
* \param dmx filter index
* \param dmx section filter param
* \return DVB_SUCCESS On success, DVB_FAILURE on error.
*/
DVB_RESULT gst_amldmxwrap_sec_secfilter(gint dev_no, gint fhandle, const struct dmx_sct_filter_params *params)
{
DVB_RESULT ret = DVB_SUCCESS;
dvb_dmx_t *dev = NULL;
dvb_dmx_filter_t *filter = NULL;
if (dmx_get_dev(dev_no, &dev))
{
GST_DEBUG("Wrong dmx device no %d", dev_no);
return DVB_FAILURE;
}
if (!params)
return DVB_FAILURE;
AMLHWDMXWRAP_CONTEXT_LOCK(dev);
filter = dmx_get_filter(dev, fhandle);
if (filter)
{
if (ioctl(filter->fd, DMX_STOP, 0) < 0)
{
GST_DEBUG("dmx stop filter failed error:%s", strerror(errno));
ret = DVB_FAILURE;
}
else if (ioctl(filter->fd, DMX_SET_FILTER, params) < 0)
{
GST_DEBUG("set filter failed error:%s", strerror(errno));
ret = DVB_FAILURE;
}
}
AMLHWDMXWRAP_CONTEXT_UNLOCK(dev);
// GST_DEBUG("pid = %x", params->pid);
return ret;
}
/**\brief set demux pes filter
* \param dmx device number
* \param dmx filter index
* \param dmx pes filter param
* \return DVB_SUCCESS On success, DVB_FAILURE on error.
*/
DVB_RESULT gst_amldmxwrap_set_pesfilter(gint dev_no, gint fhandle, const struct dmx_pes_filter_params *params)
{
DVB_RESULT ret = DVB_SUCCESS;
dvb_dmx_t *dev = NULL;
dvb_dmx_filter_t *filter = NULL;
if (dmx_get_dev(dev_no, &dev))
{
GST_ERROR("wrong dmx device no %d", dev_no);
return DVB_FAILURE;
}
if (!params)
return DVB_FAILURE;
AMLHWDMXWRAP_CONTEXT_LOCK(dev);
filter = dmx_get_filter(dev, fhandle);
if (filter)
{
ret = DVB_FAILURE;
if (ioctl(filter->fd, DMX_STOP, 0) < 0)
{
GST_ERROR("stopping demux filter fails with errno:%d(%s)", errno, strerror(errno));
}
else if (fcntl(filter->fd, F_SETFL, O_NONBLOCK) < 0)
{
GST_ERROR("setting filter non-block flag fails with errno:%d(%s)", errno, strerror(errno));
}
else if (ioctl(filter->fd, DMX_SET_PES_FILTER, params) < 0)
{
GST_ERROR("setting PES filter fails with errno:%d(%s)", errno, strerror(errno));
}
else
{
filter->flags = params->flags;
ret = DVB_SUCCESS;
}
}
AMLHWDMXWRAP_CONTEXT_UNLOCK(dev);
// GST_DEBUG("pid = %x", params->pid);
return ret;
}
/**\brief set demux filter buffer
* \param dmx device number
* \param dmx filter index
* \param dmx filter buffer size
* \return DVB_SUCCESS On success, DVB_FAILURE on error.
*/
DVB_RESULT gst_amldmxwrap_set_bufsize(gint dev_no, gint fhandle, gint size)
{
DVB_RESULT ret = DVB_SUCCESS;
dvb_dmx_t *dev = NULL;
dvb_dmx_filter_t *filter = NULL;
if (dmx_get_dev(dev_no, &dev))
{
GST_DEBUG("wrong dmx device no %d", dev_no);
return DVB_FAILURE;
}
AMLHWDMXWRAP_CONTEXT_LOCK(dev);
filter = dmx_get_filter(dev, fhandle);
if (filter)
{
if (ioctl(filter->fd, DMX_SET_BUFFER_SIZE, size) < 0)
{
GST_DEBUG("set buf size failed error:%s", strerror(errno));
ret = DVB_FAILURE;
}
}
AMLHWDMXWRAP_CONTEXT_UNLOCK(dev);
return ret;
}
/**\brief free demux filter
* \param dmx device number
* \param dmx filter index
* \return DVB_SUCCESS On success, DVB_FAILURE on error.
*/
DVB_RESULT gst_amldmxwrap_free_filter(gint dev_no, gint fhandle)
{
dvb_dmx_t *dev = NULL;
dvb_dmx_filter_t *filter = NULL;
if (dmx_get_dev(dev_no, &dev))
{
GST_DEBUG("wrong dmx device no %d", dev_no);
return DVB_FAILURE;
}
AMLHWDMXWRAP_CONTEXT_LOCK(dev);
filter = dmx_get_filter(dev, fhandle);
if (filter)
{
filter->need_free = 1;
}
AMLHWDMXWRAP_CONTEXT_UNLOCK(dev);
// GST_DEBUG("fhandle = %d", fhandle);
return DVB_SUCCESS;
}
/**\brief start demux filter
* \param dmx device number
* \param dmx filter index
* \return DVB_SUCCESS On success, DVB_FAILURE on error.
*/
DVB_RESULT gst_amldmxwrap_start_filter(gint dev_no, gint fhandle)
{
DVB_RESULT ret = DVB_SUCCESS;
dvb_dmx_t *dev = NULL;
dvb_dmx_filter_t *filter = NULL;
GST_DEBUG("trace in start filter:%d", fhandle);
if (dmx_get_dev(dev_no, &dev))
{
GST_DEBUG("wrong dmx device no %d", dev_no);
return DVB_FAILURE;
}
AMLHWDMXWRAP_CONTEXT_LOCK(dev);
filter = dmx_get_filter(dev, fhandle);
if (filter && !filter->enable)
{
if (ioctl(filter->fd, DMX_START, 0) < 0)
{
GST_DEBUG("dmx start filter failed error:%s", strerror(errno));
ret = DVB_FAILURE;
}
else
{
filter->enable = 1;
}
}
AMLHWDMXWRAP_CONTEXT_UNLOCK(dev);
return ret;
}
/**\brief stop demux filter
* \param dmx device number
* \param dmx filter index
* \return DVB_SUCCESS On success, DVB_FAILURE on error.
*/
DVB_RESULT gst_amldmxwrap_stop_filter(gint dev_no, gint fhandle)
{
DVB_RESULT ret = DVB_SUCCESS;
dvb_dmx_t *dev = NULL;
dvb_dmx_filter_t *filter = NULL;
if (dmx_get_dev(dev_no, &dev))
{
GST_DEBUG("wrong dmx device no %d", dev_no);
return DVB_FAILURE;
}
AMLHWDMXWRAP_CONTEXT_LOCK(dev);
filter = dmx_get_filter(dev, fhandle);
if (filter && filter->enable)
{
if (ioctl(filter->fd, DMX_STOP, 0) < 0)
{
GST_DEBUG("dmx stop filter failed error:%s", strerror(errno));
ret = DVB_FAILURE;
}
else
{
filter->enable = 0;
}
}
AMLHWDMXWRAP_CONTEXT_UNLOCK(dev);
return ret;
}
/**\brief set demux callback
* \param dmx device number
* \param dmx filter index
* \param dmx filter callback
* \param dmx filter callback param
* \return DVB_SUCCESS On success, DVB_FAILURE on error.
*/
DVB_RESULT gst_amldmxwrap_set_cb(gint dev_no, gint fhandle, gst_amldmxwrap_data_cb cb, void *user_data)
{
DVB_RESULT ret = DVB_SUCCESS;
dvb_dmx_t *dev = NULL;
dvb_dmx_filter_t *filter = NULL;
if (dmx_get_dev(dev_no, &dev))
{
GST_DEBUG("wrong dmx device no %d", dev_no);
return DVB_FAILURE;
}
AMLHWDMXWRAP_CONTEXT_LOCK(dev);
filter = dmx_get_filter(dev, fhandle);
if (filter)
{
filter->cb = cb;
filter->user_data = user_data;
}
else
{
ret = DVB_FAILURE;
}
AMLHWDMXWRAP_CONTEXT_UNLOCK(dev);
// GST_DEBUG("ret = %d", ret);
return ret;
}
/**\brief dmx device uninit, destroy dmx thread
* \param dmx device number
* \return DVB_SUCCESS On success, DVB_FAILURE on error.
*/
DVB_RESULT gst_amldmxwrap_close(gint dev_no)
{
gint i;
gint open_count = 0;
dvb_dmx_t *dev = NULL;
dvb_dmx_filter_t *filter = NULL;
DVB_RESULT ret = DVB_SUCCESS;
GST_INFO("trace in");
if (dmx_get_dev(dev_no, &dev))
{
GST_ERROR("wrong dmx device no %d", dev_no);
return DVB_FAILURE;
}
gst_amldmxwrap_set_flushing(dev_no, TRUE);
AMLHWDMXWRAP_CONTEXT_LOCK(dev);
for (i = 0; i < DMX_FILTER_COUNT; i++)
{
filter = &dev->filter[i];
if (filter->used && filter->dev_no == dev_no)
{
if (filter->enable)
{
if (ioctl(filter->fd, DMX_STOP, 0) < 0)
{
GST_ERROR("fails to stop filter. fd:%d", filter->fd);
ret = DVB_FAILURE;
}
}
close(filter->fd);
}
else if (filter->used)
{
open_count++;
}
}
AMLHWDMXWRAP_CONTEXT_UNLOCK(dev);
if (open_count == 0)
{
g_atomic_int_set(&dev->running, 0);
gst_task_stop(dev->task);
gst_task_join(dev->task);
gst_object_unref(dev->task);
g_rec_mutex_clear(&dev->tlock);
g_mutex_clear(&dev->lock);
}
if (dev->dmx_fd >= 0)
close(dev->dmx_fd);
if (dev->dmx_src_fd >= 0)
close(dev->dmx_src_fd);
memset(dev, 0, sizeof(dvb_dmx_t));
dev->dmx_fd = -1;
dev->dmx_src_fd = -1;
return ret;
}
DVB_RESULT gst_amldmxwrap_get_es(gint dev_no, gint fhandle, guint8 *data, guint size)
{
dvb_dmx_t *dmx = NULL;
dvb_dmx_filter_t *filter = NULL;
gint ret = 0;
DVB_RESULT dmxret = DVB_SUCCESS;
struct pollfd fd[1];
GST_DEBUG("need read size:%d", size);
if (dmx_get_dev(dev_no, &dmx))
{
GST_ERROR("get dmx error");
goto fail;
}
filter = &dmx->filter[fhandle];
if (!filter || !filter->enable || !filter->used || filter->need_free)
{
GST_ERROR("ch[%d] not used, not read", fhandle);
goto fail;
}
while (g_atomic_int_get(&dmx->running))
{
memset(fd, 0, sizeof(struct pollfd));
fd[0].events = POLLIN | POLLERR;
fd[0].fd = filter->fd;
ret = poll(fd, 1, DMX_POLL_TIMEOUT);
if (ret <= 0)
{
if (!g_atomic_int_get(&dmx->running))
{
GST_DEBUG("flushing");
goto done;
}
GST_DEBUG("poll ret:%d timeout. need continue", ret);
continue;
}
if (fd[0].revents & (POLLIN | POLLERR))
{
gint tmp = 0;
guint read_len = 0;
guint left = size;
gint retry_time = 0;
do
{
tmp = read(filter->fd, data + read_len, left);
if (tmp <= 0)
{
if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
{
g_usleep(10000);
retry_time++;
if (retry_time >= RETRY_TIMES)
{
GST_ERROR("read %d times but still read error(%d):(%s)", retry_time, errno, strerror(errno));
goto fail;
}
}
else
{
GST_ERROR("read error(%d):(%s)", errno, strerror(errno));
goto fail;
}
}
else
{
read_len += tmp;
left -= tmp;
}
GST_DEBUG("total:%d, read:%d left:%d", size, read_len, left);
} while (left > 0);
GST_DEBUG("read demux filter[%d] ok", fhandle);
break;
}
}
done:
GST_DEBUG("read ret:%d", ret);
return dmxret;
fail:
dmxret = DVB_FAILURE;
goto done;
}
DVB_RESULT gst_amldmxwrap_get_filters_mem_info(gint dev_no, struct dmx_filter_mem_info *info_all)
{
dvb_dmx_t *dmx = NULL;
if (dmx_get_dev(dev_no, &dmx))
{
GST_ERROR("get dmx error");
return DVB_FAILURE;
}
memset(info_all, 0, sizeof(struct dmx_filter_mem_info));
if (ioctl(dmx->dmx_fd, DMX_GET_FILTER_MEM_INFO, info_all) < 0)
{
GST_ERROR("dmx filter get mem info fail(%s) %d", strerror(errno), errno);
return DVB_FAILURE;
}
GST_DEBUG("got filters num:%d", info_all->filter_num);
if (info_all->filter_num > 0)
{
for (int i = 0; i < info_all->filter_num; i++)
{
struct dmx_mem_info *test_info = &info_all->info[i].filter_info;
GST_DEBUG("filter[idx:%d type:%d pid:%d] dmx_total_size:%d, dmx_buf_phy_start:0x%x, dmx_free_size:%d, dvb_core_total_size:%d, dvb_core_free_size:%d",
i, info_all->info[i].type, info_all->info[i].pid,
test_info->dmx_total_size, test_info->dmx_buf_phy_start, test_info->dmx_free_size, test_info->dvb_core_total_size, test_info->dvb_core_free_size);
}
}
return DVB_SUCCESS;
}
DVB_RESULT gst_amldmxwrap_set_hw_flow_control_th(guint size)
{
const gchar *paths[] = {"/sys/module/amlogic_dvb_demux/parameters/flow_control", NULL};
gchar cfg[128] = {0};
sprintf(cfg, "%d", size);
return gst_amldmxwrap_node_cfg(paths, cfg);
}
DVB_RESULT gst_amldmxwrap_set_video_filters_es_buf_size(guint size)
{
const gchar *paths[] = {"/sys/module/dvb_demux/parameters/video_buf_size",
"/sys/module/amlogic_dvb_demux/parameters/video_buf_size", NULL};
gchar cfg[128] = {0};
sprintf(cfg, "%d", size);
return gst_amldmxwrap_node_cfg(paths, cfg);
}
DVB_RESULT gst_amldmxwrap_set_sec_dmc_buf_size(guint size)
{
const gchar *paths[] = {"/sys/class/stb/dmc_mem", NULL};
gchar cfg[128] = {0};
sprintf(cfg, "2 %d", size); // 2 for secure-level-2
return gst_amldmxwrap_node_cfg(paths, cfg);
}
DVB_RESULT gst_amldmxwrap_set_filter_ring_buf_size(gint dev_no, gint fhandle, guint size)
{
dvb_dmx_t *dmx = NULL;
dvb_dmx_filter_t *filter = NULL;
GST_DEBUG("trace in");
if (dmx_get_dev(dev_no, &dmx))
{
GST_ERROR("get dmx error");
return DVB_FAILURE;
}
filter = dmx_get_filter(dmx, fhandle);
if (!filter)
{
GST_ERROR("get filter error");
return DVB_FAILURE;
}
if (ioctl(filter->fd, DMX_SET_BUFFER_SIZE, size) < 0)
{
GST_ERROR("dmx filter get mem info fail(%s) %d", strerror(errno), errno);
return DVB_FAILURE;
}
GST_DEBUG("trace out set size:%d ok", size);
return DVB_SUCCESS;
}