Skip to content

QDA:スクショ&録画&ミラーリング

114.jpg

MENUに画面キャプチャの様子が出ているのでその要領で

スクリーンショット:

DisplayCaptureManagerが持っているTexture2D screenTextureをそのまま使用する。

csharp
using System;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
using Anaglyph.DisplayCapture;

public class ScreenshotToSrc : MonoBehaviour
{
    [SerializeField] private DisplayCaptureManager captureManager; 
    [SerializeField] private RawImage src1;

    private void Update()
    {
        // OVRInput.GetDown(OVRInput.RawButton.A) でトリガー
        if (OVRInput.GetDown(OVRInput.RawButton.A))
        {
            SaveAndDisplayScreenshot();
        }
    }

    private void SaveAndDisplayScreenshot()
    {
        // 1. DisplayCaptureManager から最新フレームのTexture2Dを取得
        var tex = captureManager.ScreenCaptureTexture;
        if (tex == null)
        {
            Debug.LogWarning("[ScreenshotToSrc] No screen texture is available yet!");
            return;
        }

        // 2. Texture2D を PNG にエンコード
        byte[] pngData = tex.EncodeToPNG();

        // 3. Assets/SRC ではなく、Application.persistentDataPath/SRC に保存
        //    (Questなどの実機でも書き込み可能なパス)
        string directoryPath = Path.Combine(Application.persistentDataPath, "SRC");
        if (!Directory.Exists(directoryPath))
        {
            Directory.CreateDirectory(directoryPath);
        }

        // ファイル名 (時刻入り)
        string fileName = $"Capture_{DateTime.Now:yyyyMMdd_HHmmss}.png";
        string fullPath = Path.Combine(directoryPath, fileName);

        File.WriteAllBytes(fullPath, pngData);
        Debug.Log($"[ScreenshotToSrc] Screenshot saved to: {fullPath}");

        // 4. 撮りたての画像を RawImage (src1) に反映させる
        //    ここでは元テクスチャをコピーしてセットする例を示します
        Texture2D lastScreenshot = new Texture2D(tex.width, tex.height);
        // 互換性のため、引数無しコンストラクタにしています
        lastScreenshot.SetPixels(tex.GetPixels());
        lastScreenshot.Apply();

        src1.texture = lastScreenshot; 
        Debug.Log("[ScreenshotToSrc] Updated RawImage with the latest screenshot.");
    }
}

上記のコードで実装を行うと上下反対のスクショが出てきたので調整

Capture_20250127_1456341.png
csharp
using System;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
using Anaglyph.DisplayCapture;

public class ScreenshotToSrc: MonoBehaviour
{
    [SerializeField] private DisplayCaptureManager captureManager;
    [SerializeField] private RawImage src1;

    private void Update()
    {
        // Aボタンを押したらスクリーンショットを保存 & 表示
        if (OVRInput.GetDown(OVRInput.RawButton.A))
        {
            SaveAndDisplayScreenshot();
        }
    }

    private void SaveAndDisplayScreenshot()
    {
        // 1. DisplayCaptureManager が持つ最新フレームの Texture2D を取得
        Texture2D originalTex = captureManager.ScreenCaptureTexture;
        if (originalTex == null)
        {
            Debug.LogWarning("[ScreenshotToSrc] ScreenCaptureTexture is null.");
            return;
        }

        // 2. 上下反転用の新しい Texture2D を作成
        Texture2D flippedTexture = new Texture2D(originalTex.width, originalTex.height, TextureFormat.RGBA32, false);

        // 3. ピクセルを取得・上下反転
        Color[] originalPixels = originalTex.GetPixels();
        Color[] flippedPixels = new Color[originalPixels.Length];
        int width = originalTex.width;
        int height = originalTex.height;

        for (int y = 0; y < height; y++)
        {
            int flippedY = (height - 1) - y;
            for (int x = 0; x < width; x++)
            {
                flippedPixels[flippedY * width + x] = originalPixels[y * width + x];
            }
        }

        // 4. flippedPixels を flippedTexture にセット
        flippedTexture.SetPixels(flippedPixels);
        flippedTexture.Apply();

        // 5. PNGとして保存 (Application.persistentDataPath/SRC)
        byte[] pngData = flippedTexture.EncodeToPNG();
        string dirPath = Path.Combine(Application.persistentDataPath, "Screenshots");
        if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);

        string fileName = $"Capture_{DateTime.Now:yyyyMMdd_HHmmss}.png";
        string fullPath = Path.Combine(dirPath, fileName);

        File.WriteAllBytes(fullPath, pngData);
        Debug.Log($"[ScreenshotToSrc] Screenshot saved to: {fullPath}");

        // 6. 反転後のテクスチャを RawImage に表示
        src1.texture = flippedTexture;
    }
}
Capture_20250127_1547461.pngCapture_20250127_1547491.png

左下に直近の1枚をプレビューとして表示。

スクショはApplication.persistentDataPathに保存しており、パソコンにつないだ状態では、

PC\Quest 3\内部共有ストレージ\Android\data\ph.anagly.mediaprojectiondemo\files\Screenshots

から容易にアクセス可能

録画:

録画機能を付けるには、Javaなどからいじる必要あり

Javaは授業で見たくらいで実用的な知識がないためChatGPT任せになりそう

DisplayCaptureManager.java

csharp
package com.trev3d.DisplayCapture;

import static android.content.ContentValues.TAG;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.Image;
import android.media.ImageReader;
import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.Surface;

import androidx.annotation.NonNull;

import com.unity3d.player.UnityPlayer;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;

public class DisplayCaptureManager implements ImageReader.OnImageAvailableListener {
    public static DisplayCaptureManager instance = null;
    public ArrayList<IDisplayCaptureReceiver> receivers;
    private ImageReader reader;
    private MediaProjection projection;
    private VirtualDisplay virtualDisplay;

    private Intent notifServiceIntent;
    private ByteBuffer byteBuffer;
    private int width;
    private int height;

    private UnityInterface unityInterface;
    private MediaRecorder mediaRecorder;            
    private VirtualDisplay recordingVirtualDisplay; 
    private boolean isRecording = false;            

    private static class UnityInterface {
        private final String gameObjectName;

        private UnityInterface(String gameObjectName) {
            this.gameObjectName = gameObjectName;
        }

        private void Call(String functionName) {
            UnityPlayer.UnitySendMessage(gameObjectName, functionName, "");
        }

        public void OnCaptureStarted() {
            Call("OnCaptureStarted");
        }

        public void OnPermissionDenied() {
            Call("OnPermissionDenied");
        }

        public void OnCaptureStopped() {
            Call("OnCaptureStopped");
        }

        public void OnNewFrameAvailable() {
            Call("OnNewFrameAvailable");
        }

        public void OnRecordingStarted() {
            Call("OnRecordingStarted");
        }
        public void OnRecordingStopped() {
            Call("OnRecordingStopped");
        }
    }

    public DisplayCaptureManager() {
        receivers = new ArrayList<IDisplayCaptureReceiver>();
    }

    public static synchronized DisplayCaptureManager getInstance() {
        if (instance == null)
            instance = new DisplayCaptureManager();
        return instance;
    }

    @Override
    public void onImageAvailable(@NonNull ImageReader imageReader) {
        Image image = imageReader.acquireLatestImage();
        if (image == null) return;

        ByteBuffer buffer = image.getPlanes()[0].getBuffer();
        buffer.rewind();
        byteBuffer.clear();
        byteBuffer.put(buffer);

        long timestamp = image.getTimestamp();
        image.close();

        for(int i = 0; i < receivers.size(); i++) {
            buffer.rewind();
            receivers.get(i).onNewImage(byteBuffer, width, height, timestamp);
        }
        unityInterface.OnNewFrameAvailable();
    }

    private void handleScreenCaptureEnd() {
        virtualDisplay.release();
        UnityPlayer.currentContext.stopService(notifServiceIntent);
        unityInterface.OnCaptureStopped();
    }

    public void setup(String gameObjectName, int width, int height) {
        unityInterface = new UnityInterface(gameObjectName);
        this.width = width;
        this.height = height;

        int bufferSize = width * height * 4;
        byteBuffer = ByteBuffer.allocateDirect(bufferSize);

        reader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 2);
        reader.setOnImageAvailableListener(this, new Handler(Looper.getMainLooper()));
    }

    public void requestCapture() {
        Log.i(TAG, "Asking for screen capture permission...");
        Intent intent = new Intent(
                UnityPlayer.currentActivity,
                DisplayCaptureRequestActivity.class);
        UnityPlayer.currentActivity.startActivity(intent);
    }

    public void onPermissionResponse(int resultCode, Intent intent) {
        if (resultCode != Activity.RESULT_OK) {
            unityInterface.OnPermissionDenied();
            Log.i(TAG, "Screen capture permission denied!");
            return;
        }
        notifServiceIntent = new Intent(
                UnityPlayer.currentContext,
                DisplayCaptureNotificationService.class);
        UnityPlayer.currentContext.startService(notifServiceIntent);

        new Handler(Looper.getMainLooper()).postDelayed(() -> {
            Log.i(TAG, "Starting screen capture...");

            MediaProjectionManager projectionManager = (MediaProjectionManager)
                    UnityPlayer.currentContext.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
            projection = projectionManager.getMediaProjection(resultCode, intent);

            projection.registerCallback(new MediaProjection.Callback() {
                @Override
                public void onStop() {
                    Log.i(TAG, "Screen capture ended!");
                    handleScreenCaptureEnd();
                }
            }, new Handler(Looper.getMainLooper()));

            virtualDisplay = projection.createVirtualDisplay(
                    "ScreenCapture",
                    width, height,
                    300,
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                    reader.getSurface(),
                    null, null
            );

            unityInterface.OnCaptureStarted();
        }, 100);

        Log.i(TAG, "Screen capture started!");
    }

    public void stopCapture() {
        Log.i(TAG, "Stopping screen capture...");
        if(projection == null) return;
        projection.stop();
    }

    public ByteBuffer getByteBuffer() {
        return byteBuffer;
    }

    public void startRecording(String outputPath) {
        if (isRecording) {
            Log.w(TAG, "Already recording!");
            return;
        }
        if (projection == null) {
            Log.e(TAG, "MediaProjection not active. requestCapture() first!");
            return;
        }
        try {
            mediaRecorder = new MediaRecorder();
            mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
            mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
            mediaRecorder.setOutputFile(outputPath);
            mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
            mediaRecorder.setVideoSize(1280, 720);
            mediaRecorder.setVideoFrameRate(30);
            mediaRecorder.setVideoEncodingBitRate(1500000);

            mediaRecorder.prepare();

            Surface recorderSurface = mediaRecorder.getSurface();

            recordingVirtualDisplay = projection.createVirtualDisplay(
                    "RecordingDisplay",
                    1280,
                    720,
                    300,
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                    recorderSurface,
                    null,
                    null
            );

            mediaRecorder.start();
            isRecording = true;

            Log.i(TAG, "startRecording() -> " + outputPath);
            unityInterface.OnRecordingStarted();

        } catch (IOException e) {
            Log.e(TAG, "startRecording failed: " + e.getMessage());
            e.printStackTrace();
        }
    }

    public void stopRecording() {
        if (!isRecording) {
            Log.i(TAG, "Not recording.");
            return;
        }
        isRecording = false;

        try {
            mediaRecorder.stop();
        } catch (Exception e) {
            e.printStackTrace();
        }
        mediaRecorder.reset();
        mediaRecorder.release();
        mediaRecorder = null;

        if (recordingVirtualDisplay != null) {
            recordingVirtualDisplay.release();
            recordingVirtualDisplay = null;
        }

        Log.i(TAG, "stopRecording()");
        unityInterface.OnRecordingStopped();
    }
}

CaptureVideoRecorder.cs

csharp
using UnityEngine;
using System;
using System.IO;
using UnityEngine.UI;
using UnityEngine.Video;

// DisplayCaptureManager がいるパッケージ:
// もし名前空間が "com.trev3d.DisplayCapture" ではなく「Anaglyph.DisplayCapture」などなら修正してください

public class CaptureVideoRecorder : MonoBehaviour
{
    private AndroidJavaClass androidClass;
    private AndroidJavaObject androidInstance;

    [SerializeField] private bool isRecording = false;

    [Header("Preview (Optional)")]
    [SerializeField] private RawImage preview;
    [SerializeField] private VideoPlayer videoPlayer;

    private string lastRecordedFilePath;

    private void Awake()
    {
        // Java側クラス "com.trev3d.DisplayCapture.DisplayCaptureManager"
        androidClass = new AndroidJavaClass("com.trev3d.DisplayCapture.DisplayCaptureManager");
        androidInstance = androidClass.CallStatic<AndroidJavaObject>("getInstance");
    }

    private void Update()
    {
        // Bボタンで録画開始/停止をトグルする
        if (OVRInput.GetDown(OVRInput.RawButton.B))
        {
            if (!isRecording)
            {
                StartRecording();
            }
            else
            {
                StopRecording();
            }
        }
    }

    private void StartRecording()
    {
        isRecording = true;
        // 出力先 (Questなどでも書き込み可能なディレクトリ)
        string dirPath = Path.Combine(Application.persistentDataPath, "Videos");
        if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);

        lastRecordedFilePath = Path.Combine(dirPath, $"Record_{DateTime.Now:yyyyMMdd_HHmmss}.mp4");

        // Java: startRecording(String outputPath)
        androidInstance.Call("startRecording", lastRecordedFilePath);
        Debug.Log("[MediaRecorderController] Start Recording -> " + lastRecordedFilePath);
    }

    private void StopRecording()
    {
        isRecording = false;

        // Java: stopRecording()
        androidInstance.Call("stopRecording");
        Debug.Log("[MediaRecorderController] Stop Recording -> " + lastRecordedFilePath);

        // ここで録画完了後の動画を再生してみる
        PlayLastRecordedVideo();
    }

    private void PlayLastRecordedVideo()
    {
        videoPlayer.source = VideoSource.Url;
        videoPlayer.url = lastRecordedFilePath;
        videoPlayer.renderMode = VideoRenderMode.APIOnly;
        videoPlayer.prepareCompleted += OnVideoPrepared;
        videoPlayer.Prepare();
    }

    private void OnVideoPrepared(VideoPlayer source)
    {
        if (preview != null)
            preview.texture = source.texture;
        source.Play();
        Debug.Log("[MediaRecorderController] Video playback started.");
    }
}
image.png

下の動画は作成した録画機能を使用して撮影したもの。録画を始めるとQRコード読み取りの機能が止まってしまう。

Record_20250128_133847.mp4

1.スクリーンショットのように2Dテクスチャが更新されるたびにデータを取得しそれらを動画にする方法

外部の機能を入れる必要があり。FFmpegというものがあるが、Androidベースの者には対応していないようで動作せず。

スクリーンショットをフォルダに入れておいてPCに接続の上FFmpegを動かすという形であれば動画化できる可能性あり。

2.VirtualDisplay2つ用意する方法

csharp
using UnityEngine;
using System;
using System.IO;
using UnityEngine.UI;
using UnityEngine.Video;

namespace Anaglyph.DisplayCapture
{
    public class MediaRecorderController : MonoBehaviour
    {
        private bool isRecording = false;
        private string lastRecordedFilePath;

        [Header("Preview UI (Optional)")]
        [SerializeField] private RawImage preview;
        [SerializeField] private VideoPlayer videoPlayer;

        private void Update()
        {
            // Bボタン押下で録画開始/停止
            if (OVRInput.GetDown(OVRInput.RawButton.B))
            {
                if (!isRecording)
                {
                    StartRecord();
                }
                else
                {
                    StopRecord();
                }
            }
        }

        private void StartRecord()
        {
            isRecording = true;

            string dir = Path.Combine(Application.persistentDataPath, "Videos");
            if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);

            // 現在時刻でファイル名
            lastRecordedFilePath = Path.Combine(dir, $"Record_{DateTime.Now:yyyyMMdd_HHmmss}.mp4");

            // DisplayCaptureManager の StartRecording
            DisplayCaptureManager.Instance.StartRecording(lastRecordedFilePath);

            Debug.Log("[MediaRecorderController] Start Recording -> " + lastRecordedFilePath);
        }

        private void StopRecord()
        {
            isRecording = false;

            DisplayCaptureManager.Instance.StopRecording();
            Debug.Log("[MediaRecorderController] Stop Recording -> " + lastRecordedFilePath);

            // オプション: すぐ再生
            PlayLastRecordedVideo();
        }

        private void PlayLastRecordedVideo()
        {
            if (!videoPlayer || !preview) return;

            videoPlayer.source = VideoSource.Url;
            videoPlayer.url = lastRecordedFilePath;
            videoPlayer.renderMode = VideoRenderMode.APIOnly;

            videoPlayer.prepareCompleted += OnPrepared;
            videoPlayer.Prepare();
        }

        private void OnPrepared(VideoPlayer source)
        {
            preview.texture = source.texture;
            source.Play();
        }
    }
}
csharp
package com.trev3d.DisplayCapture;

import static android.content.ContentValues.TAG;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.Image;
import android.media.ImageReader;
import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.Surface;

import androidx.annotation.NonNull;

import com.unity3d.player.UnityPlayer;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;

public class DisplayCaptureManager implements ImageReader.OnImageAvailableListener {
    public static DisplayCaptureManager instance = null;

    public ArrayList<IDisplayCaptureReceiver> receivers;

    private ImageReader reader;
    private VirtualDisplay virtualDisplay;

    private MediaRecorder mediaRecorder;
    private VirtualDisplay recordingVirtualDisplay;
    private boolean isRecording = false;

    private MediaProjection projection;
    private Intent notifServiceIntent;
    private ByteBuffer byteBuffer;
    private int width;
    private int height;

    private UnityInterface unityInterface;

    private static class UnityInterface {
        private final String gameObjectName;
        private UnityInterface(String gameObjectName) {
            this.gameObjectName = gameObjectName;
        }
        private void Call(String functionName) {
            UnityPlayer.UnitySendMessage(gameObjectName, functionName, "");
        }
        public void OnCaptureStarted()  { Call("OnCaptureStarted"); }
        public void OnPermissionDenied() { Call("OnPermissionDenied"); }
        public void OnCaptureStopped()  { Call("OnCaptureStopped"); }
        public void OnNewFrameAvailable() { Call("OnNewFrameAvailable"); }
        public void OnRecordingStarted()  { Call("OnRecordingStarted"); }
        public void OnRecordingStopped()  { Call("OnRecordingStopped"); }
    }

    public DisplayCaptureManager() {
        receivers = new ArrayList<>();
    }

    public static synchronized DisplayCaptureManager getInstance() {
        if (instance == null) {
            instance = new DisplayCaptureManager();
        }
        return instance;
    }

    public void onPermissionResponse(int resultCode, Intent intent) {
        if (resultCode != Activity.RESULT_OK) {
            unityInterface.OnPermissionDenied();
            Log.i(TAG, "Screen capture permission denied!");
            return;
        }
        notifServiceIntent = new Intent(UnityPlayer.currentContext, DisplayCaptureNotificationService.class);
        UnityPlayer.currentContext.startService(notifServiceIntent);

        new Handler(Looper.getMainLooper()).postDelayed(() -> {
            Log.i(TAG, "Starting screen capture...");

            MediaProjectionManager projectionManager =
                    (MediaProjectionManager) UnityPlayer.currentContext.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
            projection = projectionManager.getMediaProjection(resultCode, intent);

            projection.registerCallback(new MediaProjection.Callback() {
                @Override
                public void onStop() {
                    Log.i(TAG, "Screen capture ended!");
                    handleScreenCaptureEnd();
                }
            }, new Handler(Looper.getMainLooper()));

            virtualDisplay = projection.createVirtualDisplay(
                    "ScreenCapture",
                    width, height,
                    300,
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                    reader.getSurface(),
                    null, null
            );

            unityInterface.OnCaptureStarted();
            Log.i(TAG, "Screen capture started!");
        }, 100);
    }

    @Override
    public void onImageAvailable(@NonNull ImageReader imageReader) {
        Image image = imageReader.acquireLatestImage();
        if (image == null) return;

        ByteBuffer buffer = image.getPlanes()[0].getBuffer();
        buffer.rewind();
        byteBuffer.clear();
        byteBuffer.put(buffer);

        long timestamp = image.getTimestamp();
        image.close();

        for (IDisplayCaptureReceiver r : receivers) {
            buffer.rewind();
            r.onNewImage(byteBuffer, width, height, timestamp);
        }
        unityInterface.OnNewFrameAvailable();
    }

    private void handleScreenCaptureEnd() {
        if (virtualDisplay != null) {
            virtualDisplay.release();
            virtualDisplay = null;
        }
        if (notifServiceIntent != null) {
            UnityPlayer.currentContext.stopService(notifServiceIntent);
            notifServiceIntent = null;
        }
        projection = null;
        unityInterface.OnCaptureStopped();
    }

    public void setup(String gameObjectName, int width, int height) {
        unityInterface = new UnityInterface(gameObjectName);

        this.width = 600;
        this.height = 600;

        int bufferSize = this.width * this.height * 4;
        byteBuffer = ByteBuffer.allocateDirect(bufferSize);

        reader = ImageReader.newInstance(this.width, this.height, PixelFormat.RGBA_8888, 2);
        reader.setOnImageAvailableListener(this, new Handler(Looper.getMainLooper()));
    }

    public void requestCapture() {
        Log.i(TAG, "Asking for screen capture permission...");
        Intent intent = new Intent(UnityPlayer.currentActivity, DisplayCaptureRequestActivity.class);
        UnityPlayer.currentActivity.startActivity(intent);
    }

    public void stopCapture() {
        Log.i(TAG, "Stopping screen capture...");
        if (projection != null) {
            projection.stop();
        }
    }

    public ByteBuffer getByteBuffer() {
        return byteBuffer;
    }

    public void startRecording(String outputPath) {
        if (isRecording) {
            Log.w(TAG, "Already recording!");
            return;
        }
        if (projection == null) {
            Log.e(TAG, "MediaProjection not active. requestCapture() first!");
            return;
        }
        try {
            mediaRecorder = new MediaRecorder();
            mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
            mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
            mediaRecorder.setOutputFile(outputPath);

            mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
            mediaRecorder.setVideoSize(600, 600);

            mediaRecorder.setVideoFrameRate(15);
            mediaRecorder.setVideoEncodingBitRate(1000000);

            mediaRecorder.prepare();

            Surface recSurface = mediaRecorder.getSurface();

            recordingVirtualDisplay = projection.createVirtualDisplay(
                    "RecordingDisplay",
                    600, 600,
                    300,
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                    recSurface,
                    null, null
            );

            mediaRecorder.start();
            isRecording = true;
            Log.i(TAG, "startRecording -> " + outputPath);

            unityInterface.OnRecordingStarted();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void stopRecording() {
        if (!isRecording) {
            Log.i(TAG, "Not recording now.");
            return;
        }
        isRecording = false;
        try {
            mediaRecorder.stop();
        } catch(Exception e) {
            e.printStackTrace();
        }
        mediaRecorder.reset();
        mediaRecorder.release();
        mediaRecorder = null;

        if (recordingVirtualDisplay != null) {
            recordingVirtualDisplay.release();
            recordingVirtualDisplay = null;
        }

        Log.i(TAG, "stopRecording()");
        unityInterface.OnRecordingStopped();
    }
}

作成しエラー除去までできたもののうまく動作せず。スクリーンショットの機能も動かなくなってしまったり、アプリが連続して落ちてしまうなど….

性能的な問題やVirtualDisplayを2つ作ることができない可能性あり。


Author: 水上 | Source: 水上\QDA:スクショ&録画&ミラーリング 8faf2081cdbc459eb094bd4bcbc4d97a.md