/* * Copyright (C) 2014-2026 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 "SubtitleService" #include "SubtitleService.h" #include #include #include "DataSourceFactory.h" #include "SocketServer.h" #include "SubtitleLog.h" SubtitleService::SubtitleService() { SUBTITLE_LOGI_FUNC_IN(); mIsInfoRetrieved = false; mStarted = false; mIsInfoRetrieved = false; mFmqReceiver = nullptr; mDumpMaps = 0; mSubtiles = nullptr; mDataSource = nullptr; mSockServ = SubSocketServer::GetInstance(); if (mSockServ) { if (!mSockServ->isServing()) { mSockServ->serve(); } else { SUBTITLE_LOGI("SubSocketServer is already be serving."); } } else { SUBTITLE_LOGE("Error! cannot start socket server!"); } std::this_thread::sleep_for(std::chrono::milliseconds(800)); } SubtitleService::~SubtitleService() { SUBTITLE_LOGI_FUNC_IN(); // android::CallStack here(LOG_TAG); if (mFmqReceiver != nullptr) { mFmqReceiver->unregisterClient(mDataSource); mFmqReceiver = nullptr; } } void SubtitleService::setupDumpRawFlags(int flagMap) { mDumpMaps = flagMap; } bool SubtitleService::startFmqReceiver(std::unique_ptr reader) { if (mFmqReceiver != nullptr) { SUBTITLE_LOGE("why still has a reference?"); mFmqReceiver = nullptr; } mFmqReceiver = std::make_unique(std::move(reader)); SUBTITLE_LOGI("start mFmqReceiver %p", mFmqReceiver.get()); if (mDataSource != nullptr) { mFmqReceiver->registClient(mDataSource); } return true; } bool SubtitleService::stopFmqReceiver() { if (mFmqReceiver == nullptr) { SUBTITLE_LOGE("mFmqReceiver was already stopped"); return false; } SUBTITLE_LOGI("stop mFmqReceiver %p", mFmqReceiver.get()); mFmqReceiver->unregisterClient(mDataSource); mFmqReceiver = nullptr; return true; } bool SubtitleService::startSubtitle(std::vector fds, int trackId, SubtitleIOType type, ParserEventNotifier *notifier) { SUBTITLE_LOGI("ioType=%d", type); std::unique_lock autolock(mLock); if (mStarted) { SUBTITLE_LOGI("Already started, exit"); return false; } mStarted = true; bool hasExtSub = fds.size() > 0; bool hasExtraFd = fds.size() > 1; std::shared_ptr subtitle( new Subtitle(hasExtSub, trackId, notifier, mSubParam.registeredOnDataCB)); std::shared_ptr datasource = DataSourceFactory::create(hasExtSub ? fds[0] : -1, hasExtraFd ? fds[1] : -1, type); if (nullptr == datasource) { SUBTITLE_LOGE("data Source is null"); return false; } SUBTITLE_LOGI("subType=%d, hasExtSub:%d, hasExtraFd:%d", mSubParam.subType, hasExtSub, hasExtraFd); mDataSource = datasource; if (TYPE_SUBTITLE_DVB == mSubParam.subType) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.dvbParam); } else if (TYPE_SUBTITLE_DVB_TELETEXT == mSubParam.subType) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.teletextParam); } else if (TYPE_SUBTITLE_DVB_TTML == mSubParam.subType) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.ttmlParam); } else if (TYPE_SUBTITLE_SCTE27 == mSubParam.subType) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.scte27Param); } else if (TYPE_SUBTITLE_ARIB_B24 == mSubParam.subType) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.arib24Param); } else if (TYPE_SUBTITLE_SMPTE_TTML == mSubParam.subType) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.smpteTtmlParam); } // Schedule subtitle subtitle->scheduleStart(); // subtitle.attach input source subtitle->attachDataSource(datasource, subtitle); if (!hasExtSub) { SUBTITLE_LOGI("setParameter when start"); subtitle->setParameter(&mSubParam); } mSubtiles = subtitle; mDataSource = datasource; if (mFmqReceiver != nullptr) { mFmqReceiver->registClient(mDataSource); } return true; } bool SubtitleService::resetForSeek() { if (mSubtiles != nullptr) { return mSubtiles->resetForSeek(); } return false; } bool SubtitleService::updateVideoPosAt(int timeMills) { static int test = 0; if (test++ % 100 == 0) { SUBTITLE_LOGI("%d(called %d times)", timeMills, test); } if (mSubtiles) { return mSubtiles->onMediaCurrentPresentationTime(timeMills); } return true; } // when play by ctc, this operation is before startSubtitle, // so keep struct mSub_param_t and when startSubtitle setParameter. void SubtitleService::setSubType(int type) { mSubParam.subType = (SubtitleType)type; } void SubtitleService::setDemuxId(int demuxId) { switch (mSubParam.subType) { case TYPE_SUBTITLE_DVB: mSubParam.dvbParam.demuxId = demuxId; break; case TYPE_SUBTITLE_DVB_TELETEXT: mSubParam.teletextParam.demuxId = demuxId; break; case TYPE_SUBTITLE_DVB_TTML: mSubParam.ttmlParam.demuxId = demuxId; break; case TYPE_SUBTITLE_SCTE27: mSubParam.scte27Param.demuxId = demuxId; break; case TYPE_SUBTITLE_ARIB_B24: mSubParam.arib24Param.demuxId = demuxId; break; case TYPE_SUBTITLE_SMPTE_TTML: mSubParam.smpteTtmlParam.demuxId = demuxId; break; default: break; } if (NULL == mDataSource) { return; } if (mSubParam.subType == TYPE_SUBTITLE_DVB) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.dvbParam); } else if (mSubParam.subType == TYPE_SUBTITLE_DVB_TELETEXT) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.teletextParam); } else if (mSubParam.subType == TYPE_SUBTITLE_DVB_TTML) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.ttmlParam); } else if (mSubParam.subType == TYPE_SUBTITLE_SCTE27) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.scte27Param); } else if (mSubParam.subType == TYPE_SUBTITLE_ARIB_B24) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.arib24Param); } else if (mSubParam.subType == TYPE_SUBTITLE_SMPTE_TTML) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.smpteTtmlParam); } } void SubtitleService::setSecureLevel(int flag) { switch (mSubParam.subType) { case TYPE_SUBTITLE_SCTE27: mSubParam.scte27Param.flag = flag; break; case TYPE_SUBTITLE_DVB: mSubParam.dvbParam.flag = flag; break; case TYPE_SUBTITLE_DVB_TELETEXT: mSubParam.teletextParam.flag = flag; break; case TYPE_SUBTITLE_ARIB_B24: mSubParam.arib24Param.flag = flag; break; case TYPE_SUBTITLE_DVB_TTML: mSubParam.ttmlParam.flag = flag; break; case TYPE_SUBTITLE_SMPTE_TTML: mSubParam.smpteTtmlParam.flag = flag; break; default: break; } if (NULL == mDataSource) { return; } if (mSubParam.subType == TYPE_SUBTITLE_DVB) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.dvbParam); } else if (mSubParam.subType == TYPE_SUBTITLE_DVB_TELETEXT) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.teletextParam); } else if (mSubParam.subType == TYPE_SUBTITLE_DVB_TTML) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.ttmlParam); } else if (mSubParam.subType == TYPE_SUBTITLE_SCTE27) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.scte27Param); } else if (mSubParam.subType == TYPE_SUBTITLE_ARIB_B24) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.arib24Param); } else if (mSubParam.subType == TYPE_SUBTITLE_SMPTE_TTML) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.smpteTtmlParam); } } void SubtitleService::setSubPid(int pid) { switch (mSubParam.subType) { case TYPE_SUBTITLE_SCTE27: mSubParam.scte27Param.SCTE27_PID = pid; break; case TYPE_SUBTITLE_DVB: mSubParam.dvbParam.pid = pid; break; case TYPE_SUBTITLE_DVB_TELETEXT: mSubParam.teletextParam.pid = pid; // for demux break; case TYPE_SUBTITLE_ARIB_B24: mSubParam.arib24Param.pid = pid; break; case TYPE_SUBTITLE_DVB_TTML: mSubParam.ttmlParam.pid = pid; break; case TYPE_SUBTITLE_SMPTE_TTML: mSubParam.smpteTtmlParam.pid = pid; break; default: break; } if (NULL == mDataSource) { return; } if (mSubParam.subType == TYPE_SUBTITLE_DVB) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.dvbParam); } else if (mSubParam.subType == TYPE_SUBTITLE_DVB_TELETEXT) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.teletextParam); } else if (mSubParam.subType == TYPE_SUBTITLE_DVB_TTML) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.ttmlParam); } else if (mSubParam.subType == TYPE_SUBTITLE_ARIB_B24) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.arib24Param); } else if (mSubParam.subType == TYPE_SUBTITLE_SMPTE_TTML) { mDataSource->updateParameter(mSubParam.subType, &mSubParam.smpteTtmlParam); } } void SubtitleService::setSubPageId(int pageId) { switch (mSubParam.subType) { case TYPE_SUBTITLE_DVB: mSubParam.dvbParam.compositionId = pageId; if (mSubtiles != nullptr) { mSubtiles->setParameter(&mSubParam); } break; case TYPE_SUBTITLE_DVB_TELETEXT: mSubParam.teletextParam.magazine = pageId; break; default: break; } } void SubtitleService::setSubAncPageId(int ancPageId) { switch (mSubParam.subType) { case TYPE_SUBTITLE_DVB: mSubParam.dvbParam.ancillaryId = ancPageId; if (mSubtiles != nullptr) { mSubtiles->setParameter(&mSubParam); } break; case TYPE_SUBTITLE_DVB_TELETEXT: mSubParam.teletextParam.pageNo = ancPageId; break; default: break; } } void SubtitleService::setChannelId(int channelId) { mSubParam.closedCaptionParam.ChannelID = channelId; } void SubtitleService::setClosedCaptionVfmt(int vfmt) { mSubParam.closedCaptionParam.vfmt = vfmt; } void SubtitleService::setClosedCaptionLang(const char *lang) { SUBTITLE_LOGI("lang=%s", lang); if (lang != nullptr && strlen(lang) < 64) { strcpy(mSubParam.closedCaptionParam.lang, lang); } } /* mode: 1 for the player id setting; 2 for the media id setting. */ void SubtitleService::setPipId(int mode, int id) { SUBTITLE_LOGI("mode = %s, id = %d\n", mode == PIP_PLAYER_ID ? "PIP_PLAYER" : "PIP_MEDIASYNC", id); bool same = true; if (PIP_PLAYER_ID == mode) { mSubParam.playerId = id; } else if (PIP_MEDIASYNC_ID == mode) { if (mSubParam.mediaId == id && id != PIP_INVALID_ID) { same = true; } else { same = false; } mSubParam.mediaId = id; } if (nullptr == mDataSource) { return; } mDataSource->setPipId(mode, id); if (mode == PIP_MEDIASYNC_ID && !same && mSubtiles != nullptr) { mSubtiles->setParameter(&mSubParam); } } int SubtitleService::getSubtitleStatus(int type, int cmd) { // DO NOT update the entire parameter. SUBTITLE_LOGI("type=%d cmd=%d", type, cmd); return mSubtiles->getSubtitleStatus(type, cmd); } bool SubtitleService::ttControl(int cmd, int magazine, int pageNo, int regionId, int param) { // DO NOT update the entire parameter. SUBTITLE_LOGI("cmd=%d, magazine=%d, pageNo=%d, region_id=%d, param=%d", cmd, magazine, pageNo, regionId, param); switch (cmd) { case TT_EVENT_GO_TO_PAGE: case TT_EVENT_GO_TO_SUBTITLE: mSubParam.teletextParam.magazine = magazine; mSubParam.teletextParam.pageNo = pageNo; // These two CMD are used in case of starting teletext, // a default region is set by user. mSubParam.teletextParam.regionId = regionId; break; case TT_EVENT_SET_REGION_ID: mSubParam.teletextParam.regionId = regionId; break; case TT_EVENT_SET_PAGE_NUMBER: mSubParam.teletextParam.pageNumber = pageNo; break; case TT_EVENT_SET_SUBPAGE_NUMBER: mSubParam.teletextParam.subPageNumber = param; break; } mSubParam.teletextParam.event = (TeletextEvent)cmd; mSubParam.subType = TYPE_SUBTITLE_DVB_TELETEXT; if (mSubtiles != nullptr) { return mSubtiles->setParameter(&mSubParam); } SUBTITLE_LOGI("nothing parameter set as null teletext parser"); return false; } int SubtitleService::ttGoHome() { SUBTITLE_LOGI_FUNC_IN(); return mSubtiles->setParameter(&mSubParam); } int SubtitleService::ttGotoPage(int pageNo, int subPageNo) { SUBTITLE_LOGI_FUNC_IN(); mSubParam.teletextParam.pageNo = pageNo; mSubParam.teletextParam.subPageNo = subPageNo; mSubParam.subType = TYPE_SUBTITLE_DVB_TELETEXT; return mSubtiles->setParameter(&mSubParam); } int SubtitleService::ttNextPage(int dir) { SUBTITLE_LOGI_FUNC_IN(); mSubParam.subType = TYPE_SUBTITLE_DVB_TELETEXT; mSubParam.teletextParam.pageDir = dir; return mSubtiles->setParameter(&mSubParam); } int SubtitleService::ttNextSubPage(int dir) { mSubParam.subType = TYPE_SUBTITLE_DVB_TELETEXT; mSubParam.teletextParam.subPageDir = dir; return mSubtiles->setParameter(&mSubParam); } // Deprecated: afd detect is deprecated through this interface. // AFD don't pass by subtitle now. // // TODO: This interface can be used to detect cc channel? bool SubtitleService::userDataOpen(ParserEventNotifier *notifier) { (void)notifier; SUBTITLE_LOGI("now deprecated!"); return true; } bool SubtitleService::userDataClose() { SUBTITLE_LOGI("now deprecated!"); return true; } bool SubtitleService::stopSubtitle() { SUBTITLE_LOGI_FUNC_IN(); std::unique_lock autolock(mLock); if (!mStarted) { SUBTITLE_LOGI("Already stopped, exit"); return false; } mStarted = false; if (mSubtiles != nullptr) { mSubtiles->dettachDataSource(mSubtiles); } if (mFmqReceiver != nullptr && mDataSource != nullptr) { mFmqReceiver->unregisterClient(mDataSource); } mDataSource = nullptr; mSubtiles = nullptr; mSubParam.reset(); SUBTITLE_LOGI_FUNC_OUT(); return true; } int SubtitleService::totalSubtitles() { if (mSubtiles == nullptr) { SUBTITLE_LOGE("Not ready or exited, ignore request!"); return -1; } // TODO: impl a state of subtitle, throw error when call in wrong state // currently we simply wait a while and return if (mIsInfoRetrieved) { return mSubtiles->getTotalSubtitles(); } for (int i = 0; i < 5; ++i) { if (mSubtiles != nullptr && mSubtiles->getTotalSubtitles() >= 0) { return mSubtiles->getTotalSubtitles(); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } mIsInfoRetrieved = true; return -1; } int SubtitleService::subtitleType() { // TODO: impl a state of subtitle, throw error when call in wrong state return mSubtiles->getSubtitleType(); } std::string SubtitleService::currentLanguage() { if (mSubtiles != nullptr) { return mSubtiles->currentLanguage(); } return std::string("N/A"); } void SubtitleService::setLanguage(std::string lang) { if (mSubtiles != nullptr) { mSubtiles->onGetLanguage(lang); } } void SubtitleService::setStartTimeStamp(int startTime) { if (mSubtiles != nullptr) { startTime = startTime * 90; mSubtiles->onRenderStartTimestamp(startTime); } } bool SubtitleService::show() { if (mSubtiles != nullptr) { return mSubtiles->show(); } return false; } bool SubtitleService::hide() { if (mSubtiles != nullptr) { return mSubtiles->hide(); } return false; } void SubtitleService::registerOnDataCB() { SUBTITLE_LOGI_FUNC_IN(); mSubParam.registeredOnDataCB = true; } void SubtitleService::dump(int fd) { dprintf(fd, "\n\n SubtitleService:\n"); dprintf( fd, "--------------------------------------------------------------------------------------\n"); dprintf(fd, "SubParams:\n"); mSubParam.dump(fd, " "); if (mFmqReceiver != nullptr) { dprintf(fd, "\nFastMessageQueue: %p\n", mFmqReceiver.get()); mFmqReceiver->dump(fd, " "); } dprintf(fd, "\nDataSource: %p\n", mDataSource.get()); if (mDataSource != nullptr) { mDataSource->dump(fd, " "); } if (mSubtiles != nullptr) { return mSubtiles->dump(fd); } } bool SubtitleService::setSubTranslationLanguage(const std::string &srcLang, const std::string &dstLang) { if (mSubtiles == nullptr) { SUBTITLE_LOGI("warning: subtitle is not ready, srcLang = %s, dstLang = %s", srcLang.empty() ? " " : srcLang.c_str(), dstLang.empty() ? " " : dstLang.c_str()); return false; } return mSubtiles->setSubTranslationLanguage(srcLang, dstLang); } bool SubtitleService::setDvbCountryCode(const std::string &iso3166CountryCode) { if (mSubtiles == nullptr) { SUBTITLE_LOGI("warning: subtitle is not ready, iso3166CountryCode = %s", iso3166CountryCode.empty() ? " " : iso3166CountryCode.c_str()); return false; } return mSubtiles->setDvbCountryCode(iso3166CountryCode); }