846 lines
27 KiB
C
846 lines
27 KiB
C
/*
|
|
* Copyright (C) 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.
|
|
*/
|
|
#define LOG_TAG "CcSourceManager"
|
|
|
|
#include "CcSourceManager.h"
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <poll.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/utsname.h>
|
|
#include <unistd.h>
|
|
|
|
#include "AmlogicUtil.h"
|
|
#include "CcSourceMediaProxy.h"
|
|
#include "SubtitleLog.h"
|
|
#include "UserdataDriver.h"
|
|
#include "VbiDriver.h"
|
|
|
|
#define AMSTREAM_IOC_MAGIC 'S'
|
|
#define AMSTREAM_IOC_UD_LENGTH _IOR(AMSTREAM_IOC_MAGIC, 0x54, unsigned long)
|
|
#define AMSTREAM_IOC_UD_POC _IOR(AMSTREAM_IOC_MAGIC, 0x55, int)
|
|
#define AMSTREAM_IOC_UD_FLUSH_USERDATA _IOR(AMSTREAM_IOC_MAGIC, 0x56, int)
|
|
#define AMSTREAM_IOC_UD_BUF_READ _IOR(AMSTREAM_IOC_MAGIC, 0x57, int)
|
|
#define AMSTREAM_IOC_UD_AVAILABLE_VDEC _IOR(AMSTREAM_IOC_MAGIC, 0x5c, unsigned int)
|
|
|
|
#ifdef NEED_CCDATA_FROM_CCDATABASE
|
|
#define DAS_RET_CHK() \
|
|
do { \
|
|
if (ret == 0) { \
|
|
continue; \
|
|
} else if (ret == -1) { \
|
|
SUBTITLE_LOGE("bad parameter"); \
|
|
continue; \
|
|
} else if (ret <= -3) { \
|
|
SUBTITLE_LOGE("dataserver disconnect"); \
|
|
ctx->das = NULL; \
|
|
stage = STAGE_INIT; \
|
|
continue; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define DAS_STAGE_CHK() \
|
|
do { \
|
|
switch (stage) { \
|
|
case STAGE_RB: \
|
|
stage = STAGE_VDECID; \
|
|
break; \
|
|
case STAGE_VDECID: \
|
|
stage = STAGE_SRC; \
|
|
break; \
|
|
case STAGE_SRC: \
|
|
stage = STAGE_DAT; \
|
|
break; \
|
|
default: \
|
|
stage = STAGE_DAT; \
|
|
break; \
|
|
} \
|
|
} while (0)
|
|
#endif
|
|
|
|
static int getKernelVersion(void) {
|
|
int version, subversion, patchlevel;
|
|
struct utsname utsn;
|
|
/* Return 0 on failure, and attempt to probe with empty kernal version */
|
|
if (uname(&utsn)) return 0;
|
|
if (sscanf(utsn.release, "%d.%d.%d", &version, &subversion, &patchlevel) != 3) return 0;
|
|
return version;
|
|
}
|
|
|
|
static void swapData(uint8_t *userData, int udSize) {
|
|
int swapBlocks = -1;
|
|
int i = 0, j = 0, k = 0, m = 0;
|
|
unsigned char cTemp = '\0';
|
|
swapBlocks = udSize >> 3;
|
|
// Swap byte order
|
|
for (i = 0; i < swapBlocks; i++) {
|
|
j = i << 3;
|
|
k = j + 7;
|
|
for (m = 0; m < 4; m++) {
|
|
cTemp = userData[j];
|
|
userData[j++] = userData[k];
|
|
userData[k--] = cTemp;
|
|
}
|
|
}
|
|
}
|
|
|
|
static const char *retToString(int ret) {
|
|
if (ret == -2) {
|
|
return "timeout";
|
|
} else if (ret == -3) {
|
|
return "disconnect";
|
|
}
|
|
|
|
static char numStr[20] = {0};
|
|
sprintf(numStr, "%d", ret);
|
|
return numStr;
|
|
}
|
|
|
|
static void dumpCcData(char *who, uint8_t *buff, int size) {
|
|
int i = -1;
|
|
char buf[4096] = {'\0'};
|
|
|
|
if (size > 1024) size = 1024;
|
|
for (i = 0; i < size; i++) {
|
|
sprintf(buf + i * 3, "%02x ", buff[i]);
|
|
}
|
|
SUBTITLE_LOGI("[ccSource]: %s, dataSize:%d data:%s", who, size, buf);
|
|
}
|
|
|
|
static void *ccUserdataDeviceTask(void *args) {
|
|
int fd = -1;
|
|
int r = -1, ret = -1;
|
|
uint8_t data[MAX_CC_DATA_LEN] = {'\0'};
|
|
int flush = 1;
|
|
int vdecIds = -1;
|
|
int readVdecId = -1;
|
|
int kernelVersion = -1;
|
|
struct pollfd pfd;
|
|
struct CcSourceUserdataParam userParaInfo;
|
|
CcSourceCtx *ctx = (CcSourceCtx *)args;
|
|
|
|
if (!ctx->running) return NULL;
|
|
|
|
fd = open(AMSTREAM_USDRDATA_DEV_FILE, O_RDONLY);
|
|
if (fd == -1) {
|
|
SUBTITLE_LOGE("cannot open userdata device");
|
|
return NULL;
|
|
}
|
|
|
|
pfd.events = POLLIN | POLLERR;
|
|
pfd.fd = fd;
|
|
kernelVersion = getKernelVersion();
|
|
while (ctx->running) {
|
|
ret = poll(&pfd, 1, USERDATA_POLL_TIMEOUT);
|
|
if (ret != 1) continue;
|
|
|
|
if (!ctx->running) break;
|
|
|
|
if (!(pfd.revents & POLLIN)) continue;
|
|
// For multi-instances supports
|
|
vdecIds = 0;
|
|
if (-1 == ioctl(fd, AMSTREAM_IOC_UD_AVAILABLE_VDEC, &vdecIds)) {
|
|
SUBTITLE_LOGE("get available vdec failed");
|
|
continue;
|
|
}
|
|
|
|
int buildVersion = property_get_int32("ro.build.version.sdk", 28);
|
|
if (kernelVersion >= 5 || buildVersion >= 29) {
|
|
SUBTITLE_LOGD("kernelVersion:%d, buildVersion:%d", kernelVersion, buildVersion);
|
|
readVdecId = vdecIds;
|
|
} else {
|
|
readVdecId = ffs(vdecIds) - 1;
|
|
}
|
|
|
|
if (flush) {
|
|
ioctl(fd, AMSTREAM_IOC_UD_FLUSH_USERDATA, &readVdecId);
|
|
flush = 0;
|
|
continue;
|
|
}
|
|
|
|
do {
|
|
if (!ctx->running) break;
|
|
|
|
memset(&userParaInfo, 0, sizeof(struct CcSourceUserdataParam));
|
|
userParaInfo.pbuf_addr = (void *)(size_t)data;
|
|
userParaInfo.buf_len = sizeof(data);
|
|
userParaInfo.instance_id = readVdecId;
|
|
|
|
if (-1 == ioctl(fd, AMSTREAM_IOC_UD_BUF_READ, &userParaInfo)) {
|
|
SUBTITLE_LOGE("call AMSTREAM_IOC_UD_BUF_READ failed\n");
|
|
continue;
|
|
}
|
|
|
|
r = userParaInfo.data_size;
|
|
if (r > MAX_CC_DATA_LEN) {
|
|
userParaInfo.data_size = MAX_CC_DATA_LEN;
|
|
r = MAX_CC_DATA_LEN;
|
|
}
|
|
|
|
if (r <= 0) continue;
|
|
|
|
if (isSubtitleDumpAvailable()) dumpCcData("userdata ioctl", data, r);
|
|
|
|
swapData(data, r);
|
|
if (ctx->param.uDataCb) {
|
|
if (isSubtitleDumpAvailable()) dumpCcData("userdata after swap", data, r);
|
|
ctx->param.uDataCb(ctx, &userParaInfo);
|
|
}
|
|
} while (userParaInfo.meta_info.records_in_que > 1);
|
|
}
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
static void *ccVbiDeviceTask(void *args) {
|
|
struct vbi_data_s vbi[50];
|
|
int fd = -1;
|
|
int type = VBI_TYPE_USCC;
|
|
int last = -1;
|
|
int ret = -1;
|
|
CcSourceCtx *ctx = (CcSourceCtx *)args;
|
|
|
|
fd = open(CC_VBI_DEV_FILE, O_RDWR);
|
|
if (fd == -1) {
|
|
SUBTITLE_LOGE("cannot open \"%s\"", CC_VBI_DEV_FILE);
|
|
return NULL;
|
|
}
|
|
|
|
if (ioctl(fd, VBI_IOC_SET_TYPE, &type) == -1)
|
|
SUBTITLE_LOGI("VBI_IOC_SET_TYPE error:%s", strerror(errno));
|
|
if (ioctl(fd, VBI_IOC_START) == -1) SUBTITLE_LOGI("VBI_IOC_START error:%s", strerror(errno));
|
|
|
|
AmlogicGetClock(&last);
|
|
ctx->vCtx.last = last;
|
|
|
|
while (ctx->running) {
|
|
struct pollfd pfd;
|
|
pfd.fd = fd;
|
|
pfd.events = POLLIN;
|
|
|
|
ret = poll(&pfd, 1, CC_POLL_TIMEOUT);
|
|
if (ctx->running && (ret > 0) && (pfd.events & POLLIN)) {
|
|
struct vbi_data_s *pd;
|
|
|
|
ret = read(fd, vbi, sizeof(vbi));
|
|
pd = vbi;
|
|
SUBTITLE_LOGI("am_vbi_data_thread running read data == %d", ret);
|
|
|
|
if (ret >= (int)sizeof(struct vbi_data_s)) {
|
|
while (ret >= (int)sizeof(struct vbi_data_s)) {
|
|
SUBTITLE_LOGD("device vbi line %d: %02x %02x", pd->line_num, pd->b[0],
|
|
pd->b[1]);
|
|
if (ctx->param.vDataCb) {
|
|
ctx->param.vDataCb(ctx, pd);
|
|
}
|
|
pd++;
|
|
ret -= sizeof(struct vbi_data_s);
|
|
}
|
|
}
|
|
if (ctx->param.vEvtHandler) {
|
|
ctx->param.vEvtHandler(ctx);
|
|
}
|
|
}
|
|
}
|
|
SUBTITLE_LOGI("am_vbi_data_thread exit");
|
|
ioctl(fd, VBI_IOC_STOP);
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
static void v4lInfoToUserdataParam(struct v4l_stream_common_s *v4lInfo,
|
|
struct CcSourceUserdataParam *pUserdataParam) {
|
|
memset(pUserdataParam, 0, sizeof(struct CcSourceUserdataParam));
|
|
|
|
pUserdataParam->version = v4lInfo->version;
|
|
pUserdataParam->instance_id = v4lInfo->instance_id;
|
|
pUserdataParam->data_size = v4lInfo->data_size;
|
|
|
|
// Meta data convert
|
|
struct userdata_meta_info_t *userdataInfo = &pUserdataParam->meta_info;
|
|
struct userdata_cc_meta_info_t *v4lMeta = &v4lInfo->cc_meta_data;
|
|
*userdataInfo = *(struct userdata_meta_info_t *)v4lMeta;
|
|
|
|
// Buffer data
|
|
pUserdataParam->pbuf_addr = &v4lInfo->data[0];
|
|
}
|
|
|
|
static void *ccMediaProxyDeviceTask(void *args) {
|
|
CcSourceCtx *ctx = (CcSourceCtx *)args;
|
|
if (!ctx || !ctx->running) return NULL;
|
|
|
|
int fd = -1;
|
|
fd = open(MEDIA_PROXY_DEV_FILE, O_RDONLY);
|
|
if (fd == -1) {
|
|
SUBTITLE_LOGE("cannot open mediaproxy device");
|
|
return NULL;
|
|
}
|
|
|
|
int len = 64;
|
|
if (ioctl(fd, MEDIAPROXY_SET_FIFO_LEN, &len) < 0) {
|
|
SUBTITLE_LOGE("Failed to set fifo len %d", len);
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
int msgType = MEDIA_VIDEO_CC_INFO;
|
|
if (ioctl(fd, MEDIAPROXY_SUBSCRIBE, &msgType) < 0) {
|
|
SUBTITLE_LOGE("Failed to subscribe msg type %x", msgType);
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
int role = MP_ROLE_CONSUMER;
|
|
if (ioctl(fd, MEDIAPROXY_CONNECT, &role) < 0) {
|
|
SUBTITLE_LOGE("Failed to connect mediaproxy");
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
int vdecId = ctx->param.playerId;
|
|
SUBTITLE_LOGI("set vdec id:%d to media proxy", vdecId);
|
|
if (ioctl(fd, MEDIAPROXY_SET_VDEC_ID, &vdecId) < 0) {
|
|
SUBTITLE_LOGE("Failed to set vid %d", vdecId);
|
|
if (ioctl(fd, MEDIAPROXY_DISCONNECT, &role) < 0) {
|
|
SUBTITLE_LOGE("Failed to disconnect mediaproxy");
|
|
}
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
int bufSize = 6 * 1024;
|
|
void *bufPtr = malloc(bufSize);
|
|
if (!bufPtr) {
|
|
SUBTITLE_LOGE("no memory for reader, size:%d", bufSize);
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
struct aml_buffer_info buf;
|
|
buf.message_type = MEDIA_VIDEO_CC_INFO;
|
|
buf.data.buf_info.size = bufSize;
|
|
buf.data.buf_info.ptr = (long long)bufPtr;
|
|
|
|
struct CcSourceUserdataParam usrParam;
|
|
struct CcSourceUserdataParam *pUsrParam = &usrParam;
|
|
|
|
while (ctx->running) {
|
|
struct pollfd pfd;
|
|
int ret;
|
|
|
|
pfd.fd = fd;
|
|
pfd.events = POLLIN | POLLERR;
|
|
|
|
ret = poll(&pfd, 1, USERDATA_POLL_TIMEOUT);
|
|
|
|
if (!ctx->running || ret <= 0 || !(pfd.events & POLLIN)) continue;
|
|
|
|
do {
|
|
ret = ioctl(fd, MEDIAPROXY_GET_BUF_INFO, &buf);
|
|
|
|
if (ret < 0) {
|
|
SUBTITLE_LOGE("mp ioctl READ fail, err:%m");
|
|
continue;
|
|
}
|
|
|
|
struct v4l_stream_common_s *v4lInfo =
|
|
(struct v4l_stream_common_s *)buf.data.buf_info.ptr;
|
|
|
|
SUBTITLE_LOGD("mediaproxy: ver:%u, inst:%u, type:%u, data_size:%u, sizeof(v4l_s):%zu",
|
|
v4lInfo->version, v4lInfo->instance_id, v4lInfo->meta_type,
|
|
v4lInfo->data_size, sizeof(struct v4l_stream_common_s));
|
|
|
|
if (v4lInfo->meta_type != V4L_STREAM_CC_INFO || v4lInfo->data_size <= 0) {
|
|
continue;
|
|
}
|
|
|
|
v4lInfoToUserdataParam(v4lInfo, pUsrParam);
|
|
|
|
if (ctx->param.uDataCb) {
|
|
if (isSubtitleDumpAvailable()) {
|
|
dumpCcData("mediaproxy data:", pUsrParam->pbuf_addr, pUsrParam->data_size);
|
|
}
|
|
ctx->param.uDataCb(ctx, pUsrParam);
|
|
}
|
|
} while (0 /*user_para_info.meta_info.records_in_que > 1*/);
|
|
}
|
|
|
|
if (ioctl(fd, MEDIAPROXY_DISCONNECT, &role) < 0) {
|
|
SUBTITLE_LOGE("Failed to disconnect mediaproxy");
|
|
}
|
|
close(fd);
|
|
free(bufPtr);
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef NEED_CCDATA_FROM_CCDATABASE
|
|
static void *ccDataserverUserdataTask(void *args) {
|
|
enum {
|
|
STAGE_INIT,
|
|
STAGE_RB = STAGE_INIT,
|
|
STAGE_VDECID,
|
|
STAGE_SRC,
|
|
STAGE_DAT,
|
|
} stage = STAGE_INIT;
|
|
|
|
CcSourceCtx *ctx = (CcSourceCtx *)args;
|
|
SUBTITLE_LOGI("dataserver userdata task started.");
|
|
|
|
int buf_size = 1024 * 10;
|
|
uint8_t *buf = (uint8_t *)malloc(buf_size);
|
|
if (!buf) {
|
|
SUBTITLE_LOGE("memory fail");
|
|
goto exit;
|
|
}
|
|
|
|
int ret = -1, last_ret = -100;
|
|
while (ctx->running) {
|
|
if (!ctx->das) {
|
|
SUBTITLE_LOGE("dataserver not connected, try reconnect...");
|
|
sleep(1);
|
|
|
|
if (!ctx->running) goto exit;
|
|
|
|
ctx->das = ctx->das_connect();
|
|
continue;
|
|
}
|
|
|
|
// Prepare for the data
|
|
uint8_t msg[sizeof(ds_msg_head_t) + 64];
|
|
ds_msg_head_t *pmsg = (ds_msg_head_t *)msg;
|
|
pmsg->magic = DS_MAGIC;
|
|
pmsg->type = DS_T_CNTL;
|
|
|
|
switch (stage) {
|
|
case STAGE_RB:
|
|
pmsg->size = snprintf((char *)pmsg->body, 64, ds_cmd_enable_ringbuffer, 1024 * 512);
|
|
pmsg->size++;
|
|
ret = dataserver_write_message(ctx->das, pmsg, sizeof(ds_msg_head_t) + pmsg->size);
|
|
DAS_RET_CHK();
|
|
break;
|
|
case STAGE_VDECID:
|
|
SUBTITLE_LOGI("dataserver set playerId:%d", ctx->param.playerId);
|
|
pmsg->size = snprintf((char *)pmsg->body, 64,
|
|
ds_cmd_set_source_para_userdata_vdec_id, ctx->param.playerId);
|
|
pmsg->size++;
|
|
ret = dataserver_write_message(ctx->das, pmsg, sizeof(ds_msg_head_t) + pmsg->size);
|
|
DAS_RET_CHK();
|
|
break;
|
|
case STAGE_SRC:
|
|
SUBTITLE_LOGI("dataserver set data source file:%s", ctx->devFile);
|
|
pmsg->size = snprintf((char *)pmsg->body, 64, ds_cmd_set_source, ctx->devFile,
|
|
ctx->param.owner);
|
|
pmsg->size++;
|
|
ret = dataserver_write_message(ctx->das, pmsg, sizeof(ds_msg_head_t) + pmsg->size);
|
|
DAS_RET_CHK();
|
|
break;
|
|
default:
|
|
// Stage is in init
|
|
break;
|
|
}
|
|
|
|
if (!ctx->running) goto exit;
|
|
|
|
ret = dataserver_read_message(ctx->das, buf, buf_size, 1000);
|
|
|
|
if (ret == -2 || ret == -3) {
|
|
if (ret != last_ret) {
|
|
SUBTITLE_LOGI("%s", retToString(ret));
|
|
}
|
|
last_ret = ret;
|
|
continue;
|
|
}
|
|
|
|
last_ret = ret;
|
|
|
|
if (!ctx->running) goto exit;
|
|
|
|
DAS_RET_CHK();
|
|
|
|
pmsg = (ds_msg_head_t *)buf;
|
|
switch (stage) {
|
|
case STAGE_RB:
|
|
case STAGE_VDECID:
|
|
case STAGE_SRC:
|
|
if (pmsg->type == DS_T_RESP) {
|
|
if (strncmp((char *)pmsg->body, "ok", 2) == 0) {
|
|
// Next stage
|
|
DAS_STAGE_CHK();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Stage fail, should exit and wait for next start.
|
|
SUBTITLE_LOGE("fail in stage %d", stage);
|
|
dataserver_disconnect(ctx->das);
|
|
dataserver_destroy(ctx->das);
|
|
ctx->das = NULL;
|
|
stage = STAGE_INIT;
|
|
goto exit;
|
|
case STAGE_DAT:
|
|
default:
|
|
if (pmsg->type == DS_T_DATA) {
|
|
unsigned int left = pmsg->size;
|
|
|
|
ds_data_userdata_t *ud = (ds_data_userdata_t *)pmsg->body;
|
|
uint32_t version = ud->version;
|
|
// See SWPL-224528, PTS is changed from 32bit to 33bit from version 2.
|
|
// - Version 1 uses ds_data_userdata_t::meta_info::vpts as the PTS;
|
|
// - Version 2 uses ds_data_userdata_t::meta_info::vpts_64 as the PTS.
|
|
if (version != 2) {
|
|
SUBTITLE_LOGE(
|
|
"[subtitle_unsupported_flag] received unsupported version %d, "
|
|
"drop the current data.",
|
|
version);
|
|
continue;
|
|
}
|
|
|
|
while (left >= sizeof(ds_data_userdata_t)) {
|
|
if (strcmp(ctx->devFile, AMSTREAM_USDRDATA_DEV_FILE) == 0) {
|
|
if (isSubtitleDumpAvailable()) {
|
|
dumpCcData("dataserver amstream ioctl", ud->data, ud->data_size);
|
|
}
|
|
// Swap data every 8 bytes.
|
|
swapData(ud->data, ud->data_size);
|
|
}
|
|
if (ctx->param.uDataCb) {
|
|
if (isSubtitleDumpAvailable())
|
|
dumpCcData("dataserver userdata", ud->data, ud->data_size);
|
|
ctx->param.uDataCb(ctx, ud);
|
|
}
|
|
|
|
int udsize = sizeof(ds_data_userdata_t) + ud->data_size;
|
|
left -= udsize;
|
|
ud = (ds_data_userdata_t *)(pmsg->body + udsize);
|
|
}
|
|
} else if (pmsg->type == DS_T_EVNT) {
|
|
SUBTITLE_LOGE("data overflow, restart data pipe");
|
|
dataserver_disconnect(ctx->das);
|
|
dataserver_destroy(ctx->das);
|
|
ctx->das = NULL;
|
|
stage = STAGE_INIT;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
exit:
|
|
if (buf) {
|
|
free(buf);
|
|
}
|
|
|
|
SUBTITLE_LOGI("dataserver userdata task exit.");
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
#ifdef NEED_CCDATA_FROM_CCDATABASE
|
|
static void *ccDataServerVbiTask(void *args) {
|
|
enum {
|
|
STAGE_INIT,
|
|
STAGE_PAR = STAGE_INIT,
|
|
STAGE_SRC,
|
|
STAGE_DAT,
|
|
} stage = STAGE_INIT;
|
|
|
|
CcSourceCtx *ctx = (CcSourceCtx *)args;
|
|
|
|
SUBTITLE_LOGI("dataserver vbi task started.");
|
|
|
|
int buf_size = sizeof(ds_data_vbi_t) * 50;
|
|
|
|
uint8_t *buf = (uint8_t *)malloc(buf_size);
|
|
|
|
int last = -1;
|
|
AmlogicGetClock(&last);
|
|
ctx->vCtx.last = last;
|
|
if (!buf) {
|
|
SUBTITLE_LOGE("memory fail");
|
|
goto exit;
|
|
}
|
|
|
|
int ret = -1, last_ret = -100;
|
|
while (ctx->running) {
|
|
if (!ctx->das) {
|
|
SUBTITLE_LOGE("dataserver not connected, try reconnect...");
|
|
sleep(1);
|
|
|
|
if (!ctx->running) goto exit;
|
|
|
|
ctx->das = ctx->das_connect();
|
|
continue;
|
|
}
|
|
|
|
// Prepare for the data
|
|
uint8_t msg[sizeof(ds_msg_head_t) + 64];
|
|
ds_msg_head_t *pmsg = (ds_msg_head_t *)msg;
|
|
pmsg->magic = DS_MAGIC;
|
|
pmsg->type = DS_T_CNTL;
|
|
|
|
switch (stage) {
|
|
case STAGE_PAR:
|
|
pmsg->size =
|
|
snprintf((char *)pmsg->body, 64, ds_cmd_set_source_para_vbi_type, 1 /*USCC*/);
|
|
pmsg->size++;
|
|
ret = dataserver_write_message(ctx->das, (uint8_t *)pmsg,
|
|
sizeof(ds_msg_head_t) + pmsg->size);
|
|
DAS_RET_CHK();
|
|
break;
|
|
case STAGE_SRC:
|
|
pmsg->size = snprintf((char *)pmsg->body, 64, ds_cmd_set_source, CC_VBI_DEV_FILE,
|
|
ctx->param.owner);
|
|
pmsg->size++;
|
|
ret = dataserver_write_message(ctx->das, pmsg, sizeof(ds_msg_head_t) + pmsg->size);
|
|
DAS_RET_CHK();
|
|
break;
|
|
default:
|
|
// Stage is in init
|
|
break;
|
|
}
|
|
|
|
if (!ctx->running) goto exit;
|
|
|
|
ret = dataserver_read_message(ctx->das, buf, buf_size, 1000);
|
|
if (ret == -2 || ret == -3) {
|
|
if (ret != last_ret) {
|
|
SUBTITLE_LOGI("%s", retToString(ret));
|
|
}
|
|
last_ret = ret;
|
|
continue;
|
|
}
|
|
|
|
last_ret = ret;
|
|
|
|
if (!ctx->running) goto exit;
|
|
|
|
DAS_RET_CHK();
|
|
|
|
pmsg = (ds_msg_head_t *)buf;
|
|
switch (stage) {
|
|
case STAGE_SRC:
|
|
case STAGE_PAR:
|
|
if (pmsg->type == DS_T_RESP) {
|
|
if (strncmp((char *)pmsg->body, "ok", 2) == 0) {
|
|
// Next stage
|
|
stage = (stage == STAGE_PAR) ? STAGE_SRC : STAGE_DAT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Stage fail, should exit and wait for next start
|
|
SUBTITLE_LOGE("fail in stage %d", stage);
|
|
dataserver_disconnect(ctx->das);
|
|
dataserver_destroy(ctx->das);
|
|
ctx->das = NULL;
|
|
stage = STAGE_INIT;
|
|
goto exit;
|
|
case STAGE_DAT:
|
|
default:
|
|
if (pmsg->type == DS_T_DATA) {
|
|
ds_data_vbi_t *vbi = (ds_data_vbi_t *)pmsg->body;
|
|
unsigned int left = pmsg->size;
|
|
|
|
while (left >= sizeof(ds_data_vbi_t)) {
|
|
if (ctx->param.vDataCb) {
|
|
SUBTITLE_LOGD("dataserver vbi line %d: %02x %02x", vbi->line_num,
|
|
vbi->b[0], vbi->b[1]);
|
|
ctx->param.vDataCb(ctx, vbi);
|
|
}
|
|
vbi++;
|
|
left -= sizeof(ds_data_vbi_t);
|
|
}
|
|
if (ctx->param.vEvtHandler) {
|
|
ctx->param.vEvtHandler(ctx);
|
|
}
|
|
|
|
} else if (pmsg->type == DS_T_EVNT) {
|
|
SUBTITLE_LOGE("data overflow, restart data pipe");
|
|
dataserver_disconnect(ctx->das);
|
|
dataserver_destroy(ctx->das);
|
|
ctx->das = NULL;
|
|
stage = STAGE_INIT;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (buf) {
|
|
free(buf);
|
|
}
|
|
SUBTITLE_LOGI("vbi task exit.");
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
CcSourceMgrHnd ccSourceManagerCreate() {
|
|
SUBTITLE_LOGI_FUNC_IN();
|
|
CcSourceCtx *ctx;
|
|
ctx = (CcSourceCtx *)calloc(1, sizeof(CcSourceCtx));
|
|
if (!ctx) {
|
|
SUBTITLE_LOGE("memory fail.");
|
|
return NULL;
|
|
}
|
|
pthread_mutex_init(&ctx->lock, NULL);
|
|
SUBTITLE_LOGI("end");
|
|
return ctx;
|
|
}
|
|
|
|
CcSourceStatus ccSourceManagerStop(CcSourceMgrHnd handle) {
|
|
SUBTITLE_LOGI_FUNC_IN();
|
|
CcSourceCtx *ctx = (CcSourceCtx *)handle;
|
|
if (ctx == NULL) {
|
|
SUBTITLE_LOGE("source context is null, return!");
|
|
return CC_SOURCE_INVALID;
|
|
}
|
|
|
|
bool join = false;
|
|
pthread_mutex_lock(&ctx->lock);
|
|
if (ctx->running) {
|
|
ctx->running = false;
|
|
join = true;
|
|
}
|
|
pthread_mutex_unlock(&ctx->lock);
|
|
|
|
#ifdef NEED_CCDATA_FROM_CCDATABASE
|
|
// Disconnect includes waking fmq message block.
|
|
if (ctx->das) {
|
|
dataserver_disconnect(ctx->das);
|
|
}
|
|
#endif
|
|
|
|
if (join) {
|
|
pthread_join(ctx->thread, NULL);
|
|
}
|
|
|
|
#ifdef NEED_CCDATA_FROM_CCDATABASE
|
|
if (ctx->das) {
|
|
dataserver_destroy(ctx->das);
|
|
ctx->das = NULL;
|
|
}
|
|
#endif
|
|
SUBTITLE_LOGI("end");
|
|
return CC_SOURCE_OK;
|
|
}
|
|
|
|
CcSourceStatus ccSourceManagerDestroy(CcSourceMgrHnd handle) {
|
|
SUBTITLE_LOGI_FUNC_IN();
|
|
CcSourceCtx *ctx = (CcSourceCtx *)handle;
|
|
if (ctx == NULL) {
|
|
SUBTITLE_LOGE("source context is null, return!");
|
|
return CC_SOURCE_INVALID;
|
|
}
|
|
pthread_mutex_destroy(&ctx->lock);
|
|
free(ctx);
|
|
ctx = NULL;
|
|
SUBTITLE_LOGI("end");
|
|
return CC_SOURCE_OK;
|
|
}
|
|
|
|
CcSourceStatus ccSourceManagerStart(CcSourceMgrHnd handle, CcSourceStartParam *param) {
|
|
SUBTITLE_LOGI_FUNC_IN();
|
|
CcSourceCtx *ctx = (CcSourceCtx *)handle;
|
|
if (ctx == NULL) {
|
|
SUBTITLE_LOGE("ctx is null, return!");
|
|
return CC_SOURCE_INVALID;
|
|
}
|
|
|
|
pthread_mutex_lock(&ctx->lock);
|
|
ctx->running = true;
|
|
pthread_mutex_unlock(&ctx->lock);
|
|
|
|
ctx->param = *param;
|
|
SUBTITLE_LOGI("srcType:%d", ctx->param.srcType);
|
|
|
|
// CC Digital Data Source strategy:
|
|
// Android/linux:
|
|
// ccdataserver exists:
|
|
// 1.if vdec is non-v4l, then read data from AMSTREAM_USDRDATA_DEV_FILE
|
|
// by ccdataserver
|
|
// 2.if vdec is v4l, then read data from MEDIA_PROXY_DEV_FILE by ccdataserver
|
|
// ccdataserver doesn't exist:
|
|
// 1.if vdec is non-v4l, then read data from AMSTREAM_USDRDATA_DEV_FILE
|
|
// 2.if vdec is v4l, then read data from MEDIA_PROXY_DEV_FILE
|
|
//
|
|
// CC Analog Data Source strategy
|
|
// Android/linux:
|
|
// ccdataserver exists:
|
|
// 1. get Line 21 data from /dev/vbi by ccdataserver
|
|
// ccdataserver doesn't exist:
|
|
// 1. get Line 21 data from /dev/vbi directly
|
|
//
|
|
// ccdataserver enable/disable by product config:
|
|
// if need ratings(xds) to enable ccdataserver otherwise disable.
|
|
switch (ctx->param.srcType) {
|
|
case CC_SOURCE_TYPE_AMSTREAM_DEVICE:
|
|
pthread_create(&ctx->thread, NULL, ccUserdataDeviceTask, ctx);
|
|
break;
|
|
case CC_SOURCE_TYPE_VBI_DEVICE:
|
|
pthread_create(&ctx->thread, NULL, ccVbiDeviceTask, ctx);
|
|
break;
|
|
case CC_SOURCE_TYPE_MEDIA_PROXY:
|
|
pthread_create(&ctx->thread, NULL, ccMediaProxyDeviceTask, ctx);
|
|
break;
|
|
case CC_SOURCE_TYPE_CCDATASERVER_MEDIA_PROXY:
|
|
#ifdef NEED_CCDATA_FROM_CCDATABASE
|
|
SUBTITLE_LOGI("dataserver_connect");
|
|
ctx->das_connect = (ds_conn_t * (*)()) dataserver_connect;
|
|
ctx->das = ctx->das_connect();
|
|
snprintf(ctx->devFile, sizeof(ctx->devFile), "%s", MEDIA_PROXY_DEV_FILE);
|
|
pthread_create(&ctx->thread, NULL, ccDataserverUserdataTask, ctx);
|
|
#endif
|
|
break;
|
|
case CC_SOURCE_TYPE_CCDATASERVER_AMSTREAM_DEVICE:
|
|
#ifdef NEED_CCDATA_FROM_CCDATABASE
|
|
SUBTITLE_LOGI("dataserver_connect");
|
|
ctx->das_connect = (ds_conn_t * (*)()) dataserver_connect;
|
|
ctx->das = ctx->das_connect();
|
|
snprintf(ctx->devFile, sizeof(ctx->devFile), "%s", AMSTREAM_USDRDATA_DEV_FILE);
|
|
pthread_create(&ctx->thread, NULL, ccDataserverUserdataTask, ctx);
|
|
#endif
|
|
break;
|
|
case CC_SOURCE_TYPE_CCDATASERVER_VBI_DEVICE:
|
|
#ifdef NEED_CCDATA_FROM_CCDATABASE
|
|
SUBTITLE_LOGI("dataserver_connect");
|
|
ctx->das_connect = (ds_conn_t * (*)()) dataserver_connect;
|
|
ctx->das = ctx->das_connect();
|
|
pthread_create(&ctx->thread, NULL, ccDataServerVbiTask, ctx);
|
|
#endif
|
|
break;
|
|
default:
|
|
pthread_create(&ctx->thread, NULL, ccUserdataDeviceTask, ctx);
|
|
break;
|
|
}
|
|
SUBTITLE_LOGI("end");
|
|
return CC_SOURCE_OK;
|
|
}
|