CCodec: Episode I --- Barely Decoding Video

- 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: I36d5f476099b5c055c0be0244e99bdf9dd28441e
gugelfrei
Wonsik Kim 7 years ago
parent cc25a713d9
commit 4f87426e12

@ -80,6 +80,7 @@ static bool gWriteMP4;
static bool gDisplayHistogram;
static bool showProgress = true;
static String8 gWriteMP4Filename;
static String8 gComponentNameOverride;
static sp<ANativeWindow> gSurface;
@ -193,7 +194,10 @@ static void playSource(sp<MediaSource> &source) {
CHECK(!gPreferSoftwareCodec);
flags |= MediaCodecList::kHardwareCodecsOnly;
}
rawSource = SimpleDecodingSource::Create(source, flags, gSurface);
rawSource = SimpleDecodingSource::Create(
source, flags, gSurface,
gComponentNameOverride.isEmpty() ? nullptr : gComponentNameOverride.c_str(),
!gComponentNameOverride.isEmpty());
if (rawSource == NULL) {
return;
}
@ -618,6 +622,7 @@ static void usage(const char *me) {
fprintf(stderr, " -o playback audio\n");
fprintf(stderr, " -w(rite) filename (write to .mp4 file)\n");
fprintf(stderr, " -k seek test\n");
fprintf(stderr, " -O(verride) name of the component\n");
fprintf(stderr, " -x display a histogram of decoding times/fps "
"(video only)\n");
fprintf(stderr, " -q don't show progress indicator\n");
@ -703,7 +708,7 @@ int main(int argc, char **argv) {
sp<ALooper> looper;
int res;
while ((res = getopt(argc, argv, "haqn:lm:b:ptsrow:kxSTd:D:")) >= 0) {
while ((res = getopt(argc, argv, "haqn:lm:b:ptsrow:kO:xSTd:D:")) >= 0) {
switch (res) {
case 'a':
{
@ -732,6 +737,12 @@ int main(int argc, char **argv) {
break;
}
case 'O':
{
gComponentNameOverride.setTo(optarg);
break;
}
case 'l':
{
listComponents = true;
@ -1073,7 +1084,7 @@ int main(int argc, char **argv) {
i, MediaExtractor::kIncludeExtensiveMetaData);
if (meta == NULL) {
break;
continue;
}
const char *mime;
meta->findCString(kKeyMIMEType, &mime);

@ -37,6 +37,8 @@ cc_library_shared {
"AudioPlayer.cpp",
"AudioSource.cpp",
"BufferImpl.cpp",
"CCodec.cpp",
"CCodecBufferChannel.cpp",
"CodecBase.cpp",
"CallbackDataSource.cpp",
"CallbackMediaSource.cpp",
@ -89,6 +91,7 @@ cc_library_shared {
"libdl",
"libdrmframework",
"libgui",
"libion",
"liblog",
"libmedia",
"libmedia_omx",
@ -100,6 +103,7 @@ cc_library_shared {
"libui",
"libutils",
"libmedia_helper",
"libstagefright_codec2",
"libstagefright_foundation",
"libstagefright_omx",
"libstagefright_omx_utils",
@ -111,6 +115,11 @@ cc_library_shared {
"android.hidl.allocator@1.0",
"android.hardware.cas.native@1.0",
"android.hardware.media.omx@1.0",
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.mapper@2.0",
// XXX: hack
"libstagefright_soft_c2avcdec",
],
static_libs: [
@ -125,6 +134,9 @@ cc_library_shared {
"libstagefright_esds",
"libstagefright_id3",
"libFLAC",
// XXX: hack
"libstagefright_codec2_vndk",
],
export_shared_lib_headers: [

@ -24,11 +24,14 @@
#include <media/ICrypto.h>
#include <utils/NativeHandle.h>
#include "include/Codec2Buffer.h"
#include "include/SecureBuffer.h"
#include "include/SharedMemoryBuffer.h"
namespace android {
// SharedMemoryBuffer
SharedMemoryBuffer::SharedMemoryBuffer(const sp<AMessage> &format, const sp<IMemory> &mem)
: MediaCodecBuffer(format, new ABuffer(mem->pointer(), mem->size())),
mMemory(mem) {
@ -39,6 +42,8 @@ SharedMemoryBuffer::SharedMemoryBuffer(const sp<AMessage> &format, const sp<TMem
mTMemory(mem) {
}
// SecureBuffer
SecureBuffer::SecureBuffer(const sp<AMessage> &format, const void *ptr, size_t size)
: MediaCodecBuffer(format, new ABuffer(nullptr, size)),
mPointer(ptr) {
@ -59,4 +64,28 @@ ICrypto::DestinationType SecureBuffer::getDestinationType() {
return ICrypto::kDestinationTypeNativeHandle;
}
// Codec2Buffer
// static
sp<Codec2Buffer> Codec2Buffer::allocate(
const sp<AMessage> &format, const std::shared_ptr<C2LinearBlock> &block) {
C2WriteView writeView(block->map().get());
if (writeView.error() != C2_OK) {
return nullptr;
}
return new Codec2Buffer(format, new ABuffer(writeView.base(), writeView.capacity()), block);
}
C2ConstLinearBlock Codec2Buffer::share() {
return mBlock->share(offset(), size(), C2Fence());
}
Codec2Buffer::Codec2Buffer(
const sp<AMessage> &format,
const sp<ABuffer> &buffer,
const std::shared_ptr<C2LinearBlock> &block)
: MediaCodecBuffer(format, buffer),
mBlock(block) {
}
} // namespace android

@ -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

@ -28,6 +28,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/MemoryDealer.h>
#include <cutils/properties.h>
#include <gui/BufferQueue.h>
#include <gui/Surface.h>
#include <media/ICrypto.h>
@ -44,6 +45,7 @@
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/ACodec.h>
#include <media/stagefright/BufferProducerWrapper.h>
#include <media/stagefright/CCodec.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaDefs.h>
@ -549,8 +551,11 @@ void MediaCodec::PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err)
//static
sp<CodecBase> MediaCodec::GetCodecBase(const AString &name, bool nameIsType) {
// at this time only ACodec specifies a mime type.
if (nameIsType || name.startsWithIgnoreCase("omx.")) {
static bool ccodecEnabled = property_get_bool("debug.stagefright.ccodec", false);
if (ccodecEnabled && !nameIsType && name.startsWithIgnoreCase("codec2.")) {
return new CCodec;
} else if (nameIsType || name.startsWithIgnoreCase("omx.")) {
// at this time only ACodec specifies a mime type.
return new ACodec;
} else if (name.startsWithIgnoreCase("android.filter.")) {
return new MediaFilter;
@ -1849,7 +1854,6 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
}
}
}
if (mFlags & kFlagIsAsync) {
onOutputFormatChanged();
} else {

@ -14,6 +14,10 @@
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "SimpleDecodingSource"
#include <utils/Log.h>
#include <gui/Surface.h>
#include <media/ICrypto.h>
@ -43,7 +47,7 @@ sp<SimpleDecodingSource> SimpleDecodingSource::Create(
//static
sp<SimpleDecodingSource> SimpleDecodingSource::Create(
const sp<MediaSource> &source, uint32_t flags, const sp<ANativeWindow> &nativeWindow,
const char *desiredCodec) {
const char *desiredCodec, bool skipMediaCodecList) {
sp<Surface> surface = static_cast<Surface*>(nativeWindow.get());
const char *mime = NULL;
sp<MetaData> meta = source->getFormat();
@ -63,32 +67,47 @@ sp<SimpleDecodingSource> SimpleDecodingSource::Create(
looper->start();
sp<MediaCodec> codec;
for (size_t i = 0; i < matchingCodecs.size(); ++i) {
const AString &componentName = matchingCodecs[i];
if (desiredCodec != NULL && componentName.compare(desiredCodec)) {
continue;
}
ALOGV("Attempting to allocate codec '%s'", componentName.c_str());
codec = MediaCodec::CreateByComponentName(looper, componentName);
auto configure = [=](const sp<MediaCodec> &codec, const AString &componentName)
-> sp<SimpleDecodingSource> {
if (codec != NULL) {
ALOGI("Successfully allocated codec '%s'", componentName.c_str());
status_t err = codec->configure(format, surface, NULL /* crypto */, 0 /* flags */);
sp<AMessage> outFormat;
if (err == OK) {
err = codec->getOutputFormat(&format);
err = codec->getOutputFormat(&outFormat);
}
if (err == OK) {
return new SimpleDecodingSource(codec, source, looper,
surface != NULL,
strcmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS) == 0,
format);
outFormat);
}
ALOGD("Failed to configure codec '%s'", componentName.c_str());
codec->release();
}
return NULL;
};
if (skipMediaCodecList) {
codec = MediaCodec::CreateByComponentName(looper, desiredCodec);
return configure(codec, desiredCodec);
}
for (size_t i = 0; i < matchingCodecs.size(); ++i) {
const AString &componentName = matchingCodecs[i];
if (desiredCodec != NULL && componentName.compare(desiredCodec)) {
continue;
}
ALOGV("Attempting to allocate codec '%s'", componentName.c_str());
codec = MediaCodec::CreateByComponentName(looper, componentName);
sp<SimpleDecodingSource> res = configure(codec, componentName);
if (res != NULL) {
return res;
} else {
codec = NULL;
}
}

@ -12,6 +12,10 @@ cc_library_shared {
"frameworks/native/include/media/hardware",
],
export_include_dirs: [
"include",
],
sanitize: {
misc_undefined: [
"unsigned-integer-overflow",

@ -9,6 +9,10 @@ cc_library_static {
"C2Store.cpp",
],
export_include_dirs: [
"include",
],
include_dirs: [
"frameworks/av/media/libstagefright/codec2/include",
"frameworks/av/media/libstagefright/codec2/vndk/include",

@ -790,6 +790,7 @@ std::shared_ptr<C2ComponentInterface> C2SoftAvcDec::intf() {
}
void C2SoftAvcDec::processQueue() {
#if 0
if (mIsInFlush) {
setFlushMode();
@ -825,9 +826,10 @@ void C2SoftAvcDec::processQueue() {
}
mIsInFlush = false;
}
#endif
std::unique_ptr<C2Work> work;
{
if (!mIsInFlush) {
std::unique_lock<std::mutex> lock(mQueueLock);
if (mQueue.empty()) {
mQueueCond.wait(lock);
@ -844,7 +846,7 @@ void C2SoftAvcDec::processQueue() {
process(work);
std::vector<std::unique_ptr<C2Work>> done;
{
if (work) {
std::unique_lock<std::mutex> lock(mPendingLock);
uint32_t index = work->input.ordinal.frame_index;
mPendingWork[index].swap(work);
@ -871,12 +873,12 @@ void C2SoftAvcDec::processQueue() {
static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) {
UNUSED(ctxt);
(void) ctxt;
return memalign(alignment, size);
}
static void ivd_aligned_free(void *ctxt, void *buf) {
UNUSED(ctxt);
(void) ctxt;
free(buf);
return;
}
@ -1001,6 +1003,7 @@ status_t C2SoftAvcDec::setNumCores() {
}
status_t C2SoftAvcDec::setFlushMode() {
ALOGV("setFlushMode");
IV_API_CALL_STATUS_T status;
ivd_ctl_flush_ip_t s_video_flush_ip;
ivd_ctl_flush_op_t s_video_flush_op;
@ -1019,7 +1022,7 @@ status_t C2SoftAvcDec::setFlushMode() {
s_video_flush_op.u4_error_code);
return UNKNOWN_ERROR;
}
mIsInFlush = true;
return OK;
}
@ -1079,7 +1082,6 @@ status_t C2SoftAvcDec::initDecoder() {
}
status_t C2SoftAvcDec::deInitDecoder() {
size_t i;
IV_API_CALL_STATUS_T status;
if (mCodecCtx) {
@ -1206,7 +1208,6 @@ void C2SoftAvcDec::process(std::unique_ptr<C2Work> &work) {
if (mSignalledError) {
return;
}
if (NULL == mCodecCtx) {
if (OK != initDecoder()) {
ALOGE("Failed to initialize decoder");
@ -1221,66 +1222,78 @@ void C2SoftAvcDec::process(std::unique_ptr<C2Work> &work) {
setParams(mStride);
}
const C2ConstLinearBlock &buffer =
work->input.buffers[0]->data().linearBlocks().front();
if (buffer.capacity() == 0) {
// TODO: result?
std::vector<std::unique_ptr<C2Work>> done;
done.emplace_back(std::move(work));
mListener->onWorkDone_nb(shared_from_this(), std::move(done));
if (!(work->input.flags & C2BufferPack::FLAG_END_OF_STREAM)) {
return;
}
uint32_t workIndex = 0;
std::unique_ptr<C2ReadView> input;
if (work) {
work->result = C2_OK;
mReceivedEOS = true;
// TODO: flush
} else if (work->input.flags & C2BufferPack::FLAG_END_OF_STREAM) {
mReceivedEOS = true;
}
const C2ConstLinearBlock &buffer =
work->input.buffers[0]->data().linearBlocks().front();
if (buffer.capacity() == 0) {
// TODO: result?
C2ReadView input = work->input.buffers[0]->data().linearBlocks().front().map().get();
uint32_t workIndex = work->input.ordinal.frame_index & 0xFFFFFFFF;
// TODO: populate --- assume display order?
if (!mAllocatedBlock) {
// TODO: error handling
// TODO: format & usage
uint32_t format = HAL_PIXEL_FORMAT_YV12;
C2MemoryUsage usage = { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite };
// TODO: lock access to interface
C2BlockPool::local_id_t poolId =
mIntf->mOutputBlockPools->flexCount() ?
mIntf->mOutputBlockPools->m.mValues[0] : C2BlockPool::BASIC_GRAPHIC;
if (!mOutputBlockPool || mOutputBlockPool->getLocalId() != poolId) {
c2_status_t err = GetCodec2BlockPool(poolId, shared_from_this(), &mOutputBlockPool);
if (err != C2_OK) {
// TODO: trip
std::vector<std::unique_ptr<C2Work>> done;
done.emplace_back(std::move(work));
mListener->onWorkDone_nb(shared_from_this(), std::move(done));
if (!(work->input.flags & C2BufferPack::FLAG_END_OF_STREAM)) {
return;
}
mReceivedEOS = true;
// TODO: flush
} else if (work->input.flags & C2BufferPack::FLAG_END_OF_STREAM) {
ALOGV("input EOS: %llu", work->input.ordinal.frame_index);
mReceivedEOS = true;
}
ALOGE("using allocator %u", mOutputBlockPool->getAllocatorId());
(void)mOutputBlockPool->fetchGraphicBlock(
mWidth, mHeight, format, usage, &mAllocatedBlock);
ALOGE("provided (%dx%d) required (%dx%d)", mAllocatedBlock->width(), mAllocatedBlock->height(), mWidth, mHeight);
input.reset(new C2ReadView(work->input.buffers[0]->data().linearBlocks().front().map().get()));
workIndex = work->input.ordinal.frame_index & 0xFFFFFFFF;
}
C2GraphicView output = mAllocatedBlock->map().get();
ALOGE("mapped err = %d", output.error());
size_t inOffset = 0u;
while (inOffset < input.capacity()) {
while (!input || inOffset < input->capacity()) {
if (!input) {
ALOGV("flushing");
}
// TODO: populate --- assume display order?
if (!mAllocatedBlock) {
// TODO: error handling
// TODO: format & usage
uint32_t format = HAL_PIXEL_FORMAT_YV12;
C2MemoryUsage usage = { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite };
// TODO: lock access to interface
C2BlockPool::local_id_t poolId =
mIntf->mOutputBlockPools->flexCount() ?
mIntf->mOutputBlockPools->m.mValues[0] : C2BlockPool::BASIC_GRAPHIC;
if (!mOutputBlockPool || mOutputBlockPool->getLocalId() != poolId) {
c2_status_t err = GetCodec2BlockPool(poolId, shared_from_this(), &mOutputBlockPool);
if (err != C2_OK) {
// TODO: trip
}
}
ALOGE("using allocator %u", mOutputBlockPool->getAllocatorId());
(void)mOutputBlockPool->fetchGraphicBlock(
mWidth, mHeight, format, usage, &mAllocatedBlock);
ALOGE("provided (%dx%d) required (%dx%d)", mAllocatedBlock->width(), mAllocatedBlock->height(), mWidth, mHeight);
}
C2GraphicView output = mAllocatedBlock->map().get();
if (output.error() != OK) {
ALOGE("mapped err = %d", output.error());
}
ivd_video_decode_ip_t s_dec_ip;
ivd_video_decode_op_t s_dec_op;
WORD32 timeDelay, timeTaken;
size_t sizeY, sizeUV;
if (!setDecodeArgs(&s_dec_ip, &s_dec_op, &input, &output, workIndex, inOffset)) {
if (!setDecodeArgs(&s_dec_ip, &s_dec_op, input.get(), &output, workIndex, inOffset)) {
ALOGE("Decoder arg setup failed");
// TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
mSignalledError = true;
return;
}
ALOGE("Decoder arg setup succeeded");
ALOGV("Decoder arg setup succeeded");
// If input dump is enabled, then write to file
DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes, mInputOffset);
@ -1321,15 +1334,24 @@ void C2SoftAvcDec::process(std::unique_ptr<C2Work> &work) {
PRINT_TIME("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay,
s_dec_op.u4_num_bytes_consumed);
ALOGI("bytes total=%u", input.capacity());
if (input) {
ALOGI("bytes total=%u", input->capacity());
}
if (s_dec_op.u4_frame_decoded_flag && !mFlushNeeded) {
mFlushNeeded = true;
}
if (1 != s_dec_op.u4_frame_decoded_flag) {
/* If the input did not contain picture data, then ignore
* the associated timestamp */
//mTimeStampsValid[workIndex] = false;
if (1 != s_dec_op.u4_frame_decoded_flag && work) {
/* If the input did not contain picture data, return work without
* buffer */
ALOGV("no picture data");
std::vector<std::unique_ptr<C2Work>> done;
done.push_back(std::move(work));
done[0]->worklets.front()->output.flags = (C2BufferPack::flags_t)0;
done[0]->worklets.front()->output.buffers.clear();
done[0]->worklets.front()->output.buffers.emplace_back(nullptr);
done[0]->worklets.front()->output.ordinal = done[0]->input.ordinal;
mListener->onWorkDone_nb(shared_from_this(), std::move(done));
}
// If the decoder is in the changing resolution mode and there is no output present,
@ -1373,10 +1395,19 @@ void C2SoftAvcDec::process(std::unique_ptr<C2Work> &work) {
}
if (s_dec_op.u4_output_present) {
ALOGV("output_present");
// TODO: outHeader->nFilledLen = (mWidth * mHeight * 3) / 2;
ALOGV("output_present: %d", s_dec_op.u4_ts);
std::vector<std::unique_ptr<C2Work>> done;
done.push_back(std::move(mPendingWork[s_dec_op.u4_ts]));
{
std::unique_lock<std::mutex> lock(mPendingLock);
done.push_back(std::move(mPendingWork[s_dec_op.u4_ts]));
mPendingWork.erase(s_dec_op.u4_ts);
}
uint32_t flags = 0;
if (done[0]->input.flags & C2BufferPack::FLAG_END_OF_STREAM) {
flags |= C2BufferPack::FLAG_END_OF_STREAM;
ALOGV("EOS");
}
done[0]->worklets.front()->output.flags = (C2BufferPack::flags_t)flags;
done[0]->worklets.front()->output.buffers.clear();
done[0]->worklets.front()->output.buffers.emplace_back(
std::make_shared<GraphicBuffer>(std::move(mAllocatedBlock)));
@ -1391,16 +1422,25 @@ void C2SoftAvcDec::process(std::unique_ptr<C2Work> &work) {
/* If EOS was recieved on input port and there is no output
* from the codec, then signal EOS on output port */
if (mReceivedEOS) {
// TODO
// outHeader->nFilledLen = 0;
// outHeader->nFlags |= OMX_BUFFERFLAG_EOS;
// outInfo->mOwnedByUs = false;
// outQueue.erase(outQueue.begin());
// outInfo = NULL;
// notifyFillBufferDone(outHeader);
// outHeader = NULL;
std::vector<std::unique_ptr<C2Work>> done;
{
std::unique_lock<std::mutex> lock(mPendingLock);
if (!mPendingWork.empty()) {
done.push_back(std::move(mPendingWork.begin()->second));
mPendingWork.erase(mPendingWork.begin());
}
}
if (!done.empty()) {
ALOGV("sending empty EOS buffer");
done[0]->worklets.front()->output.flags = C2BufferPack::FLAG_END_OF_STREAM;
done[0]->worklets.front()->output.buffers.clear();
done[0]->worklets.front()->output.buffers.emplace_back(nullptr);
done[0]->worklets.front()->output.ordinal = done[0]->input.ordinal;
mListener->onWorkDone_nb(shared_from_this(), std::move(done));
}
resetPlugin();
return;
}
}
inOffset += s_dec_op.u4_num_bytes_consumed;

@ -58,9 +58,6 @@ struct ivd_video_decode_op_t;
#define MIN(a, b) ((a) < (b)) ? (a) : (b)
/** Used to remove warnings about unused parameters */
#define UNUSED(x) ((void)(x))
/** Get time */
#define GETTIME(a, b) gettimeofday(a, b);

@ -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_

@ -18,6 +18,7 @@
#define CODEC_BASE_H_
#include <list>
#include <memory>
#include <stdint.h>
@ -26,7 +27,6 @@
#include <media/hardware/CryptoAPI.h>
#include <media/hardware/HardwareAPI.h>
#include <media/IOMX.h>
#include <media/MediaCodecInfo.h>
#include <media/stagefright/foundation/AHandler.h>
#include <media/stagefright/foundation/ColorUtils.h>

@ -57,7 +57,7 @@ private:
OWNED_BY_UPSTREAM,
};
IOMX::buffer_id mBufferID;
uint32_t mBufferID;
int32_t mGeneration;
int32_t mOutputFlags;
Status mStatus;
@ -121,7 +121,7 @@ private:
status_t allocateBuffersOnPort(OMX_U32 portIndex);
BufferInfo *findBufferByID(
uint32_t portIndex, IOMX::buffer_id bufferID,
uint32_t portIndex, uint32_t bufferID,
ssize_t *index = NULL);
void postFillThisBuffer(BufferInfo *info);
void postDrainThisBuffer(BufferInfo *info);

@ -47,7 +47,8 @@ public:
static sp<SimpleDecodingSource> Create(
const sp<MediaSource> &source, uint32_t flags,
const sp<ANativeWindow> &nativeWindow,
const char *desiredCodec = NULL);
const char *desiredCodec = NULL,
bool skipMediaCodecList = false);
static sp<SimpleDecodingSource> Create(
const sp<MediaSource> &source, uint32_t flags = 0);

Loading…
Cancel
Save