From af6293517fa80247f227fa80846d74ea5eef18c2 Mon Sep 17 00:00:00 2001 From: zhihuang Date: Sat, 17 Jun 2017 22:31:24 -0700 Subject: [PATCH] Support building WebRTC without audio and video for Android This CL makes the WebRTC Java Wrapper more modular and allows the android users to build WebRTC without audio and video(DataChannel only). The BUILD file in sdk/android/ is modified to support modular WebRTC. The peerconnection_jni.cc is split into peerconnection_jni.cc, video_jni.cc, video_renderer_jni.cc and ownedfactoryandthreads.h/cc. Add new modular build targets to JNI layer: audio_jni, video_jni, null_audio_jni, null_video_jni. The users can link with different targets to for different WebRTC functionalities. This is split from CL: https://codereview.webrtc.org/2854123003/ TBR=magjed@webrtc.org BUG=webrtc:7613 Review-Url: https://codereview.webrtc.org/2939203002 Cr-Commit-Position: refs/heads/master@{#18647} --- webrtc/sdk/android/BUILD.gn | 263 +++++++++++-- .../src/org/webrtc/PeerConnectionTest.java | 150 ++++++++ webrtc/sdk/android/src/jni/DEPS | 5 + webrtc/sdk/android/src/jni/audio_jni.cc | 26 ++ webrtc/sdk/android/src/jni/audio_jni.h | 28 ++ webrtc/sdk/android/src/jni/media_jni.cc | 35 ++ webrtc/sdk/android/src/jni/media_jni.h | 33 ++ webrtc/sdk/android/src/jni/null_audio_jni.cc | 22 ++ webrtc/sdk/android/src/jni/null_media_jni.cc | 43 +++ webrtc/sdk/android/src/jni/null_video_jni.cc | 40 ++ .../android/src/jni/ownedfactoryandthreads.cc | 64 ++++ .../android/src/jni/ownedfactoryandthreads.h | 80 ++++ .../sdk/android/src/jni/peerconnection_jni.cc | 347 +----------------- webrtc/sdk/android/src/jni/video_jni.cc | 108 ++++++ webrtc/sdk/android/src/jni/video_jni.h | 32 ++ .../sdk/android/src/jni/video_renderer_jni.cc | 190 ++++++++++ 16 files changed, 1104 insertions(+), 362 deletions(-) create mode 100644 webrtc/sdk/android/src/jni/audio_jni.cc create mode 100644 webrtc/sdk/android/src/jni/audio_jni.h create mode 100644 webrtc/sdk/android/src/jni/media_jni.cc create mode 100644 webrtc/sdk/android/src/jni/media_jni.h create mode 100644 webrtc/sdk/android/src/jni/null_audio_jni.cc create mode 100644 webrtc/sdk/android/src/jni/null_media_jni.cc create mode 100644 webrtc/sdk/android/src/jni/null_video_jni.cc create mode 100644 webrtc/sdk/android/src/jni/ownedfactoryandthreads.cc create mode 100644 webrtc/sdk/android/src/jni/ownedfactoryandthreads.h create mode 100644 webrtc/sdk/android/src/jni/video_jni.cc create mode 100644 webrtc/sdk/android/src/jni/video_jni.h create mode 100644 webrtc/sdk/android/src/jni/video_renderer_jni.cc diff --git a/webrtc/sdk/android/BUILD.gn b/webrtc/sdk/android/BUILD.gn index 9332a15d5b..07fc616017 100644 --- a/webrtc/sdk/android/BUILD.gn +++ b/webrtc/sdk/android/BUILD.gn @@ -22,30 +22,77 @@ config("libjingle_peerconnection_jni_warnings_config") { } } -rtc_static_library("libjingle_peerconnection_jni") { +rtc_source_set("base_jni") { sources = [ - "src/jni/androidhistogram_jni.cc", "src/jni/androidmediacodeccommon.h", - "src/jni/androidmediadecoder_jni.cc", - "src/jni/androidmediadecoder_jni.h", - "src/jni/androidmediaencoder_jni.cc", - "src/jni/androidmediaencoder_jni.h", - "src/jni/androidnetworkmonitor_jni.cc", - "src/jni/androidnetworkmonitor_jni.h", - "src/jni/androidvideotracksource.cc", - "src/jni/androidvideotracksource.h", - "src/jni/androidvideotracksource_jni.cc", + "src/jni/audio_jni.h", "src/jni/classreferenceholder.cc", "src/jni/classreferenceholder.h", "src/jni/jni_helpers.cc", "src/jni/jni_helpers.h", + "src/jni/media_jni.h", + "src/jni/ownedfactoryandthreads.cc", + "src/jni/ownedfactoryandthreads.h", + "src/jni/video_jni.h", + ] + + deps = [ + "//webrtc/api:libjingle_peerconnection_api", + "//webrtc/base:rtc_base", + "//webrtc/base:rtc_base_approved", + ] + + if (is_clang) { + # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163). + suppressed_configs += [ + "//build/config/clang:extra_warnings", + "//build/config/clang:find_bad_constructs", + ] + } +} + +rtc_static_library("audio_jni") { + sources = [ + "src/jni/audio_jni.cc", + ] + + deps = [ + ":base_jni", + "//webrtc/api/audio_codecs:builtin_audio_decoder_factory", + "//webrtc/api/audio_codecs:builtin_audio_encoder_factory", + "//webrtc/voice_engine:voice_engine", + ] +} + +rtc_static_library("null_audio_jni") { + sources = [ + "src/jni/null_audio_jni.cc", + ] + + deps = [ + ":base_jni", + "//webrtc/api:libjingle_peerconnection_api", + "//webrtc/base:rtc_base", + "//webrtc/base:rtc_base_approved", + ] +} + +rtc_static_library("video_jni") { + sources = [ + "src/jni/androidhistogram_jni.cc", + "src/jni/androidmediadecoder_jni.cc", + "src/jni/androidmediadecoder_jni.h", + "src/jni/androidmediaencoder_jni.cc", + "src/jni/androidmediaencoder_jni.h", + "src/jni/androidvideotracksource.cc", + "src/jni/androidvideotracksource.h", + "src/jni/androidvideotracksource_jni.cc", "src/jni/native_handle_impl.cc", "src/jni/native_handle_impl.h", - "src/jni/peerconnection_jni.cc", - "src/jni/rtcstatscollectorcallbackwrapper.cc", - "src/jni/rtcstatscollectorcallbackwrapper.h", "src/jni/surfacetexturehelper_jni.cc", "src/jni/surfacetexturehelper_jni.h", + "src/jni/video_jni.cc", + "src/jni/video_renderer_jni.cc", "src/jni/wrapped_native_i420_buffer.cc", "src/jni/wrapped_native_i420_buffer.h", ] @@ -71,22 +118,22 @@ rtc_static_library("libjingle_peerconnection_jni") { } deps = [ - "../..:webrtc_common", - "../../api:video_frame_api", - "../../api/video_codecs:video_codecs_api", - "../../base:rtc_base", - "../../base:rtc_base_approved", - "../../base:rtc_task_queue", - "../../base:sequenced_task_checker", - "../../base:weak_ptr", - "../../common_video:common_video", - "../../media:rtc_media", - "../../media:rtc_media_base", - "../../modules/utility:utility", - "../../modules/video_coding:video_coding_utility", - "../../system_wrappers:system_wrappers", - "../../voice_engine:voice_engine", - "//webrtc/pc:libjingle_peerconnection", + ":base_jni", + "//webrtc:webrtc_common", + "//webrtc/api:libjingle_peerconnection_api", + "//webrtc/api:video_frame_api", + "//webrtc/api/video_codecs:video_codecs_api", + "//webrtc/base:rtc_base", + "//webrtc/base:rtc_base_approved", + "//webrtc/base:rtc_task_queue", + "//webrtc/base:sequenced_task_checker", + "//webrtc/base:weak_ptr", + "//webrtc/common_video:common_video", + "//webrtc/media:rtc_audio_video", + "//webrtc/media:rtc_media_base", + "//webrtc/modules/utility:utility", + "//webrtc/modules/video_coding:video_coding_utility", + "//webrtc/system_wrappers:system_wrappers", ] if (rtc_build_libyuv) { @@ -100,6 +147,124 @@ rtc_static_library("libjingle_peerconnection_jni") { } } +rtc_static_library("null_video_jni") { + sources = [ + "src/jni/null_video_jni.cc", + ] + + deps = [ + ":base_jni", + "//webrtc/base:rtc_base_approved", + ] + + if (is_clang) { + # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163). + suppressed_configs += [ + "//build/config/clang:extra_warnings", + "//build/config/clang:find_bad_constructs", + ] + } +} + +rtc_static_library("media_jni") { + sources = [ + "src/jni/media_jni.cc", + ] + + deps = [ + ":base_jni", + "//webrtc/api:libjingle_peerconnection_api", + "//webrtc/api/audio_codecs:audio_codecs_api", + "//webrtc/base:rtc_base_approved", + "//webrtc/media:rtc_audio_video", + ] + + if (is_clang) { + # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163). + suppressed_configs += [ + "//build/config/clang:extra_warnings", + "//build/config/clang:find_bad_constructs", + ] + } +} + +rtc_static_library("null_media_jni") { + sources = [ + "src/jni/null_media_jni.cc", + ] + + deps = [ + ":base_jni", + "//webrtc/api:libjingle_peerconnection_api", + "//webrtc/base:rtc_base", + "//webrtc/base:rtc_base_approved", + "//webrtc/call:call_interfaces", + "//webrtc/logging:rtc_event_log_api", + ] + + if (is_clang) { + # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163). + suppressed_configs += [ + "//build/config/clang:extra_warnings", + "//build/config/clang:find_bad_constructs", + ] + } +} + +rtc_static_library("peerconnection_jni") { + sources = [ + "src/jni/androidnetworkmonitor_jni.cc", + "src/jni/androidnetworkmonitor_jni.h", + "src/jni/peerconnection_jni.cc", + "src/jni/rtcstatscollectorcallbackwrapper.cc", + "src/jni/rtcstatscollectorcallbackwrapper.h", + ] + + configs += [ ":libjingle_peerconnection_jni_warnings_config" ] + + if (is_clang) { + # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163). + suppressed_configs += [ + "//build/config/clang:extra_warnings", + "//build/config/clang:find_bad_constructs", + ] + } + + # TODO(jschuh): Bug 1348: fix this warning. + configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] + + if (is_win) { + cflags += [ + "/wd4245", # conversion from "int" to "size_t", signed/unsigned mismatch. + "/wd4389", # signed/unsigned mismatch. + ] + } + + deps = [ + ":base_jni", + "../..:webrtc_common", + "//webrtc/base:rtc_base", + "//webrtc/base:rtc_base_approved", + "//webrtc/base:rtc_task_queue", + "//webrtc/media:rtc_data", + "//webrtc/media:rtc_media_base", + "//webrtc/modules/utility:utility", + "//webrtc/pc:peerconnection", + "//webrtc/system_wrappers:system_wrappers", + ] +} + +rtc_static_library("libjingle_peerconnection_jni") { + public_deps = [ + ":audio_jni", + ":base_jni", + ":media_jni", + ":peerconnection_jni", + ":video_jni", + "//webrtc/pc:create_pc_factory", + ] +} + rtc_static_library("libjingle_peerconnection_metrics_default_jni") { sources = [ "src/jni/androidmetrics_jni.cc", @@ -108,14 +273,44 @@ rtc_static_library("libjingle_peerconnection_metrics_default_jni") { configs += [ ":libjingle_peerconnection_jni_warnings_config" ] deps = [ - ":libjingle_peerconnection_jni", - "../../system_wrappers", - "//webrtc/pc:libjingle_peerconnection", + ":base_jni", + ":peerconnection_jni", + "//webrtc/pc:peerconnection", + "//webrtc/system_wrappers", "//webrtc/system_wrappers:field_trial_default", "//webrtc/system_wrappers:metrics_default", ] } +# The modular build targets can be used to build WebRTC with different +# functionalities. The users can choose either the real implemenation or the +# null implementation of the audio/video modules based on their requirments. +# +# For example, to build WebRTC with datachannel support only, we would need the +# the peerconnection and the null implementation of the audio, video and media +# module. +# rtc_shared_library("libjingle_peerconnection_datachannelonly_so") { +# sources = [ +# "src/jni/jni_onload.cc", +# ] +# +# suppressed_configs += [ "//build/config/android:hide_all_but_jni_onload" ] +# configs += [ "//build/config/android:hide_all_but_jni" ] +# +# deps = [ +# ":base_jni", +# ":libjingle_peerconnection_metrics_default_jni", +# ":null_audio_jni", +# ":null_media_jni", +# ":null_video_jni", +# ":peerconnection_jni", +# "//webrtc/base:rtc_base", +# "//webrtc/base:rtc_base_approved", +# "//webrtc/pc:peerconnection", +# ] +# output_extension = "so" +# } + rtc_shared_library("libjingle_peerconnection_so") { sources = [ "src/jni/jni_onload.cc", @@ -127,7 +322,7 @@ rtc_shared_library("libjingle_peerconnection_so") { deps = [ ":libjingle_peerconnection_jni", ":libjingle_peerconnection_metrics_default_jni", - "../../base:rtc_base", + "//webrtc/base:rtc_base", "//webrtc/pc:libjingle_peerconnection", ] output_extension = "so" diff --git a/webrtc/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionTest.java b/webrtc/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionTest.java index 30595a391e..af6f9884fb 100644 --- a/webrtc/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionTest.java +++ b/webrtc/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionTest.java @@ -805,6 +805,156 @@ public class PeerConnectionTest { System.gc(); } + @Test + @MediumTest + public void testDataChannelOnlySession() throws Exception { + // Allow loopback interfaces too since our Android devices often don't + // have those. + PeerConnectionFactory.Options options = new PeerConnectionFactory.Options(); + options.networkIgnoreMask = 0; + PeerConnectionFactory factory = new PeerConnectionFactory(options); + + MediaConstraints pcConstraints = new MediaConstraints(); + pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true")); + + LinkedList iceServers = new LinkedList(); + iceServers.add(new PeerConnection.IceServer("stun:stun.l.google.com:19302")); + iceServers.add( + new PeerConnection.IceServer("turn:fake.example.com", "fakeUsername", "fakePassword")); + ObserverExpectations offeringExpectations = new ObserverExpectations("PCTest:offerer"); + PeerConnection offeringPC = + factory.createPeerConnection(iceServers, pcConstraints, offeringExpectations); + assertNotNull(offeringPC); + + ObserverExpectations answeringExpectations = new ObserverExpectations("PCTest:answerer"); + PeerConnection answeringPC = + factory.createPeerConnection(iceServers, pcConstraints, answeringExpectations); + assertNotNull(answeringPC); + + offeringExpectations.expectRenegotiationNeeded(); + DataChannel offeringDC = offeringPC.createDataChannel("offeringDC", new DataChannel.Init()); + assertEquals("offeringDC", offeringDC.label()); + + offeringExpectations.setDataChannel(offeringDC); + SdpObserverLatch sdpLatch = new SdpObserverLatch(); + offeringPC.createOffer(sdpLatch, new MediaConstraints()); + assertTrue(sdpLatch.await()); + SessionDescription offerSdp = sdpLatch.getSdp(); + assertEquals(offerSdp.type, SessionDescription.Type.OFFER); + assertFalse(offerSdp.description.isEmpty()); + + sdpLatch = new SdpObserverLatch(); + answeringExpectations.expectSignalingChange(SignalingState.HAVE_REMOTE_OFFER); + // SCTP DataChannels are announced via OPEN messages over the established + // connection (not via SDP), so answeringExpectations can only register + // expecting the channel during ICE, below. + answeringPC.setRemoteDescription(sdpLatch, offerSdp); + assertEquals(PeerConnection.SignalingState.STABLE, offeringPC.signalingState()); + assertTrue(sdpLatch.await()); + assertNull(sdpLatch.getSdp()); + + sdpLatch = new SdpObserverLatch(); + answeringPC.createAnswer(sdpLatch, new MediaConstraints()); + assertTrue(sdpLatch.await()); + SessionDescription answerSdp = sdpLatch.getSdp(); + assertEquals(answerSdp.type, SessionDescription.Type.ANSWER); + assertFalse(answerSdp.description.isEmpty()); + + offeringExpectations.expectIceCandidates(2); + answeringExpectations.expectIceCandidates(2); + + offeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE); + answeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE); + + sdpLatch = new SdpObserverLatch(); + answeringExpectations.expectSignalingChange(SignalingState.STABLE); + answeringPC.setLocalDescription(sdpLatch, answerSdp); + assertTrue(sdpLatch.await()); + assertNull(sdpLatch.getSdp()); + + sdpLatch = new SdpObserverLatch(); + offeringExpectations.expectSignalingChange(SignalingState.HAVE_LOCAL_OFFER); + offeringPC.setLocalDescription(sdpLatch, offerSdp); + assertTrue(sdpLatch.await()); + assertNull(sdpLatch.getSdp()); + sdpLatch = new SdpObserverLatch(); + offeringExpectations.expectSignalingChange(SignalingState.STABLE); + + offeringExpectations.expectIceConnectionChange(IceConnectionState.CHECKING); + offeringExpectations.expectIceConnectionChange(IceConnectionState.CONNECTED); + // TODO(bemasc): uncomment once delivery of ICECompleted is reliable + // (https://code.google.com/p/webrtc/issues/detail?id=3021). + answeringExpectations.expectIceConnectionChange(IceConnectionState.CHECKING); + answeringExpectations.expectIceConnectionChange(IceConnectionState.CONNECTED); + + offeringPC.setRemoteDescription(sdpLatch, answerSdp); + assertTrue(sdpLatch.await()); + assertNull(sdpLatch.getSdp()); + + assertEquals(offeringPC.getLocalDescription().type, offerSdp.type); + assertEquals(offeringPC.getRemoteDescription().type, answerSdp.type); + assertEquals(answeringPC.getLocalDescription().type, answerSdp.type); + assertEquals(answeringPC.getRemoteDescription().type, offerSdp.type); + + offeringExpectations.expectStateChange(DataChannel.State.OPEN); + // See commentary about SCTP DataChannels above for why this is here. + answeringExpectations.expectDataChannel("offeringDC"); + answeringExpectations.expectStateChange(DataChannel.State.OPEN); + + // Wait for at least one ice candidate from the offering PC and forward them to the answering + // PC. + for (IceCandidate candidate : offeringExpectations.getAtLeastOneIceCandidate()) { + answeringPC.addIceCandidate(candidate); + } + + // Wait for at least one ice candidate from the answering PC and forward them to the offering + // PC. + for (IceCandidate candidate : answeringExpectations.getAtLeastOneIceCandidate()) { + offeringPC.addIceCandidate(candidate); + } + + assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_SECONDS)); + assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_SECONDS)); + + assertEquals(PeerConnection.SignalingState.STABLE, offeringPC.signalingState()); + assertEquals(PeerConnection.SignalingState.STABLE, answeringPC.signalingState()); + + // Test send & receive UTF-8 text. + answeringExpectations.expectMessage( + ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false); + DataChannel.Buffer buffer = + new DataChannel.Buffer(ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false); + assertTrue(offeringExpectations.dataChannel.send(buffer)); + assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_SECONDS)); + + // Construct this binary message two different ways to ensure no + // shortcuts are taken. + ByteBuffer expectedBinaryMessage = ByteBuffer.allocateDirect(5); + for (byte i = 1; i < 6; ++i) { + expectedBinaryMessage.put(i); + } + expectedBinaryMessage.flip(); + offeringExpectations.expectMessage(expectedBinaryMessage, true); + assertTrue(answeringExpectations.dataChannel.send( + new DataChannel.Buffer(ByteBuffer.wrap(new byte[] {1, 2, 3, 4, 5}), true))); + assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_SECONDS)); + + offeringExpectations.expectStateChange(DataChannel.State.CLOSING); + answeringExpectations.expectStateChange(DataChannel.State.CLOSING); + offeringExpectations.expectStateChange(DataChannel.State.CLOSED); + answeringExpectations.expectStateChange(DataChannel.State.CLOSED); + answeringExpectations.dataChannel.close(); + offeringExpectations.dataChannel.close(); + + // Free the Java-land objects and collect them. + shutdownPC(offeringPC, offeringExpectations); + offeringPC = null; + shutdownPC(answeringPC, answeringExpectations); + answeringPC = null; + factory.dispose(); + System.gc(); + } + // Flaky on Android. See webrtc:7761 @DisabledTest @Test diff --git a/webrtc/sdk/android/src/jni/DEPS b/webrtc/sdk/android/src/jni/DEPS index 108f71f4c2..9f982707b1 100644 --- a/webrtc/sdk/android/src/jni/DEPS +++ b/webrtc/sdk/android/src/jni/DEPS @@ -1,11 +1,16 @@ include_rules = [ "+third_party/libyuv", + "+webrtc/call/callfactoryinterface.h", "+webrtc/common_video/h264/h264_bitstream_parser.h", "+webrtc/common_video/include", "+webrtc/common_video/libyuv/include/webrtc_libyuv.h", + "+webrtc/logging/rtc_event_log/rtc_event_log_factory_interface.h", + "+webrtc/media/base", + "+webrtc/media/engine", "+webrtc/modules/utility/include/jvm_android.h", "+webrtc/modules/video_coding/utility/vp8_header_parser.h", "+webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h", "+webrtc/pc", "+webrtc/system_wrappers/include", + "+webrtc/voice_engine/include/voe_base.h", ] diff --git a/webrtc/sdk/android/src/jni/audio_jni.cc b/webrtc/sdk/android/src/jni/audio_jni.cc new file mode 100644 index 0000000000..58ab73ec65 --- /dev/null +++ b/webrtc/sdk/android/src/jni/audio_jni.cc @@ -0,0 +1,26 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/sdk/android/src/jni/audio_jni.h" + +#include "webrtc/api/audio_codecs/builtin_audio_decoder_factory.h" +#include "webrtc/api/audio_codecs/builtin_audio_encoder_factory.h" + +namespace webrtc_jni { + +rtc::scoped_refptr CreateAudioDecoderFactory() { + return webrtc::CreateBuiltinAudioDecoderFactory(); +} + +rtc::scoped_refptr CreateAudioEncoderFactory() { + return webrtc::CreateBuiltinAudioEncoderFactory(); +} + +} // namespace webrtc_jni diff --git a/webrtc/sdk/android/src/jni/audio_jni.h b/webrtc/sdk/android/src/jni/audio_jni.h new file mode 100644 index 0000000000..3a0b0bd700 --- /dev/null +++ b/webrtc/sdk/android/src/jni/audio_jni.h @@ -0,0 +1,28 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_SDK_ANDROID_SRC_JNI_AUDIO_JNI_H_ +#define WEBRTC_SDK_ANDROID_SRC_JNI_AUDIO_JNI_H_ + +// Adding 'nogncheck' to disable the gn include headers check. +// We don't want this target depend on audio related targets +#include "webrtc/api/audio_codecs/audio_decoder_factory.h" // nogncheck +#include "webrtc/api/audio_codecs/audio_encoder_factory.h" // nogncheck +#include "webrtc/base/scoped_ref_ptr.h" + +namespace webrtc_jni { + +rtc::scoped_refptr CreateAudioDecoderFactory(); + +rtc::scoped_refptr CreateAudioEncoderFactory(); + +} // namespace webrtc_jni + +#endif // WEBRTC_SDK_ANDROID_SRC_JNI_AUDIO_JNI_H_ diff --git a/webrtc/sdk/android/src/jni/media_jni.cc b/webrtc/sdk/android/src/jni/media_jni.cc new file mode 100644 index 0000000000..7fbc67490a --- /dev/null +++ b/webrtc/sdk/android/src/jni/media_jni.cc @@ -0,0 +1,35 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "webrtc/sdk/android/src/jni/media_jni.h" + +#include "webrtc/api/audio_codecs/audio_decoder_factory.h" +#include "webrtc/api/audio_codecs/audio_encoder_factory.h" +#include "webrtc/media/engine/webrtcvideodecoderfactory.h" +#include "webrtc/media/engine/webrtcvideoencoderfactory.h" + +namespace webrtc_jni { + +rtc::scoped_refptr +CreateNativePeerConnectionFactory( + rtc::Thread* network_thread, + rtc::Thread* worker_thread, + rtc::Thread* signaling_thread, + webrtc::AudioDeviceModule* default_adm, + rtc::scoped_refptr audio_encoder_factory, + rtc::scoped_refptr audio_decoder_factory, + cricket::WebRtcVideoEncoderFactory* video_encoder_factory, + cricket::WebRtcVideoDecoderFactory* video_decoder_factory) { + return webrtc::CreatePeerConnectionFactory( + network_thread, worker_thread, signaling_thread, default_adm, + audio_encoder_factory, audio_decoder_factory, video_encoder_factory, + video_decoder_factory); +} + +} // namespace webrtc_jni diff --git a/webrtc/sdk/android/src/jni/media_jni.h b/webrtc/sdk/android/src/jni/media_jni.h new file mode 100644 index 0000000000..168b12544e --- /dev/null +++ b/webrtc/sdk/android/src/jni/media_jni.h @@ -0,0 +1,33 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_SDK_ANDROID_SRC_JNI_MEDIA_JNI_H_ +#define WEBRTC_SDK_ANDROID_SRC_JNI_MEDIA_JNI_H_ + +#include "webrtc/api/peerconnectioninterface.h" +#include "webrtc/base/scoped_ref_ptr.h" +#include "webrtc/base/thread.h" + +namespace webrtc_jni { + +rtc::scoped_refptr +CreateNativePeerConnectionFactory( + rtc::Thread* network_thread, + rtc::Thread* worker_thread, + rtc::Thread* signaling_thread, + webrtc::AudioDeviceModule* default_adm, + rtc::scoped_refptr audio_encoder_factory, + rtc::scoped_refptr audio_decoder_factory, + cricket::WebRtcVideoEncoderFactory* video_encoder_factory, + cricket::WebRtcVideoDecoderFactory* video_decoder_factory); + +} // namespace webrtc_jni + +#endif // WEBRTC_SDK_ANDROID_SRC_JNI_MEDIA_JNI_H_ diff --git a/webrtc/sdk/android/src/jni/null_audio_jni.cc b/webrtc/sdk/android/src/jni/null_audio_jni.cc new file mode 100644 index 0000000000..8599b870fa --- /dev/null +++ b/webrtc/sdk/android/src/jni/null_audio_jni.cc @@ -0,0 +1,22 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "webrtc/sdk/android/src/jni/audio_jni.h" + +namespace webrtc_jni { + +rtc::scoped_refptr CreateAudioDecoderFactory() { + return rtc::scoped_refptr(); +} + +rtc::scoped_refptr CreateAudioEncoderFactory() { + return rtc::scoped_refptr(); +} + +} // namespace webrtc_jni diff --git a/webrtc/sdk/android/src/jni/null_media_jni.cc b/webrtc/sdk/android/src/jni/null_media_jni.cc new file mode 100644 index 0000000000..b41f5c41b7 --- /dev/null +++ b/webrtc/sdk/android/src/jni/null_media_jni.cc @@ -0,0 +1,43 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/sdk/android/src/jni/media_jni.h" + +#include "webrtc/api/audio_codecs/audio_decoder_factory.h" // nogncheck +#include "webrtc/api/audio_codecs/audio_encoder_factory.h" // nogncheck +#include "webrtc/call/callfactoryinterface.h" +#include "webrtc/logging/rtc_event_log/rtc_event_log_factory_interface.h" +#include "webrtc/media/engine/webrtcvideodecoderfactory.h" // nogncheck +#include "webrtc/media/engine/webrtcvideoencoderfactory.h" // nogncheck + +namespace webrtc_jni { + +// This implementation is used for building WebRTC without audio and video +// support. +rtc::scoped_refptr +CreateNativePeerConnectionFactory( + rtc::Thread* network_thread, + rtc::Thread* worker_thread, + rtc::Thread* signaling_thread, + webrtc::AudioDeviceModule* default_adm, + rtc::scoped_refptr audio_encoder_factory, + rtc::scoped_refptr audio_decoder_factory, + cricket::WebRtcVideoEncoderFactory* video_encoder_factory, + cricket::WebRtcVideoDecoderFactory* video_decoder_factory) { + return CreateModularPeerConnectionFactory( + network_thread, worker_thread, signaling_thread, default_adm, + audio_encoder_factory, audio_decoder_factory, video_encoder_factory, + video_decoder_factory, nullptr /*audio_mixer*/, + std::unique_ptr(), + std::unique_ptr(), + std::unique_ptr()); +} + +} // namespace webrtc_jni diff --git a/webrtc/sdk/android/src/jni/null_video_jni.cc b/webrtc/sdk/android/src/jni/null_video_jni.cc new file mode 100644 index 0000000000..82394ff958 --- /dev/null +++ b/webrtc/sdk/android/src/jni/null_video_jni.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/base/scoped_ref_ptr.h" +#include "webrtc/sdk/android/src/jni/classreferenceholder.h" + +namespace cricket { +class WebRtcVideoEncoderFactory; +class WebRtcVideoDecoderFactory; +} // namespace cricket + +namespace webrtc_jni { + +class MediaCodecVideoEncoderFactory; +class MediaCodecVideoDecoderFactory; +class SurfaceTextureHelper; + +cricket::WebRtcVideoEncoderFactory* CreateVideoEncoderFactory() { + return nullptr; +} + +cricket::WebRtcVideoDecoderFactory* CreateVideoDecoderFactory() { + return nullptr; +} + +jobject GetJavaSurfaceTextureHelper( + rtc::scoped_refptr surface_texture_helper) { + return nullptr; +} + +} // namespace webrtc_jni diff --git a/webrtc/sdk/android/src/jni/ownedfactoryandthreads.cc b/webrtc/sdk/android/src/jni/ownedfactoryandthreads.cc new file mode 100644 index 0000000000..7171852dec --- /dev/null +++ b/webrtc/sdk/android/src/jni/ownedfactoryandthreads.cc @@ -0,0 +1,64 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/sdk/android/src/jni/ownedfactoryandthreads.h" + +#include "webrtc/base/logging.h" +#include "webrtc/sdk/android/src/jni/classreferenceholder.h" +#include "webrtc/sdk/android/src/jni/jni_helpers.h" + +namespace webrtc_jni { + +PeerConnectionFactoryInterface* factoryFromJava(jlong j_p) { + return reinterpret_cast(j_p)->factory(); +} + +OwnedFactoryAndThreads::~OwnedFactoryAndThreads() { + CHECK_RELEASE(factory_); + if (network_monitor_factory_ != nullptr) { + rtc::NetworkMonitorFactory::ReleaseFactory(network_monitor_factory_); + } +} + +void OwnedFactoryAndThreads::JavaCallbackOnFactoryThreads() { + JNIEnv* jni = AttachCurrentThreadIfNeeded(); + ScopedLocalRefFrame local_ref_frame(jni); + jclass j_factory_class = FindClass(jni, "org/webrtc/PeerConnectionFactory"); + jmethodID m = nullptr; + if (network_thread_->IsCurrent()) { + LOG(LS_INFO) << "Network thread JavaCallback"; + m = GetStaticMethodID(jni, j_factory_class, "onNetworkThreadReady", "()V"); + } + if (worker_thread_->IsCurrent()) { + LOG(LS_INFO) << "Worker thread JavaCallback"; + m = GetStaticMethodID(jni, j_factory_class, "onWorkerThreadReady", "()V"); + } + if (signaling_thread_->IsCurrent()) { + LOG(LS_INFO) << "Signaling thread JavaCallback"; + m = GetStaticMethodID(jni, j_factory_class, "onSignalingThreadReady", + "()V"); + } + if (m != nullptr) { + jni->CallStaticVoidMethod(j_factory_class, m); + CHECK_EXCEPTION(jni) << "error during JavaCallback::CallStaticVoidMethod"; + } +} + +void OwnedFactoryAndThreads::InvokeJavaCallbacksOnFactoryThreads() { + LOG(LS_INFO) << "InvokeJavaCallbacksOnFactoryThreads."; + network_thread_->Invoke(RTC_FROM_HERE, + [this] { JavaCallbackOnFactoryThreads(); }); + worker_thread_->Invoke(RTC_FROM_HERE, + [this] { JavaCallbackOnFactoryThreads(); }); + signaling_thread_->Invoke(RTC_FROM_HERE, + [this] { JavaCallbackOnFactoryThreads(); }); +} + +} // namespace webrtc_jni diff --git a/webrtc/sdk/android/src/jni/ownedfactoryandthreads.h b/webrtc/sdk/android/src/jni/ownedfactoryandthreads.h new file mode 100644 index 0000000000..920cb939a0 --- /dev/null +++ b/webrtc/sdk/android/src/jni/ownedfactoryandthreads.h @@ -0,0 +1,80 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_SDK_ANDROID_SRC_JNI_OWNEDFACTORYANDTHREADS_H_ +#define WEBRTC_SDK_ANDROID_SRC_JNI_OWNEDFACTORYANDTHREADS_H_ + +#include +#include +#include + +#include "webrtc/api/peerconnectioninterface.h" +#include "webrtc/base/thread.h" + +using cricket::WebRtcVideoDecoderFactory; +using cricket::WebRtcVideoEncoderFactory; +using rtc::Thread; +using webrtc::PeerConnectionFactoryInterface; + +namespace webrtc_jni { + +PeerConnectionFactoryInterface* factoryFromJava(jlong j_p); + +// Helper struct for working around the fact that CreatePeerConnectionFactory() +// comes in two flavors: either entirely automagical (constructing its own +// threads and deleting them on teardown, but no external codec factory support) +// or entirely manual (requires caller to delete threads after factory +// teardown). This struct takes ownership of its ctor's arguments to present a +// single thing for Java to hold and eventually free. +class OwnedFactoryAndThreads { + public: + OwnedFactoryAndThreads(std::unique_ptr network_thread, + std::unique_ptr worker_thread, + std::unique_ptr signaling_thread, + WebRtcVideoEncoderFactory* encoder_factory, + WebRtcVideoDecoderFactory* decoder_factory, + rtc::NetworkMonitorFactory* network_monitor_factory, + PeerConnectionFactoryInterface* factory) + : network_thread_(std::move(network_thread)), + worker_thread_(std::move(worker_thread)), + signaling_thread_(std::move(signaling_thread)), + encoder_factory_(encoder_factory), + decoder_factory_(decoder_factory), + network_monitor_factory_(network_monitor_factory), + factory_(factory) {} + + ~OwnedFactoryAndThreads(); + + PeerConnectionFactoryInterface* factory() { return factory_; } + Thread* signaling_thread() { return signaling_thread_.get(); } + Thread* worker_thread() { return worker_thread_.get(); } + WebRtcVideoEncoderFactory* encoder_factory() { return encoder_factory_; } + WebRtcVideoDecoderFactory* decoder_factory() { return decoder_factory_; } + rtc::NetworkMonitorFactory* network_monitor_factory() { + return network_monitor_factory_; + } + void clear_network_monitor_factory() { network_monitor_factory_ = nullptr; } + void InvokeJavaCallbacksOnFactoryThreads(); + + private: + void JavaCallbackOnFactoryThreads(); + + const std::unique_ptr network_thread_; + const std::unique_ptr worker_thread_; + const std::unique_ptr signaling_thread_; + WebRtcVideoEncoderFactory* encoder_factory_; + WebRtcVideoDecoderFactory* decoder_factory_; + rtc::NetworkMonitorFactory* network_monitor_factory_; + PeerConnectionFactoryInterface* factory_; // Const after ctor except dtor. +}; + +} // namespace webrtc_jni + +#endif // WEBRTC_SDK_ANDROID_SRC_JNI_OWNEDFACTORYANDTHREADS_H_ diff --git a/webrtc/sdk/android/src/jni/peerconnection_jni.cc b/webrtc/sdk/android/src/jni/peerconnection_jni.cc index dbe19af6f6..f257575964 100644 --- a/webrtc/sdk/android/src/jni/peerconnection_jni.cc +++ b/webrtc/sdk/android/src/jni/peerconnection_jni.cc @@ -61,26 +61,27 @@ #include "webrtc/base/ssladapter.h" #include "webrtc/base/stringutils.h" #include "webrtc/media/base/videocapturer.h" -#include "webrtc/media/engine/webrtcvideodecoderfactory.h" -#include "webrtc/media/engine/webrtcvideoencoderfactory.h" #include "webrtc/modules/utility/include/jvm_android.h" -#include "webrtc/system_wrappers/include/field_trial.h" #include "webrtc/pc/webrtcsdp.h" -#include "webrtc/sdk/android/src/jni/androidmediadecoder_jni.h" -#include "webrtc/sdk/android/src/jni/androidmediaencoder_jni.h" #include "webrtc/sdk/android/src/jni/androidnetworkmonitor_jni.h" -#include "webrtc/sdk/android/src/jni/androidvideotracksource.h" +// Adding 'nogncheck' to disable the gn include headers check. +// We don't want to always depend on audio and video related targets. +#include "webrtc/sdk/android/src/jni/androidvideotracksource.h" // nogncheck +#include "webrtc/sdk/android/src/jni/audio_jni.h" #include "webrtc/sdk/android/src/jni/classreferenceholder.h" #include "webrtc/sdk/android/src/jni/jni_helpers.h" -#include "webrtc/sdk/android/src/jni/native_handle_impl.h" +#include "webrtc/sdk/android/src/jni/media_jni.h" +#include "webrtc/sdk/android/src/jni/ownedfactoryandthreads.h" #include "webrtc/sdk/android/src/jni/rtcstatscollectorcallbackwrapper.h" +#include "webrtc/sdk/android/src/jni/video_jni.h" +#include "webrtc/system_wrappers/include/field_trial.h" // Adding 'nogncheck' to disable the gn include headers check. // We don't want to depend on 'system_wrappers:field_trial_default' because // clients should be able to provide their own implementation. #include "webrtc/system_wrappers/include/field_trial_default.h" // nogncheck #include "webrtc/system_wrappers/include/logcat_trace_context.h" #include "webrtc/system_wrappers/include/trace.h" -#include "webrtc/voice_engine/include/voe_base.h" +#include "webrtc/voice_engine/include/voe_base.h" // nogncheck using cricket::WebRtcVideoDecoderFactory; using cricket::WebRtcVideoEncoderFactory; @@ -113,10 +114,7 @@ using webrtc::SetSessionDescriptionObserver; using webrtc::StatsObserver; using webrtc::StatsReport; using webrtc::StatsReports; -using webrtc::VideoTrackSourceInterface; using webrtc::VideoTrackInterface; -using webrtc::VideoTrackVector; -using webrtc::kVideoCodecVP8; namespace webrtc_jni { @@ -846,125 +844,6 @@ class StatsObserverWrapper : public StatsObserver { const jmethodID j_value_ctor_; }; -// Wrapper dispatching rtc::VideoSinkInterface to a Java VideoRenderer -// instance. -class JavaVideoRendererWrapper - : public rtc::VideoSinkInterface { - public: - JavaVideoRendererWrapper(JNIEnv* jni, jobject j_callbacks) - : j_callbacks_(jni, j_callbacks), - j_render_frame_id_(GetMethodID( - jni, GetObjectClass(jni, j_callbacks), "renderFrame", - "(Lorg/webrtc/VideoRenderer$I420Frame;)V")), - j_frame_class_(jni, - FindClass(jni, "org/webrtc/VideoRenderer$I420Frame")), - j_i420_frame_ctor_id_(GetMethodID( - jni, *j_frame_class_, "", "(III[I[Ljava/nio/ByteBuffer;J)V")), - j_texture_frame_ctor_id_(GetMethodID( - jni, *j_frame_class_, "", - "(IIII[FJ)V")), - j_byte_buffer_class_(jni, FindClass(jni, "java/nio/ByteBuffer")) { - CHECK_EXCEPTION(jni); - } - - virtual ~JavaVideoRendererWrapper() {} - - void OnFrame(const webrtc::VideoFrame& video_frame) override { - ScopedLocalRefFrame local_ref_frame(jni()); - - jobject j_frame; - if (video_frame.video_frame_buffer()->type() == - webrtc::VideoFrameBuffer::Type::kNative) { - AndroidVideoFrameBuffer* android_buffer = - static_cast( - video_frame.video_frame_buffer().get()); - switch (android_buffer->android_type()) { - case AndroidVideoFrameBuffer::AndroidType::kTextureBuffer: - j_frame = ToJavaTextureFrame(&video_frame); - break; - case AndroidVideoFrameBuffer::AndroidType::kJavaBuffer: - j_frame = static_cast(android_buffer) - ->ToJavaI420Frame(jni(), video_frame.width(), - video_frame.height(), - video_frame.rotation()); - break; - default: - RTC_NOTREACHED(); - } - } else { - j_frame = ToJavaI420Frame(&video_frame); - } - // |j_callbacks_| is responsible for releasing |j_frame| with - // VideoRenderer.renderFrameDone(). - jni()->CallVoidMethod(*j_callbacks_, j_render_frame_id_, j_frame); - CHECK_EXCEPTION(jni()); - } - - private: - // Make a shallow copy of |frame| to be used with Java. The callee has - // ownership of the frame, and the frame should be released with - // VideoRenderer.releaseNativeFrame(). - static jlong javaShallowCopy(const webrtc::VideoFrame* frame) { - return jlongFromPointer(new webrtc::VideoFrame(*frame)); - } - - // Return a VideoRenderer.I420Frame referring to the data in |frame|. - jobject ToJavaI420Frame(const webrtc::VideoFrame* frame) { - jintArray strides = jni()->NewIntArray(3); - jint* strides_array = jni()->GetIntArrayElements(strides, NULL); - rtc::scoped_refptr i420_buffer = - frame->video_frame_buffer()->ToI420(); - strides_array[0] = i420_buffer->StrideY(); - strides_array[1] = i420_buffer->StrideU(); - strides_array[2] = i420_buffer->StrideV(); - jni()->ReleaseIntArrayElements(strides, strides_array, 0); - jobjectArray planes = jni()->NewObjectArray(3, *j_byte_buffer_class_, NULL); - jobject y_buffer = jni()->NewDirectByteBuffer( - const_cast(i420_buffer->DataY()), - i420_buffer->StrideY() * i420_buffer->height()); - size_t chroma_height = i420_buffer->ChromaHeight(); - jobject u_buffer = - jni()->NewDirectByteBuffer(const_cast(i420_buffer->DataU()), - i420_buffer->StrideU() * chroma_height); - jobject v_buffer = - jni()->NewDirectByteBuffer(const_cast(i420_buffer->DataV()), - i420_buffer->StrideV() * chroma_height); - - jni()->SetObjectArrayElement(planes, 0, y_buffer); - jni()->SetObjectArrayElement(planes, 1, u_buffer); - jni()->SetObjectArrayElement(planes, 2, v_buffer); - return jni()->NewObject( - *j_frame_class_, j_i420_frame_ctor_id_, - frame->width(), frame->height(), - static_cast(frame->rotation()), - strides, planes, javaShallowCopy(frame)); - } - - // Return a VideoRenderer.I420Frame referring texture object in |frame|. - jobject ToJavaTextureFrame(const webrtc::VideoFrame* frame) { - NativeHandleImpl handle = - static_cast(frame->video_frame_buffer().get()) - ->native_handle_impl(); - jfloatArray sampling_matrix = handle.sampling_matrix.ToJava(jni()); - - return jni()->NewObject( - *j_frame_class_, j_texture_frame_ctor_id_, frame->width(), - frame->height(), static_cast(frame->rotation()), - handle.oes_texture_id, sampling_matrix, javaShallowCopy(frame)); - } - - JNIEnv* jni() { - return AttachCurrentThreadIfNeeded(); - } - - ScopedGlobalRef j_callbacks_; - jmethodID j_render_frame_id_; - ScopedGlobalRef j_frame_class_; - jmethodID j_i420_frame_ctor_id_; - jmethodID j_texture_frame_ctor_id_; - ScopedGlobalRef j_byte_buffer_class_; -}; - // Adapter between the C++ RtpReceiverObserverInterface and the Java // RtpReceiver.Observer interface. Wraps an instance of the Java interface and // dispatches C++ callbacks to Java. @@ -1107,15 +986,6 @@ JOW(void, MediaSource_free)(JNIEnv*, jclass, jlong j_p) { reinterpret_cast(j_p)->Release(); } -JOW(void, VideoRenderer_freeWrappedVideoRenderer)(JNIEnv*, jclass, jlong j_p) { - delete reinterpret_cast(j_p); -} - -JOW(void, VideoRenderer_releaseNativeFrame)( - JNIEnv* jni, jclass, jlong j_frame_ptr) { - delete reinterpret_cast(j_frame_ptr); -} - JOW(void, MediaStreamTrack_free)(JNIEnv*, jclass, jlong j_p) { reinterpret_cast(j_p)->Release(); } @@ -1224,93 +1094,6 @@ JOW(void, AudioTrack_nativeSetVolume) source->SetVolume(volume); } -// Helper struct for working around the fact that CreatePeerConnectionFactory() -// comes in two flavors: either entirely automagical (constructing its own -// threads and deleting them on teardown, but no external codec factory support) -// or entirely manual (requires caller to delete threads after factory -// teardown). This struct takes ownership of its ctor's arguments to present a -// single thing for Java to hold and eventually free. -class OwnedFactoryAndThreads { - public: - OwnedFactoryAndThreads(std::unique_ptr network_thread, - std::unique_ptr worker_thread, - std::unique_ptr signaling_thread, - WebRtcVideoEncoderFactory* encoder_factory, - WebRtcVideoDecoderFactory* decoder_factory, - rtc::NetworkMonitorFactory* network_monitor_factory, - PeerConnectionFactoryInterface* factory) - : network_thread_(std::move(network_thread)), - worker_thread_(std::move(worker_thread)), - signaling_thread_(std::move(signaling_thread)), - encoder_factory_(encoder_factory), - decoder_factory_(decoder_factory), - network_monitor_factory_(network_monitor_factory), - factory_(factory) {} - - ~OwnedFactoryAndThreads() { - CHECK_RELEASE(factory_); - if (network_monitor_factory_ != nullptr) { - rtc::NetworkMonitorFactory::ReleaseFactory(network_monitor_factory_); - } - } - - PeerConnectionFactoryInterface* factory() { return factory_; } - Thread* signaling_thread() { return signaling_thread_.get(); } - Thread* worker_thread() { return worker_thread_.get(); } - WebRtcVideoEncoderFactory* encoder_factory() { return encoder_factory_; } - WebRtcVideoDecoderFactory* decoder_factory() { return decoder_factory_; } - rtc::NetworkMonitorFactory* network_monitor_factory() { - return network_monitor_factory_; - } - void clear_network_monitor_factory() { network_monitor_factory_ = nullptr; } - void InvokeJavaCallbacksOnFactoryThreads(); - - private: - void JavaCallbackOnFactoryThreads(); - - const std::unique_ptr network_thread_; - const std::unique_ptr worker_thread_; - const std::unique_ptr signaling_thread_; - WebRtcVideoEncoderFactory* encoder_factory_; - WebRtcVideoDecoderFactory* decoder_factory_; - rtc::NetworkMonitorFactory* network_monitor_factory_; - PeerConnectionFactoryInterface* factory_; // Const after ctor except dtor. -}; - -void OwnedFactoryAndThreads::JavaCallbackOnFactoryThreads() { - JNIEnv* jni = AttachCurrentThreadIfNeeded(); - ScopedLocalRefFrame local_ref_frame(jni); - jclass j_factory_class = FindClass(jni, "org/webrtc/PeerConnectionFactory"); - jmethodID m = nullptr; - if (network_thread_->IsCurrent()) { - LOG(LS_INFO) << "Network thread JavaCallback"; - m = GetStaticMethodID(jni, j_factory_class, "onNetworkThreadReady", "()V"); - } - if (worker_thread_->IsCurrent()) { - LOG(LS_INFO) << "Worker thread JavaCallback"; - m = GetStaticMethodID(jni, j_factory_class, "onWorkerThreadReady", "()V"); - } - if (signaling_thread_->IsCurrent()) { - LOG(LS_INFO) << "Signaling thread JavaCallback"; - m = GetStaticMethodID( - jni, j_factory_class, "onSignalingThreadReady", "()V"); - } - if (m != nullptr) { - jni->CallStaticVoidMethod(j_factory_class, m); - CHECK_EXCEPTION(jni) << "error during JavaCallback::CallStaticVoidMethod"; - } -} - -void OwnedFactoryAndThreads::InvokeJavaCallbacksOnFactoryThreads() { - LOG(LS_INFO) << "InvokeJavaCallbacksOnFactoryThreads."; - network_thread_->Invoke(RTC_FROM_HERE, - [this] { JavaCallbackOnFactoryThreads(); }); - worker_thread_->Invoke(RTC_FROM_HERE, - [this] { JavaCallbackOnFactoryThreads(); }); - signaling_thread_->Invoke(RTC_FROM_HERE, - [this] { JavaCallbackOnFactoryThreads(); }); -} - PeerConnectionFactoryInterface::Options ParseOptionsFromJava(JNIEnv* jni, jobject options) { jclass options_class = jni->GetObjectClass(options); @@ -1362,9 +1145,11 @@ JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnectionFactory)( signaling_thread->SetName("signaling_thread", NULL); RTC_CHECK(signaling_thread->Start()) << "Failed to start thread"; - WebRtcVideoEncoderFactory* encoder_factory = nullptr; - WebRtcVideoDecoderFactory* decoder_factory = nullptr; + WebRtcVideoEncoderFactory* video_encoder_factory = nullptr; + WebRtcVideoDecoderFactory* video_decoder_factory = nullptr; rtc::NetworkMonitorFactory* network_monitor_factory = nullptr; + auto audio_encoder_factory = CreateAudioEncoderFactory(); + auto audio_decoder_factory = CreateAudioDecoderFactory(); PeerConnectionFactoryInterface::Options options; bool has_options = joptions != NULL; @@ -1373,8 +1158,8 @@ JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnectionFactory)( } if (video_hw_acceleration_enabled) { - encoder_factory = new MediaCodecVideoEncoderFactory(); - decoder_factory = new MediaCodecVideoDecoderFactory(); + video_encoder_factory = CreateVideoEncoderFactory(); + video_decoder_factory = CreateVideoDecoderFactory(); } // Do not create network_monitor_factory only if the options are // provided and disable_network_monitor therein is set to true. @@ -1384,9 +1169,10 @@ JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnectionFactory)( } rtc::scoped_refptr factory( - webrtc::CreatePeerConnectionFactory( + CreateNativePeerConnectionFactory( network_thread.get(), worker_thread.get(), signaling_thread.get(), - nullptr, encoder_factory, decoder_factory)); + nullptr, audio_encoder_factory, audio_decoder_factory, + video_encoder_factory, video_decoder_factory)); RTC_CHECK(factory) << "Failed to create the peer connection factory; " << "WebRTC/libjingle init likely failed on this device"; // TODO(honghaiz): Maybe put the options as the argument of @@ -1396,7 +1182,7 @@ JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnectionFactory)( } OwnedFactoryAndThreads* owned_factory = new OwnedFactoryAndThreads( std::move(network_thread), std::move(worker_thread), - std::move(signaling_thread), encoder_factory, decoder_factory, + std::move(signaling_thread), video_encoder_factory, video_decoder_factory, network_monitor_factory, factory.release()); owned_factory->InvokeJavaCallbacksOnFactoryThreads(); return jlongFromPointer(owned_factory); @@ -1412,10 +1198,6 @@ JOW(void, PeerConnectionFactory_nativeFreeFactory)(JNIEnv*, jclass, jlong j_p) { webrtc::Trace::ReturnTrace(); } -static PeerConnectionFactoryInterface* factoryFromJava(jlong j_p) { - return reinterpret_cast(j_p)->factory(); -} - JOW(void, PeerConnectionFactory_nativeThreadsCallbacks)( JNIEnv*, jclass, jlong j_p) { OwnedFactoryAndThreads *factory = @@ -1432,37 +1214,6 @@ JOW(jlong, PeerConnectionFactory_nativeCreateLocalMediaStream)( return (jlong)stream.release(); } -JOW(jlong, PeerConnectionFactory_nativeCreateVideoSource) -(JNIEnv* jni, - jclass, - jlong native_factory, - jobject j_surface_texture_helper, - jboolean is_screencast) { - OwnedFactoryAndThreads* factory = - reinterpret_cast(native_factory); - - rtc::scoped_refptr source( - new rtc::RefCountedObject( - factory->signaling_thread(), jni, j_surface_texture_helper, - is_screencast)); - rtc::scoped_refptr proxy_source = - webrtc::VideoTrackSourceProxy::Create(factory->signaling_thread(), - factory->worker_thread(), source); - - return (jlong)proxy_source.release(); -} - -JOW(jlong, PeerConnectionFactory_nativeCreateVideoTrack)( - JNIEnv* jni, jclass, jlong native_factory, jstring id, - jlong native_source) { - rtc::scoped_refptr factory( - factoryFromJava(native_factory)); - rtc::scoped_refptr track(factory->CreateVideoTrack( - JavaToStdString(jni, id), - reinterpret_cast(native_source))); - return (jlong)track.release(); -} - JOW(jlong, PeerConnectionFactory_nativeCreateAudioSource)( JNIEnv* jni, jclass, jlong native_factory, jobject j_constraints) { std::unique_ptr constraints( @@ -1521,33 +1272,6 @@ JOW(void, PeerConnectionFactory_nativeSetOptions)( } } -JOW(void, PeerConnectionFactory_nativeSetVideoHwAccelerationOptions)( - JNIEnv* jni, jclass, jlong native_factory, jobject local_egl_context, - jobject remote_egl_context) { - OwnedFactoryAndThreads* owned_factory = - reinterpret_cast(native_factory); - - jclass j_eglbase14_context_class = - FindClass(jni, "org/webrtc/EglBase14$Context"); - - MediaCodecVideoEncoderFactory* encoder_factory = - static_cast - (owned_factory->encoder_factory()); - if (encoder_factory && - jni->IsInstanceOf(local_egl_context, j_eglbase14_context_class)) { - LOG(LS_INFO) << "Set EGL context for HW encoding."; - encoder_factory->SetEGLContext(jni, local_egl_context); - } - - MediaCodecVideoDecoderFactory* decoder_factory = - static_cast - (owned_factory->decoder_factory()); - if (decoder_factory) { - LOG(LS_INFO) << "Set EGL context for HW decoding."; - decoder_factory->SetEGLContext(jni, remote_egl_context); - } -} - static PeerConnectionInterface::IceTransportsType JavaIceTransportsTypeToNativeType(JNIEnv* jni, jobject j_ice_transports_type) { std::string enum_name = GetJavaEnumName( @@ -2184,39 +1908,6 @@ JOW(jobject, MediaSource_nativeState)(JNIEnv* jni, jclass, jlong j_p) { return JavaEnumFromIndex(jni, "MediaSource$State", p->state()); } -JOW(jlong, VideoRenderer_nativeWrapVideoRenderer)( - JNIEnv* jni, jclass, jobject j_callbacks) { - std::unique_ptr renderer( - new JavaVideoRendererWrapper(jni, j_callbacks)); - return (jlong)renderer.release(); -} - -JOW(void, VideoRenderer_nativeCopyPlane)( - JNIEnv *jni, jclass, jobject j_src_buffer, jint width, jint height, - jint src_stride, jobject j_dst_buffer, jint dst_stride) { - size_t src_size = jni->GetDirectBufferCapacity(j_src_buffer); - size_t dst_size = jni->GetDirectBufferCapacity(j_dst_buffer); - RTC_CHECK(src_stride >= width) << "Wrong source stride " << src_stride; - RTC_CHECK(dst_stride >= width) << "Wrong destination stride " << dst_stride; - RTC_CHECK(src_size >= src_stride * height) - << "Insufficient source buffer capacity " << src_size; - RTC_CHECK(dst_size >= dst_stride * height) - << "Insufficient destination buffer capacity " << dst_size; - uint8_t *src = - reinterpret_cast(jni->GetDirectBufferAddress(j_src_buffer)); - uint8_t *dst = - reinterpret_cast(jni->GetDirectBufferAddress(j_dst_buffer)); - if (src_stride == dst_stride) { - memcpy(dst, src, src_stride * height); - } else { - for (int i = 0; i < height; i++) { - memcpy(dst, src, width); - src += src_stride; - dst += dst_stride; - } - } -} - JOW(void, FileVideoCapturer_nativeI420ToNV21)( JNIEnv *jni, jclass, jbyteArray j_src_buffer, jint width, jint height, jbyteArray j_dst_buffer) { diff --git a/webrtc/sdk/android/src/jni/video_jni.cc b/webrtc/sdk/android/src/jni/video_jni.cc new file mode 100644 index 0000000000..cd3b4b2819 --- /dev/null +++ b/webrtc/sdk/android/src/jni/video_jni.cc @@ -0,0 +1,108 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/api/videosourceproxy.h" +#include "webrtc/base/logging.h" +#include "webrtc/media/engine/webrtcvideodecoderfactory.h" +#include "webrtc/media/engine/webrtcvideoencoderfactory.h" +#include "webrtc/sdk/android/src/jni/androidmediadecoder_jni.h" +#include "webrtc/sdk/android/src/jni/androidmediaencoder_jni.h" +#include "webrtc/sdk/android/src/jni/androidvideotracksource.h" +#include "webrtc/sdk/android/src/jni/classreferenceholder.h" +#include "webrtc/sdk/android/src/jni/ownedfactoryandthreads.h" +#include "webrtc/sdk/android/src/jni/surfacetexturehelper_jni.h" + +using cricket::WebRtcVideoDecoderFactory; +using cricket::WebRtcVideoEncoderFactory; +using webrtc::AndroidVideoTrackSource; +using webrtc::AudioSourceInterface; +using webrtc::VideoTrackSourceInterface; +using webrtc::VideoTrackInterface; + +namespace webrtc_jni { + +WebRtcVideoEncoderFactory* CreateVideoEncoderFactory() { + return new MediaCodecVideoEncoderFactory(); +} + +WebRtcVideoDecoderFactory* CreateVideoDecoderFactory() { + return new MediaCodecVideoDecoderFactory(); +} + +jobject GetJavaSurfaceTextureHelper( + rtc::scoped_refptr surface_texture_helper) { + return surface_texture_helper + ? surface_texture_helper->GetJavaSurfaceTextureHelper() + : nullptr; +} + +JOW(jlong, PeerConnectionFactory_nativeCreateVideoSource) +(JNIEnv* jni, + jclass, + jlong native_factory, + jobject j_surface_texture_helper, + jboolean is_screencast) { + OwnedFactoryAndThreads* factory = + reinterpret_cast(native_factory); + + rtc::scoped_refptr source( + new rtc::RefCountedObject( + factory->signaling_thread(), jni, j_surface_texture_helper, + is_screencast)); + rtc::scoped_refptr proxy_source = + webrtc::VideoTrackSourceProxy::Create(factory->signaling_thread(), + factory->worker_thread(), source); + + return (jlong)proxy_source.release(); +} + +JOW(jlong, PeerConnectionFactory_nativeCreateVideoTrack) +(JNIEnv* jni, jclass, jlong native_factory, jstring id, jlong native_source) { + rtc::scoped_refptr factory( + factoryFromJava(native_factory)); + rtc::scoped_refptr track(factory->CreateVideoTrack( + JavaToStdString(jni, id), + reinterpret_cast(native_source))); + return (jlong)track.release(); +} + +JOW(void, PeerConnectionFactory_nativeSetVideoHwAccelerationOptions) +(JNIEnv* jni, + jclass, + jlong native_factory, + jobject local_egl_context, + jobject remote_egl_context) { + OwnedFactoryAndThreads* owned_factory = + reinterpret_cast(native_factory); + + jclass j_eglbase14_context_class = + FindClass(jni, "org/webrtc/EglBase14$Context"); + + MediaCodecVideoEncoderFactory* encoder_factory = + static_cast( + owned_factory->encoder_factory()); + if (encoder_factory && + jni->IsInstanceOf(local_egl_context, j_eglbase14_context_class)) { + LOG(LS_INFO) << "Set EGL context for HW encoding."; + encoder_factory->SetEGLContext(jni, local_egl_context); + } + + MediaCodecVideoDecoderFactory* decoder_factory = + static_cast( + owned_factory->decoder_factory()); + if (decoder_factory) { + LOG(LS_INFO) << "Set EGL context for HW decoding."; + decoder_factory->SetEGLContext(jni, remote_egl_context); + } +} + +} // namespace webrtc_jni diff --git a/webrtc/sdk/android/src/jni/video_jni.h b/webrtc/sdk/android/src/jni/video_jni.h new file mode 100644 index 0000000000..9a885cdcad --- /dev/null +++ b/webrtc/sdk/android/src/jni/video_jni.h @@ -0,0 +1,32 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_SDK_ANDROID_SRC_JNI_VIDEO_JNI_H_ +#define WEBRTC_SDK_ANDROID_SRC_JNI_VIDEO_JNI_H_ + +#include + +#include "webrtc/base/scoped_ref_ptr.h" +// Adding 'nogncheck' to disable the gn include headers check. +// We don't want this target depend on video related targets +#include "webrtc/sdk/android/src/jni/surfacetexturehelper_jni.h" // nogncheck + +namespace webrtc_jni { + +WebRtcVideoEncoderFactory* CreateVideoEncoderFactory(); + +WebRtcVideoDecoderFactory* CreateVideoDecoderFactory(); + +jobject GetJavaSurfaceTextureHelper( + rtc::scoped_refptr surface_texture_helper); + +} // namespace webrtc_jni + +#endif // WEBRTC_SDK_ANDROID_SRC_JNI_VIDEO_JNI_H_ diff --git a/webrtc/sdk/android/src/jni/video_renderer_jni.cc b/webrtc/sdk/android/src/jni/video_renderer_jni.cc new file mode 100644 index 0000000000..b5af0003f9 --- /dev/null +++ b/webrtc/sdk/android/src/jni/video_renderer_jni.cc @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#undef JNIEXPORT +#define JNIEXPORT __attribute__((visibility("default"))) + +#include "webrtc/api/video/video_frame.h" +#include "webrtc/media/base/videosinkinterface.h" +#include "webrtc/sdk/android/src/jni/classreferenceholder.h" +#include "webrtc/sdk/android/src/jni/jni_helpers.h" +#include "webrtc/sdk/android/src/jni/native_handle_impl.h" + +namespace webrtc_jni { + +// Wrapper dispatching rtc::VideoSinkInterface to a Java VideoRenderer +// instance. +class JavaVideoRendererWrapper + : public rtc::VideoSinkInterface { + public: + JavaVideoRendererWrapper(JNIEnv* jni, jobject j_callbacks) + : j_callbacks_(jni, j_callbacks), + j_render_frame_id_( + GetMethodID(jni, + GetObjectClass(jni, j_callbacks), + "renderFrame", + "(Lorg/webrtc/VideoRenderer$I420Frame;)V")), + j_frame_class_(jni, + FindClass(jni, "org/webrtc/VideoRenderer$I420Frame")), + j_i420_frame_ctor_id_(GetMethodID(jni, + *j_frame_class_, + "", + "(III[I[Ljava/nio/ByteBuffer;J)V")), + j_texture_frame_ctor_id_( + GetMethodID(jni, *j_frame_class_, "", "(IIII[FJ)V")), + j_byte_buffer_class_(jni, FindClass(jni, "java/nio/ByteBuffer")) { + CHECK_EXCEPTION(jni); + } + + virtual ~JavaVideoRendererWrapper() {} + + void OnFrame(const webrtc::VideoFrame& video_frame) override { + ScopedLocalRefFrame local_ref_frame(jni()); + + jobject j_frame; + if (video_frame.video_frame_buffer()->type() == + webrtc::VideoFrameBuffer::Type::kNative) { + AndroidVideoFrameBuffer* android_buffer = + static_cast( + video_frame.video_frame_buffer().get()); + switch (android_buffer->android_type()) { + case AndroidVideoFrameBuffer::AndroidType::kTextureBuffer: + j_frame = ToJavaTextureFrame(&video_frame); + break; + case AndroidVideoFrameBuffer::AndroidType::kJavaBuffer: + j_frame = static_cast(android_buffer) + ->ToJavaI420Frame(jni(), video_frame.width(), + video_frame.height(), + video_frame.rotation()); + break; + default: + RTC_NOTREACHED(); + } + } else { + j_frame = ToJavaI420Frame(&video_frame); + } + // |j_callbacks_| is responsible for releasing |j_frame| with + // VideoRenderer.renderFrameDone(). + jni()->CallVoidMethod(*j_callbacks_, j_render_frame_id_, j_frame); + CHECK_EXCEPTION(jni()); + } + + private: + // Make a shallow copy of |frame| to be used with Java. The callee has + // ownership of the frame, and the frame should be released with + // VideoRenderer.releaseNativeFrame(). + static jlong javaShallowCopy(const webrtc::VideoFrame* frame) { + return jlongFromPointer(new webrtc::VideoFrame(*frame)); + } + + // Return a VideoRenderer.I420Frame referring to the data in |frame|. + jobject ToJavaI420Frame(const webrtc::VideoFrame* frame) { + jintArray strides = jni()->NewIntArray(3); + jint* strides_array = jni()->GetIntArrayElements(strides, NULL); + rtc::scoped_refptr i420_buffer = + frame->video_frame_buffer()->ToI420(); + strides_array[0] = i420_buffer->StrideY(); + strides_array[1] = i420_buffer->StrideU(); + strides_array[2] = i420_buffer->StrideV(); + jni()->ReleaseIntArrayElements(strides, strides_array, 0); + jobjectArray planes = jni()->NewObjectArray(3, *j_byte_buffer_class_, NULL); + jobject y_buffer = jni()->NewDirectByteBuffer( + const_cast(i420_buffer->DataY()), + i420_buffer->StrideY() * i420_buffer->height()); + size_t chroma_height = i420_buffer->ChromaHeight(); + jobject u_buffer = + jni()->NewDirectByteBuffer(const_cast(i420_buffer->DataU()), + i420_buffer->StrideU() * chroma_height); + jobject v_buffer = + jni()->NewDirectByteBuffer(const_cast(i420_buffer->DataV()), + i420_buffer->StrideV() * chroma_height); + + jni()->SetObjectArrayElement(planes, 0, y_buffer); + jni()->SetObjectArrayElement(planes, 1, u_buffer); + jni()->SetObjectArrayElement(planes, 2, v_buffer); + return jni()->NewObject(*j_frame_class_, j_i420_frame_ctor_id_, + frame->width(), frame->height(), + static_cast(frame->rotation()), strides, + planes, javaShallowCopy(frame)); + } + + // Return a VideoRenderer.I420Frame referring texture object in |frame|. + jobject ToJavaTextureFrame(const webrtc::VideoFrame* frame) { + NativeHandleImpl handle = + static_cast(frame->video_frame_buffer().get()) + ->native_handle_impl(); + jfloatArray sampling_matrix = handle.sampling_matrix.ToJava(jni()); + + return jni()->NewObject( + *j_frame_class_, j_texture_frame_ctor_id_, frame->width(), + frame->height(), static_cast(frame->rotation()), + handle.oes_texture_id, sampling_matrix, javaShallowCopy(frame)); + } + + JNIEnv* jni() { return AttachCurrentThreadIfNeeded(); } + + ScopedGlobalRef j_callbacks_; + jmethodID j_render_frame_id_; + ScopedGlobalRef j_frame_class_; + jmethodID j_i420_frame_ctor_id_; + jmethodID j_texture_frame_ctor_id_; + ScopedGlobalRef j_byte_buffer_class_; +}; + +JOW(void, VideoRenderer_freeWrappedVideoRenderer)(JNIEnv*, jclass, jlong j_p) { + delete reinterpret_cast(j_p); +} + +JOW(void, VideoRenderer_releaseNativeFrame) +(JNIEnv* jni, jclass, jlong j_frame_ptr) { + delete reinterpret_cast(j_frame_ptr); +} + +JOW(jlong, VideoRenderer_nativeWrapVideoRenderer) +(JNIEnv* jni, jclass, jobject j_callbacks) { + std::unique_ptr renderer( + new JavaVideoRendererWrapper(jni, j_callbacks)); + return (jlong)renderer.release(); +} + +JOW(void, VideoRenderer_nativeCopyPlane) +(JNIEnv* jni, + jclass, + jobject j_src_buffer, + jint width, + jint height, + jint src_stride, + jobject j_dst_buffer, + jint dst_stride) { + size_t src_size = jni->GetDirectBufferCapacity(j_src_buffer); + size_t dst_size = jni->GetDirectBufferCapacity(j_dst_buffer); + RTC_CHECK(src_stride >= width) << "Wrong source stride " << src_stride; + RTC_CHECK(dst_stride >= width) << "Wrong destination stride " << dst_stride; + RTC_CHECK(src_size >= src_stride * height) + << "Insufficient source buffer capacity " << src_size; + RTC_CHECK(dst_size >= dst_stride * height) + << "Insufficient destination buffer capacity " << dst_size; + uint8_t* src = + reinterpret_cast(jni->GetDirectBufferAddress(j_src_buffer)); + uint8_t* dst = + reinterpret_cast(jni->GetDirectBufferAddress(j_dst_buffer)); + if (src_stride == dst_stride) { + memcpy(dst, src, src_stride * height); + } else { + for (int i = 0; i < height; i++) { + memcpy(dst, src, width); + src += src_stride; + dst += dst_stride; + } + } +} + +} // namespace webrtc_jni