am 05eaf105: Merge "Mp3decoder: Remove unused arm assembly files"

* commit '05eaf105a2a1445c2c13801ee4533ded47f1df1b':
  Mp3decoder: Remove unused arm assembly files
diff --git a/cmds/screenrecord/Android.mk b/cmds/screenrecord/Android.mk
index d77fdb6..6747e60 100644
--- a/cmds/screenrecord/Android.mk
+++ b/cmds/screenrecord/Android.mk
@@ -19,6 +19,7 @@
 LOCAL_SRC_FILES := \
 	screenrecord.cpp \
 	EglWindow.cpp \
+	FrameOutput.cpp \
 	TextRenderer.cpp \
 	Overlay.cpp \
 	Program.cpp
diff --git a/cmds/screenrecord/EglWindow.cpp b/cmds/screenrecord/EglWindow.cpp
index aa0517f..c16f2ad 100644
--- a/cmds/screenrecord/EglWindow.cpp
+++ b/cmds/screenrecord/EglWindow.cpp
@@ -35,11 +35,16 @@
 
 
 status_t EglWindow::createWindow(const sp<IGraphicBufferProducer>& surface) {
-    status_t err = eglSetupContext();
+    if (mEglSurface != EGL_NO_SURFACE) {
+        ALOGE("surface already created");
+        return UNKNOWN_ERROR;
+    }
+    status_t err = eglSetupContext(false);
     if (err != NO_ERROR) {
         return err;
     }
 
+    // Cache the current dimensions.  We're not expecting these to change.
     surface->query(NATIVE_WINDOW_WIDTH, &mWidth);
     surface->query(NATIVE_WINDOW_HEIGHT, &mHeight);
 
@@ -56,6 +61,34 @@
     return NO_ERROR;
 }
 
+status_t EglWindow::createPbuffer(int width, int height) {
+    if (mEglSurface != EGL_NO_SURFACE) {
+        ALOGE("surface already created");
+        return UNKNOWN_ERROR;
+    }
+    status_t err = eglSetupContext(true);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    mWidth = width;
+    mHeight = height;
+
+    EGLint pbufferAttribs[] = {
+            EGL_WIDTH, width,
+            EGL_HEIGHT, height,
+            EGL_NONE
+    };
+    mEglSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, pbufferAttribs);
+    if (mEglSurface == EGL_NO_SURFACE) {
+        ALOGE("eglCreatePbufferSurface error: %#x", eglGetError());
+        eglRelease();
+        return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
 status_t EglWindow::makeCurrent() const {
     if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
         ALOGE("eglMakeCurrent failed: %#x", eglGetError());
@@ -64,7 +97,7 @@
     return NO_ERROR;
 }
 
-status_t EglWindow::eglSetupContext() {
+status_t EglWindow::eglSetupContext(bool forPbuffer) {
     EGLBoolean result;
 
     mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
@@ -82,17 +115,28 @@
     ALOGV("Initialized EGL v%d.%d", majorVersion, minorVersion);
 
     EGLint numConfigs = 0;
-    EGLint configAttribs[] = {
-        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
-        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
-        EGL_RECORDABLE_ANDROID, 1,
-        EGL_RED_SIZE, 8,
-        EGL_GREEN_SIZE, 8,
-        EGL_BLUE_SIZE, 8,
-        EGL_NONE
+    EGLint windowConfigAttribs[] = {
+            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL_RECORDABLE_ANDROID, 1,
+            EGL_RED_SIZE, 8,
+            EGL_GREEN_SIZE, 8,
+            EGL_BLUE_SIZE, 8,
+            // no alpha
+            EGL_NONE
     };
-    result = eglChooseConfig(mEglDisplay, configAttribs, &mEglConfig, 1,
-            &numConfigs);
+    EGLint pbufferConfigAttribs[] = {
+            EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL_RED_SIZE, 8,
+            EGL_GREEN_SIZE, 8,
+            EGL_BLUE_SIZE, 8,
+            EGL_ALPHA_SIZE, 8,
+            EGL_NONE
+    };
+    result = eglChooseConfig(mEglDisplay,
+            forPbuffer ? pbufferConfigAttribs : windowConfigAttribs,
+            &mEglConfig, 1, &numConfigs);
     if (result != EGL_TRUE) {
         ALOGE("eglChooseConfig error: %#x", eglGetError());
         return UNKNOWN_ERROR;
diff --git a/cmds/screenrecord/EglWindow.h b/cmds/screenrecord/EglWindow.h
index 02a2efc..69d0c31 100644
--- a/cmds/screenrecord/EglWindow.h
+++ b/cmds/screenrecord/EglWindow.h
@@ -44,6 +44,9 @@
     // Creates an EGL window for the supplied surface.
     status_t createWindow(const sp<IGraphicBufferProducer>& surface);
 
+    // Creates an EGL pbuffer surface.
+    status_t createPbuffer(int width, int height);
+
     // Return width and height values (obtained from IGBP).
     int getWidth() const { return mWidth; }
     int getHeight() const { return mHeight; }
@@ -65,7 +68,7 @@
     EglWindow& operator=(const EglWindow&);
 
     // Init display, create config and context.
-    status_t eglSetupContext();
+    status_t eglSetupContext(bool forPbuffer);
     void eglRelease();
 
     // Basic EGL goodies.
diff --git a/cmds/screenrecord/FrameOutput.cpp b/cmds/screenrecord/FrameOutput.cpp
new file mode 100644
index 0000000..6c37501
--- /dev/null
+++ b/cmds/screenrecord/FrameOutput.cpp
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2014 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_TAG "ScreenRecord"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include "FrameOutput.h"
+
+using namespace android;
+
+static const bool kShowTiming = false;      // set to "true" for debugging
+static const int kGlBytesPerPixel = 4;      // GL_RGBA
+static const int kOutBytesPerPixel = 3;     // RGB only
+
+inline void FrameOutput::setValueLE(uint8_t* buf, uint32_t value) {
+    // Since we're running on an Android device, we're (almost) guaranteed
+    // to be little-endian, and (almost) guaranteed that unaligned 32-bit
+    // writes will work without any performance penalty... but do it
+    // byte-by-byte anyway.
+    buf[0] = (uint8_t) value;
+    buf[1] = (uint8_t) (value >> 8);
+    buf[2] = (uint8_t) (value >> 16);
+    buf[3] = (uint8_t) (value >> 24);
+}
+
+status_t FrameOutput::createInputSurface(int width, int height,
+        sp<IGraphicBufferProducer>* pBufferProducer) {
+    status_t err;
+
+    err = mEglWindow.createPbuffer(width, height);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    mEglWindow.makeCurrent();
+
+    glViewport(0, 0, width, height);
+    glDisable(GL_DEPTH_TEST);
+    glDisable(GL_CULL_FACE);
+
+    // Shader for rendering the external texture.
+    err = mExtTexProgram.setup(Program::PROGRAM_EXTERNAL_TEXTURE);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    // Input side (buffers from virtual display).
+    glGenTextures(1, &mExtTextureName);
+    if (mExtTextureName == 0) {
+        ALOGE("glGenTextures failed: %#x", glGetError());
+        return UNKNOWN_ERROR;
+    }
+
+    mBufferQueue = new BufferQueue(/*new GraphicBufferAlloc()*/);
+    mGlConsumer = new GLConsumer(mBufferQueue, mExtTextureName,
+                GL_TEXTURE_EXTERNAL_OES);
+    mGlConsumer->setName(String8("virtual display"));
+    mGlConsumer->setDefaultBufferSize(width, height);
+    mGlConsumer->setDefaultMaxBufferCount(5);
+    mGlConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_TEXTURE);
+
+    mGlConsumer->setFrameAvailableListener(this);
+
+    mPixelBuf = new uint8_t[width * height * kGlBytesPerPixel];
+
+    *pBufferProducer = mBufferQueue;
+
+    ALOGD("FrameOutput::createInputSurface OK");
+    return NO_ERROR;
+}
+
+status_t FrameOutput::copyFrame(FILE* fp, long timeoutUsec, bool rawFrames) {
+    Mutex::Autolock _l(mMutex);
+    ALOGV("copyFrame %ld\n", timeoutUsec);
+
+    if (!mFrameAvailable) {
+        nsecs_t timeoutNsec = (nsecs_t)timeoutUsec * 1000;
+        int cc = mEventCond.waitRelative(mMutex, timeoutNsec);
+        if (cc == -ETIMEDOUT) {
+            ALOGV("cond wait timed out");
+            return ETIMEDOUT;
+        } else if (cc != 0) {
+            ALOGW("cond wait returned error %d", cc);
+            return cc;
+        }
+    }
+    if (!mFrameAvailable) {
+        // This happens when Ctrl-C is hit.  Apparently POSIX says that the
+        // pthread wait call doesn't return EINTR, treating this instead as
+        // an instance of a "spurious wakeup".  We didn't get a frame, so
+        // we just treat it as a timeout.
+        return ETIMEDOUT;
+    }
+
+    // A frame is available.  Clear the flag for the next round.
+    mFrameAvailable = false;
+
+    float texMatrix[16];
+    mGlConsumer->updateTexImage();
+    mGlConsumer->getTransformMatrix(texMatrix);
+
+    // The data is in an external texture, so we need to render it to the
+    // pbuffer to get access to RGB pixel data.  We also want to flip it
+    // upside-down for easy conversion to a bitmap.
+    int width = mEglWindow.getWidth();
+    int height = mEglWindow.getHeight();
+    status_t err = mExtTexProgram.blit(mExtTextureName, texMatrix, 0, 0,
+            width, height, true);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    // GLES only guarantees that glReadPixels() will work with GL_RGBA, so we
+    // need to get 4 bytes/pixel and reduce it.  Depending on the size of the
+    // screen and the device capabilities, this can take a while.
+    int64_t startWhenNsec, pixWhenNsec, endWhenNsec;
+    if (kShowTiming) {
+        startWhenNsec = systemTime(CLOCK_MONOTONIC);
+    }
+    GLenum glErr;
+    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, mPixelBuf);
+    if ((glErr = glGetError()) != GL_NO_ERROR) {
+        ALOGE("glReadPixels failed: %#x", glErr);
+        return UNKNOWN_ERROR;
+    }
+    if (kShowTiming) {
+        pixWhenNsec = systemTime(CLOCK_MONOTONIC);
+    }
+    reduceRgbaToRgb(mPixelBuf, width * height);
+    if (kShowTiming) {
+        endWhenNsec = systemTime(CLOCK_MONOTONIC);
+        ALOGD("got pixels (get=%.3f ms, reduce=%.3fms)",
+                (pixWhenNsec - startWhenNsec) / 1000000.0,
+                (endWhenNsec - pixWhenNsec) / 1000000.0);
+    }
+
+    size_t rgbDataLen = width * height * kOutBytesPerPixel;
+
+    if (!rawFrames) {
+        // Fill out the header.
+        size_t headerLen = sizeof(uint32_t) * 5;
+        size_t packetLen = headerLen - sizeof(uint32_t) + rgbDataLen;
+        uint8_t header[headerLen];
+        setValueLE(&header[0], packetLen);
+        setValueLE(&header[4], width);
+        setValueLE(&header[8], height);
+        setValueLE(&header[12], width * kOutBytesPerPixel);
+        setValueLE(&header[16], HAL_PIXEL_FORMAT_RGB_888);
+        fwrite(header, 1, headerLen, fp);
+    }
+
+    // Currently using buffered I/O rather than writev().  Not expecting it
+    // to make much of a difference, but it might be worth a test for larger
+    // frame sizes.
+    if (kShowTiming) {
+        startWhenNsec = systemTime(CLOCK_MONOTONIC);
+    }
+    fwrite(mPixelBuf, 1, rgbDataLen, fp);
+    fflush(fp);
+    if (kShowTiming) {
+        endWhenNsec = systemTime(CLOCK_MONOTONIC);
+        ALOGD("wrote pixels (%.3f ms)",
+                (endWhenNsec - startWhenNsec) / 1000000.0);
+    }
+
+    if (ferror(fp)) {
+        // errno may not be useful; log it anyway
+        ALOGE("write failed (errno=%d)", errno);
+        return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+void FrameOutput::reduceRgbaToRgb(uint8_t* buf, unsigned int pixelCount) {
+    // Convert RGBA to RGB.
+    //
+    // Unaligned 32-bit accesses are allowed on ARM, so we could do this
+    // with 32-bit copies advancing at different rates (taking care at the
+    // end to not go one byte over).
+    const uint8_t* readPtr = buf;
+    for (unsigned int i = 0; i < pixelCount; i++) {
+        *buf++ = *readPtr++;
+        *buf++ = *readPtr++;
+        *buf++ = *readPtr++;
+        readPtr++;
+    }
+}
+
+// Callback; executes on arbitrary thread.
+void FrameOutput::onFrameAvailable() {
+    Mutex::Autolock _l(mMutex);
+    mFrameAvailable = true;
+    mEventCond.signal();
+}
diff --git a/cmds/screenrecord/FrameOutput.h b/cmds/screenrecord/FrameOutput.h
new file mode 100644
index 0000000..4ac3e8a
--- /dev/null
+++ b/cmds/screenrecord/FrameOutput.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2014 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 SCREENRECORD_FRAMEOUTPUT_H
+#define SCREENRECORD_FRAMEOUTPUT_H
+
+#include "Program.h"
+#include "EglWindow.h"
+
+#include <gui/BufferQueue.h>
+#include <gui/GLConsumer.h>
+
+namespace android {
+
+/*
+ * Support for "frames" output format.
+ */
+class FrameOutput : public GLConsumer::FrameAvailableListener {
+public:
+    FrameOutput() : mFrameAvailable(false),
+        mExtTextureName(0),
+        mPixelBuf(NULL)
+        {}
+    virtual ~FrameOutput() {
+        delete[] mPixelBuf;
+    }
+
+    // Create an "input surface", similar in purpose to a MediaCodec input
+    // surface, that the virtual display can send buffers to.  Also configures
+    // EGL with a pbuffer surface on the current thread.
+    status_t createInputSurface(int width, int height,
+            sp<IGraphicBufferProducer>* pBufferProducer);
+
+    // Copy one from input to output.  If no frame is available, this will wait up to the
+    // specified number of microseconds.
+    //
+    // Returns ETIMEDOUT if the timeout expired before we found a frame.
+    status_t copyFrame(FILE* fp, long timeoutUsec, bool rawFrames);
+
+    // Prepare to copy frames.  Makes the EGL context used by this object current.
+    void prepareToCopy() {
+        mEglWindow.makeCurrent();
+    }
+
+private:
+    FrameOutput(const FrameOutput&);
+    FrameOutput& operator=(const FrameOutput&);
+
+    // (overrides GLConsumer::FrameAvailableListener method)
+    virtual void onFrameAvailable();
+
+    // Reduces RGBA to RGB, in place.
+    static void reduceRgbaToRgb(uint8_t* buf, unsigned int pixelCount);
+
+    // Put a 32-bit value into a buffer, in little-endian byte order.
+    static void setValueLE(uint8_t* buf, uint32_t value);
+
+    // Used to wait for the FrameAvailableListener callback.
+    Mutex mMutex;
+    Condition mEventCond;
+
+    // Set by the FrameAvailableListener callback.
+    bool mFrameAvailable;
+
+    // Our queue.  The producer side is passed to the virtual display, the
+    // consumer side feeds into our GLConsumer.
+    sp<BufferQueue> mBufferQueue;
+
+    // This receives frames from the virtual display and makes them available
+    // as an external texture.
+    sp<GLConsumer> mGlConsumer;
+
+    // EGL display / context / surface.
+    EglWindow mEglWindow;
+
+    // GL rendering support.
+    Program mExtTexProgram;
+
+    // External texture, updated by GLConsumer.
+    GLuint mExtTextureName;
+
+    // Pixel data buffer.
+    uint8_t* mPixelBuf;
+};
+
+}; // namespace android
+
+#endif /*SCREENRECORD_FRAMEOUTPUT_H*/
diff --git a/cmds/screenrecord/Program.cpp b/cmds/screenrecord/Program.cpp
index a198204..73cae6e 100644
--- a/cmds/screenrecord/Program.cpp
+++ b/cmds/screenrecord/Program.cpp
@@ -201,7 +201,7 @@
 
 
 status_t Program::blit(GLuint texName, const float* texMatrix,
-        int32_t x, int32_t y, int32_t w, int32_t h) const {
+        int32_t x, int32_t y, int32_t w, int32_t h, bool invert) const {
     ALOGV("Program::blit %d xy=%d,%d wh=%d,%d", texName, x, y, w, h);
 
     const float pos[] = {
@@ -218,7 +218,7 @@
     };
     status_t err;
 
-    err = beforeDraw(texName, texMatrix, pos, uv);
+    err = beforeDraw(texName, texMatrix, pos, uv, invert);
     if (err == NO_ERROR) {
         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
         err = afterDraw();
@@ -232,7 +232,7 @@
 
     status_t err;
 
-    err = beforeDraw(texName, texMatrix, vertices, texes);
+    err = beforeDraw(texName, texMatrix, vertices, texes, false);
     if (err == NO_ERROR) {
         glDrawArrays(GL_TRIANGLES, 0, count);
         err = afterDraw();
@@ -241,7 +241,7 @@
 }
 
 status_t Program::beforeDraw(GLuint texName, const float* texMatrix,
-        const float* vertices, const float* texes) const {
+        const float* vertices, const float* texes, bool invert) const {
     // Create an orthographic projection matrix based on viewport size.
     GLint vp[4];
     glGetIntegerv(GL_VIEWPORT, vp);
@@ -251,6 +251,10 @@
         0.0f,               0.0f,               1.0f,   0.0f,
         -1.0f,              1.0f,               0.0f,   1.0f,
     };
+    if (invert) {
+        screenToNdc[5] = -screenToNdc[5];
+        screenToNdc[13] = -screenToNdc[13];
+    }
 
     glUseProgram(mProgram);
 
diff --git a/cmds/screenrecord/Program.h b/cmds/screenrecord/Program.h
index e47bc0d..558be8d 100644
--- a/cmds/screenrecord/Program.h
+++ b/cmds/screenrecord/Program.h
@@ -51,9 +51,11 @@
     // Release the program and associated resources.
     void release();
 
-    // Blit the specified texture to { x, y, x+w, y+h }.
+    // Blit the specified texture to { x, y, x+w, y+h }.  Inverts the
+    // content if "invert" is set.
     status_t blit(GLuint texName, const float* texMatrix,
-            int32_t x, int32_t y, int32_t w, int32_t h) const;
+            int32_t x, int32_t y, int32_t w, int32_t h,
+            bool invert = false) const;
 
     // Draw a number of triangles.
     status_t drawTriangles(GLuint texName, const float* texMatrix,
@@ -67,7 +69,7 @@
 
     // Common code for draw functions.
     status_t beforeDraw(GLuint texName, const float* texMatrix,
-            const float* vertices, const float* texes) const;
+            const float* vertices, const float* texes, bool invert) const;
     status_t afterDraw() const;
 
     // GLES 2 shader utilities.
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 61f83e3..02ed53a 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -45,10 +45,12 @@
 #include <signal.h>
 #include <getopt.h>
 #include <sys/wait.h>
+#include <termios.h>
 #include <assert.h>
 
 #include "screenrecord.h"
 #include "Overlay.h"
+#include "FrameOutput.h"
 
 using namespace android;
 
@@ -57,10 +59,14 @@
 static const uint32_t kMaxTimeLimitSec = 180;       // 3 minutes
 static const uint32_t kFallbackWidth = 1280;        // 720p
 static const uint32_t kFallbackHeight = 720;
+static const char* kMimeTypeAvc = "video/avc";
 
 // Command-line parameters.
 static bool gVerbose = false;           // chatty on stdout
 static bool gRotate = false;            // rotate 90 degrees
+static enum {
+    FORMAT_MP4, FORMAT_H264, FORMAT_FRAMES, FORMAT_RAW_FRAMES
+} gOutputFormat = FORMAT_MP4;           // data format for output
 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?
@@ -140,14 +146,14 @@
     status_t err;
 
     if (gVerbose) {
-        printf("Configuring recorder for %dx%d video at %.2fMbps\n",
-                gVideoWidth, gVideoHeight, gBitRate / 1000000.0);
+        printf("Configuring recorder for %dx%d %s at %.2fMbps\n",
+                gVideoWidth, gVideoHeight, kMimeTypeAvc, gBitRate / 1000000.0);
     }
 
     sp<AMessage> format = new AMessage;
     format->setInt32("width", gVideoWidth);
     format->setInt32("height", gVideoHeight);
-    format->setString("mime", "video/avc");
+    format->setString("mime", kMimeTypeAvc);
     format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque);
     format->setInt32("bitrate", gBitRate);
     format->setFloat("frame-rate", displayFps);
@@ -157,16 +163,18 @@
     looper->setName("screenrecord_looper");
     looper->start();
     ALOGV("Creating codec");
-    sp<MediaCodec> codec = MediaCodec::CreateByType(looper, "video/avc", true);
+    sp<MediaCodec> codec = MediaCodec::CreateByType(looper, kMimeTypeAvc, true);
     if (codec == NULL) {
-        fprintf(stderr, "ERROR: unable to create video/avc codec instance\n");
+        fprintf(stderr, "ERROR: unable to create %s codec instance\n",
+                kMimeTypeAvc);
         return UNKNOWN_ERROR;
     }
 
     err = codec->configure(format, NULL, NULL,
             MediaCodec::CONFIGURE_FLAG_ENCODE);
     if (err != NO_ERROR) {
-        fprintf(stderr, "ERROR: unable to configure codec (err=%d)\n", err);
+        fprintf(stderr, "ERROR: unable to configure %s codec at %dx%d (err=%d)\n",
+                kMimeTypeAvc, gVideoWidth, gVideoHeight, err);
         codec->release();
         return err;
     }
@@ -298,10 +306,12 @@
  * input frames are coming from the virtual display as fast as SurfaceFlinger
  * wants to send them.
  *
+ * Exactly one of muxer or rawFp must be non-null.
+ *
  * The muxer must *not* have been started before calling.
  */
 static status_t runEncoder(const sp<MediaCodec>& encoder,
-        const sp<MediaMuxer>& muxer, const sp<IBinder>& mainDpy,
+        const sp<MediaMuxer>& muxer, FILE* rawFp, const sp<IBinder>& mainDpy,
         const sp<IBinder>& virtualDpy, uint8_t orientation) {
     static int kTimeout = 250000;   // be responsive on signal
     status_t err;
@@ -311,6 +321,8 @@
     int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec);
     DisplayInfo mainDpyInfo;
 
+    assert((rawFp == NULL && muxer != NULL) || (rawFp != NULL && muxer == NULL));
+
     Vector<sp<ABuffer> > buffers;
     err = encoder->getOutputBuffers(&buffers);
     if (err != NO_ERROR) {
@@ -342,15 +354,16 @@
         case NO_ERROR:
             // got a buffer
             if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) != 0) {
-                // ignore this -- we passed the CSD into MediaMuxer when
-                // we got the format change notification
-                ALOGV("Got codec config buffer (%u bytes); ignoring", size);
-                size = 0;
+                ALOGV("Got codec config buffer (%u bytes)", size);
+                if (muxer != NULL) {
+                    // ignore this -- we passed the CSD into MediaMuxer when
+                    // we got the format change notification
+                    size = 0;
+                }
             }
             if (size != 0) {
                 ALOGV("Got data in buffer %d, size=%d, pts=%lld",
                         bufIndex, size, ptsUsec);
-                assert(trackIdx != -1);
 
                 { // scope
                     ATRACE_NAME("orientation");
@@ -379,14 +392,23 @@
                     ptsUsec = systemTime(SYSTEM_TIME_MONOTONIC) / 1000;
                 }
 
-                // The MediaMuxer docs are unclear, but it appears that we
-                // need to pass either the full set of BufferInfo flags, or
-                // (flags & BUFFER_FLAG_SYNCFRAME).
-                //
-                // If this blocks for too long we could drop frames.  We may
-                // want to queue these up and do them on a different thread.
-                { // scope
+                if (muxer == NULL) {
+                    fwrite(buffers[bufIndex]->data(), 1, size, rawFp);
+                    // Flush the data immediately in case we're streaming.
+                    // We don't want to do this if all we've written is
+                    // the SPS/PPS data because mplayer gets confused.
+                    if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) == 0) {
+                        fflush(rawFp);
+                    }
+                } else {
+                    // The MediaMuxer docs are unclear, but it appears that we
+                    // need to pass either the full set of BufferInfo flags, or
+                    // (flags & BUFFER_FLAG_SYNCFRAME).
+                    //
+                    // If this blocks for too long we could drop frames.  We may
+                    // want to queue these up and do them on a different thread.
                     ATRACE_NAME("write sample");
+                    assert(trackIdx != -1);
                     err = muxer->writeSampleData(buffers[bufIndex], trackIdx,
                             ptsUsec, flags);
                     if (err != NO_ERROR) {
@@ -418,12 +440,14 @@
                 ALOGV("Encoder format changed");
                 sp<AMessage> newFormat;
                 encoder->getOutputFormat(&newFormat);
-                trackIdx = muxer->addTrack(newFormat);
-                ALOGV("Starting muxer");
-                err = muxer->start();
-                if (err != NO_ERROR) {
-                    fprintf(stderr, "Unable to start muxer (err=%d)\n", err);
-                    return err;
+                if (muxer != NULL) {
+                    trackIdx = muxer->addTrack(newFormat);
+                    ALOGV("Starting muxer");
+                    err = muxer->start();
+                    if (err != NO_ERROR) {
+                        fprintf(stderr, "Unable to start muxer (err=%d)\n", err);
+                        return err;
+                    }
                 }
             }
             break;
@@ -457,7 +481,45 @@
 }
 
 /*
- * Main "do work" method.
+ * Raw H.264 byte stream output requested.  Send the output to stdout
+ * if desired.  If the output is a tty, reconfigure it to avoid the
+ * CRLF line termination that we see with "adb shell" commands.
+ */
+static FILE* prepareRawOutput(const char* fileName) {
+    FILE* rawFp = NULL;
+
+    if (strcmp(fileName, "-") == 0) {
+        if (gVerbose) {
+            fprintf(stderr, "ERROR: verbose output and '-' not compatible");
+            return NULL;
+        }
+        rawFp = stdout;
+    } else {
+        rawFp = fopen(fileName, "w");
+        if (rawFp == NULL) {
+            fprintf(stderr, "fopen raw failed: %s\n", strerror(errno));
+            return NULL;
+        }
+    }
+
+    int fd = fileno(rawFp);
+    if (isatty(fd)) {
+        // best effort -- reconfigure tty for "raw"
+        ALOGD("raw video output to tty (fd=%d)", fd);
+        struct termios term;
+        if (tcgetattr(fd, &term) == 0) {
+            cfmakeraw(&term);
+            if (tcsetattr(fd, TCSANOW, &term) == 0) {
+                ALOGD("tty successfully configured for raw");
+            }
+        }
+    }
+
+    return rawFp;
+}
+
+/*
+ * Main "do work" start point.
  *
  * Configures codec, muxer, and virtual display, then starts moving bits
  * around.
@@ -499,30 +561,40 @@
 
     // Configure and start the encoder.
     sp<MediaCodec> encoder;
+    sp<FrameOutput> frameOutput;
     sp<IGraphicBufferProducer> encoderInputSurface;
-    err = prepareEncoder(mainDpyInfo.fps, &encoder, &encoderInputSurface);
+    if (gOutputFormat != FORMAT_FRAMES && gOutputFormat != FORMAT_RAW_FRAMES) {
+        err = prepareEncoder(mainDpyInfo.fps, &encoder, &encoderInputSurface);
 
-    if (err != NO_ERROR && !gSizeSpecified) {
-        // fallback is defined for landscape; swap if we're in portrait
-        bool needSwap = gVideoWidth < gVideoHeight;
-        uint32_t newWidth = needSwap ? kFallbackHeight : kFallbackWidth;
-        uint32_t newHeight = needSwap ? kFallbackWidth : kFallbackHeight;
-        if (gVideoWidth != newWidth && gVideoHeight != newHeight) {
-            ALOGV("Retrying with 720p");
-            fprintf(stderr, "WARNING: failed at %dx%d, retrying at %dx%d\n",
-                    gVideoWidth, gVideoHeight, newWidth, newHeight);
-            gVideoWidth = newWidth;
-            gVideoHeight = newHeight;
-            err = prepareEncoder(mainDpyInfo.fps, &encoder,
-                    &encoderInputSurface);
+        if (err != NO_ERROR && !gSizeSpecified) {
+            // fallback is defined for landscape; swap if we're in portrait
+            bool needSwap = gVideoWidth < gVideoHeight;
+            uint32_t newWidth = needSwap ? kFallbackHeight : kFallbackWidth;
+            uint32_t newHeight = needSwap ? kFallbackWidth : kFallbackHeight;
+            if (gVideoWidth != newWidth && gVideoHeight != newHeight) {
+                ALOGV("Retrying with 720p");
+                fprintf(stderr, "WARNING: failed at %dx%d, retrying at %dx%d\n",
+                        gVideoWidth, gVideoHeight, newWidth, newHeight);
+                gVideoWidth = newWidth;
+                gVideoHeight = newHeight;
+                err = prepareEncoder(mainDpyInfo.fps, &encoder,
+                        &encoderInputSurface);
+            }
+        }
+        if (err != NO_ERROR) return err;
+
+        // From here on, we must explicitly release() the encoder before it goes
+        // out of scope, or we will get an assertion failure from stagefright
+        // later on in a different thread.
+    } else {
+        // We're not using an encoder at all.  The "encoder input surface" we hand to
+        // SurfaceFlinger will just feed directly to us.
+        frameOutput = new FrameOutput();
+        err = frameOutput->createInputSurface(gVideoWidth, gVideoHeight, &encoderInputSurface);
+        if (err != NO_ERROR) {
+            return err;
         }
     }
-    if (err != NO_ERROR) return err;
-
-    // From here on, we must explicitly release() the encoder before it goes
-    // out of scope, or we will get an assertion failure from stagefright
-    // later on in a different thread.
-
 
     // Draw the "info" page by rendering a frame with GLES and sending
     // it directly to the encoder.
@@ -539,7 +611,7 @@
         overlay = new Overlay();
         err = overlay->start(encoderInputSurface, &bufferProducer);
         if (err != NO_ERROR) {
-            encoder->release();
+            if (encoder != NULL) encoder->release();
             return err;
         }
         if (gVerbose) {
@@ -554,40 +626,93 @@
     sp<IBinder> dpy;
     err = prepareVirtualDisplay(mainDpyInfo, bufferProducer, &dpy);
     if (err != NO_ERROR) {
-        encoder->release();
+        if (encoder != NULL) encoder->release();
         return err;
     }
 
-    // Configure muxer.  We have to wait for the CSD blob from the encoder
-    // before we can start it.
-    sp<MediaMuxer> muxer = new MediaMuxer(fileName,
-            MediaMuxer::OUTPUT_FORMAT_MPEG_4);
-    if (gRotate) {
-        muxer->setOrientationHint(90);  // TODO: does this do anything?
+    sp<MediaMuxer> muxer = NULL;
+    FILE* rawFp = NULL;
+    switch (gOutputFormat) {
+        case FORMAT_MP4: {
+            // Configure muxer.  We have to wait for the CSD blob from the encoder
+            // before we can start it.
+            muxer = new MediaMuxer(fileName, MediaMuxer::OUTPUT_FORMAT_MPEG_4);
+            if (gRotate) {
+                muxer->setOrientationHint(90);  // TODO: does this do anything?
+            }
+            break;
+        }
+        case FORMAT_H264:
+        case FORMAT_FRAMES:
+        case FORMAT_RAW_FRAMES: {
+            rawFp = prepareRawOutput(fileName);
+            if (rawFp == NULL) {
+                if (encoder != NULL) encoder->release();
+                return -1;
+            }
+            break;
+        }
+        default:
+            fprintf(stderr, "ERROR: unknown format %d\n", gOutputFormat);
+            abort();
     }
 
-    // Main encoder loop.
-    err = runEncoder(encoder, muxer, mainDpy, dpy, mainDpyInfo.orientation);
-    if (err != NO_ERROR) {
-        fprintf(stderr, "Encoder failed (err=%d)\n", err);
-        // fall through to cleanup
-    }
+    if (gOutputFormat == FORMAT_FRAMES || gOutputFormat == FORMAT_RAW_FRAMES) {
+        // TODO: if we want to make this a proper feature, we should output
+        //       an outer header with version info.  Right now we never change
+        //       the frame size or format, so we could conceivably just send
+        //       the current frame header once and then follow it with an
+        //       unbroken stream of data.
 
-    if (gVerbose) {
-        printf("Stopping encoder and muxer\n");
+        // Make the EGL context current again.  This gets unhooked if we're
+        // using "--bugreport" mode.
+        // TODO: figure out if we can eliminate this
+        frameOutput->prepareToCopy();
+
+        while (!gStopRequested) {
+            // Poll for frames, the same way we do for MediaCodec.  We do
+            // all of the work on the main thread.
+            //
+            // Ideally we'd sleep indefinitely and wake when the
+            // stop was requested, but this will do for now.  (It almost
+            // works because wait() wakes when a signal hits, but we
+            // need to handle the edge cases.)
+            bool rawFrames = gOutputFormat == FORMAT_RAW_FRAMES;
+            err = frameOutput->copyFrame(rawFp, 250000, rawFrames);
+            if (err == ETIMEDOUT) {
+                err = NO_ERROR;
+            } else if (err != NO_ERROR) {
+                ALOGE("Got error %d from copyFrame()", err);
+                break;
+            }
+        }
+    } else {
+        // Main encoder loop.
+        err = runEncoder(encoder, muxer, rawFp, mainDpy, dpy,
+                mainDpyInfo.orientation);
+        if (err != NO_ERROR) {
+            fprintf(stderr, "Encoder failed (err=%d)\n", err);
+            // fall through to cleanup
+        }
+
+        if (gVerbose) {
+            printf("Stopping encoder and muxer\n");
+        }
     }
 
     // Shut everything down, starting with the producer side.
     encoderInputSurface = NULL;
     SurfaceComposerClient::destroyDisplay(dpy);
-    if (overlay != NULL) {
-        overlay->stop();
+    if (overlay != NULL) overlay->stop();
+    if (encoder != NULL) encoder->stop();
+    if (muxer != NULL) {
+        // If we don't stop muxer explicitly, i.e. let the destructor run,
+        // it may hang (b/11050628).
+        muxer->stop();
+    } else if (rawFp != stdout) {
+        fclose(rawFp);
     }
-    encoder->stop();
-    // If we don't stop muxer explicitly, i.e. let the destructor run,
-    // it may hang (b/11050628).
-    muxer->stop();
-    encoder->release();
+    if (encoder != NULL) encoder->release();
 
     return err;
 }
@@ -749,10 +874,12 @@
         { "size",               required_argument,  NULL, 's' },
         { "bit-rate",           required_argument,  NULL, 'b' },
         { "time-limit",         required_argument,  NULL, 't' },
+        { "bugreport",          no_argument,        NULL, 'u' },
+        // "unofficial" options
         { "show-device-info",   no_argument,        NULL, 'i' },
         { "show-frame-time",    no_argument,        NULL, 'f' },
-        { "bugreport",          no_argument,        NULL, 'u' },
         { "rotate",             no_argument,        NULL, 'r' },
+        { "output-format",      required_argument,  NULL, 'o' },
         { NULL,                 0,                  NULL, 0 }
     };
 
@@ -804,20 +931,34 @@
                 return 2;
             }
             break;
+        case 'u':
+            gWantInfoScreen = true;
+            gWantFrameTime = true;
+            break;
         case 'i':
             gWantInfoScreen = true;
             break;
         case 'f':
             gWantFrameTime = true;
             break;
-        case 'u':
-            gWantInfoScreen = true;
-            gWantFrameTime = true;
-            break;
         case 'r':
             // experimental feature
             gRotate = true;
             break;
+        case 'o':
+            if (strcmp(optarg, "mp4") == 0) {
+                gOutputFormat = FORMAT_MP4;
+            } else if (strcmp(optarg, "h264") == 0) {
+                gOutputFormat = FORMAT_H264;
+            } else if (strcmp(optarg, "frames") == 0) {
+                gOutputFormat = FORMAT_FRAMES;
+            } else if (strcmp(optarg, "raw-frames") == 0) {
+                gOutputFormat = FORMAT_RAW_FRAMES;
+            } else {
+                fprintf(stderr, "Unknown format '%s'\n", optarg);
+                return 2;
+            }
+            break;
         default:
             if (ic != '?') {
                 fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic);
@@ -831,17 +972,19 @@
         return 2;
     }
 
-    // MediaMuxer tries to create the file in the constructor, but we don't
-    // learn about the failure until muxer.start(), which returns a generic
-    // error code without logging anything.  We attempt to create the file
-    // now for better diagnostics.
     const char* fileName = argv[optind];
-    int fd = open(fileName, O_CREAT | O_RDWR, 0644);
-    if (fd < 0) {
-        fprintf(stderr, "Unable to open '%s': %s\n", fileName, strerror(errno));
-        return 1;
+    if (gOutputFormat == FORMAT_MP4) {
+        // MediaMuxer tries to create the file in the constructor, but we don't
+        // learn about the failure until muxer.start(), which returns a generic
+        // error code without logging anything.  We attempt to create the file
+        // now for better diagnostics.
+        int fd = open(fileName, O_CREAT | O_RDWR, 0644);
+        if (fd < 0) {
+            fprintf(stderr, "Unable to open '%s': %s\n", fileName, strerror(errno));
+            return 1;
+        }
+        close(fd);
     }
-    close(fd);
 
     status_t err = recordScreen(fileName);
     if (err == NO_ERROR) {
diff --git a/cmds/screenrecord/screenrecord.h b/cmds/screenrecord/screenrecord.h
index 95e8a68..9b058c2 100644
--- a/cmds/screenrecord/screenrecord.h
+++ b/cmds/screenrecord/screenrecord.h
@@ -18,6 +18,6 @@
 #define SCREENRECORD_SCREENRECORD_H
 
 #define kVersionMajor 1
-#define kVersionMinor 1
+#define kVersionMinor 2
 
 #endif /*SCREENRECORD_SCREENRECORD_H*/
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
index 69fa7a0fc..6efc712 100644
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
@@ -45,7 +45,7 @@
     // MockDrmFactory
     bool MockDrmFactory::isCryptoSchemeSupported(const uint8_t uuid[16])
     {
-        return (!memcmp(uuid, mock_uuid, sizeof(uuid)));
+        return (!memcmp(uuid, mock_uuid, sizeof(mock_uuid)));
     }
 
     bool MockDrmFactory::isContentTypeSupported(const String8 &mimeType)
@@ -65,7 +65,7 @@
     // MockCryptoFactory
     bool MockCryptoFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) const
     {
-        return (!memcmp(uuid, mock_uuid, sizeof(uuid)));
+        return (!memcmp(uuid, mock_uuid, sizeof(mock_uuid)));
     }
 
     status_t MockCryptoFactory::createPlugin(const uint8_t uuid[16], const void *data,
@@ -254,7 +254,9 @@
         return OK;
     }
 
-    status_t MockDrmPlugin::getProvisionRequest(Vector<uint8_t> &request,
+    status_t MockDrmPlugin::getProvisionRequest(String8 const &certType,
+                                                String8 const &certAuthority,
+                                                Vector<uint8_t> &request,
                                                 String8 &defaultUrl)
     {
         Mutex::Autolock lock(mLock);
@@ -282,7 +284,9 @@
         return OK;
     }
 
-    status_t MockDrmPlugin::provideProvisionResponse(Vector<uint8_t> const &response)
+    status_t MockDrmPlugin::provideProvisionResponse(Vector<uint8_t> const &response,
+                                                     Vector<uint8_t> &certificate,
+                                                     Vector<uint8_t> &wrappedKey)
     {
         Mutex::Autolock lock(mLock);
         ALOGD("MockDrmPlugin::provideProvisionResponse(%s)",
@@ -600,6 +604,33 @@
         return OK;
     }
 
+    status_t MockDrmPlugin::signRSA(Vector<uint8_t> const &sessionId,
+                                    String8 const &algorithm,
+                                    Vector<uint8_t> const &message,
+                                    Vector<uint8_t> const &wrappedKey,
+                                    Vector<uint8_t> &signature)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::signRSA(sessionId=%s, algorithm=%s, keyId=%s, "
+              "message=%s, signature=%s)",
+              vectorToString(sessionId).string(),
+              algorithm.string(),
+              vectorToString(message).string(),
+              vectorToString(wrappedKey).string(),
+              vectorToString(signature).string());
+
+        // Properties used in mock test, set by mock plugin and verifed cts test app
+        //   byte[] wrappedKey         -> mock-wrappedkey
+        //   byte[] message            -> mock-message
+        //   byte[] signature          -> mock-signature
+        mByteArrayProperties.add(String8("mock-sessionid"), sessionId);
+        mStringProperties.add(String8("mock-algorithm"), algorithm);
+        mByteArrayProperties.add(String8("mock-message"), message);
+        mByteArrayProperties.add(String8("mock-wrappedkey"), wrappedKey);
+        mByteArrayProperties.add(String8("mock-signature"), signature);
+        return OK;
+    }
+
     ssize_t MockDrmPlugin::findSession(Vector<uint8_t> const &sessionId) const
     {
         ALOGD("findSession: nsessions=%d, size=%d", mSessions.size(), sessionId.size());
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
index 2297f9b..97d7052 100644
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
@@ -76,10 +76,14 @@
         status_t queryKeyStatus(Vector<uint8_t> const &sessionId,
                                 KeyedVector<String8, String8> &infoMap) const;
 
-        status_t getProvisionRequest(Vector<uint8_t> &request,
-                                             String8 &defaultUrl);
+        status_t getProvisionRequest(String8 const &certType,
+                                     String8 const &certAuthority,
+                                     Vector<uint8_t> &request,
+                                     String8 &defaultUrl);
 
-        status_t provideProvisionResponse(Vector<uint8_t> const &response);
+        status_t provideProvisionResponse(Vector<uint8_t> const &response,
+                                          Vector<uint8_t> &certificate,
+                                          Vector<uint8_t> &wrappedKey);
 
         status_t getSecureStops(List<Vector<uint8_t> > &secureStops);
         status_t releaseSecureStops(Vector<uint8_t> const &ssRelease);
@@ -122,6 +126,12 @@
                         Vector<uint8_t> const &signature,
                         bool &match);
 
+        status_t signRSA(Vector<uint8_t> const &sessionId,
+                         String8 const &algorithm,
+                         Vector<uint8_t> const &message,
+                         Vector<uint8_t> const &wrappedKey,
+                         Vector<uint8_t> &signature);
+
     private:
         String8 vectorToString(Vector<uint8_t> const &vector) const;
         String8 arrayToString(uint8_t const *array, size_t len) const;
diff --git a/include/media/IDrm.h b/include/media/IDrm.h
index 5ef26af..32ae28e 100644
--- a/include/media/IDrm.h
+++ b/include/media/IDrm.h
@@ -61,10 +61,14 @@
     virtual status_t queryKeyStatus(Vector<uint8_t> const &sessionId,
                                     KeyedVector<String8, String8> &infoMap) const = 0;
 
-    virtual status_t getProvisionRequest(Vector<uint8_t> &request,
+    virtual status_t getProvisionRequest(String8 const &certType,
+                                         String8 const &certAuthority,
+                                         Vector<uint8_t> &request,
                                          String8 &defaulUrl) = 0;
 
-    virtual status_t provideProvisionResponse(Vector<uint8_t> const &response) = 0;
+    virtual status_t provideProvisionResponse(Vector<uint8_t> const &response,
+                                              Vector<uint8_t> &certificate,
+                                              Vector<uint8_t> &wrappedKey) = 0;
 
     virtual status_t getSecureStops(List<Vector<uint8_t> > &secureStops) = 0;
 
@@ -107,6 +111,12 @@
                             Vector<uint8_t> const &signature,
                             bool &match) = 0;
 
+    virtual status_t signRSA(Vector<uint8_t> const &sessionId,
+                             String8 const &algorithm,
+                             Vector<uint8_t> const &message,
+                             Vector<uint8_t> const &wrappedKey,
+                             Vector<uint8_t> &signature) = 0;
+
     virtual status_t setListener(const sp<IDrmClient>& listener) = 0;
 
 private:
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 46c62dc..7ba5acc 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -203,7 +203,6 @@
     unsigned mDequeueCounter;
     bool mStoreMetaDataInOutputBuffers;
     int32_t mMetaDataBuffersToSubmit;
-    size_t mNumUndequeuedBuffers;
 
     int64_t mRepeatFrameDelayUs;
     int64_t mMaxPtsGapUs;
@@ -254,6 +253,8 @@
             int32_t numChannels, int32_t sampleRate, int32_t bitRate,
             int32_t aacProfile, bool isADTS);
 
+    status_t setupAC3Codec(bool encoder, int32_t numChannels, int32_t sampleRate);
+
     status_t selectAudioPortFormat(
             OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat);
 
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index 85693d4..cf5beda 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -44,6 +44,7 @@
 extern const char *MEDIA_MIMETYPE_AUDIO_FLAC;
 extern const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS;
 extern const char *MEDIA_MIMETYPE_AUDIO_MSGSM;
+extern const char *MEDIA_MIMETYPE_AUDIO_AC3;
 
 extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
 extern const char *MEDIA_MIMETYPE_CONTAINER_WAV;
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index daaf20f..5121c17 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -248,6 +248,8 @@
             int32_t numChannels, int32_t sampleRate, int32_t bitRate,
             int32_t aacProfile, bool isADTS);
 
+    status_t setAC3Format(int32_t numChannels, int32_t sampleRate);
+
     void setG711Format(int32_t numChannels);
 
     status_t setVideoPortFormatType(
diff --git a/media/libmedia/IDrm.cpp b/media/libmedia/IDrm.cpp
index f7a9a75..f1a6a9f 100644
--- a/media/libmedia/IDrm.cpp
+++ b/media/libmedia/IDrm.cpp
@@ -51,6 +51,7 @@
     ENCRYPT,
     DECRYPT,
     SIGN,
+    SIGN_RSA,
     VERIFY,
     SET_LISTENER
 };
@@ -196,11 +197,15 @@
         return reply.readInt32();
     }
 
-    virtual status_t getProvisionRequest(Vector<uint8_t> &request,
+    virtual status_t getProvisionRequest(String8 const &certType,
+                                         String8 const &certAuthority,
+                                         Vector<uint8_t> &request,
                                          String8 &defaultUrl) {
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
 
+        data.writeString8(certType);
+        data.writeString8(certAuthority);
         remote()->transact(GET_PROVISION_REQUEST, data, &reply);
 
         readVector(reply, request);
@@ -209,13 +214,18 @@
         return reply.readInt32();
     }
 
-    virtual status_t provideProvisionResponse(Vector<uint8_t> const &response) {
+    virtual status_t provideProvisionResponse(Vector<uint8_t> const &response,
+                                              Vector<uint8_t> &certificate,
+                                              Vector<uint8_t> &wrappedKey) {
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
 
         writeVector(data, response);
         remote()->transact(PROVIDE_PROVISION_RESPONSE, data, &reply);
 
+        readVector(reply, certificate);
+        readVector(reply, wrappedKey);
+
         return reply.readInt32();
     }
 
@@ -386,6 +396,25 @@
         return reply.readInt32();
     }
 
+    virtual status_t signRSA(Vector<uint8_t> const &sessionId,
+                             String8 const &algorithm,
+                             Vector<uint8_t> const &message,
+                             Vector<uint8_t> const &wrappedKey,
+                             Vector<uint8_t> &signature) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        writeVector(data, sessionId);
+        data.writeString8(algorithm);
+        writeVector(data, message);
+        writeVector(data, wrappedKey);
+
+        remote()->transact(SIGN_RSA, data, &reply);
+        readVector(reply, signature);
+
+        return reply.readInt32();
+    }
+
     virtual status_t setListener(const sp<IDrmClient>& listener) {
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
@@ -563,9 +592,13 @@
         case GET_PROVISION_REQUEST:
         {
             CHECK_INTERFACE(IDrm, data, reply);
+            String8 certType = data.readString8();
+            String8 certAuthority = data.readString8();
+
             Vector<uint8_t> request;
             String8 defaultUrl;
-            status_t result = getProvisionRequest(request, defaultUrl);
+            status_t result = getProvisionRequest(certType, certAuthority,
+                                                  request, defaultUrl);
             writeVector(reply, request);
             reply->writeString8(defaultUrl);
             reply->writeInt32(result);
@@ -576,8 +609,13 @@
         {
             CHECK_INTERFACE(IDrm, data, reply);
             Vector<uint8_t> response;
+            Vector<uint8_t> certificate;
+            Vector<uint8_t> wrappedKey;
             readVector(data, response);
-            reply->writeInt32(provideProvisionResponse(response));
+            status_t result = provideProvisionResponse(response, certificate, wrappedKey);
+            writeVector(reply, certificate);
+            writeVector(reply, wrappedKey);
+            reply->writeInt32(result);
             return OK;
         }
 
@@ -725,6 +763,20 @@
             return OK;
         }
 
+        case SIGN_RSA:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            Vector<uint8_t> sessionId, message, wrappedKey, signature;
+            readVector(data, sessionId);
+            String8 algorithm = data.readString8();
+            readVector(data, message);
+            readVector(data, wrappedKey);
+            uint32_t result = signRSA(sessionId, algorithm, message, wrappedKey, signature);
+            writeVector(reply, signature);
+            reply->writeInt32(result);
+            return OK;
+        }
+
     case SET_LISTENER: {
         CHECK_INTERFACE(IDrm, data, reply);
         sp<IDrmClient> listener =
diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp
index eebcb79..d50037f 100644
--- a/media/libmediaplayerservice/Drm.cpp
+++ b/media/libmediaplayerservice/Drm.cpp
@@ -28,9 +28,21 @@
 #include <media/stagefright/foundation/AString.h>
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/MediaErrors.h>
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
 
 namespace android {
 
+static bool checkPermission(const char* permissionString) {
+#ifndef HAVE_ANDROID_OS
+    return true;
+#endif
+    if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
+    bool ok = checkCallingPermission(String16(permissionString));
+    if (!ok) ALOGE("Request requires %s", permissionString);
+    return ok;
+}
+
 KeyedVector<Vector<uint8_t>, String8> Drm::mUUIDToLibraryPathMap;
 KeyedVector<String8, wp<SharedLibrary> > Drm::mLibraryPathToOpenLibraryMap;
 Mutex Drm::mMapLock;
@@ -373,7 +385,8 @@
     return mPlugin->queryKeyStatus(sessionId, infoMap);
 }
 
-status_t Drm::getProvisionRequest(Vector<uint8_t> &request, String8 &defaultUrl) {
+status_t Drm::getProvisionRequest(String8 const &certType, String8 const &certAuthority,
+                                  Vector<uint8_t> &request, String8 &defaultUrl) {
     Mutex::Autolock autoLock(mLock);
 
     if (mInitCheck != OK) {
@@ -384,10 +397,13 @@
         return -EINVAL;
     }
 
-    return mPlugin->getProvisionRequest(request, defaultUrl);
+    return mPlugin->getProvisionRequest(certType, certAuthority,
+                                        request, defaultUrl);
 }
 
-status_t Drm::provideProvisionResponse(Vector<uint8_t> const &response) {
+status_t Drm::provideProvisionResponse(Vector<uint8_t> const &response,
+                                       Vector<uint8_t> &certificate,
+                                       Vector<uint8_t> &wrappedKey) {
     Mutex::Autolock autoLock(mLock);
 
     if (mInitCheck != OK) {
@@ -398,7 +414,7 @@
         return -EINVAL;
     }
 
-    return mPlugin->provideProvisionResponse(response);
+    return mPlugin->provideProvisionResponse(response, certificate, wrappedKey);
 }
 
 
@@ -589,6 +605,28 @@
     return mPlugin->verify(sessionId, keyId, message, signature, match);
 }
 
+status_t Drm::signRSA(Vector<uint8_t> const &sessionId,
+                      String8 const &algorithm,
+                      Vector<uint8_t> const &message,
+                      Vector<uint8_t> const &wrappedKey,
+                      Vector<uint8_t> &signature) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    if (!checkPermission("android.permission.ACCESS_DRM_CERTIFICATES")) {
+        return -EPERM;
+    }
+
+    return mPlugin->signRSA(sessionId, algorithm, message, wrappedKey, signature);
+}
+
 void Drm::binderDied(const wp<IBinder> &the_late_who)
 {
     delete mPlugin;
diff --git a/media/libmediaplayerservice/Drm.h b/media/libmediaplayerservice/Drm.h
index 119fd50..3d4b0fc 100644
--- a/media/libmediaplayerservice/Drm.h
+++ b/media/libmediaplayerservice/Drm.h
@@ -66,10 +66,14 @@
     virtual status_t queryKeyStatus(Vector<uint8_t> const &sessionId,
                                     KeyedVector<String8, String8> &infoMap) const;
 
-    virtual status_t getProvisionRequest(Vector<uint8_t> &request,
+    virtual status_t getProvisionRequest(String8 const &certType,
+                                         String8 const &certAuthority,
+                                         Vector<uint8_t> &request,
                                          String8 &defaulUrl);
 
-    virtual status_t provideProvisionResponse(Vector<uint8_t> const &response);
+    virtual status_t provideProvisionResponse(Vector<uint8_t> const &response,
+                                              Vector<uint8_t> &certificate,
+                                              Vector<uint8_t> &wrappedKey);
 
     virtual status_t getSecureStops(List<Vector<uint8_t> > &secureStops);
 
@@ -111,6 +115,12 @@
                             Vector<uint8_t> const &signature,
                             bool &match);
 
+    virtual status_t signRSA(Vector<uint8_t> const &sessionId,
+                             String8 const &algorithm,
+                             Vector<uint8_t> const &message,
+                             Vector<uint8_t> const &wrappedKey,
+                             Vector<uint8_t> &signature);
+
     virtual status_t setListener(const sp<IDrmClient>& listener);
 
     virtual void sendEvent(DrmPlugin::EventType eventType, int extra,
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index f5fb622..08a3c7f 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -37,7 +37,9 @@
 
 #include <media/hardware/HardwareAPI.h>
 
+#include <OMX_AudioExt.h>
 #include <OMX_Component.h>
+#include <OMX_IndexExt.h>
 
 #include "include/avc_utils.h"
 
@@ -640,34 +642,18 @@
         return err;
     }
 
-    // FIXME: assume that surface is controlled by app (native window
-    // returns the number for the case when surface is not controlled by app)
-    // FIXME2: This means that minUndeqeueudBufs can be 1 larger than reported
-    // For now, try to allocate 1 more buffer, but don't fail if unsuccessful
-
-    // Use conservative allocation while also trying to reduce starvation
-    //
-    // 1. allocate at least nBufferCountMin + minUndequeuedBuffers - that is the
-    //    minimum needed for the consumer to be able to work
-    // 2. try to allocate two (2) additional buffers to reduce starvation from
-    //    the consumer
-    //    plus an extra buffer to account for incorrect minUndequeuedBufs
-    for (OMX_U32 extraBuffers = 2 + 1; /* condition inside loop */; extraBuffers--) {
-        OMX_U32 newBufferCount =
-            def.nBufferCountMin + *minUndequeuedBuffers + extraBuffers;
+    // XXX: Is this the right logic to use?  It's not clear to me what the OMX
+    // buffer counts refer to - how do they account for the renderer holding on
+    // to buffers?
+    if (def.nBufferCountActual < def.nBufferCountMin + *minUndequeuedBuffers) {
+        OMX_U32 newBufferCount = def.nBufferCountMin + *minUndequeuedBuffers;
         def.nBufferCountActual = newBufferCount;
         err = mOMX->setParameter(
                 mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
 
-        if (err == OK) {
-            *minUndequeuedBuffers += extraBuffers;
-            break;
-        }
-
-        ALOGW("[%s] setting nBufferCountActual to %lu failed: %d",
-                mComponentName.c_str(), newBufferCount, err);
-        /* exit condition */
-        if (extraBuffers == 0) {
+        if (err != OK) {
+            ALOGE("[%s] setting nBufferCountActual to %lu failed: %d",
+                    mComponentName.c_str(), newBufferCount, err);
             return err;
         }
     }
@@ -692,7 +678,6 @@
             &bufferCount, &bufferSize, &minUndequeuedBuffers);
     if (err != 0)
         return err;
-    mNumUndequeuedBuffers = minUndequeuedBuffers;
 
     ALOGV("[%s] Allocating %lu buffers from a native window of size %lu on "
          "output port",
@@ -758,7 +743,6 @@
             &bufferCount, &bufferSize, &minUndequeuedBuffers);
     if (err != 0)
         return err;
-    mNumUndequeuedBuffers = minUndequeuedBuffers;
 
     ALOGV("[%s] Allocating %lu meta buffers on output port",
          mComponentName.c_str(), bufferCount);
@@ -999,6 +983,10 @@
             "audio_decoder.flac", "audio_encoder.flac" },
         { MEDIA_MIMETYPE_AUDIO_MSGSM,
             "audio_decoder.gsm", "audio_encoder.gsm" },
+        { MEDIA_MIMETYPE_VIDEO_MPEG2,
+            "video_decoder.mpeg2", "video_encoder.mpeg2" },
+        { MEDIA_MIMETYPE_AUDIO_AC3,
+            "audio_decoder.ac3", "audio_encoder.ac3" },
     };
 
     static const size_t kNumMimeToRole =
@@ -1297,6 +1285,15 @@
         } else {
             err = setupRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
         }
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC3)) {
+        int32_t numChannels;
+        int32_t sampleRate;
+        if (!msg->findInt32("channel-count", &numChannels)
+                || !msg->findInt32("sample-rate", &sampleRate)) {
+            err = INVALID_OPERATION;
+        } else {
+            err = setupAC3Codec(encoder, numChannels, sampleRate);
+        }
     }
 
     if (err != OK) {
@@ -1493,6 +1490,44 @@
             mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
 }
 
+status_t ACodec::setupAC3Codec(
+        bool encoder, int32_t numChannels, int32_t sampleRate) {
+    status_t err = setupRawAudioFormat(
+            encoder ? kPortIndexInput : kPortIndexOutput, sampleRate, numChannels);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (encoder) {
+        ALOGW("AC3 encoding is not supported.");
+        return INVALID_OPERATION;
+    }
+
+    OMX_AUDIO_PARAM_ANDROID_AC3TYPE def;
+    InitOMXParams(&def);
+    def.nPortIndex = kPortIndexInput;
+
+    err = mOMX->getParameter(
+            mNode,
+            (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3,
+            &def,
+            sizeof(def));
+
+    if (err != OK) {
+        return err;
+    }
+
+    def.nChannels = numChannels;
+    def.nSampleRate = sampleRate;
+
+    return mOMX->setParameter(
+            mNode,
+            (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3,
+            &def,
+            sizeof(def));
+}
+
 static OMX_AUDIO_AMRBANDMODETYPE pickModeFromBitRate(
         bool isAMRWB, int32_t bps) {
     if (isAMRWB) {
@@ -2449,7 +2484,19 @@
         return;
     }
 
-    while (countBuffersOwnedByNativeWindow() > mNumUndequeuedBuffers
+    int minUndequeuedBufs = 0;
+    status_t err = mNativeWindow->query(
+            mNativeWindow.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+            &minUndequeuedBufs);
+
+    if (err != OK) {
+        ALOGE("[%s] NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)",
+                mComponentName.c_str(), strerror(-err), -err);
+
+        minUndequeuedBufs = 0;
+    }
+
+    while (countBuffersOwnedByNativeWindow() > (size_t)minUndequeuedBufs
             && dequeueBufferFromNativeWindow() != NULL) {
         // these buffers will be submitted as regular buffers; account for this
         if (mStoreMetaDataInOutputBuffers && mMetaDataBuffersToSubmit > 0) {
@@ -2575,7 +2622,7 @@
         {
             OMX_AUDIO_PORTDEFINITIONTYPE *audioDef = &def.format.audio;
 
-            switch (audioDef->eEncoding) {
+            switch ((int)audioDef->eEncoding) {
                 case OMX_AUDIO_CodingPCM:
                 {
                     OMX_AUDIO_PARAM_PCMMODETYPE params;
@@ -2681,6 +2728,24 @@
                     break;
                 }
 
+                case OMX_AUDIO_CodingAndroidAC3:
+                {
+                    OMX_AUDIO_PARAM_ANDROID_AC3TYPE params;
+                    InitOMXParams(&params);
+                    params.nPortIndex = kPortIndexOutput;
+
+                    CHECK_EQ((status_t)OK, mOMX->getParameter(
+                            mNode,
+                            (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3,
+                            &params,
+                            sizeof(params)));
+
+                    notify->setString("mime", MEDIA_MIMETYPE_AUDIO_AC3);
+                    notify->setInt32("channel-count", params.nChannels);
+                    notify->setInt32("sample-rate", params.nSampleRate);
+                    break;
+                }
+
                 default:
                     TRESPASS();
             }
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index b5d4e44..340cba7 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -42,6 +42,7 @@
 const char *MEDIA_MIMETYPE_AUDIO_FLAC = "audio/flac";
 const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS = "audio/aac-adts";
 const char *MEDIA_MIMETYPE_AUDIO_MSGSM = "audio/gsm";
+const char *MEDIA_MIMETYPE_AUDIO_AC3 = "audio/ac3";
 
 const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4";
 const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/x-wav";
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 450fb3b..96c5a32 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -42,7 +42,9 @@
 #include <utils/Vector.h>
 
 #include <OMX_Audio.h>
+#include <OMX_AudioExt.h>
 #include <OMX_Component.h>
+#include <OMX_IndexExt.h>
 
 #include "include/avc_utils.h"
 
@@ -94,7 +96,6 @@
 
 #define CODEC_LOGI(x, ...) ALOGI("[%s] "x, mComponentName, ##__VA_ARGS__)
 #define CODEC_LOGV(x, ...) ALOGV("[%s] "x, mComponentName, ##__VA_ARGS__)
-#define CODEC_LOGW(x, ...) ALOGW("[%s] "x, mComponentName, ##__VA_ARGS__)
 #define CODEC_LOGE(x, ...) ALOGE("[%s] "x, mComponentName, ##__VA_ARGS__)
 
 struct OMXCodecObserver : public BnOMXObserver {
@@ -531,6 +532,17 @@
                     sampleRate,
                     numChannels);
         }
+    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AC3, mMIME)) {
+        int32_t numChannels;
+        int32_t sampleRate;
+        CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+        CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+        status_t err = setAC3Format(numChannels, sampleRate);
+        if (err != OK) {
+            CODEC_LOGE("setAC3Format() failed (err = %d)", err);
+            return err;
+        }
     } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_ALAW, mMIME)
             || !strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_MLAW, mMIME)) {
         // These are PCM-like formats with a fixed sample rate but
@@ -1397,6 +1409,10 @@
             "audio_decoder.flac", "audio_encoder.flac" },
         { MEDIA_MIMETYPE_AUDIO_MSGSM,
             "audio_decoder.gsm", "audio_encoder.gsm" },
+        { MEDIA_MIMETYPE_VIDEO_MPEG2,
+            "video_decoder.mpeg2", "video_encoder.mpeg2" },
+        { MEDIA_MIMETYPE_AUDIO_AC3,
+            "audio_decoder.ac3", "audio_encoder.ac3" },
     };
 
     static const size_t kNumMimeToRole =
@@ -1780,42 +1796,21 @@
                 strerror(-err), -err);
         return err;
     }
-    // FIXME: assume that surface is controlled by app (native window
-    // returns the number for the case when surface is not controlled by app)
-    // FIXME2: This means that minUndeqeueudBufs can be 1 larger than reported
-    // For now, try to allocate 1 more buffer, but don't fail if unsuccessful
 
-    // Use conservative allocation while also trying to reduce starvation
-    //
-    // 1. allocate at least nBufferCountMin + minUndequeuedBuffers - that is the
-    //    minimum needed for the consumer to be able to work
-    // 2. try to allocate two (2) additional buffers to reduce starvation from
-    //    the consumer
-    //    plus an extra buffer to account for incorrect minUndequeuedBufs
-    CODEC_LOGI("OMX-buffers: min=%u actual=%u undeq=%d+1",
-            def.nBufferCountMin, def.nBufferCountActual, minUndequeuedBufs);
-
-    for (OMX_U32 extraBuffers = 2 + 1; /* condition inside loop */; extraBuffers--) {
-        OMX_U32 newBufferCount =
-            def.nBufferCountMin + minUndequeuedBufs + extraBuffers;
+    // XXX: Is this the right logic to use?  It's not clear to me what the OMX
+    // buffer counts refer to - how do they account for the renderer holding on
+    // to buffers?
+    if (def.nBufferCountActual < def.nBufferCountMin + minUndequeuedBufs) {
+        OMX_U32 newBufferCount = def.nBufferCountMin + minUndequeuedBufs;
         def.nBufferCountActual = newBufferCount;
         err = mOMX->setParameter(
                 mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-
-        if (err == OK) {
-            minUndequeuedBufs += extraBuffers;
-            break;
-        }
-
-        CODEC_LOGW("setting nBufferCountActual to %lu failed: %d",
-                newBufferCount, err);
-        /* exit condition */
-        if (extraBuffers == 0) {
+        if (err != OK) {
+            CODEC_LOGE("setting nBufferCountActual to %lu failed: %d",
+                    newBufferCount, err);
             return err;
         }
     }
-    CODEC_LOGI("OMX-buffers: min=%u actual=%u undeq=%d+1",
-            def.nBufferCountMin, def.nBufferCountActual, minUndequeuedBufs);
 
     err = native_window_set_buffer_count(
             mNativeWindow.get(), def.nBufferCountActual);
@@ -3513,6 +3508,31 @@
     return OK;
 }
 
+status_t OMXCodec::setAC3Format(int32_t numChannels, int32_t sampleRate) {
+    OMX_AUDIO_PARAM_ANDROID_AC3TYPE def;
+    InitOMXParams(&def);
+    def.nPortIndex = kPortIndexInput;
+
+    status_t err = mOMX->getParameter(
+            mNode,
+            (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3,
+            &def,
+            sizeof(def));
+
+    if (err != OK) {
+        return err;
+    }
+
+    def.nChannels = numChannels;
+    def.nSampleRate = sampleRate;
+
+    return mOMX->setParameter(
+            mNode,
+            (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3,
+            &def,
+            sizeof(def));
+}
+
 void OMXCodec::setG711Format(int32_t numChannels) {
     CHECK(!mIsEncoder);
     setRawAudioFormat(kPortIndexInput, 8000, numChannels);
@@ -4446,6 +4466,17 @@
                 mOutputFormat->setInt32(kKeyChannelCount, numChannels);
                 mOutputFormat->setInt32(kKeySampleRate, sampleRate);
                 mOutputFormat->setInt32(kKeyBitRate, bitRate);
+            } else if (audio_def->eEncoding ==
+                    (OMX_AUDIO_CODINGTYPE)OMX_AUDIO_CodingAndroidAC3) {
+                mOutputFormat->setCString(
+                        kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3);
+                int32_t numChannels, sampleRate, bitRate;
+                inputFormat->findInt32(kKeyChannelCount, &numChannels);
+                inputFormat->findInt32(kKeySampleRate, &sampleRate);
+                inputFormat->findInt32(kKeyBitRate, &bitRate);
+                mOutputFormat->setInt32(kKeyChannelCount, numChannels);
+                mOutputFormat->setInt32(kKeySampleRate, sampleRate);
+                mOutputFormat->setInt32(kKeyBitRate, bitRate);
             } else {
                 CHECK(!"Should not be here. Unknown audio encoding.");
             }
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 2c8cf8d..d1afd8b 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -508,6 +508,11 @@
                     ElementaryStreamQueue::PCM_AUDIO);
             break;
 
+        case STREAMTYPE_AC3:
+            mQueue = new ElementaryStreamQueue(
+                    ElementaryStreamQueue::AC3);
+            break;
+
         default:
             break;
     }
@@ -616,6 +621,7 @@
         case STREAMTYPE_MPEG2_AUDIO:
         case STREAMTYPE_MPEG2_AUDIO_ADTS:
         case STREAMTYPE_PCM_AUDIO:
+        case STREAMTYPE_AC3:
             return true;
 
         default:
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 8a80069..86b025f 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -89,6 +89,10 @@
         STREAMTYPE_MPEG2_AUDIO_ADTS     = 0x0f,
         STREAMTYPE_MPEG4_VIDEO          = 0x10,
         STREAMTYPE_H264                 = 0x1b,
+
+        // From ATSC A/53 Part 3:2009, 6.7.1
+        STREAMTYPE_AC3                  = 0x81,
+
         STREAMTYPE_PCM_AUDIO            = 0x83,
     };
 
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 1960b27..e9252cc 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -57,6 +57,122 @@
     }
 }
 
+// Parse AC3 header assuming the current ptr is start position of syncframe,
+// update metadata only applicable, and return the payload size
+static unsigned parseAC3SyncFrame(
+        const uint8_t *ptr, size_t size, sp<MetaData> *metaData) {
+    static const unsigned channelCountTable[] = {2, 1, 2, 3, 3, 4, 4, 5};
+    static const unsigned samplingRateTable[] = {48000, 44100, 32000};
+    static const unsigned rates[] = {32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256,
+            320, 384, 448, 512, 576, 640};
+
+    static const unsigned frameSizeTable[19][3] = {
+        { 64, 69, 96 },
+        { 80, 87, 120 },
+        { 96, 104, 144 },
+        { 112, 121, 168 },
+        { 128, 139, 192 },
+        { 160, 174, 240 },
+        { 192, 208, 288 },
+        { 224, 243, 336 },
+        { 256, 278, 384 },
+        { 320, 348, 480 },
+        { 384, 417, 576 },
+        { 448, 487, 672 },
+        { 512, 557, 768 },
+        { 640, 696, 960 },
+        { 768, 835, 1152 },
+        { 896, 975, 1344 },
+        { 1024, 1114, 1536 },
+        { 1152, 1253, 1728 },
+        { 1280, 1393, 1920 },
+    };
+
+    ABitReader bits(ptr, size);
+    unsigned syncStartPos = 0;  // in bytes
+    if (bits.numBitsLeft() < 16) {
+        return 0;
+    }
+    if (bits.getBits(16) != 0x0B77) {
+        return 0;
+    }
+
+    if (bits.numBitsLeft() < 16 + 2 + 6 + 5 + 3 + 3) {
+        ALOGV("Not enough bits left for further parsing");
+        return 0;
+    }
+    bits.skipBits(16);  // crc1
+
+    unsigned fscod = bits.getBits(2);
+    if (fscod == 3) {
+        ALOGW("Incorrect fscod in AC3 header");
+        return 0;
+    }
+
+    unsigned frmsizecod = bits.getBits(6);
+    if (frmsizecod > 37) {
+        ALOGW("Incorrect frmsizecod in AC3 header");
+        return 0;
+    }
+
+    unsigned bsid = bits.getBits(5);
+    if (bsid > 8) {
+        ALOGW("Incorrect bsid in AC3 header. Possibly E-AC-3?");
+        return 0;
+    }
+
+    unsigned bsmod = bits.getBits(3);
+    unsigned acmod = bits.getBits(3);
+    unsigned cmixlev = 0;
+    unsigned surmixlev = 0;
+    unsigned dsurmod = 0;
+
+    if ((acmod & 1) > 0 && acmod != 1) {
+        if (bits.numBitsLeft() < 2) {
+            return 0;
+        }
+        cmixlev = bits.getBits(2);
+    }
+    if ((acmod & 4) > 0) {
+        if (bits.numBitsLeft() < 2) {
+            return 0;
+        }
+        surmixlev = bits.getBits(2);
+    }
+    if (acmod == 2) {
+        if (bits.numBitsLeft() < 2) {
+            return 0;
+        }
+        dsurmod = bits.getBits(2);
+    }
+
+    if (bits.numBitsLeft() < 1) {
+        return 0;
+    }
+    unsigned lfeon = bits.getBits(1);
+
+    unsigned samplingRate = samplingRateTable[fscod];
+    unsigned payloadSize = frameSizeTable[frmsizecod >> 1][fscod];
+    if (fscod == 1) {
+        payloadSize += frmsizecod & 1;
+    }
+    payloadSize <<= 1;  // convert from 16-bit words to bytes
+
+    unsigned channelCount = channelCountTable[acmod] + lfeon;
+
+    if (metaData != NULL) {
+        (*metaData)->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3);
+        (*metaData)->setInt32(kKeyChannelCount, channelCount);
+        (*metaData)->setInt32(kKeySampleRate, samplingRate);
+    }
+
+    return payloadSize;
+}
+
+static bool IsSeeminglyValidAC3Header(const uint8_t *ptr, size_t size) {
+    return parseAC3SyncFrame(ptr, size, NULL) > 0;
+}
+
 static bool IsSeeminglyValidADTSHeader(const uint8_t *ptr, size_t size) {
     if (size < 3) {
         // Not enough data to verify header.
@@ -225,6 +341,33 @@
                 break;
             }
 
+            case AC3:
+            {
+                uint8_t *ptr = (uint8_t *)data;
+
+                ssize_t startOffset = -1;
+                for (size_t i = 0; i < size; ++i) {
+                    if (IsSeeminglyValidAC3Header(&ptr[i], size - i)) {
+                        startOffset = i;
+                        break;
+                    }
+                }
+
+                if (startOffset < 0) {
+                    return ERROR_MALFORMED;
+                }
+
+                if (startOffset > 0) {
+                    ALOGI("found something resembling an AC3 syncword at "
+                          "offset %d",
+                          startOffset);
+                }
+
+                data = &ptr[startOffset];
+                size -= startOffset;
+                break;
+            }
+
             case MPEG_AUDIO:
             {
                 uint8_t *ptr = (uint8_t *)data;
@@ -329,6 +472,8 @@
             return dequeueAccessUnitH264();
         case AAC:
             return dequeueAccessUnitAAC();
+        case AC3:
+            return dequeueAccessUnitAC3();
         case MPEG_VIDEO:
             return dequeueAccessUnitMPEGVideo();
         case MPEG4_VIDEO:
@@ -341,6 +486,51 @@
     }
 }
 
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAC3() {
+    unsigned syncStartPos = 0;  // in bytes
+    unsigned payloadSize = 0;
+    sp<MetaData> format = new MetaData;
+    while (true) {
+        if (syncStartPos + 2 >= mBuffer->size()) {
+            return NULL;
+        }
+
+        payloadSize = parseAC3SyncFrame(
+                mBuffer->data() + syncStartPos,
+                mBuffer->size() - syncStartPos,
+                &format);
+        if (payloadSize > 0) {
+            break;
+        }
+        ++syncStartPos;
+    }
+
+    if (mBuffer->size() < syncStartPos + payloadSize) {
+        ALOGV("Not enough buffer size for AC3");
+        return NULL;
+    }
+
+    if (mFormat == NULL) {
+        mFormat = format;
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(syncStartPos + payloadSize);
+    memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize);
+
+    int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize);
+    CHECK_GE(timeUs, 0ll);
+    accessUnit->meta()->setInt64("timeUs", timeUs);
+
+    memmove(
+            mBuffer->data(),
+            mBuffer->data() + syncStartPos + payloadSize,
+            mBuffer->size() - syncStartPos - payloadSize);
+
+    mBuffer->setRange(0, mBuffer->size() - syncStartPos - payloadSize);
+
+    return accessUnit;
+}
+
 sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitPCMAudio() {
     if (mBuffer->size() < 4) {
         return NULL;
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 66a8087..a2cca77 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -32,6 +32,7 @@
     enum Mode {
         H264,
         AAC,
+        AC3,
         MPEG_AUDIO,
         MPEG_VIDEO,
         MPEG4_VIDEO,
@@ -67,6 +68,7 @@
 
     sp<ABuffer> dequeueAccessUnitH264();
     sp<ABuffer> dequeueAccessUnitAAC();
+    sp<ABuffer> dequeueAccessUnitAC3();
     sp<ABuffer> dequeueAccessUnitMPEGAudio();
     sp<ABuffer> dequeueAccessUnitMPEGVideo();
     sp<ABuffer> dequeueAccessUnitMPEG4Video();