/* * 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 #include #include #include #include #include #include #include #include #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::sInstance = nullptr; sp AndroidCallbackMessageQueue::Instance() { return sInstance; } AndroidCallbackMessageQueue::AndroidCallbackMessageQueue(sp 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 data(nullptr); mLock.lock(); if (mSubtitleData.size() > 0) { auto iter = mSubtitleData.begin(); std::unique_ptr 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(); 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) { 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(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(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(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 = std::make_unique(); std::unique_ptr 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(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(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(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(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(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 mem; sp ashmemAllocator = IAllocator::getService("ashmem"); bool allocRes = false; Return r = ashmemAllocator->allocate(size+1024, [&](bool success, const hidl_memory& _mem) { if (success) { mem = HidlMemory::getInstance(_mem); sp 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(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