1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.vdmdemo.host; 18 19 import android.annotation.SuppressLint; 20 import android.companion.virtual.VirtualDeviceManager; 21 import android.companion.virtual.camera.VirtualCamera; 22 import android.companion.virtual.camera.VirtualCameraCallback; 23 import android.companion.virtual.camera.VirtualCameraConfig; 24 import android.graphics.ImageFormat; 25 import android.util.ArrayMap; 26 import android.util.Log; 27 import android.view.Surface; 28 29 import androidx.annotation.GuardedBy; 30 import androidx.annotation.NonNull; 31 32 import com.example.android.vdmdemo.common.RemoteEventProto; 33 import com.example.android.vdmdemo.common.RemoteIo; 34 import com.example.android.vdmdemo.common.VideoManager; 35 import com.google.common.util.concurrent.MoreExecutors; 36 37 import java.util.List; 38 import java.util.Objects; 39 40 @SuppressLint("NewApi") 41 final class RemoteCameraManager implements AutoCloseable { 42 private static final String TAG = RemoteCameraManager.class.getSimpleName(); 43 44 private final RemoteIo mRemoteIo; 45 private final VirtualDeviceManager.VirtualDevice mVirtualDevice; 46 private final ArrayMap<String, VirtualCamera> mVirtualCameras = new ArrayMap<>(); 47 48 RemoteCameraManager(@onNull VirtualDeviceManager.VirtualDevice virtualDevice, @NonNull RemoteIo remoteIo)49 RemoteCameraManager(@NonNull VirtualDeviceManager.VirtualDevice virtualDevice, 50 @NonNull RemoteIo remoteIo) { 51 mVirtualDevice = Objects.requireNonNull(virtualDevice); 52 mRemoteIo = Objects.requireNonNull(remoteIo); 53 } 54 createCameras(List<RemoteEventProto.CameraCapabilities> cameraCapabilities)55 void createCameras(List<RemoteEventProto.CameraCapabilities> cameraCapabilities) { 56 for (RemoteEventProto.CameraCapabilities capabilities : cameraCapabilities) { 57 VirtualCameraConfig config = new VirtualCameraConfig.Builder( 58 "Remote camera " + capabilities.getCameraId()) 59 .addStreamConfig(capabilities.getWidth(), capabilities.getHeight(), 60 ImageFormat.YUV_420_888, capabilities.getFps()) 61 .setVirtualCameraCallback(MoreExecutors.directExecutor(), 62 new RemoteCamera(capabilities.getCameraId())) 63 .setLensFacing(capabilities.getLensFacing()) 64 .setSensorOrientation(capabilities.getSensorOrientation()).build(); 65 VirtualCamera camera = mVirtualDevice.createVirtualCamera(config); 66 mVirtualCameras.put(capabilities.getCameraId(), camera); 67 68 Log.d(TAG, "Created virtual camera " + capabilities.getCameraId() 69 + "(" + camera.getConfig().getName() + ")"); 70 } 71 } 72 73 @Override close()74 public void close() { 75 for (int i = 0; i < mVirtualCameras.size(); ++i) { 76 mVirtualCameras.valueAt(i).close(); 77 } 78 } 79 80 81 private class RemoteCamera implements VirtualCameraCallback { 82 private final String mRemoteCameraId; 83 84 private final Object mLock = new Object(); 85 86 @GuardedBy("mLock") 87 private VideoManager mVideoManager; 88 RemoteCamera(String remoteCameraId)89 RemoteCamera(String remoteCameraId) { 90 mRemoteCameraId = remoteCameraId; 91 } 92 93 94 @Override onStreamConfigured(int streamId, @NonNull Surface surface, int width, int height, int format)95 public void onStreamConfigured(int streamId, @NonNull Surface surface, int width, 96 int height, int format) { 97 Log.d(TAG, "onStreamConfigured " + width + " x " + height); 98 synchronized (mLock) { 99 if (mVideoManager != null) { 100 mVideoManager.stop(); 101 } 102 mVideoManager = VideoManager.createCameraDecoder(mRemoteCameraId, mRemoteIo); 103 mVideoManager.startDecoding(surface, width, height); 104 } 105 106 mRemoteIo.sendMessage(RemoteEventProto.RemoteEvent.newBuilder() 107 .setStartCameraStream( 108 RemoteEventProto.StartCameraStream.newBuilder().setCameraId( 109 mRemoteCameraId)) 110 .build()); 111 } 112 113 @Override onStreamClosed(int streamId)114 public void onStreamClosed(int streamId) { 115 Log.d(TAG, "onStreamClosed " + streamId); 116 117 synchronized (mLock) { 118 if (mVideoManager != null) { 119 mVideoManager.stop(); 120 mVideoManager = null; 121 } 122 } 123 124 mRemoteIo.sendMessage(RemoteEventProto.RemoteEvent.newBuilder() 125 .setStopCameraStream(RemoteEventProto.StopCameraStream.newBuilder().setCameraId( 126 mRemoteCameraId)) 127 .build()); 128 } 129 } 130 } 131