1189 lines
33 KiB
C
1189 lines
33 KiB
C
/* 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;
|
||
} |