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

450 lines
16 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 "SubtitleServer"
#include "AndroidCallbackMessageQueue.h"
#include <Parser.h>
#include <RemoteData.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <algorithm>
#include <iterator>
#include <memory>
#include <thread>
#include <vector>
#include "SubtitleLog.h"
#define EVENT_ON_SUBTITLEDATA_CALLBACK 0xA00000
#define EVENT_ON_SUBTITLEAVAILABLE_CALLBACK 0xA00001
#define EVENT_ON_VIDEOAFDCHANGE_CALLBACK 0xA00002
#define EVENT_ON_MIXVIDEOEVENT_CALLBACK 0xA00003
#define EVENT_ON_SUBTITLE_DIMENSION_CALLBACK 0xA00004
#define EVENT_ON_SUBTITLE_LANGUAGE_CALLBACK 0xA00005
#define EVENT_ON_SUBTITLE_INFO_CALLBACK 0xA00006
#define SHMEMORY_ID 0x9FE7
namespace amlogic {
static const int MSG_CHECK_SUBDATA = 0;
// This hal never recreate
sp<AndroidCallbackMessageQueue> AndroidCallbackMessageQueue::sInstance = nullptr;
sp<AndroidCallbackMessageQueue> AndroidCallbackMessageQueue::Instance() {
return sInstance;
}
AndroidCallbackMessageQueue::AndroidCallbackMessageQueue(sp<AndroidCallbackHandler> proxy) {
android::AutoMutex _l(mLock);
mStopped = false;
mProxyHandler = proxy;
sInstance = this;
initSender();
mCallbackLooperThread = std::thread(&AndroidCallbackMessageQueue::looperLoop, this);
}
AndroidCallbackMessageQueue::~AndroidCallbackMessageQueue() {
deInitSender();
mStopped = true;
if (mLooper != nullptr) {
mLooper->wake();
}
// post a message to wakeup and exit.
if (mLooper != nullptr) {
mLooper->sendMessage(this, Message(MSG_CHECK_SUBDATA));
}
mCallbackLooperThread.join();
sInstance = nullptr;
}
void AndroidCallbackMessageQueue::looperLoop() {
mLooper = new Looper(false);
mLooper->sendMessageDelayed(100LL, this, Message(MSG_CHECK_SUBDATA));
// never exited
while (!mStopped && mLooper != nullptr) {
mLooper->pollAll(-1);
// If mLooper is set to nullptr at some point within the loop, ensure proper handling and
// exit the loop
if (mLooper == nullptr) {
SUBTITLE_LOGE("looperLoop mLooper is Null.");
break;
}
}
mLooper = nullptr;
}
void AndroidCallbackMessageQueue::handleMessage(const Message &message) {
std::unique_ptr<SubtitleData> data(nullptr);
mLock.lock();
if (mSubtitleData.size() > 0) {
auto iter = mSubtitleData.begin();
std::unique_ptr<SubtitleData> items = std::move(*iter);
data.swap(items);
mSubtitleData.erase(iter);
}
if (mSubtitleData.size() > 0) {
mLooper->removeMessages(this, MSG_CHECK_SUBDATA);
mLooper->sendMessage(this, Message(MSG_CHECK_SUBDATA));
} else {
mLooper->removeMessages(this, MSG_CHECK_SUBDATA);
mLooper->sendMessageDelayed(s2ns(2), this, Message(MSG_CHECK_SUBDATA));
}
mLock.unlock();
// processing, outside lock.
if (mProxyHandler == nullptr || data == nullptr) {
return;
}
auto remoteData = std::make_shared<RemoteData>();
if (remoteData == nullptr) {
SUBTITLE_LOGE("Failed to allocate memory for RemoteData");
return;
}
remoteData->callbackType = data->type;
switch (remoteData->callbackType) {
case EVENT_ON_SUBTITLEDATA_CALLBACK: {
remoteData->event = data->x;
remoteData->channelId = data->y;
SUBTITLE_LOGI("onSubtitleDataEvent EVENT_ON_SUBTITLEDATA_CALLBACK: event:%d, id:%d",
data->x, data->y);
break;
}
case EVENT_ON_SUBTITLEAVAILABLE_CALLBACK: {
remoteData->available = data->x;
SUBTITLE_LOGI("onSubtitleDataEvent EVENT_ON_SUBTITLEAVAILABLE_CALLBACK: available:%d",
data->x);
break;
}
case EVENT_ON_VIDEOAFDCHANGE_CALLBACK: {
remoteData->afd = data->x;
remoteData->playerid = data->y;
SUBTITLE_LOGI(
"onSubtitleDataEvent EVENT_ON_VIDEOAFDCHANGE_CALLBACK:afd:%d, playerid = %d",
data->x, data->y);
break;
}
case EVENT_ON_SUBTITLE_DIMENSION_CALLBACK: {
remoteData->width = data->x;
remoteData->height = data->y;
SUBTITLE_LOGI(
"onSubtitleDataEvent EVENT_ON_SUBTITLE_DIMENSION_CALLBACK: width:%d, height:%d",
data->x, data->y);
break;
}
case EVENT_ON_MIXVIDEOEVENT_CALLBACK: {
// middleware api do not need this
return;
}
case EVENT_ON_SUBTITLE_LANGUAGE_CALLBACK: {
if (data->lang != nullptr) {
strncpy(remoteData->lang, data->lang, sizeof(remoteData->lang) - 1);
remoteData->lang[sizeof(remoteData->lang) - 1] = '\0';
}
SUBTITLE_LOGI("onSubtitleDataEvent EVENT_ONSUBTITLE_LANGUAGE_CALLBACK:lang:%s",
remoteData->lang);
break;
}
case EVENT_ON_SUBTITLE_INFO_CALLBACK: {
remoteData->what = data->x;
remoteData->extra = data->y;
SUBTITLE_LOGI("onSubtitleDataEvent EVENT_ONSUBTITLE_INFO_CALLBACK:what:%d, extra:%d",
data->x, data->y);
break;
}
default: {
remoteData->type = data->type;
remoteData->width = data->width;
remoteData->height = data->height;
remoteData->size = data->size;
remoteData->isShow = data->isShow;
remoteData->x = data->x;
remoteData->y = data->y;
remoteData->videoWidth = data->videoWidth;
remoteData->videoHeight = data->videoHeight;
remoteData->shimId = data->shmid;
remoteData->objectSegmentId = data->objectSegmentId;
remoteData->isText = [](int type) -> bool {
return type == SubtitleType::TYPE_SUBTITLE_CLOSED_CAPTION ||
type == SubtitleType::TYPE_SUBTITLE_SCTE27 ||
type == SubtitleType::TYPE_SUBTITLE_SMPTE_TTML ||
type == SubtitleType::TYPE_SUBTITLE_DVB_TTML;
}(data->type);
break;
}
}
sendData(remoteData);
}
void AndroidCallbackMessageQueue::deInitSender() {
if (mDataSender != nullptr) {
mDataSender->stop();
mDataSender->join();
mDataSender = nullptr;
}
}
void AndroidCallbackMessageQueue::sendData(std::shared_ptr<RemoteData> remoteData) {
if (mDataSender == nullptr && !mStopped) {
SUBTITLE_LOGE("mDataSender is nullptr");
initSender();
if (mDataSender == nullptr) return;
}
for (auto it : mSendFds) {
if (mDataSender != nullptr) {
std::string resultStr = mDataSender->sendForResult(
it, reinterpret_cast<char *>(remoteData.get()), sizeof(RemoteData));
if (resultStr == "-1") {
SUBTITLE_LOGE("postData Server is broken, stop!!!");
deInitSender();
}
} else {
SUBTITLE_LOGE("mDataSender is nullptr");
}
}
if (remoteData->shimId) {
int shmId = shmget(remoteData->shimId, remoteData->size, 0);
if (shmctl(shmId, IPC_RMID, NULL) == -1) {
SUBTITLE_LOGE("Error removing shm ipc_id=%d, error=%m", shmId);
}
}
}
void AndroidCallbackMessageQueue::initSender() {
deInitSender();
mDataSender = std::make_shared<IpcSocket>(true);
mDataSender->start(6666, this);
if (!mDataSender->isvalid()) {
SUBTITLE_LOGI("Client: initRemoteLocked failed");
deInitSender();
SUBTITLE_LOGI("mDataSender stop success");
return;
}
mDataSender->setEventListener(
[&](int fd, void *data) -> int { return handleReceivedReadyData(fd); });
}
int AndroidCallbackMessageQueue::handleReceivedReadyData(int fd) {
char retBuf[128];
memset(retBuf, 0, sizeof(retBuf));
if (!IpcSocket::receive(fd, retBuf, sizeof(retBuf))) {
SUBTITLE_LOGE("Client broken, fd= %d, remove it.", fd);
auto it = std::find(mSendFds.begin(), mSendFds.end(), fd);
if (it != mSendFds.end()) {
mSendFds.erase(it);
}
return EventsTracker::RET_REMOVE;
}
SUBTITLE_LOGI("Receive data: %s", retBuf);
auto reply = [](int fd, const char *ret) {
std::string retStr = ret;
bool sendStatus = IpcSocket::send(fd, retStr.c_str(), retStr.size());
SUBTITLE_LOGI("Reply %s", (sendStatus ? "OK" : "NG"));
};
if (strncmp(retBuf, "recv-ready", 10) == 0) {
SUBTITLE_LOGI("New client connected, fd = %d", fd);
if (fd != -1) {
mSendFds.push_back(fd);
reply(fd, "1");
}
} else {
reply(fd, "0");
}
return EventsTracker::RET_CONTINUE;
}
void AndroidCallbackMessageQueue::onSubtitleDataEvent(int event, int channelId) {
android::AutoMutex _l(mLock);
std::unique_ptr<SubtitleData> subtitleData(new SubtitleData);
subtitleData->type = EVENT_ON_SUBTITLEDATA_CALLBACK;
subtitleData->x = event;
subtitleData->y = channelId;
mSubtitleData.push_back(std::move(subtitleData));
mLooper->sendMessage(this, Message(MSG_CHECK_SUBDATA));
}
void AndroidCallbackMessageQueue::onSubtitleDimension(int width, int height) {
android::AutoMutex _l(mLock);
// std::unique_ptr<SubtitleData> subtitleData = std::make_unique<SubtitleData>();
std::unique_ptr<SubtitleData> subtitleData(new SubtitleData);
subtitleData->type = EVENT_ON_SUBTITLE_DIMENSION_CALLBACK;
subtitleData->x = width;
subtitleData->y = height;
mSubtitleData.push_back(std::move(subtitleData));
mLooper->sendMessage(this, Message(MSG_CHECK_SUBDATA));
}
void AndroidCallbackMessageQueue::onSubtitleAvailable(int available) {
android::AutoMutex _l(mLock);
std::unique_ptr<SubtitleData> subtitleData(new SubtitleData);
subtitleData->type = EVENT_ON_SUBTITLEAVAILABLE_CALLBACK;
subtitleData->x = available;
mSubtitleData.push_back(std::move(subtitleData));
mLooper->sendMessage(this, Message(MSG_CHECK_SUBDATA));
}
void AndroidCallbackMessageQueue::onVideoAfdChange(int afd, int playerid) {
android::AutoMutex _l(mLock);
std::unique_ptr<SubtitleData> subtitleData(new SubtitleData);
subtitleData->type = EVENT_ON_VIDEOAFDCHANGE_CALLBACK;
subtitleData->x = afd;
subtitleData->y = playerid;
mSubtitleData.push_back(std::move(subtitleData));
mLooper->sendMessage(this, Message(MSG_CHECK_SUBDATA));
}
void AndroidCallbackMessageQueue::onMixVideoEvent(int val) {
android::AutoMutex _l(mLock);
std::unique_ptr<SubtitleData> subtitleData(new SubtitleData);
subtitleData->type = EVENT_ON_MIXVIDEOEVENT_CALLBACK;
subtitleData->x = val;
mSubtitleData.push_back(std::move(subtitleData));
mLooper->sendMessage(this, Message(MSG_CHECK_SUBDATA));
}
void AndroidCallbackMessageQueue::onSubtitleLanguage(char *lang) {
android::AutoMutex _l(mLock);
std::unique_ptr<SubtitleData> subtitleData(new SubtitleData);
subtitleData->type = EVENT_ON_SUBTITLE_LANGUAGE_CALLBACK;
subtitleData->lang = lang;
mSubtitleData.push_back(std::move(subtitleData));
mLooper->sendMessage(this, Message(MSG_CHECK_SUBDATA));
}
void AndroidCallbackMessageQueue::onSubtitleInfo(int what, int extra) {
android::AutoMutex _l(mLock);
std::unique_ptr<SubtitleData> subtitleData(new SubtitleData);
subtitleData->type = EVENT_ON_SUBTITLE_INFO_CALLBACK;
subtitleData->x = what;
subtitleData->y = extra;
mSubtitleData.push_back(std::move(subtitleData));
mLooper->sendMessage(this, Message(MSG_CHECK_SUBDATA));
}
int AndroidCallbackMessageQueue::copyShareMemory(const char *data, int size) {
int shmid;
char *shmaddr = nullptr;
if ((0 == size) || (data == nullptr)) {
return -1;
}
// key_t key = ftok("/tmp/subtitle",0x03);
// shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|IPC_EXCL|0666 ) ;
shmid = shmget(MIPC_SUBTITLE_PORTID, size, IPC_CREAT | IPC_EXCL | 0666);
if (shmid < 0) {
SUBTITLE_LOGE("get shm ipc_id error size=%d,shmid=%d, error=%s", size, shmid,
strerror(errno));
return -1;
}
SUBTITLE_LOGI("AndroidCallbackMessageQueue shmid=%d", shmid);
shmaddr = (char *)shmat(shmid, NULL, 0);
if (shmaddr == nullptr) {
SUBTITLE_LOGE("AndroidCallbackMessageQueue Error: shmat addr error");
return -1;
}
memcpy(shmaddr, data, size);
shmdt(shmaddr);
return MIPC_SUBTITLE_PORTID;
}
bool AndroidCallbackMessageQueue::postDisplayData(const char *data, int type, int x, int y,
int width, int height, int videoWidth,
int videoHeight, int size, int cmd,
int objectSegmentId) {
SUBTITLE_LOGE(
" AndroidCallbackMessageQueue::postDisplayData x=%d,y=%d,width=%d,height=%d,size=%d\n", x,
y, width, height, size);
/*sp<HidlMemory> mem;
sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem");
bool allocRes = false;
Return<void> r = ashmemAllocator->allocate(size+1024, [&](bool success, const hidl_memory& _mem)
{ if (success) { mem = HidlMemory::getInstance(_mem); sp<IMemory> memory = mapMemory(_mem); if
(memory == nullptr) { SUBTITLE_LOGE("map Memory Failed!!"); return; // we may need status!
}
memory->update();
if (data != nullptr) memcpy(memory->getPointer(), data, size);
memory->commit();
allocRes = true;
} else {
SUBTITLE_LOGE("alloc memory Fail");
}
});
IMemoryHeap mMemHeap = new MemoryHeapBase(size+1024, 0, "Heap Name");
if (mMemHeap->getHeapID() < 0) {
return;
}
IMemory* mMemBase = new MemoryBase(mMemHeap, 0, size+1024);
//void * addr = mMemHeap ->base();
if (mMemBase != nullptr) {
memcpy(mMemBase->base(), data, size);
}*/
// if (r.isOk() && allocRes) {
// int shmid = 1234;///copyShareMemory(data, size);
int shmid = copyShareMemory(data, size);
if (shmid < 0) {
SUBTITLE_LOGE("Fail to process share memory!!");
return true;
}
android::AutoMutex _l(mLock);
std::unique_ptr<SubtitleData> subtitleData(new SubtitleData);
subtitleData->type = type;
subtitleData->x = x;
subtitleData->y = y;
subtitleData->width = width;
subtitleData->height = height;
subtitleData->videoWidth = videoWidth;
subtitleData->videoHeight = videoHeight;
subtitleData->size = size;
subtitleData->isShow = cmd;
// subtitleData->mem = mMemBase;
subtitleData->objectSegmentId =
objectSegmentId; // objectSegmentId: the current number object segment object.
subtitleData->shmid = shmid;
mSubtitleData.push_back(std::move(subtitleData));
mLooper->sendMessage(this, Message(MSG_CHECK_SUBDATA));
SUBTITLE_LOGI("in postDisplayData: type:%d, width=%d, height=%d size=%d objectSegmentId=%d",
type, width, height, size, objectSegmentId);
return true;
}
} // namespace amlogic