summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/wifi-display/source/Converter.cpp
diff options
context:
space:
mode:
authorAndreas Huber <andih@google.com>2012-08-29 11:41:50 -0700
committerAndreas Huber <andih@google.com>2012-08-29 15:06:57 -0700
commitd7bee3a9d2ad76d073d91f0ee36d5ac5f9df480c (patch)
tree3c4c7a83313d169b13c79c9660afeb804d27b975 /media/libstagefright/wifi-display/source/Converter.cpp
parenteb941f9a0c8474324732a99387cc6d8cb4ab01ef (diff)
downloadframeworks_av-d7bee3a9d2ad76d073d91f0ee36d5ac5f9df480c.zip
frameworks_av-d7bee3a9d2ad76d073d91f0ee36d5ac5f9df480c.tar.gz
frameworks_av-d7bee3a9d2ad76d073d91f0ee36d5ac5f9df480c.tar.bz2
Initial checkin of support for acting as a wifi display source
Change-Id: I08f17efa0c7d007e17408feb7d4fbef0a19f531a
Diffstat (limited to 'media/libstagefright/wifi-display/source/Converter.cpp')
-rw-r--r--media/libstagefright/wifi-display/source/Converter.cpp281
1 files changed, 281 insertions, 0 deletions
diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp
new file mode 100644
index 0000000..655fbae
--- /dev/null
+++ b/media/libstagefright/wifi-display/source/Converter.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2012, 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 "Converter"
+#include <utils/Log.h>
+
+#include "Converter.h"
+
+#include <gui/SurfaceTextureClient.h>
+#include <media/ICrypto.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+Converter::Converter(
+ const sp<AMessage> &notify,
+ const sp<ALooper> &codecLooper,
+ const sp<AMessage> &format)
+ : mInitCheck(NO_INIT),
+ mNotify(notify),
+ mCodecLooper(codecLooper),
+ mInputFormat(format),
+ mDoMoreWorkPending(false) {
+ mInitCheck = initEncoder();
+}
+
+Converter::~Converter() {
+ if (mEncoder != NULL) {
+ mEncoder->release();
+ mEncoder.clear();
+ }
+}
+
+status_t Converter::initCheck() const {
+ return mInitCheck;
+}
+
+sp<AMessage> Converter::getOutputFormat() const {
+ return mOutputFormat;
+}
+
+status_t Converter::initEncoder() {
+ AString inputMIME;
+ CHECK(mInputFormat->findString("mime", &inputMIME));
+
+ AString outputMIME;
+ bool isAudio = false;
+ if (!strcasecmp(inputMIME.c_str(), MEDIA_MIMETYPE_AUDIO_RAW)) {
+ outputMIME = MEDIA_MIMETYPE_AUDIO_AAC;
+ isAudio = true;
+ } else if (!strcasecmp(inputMIME.c_str(), MEDIA_MIMETYPE_VIDEO_RAW)) {
+ outputMIME = MEDIA_MIMETYPE_VIDEO_AVC;
+ } else {
+ TRESPASS();
+ }
+
+ mEncoder = MediaCodec::CreateByType(
+ mCodecLooper, outputMIME.c_str(), true /* encoder */);
+
+ if (mEncoder == NULL) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ mOutputFormat = mInputFormat->dup();
+ mOutputFormat->setString("mime", outputMIME.c_str());
+
+ if (isAudio) {
+ mOutputFormat->setInt32("bitrate", 64000); // 64 kBit/sec
+ } else {
+ mOutputFormat->setInt32("bitrate", 3000000); // 3Mbit/sec
+ mOutputFormat->setInt32("frame-rate", 30);
+ mOutputFormat->setInt32("i-frame-interval", 3); // Iframes every 3 secs
+ }
+
+ ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str());
+
+ status_t err = mEncoder->configure(
+ mOutputFormat,
+ NULL /* nativeWindow */,
+ NULL /* crypto */,
+ MediaCodec::CONFIGURE_FLAG_ENCODE);
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = mEncoder->start();
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = mEncoder->getInputBuffers(&mEncoderInputBuffers);
+
+ if (err != OK) {
+ return err;
+ }
+
+ return mEncoder->getOutputBuffers(&mEncoderOutputBuffers);
+}
+
+void Converter::feedAccessUnit(const sp<ABuffer> &accessUnit) {
+ sp<AMessage> msg = new AMessage(kWhatFeedAccessUnit, id());
+ msg->setBuffer("accessUnit", accessUnit);
+ msg->post();
+}
+
+void Converter::signalEOS() {
+ (new AMessage(kWhatInputEOS, id()))->post();
+}
+
+void Converter::notifyError(status_t err) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatError);
+ notify->setInt32("err", err);
+ notify->post();
+}
+
+void Converter::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatFeedAccessUnit:
+ {
+ sp<ABuffer> accessUnit;
+ CHECK(msg->findBuffer("accessUnit", &accessUnit));
+
+ mInputBufferQueue.push_back(accessUnit);
+
+ feedEncoderInputBuffers();
+
+ scheduleDoMoreWork();
+ break;
+ }
+
+ case kWhatInputEOS:
+ {
+ mInputBufferQueue.push_back(NULL);
+
+ feedEncoderInputBuffers();
+
+ scheduleDoMoreWork();
+ break;
+ }
+
+ case kWhatDoMoreWork:
+ {
+ mDoMoreWorkPending = false;
+ status_t err = doMoreWork();
+
+ if (err != OK) {
+ notifyError(err);
+ } else {
+ scheduleDoMoreWork();
+ }
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+}
+
+void Converter::scheduleDoMoreWork() {
+ if (mDoMoreWorkPending) {
+ return;
+ }
+
+ mDoMoreWorkPending = true;
+ (new AMessage(kWhatDoMoreWork, id()))->post(1000ll);
+}
+
+status_t Converter::feedEncoderInputBuffers() {
+ while (!mInputBufferQueue.empty()
+ && !mAvailEncoderInputIndices.empty()) {
+ sp<ABuffer> buffer = *mInputBufferQueue.begin();
+ mInputBufferQueue.erase(mInputBufferQueue.begin());
+
+ size_t bufferIndex = *mAvailEncoderInputIndices.begin();
+ mAvailEncoderInputIndices.erase(mAvailEncoderInputIndices.begin());
+
+ int64_t timeUs = 0ll;
+ uint32_t flags = 0;
+
+ if (buffer != NULL) {
+ CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+
+ memcpy(mEncoderInputBuffers.itemAt(bufferIndex)->data(),
+ buffer->data(),
+ buffer->size());
+
+ void *mediaBuffer;
+ if (buffer->meta()->findPointer("mediaBuffer", &mediaBuffer)
+ && mediaBuffer != NULL) {
+ mEncoderInputBuffers.itemAt(bufferIndex)->meta()
+ ->setPointer("mediaBuffer", mediaBuffer);
+
+ buffer->meta()->setPointer("mediaBuffer", NULL);
+ }
+ } else {
+ flags = MediaCodec::BUFFER_FLAG_EOS;
+ }
+
+ status_t err = mEncoder->queueInputBuffer(
+ bufferIndex, 0, (buffer == NULL) ? 0 : buffer->size(),
+ timeUs, flags);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+status_t Converter::doMoreWork() {
+ size_t bufferIndex;
+ status_t err = mEncoder->dequeueInputBuffer(&bufferIndex);
+
+ if (err == OK) {
+ mAvailEncoderInputIndices.push_back(bufferIndex);
+ feedEncoderInputBuffers();
+ }
+
+ size_t offset;
+ size_t size;
+ int64_t timeUs;
+ uint32_t flags;
+ err = mEncoder->dequeueOutputBuffer(
+ &bufferIndex, &offset, &size, &timeUs, &flags);
+
+ if (err == OK) {
+ if (flags & MediaCodec::BUFFER_FLAG_EOS) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatEOS);
+ notify->post();
+ } else {
+ sp<ABuffer> buffer = new ABuffer(size);
+ buffer->meta()->setInt64("timeUs", timeUs);
+
+ memcpy(buffer->data(),
+ mEncoderOutputBuffers.itemAt(bufferIndex)->base() + offset,
+ size);
+
+ if (flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) {
+ mOutputFormat->setBuffer("csd-0", buffer);
+ } else {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatAccessUnit);
+ notify->setBuffer("accessUnit", buffer);
+ notify->post();
+ }
+ }
+
+ err = mEncoder->releaseOutputBuffer(bufferIndex);
+ } else if (err == -EAGAIN) {
+ err = OK;
+ }
+
+ return err;
+}
+
+} // namespace android
+