From aa35aeaa66046c4885fd2e1f771fa0dbc7f3903c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20Kalliom=C3=A4ki?= Date: Wed, 30 May 2018 09:24:19 +0200 Subject: [PATCH] Write to file on a dedicated thread in VideoFileRenderer. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Commit-Queue: Sami Kalliomäki Cr-Commit-Position: refs/heads/master@{#23483} --- .../api/org/webrtc/VideoFileRenderer.java | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/sdk/android/api/org/webrtc/VideoFileRenderer.java b/sdk/android/api/org/webrtc/VideoFileRenderer.java index 92cdeb4513..f88687150d 100644 --- a/sdk/android/api/org/webrtc/VideoFileRenderer.java +++ b/sdk/android/api/org/webrtc/VideoFileRenderer.java @@ -29,6 +29,8 @@ public class VideoFileRenderer implements VideoSink { 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 @@ public class VideoFileRenderer implements VideoSink { ("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 @@ public class VideoFileRenderer implements VideoSink { 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 @@ public class VideoFileRenderer implements VideoSink { 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); } } }