Write to file on a dedicated thread in VideoFileRenderer.

The disk cannot always keep up to with the frames produced. To solve
this, write to disk on a dedicated thread so we don't block rendering.

Bug: b/80409365
Change-Id: If9ef3eb6948d81deebb987420599fef446b082d6
Reviewed-on: https://webrtc-review.googlesource.com/79800
Reviewed-by: Paulina Hensman <phensman@webrtc.org>
Commit-Queue: Sami Kalliomäki <sakal@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23483}
diff --git a/sdk/android/api/org/webrtc/VideoFileRenderer.java b/sdk/android/api/org/webrtc/VideoFileRenderer.java
index 92cdeb4..f886871 100644
--- a/sdk/android/api/org/webrtc/VideoFileRenderer.java
+++ b/sdk/android/api/org/webrtc/VideoFileRenderer.java
@@ -29,6 +29,8 @@
 
   private final HandlerThread renderThread;
   private final Handler renderThreadHandler;
+  private final HandlerThread fileThread;
+  private final Handler fileThreadHandler;
   private final FileOutputStream videoOutFile;
   private final String outputFileName;
   private final int outputFileWidth;
@@ -57,10 +59,14 @@
         ("YUV4MPEG2 C420 W" + outputFileWidth + " H" + outputFileHeight + " Ip F30:1 A1:1\n")
             .getBytes(Charset.forName("US-ASCII")));
 
-    renderThread = new HandlerThread(TAG);
+    renderThread = new HandlerThread(TAG + "RenderThread");
     renderThread.start();
     renderThreadHandler = new Handler(renderThread.getLooper());
 
+    fileThread = new HandlerThread(TAG + "FileThread");
+    fileThread.start();
+    fileThreadHandler = new Handler(fileThread.getLooper());
+
     ThreadUtils.invokeAtFrontUninterruptibly(renderThreadHandler, new Runnable() {
       @Override
       public void run() {
@@ -108,19 +114,21 @@
     final VideoFrame.I420Buffer i420 = scaledBuffer.toI420();
     scaledBuffer.release();
 
-    YuvHelper.I420Rotate(i420.getDataY(), i420.getStrideY(), i420.getDataU(), i420.getStrideU(),
-        i420.getDataV(), i420.getStrideV(), outputFrameBuffer, i420.getWidth(), i420.getHeight(),
-        frame.getRotation());
-    i420.release();
+    fileThreadHandler.post(() -> {
+      YuvHelper.I420Rotate(i420.getDataY(), i420.getStrideY(), i420.getDataU(), i420.getStrideU(),
+          i420.getDataV(), i420.getStrideV(), outputFrameBuffer, i420.getWidth(), i420.getHeight(),
+          frame.getRotation());
+      i420.release();
 
-    try {
-      videoOutFile.write("FRAME\n".getBytes(Charset.forName("US-ASCII")));
-      videoOutFile.write(
-          outputFrameBuffer.array(), outputFrameBuffer.arrayOffset(), outputFrameSize);
-    } catch (IOException e) {
-      throw new RuntimeException("Error writing video to disk", e);
-    }
-    frameCount++;
+      try {
+        videoOutFile.write("FRAME\n".getBytes(Charset.forName("US-ASCII")));
+        videoOutFile.write(
+            outputFrameBuffer.array(), outputFrameBuffer.arrayOffset(), outputFrameSize);
+      } catch (IOException e) {
+        throw new RuntimeException("Error writing video to disk", e);
+      }
+      frameCount++;
+    });
   }
 
   /**
@@ -135,14 +143,23 @@
       cleanupBarrier.countDown();
     });
     ThreadUtils.awaitUninterruptibly(cleanupBarrier);
+    fileThreadHandler.post(() -> {
+      try {
+        videoOutFile.close();
+        Logging.d(TAG,
+            "Video written to disk as " + outputFileName + ". The number of frames is " + frameCount
+                + " and the dimensions of the frames are " + outputFileWidth + "x"
+                + outputFileHeight + ".");
+      } catch (IOException e) {
+        throw new RuntimeException("Error closing output file", e);
+      }
+      fileThread.quit();
+    });
     try {
-      videoOutFile.close();
-      Logging.d(TAG,
-          "Video written to disk as " + outputFileName + ". The number of frames is " + frameCount
-              + " and the dimensions of the frames are " + outputFileWidth + "x" + outputFileHeight
-              + ".");
-    } catch (IOException e) {
-      throw new RuntimeException("Error closing output file", e);
+      fileThread.join();
+    } catch (InterruptedException e) {
+      Thread.currentThread().interrupt();
+      Logging.e(TAG, "Interrupted while waiting for the write to disk to complete.", e);
     }
   }
 }