Files
BPI-CM9-Linux/aml-comp/vendor/amlogic/aml_subtitleserver/libsubtitle/io/VbiSource.cpp
T

317 lines
9.9 KiB
C++
Raw Normal View History

2026-08-13 11:28:40 +08:00
/*
* 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 <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 "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<std::mutex> 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<char> 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<BufferSegment>(new BufferSegment());
/*if (total > 0 && subtype > 0) {
mRenderTimeThread = std::shared_ptr<std::thread>(new
std::thread(&DeviceSource::loopRenderTime, this));
}*/
mReadThread = std::shared_ptr<std::thread>(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<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, "\n%s Current Unconsumed Data Size: %zu\n", prefix, availableDataSize());
}