/* * 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 #include "BufferQueue.h" #include #include #include #include #include #include #ifdef HAVE_SECMEM #include #include #endif #include #include 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 b; { std::unique_lock _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 _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 b; { std::unique_lock _l(mDequeuedBufferLock); auto it = std::find_if(mDequeuedBuffers.begin(), mDequeuedBuffers.end(), [buffer](const std::shared_ptr& 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 _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 b; { std::unique_lock _l(mDequeuedBufferLock); auto it = std::find_if(mDequeuedBuffers.begin(), mDequeuedBuffers.end(), [buffer](const std::shared_ptr& 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 _l(mFreeBufferLock); mFreeBuffers.push_front(b); return 0; } BufferQueue::Buffer* BufferQueue::toBuffer(const uint8_t* base) { std::shared_ptr b; { std::unique_lock _l(mDequeuedBufferLock); auto it = std::find_if(mDequeuedBuffers.begin(), mDequeuedBuffers.end(), [base](const std::shared_ptr& 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) { std::unique_lock _l(mBufferLock); std::shared_ptr 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->reset(); buffer->resize(0); std::unique_lock _l(mFreeBufferLock); mFreeBuffers.push_back(buffer); return 0; } int BufferQueue::recycleBuffer(const std::shared_ptr& buffer) { std::unique_lock _l(mRecycleBufferLock); mRecycleBuffers.push_back(buffer); ++mRecycleQueueSize; return 0; } std::shared_ptr BufferQueue::getRecycledBuffer(int64_t handle) { std::shared_ptr b; { std::unique_lock _l(mRecycleBufferLock); auto it = std::find_if(mRecycleBuffers.begin(), mRecycleBuffers.end(), [handle](const std::shared_ptr& 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 _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> buffers; { std::unique_lock _l(mBufferLock); mBuffers.swap(buffers); mBufferQueueSize = 0; } std::list> recycleBuffers; { std::unique_lock _l(mRecycleBufferLock); mRecycleBuffers.swap(recycleBuffers); mRecycleQueueSize = 0; } for (auto& b : buffers) { b->resize(0); } for (auto& b : recycleBuffers) { b->resize(0); } { std::unique_lock _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& fn) { MLOG(); mWriter = fn; } void BufferQueue::Consumer::setBufferAvailableCb(const std::function& 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 _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 acquiredBuffer; uint32_t work; Work workSequence; int ret; uint32_t writeCount = 0; MLOGI("bufq start run!"); for (;;) { { std::unique_lock _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 _l(mLock); mWork |= kWorkFeedData; } break; } case kWorkPause: { mStarted = false; signalWorkDone(kWorkPause); { std::lock_guard _l(mLock); mWork &= ~ kWorkFeedData; } MLOGI("kWorkPause done!"); break; } case kWorkRecycleBuffer: { bool canNotify = false; { std::lock_guard _l(mRecycleHandleLock); auto it = mRecycleHandles.begin(); while (it != mRecycleHandles.end()) { std::shared_ptr 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 _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 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; } }