Android Webrtc record video from the stream coming from the other peer











up vote
7
down vote

favorite












I am developing a webrtc video call Android app, it is working more than fine, I need to record the video of the other peer (remoteVideoStream) and myStream (localVideoStream) and convert it to some saveable format like mp4 or any other format, I really searched for that, but without being able to figure out how to do the job.



I have read about VideoFileRenderer, I tried to add it to my code to save the video but could not use it as well it has not any method called for example record() or save(), although it has a method called release() which will be used to end saving the video. Here is the class if any one has any idea:



@JNINamespace("webrtc::jni")
public class VideoFileRenderer implements Callbacks, VideoSink {
private static final String TAG = "VideoFileRenderer";
private final HandlerThread renderThread;
private final Handler renderThreadHandler;
private final FileOutputStream videoOutFile;
private final String outputFileName;
private final int outputFileWidth;
private final int outputFileHeight;
private final int outputFrameSize;
private final ByteBuffer outputFrameBuffer;
private EglBase eglBase;
private YuvConverter yuvConverter;
private ArrayList<ByteBuffer> rawFrames = new ArrayList();

public VideoFileRenderer(String outputFile, int outputFileWidth, int outputFileHeight, final Context sharedContext) throws IOException {
if (outputFileWidth % 2 != 1 && outputFileHeight % 2 != 1) {
this.outputFileName = outputFile;
this.outputFileWidth = outputFileWidth;
this.outputFileHeight = outputFileHeight;
this.outputFrameSize = outputFileWidth * outputFileHeight * 3 / 2;
this.outputFrameBuffer = ByteBuffer.allocateDirect(this.outputFrameSize);
this.videoOutFile = new FileOutputStream(outputFile);
this.videoOutFile.write(("YUV4MPEG2 C420 W" + outputFileWidth + " H" + outputFileHeight + " Ip F30:1 A1:1n").getBytes(Charset.forName("US-ASCII")));
this.renderThread = new HandlerThread("VideoFileRenderer");
this.renderThread.start();
this.renderThreadHandler = new Handler(this.renderThread.getLooper());
ThreadUtils.invokeAtFrontUninterruptibly(this.renderThreadHandler, new Runnable() {
public void run() {
VideoFileRenderer.this.eglBase = EglBase.create(sharedContext, EglBase.CONFIG_PIXEL_BUFFER);
VideoFileRenderer.this.eglBase.createDummyPbufferSurface();
VideoFileRenderer.this.eglBase.makeCurrent();
VideoFileRenderer.this.yuvConverter = new YuvConverter();
}
});
} else {
throw new IllegalArgumentException("Does not support uneven width or height");
}
}

public void renderFrame(I420Frame i420Frame) {
VideoFrame frame = i420Frame.toVideoFrame();
this.onFrame(frame);
frame.release();
}

public void onFrame(VideoFrame frame) {
frame.retain();
this.renderThreadHandler.post(() -> {
this.renderFrameOnRenderThread(frame);
});
}

private void renderFrameOnRenderThread(VideoFrame frame) {
Buffer buffer = frame.getBuffer();
int targetWidth = frame.getRotation() % 180 == 0 ? this.outputFileWidth : this.outputFileHeight;
int targetHeight = frame.getRotation() % 180 == 0 ? this.outputFileHeight : this.outputFileWidth;
float frameAspectRatio = (float)buffer.getWidth() / (float)buffer.getHeight();
float fileAspectRatio = (float)targetWidth / (float)targetHeight;
int cropWidth = buffer.getWidth();
int cropHeight = buffer.getHeight();
if (fileAspectRatio > frameAspectRatio) {
cropHeight = (int)((float)cropHeight * (frameAspectRatio / fileAspectRatio));
} else {
cropWidth = (int)((float)cropWidth * (fileAspectRatio / frameAspectRatio));
}

int cropX = (buffer.getWidth() - cropWidth) / 2;
int cropY = (buffer.getHeight() - cropHeight) / 2;
Buffer scaledBuffer = buffer.cropAndScale(cropX, cropY, cropWidth, cropHeight, targetWidth, targetHeight);
frame.release();
I420Buffer i420 = scaledBuffer.toI420();
scaledBuffer.release();
ByteBuffer byteBuffer = JniCommon.nativeAllocateByteBuffer(this.outputFrameSize);
YuvHelper.I420Rotate(i420.getDataY(), i420.getStrideY(), i420.getDataU(), i420.getStrideU(), i420.getDataV(), i420.getStrideV(), byteBuffer, i420.getWidth(), i420.getHeight(), frame.getRotation());
i420.release();
byteBuffer.rewind();
this.rawFrames.add(byteBuffer);
}

public void release() {
CountDownLatch cleanupBarrier = new CountDownLatch(1);
this.renderThreadHandler.post(() -> {
this.yuvConverter.release();
this.eglBase.release();
this.renderThread.quit();
cleanupBarrier.countDown();
});
ThreadUtils.awaitUninterruptibly(cleanupBarrier);

try {
Iterator var2 = this.rawFrames.iterator();

while(var2.hasNext()) {
ByteBuffer buffer = (ByteBuffer)var2.next();
this.videoOutFile.write("FRAMEn".getBytes(Charset.forName("US-ASCII")));
byte data = new byte[this.outputFrameSize];
buffer.get(data);
this.videoOutFile.write(data);
JniCommon.nativeFreeByteBuffer(buffer);
}

this.videoOutFile.close();
Logging.d("VideoFileRenderer", "Video written to disk as " + this.outputFileName + ". Number frames are " + this.rawFrames.size() + " and the dimension of the frames are " + this.outputFileWidth + "x" + this.outputFileHeight + ".");
} catch (IOException var5) {
Logging.e("VideoFileRenderer", "Error writing video to disk", var5);
}

}


}



I can't find any helpful method that can help.










share|improve this question




























    up vote
    7
    down vote

    favorite












    I am developing a webrtc video call Android app, it is working more than fine, I need to record the video of the other peer (remoteVideoStream) and myStream (localVideoStream) and convert it to some saveable format like mp4 or any other format, I really searched for that, but without being able to figure out how to do the job.



    I have read about VideoFileRenderer, I tried to add it to my code to save the video but could not use it as well it has not any method called for example record() or save(), although it has a method called release() which will be used to end saving the video. Here is the class if any one has any idea:



    @JNINamespace("webrtc::jni")
    public class VideoFileRenderer implements Callbacks, VideoSink {
    private static final String TAG = "VideoFileRenderer";
    private final HandlerThread renderThread;
    private final Handler renderThreadHandler;
    private final FileOutputStream videoOutFile;
    private final String outputFileName;
    private final int outputFileWidth;
    private final int outputFileHeight;
    private final int outputFrameSize;
    private final ByteBuffer outputFrameBuffer;
    private EglBase eglBase;
    private YuvConverter yuvConverter;
    private ArrayList<ByteBuffer> rawFrames = new ArrayList();

    public VideoFileRenderer(String outputFile, int outputFileWidth, int outputFileHeight, final Context sharedContext) throws IOException {
    if (outputFileWidth % 2 != 1 && outputFileHeight % 2 != 1) {
    this.outputFileName = outputFile;
    this.outputFileWidth = outputFileWidth;
    this.outputFileHeight = outputFileHeight;
    this.outputFrameSize = outputFileWidth * outputFileHeight * 3 / 2;
    this.outputFrameBuffer = ByteBuffer.allocateDirect(this.outputFrameSize);
    this.videoOutFile = new FileOutputStream(outputFile);
    this.videoOutFile.write(("YUV4MPEG2 C420 W" + outputFileWidth + " H" + outputFileHeight + " Ip F30:1 A1:1n").getBytes(Charset.forName("US-ASCII")));
    this.renderThread = new HandlerThread("VideoFileRenderer");
    this.renderThread.start();
    this.renderThreadHandler = new Handler(this.renderThread.getLooper());
    ThreadUtils.invokeAtFrontUninterruptibly(this.renderThreadHandler, new Runnable() {
    public void run() {
    VideoFileRenderer.this.eglBase = EglBase.create(sharedContext, EglBase.CONFIG_PIXEL_BUFFER);
    VideoFileRenderer.this.eglBase.createDummyPbufferSurface();
    VideoFileRenderer.this.eglBase.makeCurrent();
    VideoFileRenderer.this.yuvConverter = new YuvConverter();
    }
    });
    } else {
    throw new IllegalArgumentException("Does not support uneven width or height");
    }
    }

    public void renderFrame(I420Frame i420Frame) {
    VideoFrame frame = i420Frame.toVideoFrame();
    this.onFrame(frame);
    frame.release();
    }

    public void onFrame(VideoFrame frame) {
    frame.retain();
    this.renderThreadHandler.post(() -> {
    this.renderFrameOnRenderThread(frame);
    });
    }

    private void renderFrameOnRenderThread(VideoFrame frame) {
    Buffer buffer = frame.getBuffer();
    int targetWidth = frame.getRotation() % 180 == 0 ? this.outputFileWidth : this.outputFileHeight;
    int targetHeight = frame.getRotation() % 180 == 0 ? this.outputFileHeight : this.outputFileWidth;
    float frameAspectRatio = (float)buffer.getWidth() / (float)buffer.getHeight();
    float fileAspectRatio = (float)targetWidth / (float)targetHeight;
    int cropWidth = buffer.getWidth();
    int cropHeight = buffer.getHeight();
    if (fileAspectRatio > frameAspectRatio) {
    cropHeight = (int)((float)cropHeight * (frameAspectRatio / fileAspectRatio));
    } else {
    cropWidth = (int)((float)cropWidth * (fileAspectRatio / frameAspectRatio));
    }

    int cropX = (buffer.getWidth() - cropWidth) / 2;
    int cropY = (buffer.getHeight() - cropHeight) / 2;
    Buffer scaledBuffer = buffer.cropAndScale(cropX, cropY, cropWidth, cropHeight, targetWidth, targetHeight);
    frame.release();
    I420Buffer i420 = scaledBuffer.toI420();
    scaledBuffer.release();
    ByteBuffer byteBuffer = JniCommon.nativeAllocateByteBuffer(this.outputFrameSize);
    YuvHelper.I420Rotate(i420.getDataY(), i420.getStrideY(), i420.getDataU(), i420.getStrideU(), i420.getDataV(), i420.getStrideV(), byteBuffer, i420.getWidth(), i420.getHeight(), frame.getRotation());
    i420.release();
    byteBuffer.rewind();
    this.rawFrames.add(byteBuffer);
    }

    public void release() {
    CountDownLatch cleanupBarrier = new CountDownLatch(1);
    this.renderThreadHandler.post(() -> {
    this.yuvConverter.release();
    this.eglBase.release();
    this.renderThread.quit();
    cleanupBarrier.countDown();
    });
    ThreadUtils.awaitUninterruptibly(cleanupBarrier);

    try {
    Iterator var2 = this.rawFrames.iterator();

    while(var2.hasNext()) {
    ByteBuffer buffer = (ByteBuffer)var2.next();
    this.videoOutFile.write("FRAMEn".getBytes(Charset.forName("US-ASCII")));
    byte data = new byte[this.outputFrameSize];
    buffer.get(data);
    this.videoOutFile.write(data);
    JniCommon.nativeFreeByteBuffer(buffer);
    }

    this.videoOutFile.close();
    Logging.d("VideoFileRenderer", "Video written to disk as " + this.outputFileName + ". Number frames are " + this.rawFrames.size() + " and the dimension of the frames are " + this.outputFileWidth + "x" + this.outputFileHeight + ".");
    } catch (IOException var5) {
    Logging.e("VideoFileRenderer", "Error writing video to disk", var5);
    }

    }


    }



    I can't find any helpful method that can help.










    share|improve this question


























      up vote
      7
      down vote

      favorite









      up vote
      7
      down vote

      favorite











      I am developing a webrtc video call Android app, it is working more than fine, I need to record the video of the other peer (remoteVideoStream) and myStream (localVideoStream) and convert it to some saveable format like mp4 or any other format, I really searched for that, but without being able to figure out how to do the job.



      I have read about VideoFileRenderer, I tried to add it to my code to save the video but could not use it as well it has not any method called for example record() or save(), although it has a method called release() which will be used to end saving the video. Here is the class if any one has any idea:



      @JNINamespace("webrtc::jni")
      public class VideoFileRenderer implements Callbacks, VideoSink {
      private static final String TAG = "VideoFileRenderer";
      private final HandlerThread renderThread;
      private final Handler renderThreadHandler;
      private final FileOutputStream videoOutFile;
      private final String outputFileName;
      private final int outputFileWidth;
      private final int outputFileHeight;
      private final int outputFrameSize;
      private final ByteBuffer outputFrameBuffer;
      private EglBase eglBase;
      private YuvConverter yuvConverter;
      private ArrayList<ByteBuffer> rawFrames = new ArrayList();

      public VideoFileRenderer(String outputFile, int outputFileWidth, int outputFileHeight, final Context sharedContext) throws IOException {
      if (outputFileWidth % 2 != 1 && outputFileHeight % 2 != 1) {
      this.outputFileName = outputFile;
      this.outputFileWidth = outputFileWidth;
      this.outputFileHeight = outputFileHeight;
      this.outputFrameSize = outputFileWidth * outputFileHeight * 3 / 2;
      this.outputFrameBuffer = ByteBuffer.allocateDirect(this.outputFrameSize);
      this.videoOutFile = new FileOutputStream(outputFile);
      this.videoOutFile.write(("YUV4MPEG2 C420 W" + outputFileWidth + " H" + outputFileHeight + " Ip F30:1 A1:1n").getBytes(Charset.forName("US-ASCII")));
      this.renderThread = new HandlerThread("VideoFileRenderer");
      this.renderThread.start();
      this.renderThreadHandler = new Handler(this.renderThread.getLooper());
      ThreadUtils.invokeAtFrontUninterruptibly(this.renderThreadHandler, new Runnable() {
      public void run() {
      VideoFileRenderer.this.eglBase = EglBase.create(sharedContext, EglBase.CONFIG_PIXEL_BUFFER);
      VideoFileRenderer.this.eglBase.createDummyPbufferSurface();
      VideoFileRenderer.this.eglBase.makeCurrent();
      VideoFileRenderer.this.yuvConverter = new YuvConverter();
      }
      });
      } else {
      throw new IllegalArgumentException("Does not support uneven width or height");
      }
      }

      public void renderFrame(I420Frame i420Frame) {
      VideoFrame frame = i420Frame.toVideoFrame();
      this.onFrame(frame);
      frame.release();
      }

      public void onFrame(VideoFrame frame) {
      frame.retain();
      this.renderThreadHandler.post(() -> {
      this.renderFrameOnRenderThread(frame);
      });
      }

      private void renderFrameOnRenderThread(VideoFrame frame) {
      Buffer buffer = frame.getBuffer();
      int targetWidth = frame.getRotation() % 180 == 0 ? this.outputFileWidth : this.outputFileHeight;
      int targetHeight = frame.getRotation() % 180 == 0 ? this.outputFileHeight : this.outputFileWidth;
      float frameAspectRatio = (float)buffer.getWidth() / (float)buffer.getHeight();
      float fileAspectRatio = (float)targetWidth / (float)targetHeight;
      int cropWidth = buffer.getWidth();
      int cropHeight = buffer.getHeight();
      if (fileAspectRatio > frameAspectRatio) {
      cropHeight = (int)((float)cropHeight * (frameAspectRatio / fileAspectRatio));
      } else {
      cropWidth = (int)((float)cropWidth * (fileAspectRatio / frameAspectRatio));
      }

      int cropX = (buffer.getWidth() - cropWidth) / 2;
      int cropY = (buffer.getHeight() - cropHeight) / 2;
      Buffer scaledBuffer = buffer.cropAndScale(cropX, cropY, cropWidth, cropHeight, targetWidth, targetHeight);
      frame.release();
      I420Buffer i420 = scaledBuffer.toI420();
      scaledBuffer.release();
      ByteBuffer byteBuffer = JniCommon.nativeAllocateByteBuffer(this.outputFrameSize);
      YuvHelper.I420Rotate(i420.getDataY(), i420.getStrideY(), i420.getDataU(), i420.getStrideU(), i420.getDataV(), i420.getStrideV(), byteBuffer, i420.getWidth(), i420.getHeight(), frame.getRotation());
      i420.release();
      byteBuffer.rewind();
      this.rawFrames.add(byteBuffer);
      }

      public void release() {
      CountDownLatch cleanupBarrier = new CountDownLatch(1);
      this.renderThreadHandler.post(() -> {
      this.yuvConverter.release();
      this.eglBase.release();
      this.renderThread.quit();
      cleanupBarrier.countDown();
      });
      ThreadUtils.awaitUninterruptibly(cleanupBarrier);

      try {
      Iterator var2 = this.rawFrames.iterator();

      while(var2.hasNext()) {
      ByteBuffer buffer = (ByteBuffer)var2.next();
      this.videoOutFile.write("FRAMEn".getBytes(Charset.forName("US-ASCII")));
      byte data = new byte[this.outputFrameSize];
      buffer.get(data);
      this.videoOutFile.write(data);
      JniCommon.nativeFreeByteBuffer(buffer);
      }

      this.videoOutFile.close();
      Logging.d("VideoFileRenderer", "Video written to disk as " + this.outputFileName + ". Number frames are " + this.rawFrames.size() + " and the dimension of the frames are " + this.outputFileWidth + "x" + this.outputFileHeight + ".");
      } catch (IOException var5) {
      Logging.e("VideoFileRenderer", "Error writing video to disk", var5);
      }

      }


      }



      I can't find any helpful method that can help.










      share|improve this question















      I am developing a webrtc video call Android app, it is working more than fine, I need to record the video of the other peer (remoteVideoStream) and myStream (localVideoStream) and convert it to some saveable format like mp4 or any other format, I really searched for that, but without being able to figure out how to do the job.



      I have read about VideoFileRenderer, I tried to add it to my code to save the video but could not use it as well it has not any method called for example record() or save(), although it has a method called release() which will be used to end saving the video. Here is the class if any one has any idea:



      @JNINamespace("webrtc::jni")
      public class VideoFileRenderer implements Callbacks, VideoSink {
      private static final String TAG = "VideoFileRenderer";
      private final HandlerThread renderThread;
      private final Handler renderThreadHandler;
      private final FileOutputStream videoOutFile;
      private final String outputFileName;
      private final int outputFileWidth;
      private final int outputFileHeight;
      private final int outputFrameSize;
      private final ByteBuffer outputFrameBuffer;
      private EglBase eglBase;
      private YuvConverter yuvConverter;
      private ArrayList<ByteBuffer> rawFrames = new ArrayList();

      public VideoFileRenderer(String outputFile, int outputFileWidth, int outputFileHeight, final Context sharedContext) throws IOException {
      if (outputFileWidth % 2 != 1 && outputFileHeight % 2 != 1) {
      this.outputFileName = outputFile;
      this.outputFileWidth = outputFileWidth;
      this.outputFileHeight = outputFileHeight;
      this.outputFrameSize = outputFileWidth * outputFileHeight * 3 / 2;
      this.outputFrameBuffer = ByteBuffer.allocateDirect(this.outputFrameSize);
      this.videoOutFile = new FileOutputStream(outputFile);
      this.videoOutFile.write(("YUV4MPEG2 C420 W" + outputFileWidth + " H" + outputFileHeight + " Ip F30:1 A1:1n").getBytes(Charset.forName("US-ASCII")));
      this.renderThread = new HandlerThread("VideoFileRenderer");
      this.renderThread.start();
      this.renderThreadHandler = new Handler(this.renderThread.getLooper());
      ThreadUtils.invokeAtFrontUninterruptibly(this.renderThreadHandler, new Runnable() {
      public void run() {
      VideoFileRenderer.this.eglBase = EglBase.create(sharedContext, EglBase.CONFIG_PIXEL_BUFFER);
      VideoFileRenderer.this.eglBase.createDummyPbufferSurface();
      VideoFileRenderer.this.eglBase.makeCurrent();
      VideoFileRenderer.this.yuvConverter = new YuvConverter();
      }
      });
      } else {
      throw new IllegalArgumentException("Does not support uneven width or height");
      }
      }

      public void renderFrame(I420Frame i420Frame) {
      VideoFrame frame = i420Frame.toVideoFrame();
      this.onFrame(frame);
      frame.release();
      }

      public void onFrame(VideoFrame frame) {
      frame.retain();
      this.renderThreadHandler.post(() -> {
      this.renderFrameOnRenderThread(frame);
      });
      }

      private void renderFrameOnRenderThread(VideoFrame frame) {
      Buffer buffer = frame.getBuffer();
      int targetWidth = frame.getRotation() % 180 == 0 ? this.outputFileWidth : this.outputFileHeight;
      int targetHeight = frame.getRotation() % 180 == 0 ? this.outputFileHeight : this.outputFileWidth;
      float frameAspectRatio = (float)buffer.getWidth() / (float)buffer.getHeight();
      float fileAspectRatio = (float)targetWidth / (float)targetHeight;
      int cropWidth = buffer.getWidth();
      int cropHeight = buffer.getHeight();
      if (fileAspectRatio > frameAspectRatio) {
      cropHeight = (int)((float)cropHeight * (frameAspectRatio / fileAspectRatio));
      } else {
      cropWidth = (int)((float)cropWidth * (fileAspectRatio / frameAspectRatio));
      }

      int cropX = (buffer.getWidth() - cropWidth) / 2;
      int cropY = (buffer.getHeight() - cropHeight) / 2;
      Buffer scaledBuffer = buffer.cropAndScale(cropX, cropY, cropWidth, cropHeight, targetWidth, targetHeight);
      frame.release();
      I420Buffer i420 = scaledBuffer.toI420();
      scaledBuffer.release();
      ByteBuffer byteBuffer = JniCommon.nativeAllocateByteBuffer(this.outputFrameSize);
      YuvHelper.I420Rotate(i420.getDataY(), i420.getStrideY(), i420.getDataU(), i420.getStrideU(), i420.getDataV(), i420.getStrideV(), byteBuffer, i420.getWidth(), i420.getHeight(), frame.getRotation());
      i420.release();
      byteBuffer.rewind();
      this.rawFrames.add(byteBuffer);
      }

      public void release() {
      CountDownLatch cleanupBarrier = new CountDownLatch(1);
      this.renderThreadHandler.post(() -> {
      this.yuvConverter.release();
      this.eglBase.release();
      this.renderThread.quit();
      cleanupBarrier.countDown();
      });
      ThreadUtils.awaitUninterruptibly(cleanupBarrier);

      try {
      Iterator var2 = this.rawFrames.iterator();

      while(var2.hasNext()) {
      ByteBuffer buffer = (ByteBuffer)var2.next();
      this.videoOutFile.write("FRAMEn".getBytes(Charset.forName("US-ASCII")));
      byte data = new byte[this.outputFrameSize];
      buffer.get(data);
      this.videoOutFile.write(data);
      JniCommon.nativeFreeByteBuffer(buffer);
      }

      this.videoOutFile.close();
      Logging.d("VideoFileRenderer", "Video written to disk as " + this.outputFileName + ". Number frames are " + this.rawFrames.size() + " and the dimension of the frames are " + this.outputFileWidth + "x" + this.outputFileHeight + ".");
      } catch (IOException var5) {
      Logging.e("VideoFileRenderer", "Error writing video to disk", var5);
      }

      }


      }



      I can't find any helpful method that can help.







      android webrtc






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 9 at 7:49

























      asked Oct 28 at 12:07









      Armstring

      606115




      606115
























          1 Answer
          1






          active

          oldest

          votes

















          up vote
          2
          down vote



          accepted










          VideoFileRenderer class just demonstrates how you can access to decoded raw video frames for remote/local peer.
          This is not recording valid video file.

          You should implement manually the logic of encoding and muxing raw video frames into container, like mp4.



          The main flow looks like that:




          • Switch to the latest webrtc version (v.1.0.25331 for now)

          • Create video container. For example see MediaMuxer class from Android SDK

          • Implement interface VideoSink for obtaining raw frames from certain video source. For example see apprtc/CallActivity.java class ProxyVideoSink

          • Encode every frame using MediaCodec and write to video container

          • Finalize muxer






          share|improve this answer





















          • if you came 3 days early I would have granted you the 50 reputations bounty, they unfortunately got expired. thanks anyway
            – Armstring
            Nov 12 at 23:34













          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53031340%2fandroid-webrtc-record-video-from-the-stream-coming-from-the-other-peer%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          2
          down vote



          accepted










          VideoFileRenderer class just demonstrates how you can access to decoded raw video frames for remote/local peer.
          This is not recording valid video file.

          You should implement manually the logic of encoding and muxing raw video frames into container, like mp4.



          The main flow looks like that:




          • Switch to the latest webrtc version (v.1.0.25331 for now)

          • Create video container. For example see MediaMuxer class from Android SDK

          • Implement interface VideoSink for obtaining raw frames from certain video source. For example see apprtc/CallActivity.java class ProxyVideoSink

          • Encode every frame using MediaCodec and write to video container

          • Finalize muxer






          share|improve this answer





















          • if you came 3 days early I would have granted you the 50 reputations bounty, they unfortunately got expired. thanks anyway
            – Armstring
            Nov 12 at 23:34

















          up vote
          2
          down vote



          accepted










          VideoFileRenderer class just demonstrates how you can access to decoded raw video frames for remote/local peer.
          This is not recording valid video file.

          You should implement manually the logic of encoding and muxing raw video frames into container, like mp4.



          The main flow looks like that:




          • Switch to the latest webrtc version (v.1.0.25331 for now)

          • Create video container. For example see MediaMuxer class from Android SDK

          • Implement interface VideoSink for obtaining raw frames from certain video source. For example see apprtc/CallActivity.java class ProxyVideoSink

          • Encode every frame using MediaCodec and write to video container

          • Finalize muxer






          share|improve this answer





















          • if you came 3 days early I would have granted you the 50 reputations bounty, they unfortunately got expired. thanks anyway
            – Armstring
            Nov 12 at 23:34















          up vote
          2
          down vote



          accepted







          up vote
          2
          down vote



          accepted






          VideoFileRenderer class just demonstrates how you can access to decoded raw video frames for remote/local peer.
          This is not recording valid video file.

          You should implement manually the logic of encoding and muxing raw video frames into container, like mp4.



          The main flow looks like that:




          • Switch to the latest webrtc version (v.1.0.25331 for now)

          • Create video container. For example see MediaMuxer class from Android SDK

          • Implement interface VideoSink for obtaining raw frames from certain video source. For example see apprtc/CallActivity.java class ProxyVideoSink

          • Encode every frame using MediaCodec and write to video container

          • Finalize muxer






          share|improve this answer












          VideoFileRenderer class just demonstrates how you can access to decoded raw video frames for remote/local peer.
          This is not recording valid video file.

          You should implement manually the logic of encoding and muxing raw video frames into container, like mp4.



          The main flow looks like that:




          • Switch to the latest webrtc version (v.1.0.25331 for now)

          • Create video container. For example see MediaMuxer class from Android SDK

          • Implement interface VideoSink for obtaining raw frames from certain video source. For example see apprtc/CallActivity.java class ProxyVideoSink

          • Encode every frame using MediaCodec and write to video container

          • Finalize muxer







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 12 at 10:54









          Onix

          4228




          4228












          • if you came 3 days early I would have granted you the 50 reputations bounty, they unfortunately got expired. thanks anyway
            – Armstring
            Nov 12 at 23:34




















          • if you came 3 days early I would have granted you the 50 reputations bounty, they unfortunately got expired. thanks anyway
            – Armstring
            Nov 12 at 23:34


















          if you came 3 days early I would have granted you the 50 reputations bounty, they unfortunately got expired. thanks anyway
          – Armstring
          Nov 12 at 23:34






          if you came 3 days early I would have granted you the 50 reputations bounty, they unfortunately got expired. thanks anyway
          – Armstring
          Nov 12 at 23:34




















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53031340%2fandroid-webrtc-record-video-from-the-stream-coming-from-the-other-peer%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          這個網誌中的熱門文章

          Academy of Television Arts & Sciences

          L'Équipe

          1995 France bombings