CCodec: Episode III --- Video Encoding

- Implement createInputSurface()
- Handle RGB format in C2AllocatorGralloc
- Define picture type mask and handle it
- Process onWorkDone_nb() callbacks in a separate thread
- Add an unofficial option to take component name directly in screenrecord
- Force array mode for allocation latency; to be removed once proper
  buffer pool is in place.

Test: setprop debug.stagefright.ccodec yes
Test: stagefright -S -N c2.google.avc.decoder /sdcard/a.mp4
Change-Id: Iba225350e2eb2a1e6f4d45a02628d0f7ba188df3
gugelfrei
Wonsik Kim 7 years ago
parent c0f23e9450
commit 3184abdd4f

@ -73,6 +73,7 @@ static bool gMonotonicTime = false; // use system monotonic time for timesta
static enum {
FORMAT_MP4, FORMAT_H264, FORMAT_FRAMES, FORMAT_RAW_FRAMES
} gOutputFormat = FORMAT_MP4; // data format for output
static AString gCodecName = ""; // codec name override
static bool gSizeSpecified = false; // was size explicitly requested?
static bool gWantInfoScreen = false; // do we want initial info screen?
static bool gWantFrameTime = false; // do we want times on each frame?
@ -154,6 +155,7 @@ static status_t prepareEncoder(float displayFps, sp<MediaCodec>* pCodec,
if (gVerbose) {
printf("Configuring recorder for %dx%d %s at %.2fMbps\n",
gVideoWidth, gVideoHeight, kMimeTypeAvc, gBitRate / 1000000.0);
fflush(stdout);
}
sp<AMessage> format = new AMessage;
@ -169,11 +171,21 @@ static status_t prepareEncoder(float displayFps, sp<MediaCodec>* pCodec,
looper->setName("screenrecord_looper");
looper->start();
ALOGV("Creating codec");
sp<MediaCodec> codec = MediaCodec::CreateByType(looper, kMimeTypeAvc, true);
if (codec == NULL) {
fprintf(stderr, "ERROR: unable to create %s codec instance\n",
kMimeTypeAvc);
return UNKNOWN_ERROR;
sp<MediaCodec> codec;
if (gCodecName.empty()) {
codec = MediaCodec::CreateByType(looper, kMimeTypeAvc, true);
if (codec == NULL) {
fprintf(stderr, "ERROR: unable to create %s codec instance\n",
kMimeTypeAvc);
return UNKNOWN_ERROR;
}
} else {
codec = MediaCodec::CreateByComponentName(looper, gCodecName);
if (codec == NULL) {
fprintf(stderr, "ERROR: unable to create %s codec instance\n",
gCodecName.c_str());
return UNKNOWN_ERROR;
}
}
err = codec->configure(format, NULL, NULL,
@ -275,9 +287,11 @@ static status_t setDisplayProjection(
if (gRotate) {
printf("Rotated content area is %ux%u at offset x=%d y=%d\n",
outHeight, outWidth, offY, offX);
fflush(stdout);
} else {
printf("Content area is %ux%u at offset x=%d y=%d\n",
outWidth, outHeight, offX, offY);
fflush(stdout);
}
}
@ -346,6 +360,7 @@ static status_t runEncoder(const sp<MediaCodec>& encoder,
if (systemTime(CLOCK_MONOTONIC) > endWhenNsec) {
if (gVerbose) {
printf("Time limit reached\n");
fflush(stdout);
}
break;
}
@ -483,6 +498,7 @@ static status_t runEncoder(const sp<MediaCodec>& encoder,
printf("Encoder stopping; recorded %u frames in %" PRId64 " seconds\n",
debugNumFrames, nanoseconds_to_seconds(
systemTime(CLOCK_MONOTONIC) - startWhenNsec));
fflush(stdout);
}
return NO_ERROR;
}
@ -556,6 +572,7 @@ static status_t recordScreen(const char* fileName) {
printf("Main display is %dx%d @%.2ffps (orientation=%u)\n",
mainDpyInfo.w, mainDpyInfo.h, mainDpyInfo.fps,
mainDpyInfo.orientation);
fflush(stdout);
}
bool rotated = isDeviceRotated(mainDpyInfo.orientation);
@ -623,6 +640,7 @@ static status_t recordScreen(const char* fileName) {
}
if (gVerbose) {
printf("Bugreport overlay created\n");
fflush(stdout);
}
} else {
// Use the encoder's input surface as the virtual display surface.
@ -715,6 +733,7 @@ static status_t recordScreen(const char* fileName) {
if (gVerbose) {
printf("Stopping encoder and muxer\n");
fflush(stdout);
}
}
@ -761,6 +780,7 @@ static status_t notifyMediaScanner(const char* fileName) {
printf(" %s", argv[i]);
}
putchar('\n');
fflush(stdout);
}
pid_t pid = fork();
@ -898,6 +918,7 @@ int main(int argc, char* const argv[]) {
{ "show-frame-time", no_argument, NULL, 'f' },
{ "rotate", no_argument, NULL, 'r' },
{ "output-format", required_argument, NULL, 'o' },
{ "codec-name", required_argument, NULL, 'N' },
{ "monotonic-time", no_argument, NULL, 'm' },
{ NULL, 0, NULL, 0 }
};
@ -978,6 +999,9 @@ int main(int argc, char* const argv[]) {
return 2;
}
break;
case 'N':
gCodecName = optarg;
break;
case 'm':
gMonotonicTime = true;
break;

@ -105,6 +105,7 @@ cc_library_shared {
"libmedia_helper",
"libstagefright_codec2",
"libstagefright_foundation",
"libstagefright_gbs",
"libstagefright_omx",
"libstagefright_omx_utils",
"libstagefright_xmlparser",

@ -23,7 +23,9 @@
#include <C2PlatformSupport.h>
#include <gui/Surface.h>
#include <media/stagefright/BufferProducerWrapper.h>
#include <media/stagefright/CCodec.h>
#include <media/stagefright/PersistentSurface.h>
#include "include/CCodecBufferChannel.h"
@ -111,33 +113,35 @@ Mutexed<sp<CCodecWatchdog>> CCodecWatchdog::sInstance;
class CCodecListener : public C2Component::Listener {
public:
CCodecListener(const std::shared_ptr<CCodecBufferChannel> &channel)
: mChannel(channel) {
}
explicit CCodecListener(const wp<CCodec> &codec) : mCodec(codec) {}
virtual void onWorkDone_nb(
std::weak_ptr<C2Component> component,
std::vector<std::unique_ptr<C2Work>> workItems) override {
(void) component;
mChannel->onWorkDone(std::move(workItems));
(void)component;
sp<CCodec> codec(mCodec.promote());
if (!codec) {
return;
}
codec->onWorkDone(workItems);
}
virtual void onTripped_nb(
std::weak_ptr<C2Component> component,
std::vector<std::shared_ptr<C2SettingResult>> settingResult) override {
// TODO
(void) component;
(void) settingResult;
(void)component;
(void)settingResult;
}
virtual void onError_nb(std::weak_ptr<C2Component> component, uint32_t errorCode) override {
// TODO
(void) component;
(void) errorCode;
(void)component;
(void)errorCode;
}
private:
std::shared_ptr<CCodecBufferChannel> mChannel;
wp<CCodec> mCodec;
};
} // namespace
@ -159,11 +163,11 @@ std::shared_ptr<BufferChannelBase> CCodec::getBufferChannel() {
void CCodec::initiateAllocateComponent(const sp<AMessage> &msg) {
{
Mutexed<State>::Locked state(mState);
if (state->mState != RELEASED) {
if (state->get() != RELEASED) {
mCallback->onError(INVALID_OPERATION, ACTION_CODE_FATAL);
return;
}
state->mState = ALLOCATING;
state->set(ALLOCATING);
}
AString componentName;
@ -178,14 +182,14 @@ void CCodec::initiateAllocateComponent(const sp<AMessage> &msg) {
void CCodec::allocate(const AString &componentName) {
// TODO: use C2ComponentStore to create component
mListener.reset(new CCodecListener(mChannel));
mListener.reset(new CCodecListener(this));
std::shared_ptr<C2Component> comp;
c2_status_t err = GetCodec2PlatformComponentStore()->createComponent(
componentName.c_str(), &comp);
if (err != C2_OK) {
Mutexed<State>::Locked state(mState);
state->mState = RELEASED;
state->set(RELEASED);
state.unlock();
mCallback->onError(err, ACTION_CODE_FATAL);
state.lock();
@ -194,15 +198,15 @@ void CCodec::allocate(const AString &componentName) {
comp->setListener_vb(mListener, C2_MAY_BLOCK);
{
Mutexed<State>::Locked state(mState);
if (state->mState != ALLOCATING) {
state->mState = RELEASED;
if (state->get() != ALLOCATING) {
state->set(RELEASED);
state.unlock();
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
state.lock();
return;
}
state->mState = ALLOCATED;
state->mComp = comp;
state->set(ALLOCATED);
state->comp = comp;
}
mChannel->setComponent(comp);
mCallback->onComponentAllocated(comp->intf()->getName().c_str());
@ -211,7 +215,7 @@ void CCodec::allocate(const AString &componentName) {
void CCodec::initiateConfigureComponent(const sp<AMessage> &format) {
{
Mutexed<State>::Locked state(mState);
if (state->mState != ALLOCATED) {
if (state->get() != ALLOCATED) {
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
return;
}
@ -252,6 +256,9 @@ void CCodec::configure(const sp<AMessage> &msg) {
inputFormat->setInt32("sample-rate", 44100);
outputFormat->setInt32("channel-count", 1);
outputFormat->setInt32("sample-rate", 44100);
} else {
outputFormat->setInt32("width", 1080);
outputFormat->setInt32("height", 1920);
}
} else {
inputFormat->setString("mime", mime);
@ -272,32 +279,81 @@ void CCodec::configure(const sp<AMessage> &msg) {
{
Mutexed<Formats>::Locked formats(mFormats);
formats->mInputFormat = inputFormat;
formats->mOutputFormat = outputFormat;
formats->inputFormat = inputFormat;
formats->outputFormat = outputFormat;
}
mCallback->onComponentConfigured(inputFormat, outputFormat);
}
void CCodec::initiateCreateInputSurface() {
// TODO
(new AMessage(kWhatCreateInputSurface, this))->post();
}
void CCodec::createInputSurface() {
sp<IGraphicBufferProducer> producer;
sp<GraphicBufferSource> source(new GraphicBufferSource);
status_t err = source->initCheck();
if (err != OK) {
ALOGE("Failed to initialize graphic buffer source: %d", err);
mCallback->onInputSurfaceCreationFailed(err);
return;
}
producer = source->getIGraphicBufferProducer();
err = setupInputSurface(source);
if (err != OK) {
ALOGE("Failed to set up input surface: %d", err);
mCallback->onInputSurfaceCreationFailed(err);
return;
}
sp<AMessage> inputFormat;
sp<AMessage> outputFormat;
{
Mutexed<Formats>::Locked formats(mFormats);
inputFormat = formats->inputFormat;
outputFormat = formats->outputFormat;
}
mCallback->onInputSurfaceCreated(
inputFormat,
outputFormat,
new BufferProducerWrapper(producer));
}
status_t CCodec::setupInputSurface(const sp<GraphicBufferSource> &source) {
status_t err = mChannel->setGraphicBufferSource(source);
if (err != OK) {
return err;
}
// TODO: configure |source| with other settings.
return OK;
}
void CCodec::initiateSetInputSurface(const sp<PersistentSurface> &surface) {
sp<AMessage> msg = new AMessage(kWhatSetInputSurface, this);
msg->setObject("surface", surface);
msg->post();
}
void CCodec::setInputSurface(const sp<PersistentSurface> &surface) {
// TODO
(void) surface;
(void)surface;
mCallback->onInputSurfaceDeclined(ERROR_UNSUPPORTED);
}
void CCodec::initiateStart() {
{
Mutexed<State>::Locked state(mState);
if (state->mState != ALLOCATED) {
if (state->get() != ALLOCATED) {
state.unlock();
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
state.lock();
return;
}
state->mState = STARTING;
state->set(STARTING);
}
(new AMessage(kWhatStart, this))->post();
@ -307,13 +363,13 @@ void CCodec::start() {
std::shared_ptr<C2Component> comp;
{
Mutexed<State>::Locked state(mState);
if (state->mState != STARTING) {
if (state->get() != STARTING) {
state.unlock();
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
state.lock();
return;
}
comp = state->mComp;
comp = state->comp;
}
c2_status_t err = comp->start();
if (err != C2_OK) {
@ -325,20 +381,20 @@ void CCodec::start() {
sp<AMessage> outputFormat;
{
Mutexed<Formats>::Locked formats(mFormats);
inputFormat = formats->mInputFormat;
outputFormat = formats->mOutputFormat;
inputFormat = formats->inputFormat;
outputFormat = formats->outputFormat;
}
mChannel->start(inputFormat, outputFormat);
{
Mutexed<State>::Locked state(mState);
if (state->mState != STARTING) {
if (state->get() != STARTING) {
state.unlock();
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
state.lock();
return;
}
state->mState = RUNNING;
state->set(RUNNING);
}
mCallback->onStartCompleted();
}
@ -354,17 +410,17 @@ void CCodec::initiateShutdown(bool keepComponentAllocated) {
void CCodec::initiateStop() {
{
Mutexed<State>::Locked state(mState);
if (state->mState == ALLOCATED
|| state->mState == RELEASED
|| state->mState == STOPPING
|| state->mState == RELEASING) {
if (state->get() == ALLOCATED
|| state->get() == RELEASED
|| state->get() == STOPPING
|| state->get() == RELEASING) {
// We're already stopped, released, or doing it right now.
state.unlock();
mCallback->onStopCompleted();
state.lock();
return;
}
state->mState = STOPPING;
state->set(STOPPING);
}
(new AMessage(kWhatStop, this))->post();
@ -374,19 +430,19 @@ void CCodec::stop() {
std::shared_ptr<C2Component> comp;
{
Mutexed<State>::Locked state(mState);
if (state->mState == RELEASING) {
if (state->get() == RELEASING) {
state.unlock();
// We're already stopped or release is in progress.
mCallback->onStopCompleted();
state.lock();
return;
} else if (state->mState != STOPPING) {
} else if (state->get() != STOPPING) {
state.unlock();
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
state.lock();
return;
}
comp = state->mComp;
comp = state->comp;
}
mChannel->stop();
status_t err = comp->stop();
@ -397,8 +453,8 @@ void CCodec::stop() {
{
Mutexed<State>::Locked state(mState);
if (state->mState == STOPPING) {
state->mState = ALLOCATED;
if (state->get() == STOPPING) {
state->set(ALLOCATED);
}
}
mCallback->onStopCompleted();
@ -407,7 +463,7 @@ void CCodec::stop() {
void CCodec::initiateRelease(bool sendCallback /* = true */) {
{
Mutexed<State>::Locked state(mState);
if (state->mState == RELEASED || state->mState == RELEASING) {
if (state->get() == RELEASED || state->get() == RELEASING) {
// We're already released or doing it right now.
if (sendCallback) {
state.unlock();
@ -416,8 +472,8 @@ void CCodec::initiateRelease(bool sendCallback /* = true */) {
}
return;
}
if (state->mState == ALLOCATING) {
state->mState = RELEASING;
if (state->get() == ALLOCATING) {
state->set(RELEASING);
// With the altered state allocate() would fail and clean up.
if (sendCallback) {
state.unlock();
@ -426,7 +482,7 @@ void CCodec::initiateRelease(bool sendCallback /* = true */) {
}
return;
}
state->mState = RELEASING;
state->set(RELEASING);
}
std::thread([this, sendCallback] { release(sendCallback); }).detach();
@ -436,7 +492,7 @@ void CCodec::release(bool sendCallback) {
std::shared_ptr<C2Component> comp;
{
Mutexed<State>::Locked state(mState);
if (state->mState == RELEASED) {
if (state->get() == RELEASED) {
if (sendCallback) {
state.unlock();
mCallback->onReleaseCompleted();
@ -444,15 +500,15 @@ void CCodec::release(bool sendCallback) {
}
return;
}
comp = state->mComp;
comp = state->comp;
}
mChannel->stop();
comp->release();
{
Mutexed<State>::Locked state(mState);
state->mState = RELEASED;
state->mComp.reset();
state->set(RELEASED);
state->comp.reset();
}
if (sendCallback) {
mCallback->onReleaseCompleted();
@ -466,11 +522,11 @@ status_t CCodec::setSurface(const sp<Surface> &surface) {
void CCodec::signalFlush() {
{
Mutexed<State>::Locked state(mState);
if (state->mState != RUNNING) {
if (state->get() != RUNNING) {
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
return;
}
state->mState = FLUSHING;
state->set(FLUSHING);
}
(new AMessage(kWhatFlush, this))->post();
@ -480,13 +536,13 @@ void CCodec::flush() {
std::shared_ptr<C2Component> comp;
{
Mutexed<State>::Locked state(mState);
if (state->mState != FLUSHING) {
if (state->get() != FLUSHING) {
state.unlock();
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
state.lock();
return;
}
comp = state->mComp;
comp = state->comp;
}
mChannel->stop();
@ -502,7 +558,7 @@ void CCodec::flush() {
{
Mutexed<State>::Locked state(mState);
state->mState = FLUSHED;
state->set(FLUSHED);
}
mCallback->onFlushCompleted();
}
@ -510,26 +566,26 @@ void CCodec::flush() {
void CCodec::signalResume() {
{
Mutexed<State>::Locked state(mState);
if (state->mState != FLUSHED) {
if (state->get() != FLUSHED) {
state.unlock();
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
state.lock();
return;
}
state->mState = RESUMING;
state->set(RESUMING);
}
mChannel->start(nullptr, nullptr);
{
Mutexed<State>::Locked state(mState);
if (state->mState != RESUMING) {
if (state->get() != RESUMING) {
state.unlock();
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
state.lock();
return;
}
state->mState = RUNNING;
state->set(RUNNING);
}
}
@ -545,6 +601,14 @@ void CCodec::signalRequestIDRFrame() {
// TODO
}
void CCodec::onWorkDone(std::vector<std::unique_ptr<C2Work>> &workItems) {
Mutexed<std::list<std::unique_ptr<C2Work>>>::Locked queue(mWorkDoneQueue);
for (std::unique_ptr<C2Work> &item : workItems) {
queue->push_back(std::move(item));
}
(new AMessage(kWhatWorkDone, this))->post();
}
void CCodec::onMessageReceived(const sp<AMessage> &msg) {
TimePoint now = std::chrono::steady_clock::now();
switch (msg->what()) {
@ -582,6 +646,37 @@ void CCodec::onMessageReceived(const sp<AMessage> &msg) {
flush();
break;
}
case kWhatCreateInputSurface: {
// Surface operations may be briefly blocking.
setDeadline(now + 100ms);
createInputSurface();
break;
}
case kWhatSetInputSurface: {
// Surface operations may be briefly blocking.
setDeadline(now + 100ms);
sp<RefBase> obj;
CHECK(msg->findObject("surface", &obj));
sp<PersistentSurface> surface(static_cast<PersistentSurface *>(obj.get()));
setInputSurface(surface);
break;
}
case kWhatWorkDone: {
std::unique_ptr<C2Work> work;
{
Mutexed<std::list<std::unique_ptr<C2Work>>>::Locked queue(mWorkDoneQueue);
if (queue->empty()) {
break;
}
work.swap(queue->front());
queue->pop_front();
if (!queue->empty()) {
(new AMessage(kWhatWorkDone, this))->post();
}
}
mChannel->onWorkDone(work);
break;
}
default: {
ALOGE("unrecognized message");
break;

@ -14,7 +14,7 @@
* limitations under the License.
*/
#define LOG_NDEBUG 0
//#define LOG_NDEBUG 0
#define LOG_TAG "CCodecBufferChannel"
#include <utils/Log.h>
@ -48,10 +48,189 @@ using hardware::hidl_vec;
using namespace hardware::cas::V1_0;
using namespace hardware::cas::native::V1_0;
/**
* Base class for representation of buffers at one port.
*/
class CCodecBufferChannel::Buffers {
public:
Buffers() = default;
virtual ~Buffers() = default;
/**
* Set format for MediaCodec-facing buffers.
*/
void setFormat(const sp<AMessage> &format) { mFormat = format; }
/**
* Returns true if the buffers are operating under array mode.
*/
virtual bool isArrayMode() const { return false; }
/**
* Fills the vector with MediaCodecBuffer's if in array mode; otherwise,
* no-op.
*/
virtual void getArray(Vector<sp<MediaCodecBuffer>> *) const {}
protected:
// Format to be used for creating MediaCodec-facing buffers.
sp<AMessage> mFormat;
private:
DISALLOW_EVIL_CONSTRUCTORS(Buffers);
};
class CCodecBufferChannel::InputBuffers : public CCodecBufferChannel::Buffers {
public:
InputBuffers() = default;
virtual ~InputBuffers() = default;
/**
* Set a block pool to obtain input memory blocks.
*/
void setPool(const std::shared_ptr<C2BlockPool> &pool) { mPool = pool; }
/**
* Get a new MediaCodecBuffer for input and its corresponding index.
* Returns false if no new buffer can be obtained at the moment.
*/
virtual bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) = 0;
/**
* Release the buffer obtained from requestNewBuffer() and get the
* associated C2Buffer object back. Returns empty shared_ptr if the
* buffer is not on file.
*
* XXX: this is a quick hack to be removed
*/
virtual std::shared_ptr<C2Buffer> releaseBufferIndex(size_t /* index */) { return nullptr; }
/**
* Release the buffer obtained from requestNewBuffer() and get the
* associated C2Buffer object back. Returns empty shared_ptr if the
* buffer is not on file.
*/
virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) = 0;
/**
* Flush internal state. After this call, no index or buffer previously
* returned from requestNewBuffer() is valid.
*/
virtual void flush() = 0;
/**
* Return array-backed version of input buffers. The returned object
* shall retain the internal state so that it will honor index and
* buffer from previous calls of requestNewBuffer().
*/
virtual std::unique_ptr<InputBuffers> toArrayMode() = 0;
protected:
// Pool to obtain blocks for input buffers.
std::shared_ptr<C2BlockPool> mPool;
private:
DISALLOW_EVIL_CONSTRUCTORS(InputBuffers);
};
class CCodecBufferChannel::InputBufferClient {
public:
explicit InputBufferClient(
const std::shared_ptr<CCodecBufferChannel> &channel) : mChannel(channel) {}
virtual ~InputBufferClient() = default;
virtual void onInputBufferAdded(size_t index, const sp<MediaCodecBuffer> &buffer) {
std::shared_ptr<CCodecBufferChannel> channel = mChannel.lock();
if (!channel) {
return;
}
channel->mCallback->onInputBufferAvailable(index, buffer);
}
virtual void onStart() {
// no-op
}
virtual void onStop() {
// no-op
}
virtual void onRelease() {
// no-op
}
virtual void onInputBufferAvailable(size_t index, const sp<MediaCodecBuffer> &buffer) {
std::shared_ptr<CCodecBufferChannel> channel = mChannel.lock();
if (!channel) {
return;
}
channel->mCallback->onInputBufferAvailable(index, buffer);
}
protected:
InputBufferClient() = default;
std::weak_ptr<CCodecBufferChannel> mChannel;
DISALLOW_EVIL_CONSTRUCTORS(InputBufferClient);
};
class CCodecBufferChannel::OutputBuffers : public CCodecBufferChannel::Buffers {
public:
OutputBuffers() = default;
virtual ~OutputBuffers() = default;
/**
* Register output C2Buffer from the component and obtain corresponding
* index and MediaCodecBuffer object. Returns false if registration
* fails.
*/
virtual bool registerBuffer(
const std::shared_ptr<C2Buffer> &buffer,
size_t *index,
sp<MediaCodecBuffer> *codecBuffer) = 0;
/**
* Register codec specific data as a buffer to be consistent with
* MediaCodec behavior.
*/
virtual bool registerCsd(
const C2StreamCsdInfo::output * /* csd */,
size_t * /* index */,
sp<MediaCodecBuffer> * /* codecBuffer */) {
return false;
}
/**
* Release the buffer obtained from registerBuffer() and get the
* associated C2Buffer object back. Returns empty shared_ptr if the
* buffer is not on file.
*/
virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) = 0;
/**
* Flush internal state. After this call, no index or buffer previously
* returned from registerBuffer() is valid.
*/
virtual void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) = 0;
/**
* Return array-backed version of output buffers. The returned object
* shall retain the internal state so that it will honor index and
* buffer from previous calls of registerBuffer().
*/
virtual std::unique_ptr<OutputBuffers> toArrayMode() = 0;
private:
DISALLOW_EVIL_CONSTRUCTORS(OutputBuffers);
};
namespace {
constexpr int32_t kMaskI32 = ~0;
// TODO: get this info from component
const static size_t kMinBufferArraySize = 16;
const static size_t kLinearBufferSize = 524288;
template <class T>
ssize_t findBufferSlot(
@ -88,9 +267,14 @@ sp<Codec2Buffer> allocateLinearBuffer(
return Codec2Buffer::allocate(format, block);
}
class LinearBuffer : public C2Buffer {
class Buffer1D : public C2Buffer {
public:
explicit LinearBuffer(C2ConstLinearBlock block) : C2Buffer({ block }) {}
explicit Buffer1D(C2ConstLinearBlock block) : C2Buffer({ block }) {}
};
class Buffer2D : public C2Buffer {
public:
explicit Buffer2D(C2ConstGraphicBlock block) : C2Buffer({ block }) {}
};
class InputBuffersArray : public CCodecBufferChannel::InputBuffers {
@ -99,36 +283,36 @@ public:
void add(
size_t index,
const sp<MediaCodecBuffer> &clientBuffer,
const std::shared_ptr<C2Buffer> &compBuffer,
const sp<Codec2Buffer> &clientBuffer,
bool available) {
if (mBufferArray.size() < index) {
if (mBufferArray.size() <= index) {
// TODO: make this more efficient
mBufferArray.resize(index + 1);
}
mBufferArray[index].clientBuffer = clientBuffer;
mBufferArray[index].compBuffer = compBuffer;
mBufferArray[index].available = available;
}
bool isArrayMode() final { return true; }
bool isArrayMode() const final { return true; }
std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode() final {
return nullptr;
}
void getArray(Vector<sp<MediaCodecBuffer>> *array) final {
void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
array->clear();
for (const auto &entry : mBufferArray) {
for (const Entry &entry : mBufferArray) {
array->push(entry.clientBuffer);
}
}
bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
for (size_t i = 0; i < mBufferArray.size(); ++i) {
if (mBufferArray[i].available) {
if (mBufferArray[i].available && mBufferArray[i].compBuffer.expired()) {
mBufferArray[i].available = false;
*index = i;
*buffer = mBufferArray[i].clientBuffer;
(*buffer)->setRange(0, (*buffer)->capacity());
return true;
}
}
@ -138,8 +322,10 @@ public:
std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
for (size_t i = 0; i < mBufferArray.size(); ++i) {
if (!mBufferArray[i].available && mBufferArray[i].clientBuffer == buffer) {
std::shared_ptr<C2Buffer> buffer = std::make_shared<Buffer1D>(mBufferArray[i].clientBuffer->share());
mBufferArray[i].available = true;
return std::move(mBufferArray[i].compBuffer);
mBufferArray[i].compBuffer = buffer;
return buffer;
}
}
return nullptr;
@ -148,14 +334,13 @@ public:
void flush() override {
for (size_t i = 0; i < mBufferArray.size(); ++i) {
mBufferArray[i].available = true;
mBufferArray[i].compBuffer.reset();
}
}
private:
struct Entry {
sp<MediaCodecBuffer> clientBuffer;
std::shared_ptr<C2Buffer> compBuffer;
sp<Codec2Buffer> clientBuffer;
std::weak_ptr<C2Buffer> compBuffer;
bool available;
};
@ -177,7 +362,7 @@ public:
// TODO: proper max input size and usage
// TODO: read usage from intf
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
sp<Codec2Buffer> newBuffer = allocateLinearBuffer(mPool, mFormat, 65536, usage);
sp<Codec2Buffer> newBuffer = allocateLinearBuffer(mPool, mFormat, kLinearBufferSize, usage);
if (newBuffer == nullptr) {
return false;
}
@ -195,7 +380,7 @@ public:
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());
return std::make_shared<Buffer1D>(codecBuffer->share());
}
void flush() override {
@ -203,8 +388,10 @@ public:
std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode() final {
std::unique_ptr<InputBuffersArray> array(new InputBuffersArray);
array->setFormat(mFormat);
// TODO
const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
mBuffers.resize(size);
for (size_t i = 0; i < size; ++i) {
sp<Codec2Buffer> clientBuffer = mBuffers[i].promote();
bool available = false;
@ -212,13 +399,12 @@ public:
// TODO: proper max input size
// TODO: read usage from intf
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
clientBuffer = allocateLinearBuffer(mPool, mFormat, 65536, usage);
clientBuffer = allocateLinearBuffer(mPool, mFormat, kLinearBufferSize, usage);
available = true;
}
array->add(
i,
clientBuffer,
std::make_shared<LinearBuffer>(clientBuffer->share()),
available);
}
return std::move(array);
@ -230,19 +416,30 @@ private:
std::vector<wp<Codec2Buffer>> mBuffers;
};
// TODO: stub
class GraphicInputBuffers : public CCodecBufferChannel::InputBuffers {
public:
using CCodecBufferChannel::InputBuffers::InputBuffers;
GraphicInputBuffers() = default;
bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
(void)index;
(void)buffer;
return false;
*buffer = nullptr;
for (size_t i = 0; i < mAvailable.size(); ++i) {
if (mAvailable[i]) {
*index = i;
mAvailable[i] = false;
return true;
}
}
*index = mAvailable.size();
mAvailable.push_back(false);
return true;
}
std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
(void)buffer;
std::shared_ptr<C2Buffer> releaseBufferIndex(size_t index) override {
mAvailable[index] = true;
return nullptr;
}
std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &) override {
return nullptr;
}
@ -252,6 +449,9 @@ public:
std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode() final {
return nullptr;
}
private:
std::vector<bool> mAvailable;
};
class OutputBuffersArray : public CCodecBufferChannel::OutputBuffers {
@ -263,7 +463,8 @@ public:
const sp<MediaCodecBuffer> &clientBuffer,
const std::shared_ptr<C2Buffer> &compBuffer,
bool available) {
if (mBufferArray.size() < index) {
if (mBufferArray.size() <= index) {
// TODO: make this more efficient
mBufferArray.resize(index + 1);
}
mBufferArray[index].clientBuffer = clientBuffer;
@ -271,7 +472,7 @@ public:
mBufferArray[index].available = available;
}
bool isArrayMode() final { return true; }
bool isArrayMode() const final { return true; }
std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() final {
return nullptr;
@ -285,6 +486,7 @@ public:
if (mBufferArray[i].available && copy(buffer, mBufferArray[i].clientBuffer)) {
*index = i;
*codecBuffer = mBufferArray[i].clientBuffer;
(*codecBuffer)->setFormat(mFormat);
mBufferArray[i].compBuffer = buffer;
mBufferArray[i].available = false;
return true;
@ -299,10 +501,17 @@ public:
sp<MediaCodecBuffer> *codecBuffer) final {
for (size_t i = 0; i < mBufferArray.size(); ++i) {
if (mBufferArray[i].available
&& mBufferArray[i].clientBuffer->capacity() <= csd->flexCount()) {
&& mBufferArray[i].clientBuffer->capacity() >= csd->flexCount()) {
// TODO: proper format update
sp<ABuffer> csdBuffer = ABuffer::CreateAsCopy(csd->m.value, csd->flexCount());
mFormat = mFormat->dup();
mFormat->setBuffer("csd-0", csdBuffer);
memcpy(mBufferArray[i].clientBuffer->base(), csd->m.value, csd->flexCount());
mBufferArray[i].clientBuffer->setRange(0, csd->flexCount());
*index = i;
*codecBuffer = mBufferArray[i].clientBuffer;
(*codecBuffer)->setFormat(mFormat);
mBufferArray[i].available = false;
return true;
}
@ -333,9 +542,9 @@ public:
const std::shared_ptr<C2Buffer> &buffer,
const sp<MediaCodecBuffer> &clientBuffer) = 0;
void getArray(Vector<sp<MediaCodecBuffer>> *array) final {
void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
array->clear();
for (const auto &entry : mBufferArray) {
for (const Entry &entry : mBufferArray) {
array->push(entry.clientBuffer);
}
}
@ -363,6 +572,7 @@ public:
}
C2ReadView view = buffer->data().linearBlocks().front().map().get();
if (clientBuffer->capacity() < view.capacity()) {
ALOGV("view.capacity() = %u", view.capacity());
return false;
}
clientBuffer->setRange(0u, view.capacity());
@ -425,9 +635,11 @@ public:
if (ret < 0) {
return false;
}
sp<MediaCodecBuffer> newBuffer = new MediaCodecBuffer(
mFormat,
ABuffer::CreateAsCopy(csd->m.value, csd->flexCount()));
// TODO: proper format update
sp<ABuffer> csdBuffer = ABuffer::CreateAsCopy(csd->m.value, csd->flexCount());
mFormat = mFormat->dup();
mFormat->setBuffer("csd-0", csdBuffer);
sp<MediaCodecBuffer> newBuffer = new MediaCodecBuffer(mFormat, csdBuffer);
mBuffers[ret] = { newBuffer, nullptr };
*index = ret;
*codecBuffer = newBuffer;
@ -498,15 +710,17 @@ public:
std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() override {
std::unique_ptr<OutputBuffersArray> array(new LinearOutputBuffersArray);
array->setFormat(mFormat);
const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
mBuffers.resize(size);
for (size_t i = 0; i < size; ++i) {
sp<MediaCodecBuffer> clientBuffer = mBuffers[i].clientBuffer.promote();
std::shared_ptr<C2Buffer> compBuffer = mBuffers[i].bufferRef;
bool available = false;
if (clientBuffer == nullptr) {
// TODO: proper max input size
clientBuffer = new MediaCodecBuffer(mFormat, new ABuffer(65536));
clientBuffer = new MediaCodecBuffer(mFormat, new ABuffer(kLinearBufferSize));
available = true;
compBuffer.reset();
}
@ -526,8 +740,10 @@ public:
std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() override {
std::unique_ptr<OutputBuffersArray> array(new GraphicOutputBuffersArray);
array->setFormat(mFormat);
const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
mBuffers.resize(size);
for (size_t i = 0; i < size; ++i) {
sp<MediaCodecBuffer> clientBuffer = mBuffers[i].clientBuffer.promote();
std::shared_ptr<C2Buffer> compBuffer = mBuffers[i].bufferRef;
@ -543,8 +759,163 @@ public:
}
};
class BufferQueueClient : public CCodecBufferChannel::InputBufferClient {
public:
explicit BufferQueueClient(const sp<GraphicBufferSource> &source) : mSource(source) {}
virtual ~BufferQueueClient() = default;
void onInputBufferAdded(size_t index, const sp<MediaCodecBuffer> &buffer) override {
(void)buffer;
mSource->onInputBufferAdded(index & kMaskI32);
}
void onStart() override {
mSource->start();
}
void onStop() override {
mSource->stop();
}
void onRelease() override {
mSource->release();
}
void onInputBufferAvailable(size_t index, const sp<MediaCodecBuffer> &buffer) override {
ALOGV("onInputBufferEmptied index = %zu", index);
(void)buffer;
// TODO: can we really ignore fence here?
mSource->onInputBufferEmptied(index & kMaskI32, -1 /* fenceFd */);
}
private:
sp<GraphicBufferSource> mSource;
};
class GraphicBlock : public C2GraphicBlock {
using C2GraphicBlock::C2GraphicBlock;
friend class ::android::CCodecBufferChannel;
};
} // namespace
class CCodecBufferChannel::C2ComponentWrapper : public ComponentWrapper {
public:
explicit C2ComponentWrapper(
const std::shared_ptr<CCodecBufferChannel> &channel)
: mChannel(channel), mLastTimestamp(0) {}
virtual ~C2ComponentWrapper() {
for (const std::pair<int32_t, C2Handle *> &entry : mHandles) {
native_handle_delete(entry.second);
}
}
status_t submitBuffer(
int32_t bufferId, const sp<GraphicBuffer> &buffer,
int64_t timestamp, int fenceFd) override {
ALOGV("submitBuffer bufferId = %d", bufferId);
// TODO: Use fd to construct fence
(void)fenceFd;
std::shared_ptr<CCodecBufferChannel> channel = mChannel.lock();
if (!channel) {
return NO_INIT;
}
std::shared_ptr<C2Allocator> allocator = mAllocator.lock();
if (!allocator) {
c2_status_t err = GetCodec2PlatformAllocatorStore()->fetchAllocator(
C2AllocatorStore::PLATFORM_START + 1, // GRALLOC
&allocator);
if (err != OK) {
return UNKNOWN_ERROR;
}
mAllocator = allocator;
}
std::shared_ptr<C2GraphicAllocation> alloc;
C2Handle *handle = WrapNativeCodec2GrallocHandle(
buffer->handle, buffer->width, buffer->height,
buffer->format, buffer->usage, buffer->stride);
c2_status_t err = allocator->priorGraphicAllocation(handle, &alloc);
if (err != OK) {
return UNKNOWN_ERROR;
}
std::shared_ptr<C2GraphicBlock> block(new GraphicBlock(alloc));
std::unique_ptr<C2Work> work(new C2Work);
work->input.flags = (C2FrameData::flags_t)0;
work->input.ordinal.timestamp = timestamp;
work->input.ordinal.frameIndex = channel->mFrameIndex++;
work->input.buffers.clear();
work->input.buffers.emplace_back(new Buffer2D(
// TODO: fence
block->share(C2Rect(block->width(), block->height()), ::android::C2Fence())));
work->worklets.clear();
work->worklets.emplace_back(new C2Worklet);
std::list<std::unique_ptr<C2Work>> items;
items.push_back(std::move(work));
err = channel->mComponent->queue_nb(&items);
if (err != OK) {
native_handle_delete(handle);
return UNKNOWN_ERROR;
}
mLastTimestamp = timestamp;
if (mHandles.count(bufferId) > 0) {
native_handle_delete(mHandles[bufferId]);
}
mHandles[bufferId] = handle;
Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(channel->mInputBuffers);
ALOGV("releaseBufferIndex index = %d", bufferId);
(*buffers)->releaseBufferIndex(bufferId);
return OK;
}
status_t submitEos(int32_t bufferId) override {
std::shared_ptr<CCodecBufferChannel> channel = mChannel.lock();
if (!channel) {
return NO_INIT;
}
std::unique_ptr<C2Work> work(new C2Work);
work->input.flags = C2FrameData::FLAG_END_OF_STREAM;
work->input.ordinal.timestamp = mLastTimestamp;
work->input.ordinal.frameIndex = channel->mFrameIndex++;
work->input.buffers.clear();
work->input.buffers.push_back(nullptr);
work->worklets.clear();
work->worklets.emplace_back(new C2Worklet);
std::list<std::unique_ptr<C2Work>> items;
items.push_back(std::move(work));
c2_status_t err = channel->mComponent->queue_nb(&items);
Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(channel->mInputBuffers);
(*buffers)->releaseBufferIndex(bufferId);
return (err == C2_OK) ? OK : UNKNOWN_ERROR;
}
void dispatchDataSpaceChanged(
int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override {
// TODO
(void)dataSpace;
(void)aspects;
(void)pixelFormat;
}
private:
std::weak_ptr<CCodecBufferChannel> mChannel;
std::map<int32_t, C2Handle *> mHandles;
int64_t mLastTimestamp;
std::weak_ptr<C2Allocator> mAllocator;
};
CCodecBufferChannel::QueueGuard::QueueGuard(
CCodecBufferChannel::QueueSync &sync) : mSync(sync) {
std::unique_lock<std::mutex> l(mSync.mMutex);
@ -601,10 +972,14 @@ CCodecBufferChannel::~CCodecBufferChannel() {
if (mCrypto != nullptr && mDealer != nullptr && mHeapSeqNum >= 0) {
mCrypto->unsetHeap(mHeapSeqNum);
}
// TODO: is this the right place?
mInputClient->onRelease();
}
void CCodecBufferChannel::setComponent(const std::shared_ptr<C2Component> &component) {
mComponent = component;
mInputClient.reset(new InputBufferClient(shared_from_this()));
C2StreamFormatConfig::input inputFormat(0u);
C2StreamFormatConfig::output outputFormat(0u);
c2_status_t err = mComponent->intf()->query_vb(
@ -638,6 +1013,10 @@ void CCodecBufferChannel::setComponent(const std::shared_ptr<C2Component> &compo
} else {
// TODO: error
}
// TODO: remove once we switch to proper buffer pool.
if (!graphic) {
*buffers = (*buffers)->toArrayMode();
}
}
{
@ -652,6 +1031,18 @@ void CCodecBufferChannel::setComponent(const std::shared_ptr<C2Component> &compo
}
}
status_t CCodecBufferChannel::setGraphicBufferSource(
const sp<GraphicBufferSource> &source) {
ALOGV("setGraphicBufferSource");
mInputClient.reset(new BufferQueueClient(source));
// TODO: proper color aspect & dataspace
android_dataspace dataSpace = HAL_DATASPACE_BT709;
// TODO: read settings properly from the interface
return source->configure(new C2ComponentWrapper(
shared_from_this()), dataSpace, 16, 1080, 1920, GRALLOC_USAGE_SW_READ_OFTEN);
}
status_t CCodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {
QueueGuard guard(mSync);
if (!guard.isRunning()) {
@ -708,9 +1099,24 @@ status_t CCodecBufferChannel::queueSecureInputBuffer(
return -ENOSYS;
}
void CCodecBufferChannel::feedInputBufferIfAvailable() {
sp<MediaCodecBuffer> inBuffer;
size_t index;
{
Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
if (!(*buffers)->requestNewBuffer(&index, &inBuffer)) {
ALOGW("no new buffer available");
inBuffer = nullptr;
}
}
ALOGV("new input index = %zu", index);
mInputClient->onInputBufferAvailable(index, inBuffer);
}
status_t CCodecBufferChannel::renderOutputBuffer(
const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) {
ALOGV("renderOutputBuffer");
feedInputBufferIfAvailable();
std::shared_ptr<C2Buffer> c2Buffer;
{
@ -780,7 +1186,9 @@ status_t CCodecBufferChannel::discardBuffer(const sp<MediaCodecBuffer> &buffer)
}
{
Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
(void)(*buffers)->releaseBuffer(buffer);
if((*buffers)->releaseBuffer(buffer)) {
feedInputBufferIfAvailable();
}
}
return OK;
}
@ -832,13 +1240,15 @@ void CCodecBufferChannel::start(const sp<AMessage> &inputFormat, const sp<AMessa
return;
}
}
mCallback->onInputBufferAvailable(index, buffer);
mInputClient->onInputBufferAdded(index, buffer);
}
mInputClient->onStart();
}
void CCodecBufferChannel::stop() {
mSync.stop();
mFirstValidFrameIndex = mFrameIndex.load();
mInputClient->onStop();
}
void CCodecBufferChannel::flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) {
@ -852,106 +1262,103 @@ void CCodecBufferChannel::flush(const std::list<std::unique_ptr<C2Work>> &flushe
}
}
void CCodecBufferChannel::onWorkDone(std::vector<std::unique_ptr<C2Work>> workItems) {
for (const auto &work : workItems) {
sp<MediaCodecBuffer> inBuffer;
size_t index;
{
Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
if (!(*buffers)->requestNewBuffer(&index, &inBuffer)) {
ALOGW("no new buffer available");
inBuffer = nullptr;
}
}
if (inBuffer != nullptr) {
mCallback->onInputBufferAvailable(index, inBuffer);
}
void CCodecBufferChannel::onWorkDone(const std::unique_ptr<C2Work> &work) {
if (work->result != OK) {
ALOGE("work failed to complete: %d", work->result);
mOnError(work->result, ACTION_CODE_FATAL);
return;
}
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);
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.frameIndex - mFirstValidFrameIndex.load()).peek() < 0) {
// Discard frames from previous generation.
return;
}
// 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);
return;
}
const std::unique_ptr<C2Worklet> &worklet = work->worklets.front();
if ((worklet->output.ordinal.frameIndex - mFirstValidFrameIndex.load()).peek() < 0) {
// 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];
const C2StreamCsdInfo::output *csdInfo = nullptr;
for (const std::unique_ptr<C2Param> &info : worklet->output.configUpdate) {
if (info->coreIndex() == C2StreamCsdInfo::output::CORE_INDEX) {
ALOGV("csd found");
csdInfo = static_cast<const C2StreamCsdInfo::output *>(info.get());
}
}
const std::shared_ptr<C2Buffer> &buffer = worklet->output.buffers[0];
const C2StreamCsdInfo::output *csdInfo = nullptr;
if (buffer) {
// TODO: transfer infos() into buffer metadata
}
for (const auto &info : worklet->output.configUpdate) {
if (info->coreIndex() == C2StreamCsdInfo::output::CORE_INDEX) {
ALOGV("csd found");
csdInfo = static_cast<const C2StreamCsdInfo::output *>(info.get());
}
}
int32_t flags = 0;
if (worklet->output.flags & C2FrameData::FLAG_END_OF_STREAM) {
flags |= MediaCodec::BUFFER_FLAG_EOS;
ALOGV("output EOS");
}
int32_t flags = 0;
if (worklet->output.flags & C2FrameData::FLAG_END_OF_STREAM) {
flags |= MediaCodec::BUFFER_FLAG_EOS;
ALOGV("output EOS");
sp<MediaCodecBuffer> outBuffer;
size_t index;
if (csdInfo != nullptr) {
Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
if ((*buffers)->registerCsd(csdInfo, &index, &outBuffer)) {
outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp.peek());
outBuffer->meta()->setInt32("flags", flags | MediaCodec::BUFFER_FLAG_CODECCONFIG);
ALOGV("csd index = %zu", index);
buffers.unlock();
mCallback->onOutputBufferAvailable(index, outBuffer);
buffers.lock();
} else {
ALOGE("unable to register csd");
buffers.unlock();
mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
buffers.lock();
return;
}
}
sp<MediaCodecBuffer> outBuffer;
if (csdInfo != nullptr) {
Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
if ((*buffers)->registerCsd(csdInfo, &index, &outBuffer)) {
outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp.peek());
outBuffer->meta()->setInt32("flags", flags | MediaCodec::BUFFER_FLAG_CODECCONFIG);
ALOGV("csd index = %zu", index);
if (!buffer && !flags) {
ALOGV("Not reporting output buffer");
return;
}
buffers.unlock();
mCallback->onOutputBufferAvailable(index, outBuffer);
buffers.lock();
} else {
ALOGE("unable to register output buffer");
buffers.unlock();
mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
buffers.lock();
continue;
if (buffer) {
for (const std::shared_ptr<const C2Info> &info : buffer->info()) {
// TODO: properly translate these to metadata
switch (info->coreIndex().coreIndex()) {
case C2StreamPictureTypeMaskInfo::CORE_INDEX:
if (((C2StreamPictureTypeMaskInfo *)info.get())->value & C2PictureTypeKeyFrame) {
flags |= MediaCodec::BUFFER_FLAG_SYNCFRAME;
}
break;
default:
break;
}
}
}
if (!buffer && !flags) {
ALOGV("Not reporting output buffer");
continue;
}
{
Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
if (!(*buffers)->registerBuffer(buffer, &index, &outBuffer)) {
ALOGE("unable to register output buffer");
buffers.unlock();
mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
buffers.lock();
continue;
}
{
Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
if (!(*buffers)->registerBuffer(buffer, &index, &outBuffer)) {
ALOGE("unable to register output buffer");
buffers.unlock();
mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
buffers.lock();
return;
}
outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp.peek());
outBuffer->meta()->setInt32("flags", flags);
ALOGV("index = %zu", index);
mCallback->onOutputBufferAvailable(index, outBuffer);
}
outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp.peek());
outBuffer->meta()->setInt32("flags", flags);
ALOGV("out buffer index = %zu", index);
mCallback->onOutputBufferAvailable(index, outBuffer);
}
status_t CCodecBufferChannel::setSurface(const sp<Surface> &newSurface) {

@ -67,6 +67,7 @@ enum C2ParamIndexKind : C2Param::type_index_t {
kParamIndexVideoSizeTuning,
kParamIndexCsd,
kParamIndexPictureTypeMask,
// video info
@ -133,6 +134,12 @@ typedef C2PortParam<C2Tuning, C2Uint64Array, kParamIndexBlockPools> C2PortBlockP
typedef C2StreamParam<C2Info, C2BlobValue, kParamIndexCsd> C2StreamCsdInfo;
C2ENUM(C2PictureTypeMask, uint32_t,
C2PictureTypeKeyFrame = (1u << 0),
)
typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexPictureTypeMask> C2StreamPictureTypeMaskInfo;
/*
Component description fields:

@ -38,6 +38,15 @@ using ::android::hardware::graphics::mapper::V2_0::YCbCrLayout;
using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_vec;
namespace {
struct BufferDescriptorInfo {
IMapper::BufferDescriptorInfo mapperInfo;
uint32_t stride;
};
}
/* ===================================== GRALLOC ALLOCATION ==================================== */
static c2_status_t maperr2error(Error maperr) {
switch (maperr) {
@ -73,6 +82,7 @@ private:
uint32_t format;
uint32_t usage_lo;
uint32_t usage_hi;
uint32_t stride;
uint32_t magic;
};
@ -109,13 +119,16 @@ public:
static C2HandleGralloc* WrapNativeHandle(
const native_handle_t *const handle,
uint32_t width, uint32_t height, uint32_t format, uint64_t usage) {
uint32_t width, uint32_t height, uint32_t format, uint64_t usage, uint32_t stride) {
//CHECK(handle != nullptr);
if (native_handle_is_invalid(handle) ||
handle->numInts > int((INT_MAX - handle->version) / sizeof(int)) - NUM_INTS - handle->numFds) {
return nullptr;
}
ExtraData xd = { width, height, format, uint32_t(usage & 0xFFFFFFFF), uint32_t(usage >> 32), MAGIC };
ExtraData xd = {
width, height, format, uint32_t(usage & 0xFFFFFFFF), uint32_t(usage >> 32),
stride, MAGIC
};
native_handle_t *res = native_handle_create(handle->numFds, handle->numInts + NUM_INTS);
if (res != nullptr) {
memcpy(&res->data, &handle->data, sizeof(int) * (handle->numFds + handle->numInts));
@ -138,7 +151,8 @@ public:
static const C2HandleGralloc* Import(
const C2Handle *const handle,
uint32_t *width, uint32_t *height, uint32_t *format, uint64_t *usage) {
uint32_t *width, uint32_t *height, uint32_t *format,
uint64_t *usage, uint32_t *stride) {
const ExtraData *xd = getExtraData(handle);
if (xd == nullptr) {
return nullptr;
@ -147,15 +161,22 @@ public:
*height = xd->height;
*format = xd->format;
*usage = xd->usage_lo | (uint64_t(xd->usage_hi) << 32);
*stride = xd->stride;
return reinterpret_cast<const C2HandleGralloc *>(handle);
}
};
native_handle_t* UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle) {
native_handle_t *UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle) {
return C2HandleGralloc::UnwrapNativeHandle(handle);
}
C2Handle *WrapNativeCodec2GrallocHandle(
const native_handle_t *const handle,
uint32_t width, uint32_t height, uint32_t format, uint64_t usage, uint32_t stride) {
return C2HandleGralloc::WrapNativeHandle(handle, width, height, format, usage, stride);
}
class C2AllocationGralloc : public C2GraphicAllocation {
public:
virtual ~C2AllocationGralloc() override;
@ -171,7 +192,7 @@ public:
// internal methods
// |handle| will be moved.
C2AllocationGralloc(
const IMapper::BufferDescriptorInfo &info,
const BufferDescriptorInfo &info,
const sp<IMapper> &mapper,
hidl_handle &hidlHandle,
const C2HandleGralloc *const handle);
@ -179,7 +200,7 @@ public:
c2_status_t status() const;
private:
const IMapper::BufferDescriptorInfo mInfo;
const BufferDescriptorInfo mInfo;
const sp<IMapper> mMapper;
const hidl_handle mHidlHandle;
const C2HandleGralloc *mHandle;
@ -189,11 +210,11 @@ private:
};
C2AllocationGralloc::C2AllocationGralloc(
const IMapper::BufferDescriptorInfo &info,
const BufferDescriptorInfo &info,
const sp<IMapper> &mapper,
hidl_handle &hidlHandle,
const C2HandleGralloc *const handle)
: C2GraphicAllocation(info.width, info.height),
: C2GraphicAllocation(info.mapperInfo.width, info.mapperInfo.height),
mInfo(info),
mMapper(mapper),
mHidlHandle(std::move(hidlHandle)),
@ -241,83 +262,133 @@ c2_status_t C2AllocationGralloc::map(
return C2_CORRUPTED;
}
mLockedHandle = C2HandleGralloc::WrapNativeHandle(
mBuffer, mInfo.width, mInfo.height, (uint32_t)mInfo.format, mInfo.usage);
mBuffer, mInfo.mapperInfo.width, mInfo.mapperInfo.height,
(uint32_t)mInfo.mapperInfo.format, mInfo.mapperInfo.usage, mInfo.stride);
}
if (mInfo.format == PixelFormat::YCBCR_420_888 || mInfo.format == PixelFormat::YV12) {
YCbCrLayout ycbcrLayout;
mMapper->lockYCbCr(
const_cast<native_handle_t *>(mBuffer),
BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
{ (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height },
// TODO: fence
hidl_handle(),
[&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) {
err = maperr2error(maperr);
if (err == C2_OK) {
ycbcrLayout = mapLayout;
}
});
if (err != C2_OK) {
return err;
switch (mInfo.mapperInfo.format) {
case PixelFormat::YCBCR_420_888:
case PixelFormat::YV12: {
YCbCrLayout ycbcrLayout;
mMapper->lockYCbCr(
const_cast<native_handle_t *>(mBuffer),
BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
{ (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height },
// TODO: fence
hidl_handle(),
[&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) {
err = maperr2error(maperr);
if (err == C2_OK) {
ycbcrLayout = mapLayout;
}
});
if (err != C2_OK) {
return err;
}
addr[C2PlanarLayout::PLANE_Y] = (uint8_t *)ycbcrLayout.y;
addr[C2PlanarLayout::PLANE_U] = (uint8_t *)ycbcrLayout.cb;
addr[C2PlanarLayout::PLANE_V] = (uint8_t *)ycbcrLayout.cr;
layout->type = C2PlanarLayout::TYPE_YUV;
layout->numPlanes = 3;
layout->planes[C2PlanarLayout::PLANE_Y] = {
C2PlaneInfo::CHANNEL_Y, // channel
1, // colInc
(int32_t)ycbcrLayout.yStride, // rowInc
1, // mColSampling
1, // mRowSampling
8, // allocatedDepth
8, // bitDepth
0, // rightShift
C2PlaneInfo::NATIVE, // endianness
};
layout->planes[C2PlanarLayout::PLANE_U] = {
C2PlaneInfo::CHANNEL_CB, // channel
(int32_t)ycbcrLayout.chromaStep, // colInc
(int32_t)ycbcrLayout.cStride, // rowInc
2, // mColSampling
2, // mRowSampling
8, // allocatedDepth
8, // bitDepth
0, // rightShift
C2PlaneInfo::NATIVE, // endianness
};
layout->planes[C2PlanarLayout::PLANE_V] = {
C2PlaneInfo::CHANNEL_CR, // channel
(int32_t)ycbcrLayout.chromaStep, // colInc
(int32_t)ycbcrLayout.cStride, // rowInc
2, // mColSampling
2, // mRowSampling
8, // allocatedDepth
8, // bitDepth
0, // rightShift
C2PlaneInfo::NATIVE, // endianness
};
break;
}
addr[C2PlanarLayout::PLANE_Y] = (uint8_t *)ycbcrLayout.y;
addr[C2PlanarLayout::PLANE_U] = (uint8_t *)ycbcrLayout.cb;
addr[C2PlanarLayout::PLANE_V] = (uint8_t *)ycbcrLayout.cr;
layout->type = C2PlanarLayout::TYPE_YUV;
layout->numPlanes = 3;
layout->planes[C2PlanarLayout::PLANE_Y] = {
C2PlaneInfo::CHANNEL_Y, // channel
1, // colInc
(int32_t)ycbcrLayout.yStride, // rowInc
1, // mColSampling
1, // mRowSampling
8, // allocatedDepth
8, // bitDepth
0, // rightShift
C2PlaneInfo::NATIVE, // endianness
};
layout->planes[C2PlanarLayout::PLANE_U] = {
C2PlaneInfo::CHANNEL_CB, // channel
(int32_t)ycbcrLayout.chromaStep, // colInc
(int32_t)ycbcrLayout.cStride, // rowInc
2, // mColSampling
2, // mRowSampling
8, // allocatedDepth
8, // bitDepth
0, // rightShift
C2PlaneInfo::NATIVE, // endianness
};
layout->planes[C2PlanarLayout::PLANE_V] = {
C2PlaneInfo::CHANNEL_CR, // channel
(int32_t)ycbcrLayout.chromaStep, // colInc
(int32_t)ycbcrLayout.cStride, // rowInc
2, // mColSampling
2, // mRowSampling
8, // allocatedDepth
8, // bitDepth
0, // rightShift
C2PlaneInfo::NATIVE, // endianness
};
} else {
void *pointer = nullptr;
mMapper->lock(
const_cast<native_handle_t *>(mBuffer),
BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
{ (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height },
// TODO: fence
hidl_handle(),
[&err, &pointer](const auto &maperr, const auto &mapPointer) {
err = maperr2error(maperr);
if (err == C2_OK) {
pointer = mapPointer;
}
});
if (err != C2_OK) {
return err;
case PixelFormat::RGBA_8888:
// TODO: alpha channel
// fall-through
case PixelFormat::RGBX_8888: {
void *pointer = nullptr;
mMapper->lock(
const_cast<native_handle_t *>(mBuffer),
BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
{ (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height },
// TODO: fence
hidl_handle(),
[&err, &pointer](const auto &maperr, const auto &mapPointer) {
err = maperr2error(maperr);
if (err == C2_OK) {
pointer = mapPointer;
}
});
if (err != C2_OK) {
return err;
}
addr[C2PlanarLayout::PLANE_R] = (uint8_t *)pointer;
addr[C2PlanarLayout::PLANE_G] = (uint8_t *)pointer + 1;
addr[C2PlanarLayout::PLANE_B] = (uint8_t *)pointer + 2;
layout->type = C2PlanarLayout::TYPE_RGB;
layout->numPlanes = 3;
layout->planes[C2PlanarLayout::PLANE_R] = {
C2PlaneInfo::CHANNEL_R, // channel
4, // colInc
4 * (int32_t)mInfo.stride, // rowInc
1, // mColSampling
1, // mRowSampling
8, // allocatedDepth
8, // bitDepth
0, // rightShift
C2PlaneInfo::NATIVE, // endianness
};
layout->planes[C2PlanarLayout::PLANE_G] = {
C2PlaneInfo::CHANNEL_G, // channel
4, // colInc
4 * (int32_t)mInfo.stride, // rowInc
1, // mColSampling
1, // mRowSampling
8, // allocatedDepth
8, // bitDepth
0, // rightShift
C2PlaneInfo::NATIVE, // endianness
};
layout->planes[C2PlanarLayout::PLANE_B] = {
C2PlaneInfo::CHANNEL_B, // channel
4, // colInc
4 * (int32_t)mInfo.stride, // rowInc
1, // mColSampling
1, // mRowSampling
8, // allocatedDepth
8, // bitDepth
0, // rightShift
C2PlaneInfo::NATIVE, // endianness
};
break;
}
default: {
return C2_OMITTED;
}
// TODO
return C2_OMITTED;
}
mLocked = true;
@ -396,17 +467,20 @@ c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation(
// TODO: buffer usage should be determined according to |usage|
(void) usage;
IMapper::BufferDescriptorInfo info = {
width,
height,
1u, // layerCount
(PixelFormat)format,
BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
BufferDescriptorInfo info = {
{
width,
height,
1u, // layerCount
(PixelFormat)format,
BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
},
0u, // stride placeholder
};
c2_status_t err = C2_OK;
BufferDescriptor desc;
mMapper->createDescriptor(
info, [&err, &desc](const auto &maperr, const auto &descriptor) {
info.mapperInfo, [&err, &desc](const auto &maperr, const auto &descriptor) {
err = maperr2error(maperr);
if (err == C2_OK) {
desc = descriptor;
@ -421,8 +495,7 @@ c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation(
mAllocator->allocate(
desc,
1u,
[&err, &buffer](const auto &maperr, const auto &stride, auto &buffers) {
(void) stride;
[&err, &buffer, &info](const auto &maperr, const auto &stride, auto &buffers) {
err = maperr2error(maperr);
if (err != C2_OK) {
return;
@ -431,6 +504,7 @@ c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation(
err = C2_CORRUPTED;
return;
}
info.stride = stride;
buffer = std::move(buffers[0]);
});
if (err != C2_OK) {
@ -442,18 +516,20 @@ c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation(
info, mMapper, buffer,
C2HandleGralloc::WrapNativeHandle(
buffer.getNativeHandle(),
info.width, info.height, (uint32_t)info.format, info.usage)));
info.mapperInfo.width, info.mapperInfo.height,
(uint32_t)info.mapperInfo.format, info.mapperInfo.usage, info.stride)));
return C2_OK;
}
c2_status_t C2AllocatorGralloc::Impl::priorGraphicAllocation(
const C2Handle *handle,
std::shared_ptr<C2GraphicAllocation> *allocation) {
IMapper::BufferDescriptorInfo info;
info.layerCount = 1u;
BufferDescriptorInfo info;
info.mapperInfo.layerCount = 1u;
const C2HandleGralloc *grallocHandle = C2HandleGralloc::Import(
handle,
&info.width, &info.height, (uint32_t *)&info.format, (uint64_t *)&info.usage);
&info.mapperInfo.width, &info.mapperInfo.height,
(uint32_t *)&info.mapperInfo.format, (uint64_t *)&info.mapperInfo.usage, &info.stride);
if (grallocHandle == nullptr) {
return C2_BAD_VALUE;
}
@ -461,7 +537,7 @@ c2_status_t C2AllocatorGralloc::Impl::priorGraphicAllocation(
hidl_handle hidlHandle = C2HandleGralloc::UnwrapNativeHandle(grallocHandle);
allocation->reset(new C2AllocationGralloc(info, mMapper, hidlHandle, grallocHandle));
return C2_OMITTED;
return C2_OK;
}
C2AllocatorGralloc::C2AllocatorGralloc() : mImpl(new Impl) {}

@ -1,4 +1,3 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
@ -32,7 +31,17 @@ namespace android {
*
* @return a new NON-OWNING native handle that must be deleted using native_handle_delete.
*/
native_handle_t*UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle);
native_handle_t *UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle);
/**
* Wrap the gralloc handle and metadata into Codec2 handle recognized by
* C2AllocatorGralloc.
*
* @return a new NON-OWNING C2Handle that must be deleted using native_handle_delete.
*/
C2Handle *WrapNativeCodec2GrallocHandle(
const native_handle_t *const handle,
uint32_t width, uint32_t height, uint32_t format, uint64_t usage, uint32_t stride);
class C2AllocatorGralloc : public C2Allocator {
public:

@ -27,6 +27,7 @@
#include <C2Component.h>
#include <media/stagefright/foundation/Mutexed.h>
#include <media/stagefright/gbs/GraphicBufferSource.h>
#include <media/stagefright/CodecBase.h>
#include <media/ICrypto.h>
@ -35,134 +36,9 @@ namespace android {
/**
* BufferChannelBase implementation for CCodec.
*/
class CCodecBufferChannel : public BufferChannelBase {
class CCodecBufferChannel
: public BufferChannelBase, public std::enable_shared_from_this<CCodecBufferChannel> {
public:
/**
* Base class for representation of buffers at one port.
*/
class Buffers {
public:
Buffers() = default;
virtual ~Buffers() = default;
/**
* Set format for MediaCodec-facing buffers.
*/
inline void setFormat(const sp<AMessage> &format) { mFormat = format; }
/**
* Returns true if the buffers are operating under array mode.
*/
virtual bool isArrayMode() { return false; }
/**
* Fills the vector with MediaCodecBuffer's if in array mode; otherwise,
* no-op.
*/
virtual void getArray(Vector<sp<MediaCodecBuffer>> *) {}
protected:
// Format to be used for creating MediaCodec-facing buffers.
sp<AMessage> mFormat;
private:
DISALLOW_EVIL_CONSTRUCTORS(Buffers);
};
class InputBuffers : public Buffers {
public:
using Buffers::Buffers;
virtual ~InputBuffers() = default;
/**
* Set a block pool to obtain input memory blocks.
*/
inline void setPool(const std::shared_ptr<C2BlockPool> &pool) { mPool = pool; }
/**
* Get a new MediaCodecBuffer for input and its corresponding index.
* Returns false if no new buffer can be obtained at the moment.
*/
virtual bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) = 0;
/**
* Release the buffer obtained from requestNewBuffer() and get the
* associated C2Buffer object back. Returns empty shared_ptr if the
* buffer is not on file.
*/
virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) = 0;
/**
* Flush internal state. After this call, no index or buffer previously
* returned from requestNewBuffer() is valid.
*/
virtual void flush() = 0;
/**
* Return array-backed version of input buffers. The returned object
* shall retain the internal state so that it will honor index and
* buffer from previous calls of requestNewBuffer().
*/
virtual std::unique_ptr<InputBuffers> toArrayMode() = 0;
protected:
// Pool to obtain blocks for input buffers.
std::shared_ptr<C2BlockPool> mPool;
private:
DISALLOW_EVIL_CONSTRUCTORS(InputBuffers);
};
class OutputBuffers : public Buffers {
public:
using Buffers::Buffers;
virtual ~OutputBuffers() = default;
/**
* Register output C2Buffer from the component and obtain corresponding
* index and MediaCodecBuffer object. Returns false if registration
* fails.
*/
virtual bool registerBuffer(
const std::shared_ptr<C2Buffer> &buffer,
size_t *index,
sp<MediaCodecBuffer> *codecBuffer) = 0;
/**
* Register codec specific data as a buffer to be consistent with
* MediaCodec behavior.
*/
virtual bool registerCsd(
const C2StreamCsdInfo::output * /* csd */,
size_t * /* index */,
sp<MediaCodecBuffer> * /* codecBuffer */) {
return false;
}
/**
* Release the buffer obtained from registerBuffer() and get the
* associated C2Buffer object back. Returns empty shared_ptr if the
* buffer is not on file.
*/
virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) = 0;
/**
* Flush internal state. After this call, no index or buffer previously
* returned from registerBuffer() is valid.
*/
virtual void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) = 0;
/**
* Return array-backed version of output buffers. The returned object
* shall retain the internal state so that it will honor index and
* buffer from previous calls of registerBuffer().
*/
virtual std::unique_ptr<OutputBuffers> toArrayMode() = 0;
private:
DISALLOW_EVIL_CONSTRUCTORS(OutputBuffers);
};
CCodecBufferChannel(const std::function<void(status_t, enum ActionCode)> &onError);
virtual ~CCodecBufferChannel();
@ -186,23 +62,21 @@ public:
// Methods below are interface for CCodec to use.
/**
* Set the component object for buffer processing.
*/
void setComponent(const std::shared_ptr<C2Component> &component);
status_t setSurface(const sp<Surface> &surface);
/**
* Set C2BlockPool for input buffers.
*
* TODO: start timestamp?
* Set output graphic surface for rendering.
*/
void setInputBufferAllocator(const sp<C2BlockPool> &inAlloc);
status_t setSurface(const sp<Surface> &surface);
/**
* Set C2BlockPool for output buffers. This object shall never use the
* allocator itself; it's just passed
*
* TODO: start timestamp?
* Set GraphicBufferSource object from which the component extracts input
* buffers.
*/
void setOutputBufferAllocator(const sp<C2BlockPool> &outAlloc);
status_t setGraphicBufferSource(const sp<GraphicBufferSource> &source);
/**
* Start queueing buffers to the component. This object should never queue
@ -219,11 +93,17 @@ public:
void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork);
/**
* Notify MediaCodec about work done.
* Notify input client about work done.
*
* @param workItems finished work items.
* @param workItems finished work item.
*/
void onWorkDone(std::vector<std::unique_ptr<C2Work>> workItems);
void onWorkDone(const std::unique_ptr<C2Work> &work);
// Internal classes
class Buffers;
class InputBuffers;
class OutputBuffers;
class InputBufferClient;
private:
class QueueGuard;
@ -276,12 +156,17 @@ private:
bool mRunning;
};
class C2ComponentWrapper;
void feedInputBufferIfAvailable();
QueueSync mSync;
sp<MemoryDealer> mDealer;
sp<IMemory> mDecryptDestination;
int32_t mHeapSeqNum;
std::shared_ptr<C2Component> mComponent;
std::shared_ptr<InputBufferClient> mInputClient;
std::function<void(status_t, enum ActionCode)> mOnError;
std::shared_ptr<C2BlockPool> mInputAllocator;
QueueSync mQueueSync;

@ -24,6 +24,7 @@
#include <android/native_window.h>
#include <media/hardware/MetadataBufferType.h>
#include <media/stagefright/foundation/Mutexed.h>
#include <media/stagefright/gbs/GraphicBufferSource.h>
#include <media/stagefright/CodecBase.h>
#include <media/stagefright/FrameRenderTracker.h>
#include <media/stagefright/MediaDefs.h>
@ -58,6 +59,7 @@ public:
virtual void signalRequestIDRFrame() override;
void initiateReleaseIfStuck();
void onWorkDone(std::vector<std::unique_ptr<C2Work>> &workItems);
protected:
virtual ~CCodec();
@ -77,6 +79,10 @@ private:
void flush();
void release(bool sendCallback);
void createInputSurface();
void setInputSurface(const sp<PersistentSurface> &surface);
status_t setupInputSurface(const sp<GraphicBufferSource> &source);
void setDeadline(const TimePoint &deadline);
enum {
@ -86,6 +92,9 @@ private:
kWhatFlush,
kWhatStop,
kWhatRelease,
kWhatCreateInputSurface,
kWhatSetInputSurface,
kWhatWorkDone,
};
enum {
@ -104,14 +113,17 @@ private:
struct State {
inline State() : mState(RELEASED) {}
inline int get() const { return mState; }
inline void set(int newState) { mState = newState; }
std::shared_ptr<C2Component> comp;
private:
int mState;
std::shared_ptr<C2Component> mComp;
};
struct Formats {
sp<AMessage> mInputFormat;
sp<AMessage> mOutputFormat;
sp<AMessage> inputFormat;
sp<AMessage> outputFormat;
};
Mutexed<State> mState;
@ -119,6 +131,7 @@ private:
std::shared_ptr<C2Component::Listener> mListener;
Mutexed<TimePoint> mDeadline;
Mutexed<Formats> mFormats;
Mutexed<std::list<std::unique_ptr<C2Work>>> mWorkDoneQueue;
DISALLOW_EVIL_CONSTRUCTORS(CCodec);
};

Loading…
Cancel
Save