- Implement CCodec and CCodecBufferChannel: video decoder only - Modify stagefright command line tool to take component name - Fix C2SoftAvcDec around EOS and flush Bug: 69376489 Test: setprop debug.stagefright.ccodec true Test: stagefright -S -O codec2.google.avc_decoder /data/local/tmp/a.mp4 Change-Id: I36d5f476099b5c055c0be0244e99bdf9dd28441egugelfrei
parent
cc25a713d9
commit
4f87426e12
@ -0,0 +1,582 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
#define LOG_TAG "CCodec"
|
||||
#include <utils/Log.h>
|
||||
|
||||
// XXX: HACK
|
||||
#include "codecs/avcdec/C2SoftAvcDec.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <gui/Surface.h>
|
||||
#include <media/stagefright/CCodec.h>
|
||||
|
||||
#include "include/CCodecBufferChannel.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace android {
|
||||
|
||||
namespace {
|
||||
|
||||
class CCodecWatchdog : public AHandler {
|
||||
private:
|
||||
enum {
|
||||
kWhatRegister,
|
||||
kWhatWatch,
|
||||
};
|
||||
constexpr static int64_t kWatchIntervalUs = 3000000; // 3 secs
|
||||
|
||||
public:
|
||||
static sp<CCodecWatchdog> getInstance() {
|
||||
Mutexed<sp<CCodecWatchdog>>::Locked instance(sInstance);
|
||||
if (*instance == nullptr) {
|
||||
*instance = new CCodecWatchdog;
|
||||
(*instance)->init();
|
||||
}
|
||||
return *instance;
|
||||
}
|
||||
|
||||
~CCodecWatchdog() = default;
|
||||
|
||||
void registerCodec(CCodec *codec) {
|
||||
sp<AMessage> msg = new AMessage(kWhatRegister, this);
|
||||
msg->setPointer("codec", codec);
|
||||
msg->post();
|
||||
}
|
||||
|
||||
protected:
|
||||
void onMessageReceived(const sp<AMessage> &msg) {
|
||||
switch (msg->what()) {
|
||||
case kWhatRegister: {
|
||||
void *ptr = nullptr;
|
||||
CHECK(msg->findPointer("codec", &ptr));
|
||||
Mutexed<std::list<wp<CCodec>>>::Locked codecs(mCodecs);
|
||||
codecs->emplace_back((CCodec *)ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
case kWhatWatch: {
|
||||
Mutexed<std::list<wp<CCodec>>>::Locked codecs(mCodecs);
|
||||
for (auto it = codecs->begin(); it != codecs->end(); ) {
|
||||
sp<CCodec> codec = it->promote();
|
||||
if (codec == nullptr) {
|
||||
it = codecs->erase(it);
|
||||
continue;
|
||||
}
|
||||
codec->initiateReleaseIfStuck();
|
||||
++it;
|
||||
}
|
||||
msg->post(kWatchIntervalUs);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
TRESPASS("CCodecWatchdog: unrecognized message");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CCodecWatchdog() : mLooper(new ALooper) {}
|
||||
|
||||
void init() {
|
||||
mLooper->setName("CCodecWatchdog");
|
||||
mLooper->registerHandler(this);
|
||||
mLooper->start();
|
||||
(new AMessage(kWhatWatch, this))->post(kWatchIntervalUs);
|
||||
}
|
||||
|
||||
static Mutexed<sp<CCodecWatchdog>> sInstance;
|
||||
|
||||
sp<ALooper> mLooper;
|
||||
Mutexed<std::list<wp<CCodec>>> mCodecs;
|
||||
};
|
||||
|
||||
Mutexed<sp<CCodecWatchdog>> CCodecWatchdog::sInstance;
|
||||
|
||||
class CCodecListener : public C2Component::Listener {
|
||||
public:
|
||||
CCodecListener(const std::shared_ptr<CCodecBufferChannel> &channel)
|
||||
: mChannel(channel) {
|
||||
}
|
||||
|
||||
virtual void onWorkDone_nb(
|
||||
std::weak_ptr<C2Component> component,
|
||||
std::vector<std::unique_ptr<C2Work>> workItems) override {
|
||||
(void) component;
|
||||
mChannel->onWorkDone(std::move(workItems));
|
||||
}
|
||||
|
||||
virtual void onTripped_nb(
|
||||
std::weak_ptr<C2Component> component,
|
||||
std::vector<std::shared_ptr<C2SettingResult>> settingResult) override {
|
||||
// TODO
|
||||
(void) component;
|
||||
(void) settingResult;
|
||||
}
|
||||
|
||||
virtual void onError_nb(std::weak_ptr<C2Component> component, uint32_t errorCode) override {
|
||||
// TODO
|
||||
(void) component;
|
||||
(void) errorCode;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<CCodecBufferChannel> mChannel;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
CCodec::CCodec()
|
||||
: mChannel(new CCodecBufferChannel([this] (status_t err, enum ActionCode actionCode) {
|
||||
mCallback->onError(err, actionCode);
|
||||
})) {
|
||||
CCodecWatchdog::getInstance()->registerCodec(this);
|
||||
}
|
||||
|
||||
CCodec::~CCodec() {
|
||||
}
|
||||
|
||||
std::shared_ptr<BufferChannelBase> CCodec::getBufferChannel() {
|
||||
return mChannel;
|
||||
}
|
||||
|
||||
void CCodec::initiateAllocateComponent(const sp<AMessage> &msg) {
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
if (state->mState != RELEASED) {
|
||||
mCallback->onError(INVALID_OPERATION, ACTION_CODE_FATAL);
|
||||
return;
|
||||
}
|
||||
state->mState = ALLOCATING;
|
||||
}
|
||||
|
||||
AString componentName;
|
||||
if (!msg->findString("componentName", &componentName)) {
|
||||
// TODO: find componentName appropriate with the media type
|
||||
}
|
||||
|
||||
sp<AMessage> allocMsg(new AMessage(kWhatAllocate, this));
|
||||
allocMsg->setString("componentName", componentName);
|
||||
allocMsg->post();
|
||||
}
|
||||
|
||||
void CCodec::allocate(const AString &componentName) {
|
||||
// TODO: use C2ComponentStore to create component
|
||||
mListener.reset(new CCodecListener(mChannel));
|
||||
|
||||
std::shared_ptr<C2Component> comp(new C2SoftAvcDec(componentName.c_str(), 0));
|
||||
comp->setListener_sm(mListener);
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
if (state->mState != ALLOCATING) {
|
||||
state->mState = RELEASED;
|
||||
state.unlock();
|
||||
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
state.lock();
|
||||
return;
|
||||
}
|
||||
state->mState = ALLOCATED;
|
||||
state->mComp = comp;
|
||||
}
|
||||
mChannel->setComponent(comp);
|
||||
mCallback->onComponentAllocated(comp->intf()->getName().c_str());
|
||||
}
|
||||
|
||||
void CCodec::initiateConfigureComponent(const sp<AMessage> &format) {
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
if (state->mState != ALLOCATED) {
|
||||
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sp<AMessage> msg(new AMessage(kWhatConfigure, this));
|
||||
msg->setMessage("format", format);
|
||||
msg->post();
|
||||
}
|
||||
|
||||
void CCodec::configure(const sp<AMessage> &msg) {
|
||||
sp<AMessage> inputFormat(new AMessage);
|
||||
sp<AMessage> outputFormat(new AMessage);
|
||||
if (status_t err = [=] {
|
||||
AString mime;
|
||||
if (!msg->findString("mime", &mime)) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
int32_t encoder;
|
||||
if (!msg->findInt32("encoder", &encoder)) {
|
||||
encoder = false;
|
||||
}
|
||||
|
||||
sp<RefBase> obj;
|
||||
if (msg->findObject("native-window", &obj)) {
|
||||
sp<Surface> surface = static_cast<Surface *>(obj.get());
|
||||
setSurface(surface);
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
return OK;
|
||||
}() != OK) {
|
||||
mCallback->onError(err, ACTION_CODE_FATAL);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
Mutexed<Formats>::Locked formats(mFormats);
|
||||
formats->mInputFormat = inputFormat;
|
||||
formats->mOutputFormat = outputFormat;
|
||||
}
|
||||
mCallback->onComponentConfigured(inputFormat, outputFormat);
|
||||
}
|
||||
|
||||
|
||||
void CCodec::initiateCreateInputSurface() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void CCodec::initiateSetInputSurface(const sp<PersistentSurface> &surface) {
|
||||
// TODO
|
||||
(void) surface;
|
||||
}
|
||||
|
||||
void CCodec::initiateStart() {
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
if (state->mState != ALLOCATED) {
|
||||
state.unlock();
|
||||
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
state.lock();
|
||||
return;
|
||||
}
|
||||
state->mState = STARTING;
|
||||
}
|
||||
|
||||
(new AMessage(kWhatStart, this))->post();
|
||||
}
|
||||
|
||||
void CCodec::start() {
|
||||
std::shared_ptr<C2Component> comp;
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
if (state->mState != STARTING) {
|
||||
state.unlock();
|
||||
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
state.lock();
|
||||
return;
|
||||
}
|
||||
comp = state->mComp;
|
||||
}
|
||||
c2_status_t err = comp->start();
|
||||
if (err != C2_OK) {
|
||||
// TODO: convert err into status_t
|
||||
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
return;
|
||||
}
|
||||
sp<AMessage> inputFormat;
|
||||
sp<AMessage> outputFormat;
|
||||
{
|
||||
Mutexed<Formats>::Locked formats(mFormats);
|
||||
inputFormat = formats->mInputFormat;
|
||||
outputFormat = formats->mOutputFormat;
|
||||
}
|
||||
mChannel->start(inputFormat, outputFormat);
|
||||
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
if (state->mState != STARTING) {
|
||||
state.unlock();
|
||||
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
state.lock();
|
||||
return;
|
||||
}
|
||||
state->mState = RUNNING;
|
||||
}
|
||||
mCallback->onStartCompleted();
|
||||
}
|
||||
|
||||
void CCodec::initiateShutdown(bool keepComponentAllocated) {
|
||||
if (keepComponentAllocated) {
|
||||
initiateStop();
|
||||
} else {
|
||||
initiateRelease();
|
||||
}
|
||||
}
|
||||
|
||||
void CCodec::initiateStop() {
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
if (state->mState == ALLOCATED
|
||||
|| state->mState == RELEASED
|
||||
|| state->mState == STOPPING
|
||||
|| state->mState == RELEASING) {
|
||||
// We're already stopped, released, or doing it right now.
|
||||
state.unlock();
|
||||
mCallback->onStopCompleted();
|
||||
state.lock();
|
||||
return;
|
||||
}
|
||||
state->mState = STOPPING;
|
||||
}
|
||||
|
||||
(new AMessage(kWhatStop, this))->post();
|
||||
}
|
||||
|
||||
void CCodec::stop() {
|
||||
std::shared_ptr<C2Component> comp;
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
if (state->mState == RELEASING) {
|
||||
state.unlock();
|
||||
// We're already stopped or release is in progress.
|
||||
mCallback->onStopCompleted();
|
||||
state.lock();
|
||||
return;
|
||||
} else if (state->mState != STOPPING) {
|
||||
state.unlock();
|
||||
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
state.lock();
|
||||
return;
|
||||
}
|
||||
comp = state->mComp;
|
||||
}
|
||||
mChannel->stop();
|
||||
status_t err = comp->stop();
|
||||
if (err != C2_OK) {
|
||||
// TODO: convert err into status_t
|
||||
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
}
|
||||
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
if (state->mState == STOPPING) {
|
||||
state->mState = ALLOCATED;
|
||||
}
|
||||
}
|
||||
mCallback->onStopCompleted();
|
||||
}
|
||||
|
||||
void CCodec::initiateRelease(bool sendCallback /* = true */) {
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
if (state->mState == RELEASED || state->mState == RELEASING) {
|
||||
// We're already released or doing it right now.
|
||||
if (sendCallback) {
|
||||
state.unlock();
|
||||
mCallback->onReleaseCompleted();
|
||||
state.lock();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (state->mState == ALLOCATING) {
|
||||
state->mState = RELEASING;
|
||||
// With the altered state allocate() would fail and clean up.
|
||||
if (sendCallback) {
|
||||
state.unlock();
|
||||
mCallback->onReleaseCompleted();
|
||||
state.lock();
|
||||
}
|
||||
return;
|
||||
}
|
||||
state->mState = RELEASING;
|
||||
}
|
||||
|
||||
std::thread([this, sendCallback] { release(sendCallback); }).detach();
|
||||
}
|
||||
|
||||
void CCodec::release(bool sendCallback) {
|
||||
std::shared_ptr<C2Component> comp;
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
if (state->mState == RELEASED) {
|
||||
if (sendCallback) {
|
||||
state.unlock();
|
||||
mCallback->onReleaseCompleted();
|
||||
state.lock();
|
||||
}
|
||||
return;
|
||||
}
|
||||
comp = state->mComp;
|
||||
}
|
||||
mChannel->stop();
|
||||
comp->release();
|
||||
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
state->mState = RELEASED;
|
||||
state->mComp.reset();
|
||||
}
|
||||
if (sendCallback) {
|
||||
mCallback->onReleaseCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
status_t CCodec::setSurface(const sp<Surface> &surface) {
|
||||
return mChannel->setSurface(surface);
|
||||
}
|
||||
|
||||
void CCodec::signalFlush() {
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
if (state->mState != RUNNING) {
|
||||
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
return;
|
||||
}
|
||||
state->mState = FLUSHING;
|
||||
}
|
||||
|
||||
(new AMessage(kWhatFlush, this))->post();
|
||||
}
|
||||
|
||||
void CCodec::flush() {
|
||||
std::shared_ptr<C2Component> comp;
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
if (state->mState != FLUSHING) {
|
||||
state.unlock();
|
||||
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
state.lock();
|
||||
return;
|
||||
}
|
||||
comp = state->mComp;
|
||||
}
|
||||
|
||||
mChannel->stop();
|
||||
|
||||
std::list<std::unique_ptr<C2Work>> flushedWork;
|
||||
c2_status_t err = comp->flush_sm(C2Component::FLUSH_COMPONENT, &flushedWork);
|
||||
if (err != C2_OK) {
|
||||
// TODO: convert err into status_t
|
||||
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
}
|
||||
|
||||
mChannel->flush(flushedWork);
|
||||
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
state->mState = FLUSHED;
|
||||
}
|
||||
mCallback->onFlushCompleted();
|
||||
}
|
||||
|
||||
void CCodec::signalResume() {
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
if (state->mState != FLUSHED) {
|
||||
state.unlock();
|
||||
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
state.lock();
|
||||
return;
|
||||
}
|
||||
state->mState = RESUMING;
|
||||
}
|
||||
|
||||
mChannel->start(nullptr, nullptr);
|
||||
|
||||
{
|
||||
Mutexed<State>::Locked state(mState);
|
||||
if (state->mState != RESUMING) {
|
||||
state.unlock();
|
||||
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
state.lock();
|
||||
return;
|
||||
}
|
||||
state->mState = RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
void CCodec::signalSetParameters(const sp<AMessage> &msg) {
|
||||
// TODO
|
||||
(void) msg;
|
||||
}
|
||||
|
||||
void CCodec::signalEndOfInputStream() {
|
||||
}
|
||||
|
||||
void CCodec::signalRequestIDRFrame() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void CCodec::onMessageReceived(const sp<AMessage> &msg) {
|
||||
TimePoint now = std::chrono::steady_clock::now();
|
||||
switch (msg->what()) {
|
||||
case kWhatAllocate: {
|
||||
// C2ComponentStore::createComponent() should return within 100ms.
|
||||
setDeadline(now + 150ms);
|
||||
AString componentName;
|
||||
CHECK(msg->findString("componentName", &componentName));
|
||||
allocate(componentName);
|
||||
break;
|
||||
}
|
||||
case kWhatConfigure: {
|
||||
// C2Component::commit_sm() should return within 5ms.
|
||||
setDeadline(now + 50ms);
|
||||
sp<AMessage> format;
|
||||
CHECK(msg->findMessage("format", &format));
|
||||
configure(format);
|
||||
break;
|
||||
}
|
||||
case kWhatStart: {
|
||||
// C2Component::start() should return within 500ms.
|
||||
setDeadline(now + 550ms);
|
||||
start();
|
||||
break;
|
||||
}
|
||||
case kWhatStop: {
|
||||
// C2Component::stop() should return within 500ms.
|
||||
setDeadline(now + 550ms);
|
||||
stop();
|
||||
break;
|
||||
}
|
||||
case kWhatFlush: {
|
||||
// C2Component::flush_sm() should return within 5ms.
|
||||
setDeadline(now + 50ms);
|
||||
flush();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
ALOGE("unrecognized message");
|
||||
break;
|
||||
}
|
||||
}
|
||||
setDeadline(TimePoint::max());
|
||||
}
|
||||
|
||||
void CCodec::setDeadline(const TimePoint &newDeadline) {
|
||||
Mutexed<TimePoint>::Locked deadline(mDeadline);
|
||||
*deadline = newDeadline;
|
||||
}
|
||||
|
||||
void CCodec::initiateReleaseIfStuck() {
|
||||
{
|
||||
Mutexed<TimePoint>::Locked deadline(mDeadline);
|
||||
if (*deadline >= std::chrono::steady_clock::now()) {
|
||||
// We're not stuck.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
initiateRelease();
|
||||
}
|
||||
|
||||
} // namespace android
|
@ -0,0 +1,589 @@
|
||||
/*
|
||||
* Copyright 2016, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_NDEBUG 0
|
||||
#define LOG_TAG "CCodecBufferChannel"
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <numeric>
|
||||
#include <thread>
|
||||
|
||||
#include <C2PlatformSupport.h>
|
||||
|
||||
#include <android/hardware/cas/native/1.0/IDescrambler.h>
|
||||
#include <binder/MemoryDealer.h>
|
||||
#include <gui/Surface.h>
|
||||
#include <media/openmax/OMX_Core.h>
|
||||
#include <media/stagefright/foundation/ABuffer.h>
|
||||
#include <media/stagefright/foundation/AMessage.h>
|
||||
#include <media/stagefright/foundation/AUtils.h>
|
||||
#include <media/stagefright/MediaCodec.h>
|
||||
#include <media/MediaCodecBuffer.h>
|
||||
#include <system/window.h>
|
||||
|
||||
#include "include/CCodecBufferChannel.h"
|
||||
#include "include/Codec2Buffer.h"
|
||||
#include "include/SecureBuffer.h"
|
||||
#include "include/SharedMemoryBuffer.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
using hardware::hidl_handle;
|
||||
using hardware::hidl_string;
|
||||
using hardware::hidl_vec;
|
||||
using namespace hardware::cas::V1_0;
|
||||
using namespace hardware::cas::native::V1_0;
|
||||
|
||||
// TODO: get this info from component
|
||||
const static size_t kMinBufferArraySize = 16;
|
||||
|
||||
void CCodecBufferChannel::OutputBuffers::flush(
|
||||
const std::list<std::unique_ptr<C2Work>> &flushedWork) {
|
||||
(void) flushedWork;
|
||||
// This is no-op by default unless we're in array mode where we need to keep
|
||||
// track of the flushed work.
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
template <class T>
|
||||
ssize_t findBufferSlot(
|
||||
std::vector<T> *buffers,
|
||||
size_t maxSize,
|
||||
std::function<bool(const T&)> pred) {
|
||||
auto it = std::find_if(buffers->begin(), buffers->end(), pred);
|
||||
if (it == buffers->end()) {
|
||||
if (buffers->size() < maxSize) {
|
||||
buffers->emplace_back();
|
||||
return buffers->size() - 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return std::distance(buffers->begin(), it);
|
||||
}
|
||||
|
||||
class LinearBuffer : public C2Buffer {
|
||||
public:
|
||||
explicit LinearBuffer(C2ConstLinearBlock block) : C2Buffer({ block }) {}
|
||||
};
|
||||
|
||||
class LinearInputBuffers : public CCodecBufferChannel::InputBuffers {
|
||||
public:
|
||||
using CCodecBufferChannel::InputBuffers::InputBuffers;
|
||||
|
||||
virtual bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
|
||||
*buffer = nullptr;
|
||||
ssize_t ret = findBufferSlot<wp<Codec2Buffer>>(
|
||||
&mBuffers, kMinBufferArraySize,
|
||||
[] (const auto &elem) { return elem.promote() == nullptr; });
|
||||
if (ret < 0) {
|
||||
return false;
|
||||
}
|
||||
std::shared_ptr<C2LinearBlock> block;
|
||||
|
||||
status_t err = mAlloc->fetchLinearBlock(
|
||||
// TODO: proper max input size
|
||||
65536,
|
||||
{ 0, C2MemoryUsage::kSoftwareWrite },
|
||||
&block);
|
||||
if (err != OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sp<Codec2Buffer> newBuffer = Codec2Buffer::allocate(mFormat, block);
|
||||
mBuffers[ret] = newBuffer;
|
||||
*index = ret;
|
||||
*buffer = newBuffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
|
||||
auto it = std::find(mBuffers.begin(), mBuffers.end(), buffer);
|
||||
if (it == mBuffers.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
sp<Codec2Buffer> codecBuffer = it->promote();
|
||||
// We got sp<> reference from the caller so this should never happen..
|
||||
CHECK(codecBuffer != nullptr);
|
||||
return std::make_shared<LinearBuffer>(codecBuffer->share());
|
||||
}
|
||||
|
||||
virtual void flush() override {
|
||||
}
|
||||
|
||||
private:
|
||||
// Buffers we passed to the client. The index of a buffer matches what
|
||||
// was passed in BufferCallback::onInputBufferAvailable().
|
||||
std::vector<wp<Codec2Buffer>> mBuffers;
|
||||
|
||||
// Buffer array we passed to the client. This only gets initialized at
|
||||
// getInput/OutputBufferArray() and when this is set we can't add more
|
||||
// buffers.
|
||||
std::vector<sp<Codec2Buffer>> mBufferArray;
|
||||
};
|
||||
|
||||
class GraphicOutputBuffers : public CCodecBufferChannel::OutputBuffers {
|
||||
public:
|
||||
using CCodecBufferChannel::OutputBuffers::OutputBuffers;
|
||||
|
||||
virtual bool registerBuffer(
|
||||
const std::shared_ptr<C2Buffer> &buffer,
|
||||
size_t *index,
|
||||
sp<MediaCodecBuffer> *codecBuffer) override {
|
||||
*codecBuffer = nullptr;
|
||||
ssize_t ret = findBufferSlot<BufferInfo>(
|
||||
&mBuffers,
|
||||
kMinBufferArraySize,
|
||||
[] (const auto &elem) { return elem.mClientBuffer.promote() == nullptr; });
|
||||
if (ret < 0) {
|
||||
return false;
|
||||
}
|
||||
sp<MediaCodecBuffer> newBuffer = new MediaCodecBuffer(
|
||||
mFormat,
|
||||
buffer == nullptr ? kEmptyBuffer : kDummyBuffer);
|
||||
mBuffers[ret] = { newBuffer, buffer };
|
||||
*index = ret;
|
||||
*codecBuffer = newBuffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
|
||||
auto it = std::find_if(
|
||||
mBuffers.begin(), mBuffers.end(),
|
||||
[buffer] (const auto &elem) {
|
||||
return elem.mClientBuffer.promote() == buffer;
|
||||
});
|
||||
if (it == mBuffers.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->mBufferRef;
|
||||
}
|
||||
|
||||
private:
|
||||
static const sp<ABuffer> kEmptyBuffer;
|
||||
static const sp<ABuffer> kDummyBuffer;
|
||||
|
||||
struct BufferInfo {
|
||||
// wp<> of MediaCodecBuffer for MediaCodec.
|
||||
wp<MediaCodecBuffer> mClientBuffer;
|
||||
// Buffer reference to hold until mClientBuffer is valid.
|
||||
std::shared_ptr<C2Buffer> mBufferRef;
|
||||
};
|
||||
// Buffers we passed to the client. The index of a buffer matches what
|
||||
// was passed in BufferCallback::onInputBufferAvailable().
|
||||
std::vector<BufferInfo> mBuffers;
|
||||
};
|
||||
|
||||
const sp<ABuffer> GraphicOutputBuffers::kEmptyBuffer = new ABuffer(nullptr, 0);
|
||||
const sp<ABuffer> GraphicOutputBuffers::kDummyBuffer = new ABuffer(nullptr, 1);
|
||||
|
||||
} // namespace
|
||||
|
||||
CCodecBufferChannel::QueueGuard::QueueGuard(
|
||||
CCodecBufferChannel::QueueSync &sync) : mSync(sync) {
|
||||
std::unique_lock<std::mutex> l(mSync.mMutex);
|
||||
if (mSync.mCount == -1) {
|
||||
mRunning = false;
|
||||
} else {
|
||||
++mSync.mCount;
|
||||
mRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
CCodecBufferChannel::QueueGuard::~QueueGuard() {
|
||||
if (mRunning) {
|
||||
--mSync.mCount;
|
||||
}
|
||||
}
|
||||
|
||||
void CCodecBufferChannel::QueueSync::start() {
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
// If stopped, it goes to running state; otherwise no-op.
|
||||
int32_t expected = -1;
|
||||
mCount.compare_exchange_strong(expected, 0);
|
||||
}
|
||||
|
||||
void CCodecBufferChannel::QueueSync::stop() {
|
||||
std::unique_lock<std::mutex> l(mMutex);
|
||||
if (mCount == -1) {
|
||||
// no-op
|
||||
return;
|
||||
}
|
||||
int32_t expected = 0;
|
||||
while (!mCount.compare_exchange_weak(expected, -1)) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
CCodecBufferChannel::CCodecBufferChannel(
|
||||
const std::function<void(status_t, enum ActionCode)> &onError)
|
||||
: mOnError(onError),
|
||||
mInputBuffers(new LinearInputBuffers),
|
||||
mOutputBuffers(new GraphicOutputBuffers),
|
||||
mFrameIndex(0u),
|
||||
mFirstValidFrameIndex(0u) {
|
||||
}
|
||||
|
||||
CCodecBufferChannel::~CCodecBufferChannel() {
|
||||
if (mCrypto != nullptr && mDealer != nullptr && mHeapSeqNum >= 0) {
|
||||
mCrypto->unsetHeap(mHeapSeqNum);
|
||||
}
|
||||
}
|
||||
|
||||
void CCodecBufferChannel::setComponent(const std::shared_ptr<C2Component> &component) {
|
||||
mComponent = component;
|
||||
// TODO: get pool ID from params
|
||||
std::shared_ptr<C2BlockPool> pool;
|
||||
c2_status_t err = GetCodec2BlockPool(C2BlockPool::BASIC_LINEAR, component, &pool);
|
||||
if (err == C2_OK) {
|
||||
Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
|
||||
(*buffers)->setAlloc(pool);
|
||||
}
|
||||
}
|
||||
|
||||
status_t CCodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {
|
||||
QueueGuard guard(mSync);
|
||||
if (!guard.isRunning()) {
|
||||
ALOGW("No more buffers should be queued at current state.");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int64_t timeUs;
|
||||
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
|
||||
|
||||
int32_t flags = 0;
|
||||
int32_t tmp = 0;
|
||||
if (buffer->meta()->findInt32("eos", &tmp) && tmp) {
|
||||
flags |= C2BufferPack::FLAG_END_OF_STREAM;
|
||||
ALOGV("input EOS");
|
||||
}
|
||||
if (buffer->meta()->findInt32("csd", &tmp) && tmp) {
|
||||
flags |= C2BufferPack::FLAG_CODEC_CONFIG;
|
||||
}
|
||||
std::unique_ptr<C2Work> work(new C2Work);
|
||||
work->input.flags = (C2BufferPack::flags_t)flags;
|
||||
work->input.ordinal.timestamp = timeUs;
|
||||
work->input.ordinal.frame_index = mFrameIndex++;
|
||||
work->input.buffers.clear();
|
||||
{
|
||||
Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
|
||||
work->input.buffers.push_back((*buffers)->releaseBuffer(buffer));
|
||||
}
|
||||
// TODO: fill info's
|
||||
|
||||
work->worklets.clear();
|
||||
work->worklets.emplace_back(new C2Worklet);
|
||||
|
||||
std::list<std::unique_ptr<C2Work>> items;
|
||||
items.push_back(std::move(work));
|
||||
return mComponent->queue_nb(&items);
|
||||
}
|
||||
|
||||
status_t CCodecBufferChannel::queueSecureInputBuffer(
|
||||
const sp<MediaCodecBuffer> &buffer, bool secure, const uint8_t *key,
|
||||
const uint8_t *iv, CryptoPlugin::Mode mode, CryptoPlugin::Pattern pattern,
|
||||
const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
|
||||
AString *errorDetailMsg) {
|
||||
// TODO
|
||||
(void) buffer;
|
||||
(void) secure;
|
||||
(void) key;
|
||||
(void) iv;
|
||||
(void) mode;
|
||||
(void) pattern;
|
||||
(void) subSamples;
|
||||
(void) numSubSamples;
|
||||
(void) errorDetailMsg;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
status_t CCodecBufferChannel::renderOutputBuffer(
|
||||
const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) {
|
||||
ALOGV("renderOutputBuffer");
|
||||
sp<MediaCodecBuffer> inBuffer;
|
||||
size_t index;
|
||||
{
|
||||
Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
|
||||
if (!(*buffers)->requestNewBuffer(&index, &inBuffer)) {
|
||||
inBuffer = nullptr;
|
||||
}
|
||||
}
|
||||
if (inBuffer != nullptr) {
|
||||
mCallback->onInputBufferAvailable(index, inBuffer);
|
||||
}
|
||||
|
||||
std::shared_ptr<C2Buffer> c2Buffer;
|
||||
{
|
||||
Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
|
||||
c2Buffer = (*buffers)->releaseBuffer(buffer);
|
||||
}
|
||||
|
||||
Mutexed<sp<Surface>>::Locked surface(mSurface);
|
||||
if (*surface == nullptr) {
|
||||
ALOGE("no surface");
|
||||
return OK;
|
||||
}
|
||||
|
||||
std::list<C2ConstGraphicBlock> blocks = c2Buffer->data().graphicBlocks();
|
||||
if (blocks.size() != 1u) {
|
||||
ALOGE("# of graphic blocks expected to be 1, but %zu", blocks.size());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(
|
||||
blocks.front().handle(),
|
||||
GraphicBuffer::CLONE_HANDLE,
|
||||
blocks.front().width(),
|
||||
blocks.front().height(),
|
||||
HAL_PIXEL_FORMAT_YV12,
|
||||
// TODO
|
||||
1,
|
||||
(uint64_t)GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
|
||||
// TODO
|
||||
blocks.front().width()));
|
||||
|
||||
status_t result = (*surface)->attachBuffer(graphicBuffer.get());
|
||||
if (result != OK) {
|
||||
ALOGE("attachBuffer failed: %d", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: read and set crop
|
||||
|
||||
result = native_window_set_buffers_timestamp((*surface).get(), timestampNs);
|
||||
ALOGW_IF(result != OK, "failed to set buffer timestamp: %d", result);
|
||||
|
||||
// TODO: fix after C2Fence implementation
|
||||
#if 0
|
||||
const C2Fence &fence = blocks.front().fence();
|
||||
result = ((ANativeWindow *)(*surface).get())->queueBuffer(
|
||||
(*surface).get(), graphicBuffer.get(), fence.valid() ? fence.fd() : -1);
|
||||
#else
|
||||
result = ((ANativeWindow *)(*surface).get())->queueBuffer(
|
||||
(*surface).get(), graphicBuffer.get(), -1);
|
||||
#endif
|
||||
if (result != OK) {
|
||||
ALOGE("queueBuffer failed: %d", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t CCodecBufferChannel::discardBuffer(const sp<MediaCodecBuffer> &buffer) {
|
||||
ALOGV("discardBuffer");
|
||||
{
|
||||
Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
|
||||
(void) (*buffers)->releaseBuffer(buffer);
|
||||
}
|
||||
{
|
||||
Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
|
||||
(void) (*buffers)->releaseBuffer(buffer);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void fillBufferArray_l(Mutexed<Buffers>::Locked &buffers) {
|
||||
for (size_t i = 0; i < buffers->mClientBuffer.size(); ++i) {
|
||||
sp<Codec2Buffer> buffer(buffers->mClientBuffer.get(i).promote());
|
||||
if (buffer == nullptr) {
|
||||
buffer = allocateBuffer_l(buffers->mAlloc);
|
||||
}
|
||||
buffers->mBufferArray.push_back(buffer);
|
||||
}
|
||||
while (buffers->mBufferArray.size() < kMinBufferArraySize) {
|
||||
sp<Codec2Buffer> buffer = allocateBuffer_l(buffers->mAlloc);
|
||||
// allocate buffer
|
||||
buffers->mBufferArray.push_back(buffer);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void CCodecBufferChannel::getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
|
||||
(void) array;
|
||||
// TODO
|
||||
#if 0
|
||||
array->clear();
|
||||
Mutexed<Buffers>::Locked buffers(mInputBuffers);
|
||||
|
||||
if (!buffers->isArrayMode()) {
|
||||
// mBufferArray is empty.
|
||||
fillBufferArray_l(buffers);
|
||||
}
|
||||
|
||||
for (const auto &buffer : buffers->mBufferArray) {
|
||||
array->push_back(buffer);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CCodecBufferChannel::getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
|
||||
(void) array;
|
||||
// TODO
|
||||
#if 0
|
||||
array->clear();
|
||||
Mutexed<Buffers>::Locked buffers(mOutputBuffers);
|
||||
|
||||
if (!buffers->isArrayMode()) {
|
||||
if (linear) {
|
||||
// mBufferArray is empty.
|
||||
fillBufferArray_l(buffers);
|
||||
|
||||
// We need to replace the allocator so that the component only returns
|
||||
// buffer from the array.
|
||||
ArrayModeAllocator::Builder builder(buffers->mBufferArray);
|
||||
for (size_t i = 0; i < buffers->mClientBuffer.size(); ++i) {
|
||||
if (buffers->mClientBuffer.get(i).promote() != nullptr) {
|
||||
builder.markUsing(i);
|
||||
}
|
||||
}
|
||||
buffers->mAlloc.reset(builder.build());
|
||||
} else {
|
||||
for (int i = 0; i < X; ++i) {
|
||||
buffers->mBufferArray.push_back(dummy buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &buffer : buffers->mBufferArray) {
|
||||
array->push_back(buffer);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CCodecBufferChannel::start(const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) {
|
||||
if (inputFormat != nullptr) {
|
||||
Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
|
||||
(*buffers)->setFormat(inputFormat);
|
||||
}
|
||||
if (outputFormat != nullptr) {
|
||||
Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
|
||||
(*buffers)->setFormat(outputFormat);
|
||||
}
|
||||
|
||||
mSync.start();
|
||||
// TODO: use proper buffer depth instead of this random value
|
||||
for (size_t i = 0; i < kMinBufferArraySize; ++i) {
|
||||
size_t index;
|
||||
sp<MediaCodecBuffer> buffer;
|
||||
{
|
||||
Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
|
||||
if (!(*buffers)->requestNewBuffer(&index, &buffer)) {
|
||||
buffers.unlock();
|
||||
ALOGE("start: cannot allocate memory");
|
||||
mOnError(NO_MEMORY, ACTION_CODE_FATAL);
|
||||
buffers.lock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
mCallback->onInputBufferAvailable(index, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void CCodecBufferChannel::stop() {
|
||||
mSync.stop();
|
||||
mFirstValidFrameIndex = mFrameIndex.load();
|
||||
}
|
||||
|
||||
void CCodecBufferChannel::flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) {
|
||||
{
|
||||
Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
|
||||
(*buffers)->flush();
|
||||
}
|
||||
{
|
||||
Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
|
||||
(*buffers)->flush(flushedWork);
|
||||
}
|
||||
}
|
||||
|
||||
void CCodecBufferChannel::onWorkDone(std::vector<std::unique_ptr<C2Work>> workItems) {
|
||||
for (const auto &work : workItems) {
|
||||
if (work->result != OK) {
|
||||
ALOGE("work failed to complete: %d", work->result);
|
||||
mOnError(work->result, ACTION_CODE_FATAL);
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: MediaCodec usage supposedly have only one worklet
|
||||
if (work->worklets.size() != 1u) {
|
||||
ALOGE("incorrect number of worklets: %zu", work->worklets.size());
|
||||
mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::unique_ptr<C2Worklet> &worklet = work->worklets.front();
|
||||
if (worklet->output.ordinal.frame_index < mFirstValidFrameIndex) {
|
||||
// Discard frames from previous generation.
|
||||
continue;
|
||||
}
|
||||
// NOTE: MediaCodec usage supposedly have only one output stream.
|
||||
if (worklet->output.buffers.size() != 1u) {
|
||||
ALOGE("incorrect number of output buffers: %zu", worklet->output.buffers.size());
|
||||
mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::shared_ptr<C2Buffer> &buffer = worklet->output.buffers[0];
|
||||
// TODO: transfer infos() into buffer metadata
|
||||
|
||||
int32_t flags = 0;
|
||||
if (worklet->output.flags & C2BufferPack::FLAG_END_OF_STREAM) {
|
||||
flags |= MediaCodec::BUFFER_FLAG_EOS;
|
||||
ALOGV("output EOS");
|
||||
}
|
||||
|
||||
size_t index;
|
||||
sp<MediaCodecBuffer> outBuffer;
|
||||
Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
|
||||
if (!(*buffers)->registerBuffer(buffer, &index, &outBuffer)) {
|
||||
ALOGE("unable to register output buffer");
|
||||
mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
|
||||
continue;
|
||||
}
|
||||
|
||||
outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp);
|
||||
outBuffer->meta()->setInt32("flags", flags);
|
||||
ALOGV("index = %zu", index);
|
||||
mCallback->onOutputBufferAvailable(index, outBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
status_t CCodecBufferChannel::setSurface(const sp<Surface> &newSurface) {
|
||||
if (newSurface != nullptr) {
|
||||
newSurface->setScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
|
||||
}
|
||||
|
||||
Mutexed<sp<Surface>>::Locked surface(mSurface);
|
||||
// if (newSurface == nullptr) {
|
||||
// if (*surface != nullptr) {
|
||||
// ALOGW("cannot unset a surface");
|
||||
// return INVALID_OPERATION;
|
||||
// }
|
||||
// return OK;
|
||||
// }
|
||||
//
|
||||
// if (*surface == nullptr) {
|
||||
// ALOGW("component was not configured with a surface");
|
||||
// return INVALID_OPERATION;
|
||||
// }
|
||||
|
||||
*surface = newSurface;
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace android
|
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright 2017, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef A_BUFFER_CHANNEL_H_
|
||||
|
||||
#define A_BUFFER_CHANNEL_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include <C2Buffer.h>
|
||||
#include <C2Component.h>
|
||||
|
||||
#include <media/stagefright/foundation/Mutexed.h>
|
||||
#include <media/stagefright/CodecBase.h>
|
||||
#include <media/ICrypto.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/**
|
||||
* BufferChannelBase implementation for ACodec.
|
||||
*/
|
||||
class CCodecBufferChannel : public BufferChannelBase {
|
||||
public:
|
||||
class Buffers {
|
||||
public:
|
||||
Buffers() = default;
|
||||
virtual ~Buffers() = default;
|
||||
|
||||
inline void setAlloc(const std::shared_ptr<C2BlockPool> &alloc) { mAlloc = alloc; }
|
||||
inline void setFormat(const sp<AMessage> &format) { mFormat = format; }
|
||||
inline const std::shared_ptr<C2BlockPool> &getAlloc() { return mAlloc; }
|
||||
|
||||
protected:
|
||||
// Input: this object uses it to allocate input buffers with which the
|
||||
// client fills.
|
||||
// Output: this object passes it to the component.
|
||||
std::shared_ptr<C2BlockPool> mAlloc;
|
||||
sp<AMessage> mFormat;
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(Buffers);
|
||||
};
|
||||
|
||||
class InputBuffers : public Buffers {
|
||||
public:
|
||||
using Buffers::Buffers;
|
||||
virtual ~InputBuffers() = default;
|
||||
|
||||
virtual bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) = 0;
|
||||
virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) = 0;
|
||||
virtual void flush() = 0;
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(InputBuffers);
|
||||
};
|
||||
|
||||
class OutputBuffers : public Buffers {
|
||||
public:
|
||||
using Buffers::Buffers;
|
||||
virtual ~OutputBuffers() = default;
|
||||
|
||||
virtual bool registerBuffer(
|
||||
const std::shared_ptr<C2Buffer> &buffer,
|
||||
size_t *index,
|
||||
sp<MediaCodecBuffer> *codecBuffer) = 0;
|
||||
virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) = 0;
|
||||
virtual void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork);
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(OutputBuffers);
|
||||
};
|
||||
|
||||
CCodecBufferChannel(const std::function<void(status_t, enum ActionCode)> &onError);
|
||||
virtual ~CCodecBufferChannel();
|
||||
|
||||
// BufferChannelBase interface
|
||||
virtual status_t queueInputBuffer(const sp<MediaCodecBuffer> &buffer) override;
|
||||
virtual status_t queueSecureInputBuffer(
|
||||
const sp<MediaCodecBuffer> &buffer,
|
||||
bool secure,
|
||||
const uint8_t *key,
|
||||
const uint8_t *iv,
|
||||
CryptoPlugin::Mode mode,
|
||||
CryptoPlugin::Pattern pattern,
|
||||
const CryptoPlugin::SubSample *subSamples,
|
||||
size_t numSubSamples,
|
||||
AString *errorDetailMsg) override;
|
||||
virtual status_t renderOutputBuffer(
|
||||
const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) override;
|
||||
virtual status_t discardBuffer(const sp<MediaCodecBuffer> &buffer) override;
|
||||
virtual void getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) override;
|
||||
virtual void getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) override;
|
||||
|
||||
// Methods below are interface for CCodec to use.
|
||||
|
||||
void setComponent(const std::shared_ptr<C2Component> &component);
|
||||
status_t setSurface(const sp<Surface> &surface);
|
||||
|
||||
/**
|
||||
* Set C2BlockPool for input buffers.
|
||||
*
|
||||
* TODO: start timestamp?
|
||||
*/
|
||||
void setInputBufferAllocator(const sp<C2BlockPool> &inAlloc);
|
||||
|
||||
/**
|
||||
* Set C2BlockPool for output buffers. This object shall never use the
|
||||
* allocator itself; it's just passed
|
||||
*
|
||||
* TODO: start timestamp?
|
||||
*/
|
||||
void setOutputBufferAllocator(const sp<C2BlockPool> &outAlloc);
|
||||
|
||||
/**
|
||||
* Start queueing buffers to the component. This object should never queue
|
||||
* buffers before this call.
|
||||
*/
|
||||
void start(const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat);
|
||||
|
||||
/**
|
||||
* Stop queueing buffers to the component. This object should never queue
|
||||
* buffers after this call, until start() is called.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork);
|
||||
|
||||
/**
|
||||
* Notify MediaCodec about work done.
|
||||
*
|
||||
* @param workItems finished work items.
|
||||
*/
|
||||
void onWorkDone(std::vector<std::unique_ptr<C2Work>> workItems);
|
||||
|
||||
private:
|
||||
class QueueGuard;
|
||||
|
||||
class QueueSync {
|
||||
public:
|
||||
inline QueueSync() : mCount(-1) {}
|
||||
~QueueSync() = default;
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
std::mutex mMutex;
|
||||
std::atomic_int32_t mCount;
|
||||
|
||||
friend class CCodecBufferChannel::QueueGuard;
|
||||
};
|
||||
|
||||
class QueueGuard {
|
||||
public:
|
||||
QueueGuard(QueueSync &sync);
|
||||
~QueueGuard();
|
||||
inline bool isRunning() { return mRunning; }
|
||||
|
||||
private:
|
||||
QueueSync &mSync;
|
||||
bool mRunning;
|
||||
};
|
||||
|
||||
QueueSync mSync;
|
||||
sp<MemoryDealer> mDealer;
|
||||
sp<IMemory> mDecryptDestination;
|
||||
int32_t mHeapSeqNum;
|
||||
|
||||
std::shared_ptr<C2Component> mComponent;
|
||||
std::function<void(status_t, enum ActionCode)> mOnError;
|
||||
std::shared_ptr<C2BlockPool> mInputAllocator;
|
||||
QueueSync mQueueSync;
|
||||
Mutexed<std::unique_ptr<InputBuffers>> mInputBuffers;
|
||||
Mutexed<std::unique_ptr<OutputBuffers>> mOutputBuffers;
|
||||
|
||||
std::atomic_uint64_t mFrameIndex;
|
||||
std::atomic_uint64_t mFirstValidFrameIndex;
|
||||
|
||||
sp<MemoryDealer> makeMemoryDealer(size_t heapSize);
|
||||
Mutexed<sp<Surface>> mSurface;
|
||||
|
||||
inline bool hasCryptoOrDescrambler() {
|
||||
return mCrypto != NULL || mDescrambler != NULL;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // A_BUFFER_CHANNEL_H_
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2017, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef CODEC2_BUFFER_H_
|
||||
|
||||
#define CODEC2_BUFFER_H_
|
||||
|
||||
#include <C2Buffer.h>
|
||||
|
||||
#include <media/MediaCodecBuffer.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class C2Buffer;
|
||||
|
||||
/**
|
||||
* MediaCodecBuffer implementation wraps around C2LinearBlock.
|
||||
*/
|
||||
class Codec2Buffer : public MediaCodecBuffer {
|
||||
public:
|
||||
static sp<Codec2Buffer> allocate(
|
||||
const sp<AMessage> &format, const std::shared_ptr<C2LinearBlock> &block);
|
||||
|
||||
virtual ~Codec2Buffer() = default;
|
||||
|
||||
C2ConstLinearBlock share();
|
||||
|
||||
private:
|
||||
Codec2Buffer(
|
||||
const sp<AMessage> &format,
|
||||
const sp<ABuffer> &buffer,
|
||||
const std::shared_ptr<C2LinearBlock> &block);
|
||||
Codec2Buffer() = delete;
|
||||
|
||||
std::shared_ptr<C2LinearBlock> mBlock;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // CODEC2_BUFFER_H_
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef C_CODEC_H_
|
||||
#define C_CODEC_H_
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <C2Component.h>
|
||||
|
||||
#include <android/native_window.h>
|
||||
#include <media/hardware/MetadataBufferType.h>
|
||||
#include <media/stagefright/foundation/Mutexed.h>
|
||||
#include <media/stagefright/CodecBase.h>
|
||||
#include <media/stagefright/FrameRenderTracker.h>
|
||||
#include <media/stagefright/MediaDefs.h>
|
||||
#include <media/stagefright/SkipCutBuffer.h>
|
||||
#include <utils/NativeHandle.h>
|
||||
#include <hardware/gralloc.h>
|
||||
#include <nativebase/nativebase.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class CCodecBufferChannel;
|
||||
|
||||
class CCodec : public CodecBase {
|
||||
public:
|
||||
CCodec();
|
||||
|
||||
virtual std::shared_ptr<BufferChannelBase> getBufferChannel() override;
|
||||
virtual void initiateAllocateComponent(const sp<AMessage> &msg) override;
|
||||
virtual void initiateConfigureComponent(const sp<AMessage> &msg) override;
|
||||
virtual void initiateCreateInputSurface() override;
|
||||
virtual void initiateSetInputSurface(const sp<PersistentSurface> &surface) override;
|
||||
virtual void initiateStart() override;
|
||||
virtual void initiateShutdown(bool keepComponentAllocated = false) override;
|
||||
|
||||
virtual status_t setSurface(const sp<Surface> &surface) override;
|
||||
|
||||
virtual void signalFlush() override;
|
||||
virtual void signalResume() override;
|
||||
|
||||
virtual void signalSetParameters(const sp<AMessage> &msg) override;
|
||||
virtual void signalEndOfInputStream() override;
|
||||
virtual void signalRequestIDRFrame() override;
|
||||
|
||||
void initiateReleaseIfStuck();
|
||||
|
||||
protected:
|
||||
virtual ~CCodec();
|
||||
|
||||
virtual void onMessageReceived(const sp<AMessage> &msg) override;
|
||||
|
||||
private:
|
||||
typedef std::chrono::time_point<std::chrono::steady_clock> TimePoint;
|
||||
|
||||
void initiateStop();
|
||||
void initiateRelease(bool sendCallback = true);
|
||||
|
||||
void allocate(const AString &componentName);
|
||||
void configure(const sp<AMessage> &msg);
|
||||
void start();
|
||||
void stop();
|
||||
void flush();
|
||||
void release(bool sendCallback);
|
||||
|
||||
void setDeadline(const TimePoint &deadline);
|
||||
|
||||
enum {
|
||||
kWhatAllocate,
|
||||
kWhatConfigure,
|
||||
kWhatStart,
|
||||
kWhatFlush,
|
||||
kWhatStop,
|
||||
kWhatRelease,
|
||||
};
|
||||
|
||||
enum {
|
||||
RELEASED,
|
||||
ALLOCATED,
|
||||
FLUSHED,
|
||||
RUNNING,
|
||||
|
||||
ALLOCATING, // RELEASED -> ALLOCATED
|
||||
STARTING, // ALLOCATED -> RUNNING
|
||||
STOPPING, // RUNNING -> ALLOCATED
|
||||
FLUSHING, // RUNNING -> FLUSHED
|
||||
RESUMING, // FLUSHED -> RUNNING
|
||||
RELEASING, // {ANY EXCEPT RELEASED} -> RELEASED
|
||||
};
|
||||
|
||||
struct State {
|
||||
inline State() : mState(RELEASED) {}
|
||||
|
||||
int mState;
|
||||
std::shared_ptr<C2Component> mComp;
|
||||
};
|
||||
|
||||
struct Formats {
|
||||
sp<AMessage> mInputFormat;
|
||||
sp<AMessage> mOutputFormat;
|
||||
};
|
||||
|
||||
Mutexed<State> mState;
|
||||
std::shared_ptr<CCodecBufferChannel> mChannel;
|
||||
std::shared_ptr<C2Component::Listener> mListener;
|
||||
Mutexed<TimePoint> mDeadline;
|
||||
Mutexed<Formats> mFormats;
|
||||
|
||||
DISALLOW_EVIL_CONSTRUCTORS(CCodec);
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // C_CODEC_H_
|
Loading…
Reference in new issue