Files

596 lines
19 KiB
C++
Raw Permalink Normal View History

2026-08-13 11:28:40 +08:00
/*
* 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 <stdlib.h>
#include <utils/CallStack.h>
#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<FmqReader> reader) {
if (mFmqReceiver != nullptr) {
SUBTITLE_LOGE("why still has a reference?");
mFmqReceiver = nullptr;
}
mFmqReceiver = std::make_unique<FmqReceiver>(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<int> fds, int trackId, SubtitleIOType type,
ParserEventNotifier *notifier) {
SUBTITLE_LOGI("ioType=%d", type);
std::unique_lock<std::mutex> 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> subtitle(
new Subtitle(hasExtSub, trackId, notifier, mSubParam.registeredOnDataCB));
std::shared_ptr<DataSource> 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<std::mutex> 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);
}