/* * 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. */ #pragma once #include #include #include #include #include #include #include "InfoChangeListener.h" #include "StreamUtils.h" #include "SubtitleLog.h" // The data's sync words, which identify the header of the data come from. static constexpr int AML_PARSER_SYNC_WORD_PES = 0x000001bd; static constexpr int AML_PARSER_SYNC_WORD = 0x414D4C55; //'AMLU' /* 0x414D4C55 + 0x41 = Internal use for vbi data source. 0x414D4C55 + 0x77 = The packaged subtitle data from amnuplayer. 0x414D4C55 + 0xaa = The PES subtitle data from GStreamer. 0x414D4C55 + 0xab = The video width, video height and extra info from amnuplayer. */ static constexpr int SYNC_WORD_INNER_VBI_SOURCE = 0x41; static constexpr int SYNC_WORD_EXT_AMNUPLAYER_SUBTITLE = 0x77; static constexpr int SYNC_WORD_EXT_GSTREAMER_SUBTITLE = 0xaa; static constexpr int SYNC_WORD_EXT_AMNUPLAYER_EXTRA_INFO = 0xab; typedef enum { E_SUBTITLE_FMQ = 0, E_SUBTITLE_DEV, E_SUBTITLE_FILE, E_SUBTITLE_SOCK, // deprecated E_SUBTITLE_DEMUX, E_SUBTITLE_VBI, E_SUBTITLE_USERDATA, } SubtitleIOType; typedef enum { E_SOURCE_INV, E_SOURCE_STARTED, E_SOURCE_STOPPED, } SubtitleIOState; class DataListener { public: virtual int onData(const char* buffer, int len) = 0; virtual ~DataListener() = default; }; class DataSource : public DataListener { public: DataSource() = default; DataSource& operator=(const DataSource&) = delete; virtual ~DataSource() = default; virtual SubtitleIOType type() = 0; virtual bool start() = 0; virtual bool stop() = 0; virtual size_t availableDataSize() = 0; virtual size_t read(void* buffer, size_t bufSize) = 0; virtual size_t lseek(int offSet, int whence) = 0; virtual void updateParameter(int /*type*/, void* /*data*/) {} virtual void setPipId(int /*mode*/, int /*id*/) {} virtual bool isFileAvailable() { return false; } virtual int64_t getSyncTime() { return mSyncPts; } // If we got information from the data source, we notify these info to listener void registerInfoListener(std::shared_ptr listener) { std::unique_lock autolock(mLock); mInfoListeners.push_back(listener); } void unregisteredInfoListener(std::shared_ptr listener) { std::unique_lock autolock(mLock); for (auto it = mInfoListeners.begin(); it != mInfoListeners.end();) { if ((*it).expired()) { // erase() return to the next iterator of it, so don't need ++it it = mInfoListeners.erase(it); continue; } auto lsn = (*it).lock(); if (lsn && lsn.get() == listener.get()) { // erase() return to the next iterator of it, so don't need ++it it = mInfoListeners.erase(it); // Keep erasing until to std::list::end() as list can contain duplicated value } else { ++it; } } } virtual void dump(int fd, const char* prefix) = 0; // Only for Demuxer data sourcing subtitles. virtual void updateNewestAVPts(int64_t /*subtitlePts*/, int64_t /*syncedPts*/) {} // For ptsAdjustmentDiffMs: // Presentation time need to be adjusted if different time between subtitle Pts and // the newest video pts is more than 7 seconds, the shreshold is 2 seconds for ISDB-T. // // See confluence page for detail: // https://confluence.amlogic.com/pages/viewpage.action?pageId=492136430 // // See CL https://scgit.amlogic.com/#/c/522964, the original value was adjusted from // 2s to 7s for DTG verification. void checkParsedPts(int64_t subtitlePts, int ptsAdjustmentDiffMs = 7000) { // The cases need to check subtitle PTS with the newest video PTS: // 1. The first time of subtitle PES data come. // 2. subtitle PTS has a big jump with previous subtitle PTS, use 5 seconds as the // shreshold. // 3. subtitle PTS has a big gap with media sync's PTS, keep the same 7 seconds shreshold // value as MM side. // 4. For wrapped stream case, the render start timestamp should be resumed. // // Note: For TTML subtitle stream rewind scenarios, video PTS may not update in time, // leading to this logic being triggered 100% of the time. To ensure display correctness, // TTMLParser does NOT use this mechanism. bool needCheck = false; if (mNewestSubtitlePesPts == 0 || std::abs(pts2Ms(subtitlePts - mNewestSubtitlePesPts)) >= 5000 || std::abs(pts2Ms(subtitlePts - mSyncPts)) >= 7000 || mWasStreamWrapped) { updateNewestAVPts(subtitlePts, mSyncPts); needCheck = true; mWasStreamWrapped = std::abs(pts2Ms(subtitlePts - mSyncPts)) >= 7000; } mNewestSubtitlePesPts = subtitlePts; // Stop adjust the start timestamp in the cases: // 1. Some old hardware can't get the newest video pts, for example T5D; // 2. Subtitle PTS may be zero in some private TS or wrong TS streams, presentation // will handle it with a common solution. if (subtitlePts == 0 || mNewestDemuxAVPts == 0 || !needCheck) { return; } // Note: don't use mNewestDemuxAVPts-subtitlePts. // Subtitle is delay if subtitlePts > mNewestDemuxAVPts, otherwise, subtitle is fast. int64_t diffPts = subtitlePts - mNewestDemuxAVPts; int diffPtsMs = pts2Ms(diffPts); if (std::abs(diffPtsMs) >= ptsAdjustmentDiffMs) { SUBTITLE_LOGI( "[newest_avpts_tag] adjust presentation_time since 'diffPts(%d)ms" " = subtitlePts(%" PRId64 ")-mNewestDemuxAVPts(%" PRId64 ")' is more than %d ms, subtitlePts-mSyncPts= %" PRId64 " ms", diffPtsMs, pts2Ms(subtitlePts), pts2Ms(mNewestDemuxAVPts), ptsAdjustmentDiffMs, pts2Ms(subtitlePts - mSyncPts)); mPreviousAdjustedStartTime = diffPts; std::unique_lock autolock(mLock); for (auto& it : mInfoListeners) { auto lsn = it.lock(); if (lsn) lsn->onRenderStartTimestamp(mPreviousAdjustedStartTime); } } else if (mPreviousAdjustedStartTime != 0) { // Resume the start timestamp in the wrapped stream playback case. std::unique_lock autolock(mLock); for (auto& it : mInfoListeners) { auto lsn = it.lock(); if (lsn) lsn->onRenderStartTimestamp(0); } mPreviousAdjustedStartTime = 0; } } // Used for SUB_IDX subtitle, which has one index file and sub data file int getExtraFd() { return mExtraFd; } std::list> mInfoListeners; // Only for Demuxer data sourcing subtitles. // The newest audio and video PTS filtered from demuxer, which can be thought as // the same time point as subtitle PES PTS, this newest Audio/video PTS will be used // to check and adjust subtitle's render time. int64_t mNewestDemuxAVPts = 0; int64_t mNewestSubtitlePesPts = 0; bool mWasStreamWrapped = false; // The render vpts time comes from media sync, which is used by presentation. int64_t mSyncPts = 0; // Cached the adjusted render start timestamp which was triggered. int64_t mPreviousAdjustedStartTime = 0; int mDumpFd = -1; protected: std::mutex mLock; int mPlayerId = -1; int mMediaSyncId = -1; int mExtraFd = -1; };