414 lines
14 KiB
C
414 lines
14 KiB
C
/***************************************************************************
|
|
* Copyright (C) 2014-2025 Amlogic, Inc. All rights reserved.
|
|
*
|
|
* All information contained herein is Amlogic confidential.
|
|
*
|
|
* This software is provided to you pursuant to Software License Agreement
|
|
* (SLA) with Amlogic Inc ("Amlogic"). This software may be used
|
|
* only in accordance with the terms of this agreement.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification is strictly prohibited without prior written permission from
|
|
* Amlogic.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* Description:
|
|
* Linux DVB demux driver
|
|
***************************************************************************/
|
|
|
|
#define LOG_TAG "Demux"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <linux/dvb/dmx.h>
|
|
#include <poll.h>
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/eventfd.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include "AmlogicUtil.h"
|
|
#include "DemuxDriver.h"
|
|
#include "SubtitleLog.h"
|
|
|
|
#define open(a...) \
|
|
({ \
|
|
int ret, times = 3; \
|
|
do { \
|
|
ret = open(a); \
|
|
if (ret == -1) { \
|
|
usleep(100 * 1000); \
|
|
} \
|
|
} while (ret == -1 && times--); \
|
|
ret; \
|
|
})
|
|
|
|
typedef struct {
|
|
char dev_name[32];
|
|
int fd[DEMUX_FILTER_COUNT];
|
|
int event_fd;
|
|
} DVBDmx_t;
|
|
|
|
static int dvb_open(DemuxDeviceType *dev, const AmlogicDemuxOpenParameterType *para);
|
|
static int dvb_close(DemuxDeviceType *dev);
|
|
static int dvb_wake(DemuxDeviceType *dev);
|
|
static int dvb_alloc_filter(DemuxDeviceType *dev, DemuxFilterType *filter);
|
|
static int dvb_free_filter(DemuxDeviceType *dev, DemuxFilterType *filter);
|
|
static int dvb_set_sec_filter(DemuxDeviceType *dev, DemuxFilterType *filter,
|
|
const struct dmx_sct_filter_params *params);
|
|
static int dvb_set_pes_filter(DemuxDeviceType *dev, DemuxFilterType *filter,
|
|
const struct dmx_pes_filter_params *params);
|
|
static int dvb_enable_filter(DemuxDeviceType *dev, DemuxFilterType *filter, bool enable);
|
|
static int dvb_set_buf_size(DemuxDeviceType *dev, DemuxFilterType *filter, int size);
|
|
static int dvb_poll(DemuxDeviceType *dev, Demux_FilterMaskType *mask, int timeout);
|
|
static int dvb_poll_exit(DemuxDeviceType *dev);
|
|
static int dvb_read(DemuxDeviceType *dev, DemuxFilterType *filter, uint8_t *buf, int *size);
|
|
static int dvb_set_source(DemuxDeviceType *dev, AmlogicDemuxSourceType src);
|
|
static int dvb_get_newest_pts(DemuxDeviceType *dev, amdmx_filter_mem_info *demuxFilterMemInfo);
|
|
|
|
const DemuxDriverType linux_dvb_dmx_drv = {.open = dvb_open,
|
|
.close = dvb_close,
|
|
.alloc_filter = dvb_alloc_filter,
|
|
.free_filter = dvb_free_filter,
|
|
.set_sec_filter = dvb_set_sec_filter,
|
|
.set_pes_filter = dvb_set_pes_filter,
|
|
.enable_filter = dvb_enable_filter,
|
|
.set_buf_size = dvb_set_buf_size,
|
|
.poll = dvb_poll,
|
|
.poll_exit = dvb_poll_exit,
|
|
.read = dvb_read,
|
|
.set_source = dvb_set_source,
|
|
.get_newest_pts = dvb_get_newest_pts};
|
|
|
|
/****************************************************************************
|
|
* Static functions
|
|
***************************************************************************/
|
|
|
|
static int dvb_open(DemuxDeviceType *dev, const AmlogicDemuxOpenParameterType *para) {
|
|
UNUSED(para);
|
|
DVBDmx_t *dmx;
|
|
dmx = (DVBDmx_t *)malloc(sizeof(DVBDmx_t));
|
|
if (!dmx) {
|
|
SUBTITLE_LOGE("malloc fail: %m");
|
|
return DEMUX_ERROR_NO_MEM;
|
|
}
|
|
snprintf(dmx->dev_name, sizeof(dmx->dev_name), "/dev/dvb0.demux%d", dev->dev_no);
|
|
for (int i = 0; i < DEMUX_FILTER_COUNT; ++i) {
|
|
dmx->fd[i] = -1;
|
|
}
|
|
|
|
dmx->event_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
|
|
if (dmx->event_fd == -1) {
|
|
SUBTITLE_LOGE("eventfd fail: %m");
|
|
}
|
|
|
|
SUBTITLE_LOGI("event_fd = %d", dmx->event_fd);
|
|
|
|
dev->drv_data = dmx;
|
|
return AM_SUCCESS;
|
|
}
|
|
|
|
static int dvb_close(DemuxDeviceType *dev) {
|
|
DVBDmx_t *dmx = (DVBDmx_t *)dev->drv_data;
|
|
if (dmx->event_fd >= 0) {
|
|
SUBTITLE_LOGI("event_fd = %d", dmx->event_fd);
|
|
close(dmx->event_fd);
|
|
dmx->event_fd = -1;
|
|
}
|
|
free(dmx);
|
|
return AM_SUCCESS;
|
|
}
|
|
|
|
static int dvb_alloc_filter(DemuxDeviceType *dev, DemuxFilterType *filter) {
|
|
DVBDmx_t *dmx = (DVBDmx_t *)dev->drv_data;
|
|
int fd = open(dmx->dev_name, O_RDWR);
|
|
if (fd == -1) {
|
|
SUBTITLE_LOGE("fail to open %s: %m", dmx->dev_name);
|
|
return DEMUX_ERROR_CANNOT_OPEN_DEV;
|
|
}
|
|
|
|
dmx->fd[filter->id] = fd;
|
|
filter->drv_data = (void *)(long)fd;
|
|
|
|
if (dmx->event_fd == -1) {
|
|
dmx->event_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
|
|
if (dmx->event_fd == -1) {
|
|
SUBTITLE_LOGE("eventfd fail: %m");
|
|
}
|
|
}
|
|
SUBTITLE_LOGI("fd = %d, event_fd = %d", fd, dmx->event_fd);
|
|
|
|
return AM_SUCCESS;
|
|
}
|
|
|
|
static int dvb_free_filter(DemuxDeviceType *dev, DemuxFilterType *filter) {
|
|
DVBDmx_t *dmx = (DVBDmx_t *)dev->drv_data;
|
|
int fd = (long)filter->drv_data;
|
|
SUBTITLE_LOGI("fd = %d, event_fd = %d", fd, dmx->event_fd);
|
|
if (fd >= 0) {
|
|
close(fd);
|
|
dmx->fd[filter->id] = -1;
|
|
}
|
|
return AM_SUCCESS;
|
|
}
|
|
|
|
static int dvb_set_sec_filter(DemuxDeviceType *dev, DemuxFilterType *filter,
|
|
const struct dmx_sct_filter_params *params) {
|
|
struct dmx_sct_filter_params p;
|
|
int fd = (long)filter->drv_data;
|
|
int ret;
|
|
UNUSED(dev);
|
|
p = *params;
|
|
/*
|
|
if (p.filter.mask[0] == 0) {
|
|
p.filter.filter[0] = 0x00;
|
|
p.filter.mask[0] = 0x80;
|
|
}
|
|
*/
|
|
ret = ioctl(fd, DMX_SET_FILTER, &p);
|
|
if (ret == -1) {
|
|
SUBTITLE_LOGE("set section filter failed (%s)", strerror(errno));
|
|
return DEMUX_ERROR_SYS;
|
|
}
|
|
return AM_SUCCESS;
|
|
}
|
|
|
|
static int dvb_set_pes_filter(DemuxDeviceType *dev, DemuxFilterType *filter,
|
|
const struct dmx_pes_filter_params *params) {
|
|
int fd = (long)filter->drv_data;
|
|
int ret;
|
|
UNUSED(dev);
|
|
// TODO for coverity check_return
|
|
ret = fcntl(fd, F_SETFL, O_NONBLOCK);
|
|
if (ret == -1) {
|
|
SUBTITLE_LOGE("fcntl failed (%s)", strerror(errno));
|
|
return DEMUX_ERROR_SYS;
|
|
}
|
|
ret = ioctl(fd, DMX_SET_PES_FILTER, params);
|
|
if (ret == -1) {
|
|
SUBTITLE_LOGE("set section filter failed (%s)", strerror(errno));
|
|
return DEMUX_ERROR_SYS;
|
|
}
|
|
|
|
return AM_SUCCESS;
|
|
}
|
|
|
|
static int dvb_enable_filter(DemuxDeviceType *dev, DemuxFilterType *filter, bool enable) {
|
|
int fd = (long)filter->drv_data;
|
|
int ret;
|
|
UNUSED(dev);
|
|
if (enable) {
|
|
ret = ioctl(fd, DMX_START, 0);
|
|
} else {
|
|
ret = ioctl(fd, DMX_STOP, 0);
|
|
}
|
|
if (ret == -1) {
|
|
SUBTITLE_LOGE("start filter failed (%s)", strerror(errno));
|
|
return DEMUX_ERROR_SYS;
|
|
}
|
|
return AM_SUCCESS;
|
|
}
|
|
|
|
static int dvb_set_buf_size(DemuxDeviceType *dev, DemuxFilterType *filter, int size) {
|
|
int fd = (long)filter->drv_data;
|
|
int ret;
|
|
UNUSED(dev);
|
|
ret = ioctl(fd, DMX_SET_BUFFER_SIZE, size);
|
|
if (ret == -1) {
|
|
SUBTITLE_LOGE("set buffer size failed (%s)", strerror(errno));
|
|
return DEMUX_ERROR_SYS;
|
|
}
|
|
return AM_SUCCESS;
|
|
}
|
|
|
|
static int dvb_poll(DemuxDeviceType *dev, Demux_FilterMaskType *mask, int timeout) {
|
|
DVBDmx_t *dmx = (DVBDmx_t *)dev->drv_data;
|
|
struct pollfd fds[DEMUX_FILTER_COUNT_EXT];
|
|
int fids[DEMUX_FILTER_COUNT_EXT] = {0};
|
|
int i, cnt = 0, ret;
|
|
|
|
for (i = 0; i < DEMUX_FILTER_COUNT; i++) {
|
|
if (dmx->fd[i] != -1) {
|
|
fds[cnt].events = POLLIN | POLLERR;
|
|
fds[cnt].fd = dmx->fd[i];
|
|
fids[cnt] = i;
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
// add notify event fd
|
|
if (dmx->event_fd != -1) {
|
|
fds[cnt].events = POLLIN | POLLERR;
|
|
fds[cnt].fd = dmx->event_fd;
|
|
cnt++;
|
|
}
|
|
if (!cnt) return DEMUX_ERROR_TIMEOUT;
|
|
ret = poll(fds, cnt, timeout);
|
|
if (ret <= 0) {
|
|
return DEMUX_ERROR_TIMEOUT;
|
|
}
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
if (fds[i].revents & (POLLIN | POLLERR)) {
|
|
DEMUX_FILTER_MASK_SET(mask, fids[i]);
|
|
if (fds[i].fd == dmx->event_fd) {
|
|
int64_t data = 0;
|
|
int event_fd = dmx->event_fd;
|
|
if (event_fd != -1) {
|
|
ret = read(event_fd, &data, sizeof(int64_t));
|
|
if (ret < 0 && errno == EBADF) { // Bad File Descriptor
|
|
SUBTITLE_LOGW("event_fd was closed, ignoring read error");
|
|
} else {
|
|
SUBTITLE_LOGI("dvb_poll_exit read:%d\n", ret);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return AM_SUCCESS;
|
|
}
|
|
|
|
static int dvb_poll_exit(DemuxDeviceType *dev) {
|
|
SUBTITLE_LOGI_FUNC_IN();
|
|
DVBDmx_t *dmx = (DVBDmx_t *)dev->drv_data;
|
|
int64_t pad = 1;
|
|
ssize_t ret = write(dmx->event_fd, &pad, sizeof(int64_t));
|
|
if (ret != sizeof(int64_t)) {
|
|
SUBTITLE_LOGE("%m");
|
|
}
|
|
SUBTITLE_LOGI_FUNC_OUT();
|
|
return AM_SUCCESS;
|
|
}
|
|
|
|
static int dvb_read(DemuxDeviceType *dev, DemuxFilterType *filter, uint8_t *buf, int *size) {
|
|
int fd = (long)filter->drv_data;
|
|
int len = *size;
|
|
int ret;
|
|
struct pollfd pfd;
|
|
UNUSED(dev);
|
|
if (fd == -1) return DEMUX_ERROR_NOT_ALLOCATED;
|
|
pfd.events = POLLIN | POLLERR;
|
|
pfd.fd = fd;
|
|
ret = poll(&pfd, 1, 0);
|
|
if (ret <= 0) return DEMUX_ERROR_NO_DATA;
|
|
ret = read(fd, buf, len);
|
|
if (ret <= 0) {
|
|
if (errno == ETIMEDOUT) return DEMUX_ERROR_TIMEOUT;
|
|
SUBTITLE_LOGE("read demux failed (%s) %d", strerror(errno), errno);
|
|
return DEMUX_ERROR_SYS;
|
|
}
|
|
*size = ret;
|
|
return AM_SUCCESS;
|
|
}
|
|
|
|
static int dvb_set_source(DemuxDeviceType *dev, AmlogicDemuxSourceType src) {
|
|
char buf[32];
|
|
char *cmd;
|
|
snprintf(buf, sizeof(buf), "/sys/class/stb/demux%d_source", dev->dev_no);
|
|
switch (src) {
|
|
case DEMUX_SRC_TS0:
|
|
cmd = "ts0";
|
|
break;
|
|
case DEMUX_SRC_TS1:
|
|
cmd = "ts1";
|
|
break;
|
|
#if defined(CHIP_8226M) || defined(CHIP_8626X)
|
|
case DEMUX_SRC_TS2:
|
|
cmd = "ts2";
|
|
break;
|
|
#endif
|
|
case DEMUX_SRC_TS3:
|
|
cmd = "ts3";
|
|
break;
|
|
case DEMUX_SRC_HIU:
|
|
cmd = "hiu";
|
|
break;
|
|
case DEMUX_SRC_HIU1:
|
|
cmd = "hiu1";
|
|
break;
|
|
default:
|
|
SUBTITLE_LOGE("do not support demux source %d", src);
|
|
return DEMUX_ERROR_NOT_SUPPORTED;
|
|
}
|
|
return AM_SUCCESS;
|
|
}
|
|
|
|
static void dump_newest_pts(char *deviceName) {
|
|
amdmx_filter_mem_info memInfo = {0};
|
|
|
|
int fd = open(deviceName, O_RDWR);
|
|
if (fd == -1) {
|
|
SUBTITLE_LOGE("[newest_avpts_tag] fail to open device %s: %m", deviceName);
|
|
return;
|
|
}
|
|
|
|
SUBTITLE_LOGI("[newest_avpts_tag] device=%s, fd=%d", deviceName, fd);
|
|
int ret = ioctl(fd, AMDMX_GET_FILTER_MEM_INFO, &memInfo);
|
|
if (ret == -1) {
|
|
SUBTITLE_LOGE(
|
|
"[newest_avpts_tag] ioctl DMX_GET_FILTER_MEM_INFO failed,"
|
|
" fd=%d, error: %m",
|
|
fd);
|
|
}
|
|
|
|
SUBTITLE_LOGI("[newest_avpts_tag] filter_num = %d", memInfo.filter_num);
|
|
for (__u32 i = 0; i < memInfo.filter_num; ++i) {
|
|
amfilter_mem_info info = memInfo.info[i];
|
|
SUBTITLE_LOGI("[newest_avpts_tag] [%2d] type = %d, pts = %lld", i, info.type,
|
|
info.filter_info.newest_pts);
|
|
}
|
|
|
|
close(fd);
|
|
}
|
|
|
|
static int dvb_get_newest_pts(DemuxDeviceType *dev, amdmx_filter_mem_info *demuxFilterMemInfo) {
|
|
if (!dev || !demuxFilterMemInfo || !dev->drv_data) {
|
|
SUBTITLE_LOGE("[newest_avpts_tag] wrong input param");
|
|
return AM_FAILURE;
|
|
}
|
|
|
|
memset(demuxFilterMemInfo, 0, sizeof(*demuxFilterMemInfo));
|
|
DVBDmx_t *dmx = (DVBDmx_t *)dev->drv_data;
|
|
if (!dmx) {
|
|
SUBTITLE_LOGE("[newest_avpts_tag] no demux device");
|
|
return AM_FAILURE;
|
|
}
|
|
|
|
int fd = open(dmx->dev_name, O_RDWR);
|
|
if (fd == -1) {
|
|
SUBTITLE_LOGE("[newest_avpts_tag] fail to open device %s: %m", dmx->dev_name);
|
|
return AM_FAILURE;
|
|
}
|
|
|
|
int ret = ioctl(fd, AMDMX_GET_FILTER_MEM_INFO, demuxFilterMemInfo);
|
|
if (ret == -1) {
|
|
SUBTITLE_LOGE(
|
|
"[newest_avpts_tag] ioctl DMX_GET_FILTER_MEM_INFO failed,"
|
|
" device=%s fd=%d, error: %m",
|
|
dmx->dev_name, fd);
|
|
close(fd);
|
|
return AM_FAILURE;
|
|
}
|
|
|
|
close(fd);
|
|
return AM_SUCCESS;
|
|
}
|