/* * 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. */ /* *for teletext atv data source */ #define LOG_TAG "VbiSource" #include "VbiSource.h" #include #include #include #include #include #include #include #include #include #include #include "SubtitleLog.h" static const std::string VBI_DEV_FILE = "/dev/vbi"; static const std::string SYSFS_VIDEO_PTS = "/sys/class/tsync/pts_video_u64"; #define TYPE_SUBTITLE_DVB_TELETEXT 8 #define MAX_DUMP_DATA_LENGTH 3000 static inline bool pollFd(int fd, int timeout) { struct pollfd pollFd[1]; if (fd <= 0) { return false; } pollFd[0].fd = fd; pollFd[0].events = POLLIN; return (::poll(pollFd, 1, timeout) > 0); } 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; } VbiSource::VbiSource() { mState = E_SOURCE_INV; int type = VBI_TYPE_TT_625B; mExitRequested = false; mDumpFd = -1; mRdFd = ::open(VBI_DEV_FILE.c_str(), O_RDONLY); if (mRdFd == -1) { SUBTITLE_LOGI("cannot open %s", VBI_DEV_FILE.c_str()); return; } if (::ioctl(mRdFd, VBI_IOC_SET_TYPE, &type) == -1) SUBTITLE_LOGI("VBI_IOC_SET_TYPE error:%s", strerror(errno)); if (::ioctl(mRdFd, VBI_IOC_START) == -1) SUBTITLE_LOGI("VBI_IOC_START error:%s", strerror(errno)); SUBTITLE_LOGI("VbiSource: %s=%d", VBI_DEV_FILE.c_str(), mRdFd); } VbiSource::~VbiSource() { SUBTITLE_LOGI("~VbiSource: %s=%d", VBI_DEV_FILE.c_str(), mRdFd); mExitRequested = true; if (mRenderTimeThread != nullptr) { mRenderTimeThread->join(); mRenderTimeThread = nullptr; } ::close(mRdFd); if (mReadThread != nullptr) { mReadThread->join(); mReadThread = nullptr; } if (mDumpFd > 0) ::close(mDumpFd); } void VbiSource::updateParameter(int type, void *data) { (void)data; SUBTITLE_LOGI(" in updateParameter type = %d ", type); return; } bool VbiSource::notifyInfoChange() { std::unique_lock autolock(mLock); for (auto it = mInfoListeners.begin(); it != mInfoListeners.end(); it++) { auto wk_listener = (*it); if (auto lstn = wk_listener.lock()) { auto value = TYPE_SUBTITLE_DVB_TELETEXT; if (value > 0) { // 0:no sub lstn->onTypeChanged(value); } value = 1; // sysfsReadInt(SYSFS_SUBTITLE_TOTAL.c_str(), 10); if (value >= 0) { lstn->onSubtitleChanged(value); } return true; } } return false; } void VbiSource::loopRenderTime() { while (!mExitRequested) { mLock.lock(); for (auto it = mInfoListeners.begin(); it != mInfoListeners.end(); it++) { auto wk_listener = (*it); if (wk_listener.expired()) { SUBTITLE_LOGI("[threadLoop] lstn null.\n"); continue; } auto value = sysfsReadInt(SYSFS_VIDEO_PTS.c_str(), 16); static int i = 0; if (i++ % 300 == 0) { SUBTITLE_LOGI("read pts: %" PRId64, value); } if (!mExitRequested) { if (auto lstn = wk_listener.lock()) { lstn->onRenderTimeChanged(value); } } } mLock.unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(16)); } } void VbiSource::loopDriverData() { struct vbi_data_s vbi; while (!mExitRequested && mRdFd > 0) { if (pollFd(mRdFd, 15)) { struct vbi_data_s *pd; int ret = ::read(mRdFd, &vbi, sizeof(vbi)); pd = &vbi; if (ret >= (int)sizeof(struct vbi_data_s)) { // SUBTITLE_LOGI("loopDriverData=line:%d data:%s", pd->line_num, pd->b); unsigned char sub_header[ATV_TELETEXT_SUB_HEADER_LEN] = {0x41, 0x4d, 0x4c, 0x55, 0x41, 0}; sub_header[5] = 0; // line_num 16 bit, for coverity sub_header[6] = 0; // line_num 16 bit, for coverity sub_header[7] = (pd->line_num >> 8) & 0xff; sub_header[8] = pd->line_num & 0xff; int size = ATV_TELETEXT_SUB_HEADER_LEN + ATV_TELETEXT_DATA_LEN; std::shared_ptr spBuf(new char[size], [](char *buf) { delete[] buf; }); memcpy(spBuf.get(), sub_header, ATV_TELETEXT_SUB_HEADER_LEN); memcpy(spBuf.get() + ATV_TELETEXT_SUB_HEADER_LEN, (char *)pd->b, ATV_TELETEXT_DATA_LEN); mSegment->push(std::move(spBuf), size); // coverity[RESOURCE_LEAK] if (isSubtitleDumpAvailable()) { static char slice_buffer[MAX_DUMP_DATA_LENGTH * 4]; for (int i = 0; i < ATV_TELETEXT_DATA_LEN; i++) { sprintf(&slice_buffer[i * 3], " %02x", pd->b[i]); } SUBTITLE_LOGI("line data: %s", slice_buffer); } } } } } bool VbiSource::start() { if (E_SOURCE_STARTED == mState) { SUBTITLE_LOGE("already stated"); return false; } mState = E_SOURCE_STARTED; mSegment = std::shared_ptr(new BufferSegment()); /*if (total > 0 && subtype > 0) { mRenderTimeThread = std::shared_ptr(new std::thread(&DeviceSource::loopRenderTime, this)); }*/ mReadThread = std::shared_ptr(new std::thread(&VbiSource::loopDriverData, this)); // first check types notifyInfoChange(); return false; } bool VbiSource::stop() { mState = E_SOURCE_STOPPED; auto ret = ::ioctl(mRdFd, VBI_IOC_STOP); if (ret < 0) { SUBTITLE_LOGE("%m"); } // If parser has problem, it read more data than provided // then stop cannot stopped. need notify exit here. mSegment->notifyExit(); return false; } SubtitleIOType VbiSource::type() { return E_SUBTITLE_VBI; } bool VbiSource::isFileAvailable() { return false; } size_t VbiSource::availableDataSize() { if (mSegment == nullptr) return 0; return mSegment->availableDataSize(); } size_t VbiSource::readDriverData(void *buffer, size_t size) { int read_done = 0; 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_done; } size_t VbiSource::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'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 (mCurrentItem != nullptr && !mCurrentItem->isEmpty()) { read += mCurrentItem->read_l(((char *)buffer + read), size - read); // SUBTITLE_LOGI("read:%d,size:%d", read, size); if (read == size) return read; } else { // SUBTITLE_LOGI("mCurrentItem null, pop next buffer item"); mCurrentItem = mSegment->pop(); } } // read += mCurrentItem->read(((char *)buffer+read), size-read); return read; } void VbiSource::dump(int fd, const char *prefix) { dprintf(fd, "%s nVBISource:\n", prefix); { std::unique_lock 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, "\n%s Current Unconsumed Data Size: %zu\n", prefix, availableDataSize()); }