Files
2026-08-24 10:03:24 +08:00

317 lines
11 KiB
C++

/*
* Copyright (C) 2014-2025 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 "SubtitleSource"
#include "SocketSource.h"
#include <fcntl.h>
#include <unistd.h>
#include <utils/CallStack.h>
#include <string>
#include "SocketServer.h"
#include "SubtitleLog.h"
#include "SubtitleTypes.h"
static const std::string SYSFS_VIDEO_PTS = "/sys/class/tsync/pts_video_u64";
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);
}
static inline unsigned int make32bitValue(const char *buffer) {
return buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
}
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: %m", path);
}
return val;
}
static inline std::shared_ptr<char> makeNewSpBuffer(const char *buffer, int size) {
std::shared_ptr<char> copyBuffer(new char[size](), [](char *buf) { delete[] buf; });
memcpy(copyBuffer.get(), buffer, size);
return copyBuffer;
}
SocketSource::SocketSource() {
mMediaSync = MediaSync_create();
mSegment = std::shared_ptr<BufferSegment>(new BufferSegment());
// register listener: mSegment.
// mSgement register onData, SocketSource::onData.
SubSocketServer::registClient(this);
}
SubtitleIOType SocketSource::type() {
return E_SUBTITLE_SOCK;
}
SocketSource::SocketSource(const std::string url) {
(void)url;
mMediaSync = MediaSync_create();
mSegment = std::shared_ptr<BufferSegment>(new BufferSegment());
// register listener: mSegment.
// mSgement register onData, SocketSource::onData.
SubSocketServer::registClient(this);
}
void SocketSource::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;
if (mMediaSync != nullptr) {
MediaSync_destroy(mMediaSync);
mMediaSync = MediaSync_create();
}
MediaSync_bindInstance(mMediaSync, mMediaSyncId, MEDIA_VIDEO);
}
std::thread t = std::thread(&SocketSource::loopRenderTime, this);
t.detach();
}
}
SocketSource::~SocketSource() {
SUBTITLE_LOGI_FUNC_IN();
mExitRequested = true;
if (mMediaSync != nullptr) {
MediaSync_destroy(mMediaSync);
}
// TODO: no ring ref?
SubSocketServer::unregisterClient(this);
}
void SocketSource::loopRenderTime() {
while (!mExitRequested) {
mLock.lock();
for (auto it = mInfoListeners.begin(); it != mInfoListeners.end(); it++) {
auto wk_lstner = (*it);
if (wk_lstner.expired()) {
SUBTITLE_LOGI("[threadLoop] lstn null.\n");
continue;
}
int64_t value = 0;
if (-1 == mMediaSyncId) {
mSyncPts = sysfsReadInt(SYSFS_VIDEO_PTS.c_str(), 16);
} else {
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) {
if (auto lstn = wk_lstner.lock()) {
lstn->onRenderTimeChanged(mSyncPts);
}
}
}
mLock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(16));
}
}
bool SocketSource::notifyInfoChange_l(int type) {
if (mState == E_SOURCE_STOPPED) {
SUBTITLE_LOGI("return as already stopped");
return false;
}
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()) {
switch (type) {
case eTypeSubtitleRenderTime:
lstn->onRenderTimeChanged(mRenderTimeUs);
break;
case eTypeSubtitleTotal:
lstn->onSubtitleChanged(mTotalSubtitle);
break;
case eTypeSubtitleStartPts:
lstn->onRenderStartTimestamp(mStartPts);
break;
case eTypeSubtitleType:
lstn->onTypeChanged(mSubtitleType);
break;
}
}
}
return true;
}
int SocketSource::onData(const char *buffer, int len) {
int type = make32bitValue(buffer);
// SUBTITLE_LOGI("subtype=%x eTypeSubtitleRenderTime=%x size=%d", type, eTypeSubtitleRenderTime,
// len);
switch (type) {
case eTypeSubtitleRenderTime: {
static int count = 0;
mRenderTimeUs = (((int64_t)make32bitValue(buffer + 8)) << 32) |
make32bitValue(buffer + 4); // make32bitValue(buffer + 4);
if (count++ % 100 == 0)
SUBTITLE_LOGI("mRenderTimeUs:%x(updated %d times) time:%" PRId64, type, count,
mRenderTimeUs);
break;
}
case eTypeSubtitleTotal: {
mTotalSubtitle = make32bitValue(buffer + 4);
SUBTITLE_LOGI("eTypeSubtitleTotal:%x total:%d", type, mTotalSubtitle);
break;
}
case eTypeSubtitleStartPts: {
mStartPts = (((int64_t)make32bitValue(buffer + 8)) << 32) | make32bitValue(buffer + 4);
SUBTITLE_LOGI("eTypeSubtitleStartPts:%x time:%" PRId64, type, mStartPts);
break;
}
case eTypeSubtitleType: {
mSubtitleType = make32bitValue(buffer + 4);
SUBTITLE_LOGI("eTypeSubtitleType:%x %x len=%d", type, mSubtitleType, len);
break;
}
case eTypeSubtitleData:
SUBTITLE_LOGI("eTypeSubtitleData: len=%d", len);
if (mTotalSubtitle != -1 || mSubtitleType != -1)
mSegment->push(makeNewSpBuffer(buffer + 4, len - 4), len - 4);
break;
case eTypeSubtitleTypeString:
case eTypeSubtitleLangString:
case eTypeSubtitleResetServ:
case eTypeSubtitleExitServ:
SUBTITLE_LOGI("not handled message: %s", buffer + 4);
break;
default: {
SUBTITLE_LOGI("!!!!!!!!!SocketSource:onData(subtitleData): %d", /*buffer,*/ len);
mSegment->push(makeNewSpBuffer(buffer, len), len);
break;
}
}
notifyInfoChange_l(type);
return 0;
}
bool SocketSource::start() {
mState = E_SOURCE_STARTED;
return true;
}
bool SocketSource::stop() {
mState = E_SOURCE_STOPPED;
mSegment->notifyExit();
return true;
}
size_t SocketSource::read(void *buffer, size_t size) {
size_t read = 0;
// Current design of Parser Read, do not need add lock protection.
// because all the read, is in 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.
// std::unique_lock<std::mutex> autolock(mLock);
// 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 (mCurrentItem != nullptr && !mCurrentItem->isEmpty()) {
read += mCurrentItem->read_l(((char *)buffer + read), size - read);
// SUBTITLE_LOGI("read:%d,size:%d", read, size);
if (read == size) break;
} else {
SUBTITLE_LOGI("mCurrentItem null, pop next buffer item");
mCurrentItem = mSegment->pop();
}
}
if (isSubtitleDumpAvailable()) {
if (mDumpFd == -1) {
mDumpFd = ::open(DUMP_DATA_FILE_PATH, O_RDWR | O_CREAT, 0666);
SUBTITLE_LOGI("need dump Source: mDumpFd=%d %d", mDumpFd, errno);
}
if (mDumpFd > 0) {
size_t ret = write(mDumpFd, buffer, size);
if (ret != size) {
SUBTITLE_LOGE("wrote %zu/%zu", ret, size);
}
}
}
return read;
}
void SocketSource::dump(int fd, const char *prefix) {
dprintf(fd, "\n%s SocketSource:\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: %d\n", prefix, mTotalSubtitle);
dprintf(fd, "%s Current Subtitle type: %d\n", prefix, mSubtitleType);
dprintf(fd, "%s VideoStart PTS : %" PRId64 "\n", prefix, mStartPts);
dprintf(fd, "%s Current Video PTS : %" PRId64 "\n", prefix, mRenderTimeUs);
dprintf(fd, "\n%s Current Buffer Size: %zu\n", prefix, mSegment->availableDataSize());
}