Merge "add test for AudioTrack constructor"
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index ddc4b16..44ed034 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -80,6 +80,7 @@
static bool gDisplayHistogram;
static bool showProgress = true;
static String8 gWriteMP4Filename;
+static String8 gComponentNameOverride;
static sp<ANativeWindow> gSurface;
@@ -193,7 +194,10 @@
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 @@
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 @@
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 @@
break;
}
+ case 'O':
+ {
+ gComponentNameOverride.setTo(optarg);
+ break;
+ }
+
case 'l':
{
listComponents = true;
@@ -1073,7 +1084,7 @@
i, MediaExtractor::kIncludeExtensiveMetaData);
if (meta == NULL) {
- break;
+ continue;
}
const char *mime;
meta->findCString(kKeyMIMEType, &mime);
diff --git a/media/libaaudio/tests/test_various.cpp b/media/libaaudio/tests/test_various.cpp
index 9e505d5..de386da 100644
--- a/media/libaaudio/tests/test_various.cpp
+++ b/media/libaaudio/tests/test_various.cpp
@@ -41,10 +41,76 @@
// Test AAudioStream_setBufferSizeInFrames()
+constexpr int64_t NANOS_PER_MILLISECOND = 1000 * 1000;
+
+//int foo() { // To fix Android Studio formatting when editing.
+TEST(test_various, aaudio_stop_when_open) {
+ AAudioStreamBuilder *aaudioBuilder = nullptr;
+ AAudioStream *aaudioStream = nullptr;
+
+// Use an AAudioStreamBuilder to contain requested parameters.
+ ASSERT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&aaudioBuilder));
+
+// Request stream properties.
+ AAudioStreamBuilder_setDataCallback(aaudioBuilder, MyDataCallbackProc, nullptr);
+ AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+
+// Create an AAudioStream using the Builder.
+ EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream));
+
+
+ aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNKNOWN;
+ EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
+ AAUDIO_STREAM_STATE_UNKNOWN, &state,
+ 1000 * NANOS_PER_MILLISECOND));
+ EXPECT_EQ(AAUDIO_STREAM_STATE_OPEN, state);
+
+ EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream));
+
+ state = AAUDIO_STREAM_STATE_UNKNOWN;
+ EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
+ AAUDIO_STREAM_STATE_UNKNOWN, &state, 0));
+ EXPECT_EQ(AAUDIO_STREAM_STATE_OPEN, state);
+
+ AAudioStream_close(aaudioStream);
+ AAudioStreamBuilder_delete(aaudioBuilder);
+}
+
+//int boo() { // To fix Android Studio formatting when editing.
+TEST(test_various, aaudio_flush_when_started) {
+ AAudioStreamBuilder *aaudioBuilder = nullptr;
+ AAudioStream *aaudioStream = nullptr;
+
+// Use an AAudioStreamBuilder to contain requested parameters.
+ ASSERT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&aaudioBuilder));
+
+// Request stream properties.
+ AAudioStreamBuilder_setDataCallback(aaudioBuilder, MyDataCallbackProc, nullptr);
+ AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+
+// Create an AAudioStream using the Builder.
+ EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream));
+ EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStart(aaudioStream));
+
+ aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNKNOWN;
+ EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
+ AAUDIO_STREAM_STATE_STARTING, &state,
+ 1000 * NANOS_PER_MILLISECOND));
+ EXPECT_EQ(AAUDIO_STREAM_STATE_STARTED, state);
+
+ EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, AAudioStream_requestFlush(aaudioStream));
+
+ state = AAUDIO_STREAM_STATE_UNKNOWN;
+ EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
+ AAUDIO_STREAM_STATE_UNKNOWN, &state, 0));
+ EXPECT_EQ(AAUDIO_STREAM_STATE_STARTED, state);
+
+ AAudioStream_close(aaudioStream);
+ AAudioStreamBuilder_delete(aaudioBuilder);
+}
+
//int main() { // To fix Android Studio formatting when editing.
TEST(test_various, aaudio_set_buffer_size) {
-
- aaudio_result_t result = AAUDIO_OK;
int32_t bufferCapacity;
int32_t framesPerBurst = 0;
int32_t actualSize = 0;
@@ -103,5 +169,4 @@
AAudioStream_close(aaudioStream);
AAudioStreamBuilder_delete(aaudioBuilder);
- printf(" result = %d = %s\n", result, AAudio_convertResultToText(result));
}
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 0c71487..a618676 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -37,6 +37,8 @@
"AudioPlayer.cpp",
"AudioSource.cpp",
"BufferImpl.cpp",
+ "CCodec.cpp",
+ "CCodecBufferChannel.cpp",
"CodecBase.cpp",
"CallbackDataSource.cpp",
"CallbackMediaSource.cpp",
@@ -89,6 +91,7 @@
"libdl",
"libdrmframework",
"libgui",
+ "libion",
"liblog",
"libmedia",
"libmedia_omx",
@@ -100,6 +103,7 @@
"libui",
"libutils",
"libmedia_helper",
+ "libstagefright_codec2",
"libstagefright_foundation",
"libstagefright_omx",
"libstagefright_omx_utils",
@@ -111,6 +115,11 @@
"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 @@
"libstagefright_esds",
"libstagefright_id3",
"libFLAC",
+
+ // XXX: hack
+ "libstagefright_codec2_vndk",
],
export_shared_lib_headers: [
diff --git a/media/libstagefright/BufferImpl.cpp b/media/libstagefright/BufferImpl.cpp
index fee3739..9fb6d34 100644
--- a/media/libstagefright/BufferImpl.cpp
+++ b/media/libstagefright/BufferImpl.cpp
@@ -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 @@
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 @@
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
diff --git a/media/libstagefright/CCodec.cpp b/media/libstagefright/CCodec.cpp
new file mode 100644
index 0000000..080d00f
--- /dev/null
+++ b/media/libstagefright/CCodec.cpp
@@ -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
diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp
new file mode 100644
index 0000000..9868cd4
--- /dev/null
+++ b/media/libstagefright/CCodecBufferChannel.cpp
@@ -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
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 4fedab6..677d25a 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -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 @@
//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 @@
}
}
}
-
if (mFlags & kFlagIsAsync) {
onOutputFormatChanged();
} else {
diff --git a/media/libstagefright/SimpleDecodingSource.cpp b/media/libstagefright/SimpleDecodingSource.cpp
index 67e6748..9b2fb4f 100644
--- a/media/libstagefright/SimpleDecodingSource.cpp
+++ b/media/libstagefright/SimpleDecodingSource.cpp
@@ -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 @@
//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,6 +67,33 @@
looper->start();
sp<MediaCodec> codec;
+ 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(&outFormat);
+ }
+ if (err == OK) {
+ return new SimpleDecodingSource(codec, source, looper,
+ surface != NULL,
+ strcmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS) == 0,
+ 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];
@@ -73,22 +104,10 @@
ALOGV("Attempting to allocate codec '%s'", componentName.c_str());
codec = MediaCodec::CreateByComponentName(looper, componentName);
- if (codec != NULL) {
- ALOGI("Successfully allocated codec '%s'", componentName.c_str());
-
- status_t err = codec->configure(format, surface, NULL /* crypto */, 0 /* flags */);
- if (err == OK) {
- err = codec->getOutputFormat(&format);
- }
- if (err == OK) {
- return new SimpleDecodingSource(codec, source, looper,
- surface != NULL,
- strcmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS) == 0,
- format);
- }
-
- ALOGD("Failed to configure codec '%s'", componentName.c_str());
- codec->release();
+ sp<SimpleDecodingSource> res = configure(codec, componentName);
+ if (res != NULL) {
+ return res;
+ } else {
codec = NULL;
}
}
diff --git a/media/libstagefright/codec2/Android.bp b/media/libstagefright/codec2/Android.bp
index 311a20b..2f0deb4 100644
--- a/media/libstagefright/codec2/Android.bp
+++ b/media/libstagefright/codec2/Android.bp
@@ -12,6 +12,10 @@
"frameworks/native/include/media/hardware",
],
+ export_include_dirs: [
+ "include",
+ ],
+
sanitize: {
misc_undefined: [
"unsigned-integer-overflow",
diff --git a/media/libstagefright/codec2/vndk/Android.bp b/media/libstagefright/codec2/vndk/Android.bp
index 64ce5e6..d2cfebb 100644
--- a/media/libstagefright/codec2/vndk/Android.bp
+++ b/media/libstagefright/codec2/vndk/Android.bp
@@ -9,6 +9,10 @@
"C2Store.cpp",
],
+ export_include_dirs: [
+ "include",
+ ],
+
include_dirs: [
"frameworks/av/media/libstagefright/codec2/include",
"frameworks/av/media/libstagefright/codec2/vndk/include",
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
index c74ca6d..b904d26 100644
--- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
@@ -790,6 +790,7 @@
}
void C2SoftAvcDec::processQueue() {
+#if 0
if (mIsInFlush) {
setFlushMode();
@@ -825,9 +826,10 @@
}
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 @@
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 @@
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::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 @@
s_video_flush_op.u4_error_code);
return UNKNOWN_ERROR;
}
-
+ mIsInFlush = true;
return OK;
}
@@ -1079,7 +1082,6 @@
}
status_t C2SoftAvcDec::deInitDecoder() {
- size_t i;
IV_API_CALL_STATUS_T status;
if (mCodecCtx) {
@@ -1206,7 +1208,6 @@
if (mSignalledError) {
return;
}
-
if (NULL == mCodecCtx) {
if (OK != initDecoder()) {
ALOGE("Failed to initialize decoder");
@@ -1221,66 +1222,78 @@
setParams(mStride);
}
- const C2ConstLinearBlock &buffer =
- work->input.buffers[0]->data().linearBlocks().front();
- if (buffer.capacity() == 0) {
- // TODO: result?
+ uint32_t workIndex = 0;
+ std::unique_ptr<C2ReadView> input;
+ if (work) {
+ work->result = C2_OK;
- 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;
- }
+ const C2ConstLinearBlock &buffer =
+ work->input.buffers[0]->data().linearBlocks().front();
+ if (buffer.capacity() == 0) {
+ // TODO: result?
- mReceivedEOS = true;
- // TODO: flush
- } else if (work->input.flags & C2BufferPack::FLAG_END_OF_STREAM) {
- mReceivedEOS = true;
- }
-
- 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;
}
- }
- 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);
+ 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;
+ }
+
+ 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 @@
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 @@
}
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 @@
/* 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;
+ 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));
+ }
- // outInfo->mOwnedByUs = false;
- // outQueue.erase(outQueue.begin());
- // outInfo = NULL;
- // notifyFillBufferDone(outHeader);
- // outHeader = NULL;
resetPlugin();
+ return;
}
}
inOffset += s_dec_op.u4_num_bytes_consumed;
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
index 5deaf5d..91ec003 100644
--- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
@@ -58,9 +58,6 @@
#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);
diff --git a/media/libstagefright/include/CCodecBufferChannel.h b/media/libstagefright/include/CCodecBufferChannel.h
new file mode 100644
index 0000000..354cee2
--- /dev/null
+++ b/media/libstagefright/include/CCodecBufferChannel.h
@@ -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_
diff --git a/media/libstagefright/include/Codec2Buffer.h b/media/libstagefright/include/Codec2Buffer.h
new file mode 100644
index 0000000..0272cea
--- /dev/null
+++ b/media/libstagefright/include/Codec2Buffer.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_
diff --git a/media/libstagefright/include/media/stagefright/CCodec.h b/media/libstagefright/include/media/stagefright/CCodec.h
new file mode 100644
index 0000000..3e24bbe
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/CCodec.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_
diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index 9197f7b..268662f 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.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>
diff --git a/media/libstagefright/include/media/stagefright/MediaFilter.h b/media/libstagefright/include/media/stagefright/MediaFilter.h
index 0c10d11..a28c49d 100644
--- a/media/libstagefright/include/media/stagefright/MediaFilter.h
+++ b/media/libstagefright/include/media/stagefright/MediaFilter.h
@@ -57,7 +57,7 @@
OWNED_BY_UPSTREAM,
};
- IOMX::buffer_id mBufferID;
+ uint32_t mBufferID;
int32_t mGeneration;
int32_t mOutputFlags;
Status mStatus;
@@ -121,7 +121,7 @@
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);
diff --git a/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h b/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
index 5060dc1..6aede08 100644
--- a/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
+++ b/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
@@ -47,7 +47,8 @@
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);