1087 lines
36 KiB
C++
1087 lines
36 KiB
C++
/*
|
|
* Copyright (c) 2020 Amlogic, Inc. All rights reserved.
|
|
*
|
|
* This source code is subject to the terms and conditions defined in the
|
|
* file 'LICENSE' which is part of this source code package.
|
|
*
|
|
* Description:
|
|
*/
|
|
|
|
#define LOG_TAG "AmlVMXIptvMediaCas"
|
|
#include <utils/AmlMpLog.h>
|
|
#include <utils/AmlMpUtils.h>
|
|
#include <utils/Amlsysfsutils.h>
|
|
#include <cutils/ashmem.h>
|
|
#include <cutils/properties.h>
|
|
#include "AmlVMXIptvMediaCas.h"
|
|
|
|
#ifndef VMX_CA_SYSTEM_ID
|
|
#define VMX_CA_SYSTEM_ID 0x5601
|
|
#endif
|
|
|
|
#define VMX_PROP_PRE_INIT_MEDIACAS_ENABLE "vendor.media.cas.vmx.pre.init.enable"
|
|
|
|
#define VMX_PROP_PROVISION_SAVE_SERVER_INFO_ENABLE "vendor.media.cas.vmx.save.server.info.enable"
|
|
#define VMX_PROP_PROVISION_SAVE_SERVER_INFO_PATH "vendor.media.cas.vmx.save.server.info.path"
|
|
#define VMX_PROP_PROVISION_SAVE_SERVER_INFO_DEFAULT "/data/misc/media/saved_provision_server_iptv.json"
|
|
|
|
#define VMX_PROP_PROVISION_SERVER_IP_PRIMARY "vendor.media.cas.vmx.iptv.serverip.primary"
|
|
#define VMX_PROP_PROVISION_SERVER_IP_SECONDARY "vendor.media.cas.vmx.iptv.serverip.secondary"
|
|
#define VMX_PROP_PROVISION_INVALID_VALUE " "
|
|
#define VMX_PROP_PROVISION_SERVER_IP_FORCE "vendor.media.cas.vmx.iptv.serverip.force"
|
|
|
|
#define VMX_PROP_PROVISION_SERVER_PORT "vendor.media.cas.vmx.iptv.serverport"
|
|
#define VMX_PROP_PROVISION_STORE_PATH "vendor.media.cas.vmx.iptv.storepath"
|
|
#define VMX_PROP_PROVISION_KID "vendor.media.cas.vmx.iptv.kid"
|
|
#define VMX_PROP_PROVISION_STB_ID "vendor.media.cas.vmx.iptv.stbid"
|
|
#define VMX_PROP_PROVISION_CHIP_ID "vendor.media.cas.vmx.iptv.chipid"
|
|
#define VMX_PROP_PROVISION_IS_ONLINE "vendor.media.cas.vmx.iptv.isonline"
|
|
#define VMX_PROP_PROVISION_TIMEOUT "vendor.media.cas.vmx.iptv.timeout"
|
|
#define VMX_PROP_PROVISION_RETRY_COUNT "vendor.media.cas.vmx.iptv.retrycount"
|
|
#define VMX_PROP_PROVISION_IP_VERSION "vendor.media.cas.vmx.iptv.ipversion"
|
|
|
|
#define VMX_PROP_PROVISION_SERVER_IP_PRIMARY_DEFAULT "client-test-3.verimatrix.com"
|
|
#define VMX_PROP_PROVISION_SERVER_IP_SECONDARY_DEFAULT "client-test-4.verimatrix.com"
|
|
#define VMX_PROP_PROVISION_SERVER_PORT_DEFAULT "12686"
|
|
#define VMX_PROP_PROVISION_STORE_PATH_DEFAULT "/data/vendor/mediacas/Verimatrix.store"
|
|
#define VMX_PROP_PROVISION_KID_DEFAULT ""
|
|
#define VMX_PROP_PROVISION_STB_ID_DEFAULT ""
|
|
#define VMX_PROP_PROVISION_CHIP_ID_DEFAULT ""
|
|
#define VMX_PROP_PROVISION_IS_ONLINE_DEFAULT "true"
|
|
#define VMX_PROP_PROVISION_TIMEOUT_DEFAULT "2"
|
|
#define VMX_PROP_PROVISION_RETRY_COUNT_DEFAULT "2"
|
|
#define VMX_PROP_PROVISION_IP_VERSION_DEFAULT "0"
|
|
|
|
static const char* mName = LOG_TAG;
|
|
|
|
#define RETURN_ERROR_IF_NULLPTR(ptr) do { \
|
|
if (ptr == nullptr) { \
|
|
MLOGE("func:%s, line:%d, %s is nullptr", __FUNCTION__, __LINE__, #ptr); \
|
|
return VMX_CAS_ERROR;} \
|
|
}while(0)
|
|
|
|
namespace aml_mp {
|
|
|
|
typedef enum
|
|
{
|
|
VMX_CAS_SUCCESS = 0,
|
|
VMX_CAS_ERROR = 1,
|
|
VMX_CAS_GET_SERVICE_FAIL = 2,
|
|
VMX_CAS_UNSUPPORT = 3,
|
|
VMX_CAS_CREATE_PLUGIN_FAIL = 4,
|
|
|
|
//provision status
|
|
VMX_CAS_PROVISION_SUC = 1010,
|
|
VMX_CAS_PROVISION_FAIL = 1011,
|
|
|
|
VMX_CAS_SERVER_UNAVAILABLE = 1012,
|
|
} VMXCasState;
|
|
|
|
typedef struct {
|
|
//create a thread to do initialization to optimize first play KPI
|
|
pthread_t initThread;
|
|
int initThreadExited;
|
|
std::mutex initThreadLock;
|
|
|
|
//provision server
|
|
std::string currentProvisionServer;
|
|
std::string primaryServer;
|
|
std::string secondaryServer;
|
|
|
|
//make sure there is only one initialization process
|
|
std::mutex initLock;
|
|
|
|
//event listener for vmx cas
|
|
shared_ptr<MediaCasListener> listener;
|
|
|
|
//vmx cas plugin
|
|
shared_ptr<ICas> iCas;
|
|
|
|
shared_ptr<IMediaCasService> casService;
|
|
|
|
SpAIBinder casBinder;
|
|
} VMXCasInitCtrl;
|
|
|
|
static VMXCasInitCtrl vmxCasInitCtrl = {
|
|
.initThread = 0,
|
|
.initThreadExited = 0,
|
|
.currentProvisionServer = VMX_PROP_PROVISION_INVALID_VALUE,
|
|
.primaryServer = VMX_PROP_PROVISION_INVALID_VALUE,
|
|
.secondaryServer = VMX_PROP_PROVISION_INVALID_VALUE,
|
|
.listener = nullptr,
|
|
.iCas = nullptr,
|
|
.casService = nullptr
|
|
};
|
|
|
|
inline unsigned int convertSessionIdtoInt(const std::vector<uint8_t> & sessionId)
|
|
{
|
|
unsigned int cnt = 0;
|
|
unsigned int result = 0;
|
|
for (auto d : sessionId) {
|
|
result = (d << (8 * cnt)) | result;
|
|
cnt++;
|
|
if (cnt >= 4) {
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void getProperty(const char* propertyName, std::string& value)
|
|
{
|
|
std::string name = propertyName;
|
|
char bufs[PROP_VALUE_MAX] = {0};
|
|
|
|
#ifdef __linux__
|
|
#ifndef ANDROID
|
|
for (auto it = name.begin(); it != name.end(); ++it) {
|
|
if (*it == '.') {
|
|
*it = '_';
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
if (property_get(name.c_str(), bufs, 0) > 0) {
|
|
value = bufs;
|
|
}
|
|
}
|
|
|
|
static void printVector(const char *name, const std::vector<uint8_t> &data)
|
|
{
|
|
const size_t len = data.size();
|
|
const size_t hexLen = len * 2 + 1;
|
|
|
|
unsigned char* hexString = new unsigned char[hexLen];
|
|
if (!hexString) {
|
|
MLOGE("new fail");
|
|
return;
|
|
}
|
|
|
|
const char hexChars[] = "0123456789ABCDEF";
|
|
for (size_t i = 0; i < len; ++i) {
|
|
uint8_t byte = data[i];
|
|
hexString[i * 2] = static_cast<unsigned char>(hexChars[byte >> 4]); // high 4 bits
|
|
hexString[i * 2 + 1] = static_cast<unsigned char>(hexChars[byte & 0x0F]); // low 4 bits
|
|
}
|
|
|
|
hexString[hexLen - 1] = '\0';
|
|
MLOGI("%s: 0x%s", name, hexString);
|
|
delete []hexString;
|
|
}
|
|
|
|
static std::string convertVertorToHexString(const std::vector<uint8_t> &data)
|
|
{
|
|
const size_t len = data.size();
|
|
const size_t hexLen = len * 2 + 1;
|
|
|
|
unsigned char* hexString = new unsigned char[hexLen];
|
|
if (!hexString) {
|
|
MLOGE("new fail");
|
|
std::string resultTmp;
|
|
return resultTmp;
|
|
}
|
|
|
|
const char hexChars[] = "0123456789ABCDEF";
|
|
for (size_t i = 0; i < len; ++i) {
|
|
uint8_t byte = data[i];
|
|
hexString[i * 2] = static_cast<unsigned char>(hexChars[byte >> 4]); // high 4 bits
|
|
hexString[i * 2 + 1] = static_cast<unsigned char>(hexChars[byte & 0x0F]); // low 4 bits
|
|
}
|
|
|
|
hexString[hexLen - 1] = '\0';
|
|
std::string result(reinterpret_cast<const char*>(hexString));
|
|
delete []hexString;
|
|
return result;
|
|
}
|
|
|
|
static void storeProvisionServerToJson(bool isProvisionSuc, const std::string provisionServer)
|
|
{
|
|
std::string storeServerProp = "0";
|
|
getProperty(VMX_PROP_PROVISION_SAVE_SERVER_INFO_ENABLE, storeServerProp);
|
|
if (storeServerProp != "1") {
|
|
MLOGI("vendor.media.cas.vmx.save.server.info.enable is not 1, skip storing provision server");
|
|
return;
|
|
}
|
|
|
|
std::string storeServerPath = VMX_PROP_PROVISION_SAVE_SERVER_INFO_DEFAULT;
|
|
getProperty(VMX_PROP_PROVISION_SAVE_SERVER_INFO_PATH, storeServerPath);
|
|
|
|
//use "w" mode to clear all data
|
|
FILE* file = fopen(storeServerPath.c_str(), "w");
|
|
if (file == nullptr) {
|
|
MLOGE("Failed to open file %s, %s", storeServerPath.c_str(), strerror(errno));
|
|
return;
|
|
}
|
|
|
|
if (!isProvisionSuc) {
|
|
fclose(file);
|
|
MLOGI("Clear provision server info json");
|
|
return;
|
|
}
|
|
fprintf(file, "{\"serverAddr\":\"%s\"}", provisionServer.c_str());
|
|
fclose(file);
|
|
MLOGI("Stored provision server: %s to %s", provisionServer.c_str(), storeServerPath.c_str());
|
|
}
|
|
|
|
static void getProvisionServer(std::string& primaryServer, std::string& secondaryServer)
|
|
{
|
|
primaryServer = VMX_PROP_PROVISION_SERVER_IP_PRIMARY_DEFAULT;
|
|
getProperty(VMX_PROP_PROVISION_SERVER_IP_PRIMARY, primaryServer);
|
|
secondaryServer = VMX_PROP_PROVISION_SERVER_IP_SECONDARY_DEFAULT;
|
|
getProperty(VMX_PROP_PROVISION_SERVER_IP_SECONDARY, secondaryServer);
|
|
}
|
|
|
|
static string getVmxProvisionString(const std::string provisionServer)
|
|
{
|
|
//The provisionString contains the necessary provisioning information.
|
|
//it contain:
|
|
//bskid, stbid, chipid, ipaddress:port, storefile location, isOnline,timeout, retries, and ipVersion
|
|
//sample:
|
|
//,,,client-test-3.verimatrix.com:12686,/data/vendor/mediacas/Verimatrix.store,true,2,2,0
|
|
|
|
std::string propertyValue;
|
|
std::string provisionStr;
|
|
provisionStr.clear();
|
|
|
|
propertyValue = VMX_PROP_PROVISION_KID_DEFAULT;
|
|
getProperty(VMX_PROP_PROVISION_KID, propertyValue);
|
|
provisionStr.append(propertyValue);
|
|
provisionStr.append(",");
|
|
|
|
propertyValue = VMX_PROP_PROVISION_STB_ID_DEFAULT;
|
|
getProperty(VMX_PROP_PROVISION_STB_ID, propertyValue);
|
|
provisionStr.append(propertyValue);
|
|
provisionStr.append(",");
|
|
|
|
propertyValue = VMX_PROP_PROVISION_CHIP_ID_DEFAULT;
|
|
getProperty(VMX_PROP_PROVISION_CHIP_ID, propertyValue);
|
|
provisionStr.append(propertyValue);
|
|
provisionStr.append(",");
|
|
|
|
provisionStr.append(provisionServer);
|
|
provisionStr.append(":");
|
|
|
|
propertyValue = VMX_PROP_PROVISION_SERVER_PORT_DEFAULT;
|
|
getProperty(VMX_PROP_PROVISION_SERVER_PORT, propertyValue);
|
|
provisionStr.append(propertyValue);
|
|
provisionStr.append(",");
|
|
|
|
propertyValue = VMX_PROP_PROVISION_STORE_PATH_DEFAULT;
|
|
getProperty(VMX_PROP_PROVISION_STORE_PATH, propertyValue);
|
|
provisionStr.append(propertyValue);
|
|
provisionStr.append(",");
|
|
|
|
propertyValue = VMX_PROP_PROVISION_IS_ONLINE_DEFAULT;
|
|
getProperty(VMX_PROP_PROVISION_IS_ONLINE, propertyValue);
|
|
provisionStr.append(propertyValue);
|
|
provisionStr.append(",");
|
|
|
|
propertyValue = VMX_PROP_PROVISION_TIMEOUT_DEFAULT;
|
|
getProperty(VMX_PROP_PROVISION_TIMEOUT, propertyValue);
|
|
provisionStr.append(propertyValue);
|
|
provisionStr.append(",");
|
|
|
|
propertyValue = VMX_PROP_PROVISION_RETRY_COUNT_DEFAULT;
|
|
getProperty(VMX_PROP_PROVISION_RETRY_COUNT, propertyValue);
|
|
provisionStr.append(propertyValue);
|
|
provisionStr.append(",");
|
|
|
|
propertyValue = VMX_PROP_PROVISION_IP_VERSION_DEFAULT;
|
|
getProperty(VMX_PROP_PROVISION_IP_VERSION, propertyValue);
|
|
provisionStr.append(propertyValue);
|
|
|
|
return provisionStr;
|
|
}
|
|
|
|
static int checkNetPhyConnectStatus(void)
|
|
{
|
|
const char* netEth0Carrier = "/sys/class/net/eth0/carrier";
|
|
const char* netWlan0Carrier = "/sys/class/net/wlan0/carrier";
|
|
int isEthConn = amsysfs_get_sysfs_int(netEth0Carrier);
|
|
int isWifiConn = amsysfs_get_sysfs_int(netWlan0Carrier);
|
|
if (isEthConn > 0 || isWifiConn > 0) {
|
|
return VMX_CAS_SUCCESS;
|
|
}
|
|
return VMX_CAS_ERROR;
|
|
}
|
|
|
|
static int vmxProvision(const std::string provisionServer, int & provisionResult)
|
|
{
|
|
int ret = VMX_CAS_ERROR;
|
|
provisionResult = VMX_CAS_PROVISION_FAIL;
|
|
RETURN_ERROR_IF_NULLPTR(vmxCasInitCtrl.iCas);
|
|
std::string provisionStr = getVmxProvisionString(provisionServer);
|
|
MLOGI("provision string:%s", provisionStr.c_str());
|
|
auto status = vmxCasInitCtrl.iCas->provision(provisionStr);
|
|
if (!status.isOk()) {
|
|
MLOGE("provision error, ServiceSpecificError:%d", status.getServiceSpecificError());
|
|
if (status.getServiceSpecificError() == aidl::android::hardware::cas::Status::ERROR_CAS_NOT_PROVISIONED) {
|
|
MLOGE("not able to complete provision");
|
|
provisionResult = VMX_CAS_PROVISION_FAIL;
|
|
ret = VMX_CAS_ERROR;
|
|
} else if (status.getServiceSpecificError() == aidl::android::hardware::cas::Status::ERROR_CAS_DEVICE_REVOKED) {
|
|
MLOGE("no connection to VCAS server");
|
|
provisionResult = VMX_CAS_SERVER_UNAVAILABLE;
|
|
ret = VMX_CAS_ERROR;
|
|
} else {
|
|
MLOGE("provision fail, unknown reason");
|
|
provisionResult = VMX_CAS_PROVISION_FAIL;
|
|
ret = VMX_CAS_ERROR;
|
|
}
|
|
} else {
|
|
ret = VMX_CAS_SUCCESS;
|
|
provisionResult = VMX_CAS_PROVISION_SUC;
|
|
MLOGI("vmx provision success");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void reportProvisionResult(int provisionResult)
|
|
{
|
|
std::vector<uint8_t> dataTmp;
|
|
shared_ptr<MediaCasListener> listener = vmxCasInitCtrl.listener;
|
|
if (listener != nullptr) {
|
|
listener->onEvent(provisionResult, 0, dataTmp);
|
|
}
|
|
}
|
|
|
|
static VMXCasState createVmxMediaCasPlugin(AmlVMXIptvMediaCas *instance)
|
|
{
|
|
if (vmxCasInitCtrl.listener == nullptr) {
|
|
vmxCasInitCtrl.listener = SharedRefBase::make<MediaCasListener>();
|
|
if (vmxCasInitCtrl.listener == nullptr) {
|
|
MLOGE("new MediaCasListener fail");
|
|
} else {
|
|
if (instance != nullptr) {
|
|
vmxCasInitCtrl.listener->setEventCallback(instance->mCallBack, instance->mUserData);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (AServiceManager_isDeclared("android.hardware.cas.IMediaCasService/default")) {
|
|
MLOGI("AServiceManager_waitForService");
|
|
SpAIBinder binder(AServiceManager_waitForService("android.hardware.cas.IMediaCasService/default"));
|
|
vmxCasInitCtrl.casBinder = binder;
|
|
vmxCasInitCtrl.casService = IMediaCasService::fromBinder(vmxCasInitCtrl.casBinder);
|
|
} else {
|
|
vmxCasInitCtrl.casService = nullptr;
|
|
}
|
|
if (vmxCasInitCtrl.casService == nullptr) {
|
|
MLOGE("Get Cas service fail");
|
|
return VMX_CAS_GET_SERVICE_FAIL;
|
|
} else {
|
|
MLOGI("Get Cas service: %p", vmxCasInitCtrl.casService.get());
|
|
}
|
|
|
|
bool is_supported = false;
|
|
auto status = vmxCasInitCtrl.casService->isSystemIdSupported(VMX_CA_SYSTEM_ID, &is_supported);
|
|
if (!status.isOk() || !is_supported) {
|
|
MLOGE("Not support VMX cas");
|
|
return VMX_CAS_UNSUPPORT;
|
|
}
|
|
|
|
auto statusTmp = vmxCasInitCtrl.casService->createPlugin(VMX_CA_SYSTEM_ID, vmxCasInitCtrl.listener, &(vmxCasInitCtrl.iCas));
|
|
if (!statusTmp.isOk() || !(vmxCasInitCtrl.iCas)) {
|
|
MLOGE("Create plugin failed!");
|
|
return VMX_CAS_CREATE_PLUGIN_FAIL;
|
|
}
|
|
|
|
MLOGI("VMX Plugin Created, %p", vmxCasInitCtrl.iCas.get());
|
|
return VMX_CAS_SUCCESS;
|
|
}
|
|
|
|
static void releaseVmxMediaCasPlugin(void)
|
|
{
|
|
if (vmxCasInitCtrl.iCas != nullptr) {
|
|
MLOGI("VMX Plugin Release, %p", vmxCasInitCtrl.iCas.get());
|
|
auto status = vmxCasInitCtrl.iCas->release();
|
|
if (!status.isOk()) {
|
|
MLOGE("ICas release fail");
|
|
}
|
|
vmxCasInitCtrl.iCas = nullptr;
|
|
}
|
|
vmxCasInitCtrl.listener = nullptr;
|
|
vmxCasInitCtrl.casService = nullptr;
|
|
vmxCasInitCtrl.casBinder.set(nullptr);
|
|
}
|
|
|
|
static VMXCasState InitVmxMediaCas(AmlVMXIptvMediaCas *instance)
|
|
{
|
|
int provisionResult;
|
|
std::string forceServerIp = VMX_PROP_PROVISION_INVALID_VALUE;
|
|
std::lock_guard<std::mutex> lock(vmxCasInitCtrl.initLock);
|
|
if (vmxCasInitCtrl.iCas != nullptr) {
|
|
getProperty(VMX_PROP_PROVISION_SERVER_IP_FORCE, forceServerIp);
|
|
if (forceServerIp != VMX_PROP_PROVISION_INVALID_VALUE && \
|
|
forceServerIp != vmxCasInitCtrl.currentProvisionServer) {
|
|
releaseVmxMediaCasPlugin();
|
|
//when VMX_PROP_PROVISION_SERVER_IP_FORCE is set, set primaryServer and
|
|
//secondaryServer to same value.
|
|
vmxCasInitCtrl.primaryServer = forceServerIp;
|
|
vmxCasInitCtrl.secondaryServer = forceServerIp;
|
|
vmxCasInitCtrl.currentProvisionServer = VMX_PROP_PROVISION_INVALID_VALUE;
|
|
} else {
|
|
MLOGI("gICvmxCasInitCtrl.iCas inited");
|
|
return VMX_CAS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
VMXCasState retCreatePlugin = createVmxMediaCasPlugin(instance);
|
|
if (retCreatePlugin != VMX_CAS_SUCCESS) {
|
|
return retCreatePlugin;
|
|
}
|
|
|
|
if (VMX_CAS_SUCCESS != checkNetPhyConnectStatus()) {
|
|
MLOGI("Network is not connected");
|
|
releaseVmxMediaCasPlugin();
|
|
return VMX_CAS_SERVER_UNAVAILABLE;
|
|
}
|
|
|
|
getProperty(VMX_PROP_PROVISION_SERVER_IP_FORCE, forceServerIp);
|
|
if (forceServerIp != VMX_PROP_PROVISION_INVALID_VALUE) {
|
|
vmxCasInitCtrl.primaryServer = forceServerIp;
|
|
vmxCasInitCtrl.secondaryServer = forceServerIp;
|
|
vmxCasInitCtrl.currentProvisionServer = VMX_PROP_PROVISION_INVALID_VALUE;
|
|
} else if (vmxCasInitCtrl.primaryServer == VMX_PROP_PROVISION_INVALID_VALUE || \
|
|
vmxCasInitCtrl.secondaryServer == VMX_PROP_PROVISION_INVALID_VALUE) {
|
|
getProvisionServer(vmxCasInitCtrl.primaryServer, vmxCasInitCtrl.secondaryServer);
|
|
}
|
|
|
|
MLOGI("Primary Server:%s, Secondary Server:%s", vmxCasInitCtrl.primaryServer.c_str(),
|
|
vmxCasInitCtrl.secondaryServer.c_str());
|
|
int ret = vmxProvision(vmxCasInitCtrl.primaryServer, provisionResult);
|
|
if (ret == VMX_CAS_SUCCESS) {
|
|
//Primary Server Provision Success
|
|
vmxCasInitCtrl.currentProvisionServer = vmxCasInitCtrl.primaryServer;
|
|
storeProvisionServerToJson(true, vmxCasInitCtrl.primaryServer);
|
|
reportProvisionResult(provisionResult);
|
|
return VMX_CAS_SUCCESS;
|
|
} else {
|
|
//Primary Server Provision Fail
|
|
if (vmxCasInitCtrl.secondaryServer != VMX_PROP_PROVISION_INVALID_VALUE &&
|
|
vmxCasInitCtrl.secondaryServer != vmxCasInitCtrl.primaryServer) {
|
|
releaseVmxMediaCasPlugin();
|
|
retCreatePlugin = createVmxMediaCasPlugin(instance);
|
|
if (retCreatePlugin != VMX_CAS_SUCCESS) {
|
|
reportProvisionResult(provisionResult);
|
|
return retCreatePlugin;
|
|
}
|
|
ret = vmxProvision(vmxCasInitCtrl.secondaryServer, provisionResult);
|
|
if (ret == VMX_CAS_SUCCESS) {
|
|
vmxCasInitCtrl.currentProvisionServer = vmxCasInitCtrl.secondaryServer;
|
|
storeProvisionServerToJson(true, vmxCasInitCtrl.secondaryServer);
|
|
reportProvisionResult(provisionResult);
|
|
return VMX_CAS_SUCCESS;
|
|
} else {
|
|
reportProvisionResult(provisionResult);
|
|
releaseVmxMediaCasPlugin();
|
|
vmxCasInitCtrl.currentProvisionServer = VMX_PROP_PROVISION_INVALID_VALUE;
|
|
storeProvisionServerToJson(false, vmxCasInitCtrl.secondaryServer);
|
|
return VMX_CAS_ERROR;
|
|
}
|
|
} else {
|
|
reportProvisionResult(provisionResult);
|
|
releaseVmxMediaCasPlugin();
|
|
return VMX_CAS_ERROR;
|
|
}
|
|
}
|
|
|
|
return VMX_CAS_SUCCESS;
|
|
}
|
|
|
|
static void *VmxInitThread(void *arg)
|
|
{
|
|
AML_MP_UNUSED(arg);
|
|
//Set max try count to avoid endless attempts.
|
|
const int MAX_TRY_COUNT = 30;
|
|
MLOGI("VmxInitThread start");
|
|
int currentTryCnt = 0;
|
|
while (!vmxCasInitCtrl.initThreadExited) {
|
|
VMXCasState ret = InitVmxMediaCas(nullptr);
|
|
if (VMX_CAS_SUCCESS == ret) {
|
|
MLOGI("InitVmxMediaCas success, try count:%d", currentTryCnt);
|
|
vmxCasInitCtrl.initThreadExited = 1;
|
|
} else if (VMX_CAS_UNSUPPORT == ret) {
|
|
MLOGI("InitVmxMediaCas Not Support");
|
|
vmxCasInitCtrl.initThreadExited = 1;
|
|
} else {
|
|
currentTryCnt++;
|
|
MLOGE("InitVmxMediaCas fail, try again, ret:%d, try count:%d", ret, currentTryCnt);
|
|
if (currentTryCnt > MAX_TRY_COUNT) {
|
|
vmxCasInitCtrl.initThreadExited = 1;
|
|
MLOGE("InitVmxMediaCas reach max count");
|
|
break;
|
|
}
|
|
//wait 5 seconds and try again.
|
|
usleep(5000 * 1000);
|
|
}
|
|
}
|
|
MLOGI("VmxInitThread exit");
|
|
return NULL;
|
|
}
|
|
|
|
void PreInitVmxMediaCas(void)
|
|
{
|
|
int ret;
|
|
|
|
std::string preInitEnable = "0";
|
|
getProperty(VMX_PROP_PRE_INIT_MEDIACAS_ENABLE, preInitEnable);
|
|
if (preInitEnable == "0") {
|
|
MLOGI("pre init for mediacas is disable");
|
|
return;
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock(vmxCasInitCtrl.initThreadLock);
|
|
if (vmxCasInitCtrl.initThread != 0) {
|
|
MLOGI("vmx init thread already created");
|
|
return;
|
|
}
|
|
vmxCasInitCtrl.initThread = 0;
|
|
vmxCasInitCtrl.initThreadExited = 0;
|
|
ret = pthread_create(&(vmxCasInitCtrl.initThread), NULL, VmxInitThread, NULL);
|
|
if (ret < 0)
|
|
MLOGE("Create vmx init thread failed! ret=%d", ret);
|
|
MLOGI("gVmxInitThread:0x%lx", vmxCasInitCtrl.initThread);
|
|
}
|
|
|
|
void DeinitVmxMediaCas(void)
|
|
{
|
|
MLOGI("DeinitVmxMediaCas");
|
|
{
|
|
std::lock_guard<std::mutex> lock(vmxCasInitCtrl.initThreadLock);
|
|
vmxCasInitCtrl.initThreadExited = 1;
|
|
if (vmxCasInitCtrl.initThread != 0) {
|
|
pthread_join(vmxCasInitCtrl.initThread, NULL);
|
|
}
|
|
vmxCasInitCtrl.initThread = 0;
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock(vmxCasInitCtrl.initLock);
|
|
releaseVmxMediaCasPlugin();
|
|
vmxCasInitCtrl.currentProvisionServer = VMX_PROP_PROVISION_INVALID_VALUE;
|
|
vmxCasInitCtrl.primaryServer = VMX_PROP_PROVISION_INVALID_VALUE;
|
|
vmxCasInitCtrl.secondaryServer = VMX_PROP_PROVISION_INVALID_VALUE;
|
|
}
|
|
|
|
ScopedAStatus MediaCasListener::onEvent(int32_t event, int32_t arg,
|
|
const std::vector<uint8_t>& data) {
|
|
android::Mutex::Autolock autoLock(mLock);
|
|
mEvent = event;
|
|
mEventArg = arg;
|
|
mEventData = data;
|
|
MLOGI("onEvent, event:%d, arg:%d", event, arg);
|
|
printVector("onEvent", data);
|
|
switch (event) {
|
|
default:
|
|
MLOGE("Event:Unknown event %d", event);
|
|
}
|
|
|
|
std::vector<uint8_t> unusedSessionId;
|
|
reportEvent("Event", event, arg, data, unusedSessionId);
|
|
return ScopedAStatus::ok();
|
|
}
|
|
|
|
ScopedAStatus MediaCasListener::onSessionEvent(const vector<uint8_t>& sessionId, int32_t event,
|
|
int32_t arg, const vector<uint8_t>& data) {
|
|
android::Mutex::Autolock autoLock(mLock);
|
|
|
|
mSessionId = sessionId;
|
|
mEvent = event;
|
|
mEventArg = arg;
|
|
mEventData = data;
|
|
printVector("sessionId", sessionId);
|
|
MLOGI("onSessionEvent, event:%d, arg:%d", event, arg);
|
|
printVector("onSessionEvent", data);
|
|
if (event == 4) {
|
|
if (arg == 64) {
|
|
MLOGI("VCI Process: VOD movie is not active");
|
|
}
|
|
}
|
|
reportEvent("SessionEvent", event, arg, data, sessionId);
|
|
return ScopedAStatus::ok();
|
|
}
|
|
ScopedAStatus MediaCasListener::onStatusUpdate(StatusEvent event, int32_t arg) {
|
|
android::Mutex::Autolock autoLock(mLock);
|
|
mStatusEvent = event;
|
|
mEventArg = arg;
|
|
MLOGI("onStatusUpdate, event:0x%hhx, arg:%d", event, arg);
|
|
std::vector<uint8_t> unusedSessionId;
|
|
std::vector<uint8_t> unusedData;
|
|
reportEvent("StatusUpdate", static_cast<int32_t>(event), arg, unusedData, unusedSessionId);
|
|
return ScopedAStatus::ok();
|
|
}
|
|
|
|
int MediaCasListener::setEventCallback(Aml_MP_CAS_EventCallback cb, void* userData)
|
|
{
|
|
android::Mutex::Autolock autoLock(mLock);
|
|
if (mCB == cb && mUserData == userData) {
|
|
return 0;
|
|
}
|
|
mCB = cb;
|
|
mUserData = userData;
|
|
MLOGI("mCB:0x%p, mUserData:0x%p", mCB, mUserData);
|
|
return 0;
|
|
}
|
|
|
|
int MediaCasListener::reportEvent(const char *type, int32_t event, int32_t arg, \
|
|
const vector<uint8_t>& data, const vector<uint8_t>& sessionId)
|
|
{
|
|
if (mCB == NULL) {
|
|
return 0;
|
|
}
|
|
std::string dataStr;
|
|
std::string sessionIdStr;
|
|
size_t jsonLen = 0;
|
|
char* jsonStr = NULL;
|
|
|
|
if (0 == strcmp(type, "Event")) {
|
|
dataStr = convertVertorToHexString(data);
|
|
jsonLen = dataStr.size() + 128;
|
|
} else if (0 == strcmp(type, "SessionEvent")) {
|
|
dataStr = convertVertorToHexString(data);
|
|
sessionIdStr = convertVertorToHexString(sessionId);
|
|
jsonLen = dataStr.size() + sessionIdStr.size() + 128;
|
|
} else if (0 == strcmp(type, "StatusUpdate")) {
|
|
jsonLen = 128;
|
|
} else {
|
|
MLOGE("unsupport type:%s", type);
|
|
return 0;
|
|
}
|
|
|
|
jsonStr = new char[jsonLen];
|
|
if (!jsonStr) {
|
|
MLOGE("new fail");
|
|
return 0;
|
|
}
|
|
memset(jsonStr, 0, jsonLen);
|
|
if (0 == strcmp(type, "Event")) {
|
|
snprintf(jsonStr, jsonLen, "{\"type\":\"%s\", \"event\":\"%d\", \"arg\":\"%d\", \"data\":\"0x%s\"}", \
|
|
type, event, arg, dataStr.c_str());
|
|
} else if (0 == strcmp(type, "SessionEvent")) {
|
|
snprintf(jsonStr, jsonLen, "{\"type\":\"%s\", \"sessionID\":\"0x%s\", \"event\":\"%d\", \"arg\":\"%d\", \"data\":\"0x%s\"}", \
|
|
type, sessionIdStr.c_str(), event, arg, dataStr.c_str());
|
|
} else if (0 == strcmp(type, "StatusUpdate")) {
|
|
snprintf(jsonStr, jsonLen, "{\"type\":\"%s\", \"event\":\"%d\", \"arg\":\"%d\"}", \
|
|
type, event, arg);
|
|
}
|
|
|
|
int ret = mCB(mUserData, jsonStr);
|
|
MLOGI("mCB ret:%d, json:%s", ret, jsonStr);
|
|
if (jsonStr != NULL) {
|
|
delete []jsonStr;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
AmlVMXIptvMediaCas::AmlVMXIptvMediaCas(Aml_MP_CASServiceType serviceType)
|
|
:AmlCasBase(serviceType)
|
|
{
|
|
MLOGI("ctor AmlVMXIptvMediaCas");
|
|
mCaSetKeyThread = 0;
|
|
mLastEcmProcessSuc = 0;
|
|
mEcmData.clear();
|
|
mSessionId.clear();
|
|
CaClearMediacasDsmParamsState(&mMediaCasDsmParams);
|
|
}
|
|
|
|
AmlVMXIptvMediaCas::~AmlVMXIptvMediaCas()
|
|
{
|
|
MLOGI("dtor AmlVMXIptvMediaCas");
|
|
registerEventCallback(NULL, NULL);
|
|
}
|
|
|
|
int AmlVMXIptvMediaCas::registerEventCallback(Aml_MP_CAS_EventCallback cb, void* userData)
|
|
{
|
|
if (vmxCasInitCtrl.listener != nullptr) {
|
|
vmxCasInitCtrl.listener->setEventCallback(cb, userData);
|
|
} else {
|
|
MLOGE("vmxCasInitCtrl.listener not init");
|
|
}
|
|
mCallBack = cb;
|
|
mUserData = userData;
|
|
return 0;
|
|
}
|
|
|
|
bool AmlVMXIptvMediaCas::isSystemIdSupported(int32_t CASystemId)
|
|
{
|
|
shared_ptr<IMediaCasService> casServiceTmp;
|
|
if (AServiceManager_isDeclared("android.hardware.cas.IMediaCasService/default")) {
|
|
SpAIBinder binder(AServiceManager_waitForService("android.hardware.cas.IMediaCasService/default"));
|
|
casServiceTmp = IMediaCasService::fromBinder(binder);
|
|
} else {
|
|
casServiceTmp = nullptr;
|
|
}
|
|
if (casServiceTmp == nullptr) {
|
|
MLOGE("Get Cas service fail");
|
|
return false;
|
|
} else {
|
|
MLOGD("Get Cas service: %p", casServiceTmp.get());
|
|
}
|
|
|
|
bool is_supported = false;
|
|
auto status = casServiceTmp->isSystemIdSupported(CASystemId, &is_supported);
|
|
if (!status.isOk() || !is_supported) {
|
|
MLOGE("Not support VMX cas, %d", CASystemId);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int AmlVMXIptvMediaCas::openSession(int intent, int scramblingMode)
|
|
{
|
|
AML_MP_UNUSED(intent);
|
|
AML_MP_UNUSED(scramblingMode);
|
|
MLOGI("%s", __func__);
|
|
RETURN_ERROR_IF_NULLPTR(vmxCasInitCtrl.iCas);
|
|
vector<uint8_t> sessionId;
|
|
auto ret_status_v = vmxCasInitCtrl.iCas->openSessionDefault(&sessionId);
|
|
if (!ret_status_v.isOk() ) {
|
|
MLOGE("vmx media cas open session error:%s", ret_status_v.getDescription().c_str());
|
|
binder_exception_t exception = ret_status_v.getExceptionCode();
|
|
MLOGE("Exception code: %d", exception);
|
|
return VMX_CAS_ERROR;
|
|
}
|
|
|
|
if (!sessionId.empty()) {
|
|
mSessionId = sessionId;
|
|
}
|
|
printVector("sessionId", mSessionId);
|
|
return VMX_CAS_SUCCESS;
|
|
}
|
|
|
|
int AmlVMXIptvMediaCas::closeSession()
|
|
{
|
|
RETURN_ERROR_IF_NULLPTR(vmxCasInitCtrl.iCas);
|
|
if (mSessionId.empty()) {
|
|
MLOGI("%s mSessionId is empty", __func__);
|
|
return VMX_CAS_SUCCESS;
|
|
}
|
|
printVector("close sessionId", mSessionId);
|
|
|
|
auto status = vmxCasInitCtrl.iCas->closeSession(mSessionId);
|
|
if (!status.isOk()) {
|
|
MLOGE("Close session failed!");
|
|
return VMX_CAS_ERROR;
|
|
}
|
|
mSessionId.clear();
|
|
|
|
return VMX_CAS_SUCCESS;
|
|
}
|
|
|
|
int AmlVMXIptvMediaCas::sendEvent(int32_t event, int32_t arg, std::vector<uint8_t>& data)
|
|
{
|
|
RETURN_ERROR_IF_NULLPTR(vmxCasInitCtrl.iCas);
|
|
auto status = vmxCasInitCtrl.iCas->sendEvent(event, arg, data);
|
|
if (!status.isOk()) {
|
|
MLOGE("sendEvent error");
|
|
return VMX_CAS_ERROR;
|
|
}
|
|
return VMX_CAS_SUCCESS;
|
|
}
|
|
|
|
int AmlVMXIptvMediaCas::sendSessionEvent(int32_t event, int32_t arg, std::vector<uint8_t>& data)
|
|
{
|
|
RETURN_ERROR_IF_NULLPTR(vmxCasInitCtrl.iCas);
|
|
auto status = vmxCasInitCtrl.iCas->sendSessionEvent(mSessionId, event, arg, data);
|
|
if (!status.isOk()) {
|
|
MLOGE("sendSessionEvent error");
|
|
return VMX_CAS_ERROR;
|
|
}
|
|
return VMX_CAS_SUCCESS;
|
|
}
|
|
|
|
int AmlVMXIptvMediaCas::startDescrambling(const Aml_MP_IptvCASParams* params)
|
|
{
|
|
MLOG();
|
|
int ret;
|
|
AmlCasBase::startDescrambling(params);
|
|
MLOGI("videoCodec:%d, audioCodec:%d, caSystemId:0x%x, videoPid:0x%x, audioPid:0x%x, ecmPid[0]:0x%x, ecmPid[1]:0x%x", \
|
|
params->videoCodec, params->audioCodec, params->caSystemId, params->videoPid,
|
|
params->audioPid, params->ecmPid[0], params->ecmPid[1]);
|
|
|
|
VMXCasState vmxState = InitVmxMediaCas(this);
|
|
if (VMX_CAS_SUCCESS != vmxState) {
|
|
MLOGE("InitVmxMediaCas fail, vmxState:%d", vmxState);
|
|
if (vmxState == VMX_CAS_SERVER_UNAVAILABLE) {
|
|
shared_ptr<MediaCasListener> listener = vmxCasInitCtrl.listener;
|
|
std::vector<uint8_t> dataTmp;
|
|
MLOGE("no connection to VCAS server");
|
|
if (listener != nullptr) {
|
|
listener->onEvent(VMX_CAS_SERVER_UNAVAILABLE, 0, dataTmp);
|
|
}
|
|
}
|
|
return VMX_CAS_ERROR;
|
|
}
|
|
|
|
if (VMX_CAS_SUCCESS != openSession(0, 1)) {
|
|
MLOGE("Open session failed!");
|
|
return VMX_CAS_ERROR;
|
|
}
|
|
|
|
CaClearMediacasDsmParamsState(&mMediaCasDsmParams);
|
|
|
|
mMediaCasDsmParams.dev_no = params->demuxId;
|
|
mMediaCasDsmParams.vid = params->videoPid;
|
|
mMediaCasDsmParams.aid[0] = params->audioPid;
|
|
mMediaCasDsmParams.mediacasSessionId = convertSessionIdtoInt(mSessionId);
|
|
|
|
bool dsm_ret = caDsmOpenSession(mMediaCasDsmParams.dev_no, mMediaCasDsmParams.mediacasSessionId, mMediaCasDsmParams.mDsmFd);
|
|
if (dsm_ret == false) {
|
|
MLOGE("caDsmOpenSession fail!");
|
|
return VMX_CAS_ERROR;
|
|
}
|
|
|
|
ret = pthread_create(&mCaSetKeyThread, NULL, caSetKey, this);
|
|
if (ret < 0)
|
|
MLOGI("Create ca set key thread failed! ret=%d", ret);
|
|
MLOGI("mCaSetKeyThread:0x%lx", mCaSetKeyThread);
|
|
|
|
return VMX_CAS_SUCCESS;
|
|
}
|
|
|
|
int AmlVMXIptvMediaCas::stopDescrambling()
|
|
{
|
|
MLOGI("stopDescrambling");
|
|
{
|
|
std::lock_guard<std::mutex> lock(mMediaCasDsmLock);
|
|
|
|
if (mMediaCasDsmParams.hasAllocDscChannels == true) {
|
|
MLOGI("caFreeDscChannels hasAllocDscChannels %d", mMediaCasDsmParams.hasAllocDscChannels);
|
|
caFreeDscChannels(&mMediaCasDsmParams);
|
|
}
|
|
caDsmCloseSession(&mMediaCasDsmParams);
|
|
CaClearMediacasDsmParamsState(&mMediaCasDsmParams);
|
|
}
|
|
|
|
if (mCaSetKeyThread != 0) {
|
|
void *thread_result;
|
|
int ret = pthread_join(mCaSetKeyThread, &thread_result);
|
|
if (ret != 0) {
|
|
MLOGE("pthread_join fail:%d", ret);
|
|
}
|
|
mCaSetKeyThread = 0;
|
|
}
|
|
|
|
mLastEcmProcessSuc = 0;
|
|
mEcmData.clear();
|
|
mLastFailedEcmData.clear();
|
|
mLastFailedTimeMs = 0;
|
|
|
|
closeSession();
|
|
|
|
std::string preInitEnable = "0";
|
|
getProperty(VMX_PROP_PRE_INIT_MEDIACAS_ENABLE, preInitEnable);
|
|
if (preInitEnable == "0") {
|
|
//if pre-init for VMX media cas is disable,
|
|
//release VMX media cas plugin.
|
|
DeinitVmxMediaCas();
|
|
}
|
|
|
|
return VMX_CAS_SUCCESS;
|
|
}
|
|
|
|
void* AmlVMXIptvMediaCas::caSetKey(void *arg)
|
|
{
|
|
AmlVMXIptvMediaCas *mediaCas = (AmlVMXIptvMediaCas*)arg;
|
|
bool caReady = false;
|
|
int num = 0;
|
|
bool ret = false;
|
|
|
|
while (mediaCas && !caReady) {
|
|
if (num > 0 && num % 100 == 0)
|
|
MLOGI("Waiting for CA ready %ds", num/100);
|
|
std::lock_guard<std::mutex> lock(mediaCas->mMediaCasDsmLock);
|
|
if (mediaCas->mMediaCasDsmParams.mDsmFd == -1) {
|
|
MLOGI("Exit ca set key thread!");
|
|
caReady = false;
|
|
break;
|
|
}
|
|
|
|
caReady = isDsmDecSlotReady(mediaCas->mMediaCasDsmParams.mDsmFd);
|
|
if (caReady) {
|
|
MLOGI("CA is ready! num:%d", num);
|
|
break;
|
|
}
|
|
usleep(10 * 1000);
|
|
num ++;
|
|
}
|
|
|
|
if (caReady == false) {
|
|
MLOGE("CA is not ready!");
|
|
return NULL;
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock2(mediaCas->mMediaCasDsmLock);
|
|
|
|
Aml_MP_MediaCasDsmParams *params = &(mediaCas->mMediaCasDsmParams);
|
|
|
|
ret = getKeySlots(mediaCas->mMediaCasDsmParams.mDsmFd, params);
|
|
if (ret == false) {
|
|
MLOGE("getKeySlots fail");
|
|
return NULL;
|
|
}
|
|
|
|
params->mDscType = CA_DSC_COMMON_TYPE;
|
|
MLOGI("mKeyslotList count:%d", params->mKeyslotList.count);
|
|
for (uint32_t i = 0; i < params->mKeyslotList.count; i++) {
|
|
if (params->mKeyslotList.keyslots[i].is_enc == 0) {
|
|
params->mDscAlgo = params->mKeyslotList.keyslots[i].algo;
|
|
break;
|
|
}
|
|
}
|
|
if (params->mDscAlgo == DSM_ALGO_UNKNOWN) {
|
|
MLOGE("DSM_ALGO_UNKNOWN");
|
|
return NULL;
|
|
}
|
|
|
|
MLOGI("mDscAlgo:%d", params->mDscAlgo);
|
|
|
|
if (caAllocDscChannels(params) != true) {
|
|
caFreeDscChannels(params);
|
|
MLOGE("caAllocDscChannels fail.");
|
|
return NULL;
|
|
}
|
|
if (caSetDscChannelsKeyslot(params) != true) {
|
|
MLOGE("caSetDscChannelsKeyslot fail.");
|
|
return NULL;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int AmlVMXIptvMediaCas::setPrivateData(const uint8_t* data, size_t size)
|
|
{
|
|
RETURN_ERROR_IF_NULLPTR(vmxCasInitCtrl.iCas);
|
|
|
|
std::vector<uint8_t> privatedata(data, data + size);
|
|
auto status = vmxCasInitCtrl.iCas->setPrivateData(privatedata);
|
|
if (!status.isOk()) {
|
|
MLOGE("setPrivateData error");
|
|
return VMX_CAS_ERROR;
|
|
}
|
|
return VMX_CAS_SUCCESS;
|
|
}
|
|
|
|
int64_t AmlVMXIptvMediaCas::getTimeMs()
|
|
{
|
|
int64_t systime = 0;
|
|
int64_t ret = 0;
|
|
int rret = 0;
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
systime = (int64_t)tv.tv_sec * 1000 + (int64_t)(tv.tv_usec / 1000);
|
|
return systime;
|
|
}
|
|
|
|
int AmlVMXIptvMediaCas::processEcm(bool isSection, int ecmPid, const uint8_t* data, size_t size)
|
|
{
|
|
AML_MP_UNUSED(isSection);
|
|
AML_MP_UNUSED(ecmPid);
|
|
|
|
MLOGI("ecm size:%zu, data: 0x%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", size,
|
|
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9]);
|
|
|
|
if (data == NULL) {
|
|
MLOGE("parameter error, data:%p, size:%zu", data, size);
|
|
return VMX_CAS_ERROR;
|
|
}
|
|
|
|
RETURN_ERROR_IF_NULLPTR(vmxCasInitCtrl.iCas);
|
|
|
|
if (mSessionId.empty()) {
|
|
MLOGE("mSessionId is empty");
|
|
return VMX_CAS_SUCCESS;
|
|
}
|
|
|
|
const uint8_t* dataStart = data;
|
|
const uint8_t* dataEnd = data + size;
|
|
|
|
if (data[0] == 0x47) {
|
|
size_t i = 0;
|
|
int ecmDataLen = 0;
|
|
|
|
//jump TS header
|
|
dataStart = data + 5;
|
|
|
|
//required ECM data length
|
|
ecmDataLen = dataStart[2] + 3;
|
|
|
|
//find the last value that is not 0xFF
|
|
while (data[size - 1 - i] == 0xFF) {
|
|
i++;
|
|
if (i == size - 1) {
|
|
break;
|
|
}
|
|
}
|
|
dataEnd = data + size - i;
|
|
|
|
if (dataEnd < dataStart) {
|
|
MLOGE("invalid ECM data");
|
|
return VMX_CAS_ERROR;
|
|
}
|
|
|
|
if ((dataEnd > dataStart) && ((dataEnd - dataStart) < ecmDataLen)) {
|
|
//actual length is less than required ECM data length.
|
|
//maybe 0xFF was cut, add difference to dataEnd.
|
|
int n = ecmDataLen - (dataEnd - dataStart);
|
|
dataEnd += n;
|
|
}
|
|
}
|
|
|
|
std::vector<uint8_t> ecmData(dataStart, dataEnd);
|
|
|
|
if (!mLastFailedEcmData.empty()) {
|
|
int64_t nowMs = getTimeMs();
|
|
int64_t elapsedMs = nowMs - mLastFailedTimeMs;
|
|
if (elapsedMs < 500 && mLastFailedEcmData == ecmData) {
|
|
MLOGI("Skip same ECM data within 500 ms after last failure");
|
|
return VMX_CAS_ERROR;
|
|
}
|
|
if (elapsedMs >= 500) {
|
|
mLastFailedEcmData.clear();
|
|
}
|
|
}
|
|
|
|
if (1 == mLastEcmProcessSuc && mEcmData == ecmData) {
|
|
//MLOGI("same ecm data");
|
|
return VMX_CAS_SUCCESS;
|
|
} else {
|
|
auto status = vmxCasInitCtrl.iCas->processEcm(mSessionId, ecmData);
|
|
if (!status.isOk()) {
|
|
MLOGE("processEcm error:%s", status.getDescription().c_str());
|
|
binder_exception_t exception = status.getExceptionCode();
|
|
MLOGE("Exception code: %d", exception);
|
|
mLastEcmProcessSuc = 0;
|
|
mEcmData.clear();
|
|
|
|
mLastFailedEcmData = ecmData;
|
|
mLastFailedTimeMs = getTimeMs();
|
|
|
|
return VMX_CAS_ERROR;
|
|
}
|
|
mLastFailedEcmData.clear();
|
|
mLastFailedTimeMs = 0;
|
|
mLastEcmProcessSuc = 1;
|
|
mEcmData = ecmData;
|
|
}
|
|
return VMX_CAS_SUCCESS;
|
|
}
|
|
|
|
int AmlVMXIptvMediaCas::processEmm(bool isSection, int emmPid, const uint8_t* data, size_t size)
|
|
{
|
|
AML_MP_UNUSED(isSection);
|
|
AML_MP_UNUSED(emmPid);
|
|
RETURN_ERROR_IF_NULLPTR(vmxCasInitCtrl.iCas);
|
|
|
|
std::vector<uint8_t> emmData(data, data + size);
|
|
auto status = vmxCasInitCtrl.iCas->processEmm(emmData);
|
|
if (!status.isOk()) {
|
|
MLOGE("processEmm error");
|
|
return VMX_CAS_ERROR;
|
|
}
|
|
return VMX_CAS_SUCCESS;
|
|
}
|
|
|
|
}
|
|
|