/* * 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 #include #include #include #include #include #include #include #include #include #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; }