223 lines
8.8 KiB
C++
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;
|
|
};
|