Files

521 lines
12 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_NDEBUG 0
#define LOG_TAG "AmlMpPlayerDemo_AmlHwDemux"
#include <utils/AmlMpLog.h>
#include <Aml_MP/Aml_MP.h>
#include "AmlHwDemux.h"
#include <utils/AmlMpHandle.h>
#include <utils/AmlMpBuffer.h>
#include <utils/AmlMpEventLooper.h>
#include <utils/AmlMpUtils.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#ifdef HAVE_LIBDVR
#define CONFIG_AMLOGIC_DVB_COMPAT // use for write_ts to demux
extern "C" {
#include <dmx.h>
}
#endif
static const char* mName = LOG_TAG;
namespace aml_mp {
static const int kPesFilterBufferSize = 2 * 1024 * 1024;
class HwTsParser : public AmlDemuxBase::ITsParser, public LooperCallback
{
public:
HwTsParser(const std::function<FilterCallback>& cb, const std::string& name);
~HwTsParser();
int dvr_open(int demuxId, bool isHardwareSource, bool isSecureBuffer);
int feedTs(const uint8_t* buffer, size_t size);
int dvr_close();
void reset();
int addDemuxFilter(int pid, const Aml_MP_DemuxFilterParams* params) override;
void removeDemuxFilter(int pid) override;
private:
virtual int handleEvent(int fd, int events, void* data);
std::string mDemuxName;
bool mIsSecurebuffer = false;
std::mutex mLock;
std::map<int, int> mChannelFds; //pid, fd
int mDvrFd;
sptr<AmlMpBuffer> mCallbackBuffer;
private:
HwTsParser(const HwTsParser&) = delete;
HwTsParser& operator=(const HwTsParser&) = delete;
};
///////////////////////////////////////////////////////////////////////////////
AmlHwDemux::AmlHwDemux()
{
MLOG("mstopped = %d", mStopped.load());
mIsHardwareSource = false;
mIsSecureBuffer = false;
}
AmlHwDemux::~AmlHwDemux()
{
close();
}
int AmlHwDemux::open(bool isHardwareSource, Aml_MP_DemuxId demuxId, bool isSecureBuffer)
{
mDemuxId = demuxId;
mIsHardwareSource = isHardwareSource;
mIsSecureBuffer = isSecureBuffer;
std::stringstream s;
s << "/dev/dvb0.demux" << mDemuxId;
mDemuxName = s.str();
MLOGI("mDemuxName:%s", mDemuxName.c_str());
if (mTsParser == nullptr) {
mTsParser = new HwTsParser([this](int pid, const sptr<AmlMpBuffer>& data, int version) {
return notifyData(pid, data, version);
}, mDemuxName);
}
return 0;
}
int AmlHwDemux::close()
{
MLOG();
mTsParser.clear();
return 0;
}
int AmlHwDemux::start()
{
MLOGI("start");
if (mTsParser == nullptr) {
MLOGE("Demux not open, start fail");
return -1;
}
mTsParser->dvr_open(mDemuxId, mIsHardwareSource, mIsSecureBuffer);
if (mLooper == nullptr) {
mLooper = new Looper(Looper::PREPARE_ALLOW_NON_CALLBACKS);
if (mLooper == nullptr) {
MLOGE("create looper failed!");
return -1;
}
}
mThread = std::thread([this] {
threadLoop();
});
return 0;
}
int AmlHwDemux::stop()
{
MLOG();
{
std::lock_guard<std::mutex> _l(mLock);
mStopped = true;
}
if (mLooper) {
mLooper->wake();
}
MLOGI("join...");
if (mThread.joinable()) {
mThread.join();
}
mTsParser->dvr_close();
MLOGI("stopped!");
return 0;
}
int AmlHwDemux::flush()
{
MLOG();
return 0;
}
int AmlHwDemux::feedTs(const uint8_t* buffer, size_t size)
{
std::lock_guard<std::mutex> _l(mLock);
if (mStopped) {
return 0;
}
return mTsParser->feedTs(buffer, size);
}
int AmlHwDemux::addDemuxFilter(int pid, const Aml_MP_DemuxFilterParams* params)
{
int channelFd = mTsParser->addDemuxFilter(pid, params);
std::unique_ptr<FilterParams> filterParams(new FilterParams);
filterParams->pid = pid;
filterParams->fd = channelFd;
filterParams->params = *params;
int ret = mLooper->addFd(
channelFd,
Looper::POLL_CALLBACK,
Looper::EVENT_ERROR|Looper::EVENT_INPUT,
mTsParser,
filterParams.get());
if (ret <= 0) {
MLOGE("addFd failed! fd:%d", channelFd);
} else {
mFilterParams.emplace(pid, std::move(filterParams));
}
return ret;
}
int AmlHwDemux::removeDemuxFilter(int pid)
{
auto it = mFilterParams.find(pid);
if (it == mFilterParams.end()) {
MLOGE("removeFd failed! invalid pid:%d", pid);
return -1;
}
std::unique_ptr<FilterParams> filterParams = std::move(it->second);
mFilterParams.erase(it);
int channelFd = filterParams->fd;
int ret = mLooper->removeFd(channelFd);
if (ret <= 0) {
MLOGE("removeFd failed! fd:%d", channelFd);
}
mTsParser->removeDemuxFilter(pid);
return ret;
}
bool AmlHwDemux::isStopped() const
{
return mStopped.load(std::memory_order_relaxed);
}
void AmlHwDemux::threadLoop()
{
int ret = 0;
ALOGI("hwdemux thread start!");
if (mLooper == nullptr) {
MLOGE("looper is NULL!");
return;
}
Looper::setForThread(mLooper);
for (;;) {
ret = mLooper->pollOnce(10 * 1000);
if (ret == Looper::POLL_ERROR) {
MLOGE("error!");
break;
}
bool stopped = mStopped.load(std::memory_order_relaxed);
if (stopped) {
MLOGE("request stop...");
break;
}
}
MLOGI("AmlHwDemux threadLoop exited!");
}
///////////////////////////////////////////////////////////////////////////////
HwTsParser::HwTsParser(const std::function<FilterCallback>& cb, const std::string& name)
: ITsParser(cb)
, mDemuxName(name)
{
MLOG();
mDvrFd = -1;
}
HwTsParser::~HwTsParser()
{
MLOG();
}
int HwTsParser::dvr_open(int demuxId, bool isHardwareSource, bool isSecurebuffer) {
int ret = 0;
char name[32];
#ifdef HAVE_LIBDVR
snprintf(name, sizeof(name), "/dev/dvb0.dvr%d", demuxId);
mDvrFd = open(name, O_WRONLY);
if (mDvrFd == -1) {
MLOGE("cannot open \"%s\" (%s)", name, strerror(errno));
return -1;
}
MLOGI("open %s ok \n", name);
mIsSecurebuffer = isSecurebuffer;
if (isSupportMultiHwDemux()) {
if (!isHardwareSource) {
if (isSecurebuffer) {
MLOGI("set ---> INPUT_LOCAL_SEC \n");
ret = ioctl(mDvrFd, DMX_SET_INPUT, INPUT_LOCAL_SEC);
} else {
MLOGI("set ---> INPUT_LOCAL \n");
ret = ioctl(mDvrFd, DMX_SET_INPUT, INPUT_LOCAL);
}
} else {
MLOGI("set ---> INPUT_DEMOD \n" );
ret = ioctl(mDvrFd, DMX_SET_INPUT, INPUT_DEMOD);
}
MLOGI("DMX_SET_INPUT ret:%d\n", ret);
if (ret < 0) {
MLOGE("dvr_open ioctl failed %s\n", strerror(errno));
return -1;
}
}
#else
AML_MP_UNUSED(demuxId);
AML_MP_UNUSED(isHardwareSource);
AML_MP_UNUSED(isSecurebuffer);
ret = -1;
#endif
return ret;
}
int HwTsParser::dvr_close() {
if (mDvrFd > 0) {
close(mDvrFd);
}
return 0;
}
int HwTsParser::feedTs(const uint8_t* buffer, size_t size)
{
int ret;
int left = size;
int off = 0;
if (mDvrFd < 0) {
return -1;
}
#ifdef HAVE_LIBDVR
uint8_t* buffer_secure = NULL;
struct dmx_sec_ts_data ts_sec_data;
if (mIsSecurebuffer) {
ts_sec_data.buf_start = (uint32_t)(long)buffer;
ts_sec_data.buf_end = (uint32_t)(long)(buffer+size);
buffer_secure = (uint8_t *)(&ts_sec_data);
left = sizeof(struct dmx_sec_ts_data);
}
while (left > 0) {
if (mIsSecurebuffer) {
ret = write(mDvrFd, buffer_secure, left);
// MLOGI("Write DVR data: buffer_secure:%p, left:%d, ret:%d", buffer_secure, left, ret);
} else {
ret = write(mDvrFd, buffer + off, left);
}
if (ret == -1) {
if (errno != EINTR) {
MLOGE("Write DVR data failed: %s", strerror(errno));
break;
}
ret = 0;
}
left -= ret;
off += ret;
}
return (size - left);
#else
AML_MP_UNUSED(buffer);
AML_MP_UNUSED(size);
return -1;
#endif
}
int HwTsParser::addDemuxFilter(int pid, const Aml_MP_DemuxFilterParams* params)
{
#ifdef HAVE_LIBDVR
int fd = -1;
int ret = 0;
fd = ::open(mDemuxName.c_str(), O_RDWR);
if (fd < 0) {
MLOG("open %s failed! %s", mDemuxName.c_str(), strerror(errno));
return -1;
}
if (params->type == AML_MP_DEMUX_FILTER_PSI) {
int buffersize = 32 * 1024;
ret = ioctl(fd, DMX_SET_BUFFER_SIZE, buffersize);
if (ret < 0) {
MLOG("set buffer size failed!");
::close(fd);
return -1;
}
struct dmx_sct_filter_params filter_param;
memset(&filter_param, 0, sizeof(filter_param));
filter_param.pid = pid;
filter_param.flags = params->flags;
ret = ioctl(fd, DMX_SET_FILTER, &filter_param);
if (ret < 0) {
MLOG("set filter failed!");
::close(fd);
return -1;
}
} else {
int buffersize = kPesFilterBufferSize;
ret = ioctl(fd, DMX_SET_BUFFER_SIZE, buffersize);
if (ret < 0) {
MLOG("set pes buffer size failed!");
::close(fd);
return -1;
}
struct dmx_pes_filter_params filter_param;
memset(&filter_param, 0, sizeof(filter_param));
filter_param.pid = pid;
filter_param.flags = params->flags;
filter_param.input = DMX_IN_FRONTEND;
filter_param.output = DMX_OUT_TAP;
if (params->type == AML_MP_DEMUX_FILTER_AUDIO) {
filter_param.pes_type = DMX_PES_AUDIO;
} else if (params->type == AML_MP_DEMUX_FILTER_VIDEO) {
filter_param.pes_type = DMX_PES_VIDEO0;
}
MLOGI("create pes filter, pid:%d, type:%d, fd:%d, flags:%#x", pid, params->type, fd, params->flags);
ret = ioctl(fd, DMX_SET_PES_FILTER, &filter_param);
if (ret < 0) {
MLOG("set pes filter failed! %d", ret);
::close(fd);
return -1;
}
}
ret = ioctl(fd, DMX_START);
if (ret < 0) {
MLOG("dmx start failed!");
::close(fd);
return -1;
}
{
std::unique_lock<std::mutex> _l(mLock);
mChannelFds.emplace(pid, fd);
}
return fd;
#else
AML_MP_UNUSED(pid);
AML_MP_UNUSED(params);
return -1;
#endif
}
//int HwTsParser::getDemuxFilterPrivateData(int pid)
//{
//std::unique_lock<std::mutex> _l(mLock);
//auto it = mChannelFds.find(pid);
//return it != mChannelFds.end() ? it->second : -1;
//}
void HwTsParser::removeDemuxFilter(int pid)
{
int fd = -1;
{
std::unique_lock<std::mutex> _l(mLock);
auto it = mChannelFds.find(pid);
if (it != mChannelFds.end()) {
fd = it->second;
mChannelFds.erase(it);
}
}
if (fd < 0) {
MLOG("mFd is invalid");
return;
}
#ifdef HAVE_LIBDVR
int ret = ioctl(fd, DMX_STOP);
if (ret < 0) {
MLOG("dmx stop failed!");
return;
}
#endif
::close(fd);
}
void HwTsParser::reset()
{
}
int HwTsParser::handleEvent(int fd, int events, void* data)
{
sptr<AmlMpBuffer>& buffer = mCallbackBuffer;
if (mCallbackBuffer == nullptr) {
mCallbackBuffer = new AmlMpBuffer(kPesFilterBufferSize);
}
if (events & Looper::EVENT_INPUT) {
int len = ::read(fd, buffer->base(), buffer->capacity());
if (len < 0) {
MLOGE("read failed, %s", strerror(errno));
return 0;
}
buffer->setRange(0, len);
}
int version = -1;
AmlHwDemux::FilterParams* filterParams = (AmlHwDemux::FilterParams*)data;
if (filterParams->params.type == AML_MP_DEMUX_FILTER_PSI) {
version = buffer->data()[5]>>1 & 0x1F;
}
int pid = filterParams->pid;
MLOGV("fd:%d, pid:%d, size:%zu", fd, pid, buffer->size());
if (mFilterCallback) {
mFilterCallback(pid, buffer, version);
}
return 1;
}
}