1156 lines
29 KiB
C++
1156 lines
29 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_BufferQueue"
|
|
#include <utils/AmlMpLog.h>
|
|
#include "BufferQueue.h"
|
|
#include <inttypes.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/time.h>
|
|
#include <algorithm>
|
|
#ifdef HAVE_SECMEM
|
|
#include <secmem_ca.h>
|
|
#include <secmem_types.h>
|
|
#endif
|
|
#include <utils/AmlMpUtils.h>
|
|
#include <cutils/properties.h>
|
|
|
|
static const char* mName = LOG_TAG;
|
|
|
|
namespace aml_mp {
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
BufferQueue::Buffer* BufferQueue::Buffer::create(size_t size, void* secMemSession, Aml_MP_StreamType streamType)
|
|
{
|
|
Buffer* buffer = nullptr;
|
|
|
|
if (secMemSession) {
|
|
buffer = new SecBuffer(size, secMemSession, streamType);
|
|
} else {
|
|
buffer = new Buffer(size, streamType);
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
BufferQueue::Buffer::Buffer(size_t capacity, Aml_MP_StreamType streamType)
|
|
: mRangeOffset(0)
|
|
, mStreamType(streamType)
|
|
{
|
|
if (capacity > 0) {
|
|
mData = malloc(capacity);
|
|
}
|
|
|
|
if (mData == nullptr) {
|
|
mCapacity = 0;
|
|
mRangeLength = 0;
|
|
} else {
|
|
mCapacity = capacity;
|
|
mRangeLength = 0;
|
|
}
|
|
|
|
mIsNonTunnelMode = AmlMpConfig::instance().mTsPlayerNonTunnel;
|
|
|
|
MLOGV("ctor Buffer(this:%p), mData:%p", this, mData);
|
|
|
|
}
|
|
|
|
BufferQueue::Buffer::~Buffer()
|
|
{
|
|
if (mData) {
|
|
free(mData);
|
|
mData = nullptr;
|
|
}
|
|
}
|
|
|
|
void BufferQueue::Buffer::setRange(size_t offset, size_t size)
|
|
{
|
|
AML_MP_CHECK_LE(offset, mCapacity);
|
|
AML_MP_CHECK_LE(offset + size, mCapacity);
|
|
|
|
mRangeOffset = offset;
|
|
mRangeLength = size;
|
|
}
|
|
|
|
void BufferQueue::Buffer::reset()
|
|
{
|
|
mRangeOffset = 0;
|
|
mRangeLength = 0;
|
|
}
|
|
|
|
size_t BufferQueue::Buffer::append(const uint8_t* data, size_t size)
|
|
{
|
|
MLOGV("append input size:%zu, mCapacity:%zu, mRangeOffset:%zu, mRangeLength:%zu", size, mCapacity, mRangeOffset, mRangeLength);
|
|
|
|
if (mCapacity - mRangeLength - mRangeOffset < size) { //check trailing space only, no wrap around
|
|
MLOGE("no enough space!");
|
|
return 0;
|
|
}
|
|
|
|
memcpy(this->data() + mRangeLength, data, size);
|
|
mRangeLength += size;
|
|
|
|
return size;
|
|
}
|
|
|
|
int BufferQueue::Buffer::prepareReaderView()
|
|
{
|
|
if (mReaderView == nullptr) {
|
|
mReaderView = this;
|
|
}
|
|
|
|
mRecycleHandle = (int64_t)mData + mRangeOffset;
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool BufferQueue::Buffer::needRecycle() const
|
|
{
|
|
bool result = false;
|
|
|
|
//in non tunnel mode, clear video es buffer also use asynchronous write
|
|
#ifdef ANDROID
|
|
result = true;
|
|
#else
|
|
if (mIsNonTunnelMode) {
|
|
result = true;
|
|
}
|
|
#endif
|
|
|
|
if (mStreamType == AML_MP_STREAM_TYPE_AUDIO) {
|
|
result = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
enum drm_level_e {
|
|
DRM_LEVEL1 = 1,
|
|
DRM_LEVEL2 = 2,
|
|
DRM_LEVEL3 = 3,
|
|
DRM_NONE = 4,
|
|
};
|
|
|
|
#define TYPE_DRMINFO_V2 0x100
|
|
#define TYPE_DRMINFO 0x80
|
|
#define TYPE_PATTERN 0x40
|
|
|
|
struct drm_info {
|
|
enum drm_level_e drm_level;
|
|
uint32_t drm_flag;
|
|
uint32_t drm_hasesdata;
|
|
uint32_t drm_priv;
|
|
uint32_t drm_pktsize;
|
|
uint32_t drm_pktpts;
|
|
uint32_t drm_phy;
|
|
uint32_t drm_vir;
|
|
uint32_t drm_remap;
|
|
uint32_t data_offset;
|
|
uint32_t handle;
|
|
uint32_t extpad[7];
|
|
} /*drminfo_t */;
|
|
|
|
BufferQueue::SecBuffer::SecBuffer(size_t capacity, void* secMemSession, Aml_MP_StreamType streamType)
|
|
: Buffer(0, streamType)
|
|
, mSecMemSession(secMemSession)
|
|
{
|
|
int ret = 0;
|
|
uint32_t handle = UINT32_MAX;
|
|
#ifdef HAVE_SECMEM
|
|
ret = Secure_V2_MemCreate(mSecMemSession, &handle);
|
|
#endif
|
|
if (ret) {
|
|
MLOGE("secmem create handle failed:%#x", ret);
|
|
return;
|
|
}
|
|
|
|
mHandle = handle;
|
|
#ifdef __LP64__
|
|
mData = (void*)((size_t)mHandle << 32);
|
|
MLOGV("mData:%p", mData);
|
|
#else
|
|
mData = (void*)(size_t)(mHandle);
|
|
#endif
|
|
|
|
mCapacity = capacity; //defer allocation
|
|
}
|
|
|
|
BufferQueue::SecBuffer::~SecBuffer()
|
|
{
|
|
int ret = 0;
|
|
if (mPhyAddr != UINT32_MAX) {
|
|
#ifdef HAVE_SECMEM
|
|
ret = Secure_V2_MemFree(mSecMemSession, mHandle);
|
|
#endif
|
|
if (ret) {
|
|
MLOGE("secmem free failed:%#x", ret);
|
|
}
|
|
mPhyAddr = UINT32_MAX;
|
|
}
|
|
|
|
if (mHandle != UINT32_MAX) {
|
|
#ifdef HAVE_SECMEM
|
|
ret = Secure_V2_MemRelease(mSecMemSession, mHandle);
|
|
#endif
|
|
if (ret) {
|
|
MLOGE("secmem release failed:%#x", ret);
|
|
}
|
|
mHandle = UINT32_MAX;
|
|
}
|
|
|
|
if (mReaderView) {
|
|
delete mReaderView;
|
|
mReaderView = nullptr;
|
|
}
|
|
}
|
|
|
|
int BufferQueue::SecBuffer::resize(size_t capacity)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (mPhyAddr != UINT32_MAX) {
|
|
#ifdef HAVE_SECMEM
|
|
ret = Secure_V2_MemFree(mSecMemSession, mHandle);
|
|
#endif
|
|
if (ret) {
|
|
MLOGE("secmem free failed:%#x, handle:%#x", ret, mHandle);
|
|
}
|
|
mPhyAddr = UINT32_MAX;
|
|
}
|
|
|
|
if (capacity == 0) {
|
|
return 0;
|
|
}
|
|
|
|
uint32_t phyaddr = 0;
|
|
#ifdef HAVE_SECMEM
|
|
ret = Secure_V2_MemAlloc(mSecMemSession, mHandle, capacity, &phyaddr);
|
|
#endif
|
|
if (ret) {
|
|
MLOGE("secmem alloc failed:%#x, handle:%#x", ret, mHandle);
|
|
} else {
|
|
mPhyAddr = phyaddr;
|
|
MLOGV("secmem handle:%#x, phyaddr:%#x, capacity:%zu", mHandle, mPhyAddr, capacity);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
size_t BufferQueue::SecBuffer::append(const uint8_t* data __unused, size_t size)
|
|
{
|
|
MLOGV("secmem append input size:%zu, mCapacity:%zu, mRangeOffset:%zu, mRangeLength:%zu", size, mCapacity, mRangeOffset, mRangeLength);
|
|
|
|
if (mCapacity - mRangeLength - mRangeOffset < size) { //check trailing space only, no wrap around
|
|
MLOGE("secmem no enough space!");
|
|
return 0;
|
|
}
|
|
|
|
if (mHandle == UINT32_MAX) {
|
|
MLOGE("secmem fill invalid handle!");
|
|
return 0;
|
|
}
|
|
|
|
int ret = 0;
|
|
ssize_t offset = mRangeOffset + mRangeLength;
|
|
#ifdef HAVE_SECMEM
|
|
ret = Secure_V2_MemFill(mSecMemSession, mHandle, offset, (uint8_t*)data, size);
|
|
#endif
|
|
if (ret) {
|
|
MLOGE("secmem fill failed:%#x", ret);
|
|
return 0;
|
|
}
|
|
|
|
mRangeLength += size;
|
|
|
|
return size;
|
|
}
|
|
|
|
int BufferQueue::SecBuffer::prepareReaderView()
|
|
{
|
|
if (mReaderView == nullptr) {
|
|
mReaderView = Buffer::create(sizeof(drm_info), nullptr, mStreamType);
|
|
}
|
|
mRecycleHandle = mHandle;
|
|
|
|
mReaderView->setRange(0, sizeof(drm_info));
|
|
mReaderView->setPts(mPts);
|
|
|
|
drm_info* info = (drm_info*)mReaderView->data();
|
|
memset(info, 0, sizeof(drm_info));
|
|
info->drm_level = DRM_LEVEL1;
|
|
info->drm_pktsize = mRangeLength;
|
|
info->drm_pktpts = mPts;
|
|
info->drm_hasesdata = 0;
|
|
info->drm_phy = mPhyAddr;
|
|
info->drm_flag = TYPE_DRMINFO | TYPE_DRMINFO_V2;
|
|
info->data_offset = mRangeOffset;
|
|
info->handle = mHandle;
|
|
*(void**)info->extpad = mSecMemSession;
|
|
|
|
MLOGV("drm_info:%p, secmem handle:%#x, phyaddr:%#x, offset:%zu, size:%zu", info, mHandle, mPhyAddr, mRangeOffset, mRangeLength);
|
|
|
|
return 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
BufferQueue::BufferQueue(Aml_MP_StreamType streamType, size_t bufferCount, size_t bufferSize, bool isSVP, bool isUHD)
|
|
: mStreamType(streamType)
|
|
, mMaxBufferCount(bufferCount)
|
|
, mBufferCapacity(bufferSize)
|
|
, mIsSecure(isSVP)
|
|
, mIsUHD(isUHD)
|
|
, mBufferCount(0)
|
|
, mLastDequeueBufferResult(0)
|
|
, mStopped(false)
|
|
{
|
|
AmlMpConfig::instance().init();
|
|
|
|
MLOGI("constructor BufferQueue(%zu, %zu), mIsSecure:%d, isUHD:%d", bufferCount, bufferSize, mIsSecure, isUHD);
|
|
}
|
|
|
|
void BufferQueue::initSessionIfNeeded()
|
|
{
|
|
#ifdef HAVE_SECMEM
|
|
if (mIsSecure && mSecMemSession == nullptr) {
|
|
int ret = 0;
|
|
ret = Secure_V2_SessionCreate(&mSecMemSession);
|
|
if (ret) {
|
|
MLOGE("secmem session create failed:%#x", ret);
|
|
} else {
|
|
MLOGI("mSecMemSession:%p", mSecMemSession);
|
|
}
|
|
|
|
uint32_t source = SECMEM_V2_MEM_SOURCE_VDEC;
|
|
uint32_t flags = SECMEM_V2_FLAGS_USAGE(SECMEM_V2_USAGE_DRM_PLAYBACK);
|
|
if (mStreamType == AML_MP_STREAM_TYPE_VIDEO) {
|
|
flags |= SECMEM_V2_FLAGS_TVP(mIsUHD ? SECMEM_TVP_TYPE_UHD : SECMEM_TVP_TYPE_FHD);
|
|
} else if (mStreamType == AML_MP_STREAM_TYPE_AUDIO) {
|
|
flags |= SECMEM_V2_FLAGS_VP9(SECMEM_CODEC_AUDIO);
|
|
}
|
|
MLOGI("secmem session source:%d, flags:%#x", source, flags);
|
|
ret = Secure_V2_Init(mSecMemSession, source, flags, 0, 0);
|
|
if (ret) {
|
|
MLOGE("secmem session init failed:%#x", ret);
|
|
}
|
|
|
|
uint32_t count;
|
|
uint32_t size;
|
|
Secure_GetBufferConfig(&count, &size);
|
|
MLOGI("secmem session count:%u, size:%u", count, size);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
BufferQueue::~BufferQueue()
|
|
{
|
|
MLOG();
|
|
|
|
//release all buffers first
|
|
mDequeuedBuffers.clear();
|
|
mBuffers.clear();
|
|
mBufferQueueSize = 0;
|
|
mFreeBuffers.clear();
|
|
mRecycleBuffers.clear();
|
|
mRecycleQueueSize = 0;
|
|
|
|
//release session
|
|
if (mSecMemSession) {
|
|
#ifdef HAVE_SECMEM
|
|
Secure_V2_SessionDestroy(&mSecMemSession);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
int BufferQueue::dequeueBuffer(Buffer** buffer)
|
|
{
|
|
std::shared_ptr<Buffer> b;
|
|
|
|
{
|
|
std::unique_lock<std::mutex> _l(mFreeBufferLock);
|
|
if (!mFreeBuffers.empty()) {
|
|
b = *mFreeBuffers.begin();
|
|
mFreeBuffers.pop_front();
|
|
mLastDequeueBufferResult.store(0, std::memory_order_relaxed);
|
|
|
|
b->reset();
|
|
} else {
|
|
if (mBufferCount >= mMaxBufferCount) {
|
|
MLOGV("buffer overflow!, buffers:%zu, limit:%zu", mBufferCount, mMaxBufferCount);
|
|
mLastDequeueBufferResult.store(-1, std::memory_order_relaxed);
|
|
goto exit;
|
|
}
|
|
|
|
initSessionIfNeeded();
|
|
|
|
b.reset(Buffer::create(mBufferCapacity, mSecMemSession, mStreamType));
|
|
if (b != nullptr) {
|
|
++mBufferCount;
|
|
} else {
|
|
MLOGE("dequeue buffer allocate failure!");
|
|
mLastDequeueBufferResult.store(-1, std::memory_order_relaxed);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
std::unique_lock<std::mutex> _l(mDequeuedBufferLock);
|
|
if (b != nullptr) {
|
|
mDequeuedBuffers.insert(b);
|
|
}
|
|
}
|
|
|
|
if (buffer != nullptr) {
|
|
*buffer = b.get();
|
|
}
|
|
|
|
exit:
|
|
return mLastDequeueBufferResult.load(std::memory_order_relaxed);
|
|
}
|
|
|
|
int BufferQueue::queueBuffer(Buffer* buffer)
|
|
{
|
|
if (buffer == nullptr) {
|
|
return -1;
|
|
}
|
|
|
|
std::shared_ptr<Buffer> b;
|
|
{
|
|
std::unique_lock<std::mutex> _l(mDequeuedBufferLock);
|
|
auto it = std::find_if(mDequeuedBuffers.begin(), mDequeuedBuffers.end(),
|
|
[buffer](const std::shared_ptr<Buffer>& p) {
|
|
return p.get() == buffer;
|
|
});
|
|
if (it != mDequeuedBuffers.end()) {
|
|
b = *it;
|
|
mDequeuedBuffers.erase(it);
|
|
} else {
|
|
MLOGE("queueBuffer with an unknown buffer:%p", buffer);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
{
|
|
std::unique_lock<std::mutex> _l(mBufferLock);
|
|
mBuffers.push_back(b);
|
|
mBufferCond.notify_all();
|
|
++mBufferQueueSize;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int BufferQueue::cancelBuffer(Buffer* buffer)
|
|
{
|
|
if (buffer == nullptr) {
|
|
return -1;
|
|
}
|
|
|
|
std::shared_ptr<Buffer> b;
|
|
{
|
|
std::unique_lock<std::mutex> _l(mDequeuedBufferLock);
|
|
auto it = std::find_if(mDequeuedBuffers.begin(), mDequeuedBuffers.end(),
|
|
[buffer](const std::shared_ptr<Buffer>& p) {
|
|
return p.get() == buffer;
|
|
});
|
|
if (it != mDequeuedBuffers.end()) {
|
|
b = *it;
|
|
mDequeuedBuffers.erase(it);
|
|
} else {
|
|
MLOGE("cancelBuffer with an unknown buffer:%p", buffer);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
std::unique_lock<std::mutex> _l(mFreeBufferLock);
|
|
mFreeBuffers.push_front(b);
|
|
|
|
return 0;
|
|
}
|
|
|
|
BufferQueue::Buffer* BufferQueue::toBuffer(const uint8_t* base)
|
|
{
|
|
std::shared_ptr<Buffer> b;
|
|
|
|
{
|
|
std::unique_lock<std::mutex> _l(mDequeuedBufferLock);
|
|
auto it = std::find_if(mDequeuedBuffers.begin(), mDequeuedBuffers.end(),
|
|
[base](const std::shared_ptr<Buffer>& p) {
|
|
return p->base() == base;
|
|
});
|
|
if (it != mDequeuedBuffers.end()) {
|
|
b = *it;
|
|
} else {
|
|
MLOGE("toBuffer with an unknown base pointer:%p", base);
|
|
}
|
|
}
|
|
|
|
return b.get();
|
|
}
|
|
|
|
int BufferQueue::acquireBuffer(std::shared_ptr<Buffer>* buffer)
|
|
{
|
|
std::unique_lock<std::mutex> _l(mBufferLock);
|
|
std::shared_ptr<Buffer> b;
|
|
|
|
retry:
|
|
if (!mBuffers.empty()) {
|
|
b = *mBuffers.begin();
|
|
mBuffers.pop_front();
|
|
--mBufferQueueSize;
|
|
} else {
|
|
while (mBuffers.empty() && !mStopped.load(std::memory_order_relaxed)) {
|
|
if (mBufferCond.wait_for(_l, std::chrono::milliseconds(10)) == std::cv_status::timeout) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!mBuffers.empty()) {
|
|
goto retry;
|
|
}
|
|
}
|
|
|
|
if (b != nullptr) {
|
|
*buffer = b;
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int BufferQueue::releaseBuffer(const std::shared_ptr<Buffer>& buffer)
|
|
{
|
|
buffer->reset();
|
|
buffer->resize(0);
|
|
|
|
std::unique_lock<std::mutex> _l(mFreeBufferLock);
|
|
mFreeBuffers.push_back(buffer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int BufferQueue::recycleBuffer(const std::shared_ptr<Buffer>& buffer)
|
|
{
|
|
std::unique_lock<std::mutex> _l(mRecycleBufferLock);
|
|
mRecycleBuffers.push_back(buffer);
|
|
++mRecycleQueueSize;
|
|
|
|
return 0;
|
|
}
|
|
|
|
std::shared_ptr<BufferQueue::Buffer> BufferQueue::getRecycledBuffer(int64_t handle)
|
|
{
|
|
std::shared_ptr<Buffer> b;
|
|
|
|
{
|
|
std::unique_lock<std::mutex> _l(mRecycleBufferLock);
|
|
auto it = std::find_if(mRecycleBuffers.begin(), mRecycleBuffers.end(),
|
|
[handle](const std::shared_ptr<Buffer>& p) {
|
|
return p->recycleHandle() == handle;
|
|
});
|
|
if (it != mRecycleBuffers.end()) {
|
|
b = *it;
|
|
mRecycleBuffers.erase(it);
|
|
--mRecycleQueueSize;
|
|
} else if (!mRecycleBuffers.empty()) {
|
|
MLOGE("getRecycledBuffer with an unknown handle:%#" PRIx64, handle);
|
|
}
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
bool BufferQueue::stopReceive()
|
|
{
|
|
bool oldState = mStopped.exchange(true, std::memory_order_relaxed);
|
|
|
|
std::unique_lock<std::mutex> _l(mBufferLock);
|
|
mBufferCond.notify_all();
|
|
|
|
return oldState;
|
|
}
|
|
|
|
void BufferQueue::restoreReceive(bool state)
|
|
{
|
|
mStopped.store(state, std::memory_order_relaxed);
|
|
}
|
|
|
|
//TODO
|
|
void BufferQueue::flush()
|
|
{
|
|
std::list<std::shared_ptr<Buffer>> buffers;
|
|
{
|
|
std::unique_lock<std::mutex> _l(mBufferLock);
|
|
mBuffers.swap(buffers);
|
|
mBufferQueueSize = 0;
|
|
}
|
|
|
|
std::list<std::shared_ptr<Buffer>> recycleBuffers;
|
|
{
|
|
std::unique_lock<std::mutex> _l(mRecycleBufferLock);
|
|
mRecycleBuffers.swap(recycleBuffers);
|
|
mRecycleQueueSize = 0;
|
|
}
|
|
|
|
for (auto& b : buffers) {
|
|
b->resize(0);
|
|
}
|
|
|
|
for (auto& b : recycleBuffers) {
|
|
b->resize(0);
|
|
}
|
|
|
|
{
|
|
std::unique_lock<std::mutex> _l(mFreeBufferLock);
|
|
mFreeBuffers.splice(mFreeBuffers.end(), recycleBuffers);
|
|
mFreeBuffers.splice(mFreeBuffers.end(), buffers);
|
|
|
|
MLOGI("free buffer size:%zu", mFreeBuffers.size());
|
|
}
|
|
}
|
|
|
|
void BufferQueue::getFlowStatus(bool* underflow, bool* overflow)
|
|
{
|
|
size_t bufferCount = 0;
|
|
|
|
if (underflow || overflow) {
|
|
bufferCount = getBufferQueueSize() + getRecyleQueueSize();
|
|
}
|
|
|
|
if (underflow) {
|
|
*underflow = bufferCount == 0;
|
|
}
|
|
|
|
if (overflow) {
|
|
*overflow = bufferCount * 100 / mMaxBufferCount > 50;
|
|
if (*overflow) {
|
|
MLOGD("getFlowStatus: bufferCount:%zu, mMaxBufferCount:%zu", bufferCount, mMaxBufferCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
BufferQueue::Consumer::Consumer(Aml_MP_StreamType streamType, const std::string& name)
|
|
: mStreamType(streamType)
|
|
{
|
|
snprintf(mName, sizeof(mName), "%s_%s", LOG_TAG, name.c_str());
|
|
snprintf(mConsumerName, sizeof(mConsumerName), "%s", name.c_str());
|
|
MLOG();
|
|
|
|
char value[PROPERTY_VALUE_MAX];
|
|
int ret = property_get("vendor.media.dump.bufq", value, "");
|
|
if (ret > 0) {
|
|
bool enableDump = false;
|
|
if ((mStreamType == AML_MP_STREAM_TYPE_AUDIO && strstr(value, "audio")) ||
|
|
(mStreamType == AML_MP_STREAM_TYPE_VIDEO && strstr(value, "video"))) {
|
|
enableDump = true;
|
|
}
|
|
|
|
if (enableDump) {
|
|
std::string dumpPath = "/data/";
|
|
dumpPath.append(name);
|
|
|
|
struct timeval tv;
|
|
gettimeofday(&tv, nullptr);
|
|
|
|
struct tm t;
|
|
localtime_r(&tv.tv_sec, &t);
|
|
char tbuf[50];
|
|
strftime(tbuf, sizeof(tbuf), "_%Y_%m_%d_%H_%M_%S_", &t);
|
|
dumpPath.append(tbuf);
|
|
dumpPath.append(std::to_string(tv.tv_usec/1000));
|
|
|
|
MLOGI("try dump %s", dumpPath.c_str());
|
|
|
|
mDumpFd = ::open(dumpPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
|
if (mDumpFd < 0) {
|
|
MLOGE("open dump %s failed, %s", dumpPath.c_str(), strerror(errno));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BufferQueue::Consumer::~Consumer()
|
|
{
|
|
MLOG();
|
|
|
|
stop();
|
|
|
|
if (mDumpFd >= 0) {
|
|
::close(mDumpFd);
|
|
mDumpFd = -1;
|
|
}
|
|
}
|
|
|
|
void BufferQueue::Consumer::connectQueue(BufferQueue* bufferQueue)
|
|
{
|
|
MLOG();
|
|
|
|
mBufferQueue = bufferQueue;
|
|
}
|
|
|
|
void BufferQueue::Consumer::connectSink(const std::function<int(const uint8_t*, size_t, uint64_t)>& fn)
|
|
{
|
|
MLOG();
|
|
|
|
mWriter = fn;
|
|
}
|
|
|
|
void BufferQueue::Consumer::setBufferAvailableCb(const std::function<void()>& cb)
|
|
{
|
|
MLOG();
|
|
|
|
mBufferAvailableCb = cb;
|
|
}
|
|
|
|
int BufferQueue::Consumer::start()
|
|
{
|
|
MLOGD("BufferQueue start");
|
|
|
|
if (mWorker.get_id() == std::thread::id()) {
|
|
MLOGI("create mWorker!");
|
|
mWorker = std::thread([this] {
|
|
threadLoop();
|
|
});
|
|
|
|
std::string tName(mConsumerName);
|
|
tName.append("_bufq");
|
|
pthread_setname_np(mWorker.native_handle(), tName.c_str());
|
|
}
|
|
|
|
if (mBufferQueue) {
|
|
mBufferQueue->restoreReceive();
|
|
}
|
|
|
|
sendWorkCommand(kWorkStart);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//call start to resume working
|
|
int BufferQueue::Consumer::pause()
|
|
{
|
|
MLOGI("BufferQueue pause");
|
|
|
|
if (mBufferQueue) {
|
|
mBufferQueue->stopReceive();
|
|
}
|
|
|
|
sendWorkCommandAndWait(kWorkPause);
|
|
MLOGI("BufferQueue pause end.");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int BufferQueue::Consumer::stop()
|
|
{
|
|
MLOGI("BufferQueue stop");
|
|
|
|
if (mBufferQueue) {
|
|
mBufferQueue->stopReceive();
|
|
}
|
|
|
|
sendWorkCommandAndWait(kWorkQuit);
|
|
if (mWorker.joinable()) {
|
|
mWorker.join();
|
|
}
|
|
|
|
MLOG();
|
|
return 0;
|
|
}
|
|
|
|
int BufferQueue::Consumer::notifyBufferReleased(int64_t handle)
|
|
{
|
|
MLOGV("recycle handle:%#" PRIx64, handle);
|
|
|
|
{
|
|
std::lock_guard<std::mutex> _l(mRecycleHandleLock);
|
|
mRecycleHandles.push_back(handle);
|
|
}
|
|
sendWorkCommand(kWorkRecycleBuffer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int BufferQueue::Consumer::flush()
|
|
{
|
|
bool oldState = mBufferQueue->stopReceive();
|
|
MLOGI("bufq flush, oldState stopped:%d", oldState);
|
|
|
|
sendWorkCommand(kWorkFlush);
|
|
|
|
mBufferQueue->restoreReceive(oldState);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int BufferQueue::Consumer::threadLoop()
|
|
{
|
|
std::shared_ptr<Buffer> acquiredBuffer;
|
|
uint32_t work;
|
|
Work workSequence;
|
|
int ret;
|
|
uint32_t writeCount = 0;
|
|
|
|
MLOGI("bufq start run!");
|
|
|
|
for (;;) {
|
|
{
|
|
std::unique_lock<std::mutex> _l(mLock);
|
|
mCond.wait(_l, [this, &work] {return (work = mWork) != 0; });
|
|
}
|
|
|
|
if (work != 0 && !mWorkQueue.empty()) {
|
|
workSequence = *mWorkQueue.begin();
|
|
switch (workSequence) {
|
|
case kWorkQuit:
|
|
{
|
|
MLOGW("Quit!");
|
|
signalWorkDone(kWorkQuit);
|
|
goto exit;
|
|
|
|
break;
|
|
}
|
|
|
|
case kWorkStart:
|
|
{
|
|
mStarted = true;
|
|
MLOGI("start, buffer queue size:%zu, recycle queue size:%zu", mBufferQueue->getBufferQueueSize(), mBufferQueue->getRecyleQueueSize());
|
|
signalWorkDone(kWorkStart);
|
|
{
|
|
std::lock_guard<std::mutex> _l(mLock);
|
|
mWork |= kWorkFeedData;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case kWorkPause:
|
|
{
|
|
mStarted = false;
|
|
signalWorkDone(kWorkPause);
|
|
{
|
|
std::lock_guard<std::mutex> _l(mLock);
|
|
mWork &= ~ kWorkFeedData;
|
|
}
|
|
MLOGI("kWorkPause done!");
|
|
break;
|
|
}
|
|
|
|
case kWorkRecycleBuffer:
|
|
{
|
|
bool canNotify = false;
|
|
{
|
|
std::lock_guard<std::mutex> _l(mRecycleHandleLock);
|
|
auto it = mRecycleHandles.begin();
|
|
while (it != mRecycleHandles.end()) {
|
|
std::shared_ptr<Buffer> buffer = mBufferQueue->getRecycledBuffer(*it);
|
|
if (buffer == nullptr) {
|
|
break;
|
|
}
|
|
mBufferQueue->releaseBuffer(buffer);
|
|
it = mRecycleHandles.erase(it);
|
|
|
|
canNotify = true;
|
|
MLOGV("[%u] recycle releaseBuffer base:%p", ++mReleasedCount, buffer->base());
|
|
}
|
|
}
|
|
|
|
if (mBufferQueue->lastDequeueResult() != 0 && canNotify) {
|
|
MLOGV("try notify buffer available!");
|
|
if (mBufferAvailableCb) {
|
|
mBufferAvailableCb();
|
|
}
|
|
}
|
|
|
|
signalWorkDone(kWorkRecycleBuffer);
|
|
break;
|
|
}
|
|
|
|
case kWorkFlush:
|
|
{
|
|
if (acquiredBuffer) {
|
|
if (acquiredBuffer->needRecycle()) {
|
|
mBufferQueue->recycleBuffer(acquiredBuffer);
|
|
} else {
|
|
mBufferQueue->releaseBuffer(acquiredBuffer);
|
|
}
|
|
acquiredBuffer = nullptr;
|
|
}
|
|
|
|
mBufferQueue->flush();
|
|
|
|
{
|
|
std::lock_guard<std::mutex> _l(mRecycleHandleLock);
|
|
mRecycleHandles.clear();
|
|
}
|
|
|
|
MLOGI("kWorkFlush done!");
|
|
signalWorkDone(kWorkFlush);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!mStarted) {
|
|
continue;
|
|
}
|
|
|
|
if (!(work & kWorkFeedData)) {
|
|
continue;
|
|
}
|
|
|
|
if (acquiredBuffer == nullptr) {
|
|
ret = mBufferQueue->acquireBuffer(&acquiredBuffer);
|
|
if (ret < 0) {
|
|
continue;
|
|
}
|
|
acquiredBuffer->prepareReaderView();
|
|
MLOGV("[%u] acquiredBuffer base:%p, size:%zu", ++mAcquiredCount, acquiredBuffer->base(), acquiredBuffer->size());
|
|
|
|
if (mDumpFd >= 0) {
|
|
if (::write(mDumpFd, acquiredBuffer->data(), acquiredBuffer->size()) != (int)acquiredBuffer->size()) {
|
|
MLOGE("write dump file failed!");
|
|
}
|
|
}
|
|
} else if (acquiredBuffer->readerView()->size() == 0) {
|
|
if (acquiredBuffer->needRecycle()) {
|
|
MLOGV("[%u] recycleBuffer base:%p", ++mRecycledCount, acquiredBuffer->base());
|
|
mBufferQueue->recycleBuffer(acquiredBuffer);
|
|
} else {
|
|
MLOGV("[%u] releaseBuffer base:%p", ++mReleasedCount, acquiredBuffer->base());
|
|
mBufferQueue->releaseBuffer(acquiredBuffer);
|
|
if (mBufferQueue->lastDequeueResult() != 0) {
|
|
MLOGV("try notify buffer available!");
|
|
if (mBufferAvailableCb) {
|
|
mBufferAvailableCb();
|
|
}
|
|
}
|
|
}
|
|
acquiredBuffer = nullptr;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (mWriter == nullptr) {
|
|
MLOGE("mWriter is NULL!");
|
|
usleep(10*1000);
|
|
continue;
|
|
}
|
|
|
|
int len = mWriter(acquiredBuffer->readerView()->data(), acquiredBuffer->readerView()->size(), acquiredBuffer->readerView()->pts());
|
|
if (len < 0) {
|
|
len = 0;
|
|
usleep(10*1000);
|
|
} else if (len == 0) {
|
|
len = acquiredBuffer->readerView()->size();
|
|
}
|
|
|
|
acquiredBuffer->readerView()->setRange(acquiredBuffer->readerView()->offset()+len, acquiredBuffer->readerView()->size()-len);
|
|
++writeCount;
|
|
if (writeCount == 1 || writeCount%1000 == 0) {
|
|
MLOGV("bufferQueueSize:%zu (%u/%u/%u)", mBufferQueue->getBufferQueueSize(), mAcquiredCount, mRecycledCount, mReleasedCount);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
MLOGI("mWorkQueue size:%zu", mWorkQueue.size());
|
|
MLOG();
|
|
return 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
StreamParser::StreamParser(Aml_MP_StreamType streamType, StreamCategory streamCategory)
|
|
: mStreamType(streamType)
|
|
, mStreamCategory(streamCategory)
|
|
{
|
|
snprintf(mName, sizeof(mName), "%s_%s", "StreamParser", mpStreamType2Str(mStreamType));
|
|
MLOG();
|
|
}
|
|
|
|
int StreamParser::append(const uint8_t* data, size_t size)
|
|
{
|
|
size_t neededSize = (mBuffer == nullptr ? 0 : mBuffer->size()) + size;
|
|
if (mBuffer == nullptr || neededSize > mBuffer->capacity()) {
|
|
neededSize = (neededSize + 65535) & ~65535;
|
|
|
|
if (neededSize >= 4 * 1024 * 1024) {
|
|
MLOGE("neededSize is too large!");
|
|
return -1;
|
|
}
|
|
|
|
sptr<AmlMpBuffer> buffer = new AmlMpBuffer(neededSize);
|
|
if (mBuffer != nullptr) {
|
|
memcpy(buffer->data(), mBuffer->data(), mBuffer->size());
|
|
buffer->setRange(0, mBuffer->size());
|
|
} else {
|
|
buffer->setRange(0, 0);
|
|
}
|
|
|
|
mBuffer = buffer;
|
|
}
|
|
|
|
memcpy(mBuffer->data() + mBuffer->size(), data, size);
|
|
mBuffer->setRange(0, mBuffer->size() + size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int StreamParser::lockFrame(Frame* frame)
|
|
{
|
|
int ret = 0;
|
|
|
|
memset(frame, 0, sizeof(Frame));
|
|
|
|
switch (mStreamCategory) {
|
|
case PES:
|
|
{
|
|
const uint8_t* data = mBuffer->data();
|
|
size_t size = mBuffer->size();
|
|
|
|
int startPos = findPES(data, size);
|
|
if (startPos < 0) {
|
|
return -1;
|
|
}
|
|
//MLOGI("startPos:%d", startPos);
|
|
|
|
parsePES(data + startPos, size - startPos, frame);
|
|
if (frame->size == 0) {
|
|
if (size < (size_t)startPos + frame->u.pes.pes_header_length) {
|
|
MLOGW("data not enough!");
|
|
return -1;
|
|
}
|
|
|
|
int endPos = findPES(data + startPos + frame->u.pes.pes_header_length, size - startPos - frame->u.pes.pes_header_length);
|
|
if (endPos < 0) {
|
|
MLOGV("can't find endPos!");
|
|
return -1;
|
|
}
|
|
|
|
frame->size = endPos;
|
|
MLOGV("endPos:%d", endPos);
|
|
}
|
|
frame->data = data + startPos + frame->u.pes.pes_header_length;
|
|
}
|
|
break;
|
|
|
|
case ES:
|
|
{
|
|
break;
|
|
}
|
|
|
|
case MP4:
|
|
default:
|
|
MLOGE("unsupported streamCategory:%d", mStreamCategory);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int StreamParser::unlockFrame(Frame* frame)
|
|
{
|
|
size_t consumed = frame->data + frame->size - mBuffer->data();
|
|
if (consumed != mBuffer->size()) {
|
|
MLOGD("unlockFrame consumed:%zu, left:%zu", consumed, mBuffer->size()-consumed);
|
|
}
|
|
|
|
memmove(mBuffer->data(), mBuffer->data() + consumed, mBuffer->size() - consumed);
|
|
mBuffer->setRange(0, mBuffer->size() - consumed);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int StreamParser::findPES(const uint8_t* data, size_t size)
|
|
{
|
|
int pos = 0;
|
|
|
|
while (pos < (int)size-4) {
|
|
uint32_t token = *(uint32_t*)(data+pos);
|
|
//CHECK it!
|
|
if (token == 0xE0010000) {
|
|
return pos;
|
|
}
|
|
|
|
++pos;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int StreamParser::parsePES(const uint8_t* data, size_t size __unused, Frame* frame)
|
|
{
|
|
const uint8_t* p = data;
|
|
int pesStartCode = p[0] << 16 | p[1] << 8 | p[2];
|
|
AML_MP_CHECK_EQ(pesStartCode, 0x000001);
|
|
p += 3;
|
|
|
|
int stream_id = *p++;
|
|
int pes_packet_length = p[0] << 8 | p[1];
|
|
p += 2;
|
|
|
|
if (stream_id != 0xbc &&
|
|
stream_id != 0xbe &&
|
|
stream_id != 0xbf &&
|
|
stream_id != 0xf0 &&
|
|
stream_id != 0xf1 &&
|
|
stream_id != 0xff &&
|
|
stream_id != 0xf2 &&
|
|
stream_id != 0xf8) {
|
|
|
|
++p;
|
|
|
|
int PTS_DTS_flags = p[0] >> 6;
|
|
int pes_header_length = p[1];
|
|
p += 2;
|
|
|
|
int64_t pts = 0;
|
|
int64_t dts = 0;
|
|
if (PTS_DTS_flags & 0x02) {
|
|
pts = (int64_t)(p[0]&0x0e) << 29;
|
|
pts |= ((p[1]<<8 | p[2])>>1) << 15;
|
|
pts |= ((p[3]<<8 | p[4])>>1);
|
|
|
|
p += 5;
|
|
}
|
|
|
|
if (PTS_DTS_flags & 0x01) {
|
|
dts = (int64_t)(p[0]&0x0e) << 29;
|
|
dts |= ((p[1]<<8 | p[2])>>1) << 15;
|
|
dts |= ((p[3]<<8 | p[4])>>1);
|
|
|
|
p += 5;
|
|
}
|
|
|
|
if (pes_packet_length > 3 + pes_header_length) {
|
|
frame->size = pes_packet_length - 3 - pes_header_length;
|
|
}
|
|
frame->pts = pts;
|
|
frame->dts = dts;
|
|
|
|
frame->u.pes.stream_id = stream_id;
|
|
frame->u.pes.pes_header_length = 9 + pes_header_length;
|
|
|
|
MLOGV("FRAME(%u, %zu, %#" PRIx64 ", %#" PRIx64 ")",
|
|
frame->u.pes.pes_header_length, frame->size, frame->pts, frame->dts);
|
|
} else {
|
|
MLOGE("invalid stream_id:%#x", stream_id);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
}
|