blob: 0b7c78bc1b7cb896db0a3564b325d1e4c4424a3f [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
fischman@webrtc.org33584f92013-07-25 16:43:30 +00003 * Copyright 2013, Google Inc.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00004 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28// Hints for future visitors:
29// This entire file is an implementation detail of the org.webrtc Java package,
30// the most interesting bits of which are org.webrtc.PeerConnection{,Factory}.
31// The layout of this file is roughly:
32// - various helper C++ functions & classes that wrap Java counterparts and
33// expose a C++ interface that can be passed to the C++ PeerConnection APIs
34// - implementations of methods declared "static" in the Java package (named
35// things like Java_org_webrtc_OMG_Can_This_Name_Be_Any_Longer, prescribed by
36// the JNI spec).
37//
38// Lifecycle notes: objects are owned where they will be called; in other words
39// FooObservers are owned by C++-land, and user-callable objects (e.g.
40// PeerConnection and VideoTrack) are owned by Java-land.
41// When this file allocates C++ RefCountInterfaces it AddRef()s an artificial
42// ref simulating the jlong held in Java-land, and then Release()s the ref in
43// the respective free call. Sometimes this AddRef is implicit in the
44// construction of a scoped_refptr<> which is then .release()d.
45// Any persistent (non-local) references from C++ to Java must be global or weak
46// (in which case they must be checked before use)!
47//
48// Exception notes: pretty much all JNI calls can throw Java exceptions, so each
49// call through a JNIEnv* pointer needs to be followed by an ExceptionCheck()
50// call. In this file this is done in CHECK_EXCEPTION, making for much easier
51// debugging in case of failure (the alternative is to wait for control to
52// return to the Java frame that called code in this file, at which point it's
53// impossible to tell which JNI call broke).
54
55#include <jni.h>
56#undef JNIEXPORT
57#define JNIEXPORT __attribute__((visibility("default")))
58
fischman@webrtc.org32001ef2013-08-12 23:26:21 +000059#include <asm/unistd.h>
henrike@webrtc.org723d6832013-07-12 16:04:50 +000060#include <limits>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000061#include <map>
fischman@webrtc.org32001ef2013-08-12 23:26:21 +000062#include <sys/prctl.h>
63#include <sys/syscall.h>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000064
65#include "talk/app/webrtc/mediaconstraintsinterface.h"
66#include "talk/app/webrtc/peerconnectioninterface.h"
67#include "talk/app/webrtc/videosourceinterface.h"
68#include "talk/base/logging.h"
69#include "talk/base/ssladapter.h"
70#include "talk/media/base/videocapturer.h"
71#include "talk/media/base/videorenderer.h"
72#include "talk/media/devices/videorendererfactory.h"
73#include "talk/media/webrtc/webrtcvideocapturer.h"
fischman@webrtc.org3d496fb2013-07-30 17:14:35 +000074#include "third_party/icu/source/common/unicode/unistr.h"
andrew@webrtc.org31628aa2013-10-22 12:50:00 +000075#include "webrtc/system_wrappers/interface/compile_assert.h"
henrike@webrtc.org9de257d2013-07-17 14:42:53 +000076#include "webrtc/system_wrappers/interface/trace.h"
77#include "webrtc/video_engine/include/vie_base.h"
78#include "webrtc/voice_engine/include/voe_base.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000079
fischman@webrtc.org7e4d0df2013-10-01 02:40:43 +000080#ifdef ANDROID
81#include "webrtc/system_wrappers/interface/logcat_trace_context.h"
82using webrtc::LogcatTraceContext;
83#endif
84
henrike@webrtc.org28e20752013-07-10 00:45:36 +000085using icu::UnicodeString;
86using webrtc::AudioSourceInterface;
87using webrtc::AudioTrackInterface;
88using webrtc::AudioTrackVector;
89using webrtc::CreateSessionDescriptionObserver;
henrike@webrtc.org723d6832013-07-12 16:04:50 +000090using webrtc::DataBuffer;
91using webrtc::DataChannelInit;
92using webrtc::DataChannelInterface;
93using webrtc::DataChannelObserver;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000094using webrtc::IceCandidateInterface;
95using webrtc::MediaConstraintsInterface;
96using webrtc::MediaSourceInterface;
97using webrtc::MediaStreamInterface;
98using webrtc::MediaStreamTrackInterface;
99using webrtc::PeerConnectionFactoryInterface;
100using webrtc::PeerConnectionInterface;
101using webrtc::PeerConnectionObserver;
102using webrtc::SessionDescriptionInterface;
103using webrtc::SetSessionDescriptionObserver;
104using webrtc::StatsObserver;
105using webrtc::StatsReport;
106using webrtc::VideoRendererInterface;
107using webrtc::VideoSourceInterface;
108using webrtc::VideoTrackInterface;
109using webrtc::VideoTrackVector;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000110
111// Abort the process if |x| is false, emitting |msg|.
112#define CHECK(x, msg) \
113 if (x) {} else { \
114 LOG(LS_ERROR) << __FILE__ << ":" << __LINE__ << ": " << msg; \
115 abort(); \
116 }
117// Abort the process if |jni| has a Java exception pending, emitting |msg|.
118#define CHECK_EXCEPTION(jni, msg) \
119 if (0) {} else { \
120 if (jni->ExceptionCheck()) { \
121 jni->ExceptionDescribe(); \
122 jni->ExceptionClear(); \
123 CHECK(0, msg); \
124 } \
125 }
126
fischman@webrtc.org32001ef2013-08-12 23:26:21 +0000127// Helper that calls ptr->Release() and logs a useful message if that didn't
128// actually delete *ptr because of extra refcounts.
129#define CHECK_RELEASE(ptr) \
130 do { \
131 int count = (ptr)->Release(); \
132 if (count != 0) { \
133 LOG(LS_ERROR) << "Refcount unexpectedly not 0: " << (ptr) \
134 << ": " << count; \
135 } \
136 CHECK(!count, "Unexpected refcount"); \
137 } while (0)
138
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000139namespace {
140
141static JavaVM* g_jvm = NULL; // Set in JNI_OnLoad().
142
143static pthread_once_t g_jni_ptr_once = PTHREAD_ONCE_INIT;
144static pthread_key_t g_jni_ptr; // Key for per-thread JNIEnv* data.
145
fischman@webrtc.org32001ef2013-08-12 23:26:21 +0000146// Return thread ID as a string.
147static std::string GetThreadId() {
148 char buf[21]; // Big enough to hold a kuint64max plus terminating NULL.
149 CHECK(snprintf(buf, sizeof(buf), "%llu", syscall(__NR_gettid)) <= sizeof(buf),
150 "Thread id is bigger than uint64??");
151 return std::string(buf);
152}
153
154// Return the current thread's name.
155static std::string GetThreadName() {
156 char name[17];
157 CHECK(prctl(PR_GET_NAME, name) == 0, "prctl(PR_GET_NAME) failed");
158 name[16] = '\0';
159 return std::string(name);
160}
161
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000162static void ThreadDestructor(void* unused) {
163 jint status = g_jvm->DetachCurrentThread();
164 CHECK(status == JNI_OK, "Failed to detach thread: " << status);
165}
166
167static void CreateJNIPtrKey() {
168 CHECK(!pthread_key_create(&g_jni_ptr, &ThreadDestructor),
169 "pthread_key_create");
170}
171
172// Deal with difference in signatures between Oracle's jni.h and Android's.
173static JNIEnv* AttachCurrentThreadIfNeeded() {
174 CHECK(!pthread_once(&g_jni_ptr_once, &CreateJNIPtrKey),
175 "pthread_once");
176 JNIEnv* jni = reinterpret_cast<JNIEnv*>(pthread_getspecific(g_jni_ptr));
177 if (jni == NULL) {
178#ifdef _JAVASOFT_JNI_H_ // Oracle's jni.h violates the JNI spec!
179 void* env;
180#else
181 JNIEnv* env;
182#endif
fischman@webrtc.org32001ef2013-08-12 23:26:21 +0000183 char* name = strdup((GetThreadName() + " - " + GetThreadId()).c_str());
184 JavaVMAttachArgs args;
185 args.version = JNI_VERSION_1_6;
186 args.name = name;
187 args.group = NULL;
188 CHECK(!g_jvm->AttachCurrentThread(&env, &args), "Failed to attach thread");
189 free(name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000190 CHECK(env, "AttachCurrentThread handed back NULL!");
191 jni = reinterpret_cast<JNIEnv*>(env);
192 CHECK(!pthread_setspecific(g_jni_ptr, jni), "pthread_setspecific");
193 }
194 return jni;
195}
196
fischman@webrtc.org87881672013-09-03 18:58:12 +0000197// Return a |jlong| that will automatically convert back to |ptr| when assigned
198// to a |uint64|
199static jlong jlongFromPointer(void* ptr) {
200 COMPILE_ASSERT(sizeof(intptr_t) <= sizeof(uint64),
201 Time_to_rethink_the_use_of_jlongs);
202 // Guaranteed to fit by the COMPILE_ASSERT above.
203 uint64 u64 = reinterpret_cast<intptr_t>(ptr);
204 // If the unsigned value fits in the signed type, return it directly.
205 if (u64 <= std::numeric_limits<int64>::max())
206 return u64;
207 // Otherwise, we need to get move u64 into the range of [int64min, -1] subject
208 // to the constraints of remaining equal to |u64| modulo |2^64|.
209 u64 = std::numeric_limits<uint64>::max() - u64; // In [0,int64max].
210 int64 i64 = -u64; // In [-int64max, 0].
211 i64 -= 1; // In [int64min, -1], and i64+2^64==u64.
212 return i64;
213}
214
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000215// Android's FindClass() is trickier than usual because the app-specific
216// ClassLoader is not consulted when there is no app-specific frame on the
217// stack. Consequently, we only look up classes once in JNI_OnLoad.
218// http://developer.android.com/training/articles/perf-jni.html#faq_FindClass
219class ClassReferenceHolder {
220 public:
221 explicit ClassReferenceHolder(JNIEnv* jni) {
222 LoadClass(jni, "java/nio/ByteBuffer");
223 LoadClass(jni, "org/webrtc/AudioTrack");
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000224 LoadClass(jni, "org/webrtc/DataChannel");
225 LoadClass(jni, "org/webrtc/DataChannel$Buffer");
226 LoadClass(jni, "org/webrtc/DataChannel$Init");
227 LoadClass(jni, "org/webrtc/DataChannel$State");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000228 LoadClass(jni, "org/webrtc/IceCandidate");
229 LoadClass(jni, "org/webrtc/MediaSource$State");
230 LoadClass(jni, "org/webrtc/MediaStream");
231 LoadClass(jni, "org/webrtc/MediaStreamTrack$State");
232 LoadClass(jni, "org/webrtc/PeerConnection$SignalingState");
233 LoadClass(jni, "org/webrtc/PeerConnection$IceConnectionState");
234 LoadClass(jni, "org/webrtc/PeerConnection$IceGatheringState");
235 LoadClass(jni, "org/webrtc/SessionDescription");
236 LoadClass(jni, "org/webrtc/SessionDescription$Type");
237 LoadClass(jni, "org/webrtc/StatsReport");
238 LoadClass(jni, "org/webrtc/StatsReport$Value");
239 LoadClass(jni, "org/webrtc/VideoRenderer$I420Frame");
240 LoadClass(jni, "org/webrtc/VideoTrack");
241 }
242
243 ~ClassReferenceHolder() {
244 CHECK(classes_.empty(), "Must call FreeReferences() before dtor!");
245 }
246
247 void FreeReferences(JNIEnv* jni) {
248 for (std::map<std::string, jclass>::const_iterator it = classes_.begin();
249 it != classes_.end(); ++it) {
250 jni->DeleteGlobalRef(it->second);
251 }
252 classes_.clear();
253 }
254
255 jclass GetClass(const std::string& name) {
256 std::map<std::string, jclass>::iterator it = classes_.find(name);
257 CHECK(it != classes_.end(), "Unexpected GetClass() call for: " << name);
258 return it->second;
259 }
260
261 private:
262 void LoadClass(JNIEnv* jni, const std::string& name) {
263 jclass localRef = jni->FindClass(name.c_str());
264 CHECK_EXCEPTION(jni, "error during FindClass: " << name);
265 CHECK(localRef, name);
266 jclass globalRef = reinterpret_cast<jclass>(jni->NewGlobalRef(localRef));
267 CHECK_EXCEPTION(jni, "error during NewGlobalRef: " << name);
268 CHECK(globalRef, name);
269 bool inserted = classes_.insert(std::make_pair(name, globalRef)).second;
270 CHECK(inserted, "Duplicate class name: " << name);
271 }
272
273 std::map<std::string, jclass> classes_;
274};
275
276// Allocated in JNI_OnLoad(), freed in JNI_OnUnLoad().
277static ClassReferenceHolder* g_class_reference_holder = NULL;
278
279// JNIEnv-helper methods that CHECK success: no Java exception thrown and found
280// object/class/method/field is non-null.
281jmethodID GetMethodID(
282 JNIEnv* jni, jclass c, const std::string& name, const char* signature) {
283 jmethodID m = jni->GetMethodID(c, name.c_str(), signature);
284 CHECK_EXCEPTION(jni,
285 "error during GetMethodID: " << name << ", " << signature);
286 CHECK(m, name << ", " << signature);
287 return m;
288}
289
290jmethodID GetStaticMethodID(
291 JNIEnv* jni, jclass c, const char* name, const char* signature) {
292 jmethodID m = jni->GetStaticMethodID(c, name, signature);
293 CHECK_EXCEPTION(jni,
294 "error during GetStaticMethodID: "
295 << name << ", " << signature);
296 CHECK(m, name << ", " << signature);
297 return m;
298}
299
300jfieldID GetFieldID(
301 JNIEnv* jni, jclass c, const char* name, const char* signature) {
302 jfieldID f = jni->GetFieldID(c, name, signature);
303 CHECK_EXCEPTION(jni, "error during GetFieldID");
304 CHECK(f, name << ", " << signature);
305 return f;
306}
307
308jclass FindClass(JNIEnv* jni, const char* name) {
309 return g_class_reference_holder->GetClass(name);
310}
311
312jclass GetObjectClass(JNIEnv* jni, jobject object) {
313 jclass c = jni->GetObjectClass(object);
314 CHECK_EXCEPTION(jni, "error during GetObjectClass");
315 CHECK(c, "");
316 return c;
317}
318
319jobject GetObjectField(JNIEnv* jni, jobject object, jfieldID id) {
320 jobject o = jni->GetObjectField(object, id);
321 CHECK_EXCEPTION(jni, "error during GetObjectField");
322 CHECK(o, "");
323 return o;
324}
325
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000326jstring GetStringField(JNIEnv* jni, jobject object, jfieldID id) {
327 return static_cast<jstring>(GetObjectField(jni, object, id));
328}
329
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000330jlong GetLongField(JNIEnv* jni, jobject object, jfieldID id) {
331 jlong l = jni->GetLongField(object, id);
332 CHECK_EXCEPTION(jni, "error during GetLongField");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000333 return l;
334}
335
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000336jint GetIntField(JNIEnv* jni, jobject object, jfieldID id) {
337 jint i = jni->GetIntField(object, id);
338 CHECK_EXCEPTION(jni, "error during GetIntField");
339 return i;
340}
341
342bool GetBooleanField(JNIEnv* jni, jobject object, jfieldID id) {
343 jboolean b = jni->GetBooleanField(object, id);
344 CHECK_EXCEPTION(jni, "error during GetBooleanField");
345 return b;
346}
347
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000348jobject NewGlobalRef(JNIEnv* jni, jobject o) {
349 jobject ret = jni->NewGlobalRef(o);
350 CHECK_EXCEPTION(jni, "error during NewGlobalRef");
351 CHECK(ret, "");
352 return ret;
353}
354
355void DeleteGlobalRef(JNIEnv* jni, jobject o) {
356 jni->DeleteGlobalRef(o);
357 CHECK_EXCEPTION(jni, "error during DeleteGlobalRef");
358}
359
360// Given a jweak reference, allocate a (strong) local reference scoped to the
361// lifetime of this object if the weak reference is still valid, or NULL
362// otherwise.
363class WeakRef {
364 public:
365 WeakRef(JNIEnv* jni, jweak ref)
366 : jni_(jni), obj_(jni_->NewLocalRef(ref)) {
367 CHECK_EXCEPTION(jni, "error during NewLocalRef");
368 }
369 ~WeakRef() {
370 if (obj_) {
371 jni_->DeleteLocalRef(obj_);
372 CHECK_EXCEPTION(jni_, "error during DeleteLocalRef");
373 }
374 }
375 jobject obj() { return obj_; }
376
377 private:
378 JNIEnv* const jni_;
379 jobject const obj_;
380};
381
382// Given a local ref, take ownership of it and delete the ref when this goes out
383// of scope.
384template<class T> // T is jclass, jobject, jintArray, etc.
385class ScopedLocalRef {
386 public:
387 ScopedLocalRef(JNIEnv* jni, T obj)
388 : jni_(jni), obj_(obj) {}
389 ~ScopedLocalRef() {
390 jni_->DeleteLocalRef(obj_);
391 }
392 T operator*() const {
393 return obj_;
394 }
395 private:
396 JNIEnv* jni_;
397 T obj_;
398};
399
400// Scoped holder for global Java refs.
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000401template<class T> // T is jclass, jobject, jintArray, etc.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000402class ScopedGlobalRef {
403 public:
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000404 explicit ScopedGlobalRef(JNIEnv* jni, T obj)
405 : obj_(static_cast<T>(jni->NewGlobalRef(obj))) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000406 ~ScopedGlobalRef() {
407 DeleteGlobalRef(AttachCurrentThreadIfNeeded(), obj_);
408 }
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000409 T operator*() const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000410 return obj_;
411 }
412 private:
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000413 T obj_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000414};
415
416// Return the (singleton) Java Enum object corresponding to |index|;
417// |state_class_fragment| is something like "MediaSource$State".
418jobject JavaEnumFromIndex(
419 JNIEnv* jni, const std::string& state_class_fragment, int index) {
420 std::string state_class_name = "org/webrtc/" + state_class_fragment;
421 jclass state_class = FindClass(jni, state_class_name.c_str());
422 jmethodID state_values_id = GetStaticMethodID(
423 jni, state_class, "values", ("()[L" + state_class_name + ";").c_str());
424 ScopedLocalRef<jobjectArray> state_values(
425 jni,
426 (jobjectArray)jni->CallStaticObjectMethod(state_class, state_values_id));
427 CHECK_EXCEPTION(jni, "error during CallStaticObjectMethod");
428 jobject ret = jni->GetObjectArrayElement(*state_values, index);
429 CHECK_EXCEPTION(jni, "error during GetObjectArrayElement");
430 return ret;
431}
432
433// Given a UTF-8 encoded |native| string return a new (UTF-16) jstring.
434static jstring JavaStringFromStdString(JNIEnv* jni, const std::string& native) {
435 UnicodeString ustr(UnicodeString::fromUTF8(native));
436 jstring jstr = jni->NewString(ustr.getBuffer(), ustr.length());
437 CHECK_EXCEPTION(jni, "error during NewString");
438 return jstr;
439}
440
441// Given a (UTF-16) jstring return a new UTF-8 native string.
442static std::string JavaToStdString(JNIEnv* jni, const jstring& j_string) {
443 const jchar* jchars = jni->GetStringChars(j_string, NULL);
444 CHECK_EXCEPTION(jni, "Error during GetStringChars");
445 UnicodeString ustr(jchars, jni->GetStringLength(j_string));
446 CHECK_EXCEPTION(jni, "Error during GetStringLength");
447 jni->ReleaseStringChars(j_string, jchars);
448 CHECK_EXCEPTION(jni, "Error during ReleaseStringChars");
449 std::string ret;
450 return ustr.toUTF8String(ret);
451}
452
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000453static DataChannelInit JavaDataChannelInitToNative(
454 JNIEnv* jni, jobject j_init) {
455 DataChannelInit init;
456
457 jclass j_init_class = FindClass(jni, "org/webrtc/DataChannel$Init");
458 jfieldID ordered_id = GetFieldID(jni, j_init_class, "ordered", "Z");
459 jfieldID max_retransmit_time_id =
460 GetFieldID(jni, j_init_class, "maxRetransmitTimeMs", "I");
461 jfieldID max_retransmits_id =
462 GetFieldID(jni, j_init_class, "maxRetransmits", "I");
463 jfieldID protocol_id =
464 GetFieldID(jni, j_init_class, "protocol", "Ljava/lang/String;");
465 jfieldID negotiated_id = GetFieldID(jni, j_init_class, "negotiated", "Z");
466 jfieldID id_id = GetFieldID(jni, j_init_class, "id", "I");
467
468 init.ordered = GetBooleanField(jni, j_init, ordered_id);
469 init.maxRetransmitTime = GetIntField(jni, j_init, max_retransmit_time_id);
470 init.maxRetransmits = GetIntField(jni, j_init, max_retransmits_id);
471 init.protocol = JavaToStdString(
472 jni, GetStringField(jni, j_init, protocol_id));
473 init.negotiated = GetBooleanField(jni, j_init, negotiated_id);
474 init.id = GetIntField(jni, j_init, id_id);
475
476 return init;
477}
478
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000479class ConstraintsWrapper;
480
481// Adapter between the C++ PeerConnectionObserver interface and the Java
482// PeerConnection.Observer interface. Wraps an instance of the Java interface
483// and dispatches C++ callbacks to Java.
484class PCOJava : public PeerConnectionObserver {
485 public:
486 PCOJava(JNIEnv* jni, jobject j_observer)
487 : j_observer_global_(jni, j_observer),
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000488 j_observer_class_(jni, GetObjectClass(jni, *j_observer_global_)),
489 j_media_stream_class_(jni, FindClass(jni, "org/webrtc/MediaStream")),
490 j_media_stream_ctor_(GetMethodID(
491 jni, *j_media_stream_class_, "<init>", "(J)V")),
492 j_audio_track_class_(jni, FindClass(jni, "org/webrtc/AudioTrack")),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000493 j_audio_track_ctor_(GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000494 jni, *j_audio_track_class_, "<init>", "(J)V")),
495 j_video_track_class_(jni, FindClass(jni, "org/webrtc/VideoTrack")),
496 j_video_track_ctor_(GetMethodID(
497 jni, *j_video_track_class_, "<init>", "(J)V")),
498 j_data_channel_class_(jni, FindClass(jni, "org/webrtc/DataChannel")),
499 j_data_channel_ctor_(GetMethodID(
500 jni, *j_data_channel_class_, "<init>", "(J)V")) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000501 }
502
503 virtual ~PCOJava() {}
504
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000505 virtual void OnIceCandidate(const IceCandidateInterface* candidate) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000506 std::string sdp;
507 CHECK(candidate->ToString(&sdp), "got so far: " << sdp);
508 jclass candidate_class = FindClass(jni(), "org/webrtc/IceCandidate");
509 jmethodID ctor = GetMethodID(jni(), candidate_class,
510 "<init>", "(Ljava/lang/String;ILjava/lang/String;)V");
511 ScopedLocalRef<jstring> j_mid(
512 jni(), JavaStringFromStdString(jni(), candidate->sdp_mid()));
513 ScopedLocalRef<jstring> j_sdp(jni(), JavaStringFromStdString(jni(), sdp));
514 ScopedLocalRef<jobject> j_candidate(jni(), jni()->NewObject(
515 candidate_class, ctor, *j_mid, candidate->sdp_mline_index(), *j_sdp));
516 CHECK_EXCEPTION(jni(), "error during NewObject");
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000517 jmethodID m = GetMethodID(jni(), *j_observer_class_,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000518 "onIceCandidate", "(Lorg/webrtc/IceCandidate;)V");
519 jni()->CallVoidMethod(*j_observer_global_, m, *j_candidate);
520 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
521 }
522
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000523 virtual void OnError() OVERRIDE {
524 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onError", "(V)V");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000525 jni()->CallVoidMethod(*j_observer_global_, m);
526 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
527 }
528
529 virtual void OnSignalingChange(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000530 PeerConnectionInterface::SignalingState new_state) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000531 jmethodID m = GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000532 jni(), *j_observer_class_, "onSignalingChange",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000533 "(Lorg/webrtc/PeerConnection$SignalingState;)V");
534 ScopedLocalRef<jobject> new_state_enum(jni(), JavaEnumFromIndex(
535 jni(), "PeerConnection$SignalingState", new_state));
536 jni()->CallVoidMethod(*j_observer_global_, m, *new_state_enum);
537 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
538 }
539
540 virtual void OnIceConnectionChange(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000541 PeerConnectionInterface::IceConnectionState new_state) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000542 jmethodID m = GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000543 jni(), *j_observer_class_, "onIceConnectionChange",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000544 "(Lorg/webrtc/PeerConnection$IceConnectionState;)V");
545 ScopedLocalRef<jobject> new_state_enum(jni(), JavaEnumFromIndex(
546 jni(), "PeerConnection$IceConnectionState", new_state));
547 jni()->CallVoidMethod(*j_observer_global_, m, *new_state_enum);
548 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
549 }
550
551 virtual void OnIceGatheringChange(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000552 PeerConnectionInterface::IceGatheringState new_state) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000553 jmethodID m = GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000554 jni(), *j_observer_class_, "onIceGatheringChange",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000555 "(Lorg/webrtc/PeerConnection$IceGatheringState;)V");
556 ScopedLocalRef<jobject> new_state_enum(jni(), JavaEnumFromIndex(
557 jni(), "PeerConnection$IceGatheringState", new_state));
558 jni()->CallVoidMethod(*j_observer_global_, m, *new_state_enum);
559 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
560 }
561
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000562 virtual void OnAddStream(MediaStreamInterface* stream) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000563 ScopedLocalRef<jobject> j_stream(jni(), jni()->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000564 *j_media_stream_class_, j_media_stream_ctor_, (jlong)stream));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000565 CHECK_EXCEPTION(jni(), "error during NewObject");
566
567 AudioTrackVector audio_tracks = stream->GetAudioTracks();
568 for (size_t i = 0; i < audio_tracks.size(); ++i) {
569 AudioTrackInterface* track = audio_tracks[i];
570 ScopedLocalRef<jstring> id(
571 jni(), JavaStringFromStdString(jni(), track->id()));
572 ScopedLocalRef<jobject> j_track(jni(), jni()->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000573 *j_audio_track_class_, j_audio_track_ctor_, (jlong)track, *id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000574 CHECK_EXCEPTION(jni(), "error during NewObject");
fischman@webrtc.org32001ef2013-08-12 23:26:21 +0000575 jfieldID audio_tracks_id = GetFieldID(jni(),
576 *j_media_stream_class_,
577 "audioTracks",
578 "Ljava/util/LinkedList;");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000579 ScopedLocalRef<jobject> audio_tracks(jni(), GetObjectField(
580 jni(), *j_stream, audio_tracks_id));
581 jmethodID add = GetMethodID(jni(),
582 GetObjectClass(jni(), *audio_tracks), "add", "(Ljava/lang/Object;)Z");
583 jboolean added = jni()->CallBooleanMethod(*audio_tracks, add, *j_track);
584 CHECK_EXCEPTION(jni(), "error during CallBooleanMethod");
585 CHECK(added, "");
586 }
587
588 VideoTrackVector video_tracks = stream->GetVideoTracks();
589 for (size_t i = 0; i < video_tracks.size(); ++i) {
590 VideoTrackInterface* track = video_tracks[i];
591 ScopedLocalRef<jstring> id(
592 jni(), JavaStringFromStdString(jni(), track->id()));
593 ScopedLocalRef<jobject> j_track(jni(), jni()->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000594 *j_video_track_class_, j_video_track_ctor_, (jlong)track, *id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000595 CHECK_EXCEPTION(jni(), "error during NewObject");
fischman@webrtc.org32001ef2013-08-12 23:26:21 +0000596 jfieldID video_tracks_id = GetFieldID(jni(),
597 *j_media_stream_class_,
598 "videoTracks",
599 "Ljava/util/LinkedList;");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000600 ScopedLocalRef<jobject> video_tracks(jni(), GetObjectField(
601 jni(), *j_stream, video_tracks_id));
602 jmethodID add = GetMethodID(jni(),
603 GetObjectClass(jni(), *video_tracks), "add", "(Ljava/lang/Object;)Z");
604 jboolean added = jni()->CallBooleanMethod(*video_tracks, add, *j_track);
605 CHECK_EXCEPTION(jni(), "error during CallBooleanMethod");
606 CHECK(added, "");
607 }
608 streams_[stream] = jni()->NewWeakGlobalRef(*j_stream);
609 CHECK_EXCEPTION(jni(), "error during NewWeakGlobalRef");
610
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000611 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onAddStream",
612 "(Lorg/webrtc/MediaStream;)V");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000613 jni()->CallVoidMethod(*j_observer_global_, m, *j_stream);
614 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
615 }
616
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000617 virtual void OnRemoveStream(MediaStreamInterface* stream) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000618 NativeToJavaStreamsMap::iterator it = streams_.find(stream);
619 CHECK(it != streams_.end(), "unexpected stream: " << std::hex << stream);
620
621 WeakRef s(jni(), it->second);
622 streams_.erase(it);
623 if (!s.obj())
624 return;
625
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000626 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onRemoveStream",
627 "(Lorg/webrtc/MediaStream;)V");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000628 jni()->CallVoidMethod(*j_observer_global_, m, s.obj());
629 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
630 }
631
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000632 virtual void OnDataChannel(DataChannelInterface* channel) OVERRIDE {
633 ScopedLocalRef<jobject> j_channel(jni(), jni()->NewObject(
634 *j_data_channel_class_, j_data_channel_ctor_, (jlong)channel));
635 CHECK_EXCEPTION(jni(), "error during NewObject");
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000636
637 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onDataChannel",
638 "(Lorg/webrtc/DataChannel;)V");
639 jni()->CallVoidMethod(*j_observer_global_, m, *j_channel);
fischman@webrtc.org32001ef2013-08-12 23:26:21 +0000640
641 // Channel is now owned by Java object, and will be freed from
642 // DataChannel.dispose(). Important that this be done _after_ the
643 // CallVoidMethod above as Java code might call back into native code and be
644 // surprised to see a refcount of 2.
645 int bumped_count = channel->AddRef();
646 CHECK(bumped_count == 2, "Unexpected refcount OnDataChannel");
647
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000648 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
649 }
650
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000651 void SetConstraints(ConstraintsWrapper* constraints) {
652 CHECK(!constraints_.get(), "constraints already set!");
653 constraints_.reset(constraints);
654 }
655
656 const ConstraintsWrapper* constraints() { return constraints_.get(); }
657
658 private:
659 JNIEnv* jni() {
660 return AttachCurrentThreadIfNeeded();
661 }
662
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000663 const ScopedGlobalRef<jobject> j_observer_global_;
664 const ScopedGlobalRef<jclass> j_observer_class_;
665 const ScopedGlobalRef<jclass> j_media_stream_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000666 const jmethodID j_media_stream_ctor_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000667 const ScopedGlobalRef<jclass> j_audio_track_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000668 const jmethodID j_audio_track_ctor_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000669 const ScopedGlobalRef<jclass> j_video_track_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000670 const jmethodID j_video_track_ctor_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000671 const ScopedGlobalRef<jclass> j_data_channel_class_;
672 const jmethodID j_data_channel_ctor_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000673 typedef std::map<void*, jweak> NativeToJavaStreamsMap;
674 NativeToJavaStreamsMap streams_; // C++ -> Java streams.
675 talk_base::scoped_ptr<ConstraintsWrapper> constraints_;
676};
677
678// Wrapper for a Java MediaConstraints object. Copies all needed data so when
679// the constructor returns the Java object is no longer needed.
680class ConstraintsWrapper : public MediaConstraintsInterface {
681 public:
682 ConstraintsWrapper(JNIEnv* jni, jobject j_constraints) {
683 PopulateConstraintsFromJavaPairList(
684 jni, j_constraints, "mandatory", &mandatory_);
685 PopulateConstraintsFromJavaPairList(
686 jni, j_constraints, "optional", &optional_);
687 }
688
689 virtual ~ConstraintsWrapper() {}
690
691 // MediaConstraintsInterface.
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000692 virtual const Constraints& GetMandatory() const OVERRIDE {
693 return mandatory_;
694 }
695
696 virtual const Constraints& GetOptional() const OVERRIDE {
697 return optional_;
698 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000699
700 private:
701 // Helper for translating a List<Pair<String, String>> to a Constraints.
702 static void PopulateConstraintsFromJavaPairList(
703 JNIEnv* jni, jobject j_constraints,
704 const char* field_name, Constraints* field) {
705 jfieldID j_id = GetFieldID(jni,
706 GetObjectClass(jni, j_constraints), field_name, "Ljava/util/List;");
707 jobject j_list = GetObjectField(jni, j_constraints, j_id);
708 jmethodID j_iterator_id = GetMethodID(jni,
709 GetObjectClass(jni, j_list), "iterator", "()Ljava/util/Iterator;");
710 jobject j_iterator = jni->CallObjectMethod(j_list, j_iterator_id);
711 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
712 jmethodID j_has_next = GetMethodID(jni,
713 GetObjectClass(jni, j_iterator), "hasNext", "()Z");
714 jmethodID j_next = GetMethodID(jni,
715 GetObjectClass(jni, j_iterator), "next", "()Ljava/lang/Object;");
716 while (jni->CallBooleanMethod(j_iterator, j_has_next)) {
717 CHECK_EXCEPTION(jni, "error during CallBooleanMethod");
718 jobject entry = jni->CallObjectMethod(j_iterator, j_next);
719 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
720 jmethodID get_key = GetMethodID(jni,
721 GetObjectClass(jni, entry), "getKey", "()Ljava/lang/String;");
722 jstring j_key = reinterpret_cast<jstring>(
723 jni->CallObjectMethod(entry, get_key));
724 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
725 jmethodID get_value = GetMethodID(jni,
726 GetObjectClass(jni, entry), "getValue", "()Ljava/lang/String;");
727 jstring j_value = reinterpret_cast<jstring>(
728 jni->CallObjectMethod(entry, get_value));
729 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
730 field->push_back(Constraint(JavaToStdString(jni, j_key),
731 JavaToStdString(jni, j_value)));
732 }
733 CHECK_EXCEPTION(jni, "error during CallBooleanMethod");
734 }
735
736 Constraints mandatory_;
737 Constraints optional_;
738};
739
740static jobject JavaSdpFromNativeSdp(
741 JNIEnv* jni, const SessionDescriptionInterface* desc) {
742 std::string sdp;
743 CHECK(desc->ToString(&sdp), "got so far: " << sdp);
744 ScopedLocalRef<jstring> j_description(jni, JavaStringFromStdString(jni, sdp));
745
746 jclass j_type_class = FindClass(
747 jni, "org/webrtc/SessionDescription$Type");
748 jmethodID j_type_from_canonical = GetStaticMethodID(
749 jni, j_type_class, "fromCanonicalForm",
750 "(Ljava/lang/String;)Lorg/webrtc/SessionDescription$Type;");
751 ScopedLocalRef<jstring> j_type_string(
752 jni, JavaStringFromStdString(jni, desc->type()));
753 jobject j_type = jni->CallStaticObjectMethod(
754 j_type_class, j_type_from_canonical, *j_type_string);
755 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
756
757 jclass j_sdp_class = FindClass(jni, "org/webrtc/SessionDescription");
758 jmethodID j_sdp_ctor = GetMethodID(
759 jni, j_sdp_class, "<init>",
760 "(Lorg/webrtc/SessionDescription$Type;Ljava/lang/String;)V");
761 jobject j_sdp = jni->NewObject(
762 j_sdp_class, j_sdp_ctor, j_type, *j_description);
763 CHECK_EXCEPTION(jni, "error during NewObject");
764 return j_sdp;
765}
766
767template <class T> // T is one of {Create,Set}SessionDescriptionObserver.
768class SdpObserverWrapper : public T {
769 public:
770 SdpObserverWrapper(JNIEnv* jni, jobject j_observer,
771 ConstraintsWrapper* constraints)
772 : constraints_(constraints),
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000773 j_observer_global_(jni, j_observer),
774 j_observer_class_(jni, GetObjectClass(jni, j_observer)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000775 }
776
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000777 virtual ~SdpObserverWrapper() {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000778
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000779 // Can't mark OVERRIDE because of templating.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000780 virtual void OnSuccess() {
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000781 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onSetSuccess", "()V");
782 jni()->CallVoidMethod(*j_observer_global_, m);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000783 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
784 }
785
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000786 // Can't mark OVERRIDE because of templating.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000787 virtual void OnSuccess(SessionDescriptionInterface* desc) {
788 jmethodID m = GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000789 jni(), *j_observer_class_, "onCreateSuccess",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000790 "(Lorg/webrtc/SessionDescription;)V");
791 ScopedLocalRef<jobject> j_sdp(jni(), JavaSdpFromNativeSdp(jni(), desc));
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000792 jni()->CallVoidMethod(*j_observer_global_, m, *j_sdp);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000793 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
794 }
795
796 protected:
797 // Common implementation for failure of Set & Create types, distinguished by
798 // |op| being "Set" or "Create".
799 void OnFailure(const std::string& op, const std::string& error) {
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000800 jmethodID m = GetMethodID(jni(), *j_observer_class_, "on" + op + "Failure",
801 "(Ljava/lang/String;)V");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000802 ScopedLocalRef<jstring> j_error_string(
803 jni(), JavaStringFromStdString(jni(), error));
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000804 jni()->CallVoidMethod(*j_observer_global_, m, *j_error_string);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000805 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
806 }
807
808 private:
809 JNIEnv* jni() {
810 return AttachCurrentThreadIfNeeded();
811 }
812
813 talk_base::scoped_ptr<ConstraintsWrapper> constraints_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000814 const ScopedGlobalRef<jobject> j_observer_global_;
815 const ScopedGlobalRef<jclass> j_observer_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000816};
817
818class CreateSdpObserverWrapper
819 : public SdpObserverWrapper<CreateSessionDescriptionObserver> {
820 public:
821 CreateSdpObserverWrapper(JNIEnv* jni, jobject j_observer,
822 ConstraintsWrapper* constraints)
823 : SdpObserverWrapper(jni, j_observer, constraints) {}
824
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000825 virtual void OnFailure(const std::string& error) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000826 SdpObserverWrapper::OnFailure(std::string("Create"), error);
827 }
828};
829
830class SetSdpObserverWrapper
831 : public SdpObserverWrapper<SetSessionDescriptionObserver> {
832 public:
833 SetSdpObserverWrapper(JNIEnv* jni, jobject j_observer,
834 ConstraintsWrapper* constraints)
835 : SdpObserverWrapper(jni, j_observer, constraints) {}
836
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000837 virtual void OnFailure(const std::string& error) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000838 SdpObserverWrapper::OnFailure(std::string("Set"), error);
839 }
840};
841
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000842// Adapter for a Java DataChannel$Observer presenting a C++ DataChannelObserver
843// and dispatching the callback from C++ back to Java.
844class DataChannelObserverWrapper : public DataChannelObserver {
845 public:
846 DataChannelObserverWrapper(JNIEnv* jni, jobject j_observer)
847 : j_observer_global_(jni, j_observer),
848 j_observer_class_(jni, GetObjectClass(jni, j_observer)),
849 j_on_state_change_mid_(GetMethodID(jni, *j_observer_class_,
850 "onStateChange", "()V")),
851 j_on_message_mid_(GetMethodID(jni, *j_observer_class_, "onMessage",
852 "(Lorg/webrtc/DataChannel$Buffer;)V")),
853 j_buffer_class_(jni, FindClass(jni, "org/webrtc/DataChannel$Buffer")),
854 j_buffer_ctor_(GetMethodID(jni, *j_buffer_class_,
855 "<init>", "(Ljava/nio/ByteBuffer;Z)V")) {
856 }
857
858 virtual ~DataChannelObserverWrapper() {}
859
860 virtual void OnStateChange() OVERRIDE {
861 jni()->CallVoidMethod(*j_observer_global_, j_on_state_change_mid_);
862 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
863 }
864
865 virtual void OnMessage(const DataBuffer& buffer) OVERRIDE {
866 jobject byte_buffer =
867 jni()->NewDirectByteBuffer(const_cast<char*>(buffer.data.data()),
868 buffer.data.length());
869 jobject j_buffer = jni()->NewObject(*j_buffer_class_, j_buffer_ctor_,
870 byte_buffer, buffer.binary);
871 jni()->CallVoidMethod(*j_observer_global_, j_on_message_mid_, j_buffer);
872 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
873 }
874
875 private:
876 JNIEnv* jni() {
877 return AttachCurrentThreadIfNeeded();
878 }
879
880 const ScopedGlobalRef<jobject> j_observer_global_;
881 const ScopedGlobalRef<jclass> j_observer_class_;
882 const ScopedGlobalRef<jclass> j_buffer_class_;
883 const jmethodID j_on_state_change_mid_;
884 const jmethodID j_on_message_mid_;
885 const jmethodID j_buffer_ctor_;
886};
887
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000888// Adapter for a Java StatsObserver presenting a C++ StatsObserver and
889// dispatching the callback from C++ back to Java.
890class StatsObserverWrapper : public StatsObserver {
891 public:
892 StatsObserverWrapper(JNIEnv* jni, jobject j_observer)
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000893 : j_observer_global_(jni, j_observer),
894 j_observer_class_(jni, GetObjectClass(jni, j_observer)),
895 j_stats_report_class_(jni, FindClass(jni, "org/webrtc/StatsReport")),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000896 j_stats_report_ctor_(GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000897 jni, *j_stats_report_class_, "<init>",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000898 "(Ljava/lang/String;Ljava/lang/String;D"
899 "[Lorg/webrtc/StatsReport$Value;)V")),
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000900 j_value_class_(jni, FindClass(
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000901 jni, "org/webrtc/StatsReport$Value")),
902 j_value_ctor_(GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000903 jni, *j_value_class_, "<init>",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000904 "(Ljava/lang/String;Ljava/lang/String;)V")) {
905 }
906
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000907 virtual ~StatsObserverWrapper() {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000908
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000909 virtual void OnComplete(const std::vector<StatsReport>& reports) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000910 ScopedLocalRef<jobjectArray> j_reports(jni(),
911 ReportsToJava(jni(), reports));
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000912 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onComplete",
913 "([Lorg/webrtc/StatsReport;)V");
914 jni()->CallVoidMethod(*j_observer_global_, m, *j_reports);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000915 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
916 }
917
918 private:
919 jobjectArray ReportsToJava(
920 JNIEnv* jni, const std::vector<StatsReport>& reports) {
921 jobjectArray reports_array = jni->NewObjectArray(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000922 reports.size(), *j_stats_report_class_, NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000923 for (int i = 0; i < reports.size(); ++i) {
924 const StatsReport& report = reports[i];
925 ScopedLocalRef<jstring> j_id(
926 jni, JavaStringFromStdString(jni, report.id));
927 ScopedLocalRef<jstring> j_type(
928 jni, JavaStringFromStdString(jni, report.type));
929 ScopedLocalRef<jobjectArray> j_values(
930 jni, ValuesToJava(jni, report.values));
931 ScopedLocalRef<jobject> j_report(jni, jni->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000932 *j_stats_report_class_, j_stats_report_ctor_, *j_id, *j_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000933 report.timestamp, *j_values));
934 jni->SetObjectArrayElement(reports_array, i, *j_report);
935 }
936 return reports_array;
937 }
938
939 jobjectArray ValuesToJava(JNIEnv* jni, const StatsReport::Values& values) {
940 jobjectArray j_values = jni->NewObjectArray(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000941 values.size(), *j_value_class_, NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000942 for (int i = 0; i < values.size(); ++i) {
943 const StatsReport::Value& value = values[i];
944 ScopedLocalRef<jstring> j_name(
945 jni, JavaStringFromStdString(jni, value.name));
946 ScopedLocalRef<jstring> j_value(
947 jni, JavaStringFromStdString(jni, value.value));
948 ScopedLocalRef<jobject> j_element_value(jni, jni->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000949 *j_value_class_, j_value_ctor_, *j_name, *j_value));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000950 jni->SetObjectArrayElement(j_values, i, *j_element_value);
951 }
952 return j_values;
953 }
954
955 JNIEnv* jni() {
956 return AttachCurrentThreadIfNeeded();
957 }
958
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000959 const ScopedGlobalRef<jobject> j_observer_global_;
960 const ScopedGlobalRef<jclass> j_observer_class_;
961 const ScopedGlobalRef<jclass> j_stats_report_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000962 const jmethodID j_stats_report_ctor_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000963 const ScopedGlobalRef<jclass> j_value_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000964 const jmethodID j_value_ctor_;
965};
966
967// Adapter presenting a cricket::VideoRenderer as a
968// webrtc::VideoRendererInterface.
969class VideoRendererWrapper : public VideoRendererInterface {
970 public:
971 static VideoRendererWrapper* Create(cricket::VideoRenderer* renderer) {
972 if (renderer)
973 return new VideoRendererWrapper(renderer);
974 return NULL;
975 }
976
977 virtual ~VideoRendererWrapper() {}
978
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000979 virtual void SetSize(int width, int height) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000980 const bool kNotReserved = false; // What does this param mean??
981 renderer_->SetSize(width, height, kNotReserved);
982 }
983
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000984 virtual void RenderFrame(const cricket::VideoFrame* frame) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000985 renderer_->RenderFrame(frame);
986 }
987
988 private:
989 explicit VideoRendererWrapper(cricket::VideoRenderer* renderer)
990 : renderer_(renderer) {}
991
992 talk_base::scoped_ptr<cricket::VideoRenderer> renderer_;
993};
994
995// Wrapper dispatching webrtc::VideoRendererInterface to a Java VideoRenderer
996// instance.
997class JavaVideoRendererWrapper : public VideoRendererInterface {
998 public:
999 JavaVideoRendererWrapper(JNIEnv* jni, jobject j_callbacks)
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001000 : j_callbacks_(jni, j_callbacks),
1001 j_set_size_id_(GetMethodID(
1002 jni, GetObjectClass(jni, j_callbacks), "setSize", "(II)V")),
1003 j_render_frame_id_(GetMethodID(
1004 jni, GetObjectClass(jni, j_callbacks), "renderFrame",
1005 "(Lorg/webrtc/VideoRenderer$I420Frame;)V")),
1006 j_frame_class_(jni,
1007 FindClass(jni, "org/webrtc/VideoRenderer$I420Frame")),
1008 j_frame_ctor_id_(GetMethodID(
1009 jni, *j_frame_class_, "<init>", "(II[I[Ljava/nio/ByteBuffer;)V")),
1010 j_byte_buffer_class_(jni, FindClass(jni, "java/nio/ByteBuffer")) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001011 CHECK_EXCEPTION(jni, "");
1012 }
1013
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001014 virtual ~JavaVideoRendererWrapper() {}
1015
1016 virtual void SetSize(int width, int height) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001017 jni()->CallVoidMethod(*j_callbacks_, j_set_size_id_, width, height);
1018 CHECK_EXCEPTION(jni(), "");
1019 }
1020
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001021 virtual void RenderFrame(const cricket::VideoFrame* frame) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001022 ScopedLocalRef<jobject> j_frame(jni(), CricketToJavaFrame(frame));
1023 jni()->CallVoidMethod(*j_callbacks_, j_render_frame_id_, *j_frame);
1024 CHECK_EXCEPTION(jni(), "");
1025 }
1026
1027 private:
1028 // Return a VideoRenderer.I420Frame referring to the data in |frame|.
1029 jobject CricketToJavaFrame(const cricket::VideoFrame* frame) {
1030 ScopedLocalRef<jintArray> strides(jni(), jni()->NewIntArray(3));
1031 jint* strides_array = jni()->GetIntArrayElements(*strides, NULL);
1032 strides_array[0] = frame->GetYPitch();
1033 strides_array[1] = frame->GetUPitch();
1034 strides_array[2] = frame->GetVPitch();
1035 jni()->ReleaseIntArrayElements(*strides, strides_array, 0);
1036 ScopedLocalRef<jobjectArray> planes(
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001037 jni(), jni()->NewObjectArray(3, *j_byte_buffer_class_, NULL));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001038 ScopedLocalRef<jobject> y_buffer(jni(), jni()->NewDirectByteBuffer(
1039 const_cast<uint8*>(frame->GetYPlane()),
1040 frame->GetYPitch() * frame->GetHeight()));
1041 ScopedLocalRef<jobject> u_buffer(jni(), jni()->NewDirectByteBuffer(
1042 const_cast<uint8*>(frame->GetUPlane()), frame->GetChromaSize()));
1043 ScopedLocalRef<jobject> v_buffer(jni(), jni()->NewDirectByteBuffer(
1044 const_cast<uint8*>(frame->GetVPlane()), frame->GetChromaSize()));
1045 jni()->SetObjectArrayElement(*planes, 0, *y_buffer);
1046 jni()->SetObjectArrayElement(*planes, 1, *u_buffer);
1047 jni()->SetObjectArrayElement(*planes, 2, *v_buffer);
1048 return jni()->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001049 *j_frame_class_, j_frame_ctor_id_,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001050 frame->GetWidth(), frame->GetHeight(), *strides, *planes);
1051 }
1052
1053 JNIEnv* jni() {
1054 return AttachCurrentThreadIfNeeded();
1055 }
1056
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001057 ScopedGlobalRef<jobject> j_callbacks_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001058 jmethodID j_set_size_id_;
1059 jmethodID j_render_frame_id_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001060 ScopedGlobalRef<jclass> j_frame_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001061 jmethodID j_frame_ctor_id_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001062 ScopedGlobalRef<jclass> j_byte_buffer_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001063};
1064
1065} // anonymous namespace
1066
1067
1068// Convenience macro defining JNI-accessible methods in the org.webrtc package.
1069// Eliminates unnecessary boilerplate and line-wraps, reducing visual clutter.
1070#define JOW(rettype, name) extern "C" rettype JNIEXPORT JNICALL \
1071 Java_org_webrtc_##name
1072
1073extern "C" jint JNIEXPORT JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
1074 CHECK(!g_jvm, "JNI_OnLoad called more than once!");
1075 g_jvm = jvm;
1076 CHECK(g_jvm, "JNI_OnLoad handed NULL?");
1077
1078 CHECK(talk_base::InitializeSSL(), "Failed to InitializeSSL()");
1079
1080 JNIEnv* jni;
1081 if (jvm->GetEnv(reinterpret_cast<void**>(&jni), JNI_VERSION_1_6) != JNI_OK)
1082 return -1;
1083 g_class_reference_holder = new ClassReferenceHolder(jni);
1084
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001085 return JNI_VERSION_1_6;
1086}
1087
1088extern "C" void JNIEXPORT JNICALL JNI_OnUnLoad(JavaVM *jvm, void *reserved) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001089 delete g_class_reference_holder;
1090 g_class_reference_holder = NULL;
1091 CHECK(talk_base::CleanupSSL(), "Failed to CleanupSSL()");
1092}
1093
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001094static DataChannelInterface* ExtractNativeDC(JNIEnv* jni, jobject j_dc) {
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001095 jfieldID native_dc_id = GetFieldID(jni,
1096 GetObjectClass(jni, j_dc), "nativeDataChannel", "J");
1097 jlong j_d = GetLongField(jni, j_dc, native_dc_id);
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001098 return reinterpret_cast<DataChannelInterface*>(j_d);
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001099}
1100
1101JOW(jlong, DataChannel_registerObserverNative)(
1102 JNIEnv* jni, jobject j_dc, jobject j_observer) {
1103 talk_base::scoped_ptr<DataChannelObserverWrapper> observer(
1104 new DataChannelObserverWrapper(jni, j_observer));
1105 ExtractNativeDC(jni, j_dc)->RegisterObserver(observer.get());
1106 return reinterpret_cast<jlong>(observer.release());
1107}
1108
1109JOW(void, DataChannel_unregisterObserverNative)(
1110 JNIEnv* jni, jobject j_dc, jlong native_observer) {
1111 ExtractNativeDC(jni, j_dc)->UnregisterObserver();
1112 delete reinterpret_cast<DataChannelObserverWrapper*>(native_observer);
1113}
1114
1115JOW(jstring, DataChannel_label)(JNIEnv* jni, jobject j_dc) {
1116 return JavaStringFromStdString(jni, ExtractNativeDC(jni, j_dc)->label());
1117}
1118
1119JOW(jobject, DataChannel_state)(JNIEnv* jni, jobject j_dc) {
1120 return JavaEnumFromIndex(
1121 jni, "DataChannel$State", ExtractNativeDC(jni, j_dc)->state());
1122}
1123
1124JOW(jlong, DataChannel_bufferedAmount)(JNIEnv* jni, jobject j_dc) {
1125 uint64 buffered_amount = ExtractNativeDC(jni, j_dc)->buffered_amount();
1126 CHECK(buffered_amount <= std::numeric_limits<int64>::max(),
1127 "buffered_amount overflowed jlong!");
1128 return static_cast<jlong>(buffered_amount);
1129}
1130
1131JOW(void, DataChannel_close)(JNIEnv* jni, jobject j_dc) {
1132 ExtractNativeDC(jni, j_dc)->Close();
1133}
1134
1135JOW(jboolean, DataChannel_sendNative)(JNIEnv* jni, jobject j_dc,
1136 jbyteArray data, jboolean binary) {
1137 jbyte* bytes = jni->GetByteArrayElements(data, NULL);
1138 bool ret = ExtractNativeDC(jni, j_dc)->Send(DataBuffer(
1139 talk_base::Buffer(bytes, jni->GetArrayLength(data)),
1140 binary));
1141 jni->ReleaseByteArrayElements(data, bytes, JNI_ABORT);
1142 return ret;
1143}
1144
1145JOW(void, DataChannel_dispose)(JNIEnv* jni, jobject j_dc) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001146 CHECK_RELEASE(ExtractNativeDC(jni, j_dc));
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001147}
1148
fischman@webrtc.orgc883fdc2013-08-06 19:00:53 +00001149JOW(void, Logging_nativeEnableTracing)(
1150 JNIEnv* jni, jclass, jstring j_path, jint nativeLevels,
1151 jint nativeSeverity) {
1152 std::string path = JavaToStdString(jni, j_path);
1153 if (nativeLevels != webrtc::kTraceNone) {
andrew@webrtc.org90805182013-09-05 16:40:43 +00001154 webrtc::Trace::set_level_filter(nativeLevels);
fischman@webrtc.org7e4d0df2013-10-01 02:40:43 +00001155#ifdef ANDROID
1156 if (path != "logcat:") {
1157#endif
1158 CHECK(webrtc::Trace::SetTraceFile(path.c_str(), false) == 0,
1159 "SetTraceFile failed");
1160#ifdef ANDROID
1161 } else {
1162 // Intentionally leak this to avoid needing to reason about its lifecycle.
1163 // It keeps no state and functions only as a dispatch point.
1164 static LogcatTraceContext* g_trace_callback = new LogcatTraceContext();
1165 }
1166#endif
fischman@webrtc.orgc883fdc2013-08-06 19:00:53 +00001167 }
1168 talk_base::LogMessage::LogToDebug(nativeSeverity);
1169}
1170
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001171JOW(void, PeerConnection_freePeerConnection)(JNIEnv*, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001172 CHECK_RELEASE(reinterpret_cast<PeerConnectionInterface*>(j_p));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001173}
1174
1175JOW(void, PeerConnection_freeObserver)(JNIEnv*, jclass, jlong j_p) {
1176 PCOJava* p = reinterpret_cast<PCOJava*>(j_p);
1177 delete p;
1178}
1179
1180JOW(void, MediaSource_free)(JNIEnv*, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001181 CHECK_RELEASE(reinterpret_cast<MediaSourceInterface*>(j_p));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001182}
1183
1184JOW(void, VideoCapturer_free)(JNIEnv*, jclass, jlong j_p) {
1185 delete reinterpret_cast<cricket::VideoCapturer*>(j_p);
1186}
1187
1188JOW(void, VideoRenderer_free)(JNIEnv*, jclass, jlong j_p) {
1189 delete reinterpret_cast<VideoRendererWrapper*>(j_p);
1190}
1191
1192JOW(void, MediaStreamTrack_free)(JNIEnv*, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001193 CHECK_RELEASE(reinterpret_cast<MediaStreamTrackInterface*>(j_p));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001194}
1195
1196JOW(jboolean, MediaStream_nativeAddAudioTrack)(
1197 JNIEnv* jni, jclass, jlong pointer, jlong j_audio_track_pointer) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001198 return reinterpret_cast<MediaStreamInterface*>(pointer)->AddTrack(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001199 reinterpret_cast<AudioTrackInterface*>(j_audio_track_pointer));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001200}
1201
1202JOW(jboolean, MediaStream_nativeAddVideoTrack)(
1203 JNIEnv* jni, jclass, jlong pointer, jlong j_video_track_pointer) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001204 return reinterpret_cast<MediaStreamInterface*>(pointer)
1205 ->AddTrack(reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001206}
1207
1208JOW(jboolean, MediaStream_nativeRemoveAudioTrack)(
1209 JNIEnv* jni, jclass, jlong pointer, jlong j_audio_track_pointer) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001210 return reinterpret_cast<MediaStreamInterface*>(pointer)->RemoveTrack(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001211 reinterpret_cast<AudioTrackInterface*>(j_audio_track_pointer));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001212}
1213
1214JOW(jboolean, MediaStream_nativeRemoveVideoTrack)(
1215 JNIEnv* jni, jclass, jlong pointer, jlong j_video_track_pointer) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001216 return reinterpret_cast<MediaStreamInterface*>(pointer)->RemoveTrack(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001217 reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001218}
1219
1220JOW(jstring, MediaStream_nativeLabel)(JNIEnv* jni, jclass, jlong j_p) {
1221 return JavaStringFromStdString(
1222 jni, reinterpret_cast<MediaStreamInterface*>(j_p)->label());
1223}
1224
1225JOW(void, MediaStream_free)(JNIEnv*, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001226 CHECK_RELEASE(reinterpret_cast<MediaStreamInterface*>(j_p));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001227}
1228
1229JOW(jlong, PeerConnectionFactory_nativeCreateObserver)(
1230 JNIEnv * jni, jclass, jobject j_observer) {
1231 return (jlong)new PCOJava(jni, j_observer);
1232}
1233
1234#ifdef ANDROID
1235JOW(jboolean, PeerConnectionFactory_initializeAndroidGlobals)(
1236 JNIEnv* jni, jclass, jobject context) {
1237 CHECK(g_jvm, "JNI_OnLoad failed to run?");
1238 bool failure = false;
fischman@webrtc.org4e65e072013-10-03 18:23:13 +00001239 failure |= webrtc::VideoEngine::SetAndroidObjects(g_jvm);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001240 failure |= webrtc::VoiceEngine::SetAndroidObjects(g_jvm, jni, context);
1241 return !failure;
1242}
1243#endif // ANDROID
1244
1245JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnectionFactory)(
1246 JNIEnv* jni, jclass) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001247 webrtc::Trace::CreateTrace();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001248 talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
1249 webrtc::CreatePeerConnectionFactory());
1250 return (jlong)factory.release();
1251}
1252
1253JOW(void, PeerConnectionFactory_freeFactory)(JNIEnv*, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001254 CHECK_RELEASE(reinterpret_cast<PeerConnectionFactoryInterface*>(j_p));
1255 webrtc::Trace::ReturnTrace();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001256}
1257
1258JOW(jlong, PeerConnectionFactory_nativeCreateLocalMediaStream)(
1259 JNIEnv* jni, jclass, jlong native_factory, jstring label) {
1260 talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
1261 reinterpret_cast<PeerConnectionFactoryInterface*>(native_factory));
1262 talk_base::scoped_refptr<MediaStreamInterface> stream(
1263 factory->CreateLocalMediaStream(JavaToStdString(jni, label)));
1264 return (jlong)stream.release();
1265}
1266
1267JOW(jlong, PeerConnectionFactory_nativeCreateVideoSource)(
1268 JNIEnv* jni, jclass, jlong native_factory, jlong native_capturer,
1269 jobject j_constraints) {
1270 talk_base::scoped_ptr<ConstraintsWrapper> constraints(
1271 new ConstraintsWrapper(jni, j_constraints));
1272 talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
1273 reinterpret_cast<PeerConnectionFactoryInterface*>(native_factory));
1274 talk_base::scoped_refptr<VideoSourceInterface> source(
1275 factory->CreateVideoSource(
1276 reinterpret_cast<cricket::VideoCapturer*>(native_capturer),
1277 constraints.get()));
1278 return (jlong)source.release();
1279}
1280
1281JOW(jlong, PeerConnectionFactory_nativeCreateVideoTrack)(
1282 JNIEnv* jni, jclass, jlong native_factory, jstring id,
1283 jlong native_source) {
1284 talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
1285 reinterpret_cast<PeerConnectionFactoryInterface*>(native_factory));
1286 talk_base::scoped_refptr<VideoTrackInterface> track(
1287 factory->CreateVideoTrack(
1288 JavaToStdString(jni, id),
1289 reinterpret_cast<VideoSourceInterface*>(native_source)));
1290 return (jlong)track.release();
1291}
1292
1293JOW(jlong, PeerConnectionFactory_nativeCreateAudioTrack)(
1294 JNIEnv* jni, jclass, jlong native_factory, jstring id) {
1295 talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
1296 reinterpret_cast<PeerConnectionFactoryInterface*>(native_factory));
1297 talk_base::scoped_refptr<AudioTrackInterface> track(
1298 factory->CreateAudioTrack(JavaToStdString(jni, id), NULL));
1299 return (jlong)track.release();
1300}
1301
1302static void JavaIceServersToJsepIceServers(
1303 JNIEnv* jni, jobject j_ice_servers,
1304 PeerConnectionInterface::IceServers* ice_servers) {
1305 jclass list_class = GetObjectClass(jni, j_ice_servers);
1306 jmethodID iterator_id = GetMethodID(
1307 jni, list_class, "iterator", "()Ljava/util/Iterator;");
1308 jobject iterator = jni->CallObjectMethod(j_ice_servers, iterator_id);
1309 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
1310 jmethodID iterator_has_next = GetMethodID(
1311 jni, GetObjectClass(jni, iterator), "hasNext", "()Z");
1312 jmethodID iterator_next = GetMethodID(
1313 jni, GetObjectClass(jni, iterator), "next", "()Ljava/lang/Object;");
1314 while (jni->CallBooleanMethod(iterator, iterator_has_next)) {
1315 CHECK_EXCEPTION(jni, "error during CallBooleanMethod");
1316 jobject j_ice_server = jni->CallObjectMethod(iterator, iterator_next);
1317 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
1318 jclass j_ice_server_class = GetObjectClass(jni, j_ice_server);
1319 jfieldID j_ice_server_uri_id =
1320 GetFieldID(jni, j_ice_server_class, "uri", "Ljava/lang/String;");
1321 jfieldID j_ice_server_username_id =
1322 GetFieldID(jni, j_ice_server_class, "username", "Ljava/lang/String;");
1323 jfieldID j_ice_server_password_id =
1324 GetFieldID(jni, j_ice_server_class, "password", "Ljava/lang/String;");
1325 jstring uri = reinterpret_cast<jstring>(
1326 GetObjectField(jni, j_ice_server, j_ice_server_uri_id));
1327 jstring username = reinterpret_cast<jstring>(
1328 GetObjectField(jni, j_ice_server, j_ice_server_username_id));
1329 jstring password = reinterpret_cast<jstring>(
1330 GetObjectField(jni, j_ice_server, j_ice_server_password_id));
1331 PeerConnectionInterface::IceServer server;
1332 server.uri = JavaToStdString(jni, uri);
1333 server.username = JavaToStdString(jni, username);
1334 server.password = JavaToStdString(jni, password);
1335 ice_servers->push_back(server);
1336 }
1337 CHECK_EXCEPTION(jni, "error during CallBooleanMethod");
1338}
1339
1340JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnection)(
1341 JNIEnv *jni, jclass, jlong factory, jobject j_ice_servers,
1342 jobject j_constraints, jlong observer_p) {
1343 talk_base::scoped_refptr<PeerConnectionFactoryInterface> f(
1344 reinterpret_cast<PeerConnectionFactoryInterface*>(factory));
1345 PeerConnectionInterface::IceServers servers;
1346 JavaIceServersToJsepIceServers(jni, j_ice_servers, &servers);
1347 PCOJava* observer = reinterpret_cast<PCOJava*>(observer_p);
1348 observer->SetConstraints(new ConstraintsWrapper(jni, j_constraints));
1349 talk_base::scoped_refptr<PeerConnectionInterface> pc(f->CreatePeerConnection(
1350 servers, observer->constraints(), NULL, observer));
1351 return (jlong)pc.release();
1352}
1353
1354static talk_base::scoped_refptr<PeerConnectionInterface> ExtractNativePC(
1355 JNIEnv* jni, jobject j_pc) {
1356 jfieldID native_pc_id = GetFieldID(jni,
1357 GetObjectClass(jni, j_pc), "nativePeerConnection", "J");
1358 jlong j_p = GetLongField(jni, j_pc, native_pc_id);
1359 return talk_base::scoped_refptr<PeerConnectionInterface>(
1360 reinterpret_cast<PeerConnectionInterface*>(j_p));
1361}
1362
1363JOW(jobject, PeerConnection_getLocalDescription)(JNIEnv* jni, jobject j_pc) {
1364 const SessionDescriptionInterface* sdp =
1365 ExtractNativePC(jni, j_pc)->local_description();
1366 return sdp ? JavaSdpFromNativeSdp(jni, sdp) : NULL;
1367}
1368
1369JOW(jobject, PeerConnection_getRemoteDescription)(JNIEnv* jni, jobject j_pc) {
1370 const SessionDescriptionInterface* sdp =
1371 ExtractNativePC(jni, j_pc)->remote_description();
1372 return sdp ? JavaSdpFromNativeSdp(jni, sdp) : NULL;
1373}
1374
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001375JOW(jobject, PeerConnection_createDataChannel)(
1376 JNIEnv* jni, jobject j_pc, jstring j_label, jobject j_init) {
1377 DataChannelInit init = JavaDataChannelInitToNative(jni, j_init);
1378 talk_base::scoped_refptr<DataChannelInterface> channel(
1379 ExtractNativePC(jni, j_pc)->CreateDataChannel(
1380 JavaToStdString(jni, j_label), &init));
fischman@webrtc.org87881672013-09-03 18:58:12 +00001381 // Mustn't pass channel.get() directly through NewObject to avoid reading its
1382 // vararg parameter as 64-bit and reading memory that doesn't belong to the
1383 // 32-bit parameter.
1384 jlong nativeChannelPtr = jlongFromPointer(channel.get());
1385 CHECK(nativeChannelPtr, "Failed to create DataChannel");
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001386 jclass j_data_channel_class = FindClass(jni, "org/webrtc/DataChannel");
1387 jmethodID j_data_channel_ctor = GetMethodID(
1388 jni, j_data_channel_class, "<init>", "(J)V");
1389 jobject j_channel = jni->NewObject(
fischman@webrtc.org87881672013-09-03 18:58:12 +00001390 j_data_channel_class, j_data_channel_ctor, nativeChannelPtr);
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001391 CHECK_EXCEPTION(jni, "error during NewObject");
1392 // Channel is now owned by Java object, and will be freed from there.
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001393 int bumped_count = channel->AddRef();
1394 CHECK(bumped_count == 2, "Unexpected refcount");
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001395 return j_channel;
1396}
1397
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001398JOW(void, PeerConnection_createOffer)(
1399 JNIEnv* jni, jobject j_pc, jobject j_observer, jobject j_constraints) {
1400 ConstraintsWrapper* constraints =
1401 new ConstraintsWrapper(jni, j_constraints);
1402 talk_base::scoped_refptr<CreateSdpObserverWrapper> observer(
1403 new talk_base::RefCountedObject<CreateSdpObserverWrapper>(
1404 jni, j_observer, constraints));
1405 ExtractNativePC(jni, j_pc)->CreateOffer(observer, constraints);
1406}
1407
1408JOW(void, PeerConnection_createAnswer)(
1409 JNIEnv* jni, jobject j_pc, jobject j_observer, jobject j_constraints) {
1410 ConstraintsWrapper* constraints =
1411 new ConstraintsWrapper(jni, j_constraints);
1412 talk_base::scoped_refptr<CreateSdpObserverWrapper> observer(
1413 new talk_base::RefCountedObject<CreateSdpObserverWrapper>(
1414 jni, j_observer, constraints));
1415 ExtractNativePC(jni, j_pc)->CreateAnswer(observer, constraints);
1416}
1417
1418// Helper to create a SessionDescriptionInterface from a SessionDescription.
1419static SessionDescriptionInterface* JavaSdpToNativeSdp(
1420 JNIEnv* jni, jobject j_sdp) {
1421 jfieldID j_type_id = GetFieldID(
1422 jni, GetObjectClass(jni, j_sdp), "type",
1423 "Lorg/webrtc/SessionDescription$Type;");
1424 jobject j_type = GetObjectField(jni, j_sdp, j_type_id);
1425 jmethodID j_canonical_form_id = GetMethodID(
1426 jni, GetObjectClass(jni, j_type), "canonicalForm",
1427 "()Ljava/lang/String;");
1428 jstring j_type_string = (jstring)jni->CallObjectMethod(
1429 j_type, j_canonical_form_id);
1430 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
1431 std::string std_type = JavaToStdString(jni, j_type_string);
1432
1433 jfieldID j_description_id = GetFieldID(
1434 jni, GetObjectClass(jni, j_sdp), "description", "Ljava/lang/String;");
1435 jstring j_description = (jstring)GetObjectField(jni, j_sdp, j_description_id);
1436 std::string std_description = JavaToStdString(jni, j_description);
1437
1438 return webrtc::CreateSessionDescription(
1439 std_type, std_description, NULL);
1440}
1441
1442JOW(void, PeerConnection_setLocalDescription)(
1443 JNIEnv* jni, jobject j_pc,
1444 jobject j_observer, jobject j_sdp) {
1445 talk_base::scoped_refptr<SetSdpObserverWrapper> observer(
1446 new talk_base::RefCountedObject<SetSdpObserverWrapper>(
1447 jni, j_observer, reinterpret_cast<ConstraintsWrapper*>(NULL)));
1448 ExtractNativePC(jni, j_pc)->SetLocalDescription(
1449 observer, JavaSdpToNativeSdp(jni, j_sdp));
1450}
1451
1452JOW(void, PeerConnection_setRemoteDescription)(
1453 JNIEnv* jni, jobject j_pc,
1454 jobject j_observer, jobject j_sdp) {
1455 talk_base::scoped_refptr<SetSdpObserverWrapper> observer(
1456 new talk_base::RefCountedObject<SetSdpObserverWrapper>(
1457 jni, j_observer, reinterpret_cast<ConstraintsWrapper*>(NULL)));
1458 ExtractNativePC(jni, j_pc)->SetRemoteDescription(
1459 observer, JavaSdpToNativeSdp(jni, j_sdp));
1460}
1461
1462JOW(jboolean, PeerConnection_updateIce)(
1463 JNIEnv* jni, jobject j_pc, jobject j_ice_servers, jobject j_constraints) {
1464 PeerConnectionInterface::IceServers ice_servers;
1465 JavaIceServersToJsepIceServers(jni, j_ice_servers, &ice_servers);
1466 talk_base::scoped_ptr<ConstraintsWrapper> constraints(
1467 new ConstraintsWrapper(jni, j_constraints));
1468 return ExtractNativePC(jni, j_pc)->UpdateIce(ice_servers, constraints.get());
1469}
1470
1471JOW(jboolean, PeerConnection_nativeAddIceCandidate)(
1472 JNIEnv* jni, jobject j_pc, jstring j_sdp_mid,
1473 jint j_sdp_mline_index, jstring j_candidate_sdp) {
1474 std::string sdp_mid = JavaToStdString(jni, j_sdp_mid);
1475 std::string sdp = JavaToStdString(jni, j_candidate_sdp);
1476 talk_base::scoped_ptr<IceCandidateInterface> candidate(
1477 webrtc::CreateIceCandidate(sdp_mid, j_sdp_mline_index, sdp, NULL));
1478 return ExtractNativePC(jni, j_pc)->AddIceCandidate(candidate.get());
1479}
1480
1481JOW(jboolean, PeerConnection_nativeAddLocalStream)(
1482 JNIEnv* jni, jobject j_pc, jlong native_stream, jobject j_constraints) {
1483 talk_base::scoped_ptr<ConstraintsWrapper> constraints(
1484 new ConstraintsWrapper(jni, j_constraints));
1485 return ExtractNativePC(jni, j_pc)->AddStream(
1486 reinterpret_cast<MediaStreamInterface*>(native_stream),
1487 constraints.get());
1488}
1489
1490JOW(void, PeerConnection_nativeRemoveLocalStream)(
1491 JNIEnv* jni, jobject j_pc, jlong native_stream) {
1492 ExtractNativePC(jni, j_pc)->RemoveStream(
1493 reinterpret_cast<MediaStreamInterface*>(native_stream));
1494}
1495
1496JOW(bool, PeerConnection_nativeGetStats)(
1497 JNIEnv* jni, jobject j_pc, jobject j_observer, jlong native_track) {
1498 talk_base::scoped_refptr<StatsObserverWrapper> observer(
1499 new talk_base::RefCountedObject<StatsObserverWrapper>(jni, j_observer));
1500 return ExtractNativePC(jni, j_pc)->GetStats(
1501 observer, reinterpret_cast<MediaStreamTrackInterface*>(native_track));
1502}
1503
1504JOW(jobject, PeerConnection_signalingState)(JNIEnv* jni, jobject j_pc) {
1505 PeerConnectionInterface::SignalingState state =
1506 ExtractNativePC(jni, j_pc)->signaling_state();
1507 return JavaEnumFromIndex(jni, "PeerConnection$SignalingState", state);
1508}
1509
1510JOW(jobject, PeerConnection_iceConnectionState)(JNIEnv* jni, jobject j_pc) {
1511 PeerConnectionInterface::IceConnectionState state =
1512 ExtractNativePC(jni, j_pc)->ice_connection_state();
1513 return JavaEnumFromIndex(jni, "PeerConnection$IceConnectionState", state);
1514}
1515
1516JOW(jobject, PeerGathering_iceGatheringState)(JNIEnv* jni, jobject j_pc) {
1517 PeerConnectionInterface::IceGatheringState state =
1518 ExtractNativePC(jni, j_pc)->ice_gathering_state();
1519 return JavaEnumFromIndex(jni, "PeerGathering$IceGatheringState", state);
1520}
1521
1522JOW(void, PeerConnection_close)(JNIEnv* jni, jobject j_pc) {
1523 ExtractNativePC(jni, j_pc)->Close();
1524 return;
1525}
1526
1527JOW(jobject, MediaSource_nativeState)(JNIEnv* jni, jclass, jlong j_p) {
1528 talk_base::scoped_refptr<MediaSourceInterface> p(
1529 reinterpret_cast<MediaSourceInterface*>(j_p));
1530 return JavaEnumFromIndex(jni, "MediaSource$State", p->state());
1531}
1532
1533JOW(jlong, VideoCapturer_nativeCreateVideoCapturer)(
1534 JNIEnv* jni, jclass, jstring j_device_name) {
1535 std::string device_name = JavaToStdString(jni, j_device_name);
1536 talk_base::scoped_ptr<cricket::DeviceManagerInterface> device_manager(
1537 cricket::DeviceManagerFactory::Create());
1538 CHECK(device_manager->Init(), "DeviceManager::Init() failed");
1539 cricket::Device device;
1540 if (!device_manager->GetVideoCaptureDevice(device_name, &device)) {
fischman@webrtc.org4e65e072013-10-03 18:23:13 +00001541 LOG(LS_ERROR) << "GetVideoCaptureDevice failed for " << device_name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001542 return 0;
1543 }
1544 talk_base::scoped_ptr<cricket::VideoCapturer> capturer(
1545 device_manager->CreateVideoCapturer(device));
1546 return (jlong)capturer.release();
1547}
1548
1549JOW(jlong, VideoRenderer_nativeCreateGuiVideoRenderer)(
1550 JNIEnv* jni, jclass, int x, int y) {
1551 talk_base::scoped_ptr<VideoRendererWrapper> renderer(
1552 VideoRendererWrapper::Create(
1553 cricket::VideoRendererFactory::CreateGuiVideoRenderer(x, y)));
1554 return (jlong)renderer.release();
1555}
1556
1557JOW(jlong, VideoRenderer_nativeWrapVideoRenderer)(
1558 JNIEnv* jni, jclass, jobject j_callbacks) {
1559 talk_base::scoped_ptr<JavaVideoRendererWrapper> renderer(
1560 new JavaVideoRendererWrapper(jni, j_callbacks));
1561 return (jlong)renderer.release();
1562}
1563
fischman@webrtc.org4e65e072013-10-03 18:23:13 +00001564JOW(jlong, VideoSource_stop)(JNIEnv* jni, jclass, jlong j_p) {
1565 cricket::VideoCapturer* capturer =
1566 reinterpret_cast<VideoSourceInterface*>(j_p)->GetVideoCapturer();
1567 talk_base::scoped_ptr<cricket::VideoFormatPod> format(
1568 new cricket::VideoFormatPod(*capturer->GetCaptureFormat()));
1569 capturer->Stop();
1570 return jlongFromPointer(format.release());
1571}
1572
1573JOW(void, VideoSource_restart)(
1574 JNIEnv* jni, jclass, jlong j_p_source, jlong j_p_format) {
1575 talk_base::scoped_ptr<cricket::VideoFormatPod> format(
1576 reinterpret_cast<cricket::VideoFormatPod*>(j_p_format));
1577 reinterpret_cast<VideoSourceInterface*>(j_p_source)->GetVideoCapturer()->
1578 StartCapturing(cricket::VideoFormat(*format));
1579}
1580
fischman@webrtc.orga7266ca2013-10-03 19:04:18 +00001581JOW(void, VideoSource_freeNativeVideoFormat)(
fischman@webrtc.org4e65e072013-10-03 18:23:13 +00001582 JNIEnv* jni, jclass, jlong j_p) {
1583 delete reinterpret_cast<cricket::VideoFormatPod*>(j_p);
1584}
1585
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001586JOW(jstring, MediaStreamTrack_nativeId)(JNIEnv* jni, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001587 return JavaStringFromStdString(
1588 jni, reinterpret_cast<MediaStreamTrackInterface*>(j_p)->id());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001589}
1590
1591JOW(jstring, MediaStreamTrack_nativeKind)(JNIEnv* jni, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001592 return JavaStringFromStdString(
1593 jni, reinterpret_cast<MediaStreamTrackInterface*>(j_p)->kind());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001594}
1595
1596JOW(jboolean, MediaStreamTrack_nativeEnabled)(JNIEnv* jni, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001597 return reinterpret_cast<MediaStreamTrackInterface*>(j_p)->enabled();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001598}
1599
1600JOW(jobject, MediaStreamTrack_nativeState)(JNIEnv* jni, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001601 return JavaEnumFromIndex(
1602 jni,
1603 "MediaStreamTrack$State",
1604 reinterpret_cast<MediaStreamTrackInterface*>(j_p)->state());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001605}
1606
1607JOW(jboolean, MediaStreamTrack_nativeSetState)(
1608 JNIEnv* jni, jclass, jlong j_p, jint j_new_state) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001609 MediaStreamTrackInterface::TrackState new_state =
1610 (MediaStreamTrackInterface::TrackState)j_new_state;
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001611 return reinterpret_cast<MediaStreamTrackInterface*>(j_p)
1612 ->set_state(new_state);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001613}
1614
1615JOW(jboolean, MediaStreamTrack_nativeSetEnabled)(
1616 JNIEnv* jni, jclass, jlong j_p, jboolean enabled) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001617 return reinterpret_cast<MediaStreamTrackInterface*>(j_p)
1618 ->set_enabled(enabled);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001619}
1620
1621JOW(void, VideoTrack_nativeAddRenderer)(
1622 JNIEnv* jni, jclass,
1623 jlong j_video_track_pointer, jlong j_renderer_pointer) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001624 reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer)->AddRenderer(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001625 reinterpret_cast<VideoRendererInterface*>(j_renderer_pointer));
1626}
1627
1628JOW(void, VideoTrack_nativeRemoveRenderer)(
1629 JNIEnv* jni, jclass,
1630 jlong j_video_track_pointer, jlong j_renderer_pointer) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001631 reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer)->RemoveRenderer(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001632 reinterpret_cast<VideoRendererInterface*>(j_renderer_pointer));
1633}