Files
BPI-CM9-Linux/aml-comp/vendor/amlogic/aml_subtitleserver/libsubtitle/io/DataSource.h
T
2026-08-24 10:03:24 +08:00

223 lines
8.8 KiB
C++

/*
* 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 <inttypes.h>
#include <cstdlib>
#include <list>
#include <memory>
#include <mutex>
#include <cstdlib>
#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<InfoChangeListener> listener) {
std::unique_lock<std::mutex> autolock(mLock);
mInfoListeners.push_back(listener);
}
void unregisteredInfoListener(std::shared_ptr<InfoChangeListener> listener) {
std::unique_lock<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::weak_ptr<InfoChangeListener>> 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;
};