/* * 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 "Subtitle" #include "Subtitle.h" #include "SubtitleLog.h" Subtitle::Subtitle(bool isExtSub, int trackId, ParserEventNotifier *notifier, bool registeredOnDataCB) { SUBTITLE_LOGI("isExtSub=%d, trackId=%d", isExtSub, trackId); mParserNotifier = notifier; mIsExtSub = isExtSub; mIdxSubTrack = trackId; mSubPrams = std::shared_ptr(new SubtitleParamType()); mSubPrams->dvbParam.demuxId = -1; mSubPrams->dvbParam.pid = -1; mSubPrams->dvbParam.ancillaryId = -1; mSubPrams->dvbParam.compositionId = -1; mSubPrams->arib24Param.demuxId = -1; mSubPrams->arib24Param.pid = -1; mSubPrams->arib24Param.languageCodeId = 0; mSubPrams->ttmlParam.demuxId = -1; mSubPrams->ttmlParam.pid = -1; mSubPrams->smpteTtmlParam.demuxId = 0; mSubPrams->smpteTtmlParam.pid = 0; mPresentation = std::make_shared(registeredOnDataCB); } Subtitle::~Subtitle() { SUBTITLE_LOGI_FUNC_IN(); { std::unique_lock autolock(mMutex); mExitRequested = true; } mCv.notify_all(); if (mThread != nullptr) { mThread->join(); mThread = nullptr; } if (mDataSource != nullptr) { mDataSource->stop(); mDataSource = nullptr; } if (mParser != nullptr) { mParser->stopParser(); mParser = nullptr; } if (mPresentation) { mPresentation->stopPresent(); mPresentation = nullptr; } SUBTITLE_LOGI_FUNC_OUT(); } void Subtitle::attachDataSource(std::shared_ptr source, std::shared_ptr listener) { SUBTITLE_LOGI_FUNC_IN(); mDataSource = source; mDataSource->registerInfoListener(listener); mDataSource->start(); } void Subtitle::dettachDataSource(std::shared_ptr listener) { SUBTITLE_LOGI_FUNC_IN(); if (mDataSource) { mDataSource->unregisteredInfoListener(listener); } // Added log here to track block issues as there is a lock in datasource SUBTITLE_LOGI_FUNC_OUT(); } void Subtitle::onSubtitleChanged(int newTotal) { if (newTotal == mSubtitleTracks) return; SUBTITLE_LOGI("newTotal=%d", newTotal); mSubtitleTracks = newTotal; } void Subtitle::onRenderStartTimestamp(int64_t startPts) { if (mPresentation) { mPresentation->notifyStartTimeStamp(startPts); } } void Subtitle::onRenderTimeChanged(int64_t renderTimePts) { // SUBTITLE_LOGI("onRenderTimeChanged: renderTime=%" PRId64, renderTime); mRenderTimePts = renderTimePts; if (mPresentation) { mPresentation->syncCurrentPresentTime(mRenderTimePts); } } void Subtitle::onGetLanguage(std::string &lang) { mCurrentLanguage = lang; } void Subtitle::onTypeChanged(int newType) { { std::unique_lock autolock(mMutex); if (newType == mCurrentSubtitleType) return; SUBTITLE_LOGI("newType=%d, oldType=%d", newType, mCurrentSubtitleType); if (newType <= TYPE_SUBTITLE_INVALID || newType >= TYPE_SUBTITLE_MAX) { SUBTITLE_LOGE("invalid type=%d", newType); return; } mCurrentSubtitleType = newType; mSubPrams->subType = static_cast(newType); mPendingAction = ACTION_SUBTITLE_RECEIVED_SUBTYPE; } mCv.notify_all(); } bool Subtitle::onMediaCurrentPresentationTime(int ptsMills) { unsigned int pts = (unsigned int)ptsMills; if (mPresentation) { mPresentation->syncCurrentPresentTime(pts); } return true; } int Subtitle::getTotalSubtitles() { return mSubtitleTracks; } int Subtitle::getSubtitleType() { return mCurrentSubtitleType; } int Subtitle::getSubtitleStatus(int type, int cmd) { int ret = -1; if (mParser) { ret = mParser->getSubtitleStatus(type, cmd); } return ret; } std::string Subtitle::currentLanguage() { return mCurrentLanguage; } bool Subtitle::setParameter(void *params) { { std::unique_lock autolock(mMutex); SubtitleParamType *p = new SubtitleParamType(); *p = *(static_cast(params)); mSubPrams = std::shared_ptr(p); if (mSubPrams->subType == TYPE_SUBTITLE_CLOSED_CAPTION && mSubPrams->mediaId < 0 && (mSubPrams->closedCaptionParam.ChannelID >> 8 == CLOSED_CAPTION_INPUT_USERDATA)) { SUBTITLE_LOGI("Warning: no media sync id for DTV cc, return"); return true; } // Process ttx skip page func. if (mSubPrams->subType == TYPE_SUBTITLE_DVB || mSubPrams->subType == TYPE_SUBTITLE_DVB_TELETEXT || mSubPrams->subType == TYPE_SUBTITLE_DVB_TTML || mSubPrams->subType == TYPE_SUBTITLE_CLOSED_CAPTION || mSubPrams->subType == TYPE_SUBTITLE_SCTE27 || mSubPrams->subType == TYPE_SUBTITLE_ARIB_B24 || mSubPrams->subType == TYPE_SUBTITLE_SMPTE_TTML) { mPendingAction = ACTION_SUBTITLE_SET_PARAM; mCv.notify_all(); return true; } mPendingAction = ACTION_SUBTITLE_RECEIVED_SUBTYPE; } mCv.notify_all(); return true; } bool Subtitle::show() { if (mPresentation) { return mPresentation->show(); } return false; } bool Subtitle::hide() { if (mPresentation) { return mPresentation->hide(); } return false; } bool Subtitle::setSubTranslationLanguage(const std::string &srcLang, const std::string &dstLang) { if (!mPresentation) { SUBTITLE_LOGE("presentation is not ready, srcLang = %s, dstLang = %s", srcLang.empty() ? " " : srcLang.c_str(), dstLang.empty() ? " " : dstLang.c_str()); return false; } mPresentation->setSubTranslationLanguage(srcLang, dstLang); return true; } bool Subtitle::setDvbCountryCode(const std::string &iso3166CountryCode) { { std::unique_lock autolock(mMutex); SUBTITLE_LOGI("change from %s to %s", mDvbCountryCode.empty() ? "NA" : mDvbCountryCode.c_str(), iso3166CountryCode.empty() ? "NA" : iso3166CountryCode.c_str()); if (iso3166CountryCode.empty()) { return false; } mDvbCountryCode = iso3166CountryCode; if (mParser == nullptr) { SUBTITLE_LOGI("parser is no ready"); return false; } if (mParser->getParseType() != TYPE_SUBTITLE_DVB_TELETEXT) { SUBTITLE_LOGE("unexpected call"); return false; } mPendingAction = ACTION_SUBTITLE_SET_DVB_COUNTRY_CODE; } mCv.notify_all(); return true; } void Subtitle::dump(int fd) { dprintf(fd, "\n\n Subtitle:\n"); dprintf( fd, "--------------------------------------------------------------------------------------\n"); dprintf(fd, "isExitedRequested? %s\n", mExitRequested ? "Yes" : "No"); dprintf(fd, "PendingAction: %d\n", mPendingAction); dprintf(fd, "\n"); dprintf(fd, "DataSource : %p\n", mDataSource.get()); dprintf(fd, "Presentation: %p\n", mPresentation.get()); dprintf(fd, "Parser : %p\n", mParser.get()); dprintf(fd, "\n"); dprintf(fd, "Total Subtitle Tracks: %d\n", mSubtitleTracks); dprintf(fd, "Current Subtitle type: %d\n", mCurrentSubtitleType); dprintf(fd, "Current Video PTS : %" PRId64 "\n", mRenderTimePts); if (mSubPrams) { dprintf(fd, "\nSubParams:\n"); mSubPrams->dump(fd, " "); } if (mPresentation) { dprintf(fd, "\nPresentation: %p\n", mPresentation.get()); mPresentation->dump(fd, " "); } if (mParser) { dprintf(fd, "\nParser: %p\n", mParser.get()); mParser->dump(fd, " "); } } bool Subtitle::resetForSeek() { { std::unique_lock autolock(mMutex); mPendingAction = ACTION_SUBTITLE_RESET_FOR_SEEK; } mCv.notify_all(); return true; } void Subtitle::scheduleStart() { SUBTITLE_LOGI("subType=%d", mSubPrams->subType); if (mDataSource) { mDataSource->start(); } mThread = std::shared_ptr(new std::thread(&Subtitle::run, this)); } // Run in a new thread. any access to this object's member field need protected by lock void Subtitle::run() { SUBTITLE_LOGI("start: mExitRequested=%d, subType=%d, mIsExtSub=%d", mExitRequested, mSubPrams->subType, mIsExtSub); std::unique_lock autolock(mMutex); bool ret = false; while (!mExitRequested) { mCv.wait_for(autolock, std::chrono::milliseconds(100)); if (mExitRequested) { SUBTITLE_LOGI("exit thread"); break; } if (mIsExtSub) { if (mParser == nullptr) { mSubPrams->subType = TYPE_SUBTITLE_EXTERNAL; mSubPrams->idxSubTrackId = mIdxSubTrack; mParser = ParserFactory::create(mSubPrams, mDataSource); if (mParser == nullptr) { SUBTITLE_LOGE("Fail to create parser"); break; } mParser->startParser(mParserNotifier, mPresentation.get()); if (mPresentation) { ret = mPresentation->startPresent(mParser); } mPendingAction = -1; } else if (mParser->getParseType() == TYPE_SUBTITLE_CLOSED_CAPTION) { /* * When ccparser is created by default, * it can be changed to the correct parser type * when specifying the parser type subsequently */ mParser->stopParser(); if (mPresentation) { mPresentation->stopPresent(); } mParser = nullptr; mSubPrams->subType = TYPE_SUBTITLE_EXTERNAL; mSubPrams->idxSubTrackId = mIdxSubTrack; mParser = ParserFactory::create(mSubPrams, mDataSource); /* * This is correct, create parser and judge if null, * ignore coverity check_after_deref */ /* coverity[check_after_deref:SUPPRESS] */ if (mParser == nullptr) { SUBTITLE_LOGE("Fail to create parser"); break; } mParser->startParser(mParserNotifier, mPresentation.get()); if (mPresentation) { mPresentation->startPresent(mParser); } mPendingAction = -1; } } switch (mPendingAction) { case ACTION_SUBTITLE_SET_PARAM: { SUBTITLE_LOGI("ACTION_SUBTITLE_SET_PARAM, ccType = %d, currentSubType = %d", TYPE_SUBTITLE_CLOSED_CAPTION, mSubPrams->subType); bool createAndStart = false; if (mParser == nullptr) { // when the first time start subtitle, some parameter may affect the behavior // such as cached teletext. we use tsid/onid/pid to check need use cached ttx or // not createAndStart = true; mParser = ParserFactory::create(mSubPrams, mDataSource); if (mParser == nullptr) { SUBTITLE_LOGE("Fail to create parser"); break; } } if (mSubPrams->subType == TYPE_SUBTITLE_DVB) { mParser->updateParameter(TYPE_SUBTITLE_DVB, &mSubPrams->dvbParam); } else if (mSubPrams->subType == TYPE_SUBTITLE_DVB_TELETEXT) { mParser->updateParameter(TYPE_SUBTITLE_DVB_TELETEXT, &mSubPrams->teletextParam); if (!mDvbCountryCode.empty() && !mParser->isDvbCountryCodeSet()) { mParser->setDvbCountryCode(mDvbCountryCode); } } else if (mSubPrams->subType == TYPE_SUBTITLE_DVB_TTML) { mParser->updateParameter(TYPE_SUBTITLE_DVB_TTML, &mSubPrams->ttmlParam); } else if (mSubPrams->subType == TYPE_SUBTITLE_ARIB_B24) { mParser->updateParameter(TYPE_SUBTITLE_ARIB_B24, &mSubPrams->arib24Param); } else if (mSubPrams->subType == TYPE_SUBTITLE_SCTE27) { mParser->updateParameter(TYPE_SUBTITLE_SCTE27, &mSubPrams->scte27Param); } else if (mSubPrams->subType == TYPE_SUBTITLE_CLOSED_CAPTION) { mParser->updateParameter(TYPE_SUBTITLE_CLOSED_CAPTION, &mSubPrams->closedCaptionParam); } else if (mSubPrams->subType == TYPE_SUBTITLE_SMPTE_TTML) { mParser->updateParameter(TYPE_SUBTITLE_SMPTE_TTML, &mSubPrams->smpteTtmlParam); } if (createAndStart) { mParser->startParser(mParserNotifier, mPresentation.get()); if (mParser->getParseType() == TYPE_SUBTITLE_DVB_TELETEXT && !mDvbCountryCode.empty()) { mParser->setDvbCountryCode(mDvbCountryCode); } if (mPresentation) { ret = mPresentation->startPresent(mParser); } } } break; case ACTION_SUBTITLE_RECEIVED_SUBTYPE: { SUBTITLE_LOGI("ACTION_SUBTITLE_RECEIVED_SUBTYPE, newType = %d", mSubPrams->subType); if (mParser) { SUBTITLE_LOGI("Stop legacy parser and presentation"); mParser->stopParser(); if (mPresentation) { mPresentation->stopPresent(); } mParser = nullptr; } mParser = ParserFactory::create(mSubPrams, mDataSource); if (mParser == nullptr) { SUBTITLE_LOGE("Fail to create parser"); break; } mParser->startParser(mParserNotifier, mPresentation.get()); if (mPresentation) { ret = mPresentation->startPresent(mParser); } if (mSubPrams->subType == TYPE_SUBTITLE_DVB) { mParser->updateParameter(TYPE_SUBTITLE_DVB, &mSubPrams->dvbParam); } else if (mSubPrams->subType == TYPE_SUBTITLE_DVB_TELETEXT) { mParser->updateParameter(TYPE_SUBTITLE_DVB_TELETEXT, &mSubPrams->teletextParam); } else if (mSubPrams->subType == TYPE_SUBTITLE_DVB_TTML) { mParser->updateParameter(TYPE_SUBTITLE_DVB_TTML, &mSubPrams->ttmlParam); } else if (mSubPrams->subType == TYPE_SUBTITLE_CLOSED_CAPTION) { mParser->updateParameter(TYPE_SUBTITLE_CLOSED_CAPTION, &mSubPrams->closedCaptionParam); } else if (mSubPrams->subType == TYPE_SUBTITLE_SCTE27) { mParser->updateParameter(TYPE_SUBTITLE_SCTE27, &mSubPrams->scte27Param); } else if (mSubPrams->subType == TYPE_SUBTITLE_ARIB_B24) { mParser->updateParameter(TYPE_SUBTITLE_ARIB_B24, &mSubPrams->arib24Param); } else if (mSubPrams->subType == TYPE_SUBTITLE_SMPTE_TTML) { mParser->updateParameter(TYPE_SUBTITLE_SMPTE_TTML, &mSubPrams->smpteTtmlParam); } } break; case ACTION_SUBTITLE_RESET_MEDIASYNC: if (mParser) { mParser->setPipId(PIP_MEDIASYNC_ID, mSubPrams->mediaId); } break; case ACTION_SUBTITLE_RESET_FOR_SEEK: if (mParser) { mParser->resetForSeek(); } if (mPresentation) { mPresentation->resetForSeek(); } break; case ACTION_SUBTITLE_SET_DVB_COUNTRY_CODE: assert(!mDvbCountryCode.empty()); if (mParser) { SUBTITLE_LOGI("ACTION_SUBTITLE_SET_DVB_COUNTRY_CODE, type=%d, country_code=%s", mParser->getParseType(), mDvbCountryCode.c_str()); assert(mParser->getParseType() == TYPE_SUBTITLE_DVB_TELETEXT); mParser->setDvbCountryCode(mDvbCountryCode); } else { SUBTITLE_LOGE("parser is not ready"); } break; default: break; } mPendingAction = -1; if (mParser) continue; // Still no parser, try start default CC int value = mSubPrams->closedCaptionParam.ChannelID >> 8 == 1 ? 1 : (mSubPrams->playerId > 0 ? 1 : 0); if (value) { SUBTITLE_LOGI("Try creating default CC"); mParser = ParserFactory::create(mSubPrams, mDataSource); if (mParser == nullptr) { SUBTITLE_LOGE("Fail to create detault CC"); // Break the thread break; } mParser->startParser(mParserNotifier, mPresentation.get()); if (mPresentation) { ret = mPresentation->startPresent(mParser); } } } SUBTITLE_LOGI("exit: mExitRequested=%d, subType=%d", mExitRequested, mSubPrams->subType); }