Support RGBA input buffers.

This change adds separate EGL shader for RGBA->YUV conversion
and modifies the JPEG compression to render input texture
into temporary framebuffer (doing the compression if necessary).

Bug: 301023410
Test: atest virtual_camera_tests
Test: atest VirtualCameraTest

Change-Id: Id3bd19d4c364691e2b1554fcf78d5f9940754314
diff --git a/services/camera/virtualcamera/util/EglFramebuffer.cc b/services/camera/virtualcamera/util/EglFramebuffer.cc
index acf0122..57b94d3 100644
--- a/services/camera/virtualcamera/util/EglFramebuffer.cc
+++ b/services/camera/virtualcamera/util/EglFramebuffer.cc
@@ -112,6 +112,10 @@
   return mHeight;
 }
 
+std::shared_ptr<AHardwareBuffer> EglFrameBuffer::getHardwareBuffer() {
+  return mHardwareBuffer;
+}
+
 }  // namespace virtualcamera
 }  // namespace companion
 }  // namespace android
diff --git a/services/camera/virtualcamera/util/EglFramebuffer.h b/services/camera/virtualcamera/util/EglFramebuffer.h
index 35f85e2..b39df6d 100644
--- a/services/camera/virtualcamera/util/EglFramebuffer.h
+++ b/services/camera/virtualcamera/util/EglFramebuffer.h
@@ -50,6 +50,9 @@
   // Return height of framebuffer (in pixels).
   int getHeight() const;
 
+  // Return underlying hardware buffer.
+  std::shared_ptr<AHardwareBuffer> getHardwareBuffer();
+
  private:
   // Keeping shared_ptr to hardware buffer instance here prevents it from being
   // freed while tied to EGL framebufer / EGL texture.
diff --git a/services/camera/virtualcamera/util/EglProgram.cc b/services/camera/virtualcamera/util/EglProgram.cc
index c468d39..510fd33 100644
--- a/services/camera/virtualcamera/util/EglProgram.cc
+++ b/services/camera/virtualcamera/util/EglProgram.cc
@@ -76,17 +76,29 @@
     vTextureCoord = aTextureCoord;
   })";
 
-constexpr char kExternalTextureFragmentShader[] = R"(#version 300 es
+constexpr char kExternalYuvTextureFragmentShader[] = R"(#version 300 es
     #extension GL_OES_EGL_image_external_essl3 : require
     #extension GL_EXT_YUV_target : require
     precision mediump float;
     in vec2 vTextureCoord;
-    out vec4 fragColor;
+    layout (yuv) out vec4 fragColor;
     uniform __samplerExternal2DY2YEXT uTexture;
     void main() {
       fragColor = texture(uTexture, vTextureCoord);
     })";
 
+constexpr char kExternalRgbaTextureFragmentShader[] = R"(#version 300 es
+    #extension GL_OES_EGL_image_external : require
+    #extension GL_EXT_YUV_target : require
+    precision mediump float;
+    in vec2 vTextureCoord;
+    layout (yuv) out vec4 fragColor;
+    uniform samplerExternalOES uTexture;
+    void main() {
+      vec4 rgbaColor = texture(uTexture, vTextureCoord);
+      fragColor = vec4(rgb_2_yuv(rgbaColor.xyz, itu_601_full_range), 0.0);
+    })";
+
 constexpr int kCoordsPerVertex = 3;
 constexpr std::array<float, 12> kSquareCoords{-1.f, 1.0f, 0.0f,  // top left
                                               -1.f, -1.f, 0.0f,  // bottom left
@@ -237,7 +249,7 @@
   return true;
 }
 
-EglTextureProgram::EglTextureProgram() {
+EglTextureProgram::EglTextureProgram(const TextureFormat textureFormat) {
   if (!isGlExtensionSupported(kGlExtYuvTarget)) {
     ALOGE(
         "Cannot initialize external texture program due to missing "
@@ -245,7 +257,10 @@
     return;
   }
 
-  if (initialize(kExternalTextureVertexShader, kExternalTextureFragmentShader)) {
+  const char* fragmentShaderSrc = textureFormat == TextureFormat::YUV
+                                      ? kExternalYuvTextureFragmentShader
+                                      : kExternalRgbaTextureFragmentShader;
+  if (initialize(kExternalTextureVertexShader, fragmentShaderSrc)) {
     ALOGV("Successfully initialized EGL shaders for external texture program.");
   } else {
     ALOGE("External texture EGL shader program initialization failed.");
diff --git a/services/camera/virtualcamera/util/EglProgram.h b/services/camera/virtualcamera/util/EglProgram.h
index 8e394e7..1b5f2cd 100644
--- a/services/camera/virtualcamera/util/EglProgram.h
+++ b/services/camera/virtualcamera/util/EglProgram.h
@@ -55,7 +55,9 @@
 // TODO(b/301023410) Add support for translation / cropping.
 class EglTextureProgram : public EglProgram {
  public:
-  EglTextureProgram();
+  enum class TextureFormat { RGBA, YUV };
+
+  EglTextureProgram(TextureFormat textureFormat = TextureFormat::YUV);
 
   bool draw(GLuint textureId);
 };
diff --git a/services/camera/virtualcamera/util/Util.cc b/services/camera/virtualcamera/util/Util.cc
index 11dc3a6..df771b1 100644
--- a/services/camera/virtualcamera/util/Util.cc
+++ b/services/camera/virtualcamera/util/Util.cc
@@ -18,20 +18,26 @@
 
 #include <unistd.h>
 
+#include <algorithm>
+#include <array>
+
 #include "jpeglib.h"
 
 namespace android {
 namespace companion {
 namespace virtualcamera {
 
+using ::aidl::android::companion::virtualcamera::Format;
+using ::aidl::android::hardware::common::NativeHandle;
+
 // Lower bound for maximal supported texture size is at least 2048x2048
 // but on most platforms will be more.
 // TODO(b/301023410) - Query actual max texture size.
 constexpr int kMaxTextureSize = 2048;
 constexpr int kLibJpegDctSize = DCTSIZE;
 
-using ::aidl::android::companion::virtualcamera::Format;
-using ::aidl::android::hardware::common::NativeHandle;
+constexpr std::array<Format, 2> kSupportedFormats{Format::YUV_420_888,
+                                                  Format::RGBA_8888};
 
 sp<Fence> importFence(const NativeHandle& aidlHandle) {
   if (aidlHandle.fds.size() != 1) {
@@ -41,11 +47,15 @@
   return sp<Fence>::make(::dup(aidlHandle.fds[0].get()));
 }
 
+bool isPixelFormatSupportedForInput(const Format format) {
+  return std::find(kSupportedFormats.begin(), kSupportedFormats.end(),
+                   format) != kSupportedFormats.end();
+}
+
 // Returns true if specified format is supported for virtual camera input.
 bool isFormatSupportedForInput(const int width, const int height,
                                const Format format) {
-  if (format != Format::YUV_420_888) {
-    // For now only YUV_420_888 is supported for input.
+  if (!isPixelFormatSupportedForInput(format)) {
     return false;
   }
 
diff --git a/services/camera/virtualcamera/util/Util.h b/services/camera/virtualcamera/util/Util.h
index b778321..a73c99b 100644
--- a/services/camera/virtualcamera/util/Util.h
+++ b/services/camera/virtualcamera/util/Util.h
@@ -43,6 +43,10 @@
 sp<Fence> importFence(
     const ::aidl::android::hardware::common::NativeHandle& handle);
 
+// Returns true if specified pixel format is supported for virtual camera input.
+bool isPixelFormatSupportedForInput(
+    ::aidl::android::companion::virtualcamera::Format format);
+
 // Returns true if specified format is supported for virtual camera input.
 bool isFormatSupportedForInput(
     int width, int height,