Files

618 lines
22 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.
*/
#define LOG_TAG "DemuxSource"
#include "DemuxSource.h"
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <utils/CallStack.h>
#include <string>
#include "ParserFactory.h"
#include "SubtitleLog.h"
extern "C" {
#include "MediaSyncInterface.h"
mediasync_result MediaSync_bindInstance(void *handle, uint32_t SyncInsId,
sync_stream_type streamtype);
mediasync_result MediaSync_getTrackMediaTime(void *handle, int64_t *outMediaUs);
mediasync_result MediaSync_getFirstVideoFrameInfo(void *handle, mediasync_frameinfo *arg);
}
// Lagecy tsync is only used in:
// - Old MM demux architecture before Android T
// - New MM demux architecture before Android R
// tsync/pts_video returns 32bit 90kHz Pts.
// tsync/pts_video_u64 returns 33bit 90kHz Pts.
static const std::string SYSFS_VIDEO_PTS = "/sys/class/tsync/pts_video_u64";
static const std::string SYSFS_VIDEO_FIRSTRRAME =
"/sys/module/amvideo/parameters/first_frame_toggled";
static const std::string SYSFS_SUBTITLE_TYPE = "/sys/class/subtitle/subtype";
static const std::string SYSFS_SUBTITLE_SIZE = "/sys/class/subtitle/size";
static const std::string SYSFS_SUBTITLE_TOTAL = "/sys/class/subtitle/total";
static const std::string SUBTITLE_READ_DEVICE = "/dev/amstream_sub_read";
#define AMSTREAM_IOC_MAGIC 'S'
#define AMSTREAM_IOC_SUB_LENGTH _IOR(AMSTREAM_IOC_MAGIC, 0x1b, int)
#define AMSTREAM_IOC_SUB_RESET _IOW(AMSTREAM_IOC_MAGIC, 0x1a, int)
#define SECTION_DEMUX_INDEX 2
#define DMX_SUBTITLE_NO_SEC 0
#define DMX_SUBTITLE_SEC (2 << 10)
#ifdef USE_FB
#define DMX_SUBTITLE_BUFFER_SIZE 0x20000
#else
#define DMX_SUBTITLE_BUFFER_SIZE 0x80000
#endif
DemuxSource *DemuxSource::sInstance = nullptr;
DemuxSource *DemuxSource::getCurrentInstance() {
return DemuxSource::sInstance;
}
namespace {
static inline int64_t sysfsReadInt(const char *path, int base) {
int fd;
int64_t val = 0;
char bcmd[64] = {0};
fd = ::open(path, O_RDONLY);
if (fd >= 0) {
int c = read(fd, bcmd, sizeof(bcmd));
if (c > 0) {
val = strtoll(bcmd, NULL, base);
}
::close(fd);
} else {
SUBTITLE_LOGE("unable to open file %s,err: %s", path, strerror(errno));
}
return val;
}
static void pes_data_cb(int dev_no, int fhandle, const uint8_t *data, int len, void *user_data) {
(void)dev_no;
(void)fhandle;
(void)user_data;
std::shared_ptr<char> spBuf(new char[len], [](char *buf) { delete[] buf; });
memcpy(spBuf.get(), data, len);
DemuxSource::getCurrentInstance()->mSegment->push(std::move(spBuf), len);
DemuxSource::getCurrentInstance()->onPesCallback();
// coverity[RESOURCE_LEAK]
if (isSubtitleDumpAvailable()) {
if (DemuxSource::getCurrentInstance()->mDumpFd == -1) {
DemuxSource::getCurrentInstance()->mDumpFd =
::open(DUMP_DATA_FILE_PATH, O_RDWR | O_CREAT, 0666);
if (DemuxSource::getCurrentInstance()->mDumpFd < 0) {
SUBTITLE_LOGI("mDumpFd=%d %m", DemuxSource::getCurrentInstance()->mDumpFd);
return;
}
}
auto ret = write(DemuxSource::getCurrentInstance()->mDumpFd, data, len);
if (ret != len) {
SUBTITLE_LOGE("Failed to write data: ret=%zd, %m", ret);
}
}
}
static int open_dvb_dmx(TVSubtitleData *data, int dmx_id, int pid, int flag) {
SUBTITLE_LOGI_FUNC_IN();
int ret;
AmlogicDemuxOpenParameterType op;
struct dmx_pes_filter_params pesp;
struct dmx_sct_filter_params param;
int subtype = DemuxSource::getCurrentInstance()->mSubType;
SUBTITLE_LOGI("dmx_id=%d, pid=%d, flag=%d, subtype=%d", dmx_id, pid, flag, subtype);
data->dmx_id = -1;
data->filter_handle = -1;
memset(&op, 0, sizeof(op));
data->dmx_id = dmx_id;
if (data->dmx_id == -1) {
SUBTITLE_LOGE("invalid argument, data->dmx_id is -1");
return -1;
}
ret = DemuxOpen(dmx_id, &op);
if (ret != AM_SUCCESS) goto error;
ret = DemuxAllocateFilter(dmx_id, &data->filter_handle);
if (ret != AM_SUCCESS) goto error;
SUBTITLE_LOGI("data->filter_handle:%d", data->filter_handle);
ret = DemuxSetBufferSize(dmx_id, data->filter_handle, DMX_SUBTITLE_BUFFER_SIZE);
if (ret != AM_SUCCESS) goto error;
memset(&pesp, 0, sizeof(pesp));
pesp.pid = pid;
pesp.output = DMX_OUT_TAP;
if (flag) {
pesp.flags = DMX_SUBTITLE_SEC;
} else {
pesp.flags = DMX_SUBTITLE_NO_SEC;
}
if (TYPE_SUBTITLE_DVB == subtype) {
SUBTITLE_LOGI("use PES filter subtype=%d", subtype);
pesp.pes_type = DMX_PES_SUBTITLE;
pesp.input = DMX_IN_FRONTEND;
ret = DemuxSetPesFilter(dmx_id, data->filter_handle, &pesp);
if (ret != AM_SUCCESS) goto error;
} else if (TYPE_SUBTITLE_DVB_TELETEXT == subtype) {
SUBTITLE_LOGI("use PES filter subtype=%d", subtype);
pesp.pes_type = DMX_PES_SUBTITLE; // DMX_PES_TELETEXT;
pesp.input = DMX_IN_FRONTEND;
ret = DemuxSetPesFilter(dmx_id, data->filter_handle, &pesp);
if (ret != AM_SUCCESS) goto error;
} else if (TYPE_SUBTITLE_SCTE27 == subtype) {
// the scte27 data is section
SUBTITLE_LOGI("use section filter subtype=%d", subtype);
memset(&param, 0, sizeof(param));
param.pid = pid;
param.filter.filter[0] = SCTE27_TID;
param.filter.mask[0] = 0xff;
param.flags = DMX_CHECK_CRC;
ret = DemuxSetSecFilter(dmx_id, data->filter_handle, &param);
if (ret != AM_SUCCESS) goto error;
} else if (TYPE_SUBTITLE_ARIB_B24 == subtype) {
// the arib data is section
SUBTITLE_LOGI("use PES filter subtype=%d", subtype);
pesp.pes_type = DMX_PES_SUBTITLE;
pesp.input = DMX_IN_FRONTEND;
ret = DemuxSetPesFilter(dmx_id, data->filter_handle, &pesp);
if (ret != AM_SUCCESS) goto error;
} else if (TYPE_SUBTITLE_DVB_TTML == subtype) {
// the ttml data is section
SUBTITLE_LOGI("use PES filter subtype=%d", subtype);
pesp.pes_type = DMX_PES_SUBTITLE;
pesp.input = DMX_IN_FRONTEND;
ret = DemuxSetPesFilter(dmx_id, data->filter_handle, &pesp);
if (ret != AM_SUCCESS) goto error;
}
ret = DemuxSetCallback(dmx_id, data->filter_handle, pes_data_cb, data);
if (ret != AM_SUCCESS) goto error;
ret = DemuxStartFilter(dmx_id, data->filter_handle);
if (ret != AM_SUCCESS) goto error;
SUBTITLE_LOGI_FUNC_OUT();
return 0;
error:
SUBTITLE_LOGE("ERROR !!!!!!!!!!!!!!!!!!!!!!!!!!!!");
if (data->filter_handle != -1) {
DemuxFreeFilter(dmx_id, data->filter_handle);
}
if (data->dmx_id != -1) {
DemuxClose(dmx_id);
}
SUBTITLE_LOGI_FUNC_OUT();
return -1;
}
static void close_dvb_dmx(TVSubtitleData *data, int dmx_id) {
SUBTITLE_LOGI_FUNC_IN();
if (data == nullptr) {
SUBTITLE_LOGE("data is nullptr");
return;
}
if (data->filter_handle != -1) {
DemuxStopFilter(dmx_id, data->filter_handle);
DemuxSetCallback(dmx_id, data->filter_handle, NULL, NULL);
DemuxFreeFilter(dmx_id, data->filter_handle);
data->filter_handle = -1;
}
if (dmx_id != -1) {
DemuxClose(dmx_id);
}
data->dmx_id = -1;
SUBTITLE_LOGI_FUNC_OUT();
}
} // namespace
DemuxSource::DemuxSource() {
sInstance = this;
mMediaSync = MediaSync_create();
SUBTITLE_LOGI("subtitle mediasync created");
}
DemuxSource::~DemuxSource() {
SUBTITLE_LOGI_FUNC_IN();
mExitRequested = true;
if (mRenderTimeThread != nullptr) {
mRenderTimeThread->join();
mRenderTimeThread = nullptr;
}
if (mMediaSync != nullptr) {
MediaSync_destroy(mMediaSync);
mMediaSync = nullptr;
}
mPlayerId = -1;
mMediaSyncId = -1;
mMediaSyncDestroyFlag = false;
if (mDemuxContext != nullptr) {
close_dvb_dmx(mDemuxContext, mDemuxId);
delete mDemuxContext;
mDemuxContext = nullptr;
}
if (mDumpFd > 0) {
::close(mDumpFd);
mDumpFd = -1;
}
mSegment = nullptr;
SUBTITLE_LOGI_FUNC_OUT();
}
bool DemuxSource::notifyInfoChange() {
std::unique_lock<std::mutex> autolock(mLock);
for (auto it = mInfoListeners.begin(); it != mInfoListeners.end(); it++) {
auto wk_listener = (*it);
if (auto lstn = wk_listener.lock()) {
lstn->onTypeChanged(mSubType);
return true;
}
}
return false;
}
void DemuxSource::loopRenderTime() {
while (!mExitRequested) {
mLock.lock();
for (auto it = mInfoListeners.begin(); it != mInfoListeners.end();) {
// Using weak_ Ptr. lock() to obtain shared_ Ptr, ensuring that null pointer exceptions
// are not caused by expired listeners during processing
auto wk_listener = it->lock();
if (!wk_listener) {
SUBTITLE_LOGI("Listener is null or expired.");
// Use erase to remove expired weeks_ PTR
it = mInfoListeners.erase(it);
continue;
}
int64_t value = 0;
if (-1 == mMediaSyncId) {
mSyncPts = sysfsReadInt(SYSFS_VIDEO_PTS.c_str(), 16);
} else if (mMediaSync != nullptr && !mMediaSyncDestroyFlag) {
MediaSync_getTrackMediaTime(mMediaSync, &value);
mSyncPts = us2Pts(value);
}
static int i = 0;
if (i++ % 300 == 0) {
SUBTITLE_LOGE("read pts: %" PRId64 ", mMediaSyncId: %d", mSyncPts, mMediaSyncId);
}
if (!mExitRequested && mSyncPts > 0) {
wk_listener->onRenderTimeChanged(mSyncPts);
}
++it;
}
mLock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(16));
}
}
SubtitleIOType DemuxSource::type() {
return E_SUBTITLE_DEMUX;
}
bool DemuxSource::start() {
if (E_SOURCE_STARTED == mState) {
SUBTITLE_LOGE("already stated");
return false;
}
SUBTITLE_LOGI("DemuxSource start mPid:%d mSecureLevelFlag:%d", mPid, mSecureLevelFlag);
mState = E_SOURCE_STARTED;
mSegment = std::shared_ptr<BufferSegment>(new BufferSegment());
mDemuxContext = new TVSubtitleData();
mRenderTimeThread =
std::shared_ptr<std::thread>(new std::thread(&DemuxSource::loopRenderTime, this));
int ret = open_dvb_dmx(mDemuxContext, mDemuxId, mPid, mSecureLevelFlag);
if (ret == -1) {
return false;
}
notifyInfoChange();
return true;
}
void DemuxSource::updateParameter(int type, void *data) {
SUBTITLE_LOGI("type = %d ", type);
bool restartDemux = false;
if (TYPE_SUBTITLE_DVB == type) {
DvbParam *pDvbParam = (DvbParam *)data;
if ((mState == E_SOURCE_STARTED) && (mPid != pDvbParam->pid)) {
restartDemux = true;
}
mDemuxId = pDvbParam->demuxId;
mPid = pDvbParam->pid;
mSecureLevelFlag = pDvbParam->flag;
mParam1 = pDvbParam->compositionId;
mParam2 = pDvbParam->ancillaryId;
} else if (TYPE_SUBTITLE_DVB_TELETEXT == type) {
TeletextParam *pTeletextParam = (TeletextParam *)data;
if ((mState == E_SOURCE_STARTED) && (mPid != pTeletextParam->pid)) {
restartDemux = true;
}
mDemuxId = pTeletextParam->demuxId;
mPid = pTeletextParam->pid;
mSecureLevelFlag = pTeletextParam->flag;
mParam1 = pTeletextParam->magazine;
mParam2 = pTeletextParam->pageNo;
} else if (TYPE_SUBTITLE_SCTE27 == type) {
Scte27Param *pScte27Param = (Scte27Param *)data;
if ((mState == E_SOURCE_STARTED) && (mPid != pScte27Param->SCTE27_PID)) {
restartDemux = true;
}
mDemuxId = pScte27Param->demuxId;
mPid = pScte27Param->SCTE27_PID;
mSecureLevelFlag = pScte27Param->flag;
} else if (TYPE_SUBTITLE_ARIB_B24 == type) {
Arib24Param *pArib24Param = (Arib24Param *)data;
if ((mState == E_SOURCE_STARTED) && (mPid != pArib24Param->pid)) {
restartDemux = true;
}
mDemuxId = pArib24Param->demuxId;
mPid = pArib24Param->pid;
mSecureLevelFlag = pArib24Param->flag;
mParam1 = pArib24Param->languageCodeId;
} else if (TYPE_SUBTITLE_DVB_TTML == type) {
TtmlParam *pTtmlParam = (TtmlParam *)data;
if ((mState == E_SOURCE_STARTED) && (mPid != pTtmlParam->pid)) {
restartDemux = true;
}
mDemuxId = pTtmlParam->demuxId;
mPid = pTtmlParam->pid;
mSecureLevelFlag = pTtmlParam->flag;
} else if (TYPE_SUBTITLE_SMPTE_TTML == type) {
SmpteTtmlParam *pSmpteTtmlParam = (SmpteTtmlParam *)data;
if ((mState == E_SOURCE_STARTED) && (mPid != pSmpteTtmlParam->pid)) {
restartDemux = true;
}
mDemuxId = pSmpteTtmlParam->demuxId;
mPid = pSmpteTtmlParam->pid;
mSecureLevelFlag = pSmpteTtmlParam->flag;
}
SUBTITLE_LOGI("restartDemux=%d ", restartDemux);
mSubType = type;
if (restartDemux) {
mExitRequested = true;
if (mRenderTimeThread != nullptr) {
mRenderTimeThread->join();
mRenderTimeThread = nullptr;
}
if (mDemuxContext != nullptr) {
close_dvb_dmx(mDemuxContext, mDemuxId);
delete mDemuxContext;
mDemuxContext = nullptr;
}
mDemuxContext = new TVSubtitleData();
int ret = open_dvb_dmx(mDemuxContext, mDemuxId, mPid, mSecureLevelFlag);
if (ret == 0) {
mExitRequested = false;
mRenderTimeThread =
std::shared_ptr<std::thread>(new std::thread(&DemuxSource::loopRenderTime, this));
notifyInfoChange();
} else {
delete mDemuxContext;
mDemuxContext = nullptr;
}
}
SUBTITLE_LOGI("mPid:%d, demuxId = %d, mSecureLevelFlag = %d", mPid, mDemuxId, mSecureLevelFlag);
return;
}
void DemuxSource::setPipId(int mode, int id) {
SUBTITLE_LOGI("mode:%d, id = %d", mode, id);
if (PIP_PLAYER_ID == mode) {
mPlayerId = id;
} else if (PIP_MEDIASYNC_ID == mode) {
if ((-1 != mMediaSyncId) || (mMediaSyncId != id)) {
mMediaSyncId = id;
mMediaSyncDestroyFlag = true;
if (mMediaSync != nullptr) {
MediaSync_destroy(mMediaSync);
mMediaSync = MediaSync_create();
}
MediaSync_bindInstance(mMediaSync, mMediaSyncId, MEDIA_SUBTITLE);
mMediaSyncDestroyFlag = false;
}
}
}
bool DemuxSource::stop() {
SUBTITLE_LOGI_FUNC_IN();
mState = E_SOURCE_STOPPED;
mExitRequested = true;
if (mRenderTimeThread != nullptr) {
mRenderTimeThread->join();
mRenderTimeThread = nullptr;
}
// If parser has problem, it read more data than provided
// then stop cannot stopped. need notify exit here.
if (mSegment != nullptr) {
mSegment->notifyExit();
}
if (mDemuxContext != nullptr) {
close_dvb_dmx(mDemuxContext, mDemuxId);
delete mDemuxContext;
mDemuxContext = nullptr;
}
mDemuxId = -1;
SUBTITLE_LOGI_FUNC_OUT();
return false;
}
bool DemuxSource::isFileAvailable() {
auto firstFrameToggle = sysfsReadInt(SYSFS_VIDEO_FIRSTRRAME.c_str(), 10);
mediasync_frameinfo tempInfo;
tempInfo.framePts = -1;
SUBTITLE_LOGI("mMediaSyncId:%d, mMediaSync:%p", mMediaSyncId, mMediaSync);
if (mMediaSyncId != -1 && mMediaSync != nullptr) {
MediaSync_getFirstVideoFrameInfo(mMediaSync, &tempInfo);
}
SUBTITLE_LOGI("firstFrameToggle:%" PRId64 ",tempInfo.framePts:%" PRId64, firstFrameToggle,
tempInfo.framePts);
return (firstFrameToggle == 1 || tempInfo.framePts > 0);
}
size_t DemuxSource::availableDataSize() {
if (mSegment == nullptr) return 0;
return mSegment->availableDataSize();
}
size_t DemuxSource::read(void *buffer, size_t size) {
size_t read = 0;
int isReadItemEnd = 0;
// Current design of Parser Read, do not need add lock protection.
// because all the read, is in Parser's parser thread.
// We only need add lock here, is for protect access the mCurrentItem's
// member function multi-thread.
// Current Impl do not need lock, if required, then redesign the SegmentBuffer.
// in case of applied size more than 1024*2, such as dvb subtitle,
// and data process error for two reads.
// so add until read applied data then exit.
while (read != size && mState == E_SOURCE_STARTED) {
if (mSegment == nullptr) {
return 0;
}
if (mCurrentItem != nullptr && !mCurrentItem->isEmpty()) {
if (size == 0xffff) {
// if size is 0xffff, it means to get all the data of the current item
size = mCurrentItem->getSize();
}
read += mCurrentItem->read_check(((char *)buffer + read), size - read, &isReadItemEnd,
E_SUBTITLE_DEMUX);
// SUBTITLE_LOGV("read:%d,size:%d isReadItemEnd:%d SourceType:%d", read, size,
// isReadItemEnd, E_SUBTITLE_DEMUX);
if (read == size || isReadItemEnd == -1) return read;
} else {
// SUBTITLE_LOGV("mCurrentItem null, pop next buffer item");
mCurrentItem = mSegment->pop();
}
}
return read;
}
void DemuxSource::dump(int fd, const char *prefix) {
dprintf(fd, "%s nDemuxSource:\n", prefix);
{
std::unique_lock<std::mutex> autolock(mLock);
for (auto it = mInfoListeners.begin(); it != mInfoListeners.end(); it++) {
auto wk_listener = (*it);
if (auto lstn = wk_listener.lock())
dprintf(fd, "%s InfoListener: %p\n", prefix, lstn.get());
}
}
dprintf(fd, "%s state:%d\n\n", prefix, mState);
dprintf(fd, "%s Total Subtitle Tracks: (%s)%" PRId64 "\n", prefix,
SYSFS_SUBTITLE_TOTAL.c_str(), sysfsReadInt(SYSFS_SUBTITLE_TOTAL.c_str(), 10));
dprintf(fd, "%s Current Subtitle type: (%s)%" PRId64 "\n", prefix,
SYSFS_SUBTITLE_TYPE.c_str(), sysfsReadInt(SYSFS_SUBTITLE_TYPE.c_str(), 10));
dprintf(fd, "\n%s Current Unconsumed Data Size: %zu\n", prefix, availableDataSize());
}
void DemuxSource::onPesCallback() {
// See DataSource::checkParsedPts(), mNewestDemuxAVPts is updated within some conditions.
if (mNewestDemuxAVPts != 0) {
return;
}
updateNewestAVPts(0, 0);
}
void DemuxSource::updateNewestAVPts(int64_t subtitlePts, int64_t syncedPts) {
amdmx_filter_mem_info demuxFilterMemInfo;
memset(&demuxFilterMemInfo, 0, sizeof(demuxFilterMemInfo));
auto ret = DemuxGetNewestPts(mDemuxId, &demuxFilterMemInfo);
if (ret != AM_SUCCESS) {
SUBTITLE_LOGE("[newest_avpts_tag] fail to DemuxGetNewestPts");
return;
}
int64_t audioPts = 0;
int64_t videoPts = 0;
int64_t subtitleDemuxPts = 0;
int64_t teletextPts = 0;
for (__u32 i = 0; i < demuxFilterMemInfo.filter_num; ++i) {
auto info = demuxFilterMemInfo.info[i];
if (info.type == AMDMX_AUDIO_TYPE) {
audioPts = info.filter_info.newest_pts;
} else if (info.type == AMDMX_VIDEO_TYPE) {
videoPts = info.filter_info.newest_pts;
} else if (info.type == AMDMX_SUBTITLE_TYPE) {
subtitleDemuxPts = info.filter_info.newest_pts;
} else if (info.type == AMDMX_TELETEXT_TYPE) {
teletextPts = info.filter_info.newest_pts;
}
}
if (subtitlePts != 0 && syncedPts != 0) {
SUBTITLE_LOGI("[newest_avpts_tag_check] PES's PTS difference: video-audio = %" PRId64
" ms, video-subtitle = %" PRId64 " ms, audio-subtitle = %" PRId64
" ms, video-syncedPts = %" PRId64 " ms",
pts2Ms(videoPts - audioPts), pts2Ms(videoPts - subtitlePts),
pts2Ms(audioPts - subtitlePts), pts2Ms(videoPts - syncedPts));
}
if (audioPts == 0 || videoPts == 0) {
if (subtitlePts != 0 && syncedPts != 0) {
SUBTITLE_LOGE("[newest_avpts_tag] audioPts = %" PRId64 " ms, videoPts = %" PRId64
" ms, teletextPts = %" PRId64 " ms, subtitlePts = %" PRId64 " ms",
pts2Ms(audioPts), pts2Ms(videoPts), pts2Ms(teletextPts),
pts2Ms(subtitleDemuxPts));
}
mNewestDemuxAVPts = std::max(audioPts, videoPts);
} else {
mNewestDemuxAVPts = std::min(audioPts, videoPts);
}
}